/************************************************************************
 *	Modify to taste in order to comply with your authentication	*
 *	(e.g. Radius or shadow passwd) and mailbox conventions		*
 *									*
 *	You have the liberty to redefine the identity typedef in	*
 *	any way you see fit, so that it can hold state information	*
 *	you need to authenticate your users				*
 *									*
 *	Copyright (c) 1996-1997, S.R. van den Berg, The Netherlands	*
 *	#include "../README" or "README"				*
 ************************************************************************/
#ifdef RCS
static /*const*/char rcsid[]=
 "$Id: authenticate.c,v 1.19 1998/05/13 16:57:39 srb Exp $";
#endif

#ifdef INET6
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif

#ifdef PROCMAIL
#include "includes.h"
#include "robust.h"
#include "shell.h"
#include "misc.h"
#else
#include "config.h"

#define _XOPEN_SOURCE

#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <stdlib.h>

#ifdef SHADOW_PASSWD
#include <shadow.h>
#endif
#endif /* PROCMAIL */

#include "authenticate.h"

#define VIRTUALSERVER	/**/

#ifndef SENDMAILLIB
#define SENDMAILLIB	"/etc/mail"
#endif
#define VIRTUALHOSTDB	"vpop.db"
#define VIRTUALUSER	"vpop"

#ifndef MAILSPOOLDIR
#define MAILSPOOLDIR	"/var/mail/"	     /* watch the trailing / */
#endif
#ifndef MAILSPOOLHASH
#define MAILSPOOLHASH	0      /* 2 would deliver to /var/spool/mail/b/a/bar */
#endif
/*#define MAILSPOOLHOME "/.mail"		      /* watch the leading / */
						  /* delivers to $HOME/.mail */
#ifdef VIRTUALSERVER
#include <netdb.h>
#include <netinet/in.h>

#include "sdb.h"
#include "simplecrypt.h"

const char virtualhostdb[]=SENDMAILLIB"/"VIRTUALHOSTDB,
 virtualuser[]=VIRTUALUSER;
#endif /* VIRTUALSERVER */

#define STRLEN(x)	(sizeof(x)-1)

struct auth_identity
{ const struct passwd*pw;
  char*mbox,*usersecret;
  int sock;
};

char*auth_logname;

static auth_identity authi;		      /* reuse copy, only one active */

static void castlower(str)register char*str;   /* and I'll take the low road */
{ for(;*str;str++)
     if((unsigned)*str-'A'<='Z'-'A')		     /* uppercase character? */
	*str+='a'-'A';				     /* cast it to lowercase */
}

static void getlogname(user,sock)const char*user;const int sock;
{
#ifdef INET6
  static char hbuf[NI_MAXHOST];
  struct sockaddr_storage sockname;
#ifdef NI_WITHSCOPEID
  const int niflags = NI_NAMEREQD | NI_WITHSCOPEID;
#else
  const int niflags = NI_NAMEREQD;
#endif
#else
  struct sockaddr_in sockname;
#endif
  int namelen=sizeof sockname;const char*retval="";
  if(!getsockname(sock,(struct sockaddr*)&sockname,&namelen))
   {
#ifdef INET6
    if (getnameinfo((struct sockaddr *)&sockname, namelen, hbuf, sizeof(hbuf),
	NULL, 0, niflags) == 0) {
      retval = hbuf;
    }
#else
     const struct hostent*hent=
      gethostbyaddr((void*)&sockname.sin_addr.s_addr,sizeof sockname.sin_addr,
       sockname.sin_family);
     if(hent)
	retval=hent->h_name;
#endif
   }
  if(auth_logname=			   /* memory leak when out of memory */
   realloc(auth_logname,(namelen=strlen(retval))+1+strlen(user)+1))
   { strcpy(auth_logname,retval)[namelen]='/';
     strcpy(auth_logname+namelen+1,user);
   }
}

static const struct passwd*cgetpwnam(user,sock)const char*user;
 const int sock;
{
#ifdef VIRTUALSERVER
  DB_ENV dbenv;DB*db;
  memset(&dbenv,0,sizeof dbenv);
  if(!db_appinit(0,0,&dbenv,0))
   { if(!db_open(virtualhostdb,DB_HASH,DB_RDONLY,0666,&dbenv,(void*)0,&db))
      { getlogname(user,sock);
	if(auth_logname)
	 { DBT k,d;unsigned foundentry=1;
	   memset(&k,0,sizeof k);memset(&d,0,sizeof d);
	   k.size=strlen(k.data=auth_logname);
	   if(db->get(db,0,&k,&d,0))
	    { k.size=strlen(strcpy(auth_logname,user));
	      foundentry=!db->get(db,0,&k,&d,0)<<1;	   /* fallback check */
	    }
	   if(foundentry&&(authi.usersecret=malloc(d.size+1)))
	    { scmorph(&k,&d);memcpy(authi.usersecret,d.data,d.size);
	      authi.usersecret[d.size]='\0';memset(d.data,0,d.size);
	      if(foundentry&2)
		 goto freesec;
	      else if(*authi.usersecret)	 /* no empty secrets allowed */
		 user=virtualuser;
	      else
freesec:       { free(authi.usersecret);authi.usersecret=0;
		 goto novirt;
	       }
	    }
	   else
novirt:	      strcpy(auth_logname,user);
	 }
	db->close(db,0);
      }					   /* memory leak when out of memory */
     else if(auth_logname=realloc(auth_logname,strlen(user)+1))
	strcpy(auth_logname,user);			 /* DB doesn't exist */
     db_appexit(&dbenv);
   }
  else						     /* DB subsystem problem */
   { if(auth_logname=realloc(auth_logname,strlen(user)+1))
	strcpy(auth_logname,user);
   }
#endif
  return getpwnam(user);	       /* this should be selfexplanatory :-) */
}

static const struct passwd*cgetpwuid(uid,sock)const uid_t uid;const int sock;
{ const struct passwd*pass;
  pass=getpwuid(uid);
#ifdef VIRTUALSERVER
  ;{ const char*logname=pass->pw_name;
     if(pass&&!auth_logname&&(auth_logname=malloc(strlen(logname))))
	strcpy(auth_logname,logname);
   }
#endif
  return pass;
}

/*const*/auth_identity*auth_finduser(user,sock)char*const user;const int sock;
{ if(authi.usersecret)
     free(authi.usersecret),authi.usersecret=0;
  if(!(authi.pw=cgetpwnam(user,sock)))		  /* /etc/passwd user lookup */
   { char*p;
     if(p=strchr(user,'@'))		  /* does the username contain an @? */
	*p='\0';		      /* clueless user using the mailaddress */
     castlower(user);	      /* make it all lowercase (luser problem no. 1) */
     if(!(authi.pw=cgetpwnam(user,sock)))	/* ok, be nice and try again */
	return 0;		       /* sorry, no such user on this planet */
   }
  authi.sock=sock;  /* save the filedescriptor for virtual server separation */
  if(authi.mbox)			  /* any old mailbox reference left? */
     free(authi.mbox),authi.mbox=0;		      /* clear the reference */
  return &authi;					       /* user found */
}

/*const*/auth_identity*auth_finduid(uid,sock)const uid_t uid;const int sock;
{ if(!(authi.pw=cgetpwuid(uid,sock)))		  /* /etc/passwd user lookup */
     return 0;							     /* nada */
  authi.sock=sock;		    /* save filedescriptor for later perusal */
  if(authi.usersecret)
     free(authi.usersecret),authi.usersecret=0;
  if(authi.mbox)				   /* old mailbox reference? */
     free(authi.mbox),authi.mbox=0;		/* nix old mailbox reference */
  return &authi;					       /* user found */
}

#ifndef PROCMAIL
int auth_checkpassword(pass,pw,allowemptypw)const auth_identity*const pass;
 const char*const pw;const int allowemptypw;
{ const char*rpw;
  rpw=pass->pw->pw_passwd;	     /* get the regular (encrypted) password */
#ifdef SHADOW_PASSWD
  ;{ struct spwd*spwd;
     if(spwd=getspnam(pass->pw->pw_name))	     /* any shadow password? */
	rpw=spwd->sp_pwdp;			 /* override the regular one */
   }
#endif
  if(!*rpw)					     /* empty password found */
     return allowemptypw;			    /* should we allow this? */
  return !strcmp(rpw,crypt(pw,rpw));		    /* compare the passwords */
}

const char*auth_getsecret(pass)const auth_identity*const pass;
{ return authi.usersecret;
}

int auth_checkvalidshell(pass)const auth_identity*const pass;
{
}
#else /* PROCMAIL */
auth_identity*auth_newid P((void))
{ auth_identity*pass;		   /* create a new auth_identity placeholder */
  (pass=malloc(sizeof*pass))->pw=0;pass->mbox=0;return pass;
}

void auth_copyid(newpass,oldpass)auth_identity*newpass;
 const auth_identity*oldpass;
{ struct passwd*np;const struct passwd*op;
  if(newpass->mbox)
     free(newpass->mbox),newpass->mbox=0;
  newpass->sock=oldpass->sock;
  if(!(np=(struct passwd*)newpass->pw))
   { np=(struct passwd*)(newpass->pw=malloc(sizeof*np));
     np->pw_name=np->pw_dir=np->pw_shell=0;
   }
  np->pw_uid=(op=oldpass->pw)->pw_uid;np->pw_gid=op->pw_gid;
  np->pw_name=cstr(np->pw_name,op->pw_name);
  np->pw_dir=cstr(np->pw_dir,op->pw_dir);
  np->pw_shell=cstr(np->pw_shell,op->pw_shell);
}

void auth_freeid(pass)auth_identity*pass;
{ struct passwd*p;
  if(p=(struct passwd*)pass->pw)
     free(p->pw_name),free(p->pw_dir),free(p->pw_shell),free(p);
  if(pass->mbox)
     free(pass->mbox);
  free(pass);
}

int auth_filledid(pass)const auth_identity*pass;
{ return !!pass->pw;
}
#endif /* PROCMAIL */

const char*auth_mailboxname(pass)auth_identity*const pass;
{ if(!pass->mbox)
#ifdef MAILSPOOLHOME
   { static const char mailfile[]=MAILSPOOLHOME;size_t i;
     if(!(pass->mbox=malloc((i=strlen(pass->pw->pw_dir))+STRLEN(mailfile)+1)))
	return "";
     strcpy(pass->mbox,pass->pw->pw_dir);
     strcpy(pass->mbox+i,mailfile);
#else
   { static const char mailspooldir[]=MAILSPOOLDIR;size_t servernlen;
#ifdef VIRTUALSERVER
     char*logname=auth_logname;
#else
     char*logname=pass->pw->pw_name;
#endif
     if(!logname||!(pass->mbox=malloc(STRLEN(mailspooldir)+
      MAILSPOOLHASH*2+strlen(logname)+1)))
	return "";
     strcpy(pass->mbox,mailspooldir);
     ;{ char*p,*n;size_t i;int c;
	for(p=pass->mbox+STRLEN(mailspooldir),n=logname,i=MAILSPOOLHASH;
	    i--;
	    *p++='/')
	 { if(*n)
	      c= *n++;
	   *p++=c;
	 }
	strcpy(p,logname);
      }
#endif /* MAILSPOOLHOME */
   }
  return pass->mbox;
}

uid_t auth_whatuid(pass)const auth_identity*const pass;
{ return pass->pw->pw_uid;
}

uid_t auth_whatgid(pass)const auth_identity*const pass;
{ return pass->pw->pw_gid;
}

const char*auth_homedir(pass)const auth_identity*const pass;
{ return pass->pw->pw_dir;
}

const char*auth_shell(pass)const auth_identity*const pass;
{ return pass->pw->pw_shell;
}

const char*auth_username(pass)const auth_identity*const pass;
{ return auth_logname?auth_logname:pass->pw->pw_name;
}

void auth_end P((void))
{ if(authi.mbox)
     free(authi.mbox),authi.mbox=0;	    /* discard the mailbox reference */
  endpwent();
#ifdef SHADOW_PASSWD
  endspent();
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1