/* auth.c

$Author: dm3e $ $Date: 1993/01/08 19:20:40 $ $Revision: 1.19 $$State: Exp $
   $Locker: dm3e $ */

/***********************************************************
        Copyright 1991 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#include "emtd.h"

#define LIFETIME (MAXKTCTICKETLIFETIME)

extern struct ktc_encryptionKey ukey;

static struct ktc_token work_token;
static struct ktc_token *work_tokenp = &work_token;
static long work_viceID;

char userbuf[16] = "USER=";
char instancebuf[128] = "INSTANCE=";
char homebuf[128] = "HOME=";
char shellbuf[128] = "SHELL=";
char pathbuf[128] = "PATH=:/usr/ucb:/bin:/usr/bin";
char *cleanenv[]=
{
    userbuf, homebuf, shellbuf, pathbuf, 0, 0
};
char *user = "root";
char *shell = "/bin/sh";
int fulllogin;
int fastlogin;

char krbbuf[128] = "KRBTKFILE=";
extern char *ktc_tkt_string ();

#ifdef SHELLHACK
#include <sys/stat.h>
int customshell = 0;
#endif

extern char **environ;
struct passwd *pwd;
char *crypt ();
char *getpass ();
char *getenv2 ();
char *getlogin ();
char *malloc ();


/**********************************************************
 * Parses a white-space- or comment-terminated string from
 * another string, moving the string pointer past the string on return.
 */
int
  getstr (cpp, spp)
char **cpp;		/* string pointer */
char **spp;		/* where to put string */
{
    char *cp;
    
    cp = *cpp;
    while (*cp == ' ' || *cp == '\t')
      cp++;
    if (*cp == '#' || *cp == '\n' || *cp == '\0')
      return -1;
    *spp = cp;
    while (*cp != '\0')
      {
	  if (*cp == ' ' || *cp == '\t')
	    {
		*cp++ = '\0';
		break;
	    }
	  if (*cp == '#' || *cp == '\n')
	    {
		*cp = '\0';
		break;
	    }
	  cp++;
      }
    *cpp = cp;
    return 0;
}

/***************************************************************************
 * [exported] emt_FindUser
 *
 * Description:
 *      Determines if a user is an admin in the given tree.
 *      Returns 0, if the user is not an admin of the tree.
 *      Returns non-zero, if the user is an admin of the tree.
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Thu May 28 15:15:51 1992 - created by David Allen Markley
 *
 ***************************************************************************/
emt_FindUser (tree, uname)
char *tree;
char *uname;
{
    int code;
    FILE *userlist;
    char buf[BUFSIZ], *cp, *user;
    
    sprintf (buf, "%s/admin-%s", EMTD_WORKING_DIRECTORY, tree);
    userlist = fopen (buf, "r");
    if (userlist != NULL)
      {
	  code = 0;
	  while ((cp = fgets (buf, BUFSIZ, userlist)) != NULL)
	    {
		if (getstr (&cp, &user))
		  continue;
		if (!strncmp (uname, user, strlen (uname)))
		  {
		      code = 1;
		      break;
		  }
	    }
	  fclose (userlist);
      }
    return code;
}

static void
  remove_cr (str, len)
char *str;
int len;
{
    register int i;
    
    for (i = 0; (*(str + i) != '\0') && (i < len); i++)
      if (*(str + i) == '\n')
	{
	    *(str + i) = '\0';
	    return;
	}
}

long
  CheckAuth ()
{
    struct timeval tp;
    struct ktc_principal lserver;
    struct ktc_principal lclient;
    char *keystr, *time, mytime[9];
    int i, local;
    register long code;
    /* set tokens to user */
    
    gettimeofday (&tp, NULL);
    if (tp.tv_sec + REAUTH_MIN_TIME > work_tokenp->endTime)
      {
	  struct ktc_encryptionKey key;
	  long keyId = 0;

	  Log ("Trying to reauth user.\n");
	  IF_RTN(emtd_conf_GetLatestKey(NULL, &keyId, &key));
	  IF_RTN(emt_GetUserTickets (EMTD_WORK_NAME, EMTD_WORK_INSTANCE, THISCELL, &key,
			      LIFETIME, work_tokenp, &work_viceID));
	  IF_RTN(emt_ReauthADM ());
      }
    strcpy (lserver.name, "afs");
    lserver.instance[0] = 0;
    IF_RTN (ka_ExpandCell (THISCELL, lserver.cell, &local));
    sprintf (lclient.name, "AFS ID %d", work_viceID);
    strcpy (lclient.instance, "");
    strcpy (lclient.cell, lserver.cell);
    code = ktc_SetToken (&lserver, work_tokenp, &lclient);
    if (code)
      {
	  Log ("Error setting tokens.\n");
	  return code;
      }
    return OK;
}

long
  Authenticate ()
{
    register long code;
    char *keystr;
    char ticket_file[32];
    struct ktc_principal lserver;
    struct ktc_principal lclient;
    struct ktc_encryptionKey key;
    long keyId = 0;
    int local;
    
    code = ka_Init (0);
    if (code)
      {
	  Log ("Couldn't initialize auth package\n");
	  exit (code);
      }
    setpag ();
#ifdef KERBEROS_ENV		/* ANDREW_CMU_EDU */
    ktc_newpag ();
#endif
#ifdef USE_MITKERBEROS
    IF_RTN(emt_GetKeyFromKeyFile (KEYFILE, EMTD_WORK_NAME, EMTD_WORK_INSTANCE, -1,
				  &key));
#endif				/* USE_MITKERBEROS */
    IF_RTN(emtd_conf_GetLatestKey(NULL, &keyId, &key));
    if (code = emt_SwitchUser (EMTD_WORK_NAME, EMTD_WORK_INSTANCE,
			       THISCELL, EMTD_WORK_ID, &key))
      {
	  Log ("emtd: error in su \(%ld\).\n", code);
	  exit (code);
      }
#ifdef USE_MITKERBEROS
    Log ("Ticket file: %s\n", ktc_tkt_string ());
#endif				/* USE_MITKERBEROS */
    return CheckAuth ();
}

/***************************************************************************
 * [exported] emt_AuthLevel
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Sep 25 12:16:14 1991 - created by David Allen Markley
 *      Fri Oct 11 14:14:18 1991 - David Allen Markley (dm3e) -
 *           Added search for global users.
 *
 ***************************************************************************/
int
  emt_AuthLevel (call, tree, collection)
struct rx_call *call;
char *tree;
char *collection;
{
    register long code;
    char name[MAXKTCNAMELEN];
    char instance[MAXKTCNAMELEN];
    char cell[MAXKTCNAMELEN];
    char identity[MAXBUFSIZE];
    long authorization;
    register int i, j;
    namelist list, names;
    idlist ids;
    char *group;
    long sec = 0;
    
    authorization = EMTAUTH_NONENTITY;
    if (code = emtd_rpc_GetServerInfo
	(call, (struct rxkad_level *) 0, (unsigned long *) 0, name, instance,
	 cell, (long *) 0))
      return authorization;
    code = pr_Initialize (sec, AFSCONF_CLIENTNAME, THISCELL);
    group = gen_group_name (collection);
    if (NULL != name[0])
      {
	  strcpy (identity, name);
	  if (NULL != instance[0])
	    {
		strcat (identity, ".");
		strcat (identity, instance);
	    }
	  strcat (identity, "@");
	  if (NULL != cell[0])
	    {
		strcat (identity, cell);
	    }
	  else
	    {
		strcat (identity, EMTD_SERVER_CELL);
	    }
	  if (emt_FindUser (tree, identity))
	    {
		authorization |= EMTAUTH_SUPERUSER;
	    }
	  if (emt_FindUser ("global", identity))
	    {
		authorization |= EMTAUTH_GLOBALUSER;
	    }
	  if (NULL != cell[0] && strcasecmp (cell, EMTD_SERVER_CELL))
	    {
		return authorization;
	    }
	  names.namelist_val = (prname *) malloc (PR_MAXNAMELEN);
	  names.namelist_len = 1;
	  strncpy (names.namelist_val[0], group, PR_MAXNAMELEN);
	  bzero (&ids, sizeof (idlist));
	  code = pr_NameToId (&names, &ids);
	  for (i = 0; i < ids.idlist_len; i++)
	    {
		long id = ids.idlist_val[i];
		char *gname = names.namelist_val[i];
		if (id == ANONYMOUSID)
		  continue;		/* bad entry */
		list.namelist_val = 0;
		list.namelist_len = 0;
		code = pr_IDListMembers (ids.idlist_val[i], &list);
		if (code)
		  {
		      Log ("emtd: failed to get membership of %s (id: %d)",
			   gname, id);
		      continue;
		  }
		for (j = 0; j < list.namelist_len; j++)
		  {
		      if (!strncmp (name, list.namelist_val[j]))
			{
			    authorization |= EMTAUTH_MAINTAINER;
			}
		  }
		if (list.namelist_val)
		  free (list.namelist_val);
	    }
	  if (ids.idlist_val)
	    free (ids.idlist_val);
	  if (names.namelist_val)
	    free (names.namelist_val);
      }
    return authorization;
}

/***************************************************************************
 * [exported] emt_UserName
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Sep 25 12:16:01 1991 - created by David Allen Markley
 *
 ***************************************************************************/
char *
  emt_UserName (call, data)
struct rx_call *call;
struct emtd_in *data;
{
    register long code;
    char name[MAXKTCNAMELEN];
    char instance[MAXKTCNAMELEN];
    
    if (code =
	emtd_rpc_GetServerInfo (call, (struct rxkad_level *) 0,
				(unsigned long *) 0, name, instance,
				(char *) 0, (long *) 0))
      return NULL;
    return (char *) copy_string (name);
}

long
  emt_GetUserTickets (name, instance, realm, key, lifetime, token, viceID)
char *name;
char *instance;
char *realm;
struct ktc_encryptionKey *key;
Date lifetime;
struct ktc_token *token;
long *viceID;
{
    int remainingTime = 0;
    long code;
    struct ktc_principal server, client;
    int (*old) ();
    struct ktc_token afstoken;
    
    
    {
	extern int rx_socket;	/* Rx uses timers, save to be safe */
	if (rx_socket)
	  {
	      /* don't reset alarms, rx already running */
	      remainingTime = 0;
	  }
	else
	  remainingTime = alarm (0);
    }
    
    /* handle smoothly the case where no AFS system calls exists (yet) */
    old = (int (*)()) signal (SIGSYS, SIG_IGN);
    IF_RTN(ka_GetAuthToken (name, instance, realm, key, lifetime));
    IF_RTN(ka_GetServerToken ("afs", "", realm, lifetime, &afstoken, 1));
    bcopy (&afstoken, token, sizeof (struct ktc_token));
    if (ktc_OldPioctl ())
      {
	  int local;
	  char username[MAXKTCNAMELEN];
	  int len;
	  char *whoami = "UserAuthenticate: ptserver";
	  
	  strcpy (server.name, "afs");
	  strcpy (server.instance, "");
	  IF_RTN(ka_ExpandCell (realm, server.cell, &local));
	  code = pr_Initialize (0, AFSCONF_CLIENTNAME, server.cell);
	  if (code)
	    {
		com_err (whoami, code, "initializing ptserver in cell '%s'",
			 server.cell);
		return 0;
	    }
	  len = strlen (name);
	  if (instance[0])
	    len += strlen (instance) + 1;
	  if (len >= sizeof (username))
	    {
		Log ("user's name '%s'.'%s' would be too large\n",
		     name, instance);
		return 0;
	    }
	  strcpy (username, name);
	  if (instance[0])
	    {
		strcat (username, ".");
		strcat (username, instance);
	    }
	  code = pr_SNameToId (username, viceID);
	  if ((code == 0) && (*viceID == ANONYMOUSID))
	    code = PRNOENT;
	  if (code)
	    {
		com_err (whoami, code, "translating %s to id", username);
		return 0;
	    }
	  sprintf (client.name, "AFS ID %d", *viceID);
	  strcpy (client.instance, "");
	  strcpy (client.cell, realm);
	  code = ktc_SetToken (&server, &afstoken, &client);
	  if (code) {
	      Log ("Trouble setting tokens (%ld).\n", code);
	      return code;
	  }
      }
    if (remainingTime)
      {
	  rx_Finalize ();
	  alarm (remainingTime);	/* restore timer, if any */
      }
    return code;
}

long
  emt_SwitchUser (name, instance, cell, uid, key)
char *name, *instance, *cell;
int uid;
struct ktc_encryptionKey *key;
{
    char buf[1000];
    char ticket_file[32];
    FILE *fp;
    register char *p;
    struct ktc_token token;
    struct ktc_token *tokenp = &token;
    long viceID;
    
    if ((pwd = getpwuid (uid)) == NULL)
      {
	  Log ("Error finding pwd\n");
	  if ((pwd = getpwnam (name)) == NULL)
	    {
		Log ("Unknown login: %s\n", name);
		exit (errno);
	    }
      }
    endpwent ();
#ifdef KERBEROS_ENV
    (void) chown (ktc_tkt_string (), pwd->pw_uid, pwd->pw_gid);
#endif				/* KERBEROS_ENV */
    if (setgid (pwd->pw_gid) < 0)
      {
	  Log ("error in setgid.\n");
	  exit (errno);
      }
    if (initgroups (name, pwd->pw_gid))
      {
	  Log ("initgroups failed\n");
      }
    if (setuid (pwd->pw_uid) < 0)
      {
	  Log ("setuid failed\n");
	  exit (errno);
      }
    IF_RTN(emt_GetUserTickets (name, instance, cell, key, 32 * 60, &token, &viceID));
    if (pwd->pw_shell && *pwd->pw_shell) {
	shell = pwd->pw_shell;
    }
    if (fulllogin) {
	cleanenv[4] = getenv2 ("TERM");
	environ = cleanenv;
    }
    if (strcmp (name, "root"))
      IF_RTN(emt_setenv ("USER", pwd->pw_name, userbuf));
    IF_RTN(emt_setenv ("SHELL", shell, shellbuf));
    IF_RTN(emt_setenv ("HOME", pwd->pw_dir, homebuf));
#ifdef KERBEROS_ENV
    IF_RTN(emt_setenv("KRBTKFILE", ktc_tkt_string(), krbbuf));
#endif
#ifdef AFS
    if (instance[0] || getenv2 ("INSTANCE"))
      {
	  IF_RTN(emt_setenv ("INSTANCE", instance, instancebuf));
      }
#endif
    setpriority (PRIO_PROCESS, 0, 0);
    return 0;
}

long emt_setenv (ename, eval, buf)
char *ename, *eval, *buf;
{
    register char *cp, *dp;
    register char **ep = environ, **newep;
    int envsize;
    
    while (dp = *ep++)
      {
	  for (cp = ename; *cp == *dp && *cp; cp++, dp++)
	    continue;
	  if (*cp == 0 && (*dp == '=' || *dp == 0))
	    {
		strcat (buf, eval);
		*--ep = buf;
		return OK;
	    }
      }
    
    envsize = ep - environ;
    newep = (char **) malloc ((envsize + 1) * sizeof (char *));
    if (!newep)
      return OK;
    
    strcat (buf, eval);
    
    ep = environ;
    environ = newep;
    while (*ep)
      *newep++ = *ep++;
    *newep++ = buf;
    *newep = 0;
    return OK;
}

char *
  getenv2 (ename)
char *ename;
{
    register char *cp, *dp;
    register char **ep = environ;
    
    while (dp = *ep++)
      {
	  for (cp = ename; *cp == *dp && *cp; cp++, dp++)
	    continue;
	  if (*cp == 0 && (*dp == '=' || *dp == 0))
	    return (*--ep);
      }
    return ((char *) 0);
}


