/*
 * Notes:
 *
 * This is an AFS-aware login.
 *
 * Machine/os specific code is surrounded by #ifdef <system>
 *
 * The Q_SETUID and Q_DOWARN #ifdefs are for IBM's AOS which doesn't
 * define them if NFS is configured. Like that matters anymore
 */

/*
 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * login [ name ]
 * login -h hostname	(for telnetd, etc.)
 * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
 */

#if !defined(UT_NAMESIZE)
#define UT_NAMESIZE 8
#endif

#include <afsconfig.h>
#include <afs/param.h>

RCSID("$Header: /cvs/openafs/src/login/login.c,v 1.5.2.1 2002/11/14 21:40:39 zacheiss Exp $");

#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <afs/kautils.h>
#ifdef	AFS_SUN5_ENV
#include <sys/fs/ufs_quota.h>
#else
#if  !defined(AFS_HPUX_ENV) && !defined(AFS_AIX_ENV)
#if defined(AFS_SUN_ENV) || (defined(AFS_ATHENA_STDENV) && !defined(AFS_DEC_ENV)) || defined(AFS_OSF_ENV)
#include <ufs/quota.h>
#elif defined(AFS_FBSD_ENV)
#include <ufs/ufs/quota.h>
#else
#include <sys/quota.h>
#endif
#endif /* AIX */
#endif /* SUN5 */
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#ifdef	AFS_SUN5_ENV
#include <sys/termios.h>
#include <sys/ttychars.h>
/* hack to get it to build */
#define	CBRK	0377
#include <sys/ttydev.h>
#include <sys/ttold.h>
#include <sys/filio.h>
#endif
#ifdef AFS_FBSD_ENV
#define USE_OLD_TTY 1
#include <sys/ttydefaults.h>
#endif
#include <sys/ioctl.h>

#include <utmp.h>
#ifdef	AFS_SUN5_ENV
#include <utmpx.h>
#include <dirent.h>
#endif
#include <signal.h>
#if !defined(AIX) && !defined(AFS_HPUX_ENV) && !defined(AFS_AIX32_ENV) && !defined(AFS_FBSD_ENV)
#include <lastlog.h>
#endif
#include <errno.h>
#if defined(AIX)
#include <stand.h>
#undef B300
#undef B1200
#undef B9600
#include <sys/termio.h>
#include <sys/syslog.h>
#include <fcntl.h>
#else
#if !defined(AFS_HPUX_ENV) && !defined(AFS_SUN5_ENV)
#include <ttyent.h>
#endif
#if defined(AFS_HPUX_ENV)
#include <sgtty.h>
#include <sys/bsdtty.h>
#endif
#include <syslog.h>
#endif
#include <grp.h>
#include <pwd.h>
#include <setjmp.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define	TTYGRPNAME	"tty"		/* name of group to own ttys */

#define	MOTDFILE	"/etc/motd"

#if defined(AFS_SUN5_ENV) || defined(AFS_HPUX100_ENV)
#ifndef NO_MAIL
#ifdef V4FS
#define MAILDIR         "/var/mail/"
#else
#define MAILDIR         "/usr/mail/"
#endif /* V4FS */
static char mail[30];
#endif /* NO_MAIL */
#else /* AFS_SUN5_ENV || AFS_HPUX100_ENV */
#define MAILDIR         "/usr/spool/mail"
#endif /* AFS_SUN5_ENV || AFS_HPUX100_ENV */

#define	NOLOGIN		"/etc/nologin"
#define	HUSHLOGIN	".hushlogin"
#define	LASTLOG		"/usr/adm/lastlog"
#define	BSHELL		"/bin/sh"
#define SECURETTY_FILE	"/etc/securetty"

/*
 * This bounds the time given to login.  Not a define so it can
 * be patched on machines where it's too small.
 */
int	timeout = 300;

struct	passwd *pwd, *getpwnam();
int	failures;
char	term[64], *hostname, *username, *tty;
#ifdef	AFS_OSF_ENV
char	ousername[256];
#endif
struct	sgttyb sgttyb;

#ifdef	AFS_SUN5_ENV
#include <deflt.h>
extern char *defread(), *strdup();
static	void	defaults();
static	char	*Pndefault	= "/etc/default/login";
static	char	*Altshell	= NULL;
static	char	*Console	= NULL;
static	int	Idleweeks	= -1;
static	char	*Passreq	= NULL;
#define	DEFUMASK	022
static	mode_t	Umask		= DEFUMASK;
static	char 	*Def_tz		= NULL;
static	char 	*tmp_tz		= NULL;
static	char 	*Def_hertz	= NULL;
#define	SET_FSIZ	2			/* ulimit() command arg */
static	long	Def_ulimit	= 0;
#define	MAX_TIMEOUT	(15 * 60)
#define	DEF_TIMEOUT	5*60
static	unsigned Def_timeout	= DEF_TIMEOUT;
static	char	*Def_path	= NULL;
static	char	*Def_supath	= NULL;
#define	DEF_PATH	"/usr/bin:" 	/* same as PATH */
#define	DEF_SUPATH	"/usr/sbin:/usr/bin" /* same as ROOTPATH */
/*
 * Intervals to sleep after failed login
 */
#ifndef	SLEEPTIME
#	define	SLEEPTIME 4	/* sleeptime before login incorrect msg */
#endif
static int	Sleeptime = SLEEPTIME;

#include <shadow.h>
/* This is for Solaris's shadow pass struc */
static struct spwd *spwd;
char	*ttypmt = NULL;
char    *d_tz = NULL;
char	*d_dev= NULL;
static int authenticated_remote_user = 0;
static char rhost[64], rusername[10], lusername[10];
#endif
#ifdef	AFS_SUN_ENV
int	usererr = -1;
#endif

/* For setting of TZ environment on HPUX */
#ifdef AFS_HPUX_ENV
#define MAX_TZ_LEN 256
static char TZ[MAX_TZ_LEN];
#endif /* AFS_HPUX_ENV */

#ifdef AFS_KERBEROS_ENV
extern char *ktc_tkt_string();
#endif

#if !defined(AIX) && !defined(AFS_HPUX_ENV)
struct	tchars tc = {
/*	CINTR, CQUIT, CSTART, CSTOP, 000, 000 */
	CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
};

struct	ltchars ltc = {
	CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
};
#endif

#ifdef	AFS_OSF_ENV
#include <sia.h>
#include <paths.h>
SIAENTITY *entity=NULL;

#ifdef	AFS_OSF20_ENV
#include <sys/security.h>
#endif
#include <prot.h>
#endif

#ifndef	NOPAG
#define	NOPAG	0xffffffff
#endif

/* For avoiding making an osi_audit call when non-root */
osi_audit() 
{
return 0;
}

static void
timedout(x)
{
	fprintf(stderr, "Login timed out after %d seconds\n", timeout);
	exit(0);
}

char *cv2string(ttp, aval)
    register char *ttp;
    register unsigned long aval;
{
    register char *tp = ttp;
    register int  i;
    int any = 0;
    
    *(--tp) = 0;
    while (aval != 0) {
	i = aval % 10;
	*(--tp) = '0' + i;
	aval /= 10;
	any = 1;
    }
    if (!any)
	*(--tp) = '0';
    return tp;
}

#include "AFS_component_version_number.c"


#ifdef	AFS_SUN5_ENV
struct utmpx utmp;
#else
struct utmp utmp;
#endif

#if	defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV)
/* the following utmp field is used for user counting: */
#define how_to_count ut_exit.e_exit
char *ttyntail;
#define SUBLOGIN	"<!sublogin>"
int sublogin;
main(argc, argv, renvp)
char **renvp;
#else
main(argc, argv)
#endif
	int argc;
	char **argv;
{
	extern int errno, optind;
	extern char *optarg, **environ;
	struct group *gr, *getgrnam();
	register int ch;
	register char *p;
	int ask, fflag, hflag, rflag, pflag, cnt, locl, zero = 0, dflag=0, rlog=0;
	int quietlog, passwd_req, ioctlval;
	char *domain, *salt, *envinit[2], *ttyn=0, *pp;
	char tbuf[MAXPATHLEN + 2];
	char *ttyname(), *stypeof(), *crypt(), *getpass();
	extern off_t lseek();
	int local, code = 0;
	char *lcell;			/* local cellname */
	char  realm[MAXKTCREALMLEN];
	char buf1[256], **envp;
	afs_int32 j, pagval = NOPAG, ngroups, groups[NGROUPS_MAX];
	long password_expires = -1;
	extern	char*	AFSVersion;	/* generated version */
	int afsLoginFail=0;
	char *reason;

#if defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV)
        char retry_chdir = 0;
#endif /* AFS_SUN5_ENV || AFS_HPUX_ENV */

#ifdef	AFS_OSF_ENV
	char tname[sizeof(_PATH_TTY) + 10], *loginname=NULL;
	int oargc = argc;
	char **oargv = argv;

#else
#ifdef	AFS_SUN5_ENV

	defaults();					/* Set up Defaults and flags */
	if (Umask < 0 || ((mode_t) 0777) < Umask)	/* Set up default umask */
	    Umask = DEFUMASK;
	(void) umask(Umask);

	if (Def_timeout > MAX_TIMEOUT)			/* Set up default timeouts and delays */
	    Def_timeout = MAX_TIMEOUT;
	if (Sleeptime < 0 || Sleeptime > 5)
	    Sleeptime = SLEEPTIME;
	(void) alarm(Def_timeout);
#else
	(void)alarm((u_int)timeout);
#endif
 	(void)signal(SIGALRM, timedout);
#endif
	(void)signal(SIGQUIT, SIG_IGN);
	(void)signal(SIGINT, SIG_IGN);
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
	(void)setpriority(PRIO_PROCESS, 0, 0);
#endif
#ifdef Q_SETUID
	(void)quota(Q_SETUID, 0, 0, 0);
#endif /* Q_SETUID */
	
	/*
	 * -p is used by getty to tell login not to destroy the environment
 	 * -f is used to skip a second login authentication 
	 * -h is used by other servers to pass the name of the remote
	 *    host to login so that it may be placed in utmp and wtmp
	 */
	(void)gethostname(tbuf, sizeof(tbuf));
	domain = strchr(tbuf, '.');

#ifdef	AFS_HPUX_ENV
        /*
	 * Set flag to disable the pid check if you find that you are
	 * a subsystem login.
	 */
	sublogin = 0;
	if (*renvp && strcmp(*renvp,SUBLOGIN) == 0)
		sublogin = 1;
#endif
	fflag = hflag = pflag = rflag = 0;
	memset(&utmp, '\0', sizeof(utmp));
	passwd_req = 1;
#ifdef	AFS_SUN_ENV
#ifdef	AFS_SUN5_ENV
	while ((ch = getopt(argc, argv, "fh:prd")) != -1) {
#else
	while ((ch = getopt(argc, argv, "fh:pr")) != -1) {
#endif
#else
	while ((ch = getopt(argc, argv, "fh:p")) != EOF) {
#endif
/*printf("ch='%c'\n", ch);*/
		switch (ch) {
		case 'f':
			fflag = 1;
			break;
		case 'h':
#ifdef	AFS_SUN_ENV
			if (hflag || rflag) {
			    fprintf(stderr,"Only one of -r and -h allowed\n");
			    exit(1);
			}
#endif
			if (getuid()) {
				fprintf(stderr,
				    "login: -h for super-user only.\n");

				exit(1);
			}
			rlog = 1;
			hflag = 1;
			if (domain && (p = strchr(optarg, '.')) &&
			    strcasecmp(p, domain) == 0)
				*p = 0;
			hostname = optarg;
			break;
#ifdef	AFS_SUN_ENV
		case 'r':
			if (hflag || rflag) {
			    fprintf(stderr,"Only one of -r and -h allowed\n");
			    exit(1);
			}
			if (argv[2] == 0)
				exit(1);
#ifndef	AFS_SUN5_ENV
/*			fflag = 1;*/
#endif
			rflag = 1;
#ifndef	AFS_SUN5_ENV
			usererr = doremotelogin(argv[2]);
			strncpy(utmp.ut_host, argv[2], sizeof(utmp.ut_host));
			argc -= 2;
			argv += 2;
#endif
			continue;

			break;
#ifdef	AFS_SUN5_ENV
	        case 'd':	/* Ignode the '-d device' option for now!!! */
			dflag = 0;	/* XXX */
			d_dev = *++argv;
			argc--;
			break;
#endif
#endif
		case 'p':
			pflag = 1;
			break;
		case '?':
		default:
			fprintf(stderr, "usage: login [-fp] [username]\n");
			exit(1);
		}
	    }
	argc -= optind;
	argv += optind;
#ifdef	AFS_SUN5_ENV
	if (rflag)
	    strcpy(rhost, *argv++);
	if (hflag) {
	    if ( argv[0] && (*argv[0] != '-')) {
		if ((!strncmp(*argv, "TERM=", strlen("TERM=")))) {
		    strncpy(term, &((*argv)[strlen("TERM=")]), sizeof(term));
		}
	    }
	    ask = 1;
	} else
#endif
#if	defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)
	if (!rflag && *argv) {
#else
	if (*argv) {
#endif
#ifdef	AFS_OSF_ENV
		memset(ousername,'\0',sizeof(ousername));
		strcpy(ousername,*argv);
		loginname=ousername;
#else
		username = *argv;
#endif
		ask = 0;
	} else {
#ifdef	AFS_SUN5_ENV
	    ttypmt = getenv("TTYPROMPT");
	    if (ttypmt && (*ttypmt != '\0')) {
		extern char **getargs();
		/*
		 * if ttyprompt is set, there should be data on the stream already. 
		 */
		if ((envp = getargs(buf1)) != (char**)NULL) {
		    username =  *envp;
		}
	    }
	    ask = 0;
#else
#ifdef	AFS_SUN_ENV
	    if (rflag)
		ask = 0;
	    else
#endif
	    ask = 1;
#endif
	}
	ioctlval = 0;
#if !defined(AIX) && !defined(AFS_HPUX_ENV) && !defined(AFS_OSF_ENV)
	(void)ioctl(0, TIOCLSET, &ioctlval);

	(void)ioctl(0, TIOCNXCL, 0);

	(void)fcntl(0, F_SETFL, ioctlval);
	(void)ioctl(0, TIOCGETP, &sgttyb);
#ifdef	AFS_SUN_ENV
	(void)ioctl(0, FIONBIO, &zero);
	(void)ioctl(0, FIOASYNC, &zero);
	(void)ioctl(0, TIOCLGET, &locl);
	/*
	 * If talking to an rlogin process,
	 * propagate the terminal type and
	 * baud rate across the network.
	 */
#ifdef	AFS_SUN5_ENV
	if (rflag) {
	    doremotelogin();
#else
	if (rflag || rlog) {
		doremoteterm(term, &sgttyb);
#endif
	    }
	locl &= LPASS8;
	locl |= LCRTBS|LCTLECH|LDECCTQ;
	if (sgttyb.sg_ospeed >= B1200)
		locl |= LCRTERA|LCRTKIL;
	(void)ioctl(0, TIOCLSET, &locl);
#endif
	sgttyb.sg_erase = CERASE;
	sgttyb.sg_kill = CKILL;
	(void)ioctl(0, TIOCSLTC, &ltc);
	(void)ioctl(0, TIOCSETC, &tc);
#ifdef	AFS_SUN5_ENV
	if (!rflag)
#endif
	(void)ioctl(0, TIOCSETP, &sgttyb);
#endif /* AIX  */
	for (cnt = getdtablesize(); cnt > 2; cnt--)
		close(cnt);
#ifdef	AFS_SUN52_ENV
	if (dflag) {
	    ttyn = d_dev;
	} else
#endif
	if (ttyn == NULL) {
	ttyn = ttyname(0);
	if (ttyn == NULL || *ttyn == '\0') {
#ifdef	AFS_OSF_ENV
		(void)sprintf(tname, "%s??", _PATH_TTY);
		ttyn = tname;
#else
		ttyn = "/dev/tty??";
#endif

	}
        }
#ifdef	AFS_HPUX_ENV
	ttyntail = ttyn + sizeof("/dev/") - 1;
#endif
	if (tty = strrchr(ttyn, '/'))
		++tty;
	else
		tty = ttyn;

	openlog("login", LOG_ODELAY, LOG_AUTH);

#ifdef	AFS_OSF_ENV
#define _PATH_HUSHLOGIN ".hushlogin"
#define _PATH_MAILDIR   "/var/spool/mail"
#ifndef _PATH_DEFPATH
#define _PATH_DEFPATH   "/usr/bin:."
#endif
    {
	int cnt;
	int authret=0, doafs=0;
	char	shell[PATH_MAX+1] = _PATH_BSHELL, *pass, user[40], ouser[40];
	int (*sia_collect)()=sia_collect_trm;
	int getloginp(), uid;
	struct passwd *pwd;

/*printf("loginname=%s,  ttyn=%s, fflag=%d\n", loginname, ttyn, fflag);*/
	quietlog=0;
	if((sia_ses_init(&entity, oargc, oargv, hostname, loginname, ttyn, 1, NULL)) == SIASUCCESS) {
	    static int useGetPass = 0;
	    int cnt1 = 0, first=0;
	    char *prompt, *reason, pword[BUFSIZ];
	    /***** SIA SESSION AUTHENTICATION *****/
	    if(!fflag) {
		for(cnt=5; cnt; cnt--) {
		    doafs = 0;
		    if (first++ || !entity->name) {
			clean_entity_pwd(entity);
			if (getloginp(entity) < 0) {
			    printf("Illegal login name\n");
			    doafs = -1;
			    break;
			}
		    }
                    strcpy(user, entity->name);
		    prompt = "Password:";
		    if ((pwd = getpwnam(user))) {
			if (!strcmp(pwd->pw_passwd, "X")) {
			    doafs = 1;
			}
		    }
		    if (useGetPass) {
			pass = getpass(prompt);
		    } else {
			code = ka_UserReadPassword(prompt, pword, sizeof(pword), &reason);
                        pass = pword;
			if (code) {
			    if (code != KANULLPASSWORD) {
				if (++cnt1 > 3)
				    useGetPass = 1;
			    }
			    fprintf(stderr, "Unable to read password because %s\n", reason);
			    continue;
			}
		    }

		    if (!pwd) {
		       printf("Login incorrect\n"); fflush(stdout);
		       continue;
		    }

		    if (doafs || ((authret=sia_ses_authent(sia_collect,pass,entity)) == SIASUCCESS)) {
			if (doafs) {
			    uid = pwd->pw_uid;
			    sia_make_entity_pwd(pwd, entity);
			} else {
			    uid = entity->pwd->pw_uid;
			    pwd = entity->pwd;		
			}
doafs:
			    authret = do_afs_auth(pwd, pass, doafs);
			    if (!authret) {
				strcpy(ouser, user);				
				printf("Login incorrect\n"); fflush(stdout);
				continue;
			    }

			printf("AFS (R) %s Login\n", AFSVersion);

			if (uid) {
			    groups[0] = groups[1] = 0;
			    ngroups = getgroups(NGROUPS, groups);
			    pagval = get_pag_from_groups(groups[0], groups[1]);
			}
			break;
		    } else if (authret & SIASTOP) {
			break;
		    }
		    else if ( authret == SIAFAIL ) {
							/* why did it fail ? */
			struct ttyent *ptr;
			setttyent();
			/* 
			** if the "ptys" command does not exist in the
			** /etc/securettys file, then root login's are
			** allowed only from the console
			*/
			if ( !pwd->pw_uid )		/* for root */
			    if (((ptr=getttynam("ptys"))==NULL) || 
				!( ptr->ty_status & TTY_SECURE))
			    {
				printf("root login refused on this terminal\n");
				continue;
			    }
		    }
		    if (doafs || (authret != SIASUCCESS)) {
			uid = pwd->pw_uid;
			sia_make_entity_pwd(pwd, entity);
			goto doafs;
		    }
		    printf("login incorrect\n");
		}
		if(cnt <= 0 || (doafs == -1)) {
		    sia_ses_release(&entity);
		    exit(1);
		}
	    }
	    /***** SIA SESSION ESTABLISHMENT *****/
	    if(sia_ses_estab(sia_collect,entity) == SIASUCCESS) {
		/*
		 * Display and update lastlog file entry.  -DAL003
		 */
		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
		if(!quietlog)
		    quietlog = !*entity->pwd->pw_passwd && !usershell(entity->pwd->pw_shell);
		if (!entity->tty)
		    dolastlog(quietlog);
		/* END DAL003 */
#ifdef AFS_KERBEROS_ENV
		(void)chown(ktc_tkt_string(), pwd->pw_uid, pwd->pw_gid);
#endif /* AFS_KERBEROS_ENV */
		/***** SIA LAUNCHING SESSION *****/
		if(sia_ses_launch(sia_collect,entity) == SIASUCCESS) {
		    ngroups = getgroups(NGROUPS, groups);
		    if ((pagval != NOPAG) && 
			(afs_get_pag_from_groups(groups[0], groups[1])) == NOPAG) {
			/* we will have to shift grouplist to make room for pag */
			if (ngroups + 2 > NGROUPS) {
			    perror("Too many groups");    	/* XXX */
			    exit (3);
			}
			for (j = ngroups - 1; j >= 0; j--) {
			    groups[j + 2] = groups[j];
			}
			ngroups += 2;
			get_groups_from_pag(pagval, &groups[0], &groups[1]);
                        seteuid(0);	/* Since the seteuid was set the user already */
			if (setgroups(ngroups, groups) == -1) {
			    perror("setgroups");
			    exit(3);
			}
                        seteuid(entity->pwd->pw_uid);	/* Set euid back */
		    }
		    /****** Nothing left to fail *******/
		    if(setreuid(geteuid(),geteuid()) < 0) {
			perror("setreuid()");
			exit(3);
		    }
		    /****** set up environment   *******/
		    /* destroy environment unless user has requested preservation */
		    envinit[0] = envinit[1] = 0;
		    if (environ) for (cnt = 0; environ[cnt] != NULL; cnt++)
			if (!strncmp(environ[cnt], "TERM=", 5)) {
			    envinit[0] = environ[cnt];
			    break;
			}
		    if (!pflag)
			environ = envinit;
		    (void)setenv("HOME", entity->pwd->pw_dir, 1);
		    if(entity->pwd->pw_shell && *entity->pwd->pw_shell)
			strncpy(shell, entity->pwd->pw_shell, sizeof shell);
		    (void)setenv("SHELL", shell, 1);
		    if (term[0] == '\0')
			(void)strncpy(term, stypeof(tty), sizeof(term));
		    (void)setenv("TERM", term, 0);
		    (void)setenv("USER", entity->pwd->pw_name, 1);
		    (void)setenv("LOGNAME", entity->pwd->pw_name, 1);
		    (void)setenv("PATH", _PATH_DEFPATH, 0);
#ifdef AFS_KERBEROS_ENV
		    (void)setenv("KRBTKFILE", ktc_tkt_string(), 0);
#endif /* AFS_KERBEROS_ENV */
		    if (!quietlog) {
			struct stat st;
				
			motd();
			(void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, entity->pwd->pw_name);
			if (stat(tbuf, &st) == 0 && st.st_size != 0)
			    (void)printf("You have %smail.\n", (st.st_mtime > st.st_atime) ?  "new " : "");
		    }
		    /******* Setup default signals **********/
		    (void)signal(SIGALRM, SIG_DFL);
		    (void)signal(SIGQUIT, SIG_DFL);
		    (void)signal(SIGINT, SIG_DFL);
		    (void)signal(SIGTSTP, SIG_IGN);
		    
		    tbuf[0] = '-';
		    (void)strcpy(tbuf + 1, (p = strrchr(shell, '/')) ? p + 1 : shell);
		    sia_ses_release(&entity);
		    execlp(shell, tbuf, 0);
		    (void)printf("login: no shell: %s.\n", strerror(errno));
		    exit(0);
		}
		/***** SIA session launch failure *****/
	    }
	    /***** SIA session establishment failure *****/
	}
	if (entity != NULL) {
	    sia_ses_release(&entity);
	}
	syslog(LOG_ERR, " LOGIN FAILURE ");
	exit(1);
    }
#else /* AFS_OSF_ENV */
#ifdef AFS_HPUX_ENV
        {   /* This block sets TZ environment on HPUX */
            /* First set TZ environment here, so that
               LOGIN FAILURE will get correct time stamp.
               For logins from console, -p flag is not set,
               the TZ environment set here will be cleared later.
               We're gonna set it once again later after it's cleared.
            */ 
            FILE *fp;
            char buf[MAX_TZ_LEN];
            char *env_p=NULL;
            int tz_incorrect=0;

            /* set TZ only if tz_incorrect
               (*env_p==0 or env_p==0 ) and there is a correct /etc/TIMEZONE
               otherwise, just let it be default (US eastern on HP)
            */
            env_p = getenv("TZ");
            if (!env_p)
                tz_incorrect = 1;
            else if (!(*env_p)) 
                tz_incorrect = 1;
            
            if (tz_incorrect) {
                /* HP UX 10.0 or later has different file format */
#ifdef AFS_HPUX100_ENV
                /* /etc/TIMEZONE file example (HP-UX 10 or later)
                   TZ=EST5EDT
                   export TZ
                */
                fp = fopen ("/etc/TIMEZONE","r");
                if (fp) {
                    if ( (fgets (buf, sizeof(buf), fp)) != NULL ) {
                        buf[strlen(buf)-1] = 0;
                        if (!strncmp (buf, "TZ=", 3)) {
                            strncpy (TZ, buf, MAX_TZ_LEN);
                            putenv (TZ);
                        }
                    }
                    fclose(fp);
                }
#else /* AFS_HPUX100_ENV */
                /* /etc/src.sh file example (HP-UX 9 or earlier)
                   SYSTEM_NAME=myname; export SYSTEM_NAME
                   TZ=EST5EDT; export TZ
                */
                fp = fopen ("/etc/src.sh","r");
                if (fp) {
                    while ( (fgets (buf, sizeof(buf), fp)) != NULL ) {
                        buf[strlen(buf)-1] = 0;
                        if (!strncmp (buf, "TZ=", 3)) {
                            char *p = strchr (buf, ';');
                            if (p) *p = 0;
                            strncpy (TZ, buf, MAX_TZ_LEN);
                            putenv (TZ);
                        }
                    }
                    fclose(fp);
                }
#endif /* AFS_HPUX100_ENV */
            }
            else {
                *TZ = 0;
            }
        }
#endif /* AFS_HPUX_ENV */
	for (cnt = 0;; ask = 1) {
		ioctlval = 0;
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
		(void)ioctl(0, TIOCSETD, &ioctlval);
#endif /* !AIX and !hpux */

		if (ask | !username) {
			fflag = 0;
			getloginname(1);
		}
		/*
		 * Note if trying multiple user names;
		 * log failures for previous user name,
		 * but don't bother logging one failure
		 * for nonexistent name (mistyped username).
		 */
		if (failures && strcmp(tbuf, username)) {
			if (failures > (pwd ? 0 : 1))
				badlogin(tbuf);
			failures = 0;
		}
		(void)strcpy(tbuf, username);
		salt = "xx";
#ifdef	AFS_SUN5_ENV
		if ((pwd = getpwnam(username)) && (spwd = getspnam(username))) {
		    initgroups(username, pwd->pw_gid);	/* XXX */
		    salt = spwd->sp_pwdp;
		}
#else /* if !SUN5 */
		if (pwd = getpwnam(username))
			salt = pwd->pw_passwd;
#endif /* else !SUN5 */

		/* if user not super-user, check for disabled logins */
		if (pwd == NULL || pwd->pw_uid) {
			checknologin();
		    }

		/*
		 * Disallow automatic login to root; if not invoked by
		 * root, disallow if the uid's differ.
		 */
		if (fflag && pwd) {
			uid_t uid = getuid();

			/*
			 *  allow f flag only if user running login is
			 *  same as target user or superuser
			 */
			fflag = (uid == 0) || (uid == pwd->pw_uid);
			if (fflag) passwd_req = 0;
/*			passwd_req = pwd->pw_uid == 0 || (uid && uid != pwd->pw_uid);*/
		}

		/*
		 * If no pre-authentication and a password exists
		 * for this user, prompt for one and verify it.
		 */
		if (!passwd_req || (pwd && !*pwd->pw_passwd)) {
			break;
		    }
#if	defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)
		if (!usererr) 
		    break;
#endif
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
		setpriority(PRIO_PROCESS, 0, -4);
#endif

		/*
		 * The basic model for logging in now, is that *if* there
		 * is a kerberos record for this individual user we will
		 * trust kerberos (provided the user really has an account
		 * locally.)  If there is no kerberos record (or the password
		 * were typed incorrectly.) we would attempt to authenticate
		 * against the local password file entry.  Naturally, if
		 * both fail we use existing failure code.
		 */
		if(pwd)
		  {
		    char pword[BUFSIZ];
		    int cnt1 =0;
		    static int useGetPass = 0;

		    if(useGetPass) {
			pp = getpass("Password:");
			memcpy(pword, pp, 8);
		    } else {
			code = ka_UserReadPassword("Password:",
							pword,
							sizeof(pword),
							&reason);
			
			if(code) {
			    if (code != KANULLPASSWORD) {
				if (++cnt1 > 5)
				    useGetPass = 1;
			    }
			    fprintf(stderr,
				    "Unable to read password because %s\n",
				    reason);
			    goto loginfailed;
			}
		    }
		    pp = pword;
		    if(ka_UserAuthenticateGeneral(
			   KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,
			   pwd->pw_name, /* kerberos name */
			   (char *)0, /* instance */
			   (char *)0, /* realm */
			    pp, /* password */
			    0, /* default lifetime */
			    &password_expires,
			    0, /* spare 2 */
			    &reason /* error string */
			    )) {

		        afsLoginFail = 1;
			pword[8] = '\0';
		    } else {
			if(strcmp(pwd->pw_passwd,"*"))
			    break; /* out of for loop */
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
			(void)ioctl(0, TIOCHPCL, (struct sgttyb *)NULL);
#endif /* AIX */
			goto loginfailed;
		      }
		} else {
		    pp = getpass("Password:");
		}

		p = crypt(pp, salt);
		(void) memset(pp, '\0', strlen(pp));
#ifdef	AFS_SUN5_ENV
		if (spwd && !strcmp(p, spwd->sp_pwdp))
#else
		if (pwd && !strcmp(p, pwd->pw_passwd))
#endif
		{
		   /* Only print this if local authentication is successful */
		   if (afsLoginFail) {
		      printf("Unable to authenticate to AFS because %s\n", reason);
		      printf("proceeding with local authentication...\n");
		   }
		   afsLoginFail = 0;
		   break;
		}
		afsLoginFail = 0;

	      loginfailed:
		printf("Login incorrect\n");
		failures++;
		/* we allow 10 tries, but after 3 we start backing off */
		if (++cnt > 3) {
			if (cnt >= 6) {
				badlogin(username);
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
				(void)ioctl(0, TIOCHPCL, (struct sgttyb *)NULL);
#endif /* AIX */
				(void)close(0);
				(void)close(1);
				(void)close(2);
				sleep(10);
				exit(1);
			}
			sleep((u_int)((cnt - 3) * 5));
		}
	    }
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
	setpriority(PRIO_PROCESS, 0, 0);
#endif /* AIX */
#ifdef	AFS_SUN5_ENV
	/*  Exits if root login not on system console. */
	if (pwd->pw_uid == 0) {
	    if ((Console != NULL) && (strcmp(ttyn, Console) != 0)) {
		(void) printf("Not on system console\n");
		exit(10);
	    }
	    if (Def_supath != NULL)
		Def_path = Def_supath;
	    else
		Def_path = DEF_SUPATH;
	}
#endif /* AFS_SUN5_ENV */

	printf("AFS (R) %s Login\n", AFSVersion); /* generated version */

	/* committed to login -- turn off timeout */
 	(void)alarm((u_int)0);

	/*
	 * If valid so far and root is logging in, see if root logins on
	 * this terminal are permitted.
	 */
#if !defined(AIX) && !defined(AFS_SUN5_ENV)
#ifdef	AFS_HPUX_ENV
	 /* This is the /etc/securetty feature.  We wanted to 1st prompt for a
	 * password even if we know that this is not a securetty because
	 * we don't want to give any clue if the potential intruder guesses
	 * the correct password.
	 */
	if ((pwd->pw_uid == 0) && !rootterm(ttyn+sizeof("/dev/")-1)) {
#else
	if (pwd->pw_uid == 0 && !rootterm(tty)) {
#endif
		if (hostname)
			syslog(LOG_NOTICE, "ROOT LOGIN REFUSED FROM %s",
			    hostname);
		else
			syslog(LOG_NOTICE, "ROOT LOGIN REFUSED ON %s", tty);
		printf("Login incorrect\n");

		/*
		Reset tty line.
		*/

		(void)chmod(ttyn,0666);

		sleepexit(1);
	}
#endif /* !AIX & !SUN5 */
#ifdef Q_SETUID
	if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
		switch(errno) {
		case EUSERS:
			fprintf(stderr,
		"Too many users logged on already.\nTry again later.\n");
			break;
		case EPROCLIM:
			fprintf(stderr,
			    "You have too many processes running.\n");
			break;
		default:
			perror("quota (Q_SETUID)");
		}

		/*
		Reset tty line.
		*/

		(void)chmod(ttyn,0666);

		sleepexit(0);
	}
#endif /* Q_SETUID */

        /* For Solaris and HP, we might want to chdir later for remotely 
           mounted home directory because home direcotry may be
           protected now
        */
	if (chdir(pwd->pw_dir) < 0) {
#if !defined(AFS_SUN5_ENV) && !defined(AFS_HPUX_ENV)
		printf("No directory %s!\n", pwd->pw_dir);
#endif /* !defined(AFS_SUN5_ENV) && !defined(AFS_HPUX_ENV) */
		if (chdir("/"))
		{

			/*
			Reset tty line.
			*/

			(void)chmod(ttyn,0666);

			exit(0);
		}

#if !defined(AFS_SUN5_ENV) && !defined(AFS_HPUX_ENV)
		pwd->pw_dir = "/";
		printf("Logging in with home = \"/\".\n");
#else /* !defined(AFS_SUN5_ENV) && !defined(AFS_HPUX_ENV) */
                retry_chdir = 1;
#endif /* !defined(AFS_SUN5_ENV) && !defined(AFS_HPUX_ENV) */
	}

	/* nothing else left to fail -- really log in */
	{
#ifdef	AFS_SUN5_ENV
		long tlen;
		struct utmpx *utx, *getutxent(), *pututxline();
#endif
#if defined(AFS_HPUX_ENV)
		register struct utmp *u = (struct utmp *) 0;
		extern pid_t getpid();
#endif

#ifdef	AFS_SUN5_ENV
		(void)time(&utmp.ut_tv.tv_sec);
#else
		(void)time(&utmp.ut_time);
#endif
		strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
#if !defined(AIX) /*&& !defined(AFS_SUN5_ENV)*/
		if (hostname)
			strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
#endif
		strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
#if defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
		utmp.ut_type = USER_PROCESS;
		if (!strncmp(ttyn, "/dev/", strlen("/dev/"))) {
		  strncpy(utmp.ut_line, &(ttyn[strlen("/dev/")]), sizeof(utmp.ut_line));
		}

		utmp.ut_pid = getpid();
		if ((!strncmp(tty, "tty", strlen("tty"))) ||
		    (!strncmp(tty, "pty", strlen("pty"))) ) {
		  strncpy(utmp.ut_id, &(tty[strlen("tty")]), sizeof(utmp.ut_id));
		}
#endif /* AFS_HPUX_ENV */
#ifdef	AFS_HPUX_ENV
		/*
		 * Find the entry for this pid (or line if we are a sublogin)
		 * in the utmp file.
		 */
		while ((u = getutent()) != NULL) {
		    if ( (u->ut_type == INIT_PROCESS ||
			  u->ut_type == LOGIN_PROCESS ||
			  u->ut_type == USER_PROCESS)
			&& ( (sublogin &&
			      strncmp(u->ut_line,ttyntail,sizeof(u->ut_line))==0) ||
			    u->ut_pid == utmp.ut_pid) ) {

	    		/*
			 * Copy in the name of the tty minus the
			 * "/dev/", the id, and set the type of entry
			 * to USER_PROCESS.
			 */
			(void) strncpy(utmp.ut_line, ttyntail, sizeof(utmp.ut_line));
			utmp.ut_id[0] = u->ut_id[0];
			utmp.ut_id[1] = u->ut_id[1];
			utmp.ut_id[2] = u->ut_id[2];
			utmp.ut_id[3] = u->ut_id[3];
			utmp.ut_type = USER_PROCESS;
			utmp.how_to_count = add_count();

			/*
			 * Copy remote host information
			 */
			strncpy(utmp.ut_host, u->ut_host, sizeof(utmp.ut_host));
			utmp.ut_addr = u->ut_addr;

			/* Return the new updated utmp file entry. */
			pututline(&utmp);
			break;
		}
	}
	endutent();		/* Close utmp file */
#endif
#ifdef	AFS_SUN5_ENV
		utmp.ut_syslen = (tlen = strlen(utmp.ut_host)) ? min(tlen+1, sizeof(utmp.ut_host)) : 0;
		strncpy(utmp.ut_user, username, sizeof(utmp.ut_user));
		while ((utx = getutxent()) != NULL) {
		    if ((utx->ut_type == INIT_PROCESS || utx->ut_type == LOGIN_PROCESS ||
			 utx->ut_type == USER_PROCESS) && (utx->ut_pid == utmp.ut_pid)) {
			strncpy(utmp.ut_line,(ttyn+sizeof("/dev/")-1), sizeof(utmp.ut_line));
			utmp.ut_id[0] = utx->ut_id[0];
			utmp.ut_id[1] = utx->ut_id[1];
			utmp.ut_id[2] = utx->ut_id[2];
			utmp.ut_id[3] = utx->ut_id[3];
			utmp.ut_type = USER_PROCESS;
			pututxline(&utmp);
			break;
		    }
		}
		endutxent();		/* Close utmp file */
		if (!utx) {
		    printf("No utmpx entry.  You must exec 'login' from the lowest level 'sh'\n");
		    exit(1);
		} else 
		    updwtmpx(WTMPX_FILE, &utmp);
#else
		login(&utmp);
#endif
	}

	quietlog = access(HUSHLOGIN, F_OK) == 0;
	dolastlog(quietlog);

#if !defined(AIX) && !defined(AFS_HPUX_ENV)
#ifdef	AFS_SUN_ENV
	if (!hflag && !rflag) {					/* XXX */
#else
	if (!hflag) {					/* XXX */
#endif
		static struct winsize win = { 0, 0, 0, 0 };

		(void)ioctl(0, TIOCSWINSZ, &win);
	}
#endif /* !AIX and !hpux */

#ifdef	AFS_SUN5_ENV
	/*
	 * Set the user's ulimit
	 */
	if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
	    (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);

#endif
#if	defined(AFS_SUN_ENV)
	/*
	 * Set owner/group/permissions of framebuffer devices
	 */
	(void) set_fb_attrs(ttyn, pwd->pw_uid, pwd->pw_gid);
#endif

	(void)chown(ttyn, pwd->pw_uid,
	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);


	(void)chmod(ttyn, 0620);
	(void)setgid(pwd->pw_gid);

#ifdef	AFS_HPUX_ENV
	if ((pwd->pw_gid < 0) || (pwd->pw_gid > MAXUID)) {
	    printf("Bad group id.\n");
	    exit(1);
	}
	if ((pwd->pw_uid < 0) || (pwd->pw_uid > MAXUID)) {
	    printf("Bad user id.\n");
	    exit(1);
	}
#endif

#ifdef AFS_KERBEROS_ENV
	(void)chown(ktc_tkt_string(), pwd->pw_uid, pwd->pw_gid);

#endif

	groups[0] = groups[1] = 0;
	ngroups = getgroups(NGROUPS_MAX, groups);
	pagval = get_pag_from_groups(groups[0], groups[1]);
	initgroups(username, pwd->pw_gid);
	ngroups = getgroups(NGROUPS_MAX, groups);
	if ((pagval != NOPAG) && 
	    (afs_get_pag_from_groups(groups[0], groups[1])) == NOPAG) {
            /* we will have to shift grouplist to make room for pag */
	    if (ngroups + 2 > NGROUPS_MAX) 
		return E2BIG;    	/* XXX */
	    for (j = ngroups - 1; j >= 0; j--) {
		groups[j + 2] = groups[j];
	    }
	    ngroups += 2;
	    get_groups_from_pag(pagval, &groups[0], &groups[1]);
	    if (setgroups(ngroups, groups) == -1) {
		perror("setgroups");
		return -1;
	    }
	}

#ifdef Q_DOWARN
	quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
#endif /* Q_DOWARN */

	/* destroy environment unless user has requested preservation */
	if (!pflag) {
	    envinit[0] = (char *)0;
	    envinit[1] = (char *)0;
	    environ = envinit;
	}
	(void)setuid(pwd->pw_uid);

#if defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV)
        /* This is the time to retry chdir for Solaris and HP
        */
        if (retry_chdir && chdir(pwd->pw_dir)<0) {
            printf ("No directory %s!\n", pwd->pw_dir);
            if (chdir("/")<0) {
                /* reset tty line and exit */
                (void)chmod(ttyn, 0666);
                exit(0);
            }
            pwd->pw_dir = "/";
            printf ("Logging with home = \"/\".\n");
        }
#endif /* AFS_SUN5_ENV || AFS_HPUX_ENV */

	if (*pwd->pw_shell == '\0')
		pwd->pw_shell = BSHELL;
#if !defined(AIX) && !defined(AFS_HPUX_ENV) 
	/* turn on new line discipline for the csh */
	else if (!strcmp(pwd->pw_shell, "/bin/csh")) {
		ioctlval = NTTYDISC;
		(void)ioctl(0, TIOCSETD, &ioctlval);
	}
#endif /* AIX */
#ifdef	AFS_SUN5_ENV
	 else {
	    if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
		(void)setenv("SHELL", pwd->pw_shell, 1);
	    }
	}
	if (Def_tz != NULL) {
	    tmp_tz = Def_tz;
	}
	Def_tz = getenv("TZ");
	if (Def_tz) {
	    setenv("TZ", Def_tz, 1);
	} else {
	    if (tmp_tz) {
		Def_tz = tmp_tz;
		setenv("TZ", Def_tz, 1);
	    }
	}
	if (Def_hertz == NULL)
	    (void)setenv("HZ", HZ, 1);
	else
	    (void)setenv("HZ", Def_hertz, 1);

#endif

#ifdef AFS_HPUX_ENV
        /* if pflag is not set
           the TZ environment was cleared moments ago */
        if (*TZ && !pflag) {
            setenv ("TZ", &TZ[3], 1); 
        }
#endif /* AFS_HPUX_ENV */

	(void)setenv("HOME", pwd->pw_dir, 1);
	(void)setenv("SHELL", pwd->pw_shell, 1);
	if (term[0] == '\0')
		strncpy(term, stypeof(tty), sizeof(term));
	(void)setenv("TERM", term, 0);
	(void)setenv("USER", pwd->pw_name, 1);
	(void)setenv("LOGNAME", pwd->pw_name, 1);
#ifdef	AFS_SUN5_ENV
	if (Def_path == NULL)
	    (void)setenv("PATH", DEF_PATH, 0);
	else
	    (void)setenv("PATH", Def_path, 0);
#else
	(void)setenv("PATH", "/usr/ucb:/bin:/usr/bin:", 0);
#endif

#if     defined(AFS_SUN5_ENV) || defined(AFS_HPUX100_ENV)
#ifndef NO_MAIL
        /* Sol, HP: set MAIL only if NO_MAIL is not set */
        sprintf (mail, "%s%s", MAILDIR, pwd->pw_name);
        (void)setenv("MAIL", mail, 0);
#endif /* NO_MAIL */
#endif /* AFS_SUN5_ENV || AFS_HPUX100_ENV */

#ifdef AFS_KERBEROS_ENV
	(void)setenv("KRBTKFILE", ktc_tkt_string(), 0);
#endif

	if (password_expires >= 0) {
	    char sbuffer[100];
	  (void)setenv("PASSWORD_EXPIRES", cv2string(&sbuffer[100], password_expires), 1);
	}

	if (tty[sizeof("tty")-1] == 'd')
		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
	if (pwd->pw_uid == 0)
		if (hostname)
			syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s",
			    tty, hostname);
		else
			syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty);

	if (!quietlog) {
		struct stat st;

#if	! defined(AFS_SUN5_ENV) && (! defined(AFS_HPUX_ENV))
		/* In Solaris the shell displays motd */
		/* On hp700s /etc/profile also does it so it's redundant here too */
		motd();
                /* Don't check email either, should be done in shell */
		(void)sprintf(tbuf, "%s/%s", MAILDIR, pwd->pw_name);
		if (stat(tbuf, &st) == 0 && st.st_size != 0)
			printf("You have %smail.\n",
			    (st.st_mtime > st.st_atime) ? "new " : "");
#endif
	}

	(void)signal(SIGALRM, SIG_DFL);
	(void)signal(SIGQUIT, SIG_DFL);
	(void)signal(SIGINT, SIG_DFL);
#if !defined(AIX) && !defined(AFS_HPUX_ENV)
	(void)signal(SIGTSTP, SIG_IGN);
#endif /* AIX */

	tbuf[0] = '-';
	strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
	    p + 1 : pwd->pw_shell);
	execlp(pwd->pw_shell, tbuf, 0);
	fprintf(stderr, "login: no shell: ");
	perror(pwd->pw_shell);
	exit(0);
#endif /* else AFS_OSF_ENV */
}

getloginname(prompt)
int prompt;
{
	register int ch;
	register char *p;
	static char nbuf[UT_NAMESIZE + 1];

	for (;;) {
		if (prompt) {
		    printf("login: ");
		    prompt = 0;
		}
		for (p = nbuf; (ch = getchar()) != '\n'; ) {
			if (ch == EOF) {
				badlogin(username);
				exit(0);
			}
			if (p < nbuf + UT_NAMESIZE)
				*p++ = (char) ch;
		}
		if (p > nbuf) {
			if (nbuf[0] == '-')
				fprintf(stderr,
				    "login names may not start with '-'.\n");
			else {
				*p = '\0';
				username = nbuf;
				break;
			}
		    } else prompt = 1;
	}
}


#ifdef	AFS_HPUX_ENV
rootterm(tty)
char *tty;
{
    register FILE *fd;
    char buf[100];

    /*
     * check to see if /etc/securetty exists, if it does then scan
     * file to see if passwd tty is in the file.  Return 1 if it is, 0 if not.
     */
    if ((fd = fopen(SECURETTY_FILE, "r")) == NULL)
	return(1);
    while (fgets(buf, sizeof buf, fd) != NULL) {
	buf[strlen(buf)-1] = '\0';
	if (strcmp(tty, buf) == 0) {
	    fclose(fd);
	    return(1);
	}
    }
    fclose(fd);
    return(0);
}
#endif
#if !defined(AIX) && !defined(AFS_HPUX_ENV) && !defined(AFS_SUN5_ENV)
rootterm(ttyn)
	char *ttyn;
{
	struct ttyent *t;

	return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
}
#endif 

jmp_buf motdinterrupt;

motd()
{
	register int fd, nchars;
	void (*oldint)();
	void sigint();
	char tbuf[8192];

	if ((fd = open(MOTDFILE, O_RDONLY, 0)) < 0)
		return;
	oldint = signal(SIGINT, sigint);
	if (setjmp(motdinterrupt) == 0)
		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
			(void)write(fileno(stdout), tbuf, nchars);
	(void)signal(SIGINT, oldint);
	(void)close(fd);
}

void
sigint()
{
	longjmp(motdinterrupt, 1);
}

checknologin()
{
	register int fd, nchars;
	char tbuf[8192];

	if ((fd = open(NOLOGIN, O_RDONLY, 0)) >= 0) {
		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
			(void)write(fileno(stdout), tbuf, nchars);
		sleepexit(0);
	}
}

#if defined(AIX) || defined(AFS_HPUX_ENV) || defined(AFS_AIX32_ENV)
dolastlog(quiet)
	int quiet;
{
  return 0;
}
#else
#ifdef	AFS_OSF_ENV
dolastlog(quiet)
    int quiet;
{
#define _PATH_LASTLOG   "/var/adm/lastlog"
    struct lastlog ll;
    int fd;
    struct passwd *pwd;

    pwd = entity->pwd;
    if ((fd = open(_PATH_LASTLOG, (O_RDWR|O_CREAT), 0666)) < 0)
	fprintf(stderr, "Can't open %s", _PATH_LASTLOG);
    else {
	(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
	if (!quiet) {
	    if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && ll.ll_time != 0) {
		(void)printf("Last login: %.*s ", 24-5, (char *)ctime(&ll.ll_time));
		if (*ll.ll_host != '\0')
		    (void)printf("from %.*s\n", sizeof(ll.ll_host), ll.ll_host);
		else
		    (void)printf("on %.*s\n", sizeof(ll.ll_line), ll.ll_line);
	    }
	}
	(void)close(fd);
    }
}
#else
dolastlog(quiet)
	int quiet;
{
	struct lastlog ll;
	int fd;

	if ((fd = open(LASTLOG, O_RDWR, 0)) >= 0) {
		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
		if (!quiet) {
			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
			    ll.ll_time != 0) {
				printf("Last login: %.*s ",
				    24-5, (char *)ctime(&ll.ll_time));
				if (*ll.ll_host != '\0')
					printf("from %.*s\n",
					    sizeof(ll.ll_host), ll.ll_host);
				else
					printf("on %.*s\n",
					    sizeof(ll.ll_line), ll.ll_line);
			}
			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
		}
		memset(&ll, '\0', sizeof(ll));
		(void)time(&ll.ll_time);
		strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
		if (hostname)
			strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
		(void)write(fd, (char *)&ll, sizeof(ll));
		(void)close(fd);
	}
}
#endif
#endif /* AIX */

badlogin(name)
	char *name;
{
	if (failures == 0)
		return;
	if (hostname)
		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s",
		    failures, failures > 1 ? "S" : "", hostname, name);
	else
		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s",
		    failures, failures > 1 ? "S" : "", tty, name);
}

#undef	UNKNOWN
#ifdef	AFS_SUN5_ENV
#define	UNKNOWN	"sun"
#else
#define	UNKNOWN	"su"
#endif

#if defined(AIX) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
char *
stypeof(ttyid)
	char *ttyid;
{
	return(UNKNOWN);
}
#else
char *
stypeof(ttyid)
	char *ttyid;
{
	struct ttyent *t;

	return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
}
#endif

getstr(buf, cnt, err)
	char *buf, *err;
	int cnt;
{
	char ch;

	do {
		if (read(0, &ch, sizeof(ch)) != sizeof(ch))
			exit(1);
		if (--cnt < 0) {
			fprintf(stderr, "%s too long\r\n", err);
			sleepexit(1);
		}
		*buf++ = ch;
	} while (ch);
}

sleepexit(eval)
	int eval;
{
	sleep((u_int)5);
	exit(eval);
}

    
int get_pag_from_groups(g0, g1)
afs_uint32 g0, g1;
{
	afs_uint32 h, l, result;

	g0 -= 0x3f00;
	g1 -= 0x3f00;
	if (g0 < 0xc000 && g1 < 0xc000) {
		l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
		h = (g0 >> 14);
		h = (g1 >> 14) + h + h + h;
		result =  ((h << 28) | l);
		/* Additional testing */	
		if (((result >> 24) & 0xff) == 'A')
			return result;
		else
			return NOPAG;
	}
	return NOPAG;
}


get_groups_from_pag(pag, g0p, g1p)
afs_uint32 pag;
afs_uint32 *g0p, *g1p;
{
	unsigned short g0, g1;

	pag &= 0x7fffffff;
	g0 = 0x3fff & (pag >> 14);
	g1 = 0x3fff & pag;
	g0 |= ((pag >> 28) / 3) << 14;
	g1 |= ((pag >> 28) % 3) << 14;
	*g0p = g0 + 0x3f00;
	*g1p = g1 + 0x3f00;
}


#if	defined(AFS_SUN_ENV)
/*
 * set_fb_attrs -- change owner/group/permissions of framebuffers
 *		   listed in /etc/fbtab.
 *
 * Note:
 * Exiting from set_fb_attrs upon error is not advisable
 * since it would disable logins on console devices.
 *
 * File format:
 * console mode device_name[:device_name ...]
 * # begins a comment and may appear anywhere on a line.
 *
 * Example:
 * /dev/console 0660 /dev/fb:/dev/cgtwo0:/dev/bwtwo0
 * /dev/console 0660 /dev/gpone0a:/dev/gpone0b
 *
 * Description:
 * The example above sets the owner/group/permissions of the listed
 * devices to uid/gid/0660 if ttyn is /dev/console
 */

#define	FIELD_DELIMS 	" \t\n"
#define	COLON 		":"
#define COLON_C         ':'
#define WILDCARD        "/*"
#define WILDCARD_LEN    2
#define MAX_LINELEN     256
#ifdef AFS_SUN5_ENV
#define FBTAB           "/etc/logindevperm"
#else
#define FBTAB           "/etc/fbtab"
#endif


set_fb_attrs(ttyn, uid, gid)
	char *ttyn;
	int uid;
	int gid;
{
	char line[MAX_LINELEN];
	char *console;
	char *mode_str;
	char *dev_list;
	char *device;
	char *ptr;
	int  mode;
	long strtol();
	FILE *fp;

	if ((fp = fopen(FBTAB, "r")) == NULL)
		return;
	while (fgets(line, MAX_LINELEN, fp)) {
		if (ptr = strchr(line, '#'))
			*ptr = '\0';	/* handle comments */
		if ((console = strtok(line, FIELD_DELIMS)) == NULL)
			continue;	/* ignore blank lines */
		if (strcmp(console, ttyn) != 0)
			continue;	/* ignore non-consoles */
		mode_str = strtok((char *)NULL, FIELD_DELIMS);
		if (mode_str == NULL) {
			(void) fprintf(stderr, "%s: invalid entry -- %s\n",
				FBTAB, line);
			continue;
		}
		/* convert string to octal value */
		mode = (int) strtol(mode_str, (char **)NULL, 8);
		if (mode < 0 || mode > 0777) {
			(void) fprintf(stderr, "%s: invalid mode -- %s\n",
				FBTAB, mode_str);
			continue;
		}
		dev_list = strtok((char *)NULL, FIELD_DELIMS);
		if (dev_list == NULL) {
			(void) fprintf(stderr, "%s: %s -- empty device list\n",
				FBTAB, console);
			continue;
		}
#ifdef	AFS_SUN5_ENV
		device = strtok(dev_list, COLON);
		while (device) {
		    ptr = strstr(device, WILDCARD);
		    if (ptr && 
			(strlen(device) - (ptr - &device[0])) == WILDCARD_LEN){
			/* The device was a (legally-specified) directory. */
			DIR           *dev_dir;
			struct dirent *dir_e;
			char dev_file[MAXPATHLEN];
			
			*ptr = '\0'; /* Remove the wildcard from the dir name. */
			if ((dev_dir = opendir(device)) == (DIR *) NULL) {
			    syslog(LOG_ERR, "bad device %s%s in %s, ignored",
				   device, WILDCARD, FBTAB);
			    continue;
			}
			
			/* Directory is open; alter its files. */
			/* Must link with /usr/lib/libc.a before 
			   /usr/ucblib/libucb.a or the d_name structs
			   miss the first two characters of the filename */
			while (dir_e = readdir(dev_dir)) {
			    if (strcmp(dir_e->d_name, "..") &&
				strcmp(dir_e->d_name, ".")) {
				strcpy(dev_file, device);
				strcat(dev_file, "/");
				strcat(dev_file, dir_e->d_name);
				(void) chown(dev_file, uid, gid);
				(void) chmod(dev_file, mode);
			    }
			}
			(void) closedir(dev_dir);
		    } else {
			/* 'device' was not a directory, so we can just do it. */
			(void) chown(device, uid, gid);
			(void) chmod(device, mode);
		    }
		    device = strtok((char *)NULL, COLON); /* Move thru list. */
#else
		device = strtok(dev_list, COLON);
		while (device) {
			(void) chown(device, uid, gid);
			(void) chmod(device, mode);
			device = strtok((char *)NULL, COLON);
#endif
		}
	}
	(void) fclose(fp);
}
#endif


#ifdef	AFS_SUN5_ENV
static int quotec()
{
	register int c, i, num;

	switch( c = getc(stdin)) {
	case 'n':
		c = '\n';
		break;
	case 'r':
		c = '\r';
		break;
	case 'v':
		c = '\013';
		break;
	case 'b':
		c = '\b';
		break;
	case 't':
		c = '\t';
		break;
	case 'f':
		c = '\f';
		break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
		for( num=0,i=0; i<3; i++ ) {
		    num = num * 8 + (c - '0');
		    if ((c = getc(stdin)) < '0' || c > '7')
			break;
		}
		ungetc(c,stdin);
		c = num & 0377;
		break;
	default:
		break;
	}
	return(c);
}

static char **getargs(inline)
    char *inline;
{
    static char envbuf[256];
    static char *args[63];
    register char *ptr,**answer;
    register int c;
    int state;
    extern int quotec();

    for (ptr= envbuf; ptr < &envbuf[sizeof(envbuf)];) *ptr++ = '\0';
    for (answer= args; answer < &args[63];) *answer++ = (char *)NULL;
    for (ptr= envbuf,answer= &args[0],state = 0; (c = getc(stdin)) != EOF;) {
	*(inline++) = c;
	switch (c) {
	  case '\n':
	    if (ptr == &envbuf[0]) return((char **)NULL);
	    else return(&args[0]);
	  case ' ':
	  case '\t':
	    if (state == 1) {
		*ptr++ = '\0';
		state = 0;
	    }
	    break;
	  case '\\':
	    c = quotec();
	  default:
	    if (state == 0) {
		*answer++ = ptr;
		state = 1;
	    }
	    *ptr++ = c;
	}
	if (ptr == &envbuf[sizeof(envbuf)-1]) {
	    ungetc('\n',stdin);
	    putc('\n',stdout);
	}
    }
    exit(0);
}
#endif

#ifdef	AFS_SUN_ENV
#ifdef	AFS_SUN5_ENV
doremotelogin()
{
    static struct passwd nouser = { "", "no:password", ~0 };

    getstr(rusername, sizeof (rusername), "remuser");
    getstr(lusername, sizeof (lusername), "locuser");
    if (!username) username = lusername;
    getstr(term, sizeof (term), "Terminal type");
    if (getuid()) {
	pwd = &nouser;
	goto out;

    }
    pwd = getpwnam(lusername);
    if (pwd == NULL) {
	pwd = &nouser;
	goto out;
    }
 out:
    doremoteterm(term);
    return;
}
#else
doremotelogin(host)
    char *host;
{
    int code;
    static char  rusername[100], lusername[100];
    static struct passwd nouser = { "", "no:password", ~0 };

    getstr(rusername, sizeof (rusername), "remuser");
    getstr(lusername, sizeof (lusername), "locuser");
    if (!username) username = lusername;
    getstr(term, sizeof (term), "Terminal type");
    if (getuid()) {
	pwd = &nouser;
	return -1;
    }
    pwd = getpwnam(lusername);
    if (pwd == NULL) {
	pwd = &nouser;
	return -1;
    }
    code = ruserok(host, (pwd->pw_uid == 0), rusername, lusername);
/*printf("DOREM=%d, host=%s, uid=%d, ruser=%s, luser=%s\n", code, host, pwd->pw_uid, rusername, lusername);*/
    return code;
}
#endif

char	*speeds[] =
	{ "0", "50", "75", "110", "134", "150", "200", "300",
	"600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
#define	NSPEEDS	(sizeof (speeds) / sizeof (speeds[0]))

#ifdef	AFS_SUN5_ENV
doremoteterm(terminal)
#else
doremoteterm(terminal, tp)
    struct sgttyb *tp;
#endif
    char *terminal;
{
#ifdef	AFS_SUN5_ENV
    struct termios tp;
#endif
    register char *cp = strchr(terminal, '/'), **cpp;
    char *speed;

#ifdef	AFS_SUN5_ENV
    (void) ioctl(0, TCGETS, &tp);
#endif
    if (cp) {
	*cp++ = '\0';
	speed = cp;
	cp = strchr(speed, '/');
	if (cp)
	    *cp++ = '\0';
	for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
	    if (strcmp(*cpp, speed) == 0) {
#ifdef	AFS_SUN5_ENV
		tp.c_cflag = ((tp.c_cflag & ~CBAUD) | ((cpp-speeds) & CBAUD));
#else
		tp->sg_ispeed = tp->sg_ospeed = cpp-speeds;
#endif
		break;
	    }
    }
#ifdef	AFS_SUN5_ENV
    tp.c_lflag = ECHO|ICANON|IGNPAR|ICRNL;
    (void) ioctl(0, TCSETS, &tp);
#else
    tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
#endif
}
#endif

#ifdef AFS_OSF_ENV
/*
 *  Check if the specified program is really a shell (e.g. "sh" or "csh").
 */
usershell(shell)
	char *shell;
{
	register char *p;
	char *getusershell();

	setusershell();
	while ((p = getusershell()) != NULL)
		if (strcmp(p, shell) == 0)
			break;
	endusershell();
	return(!!p);
}

static char *getlinep(const char *string, int size, FILE *stream) {
    extern int errno;
    int c;
    char *cp;

    errno = 0;
    if(!fgets(string, size, stream) || ferror(stream) || errno == EINTR)
	return NULL;
    else
	if(cp=strchr(string, '\n'))
	    *cp = '\0';
	else
	    while((c=getc(stdin)) != '\n' && c != EOF && errno != EINTR)
		;
    return string;
}

clean_entity_pwd(SIAENTITY *entity) 
{
    if (entity->acctname != NULL) {
	free(entity->acctname);
	entity->acctname = NULL;
    }
    if (entity->pwd != NULL) {
	if (entity->pwd->pw_name) {
	    memset(entity->pwd->pw_name,'\0',strlen(entity->pwd->pw_name));
	    free(entity->pwd->pw_name);
	    entity->pwd->pw_name = NULL;
	}
	if (entity->pwd->pw_passwd) {
	    memset(entity->pwd->pw_passwd,'\0',strlen(entity->pwd->pw_passwd));
	    free(entity->pwd->pw_passwd);
	    entity->pwd->pw_passwd = NULL;
	}
	if (entity->pwd->pw_comment) {
	    memset(entity->pwd->pw_comment,'\0',strlen(entity->pwd->pw_comment));
	    free(entity->pwd->pw_comment);
	    entity->pwd->pw_comment = NULL;
	}
	if (entity->pwd->pw_gecos) {
	    memset(entity->pwd->pw_gecos,'\0',strlen(entity->pwd->pw_gecos));
	    free(entity->pwd->pw_gecos);
	    entity->pwd->pw_gecos = NULL;
	}
	if (entity->pwd->pw_dir) {
	    memset(entity->pwd->pw_dir,'\0',strlen(entity->pwd->pw_dir));
	    free(entity->pwd->pw_dir);
	    entity->pwd->pw_dir = NULL;
	}
	if (entity->pwd->pw_shell) {
	    memset(entity->pwd->pw_shell,'\0',strlen(entity->pwd->pw_shell));
	    free(entity->pwd->pw_shell);
	    entity->pwd->pw_shell = NULL;
	}
	free(entity->pwd);
	entity->pwd = NULL;
    }
}

int getloginp(entity)
    SIAENTITY *entity;
{
    char result[100];

    fputs("login: ", stdout);
    if (getlinep(result,sizeof result,stdin) == (char *) NULL) {
	return (-1);
    }
    entity->name = (char *)malloc(SIANAMEMIN+1);	
    if (entity->name == NULL)
	return (-1);
    strcpy((unsigned char *)entity->name, result); 
    return(1);
}

#ifdef AFS_KERBEROS_ENV
extern char *ktc_tkt_string();
#endif

int do_afs_auth(pwd, pass, doafs) 
struct passwd *pwd;
char *pass;
int doafs; {
    char *namep;
    int code, afsLoginFail=0;
    char *lcell, realm[MAXKTCREALMLEN], *pword, prompt[256];
    char *shell, *reason;

loop:
    if (pass) {
	pword = pass;
    } else {
	sprintf(prompt, "Enter AFS password: ");
	pword = getpass(prompt);
	if (strlen(pword) == 0) {
	    printf("Unable to read password because zero length password is illegal\n");
	    printf("Login incorrect\n"); fflush(stdout);
	    goto loop;
	}
    }

    if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,
				   pwd->pw_name, /* kerberos name */
				   (char *)0, /* instance */
				   (char *)0, /* realm */
				   pword, /* password */
				   0, /* default lifetime */
				   0, /* spare 1 */
				   0, /* spare 2 */
				   &reason /* error string */)) {
        afsLoginFail = 1;
	if (doafs) return 0;
    } else {
	if (pwd->pw_passwd && strcmp(pwd->pw_passwd,"*")) 
	    return 1;	/* Success */
    }

    if (!pwd->pw_passwd) return 0;
    namep = crypt(pword, pwd->pw_passwd);
    code = strcmp(namep, pwd->pw_passwd) == 0 ? 1 : 0 ;
    if ((code == 1) && afsLoginFail) {
       /* Only print this if local authentication is successful */
       printf("Unable to authenticate to AFS because %s\n", reason);
       printf("proceeding with local authentication...\n");
    }
    return code;
}


#endif

#ifdef	AFS_SUN5_ENV
static void defaults()
{
    extern int defopen(), defcntl();
    register int  flags;
    register char *ptr;

    if (defopen(Pndefault) == 0) {
	/*
	 * ignore case
	 */
	flags = defcntl(DC_GETFLAGS, 0);
	TURNOFF(flags, DC_CASE);
	defcntl(DC_SETFLAGS, flags);

	if ((Console = defread("CONSOLE=")) != NULL)
	    Console = strdup(Console);
	if ((Altshell = defread("ALTSHELL=")) != NULL)
	    Altshell = strdup(Altshell);
	if ((Passreq = defread("PASSREQ=")) != NULL)
	    Passreq = strdup(Passreq);
	if ((Def_tz = defread("TIMEZONE=")) != NULL)
	    Def_tz = strdup(Def_tz);
	if ((Def_hertz = defread("HZ=")) != NULL)
	    Def_hertz = strdup(Def_hertz);
	if ((Def_path   = defread("PATH=")) != NULL)
	    Def_path = strdup(Def_path);
	if ((Def_supath = defread("SUPATH=")) != NULL)
	    Def_supath = strdup(Def_supath);
	if ((ptr = defread("ULIMIT=")) != NULL)
	    Def_ulimit = atol(ptr);
	if ((ptr = defread("TIMEOUT=")) != NULL)
	    Def_timeout = (unsigned) atoi(ptr);
	if ((ptr = defread("UMASK=")) != NULL)
	    if (sscanf(ptr, "%lo", &Umask) != 1)
		Umask = DEFUMASK;
	if ((ptr = defread("IDLEWEEKS=")) != NULL)
	    Idleweeks = atoi(ptr);
	if ((ptr = defread("SLEEPTIME=")) != NULL)
	    Sleeptime = atoi(ptr);
	(void) defopen(NULL);
    }
    return;
}
#endif

#ifdef	AFS_HPUX_ENV
/*
 * returns 1 if user should be counted as 1 user;
 * returns 3 if user should be counted as a pty
 * may return other stuff in the future
 */
int
add_count()
{
    if (isapty()) {
	if (is_nlio())
	    return(1);
	else
	    return(3);
    } else
	return(1);
}

/*
 * returns 1 if user is a pty, 0 otherwise
 */
#define SSTRPTYMAJOR 157        /* major number for Streams slave pty's */
#define SPTYMAJOR 17            /* major number for slave  pty's */
#define PTYSC     0x00          /* select code for pty's         */
#define select_code(x) (((x) & 0xff0000) >> 16)
isapty()
{
    struct stat sbuf;
    static int firsttime = 1;
    static int retval;

    if (firsttime) {
	firsttime = 0;
	if (fstat(0, &sbuf) != 0 ||                /* can't stat */
	    (sbuf.st_mode & S_IFMT) != S_IFCHR ||  /* not char special */
	    major(sbuf.st_rdev) != SPTYMAJOR ||    /* not pty major num */
	    select_code(sbuf.st_rdev) != PTYSC) {  /* not pty minor num */
                /* Check to see if it is a Streams PTY */
                if (major(sbuf.st_rdev) == SSTRPTYMAJOR) {
                        retval = 1;
                }
                else
                        retval = 0;     /* neither a BSD nor a Streams PTY */
	} else {
	    retval = 1;
#ifdef TRUX
            if (ISB1 && !remote_login)
                retval=0;               /* local pty for td tty simulation */
#endif /* TRUX */
	}
    }
    return(retval);
}

#define NLIOUCOUNT	_IO('K',28)	/* _IO(K, 28) */
#define NLIOSERV	0x4b33		/* (('K' << 8) + 51 */

/*
 * returns 1 if user is an NL server, else returns 0
 */
is_nlio()
{
    if (ioctl(0, NLIOUCOUNT) == NLIOSERV)
	return(1);
    else
	return(0);
}
#endif
