/********************************************************************
 * lindner
 * 3.13
 * 1995/02/06 21:25:13
 * /home/arcwelder/GopherSrc/CVS/gopher+/gopherd/AUTH.c,v
 *
 * Paul Lindner, University of Minnesota DCS.
 *
 * Copyright 1991, 92, 93, 94 by the Regents of the University of Minnesota
 * see the file "Copyright" in the distribution for conditions of use.
 *********************************************************************
 * MODULE: AUTH.c
 * Routines to verify authentication
 *********************************************************************
 * Revision History:
 * AUTH.c,v
 * Revision 3.13  1995/02/06  21:25:13  lindner
 * Better stdio include
 *
 * Revision 3.12  1995/02/02  17:13:52  lindner
 * Fix memory leaks
 *
 * Revision 3.11  1994/12/31  07:45:22  lindner
 * Remove Item is protected note
 *
 * Revision 3.10  1994/12/15  17:32:21  lindner
 * Fix for A/UX and undefined FILE*, AIX uses compat fgetpwent routines now
 *
 * Revision 3.9  1994/12/12  18:19:07  lindner
 * Fix aix hack
 *
 * Revision 3.8  1994/12/12  17:41:08  lindner
 * Hack around AIX
 *
 * Revision 3.7  1994/12/12  16:58:47  lindner
 * Use fgetpwent() instead of setpwfile()
 *
 * Revision 3.6  1994/12/10  06:12:13  lindner
 * Add support for a custom passwd file just for the gopher server
 *
 * Revision 3.5  1994/10/13  05:17:46  lindner
 * Compiler complaint fixes
 *
 * Revision 3.4  1994/07/22  22:27:20  lindner
 * NO_AUTHENTICATION mods
 *
 * Revision 3.3  1994/07/21  15:45:34  lindner
 * Fix for systems without unistd.h
 *
 * Revision 3.2  1994/07/03  21:18:11  lindner
 * Add initgroup() call
 *
 * Revision 3.1  1994/06/29  05:34:19  lindner
 * New authentication routines
 *
 *
 *********************************************************************/

#ifndef NO_AUTHENTICATION

#include "AUTH.h"
#include "compatible.h"

#include "Malloc.h"

#include "fileio.h"		/* For running scripts */
#include "String.h"		/* strchr and friends */

#include "Debug.h"
#include <stdio.h>

#ifndef VMS
#  include <pwd.h>
#endif

AUTH*
AUTHnew()
{
     AUTH* temp;

     temp = (AUTH*) malloc(sizeof(AUTH));
     temp->type = AUTH_UNINITED;
     temp->name = STRnew();
     temp->scriptname = STRnew();
     temp->authfcn    = NULL;
     temp->authfcnask = NULL;

     return(temp);
}


void
AUTHdestroy(auth)
  AUTH *auth;
{
     STRdestroy(auth->name);
     STRdestroy(auth->scriptname);
     free(auth);
}

void
AUTHinit(auth)
  AUTH *auth;
{
     STRinit(auth->name);
     STRinit(auth->scriptname);
     auth->authfcn    = NULL;
     auth->authfcnask = NULL;
     auth->type       = AUTH_UNINITED;
}

AUTH*
AUTHcpy(dest, orig)
  AUTH *dest, *orig;
{
     STRcpy(dest->name, orig->name);
     STRcpy(dest->scriptname, orig->scriptname);

     dest->authfcn    = orig->authfcn;
     dest->authfcnask = orig->authfcnask;
     dest->type       = orig->type;

     return(dest);
}

/*
 * A very generic ASK block
 */

char**
AUTHgenericAsk(directory, defuser, defpw)
  char *directory;
  char *defuser;
  char *defpw;
{
     static char **Askstorage = NULL;

     Debug("Trying to do the generic ask block for %s\n", directory);

     if (Askstorage == NULL) {
	  Askstorage = (char**)malloc(sizeof(char*)*5);
	  
	  Askstorage[0] = (char*) malloc(sizeof(char) * 80);
	  Askstorage[1] = (char*) malloc(sizeof(char) * 80);
	  Askstorage[2] = (char*) malloc(sizeof(char) * 80);
	  Askstorage[3] = NULL;
     }

     if (defuser == NULL)
	  defuser = "";
 
     if (defpw == NULL)
	  defpw = "";

/*     if (directory == NULL)
	  strcpy(Askstorage[0], "Note: This item is protected");
     else
	  sprintf(Askstorage[0], "Note: The item %s is protected", directory);
*/

     sprintf(Askstorage[1], "Ask: Your User Name:\t%s", defuser);
     sprintf(Askstorage[2], "AskP: Your Password:\t%s", defpw);

     return(Askstorage);
}

/*
 * Check to see if 'password' is the users password.
 */

static AUTHresult
AUTHunix_checkuser(pw, password)
  struct passwd *pw;
  char *password;
{
     char salt[3];
     char *encodedpw;

     strncpy(salt, pw->pw_passwd, 2);
     
     encodedpw = (char*) crypt(password, salt);

     if (strcmp(encodedpw, pw->pw_passwd) == 0) {
	  if (getuid() == 0) {
	       initgroups(pw->pw_name, pw->pw_gid);
	       setuid(pw->pw_uid);
	       setgid(pw->pw_gid);
	  }
	  return(AUTHRES_OK);
     }
     return(AUTHRES_BADPW);
}


/*
 * Use a password file in the etc/passwd in the gopher-data directory
 * to authenticate users.
 */

AUTHresult
AUTHunixfile(auth, username, password, hostname, hostip)
  AUTH *auth;
  char *username, *password, *hostname, *hostip;
{
#if !defined(VMS)
     FILE *ourpasswd;
     struct passwd *pw;

     Debug("Trying to authenticate %s\n", username);
     
     /*** This may or may not work.. (doesn't on sunos 4.1.3) ***/
     ourpasswd = fopen("etc/passwd", "r");

     if (ourpasswd == NULL) {
	  Debugmsg("Couldn't open etc/passwd\n");
	  return(AUTHRES_SYSERR);
     }

     if (fgetpwent(ourpasswd) == NULL) {
	  Debugmsg("fgetpwent failed...");
	  return(AUTHRES_SYSERR);
     }

     fseek(ourpasswd, 0L,SEEK_SET);

     /** Mimic getpwnam, sigh why isn't setpwfile() everywhere....**/

     while ((pw = fgetpwent(ourpasswd)) != NULL) {
	  if (strcmp(pw->pw_name, username) == 0)
	       return(AUTHunix_checkuser(pw, password));
     }
     return(AUTHRES_NOUSER);
#else
     return(AUTHRES_SYSERR);
#endif /*vms*/
}
     

/*
 * Do the unix style /etc/passwd authentication...
 */

AUTHresult
AUTHunix(auth, username, password, hostname, hostip)
  AUTH *auth;
  char *username, *password, *hostname, *hostip;
{
#ifndef VMS
     struct passwd *pw;

     pw = getpwnam(username);
     
     if (pw == NULL)
	  return(AUTHRES_NOUSER);

     return(AUTHunix_checkuser(pw, password));
#else
     return(AUTHRES_SYSERR)
#endif /*VMS*/
}


AUTHresult
AUTHvalidate(auth, username, password, hostname, hostip)
  AUTH *auth;
  char *username, *password, *hostname, *hostip;
{
     int    result;
     char   *envstr;


     switch (AUTHgetType(auth)) {
     case AUTH_SCRIPT:
	  /*
	   * Need to set environment variables here...
	   * note envstr must be malloced.
	   */
	  envstr = (char*) malloc(sizeof(char) * (16+strlen(username)));
	  sprintf(envstr, "GOPHER_USER=%s", username);
	  putenv(envstr);
	  
	  envstr = (char*) malloc(sizeof(char) * (16+strlen(password)));
	  sprintf(envstr, "GOPHER_PW=%s", password);
	  putenv(envstr);
	  
	  envstr = (char*) malloc(sizeof(char) * (16+strlen(hostname)));
	  sprintf(envstr, "GOPHER_HOST=%s", hostname);
	  putenv(envstr);
	  
	  envstr = (char*) malloc(sizeof(char) * (16+strlen(hostip)));
	  sprintf(envstr, "GOPHER_IP=%s", hostip);
	  putenv(envstr);
	  
	  
	  result = FIOsystem(AUTHgetScriptName(auth));
	  
	  switch (result) {
	  case 0:			
	       return(AUTHRES_OK);
	  case 1:
	       return(AUTHRES_BADPW);
	  case 2:
	       return(AUTHRES_EXPIRED);
	  case 3:
	       return(AUTHRES_NOUSER);
	       
	  default:
	       return(AUTHRES_BADPW);
	  }
	  break;

     case AUTH_INTERNAL:
	  return(auth->authfcn(auth, username, password, hostname, hostip));

     case AUTH_UNINITED:
	  return(AUTHRES_BADPW);

     }
     return(AUTHRES_OK);

}

/*************************************************************************
 * AUTH array routines (AUTHA)
 */

AUTHarray*
AUTHAnew(size)
  int size;
{
     AUTHarray *aa;
     AUTH      *auth;

     aa = DAnew((size),AUTHnew,AUTHinit,AUTHdestroy,AUTHcpy);

     auth = AUTHnew();
     if (auth == NULL)
	  return(NULL);
     
     /** Always supply some defaults... **/

     AUTHsetType(auth, AUTH_INTERNAL);
     AUTHsetName(auth, "unix");
     AUTHsetFcn(auth, AUTHunix);
     AUTHsetAskFcn(auth, AUTHgenericAsk);
     AUTHApush(aa, auth);


     AUTHsetType(auth, AUTH_INTERNAL);
     AUTHsetName(auth, "unixfile");
     AUTHsetFcn(auth, AUTHunixfile);
     AUTHsetAskFcn(auth, AUTHgenericAsk);
     AUTHApush(aa, auth);


     AUTHdestroy(auth);

     return(aa);
}


/*
 * Process a gopherd.conf style line and add it to the array
 * really only for scripts.
 */

boolean
AUTHAprocessLine(autharr, inputline)
  AUTHarray *autharr;
  char      *inputline;
{
     AUTH *auth;
     char *cp;

     auth = AUTHnew();

     if (auth == NULL)
	  return(FALSE);

     cp = strchr(inputline, ' ');
     *cp = '\0';
     cp++;

     AUTHsetType(auth, AUTH_SCRIPT);
     AUTHsetScriptName(auth, cp);
     AUTHsetName(auth,inputline);
     AUTHsetAskFcn(auth, AUTHgenericAsk);
     
     AUTHApush(autharr, auth);
     
     AUTHdestroy(auth);

     return(TRUE);
}

/*
 * Find the authentication method, and pass the information to the
 * routine that handles it.
 */

AUTHresult
AUTHAvalidate(autha, type, username, password, hostname, hostip)
  AUTHarray *autha;
  char *type;
  char *username, *password, *hostname, *hostip;
{
     int i;
     AUTH *auth;

     if (type == NULL)
	  return(AUTHRES_BADPW);

     for (i=0; i < AUTHAgetTop(autha); i++) {
	  auth = AUTHAgetEntry(autha, i);
	  if (strcasecmp(type, AUTHgetName(auth)) == 0) {
	       return(AUTHvalidate(auth,username,password,hostname,hostip));
	  }
     }

     /** Shouldn't get to here.. **/
     return(AUTHRES_BADPW);
}


/*
 * Find an AUTH item in the autha array
 */

AUTH*
AUTHAlocate(autha, type)
  AUTHarray *autha;
  char *type;
{
     int i;
     AUTH *auth;

     if (type == NULL)
	  return(NULL);

     for (i=0; i < AUTHAgetTop(autha); i++) {
	  auth = AUTHAgetEntry(autha, i);
	  if (strcasecmp(type, AUTHgetName(auth)) == 0) {
	       return(auth);
	  }
     }

     /** Shouldn't get to here.. **/
     return(NULL);
}


	       

/*********************************************************************
 * AUTHITEMS array
 */

#include "Regex.h"


AUTHITEMS *
AUTHITEMSnew(size)
  int size;
{
     AUTHITEMS *ai;

     ai = (AUTHITEMS*) malloc(sizeof(AUTHITEMS));
     
     if (ai != NULL) {
	  ai->regexps   = STAnew(size);
	  ai->authtypes = STAnew(size);
     }
     return(ai);
}

void
AUTHITEMSdestroy(ai)
  AUTHITEMS *ai;
{
     if (ai == NULL)
	  return;

     STAdestroy(ai->regexps);
     STAdestroy(ai->authtypes);
}

void
AUTHITEMSpush(ai, regexp, authtype)
  AUTHITEMS *ai;
  char *regexp;
  char *authtype;
{
     String *str;

     if (ai == NULL)
	  return;

     str = STRnew();
     if (str == NULL)
	  return;

     STRset(str, regexp);
     STApush(ai->regexps, str);

     STRset(str, authtype);
     STApush(ai->authtypes, str);
     
     STRdestroy(str);
}

/*
 * Find a match for the item in the regular expressions
 *
 * returns NULL if no match found..
 */

char *
AUTHITEMSfindType(ai, fname)
  AUTHITEMS *ai;
  char      *fname;
{
     int  i;
     char *rexp;

     if (ai == NULL)
	  return(NULL);

     for (i=0; i < STAgetTop(ai->regexps); i++) {
	  rexp = STAgetText(ai->regexps, i);

	  if (re_comp(rexp))
	       return(NULL);

	  if (re_exec(fname) == 1) {
	       /* We have a match */
	       return(STAgetText(ai->authtypes, i));
	  }
     }
     return(NULL);		/* No match found.. */
}


/*
 * Find the auth item required..
 */

AUTH*
AUTHITEMSfindAUTH(ai, aa, fname)
  AUTHITEMS *ai;
  AUTHarray *aa;
  char      *fname;
{
     int  i;
     char *rexp;

     if (ai == NULL)
	  return(NULL);

     for (i=0; i < STAgetTop(ai->regexps); i++) {
	  rexp = STAgetText(ai->regexps, i);
	  Debug("Trying %s\n", rexp);

	  if (re_comp(rexp))
	       return(NULL);

	  if (re_exec(fname) == 1) {
	       /* We have a match */
	       return(AUTHAlocate(aa, STAgetText(ai->authtypes, i)));

	  }
     }
     return(NULL);		/* No match found.. */
}



/*
 * Process a gopherd.conf style line and put it into the arrays.
 */

boolean
AUTHITEMSprocessLine(ai, inputline)
  AUTHITEMS *ai;
  char      *inputline;
{
     char *cp;

     cp = strchr(inputline, ' ');
     
     if (cp == NULL)
	  return(FALSE);
     
     *cp = '\0';

     AUTHITEMSpush(ai, cp+1, inputline);

     return(TRUE);
}

#endif
