[551] in Kerberos-V5-bugs

home help back first fref pref prev next nref lref last post

login.c

daemon@ATHENA.MIT.EDU (Brian Kantor)
Thu Jun 30 16:04:01 1994

Date: Thu, 30 Jun 1994 13:03:35 -0700
From: brian@nothing.ucsd.edu (Brian Kantor)
To: krb5-bugs@MIT.EDU

I enclose 'login.c' which I have heavily patched by ripping out all of
the KRB4 code and sticking in krb5 code.  We're successfully using this
on a small cluster of Sun SPARCS running Sunix 4.1.3U2 without any
problems. Kerberos V5B3.

Note that this login is set up to automatically issue a ticket when you
log in and authenticate locally, so for most users, the 'kinit' program
doesn't have to be separately invoked.

The diffs would have been larger than the source, so here's the whole
thing.
	- Brian
/*
 *	$Source: /mit/krb5/.cvsroot/src/appl/bsd/login.c,v $
 *	$Author: tytso $
 *	$Id: Exp $
 *
 * look for 'SITE' for configuration options
 *
 */

#ifndef lint
static char rcsid_login_c[] = "$Id: $";
#endif lint

/*
 * 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.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)login.c	5.25 (Berkeley) 1/6/89";
#endif /* not lint */

#define DEFPATH	"/usr/local/bin:/usr/bin/X11:/usr/ucb:/bin:/usr/bin:/usr/kerberos/bin:."

/*
 * login [ name ]
 * login -r hostname	(for rlogind)
 * login -h hostname	(for telnetd, etc.)
 * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
 * login -e name	(for pre-authenticated encrypted, must do term
 *			 negotiation)
 * ifdef KERBEROS
 * login -k hostname (for Kerberos V5 rlogind with password access)
 * login -K hostname (for Kerberos V5 rlogind with restricted access)
 * endif KERBEROS 
 *
 * only one of: -r -f -e -k -K
 * only one of: -r -h -k -K
 */

#include <sys/types.h>
#include <sys/param.h>
#ifdef OQUOTA
#include <sys/quota.h>
#endif
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include <utmp.h>
#include <signal.h>
#include <lastlog.h>
#include <errno.h>
#ifndef NOTTYENT
#include <ttyent.h>
#endif /* NOTTYENT */
#include <syslog.h>
#include <grp.h>
#include <pwd.h>
#include <setjmp.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>

#ifdef POSIX
#include <stdlib.h>
#include <termios.h>
#endif

#ifdef UIDGID_T
uid_t getuid();
#define uid_type uid_t
#define gid_type gid_t
#else
int getuid();
#define uid_type int
#define gid_type int
#endif /* UIDGID_T */

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

#define	MOTDFILE	"/etc/motd"
#define	MAILDIR		"/var/spool/mail"
#define	NOLOGIN		"/etc/nologin"
#define	HUSHLOGIN	".hushlogin"
#define	LASTLOG		"/var/adm/lastlog"
#define	BSHELL		"/bin/sh"

#if !defined(OQUOTA) && !defined(QUOTAWARN)
#define QUOTAWARN	"/usr/ucb/quota" /* warn user about quotas */
#endif

#define	UT_HOSTSIZE	sizeof(((struct utmp *)0)->ut_host)
#define	UT_NAMESIZE	sizeof(((struct utmp *)0)->ut_name)

#define MAXENVIRON	32

#ifdef KERBEROS
#define	KRB5_DEFAULT_OPTIONS	0
#define	KRB5_DEFAULT_LIFE	60*60*8	/* 8 hours */
/*
 * WARNING: Danger Will Robinson: the following filenames are stupidly
 * hard-coded elsewhere in Kerberos and MUST match that in
 * lib/ccache/cc_defname or you are in deep shit.
 *
 * you have to do it this way or else someone failing to log in to your
 * username on your machine will destroy your perfectly-valid
 * credentials cache, which is a denial of service you betcha!
 */
#define TKT_ROOT		"FILE:/tmp/krb5cc_"
#define TKT_RTMP		"FILE:/tmp/#krb5cc_"

#include <krb5/krb5.h>
#include <krb5/ext-proto.h>
#include <krb5/los-proto.h>
#include <com_err.h>

krb5_ccache ccache;
krb5_address **my_addresses;
krb5_error_code krb5_err;
krb5_principal me;
krb5_principal server;
krb5_creds my_creds;
krb5_timestamp now;
int preauth_type = -1;
krb5_keytab keytab = NULL;
krb5_keytab_entry kt_ent;
char password[255], *client_name, tmpcf[128], tmpcn[128];
int pwsize;

krb5_data tgtname = { KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };

#endif	/* KERBEROS */

/*
 * 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;
char term[64], *hostname, *username;

#ifndef POSIX
struct sgttyb sgttyb;
struct tchars tc = {
	CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
};
struct ltchars ltc = {
	CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
};
#endif

extern int errno;

char *getenv();
char *strsave();

#ifdef POSIX
typedef void sigtype;
#else
typedef int sigtype;
#endif /* POSIX */

#define EXCL_AUTH_TEST if (rflag || kflag || Kflag || eflag) { \
				fprintf(stderr, \
				    "login: only one of -r, -k, -K, -e, and -f allowed.\n"); \
				exit(1);\
			}

#define EXCL_HOST_TEST if (rflag || kflag || Kflag || hflag) { \
				fprintf(stderr, \
				    "login: only one of -r, -k, -K, and -h allowed.\n"); \
				exit(1);\
			}

main(argc, argv)
	int argc;
	char **argv;
{
	extern int optind;
	extern char *optarg, **environ;
	struct group *gr;
	register int ch, i;
	register char *p;
	int fflag, hflag, pflag, rflag, cnt;
	int kflag, Kflag, eflag;
	int quietlog, passwd_req, ioctlval;
	sigtype timedout();
	int lpass_ok = 0;
	int kpass_ok = 0;
	char *domain, *salt, **envinit, *ttyn, *tty, *ktty;
	char tbuf[MAXPATHLEN + 2];
	char *ttyname(), *stypeof(), *crypt(), *getpass();
	time_t time(), login_time;
	off_t lseek();
#ifdef POSIX
	struct termios tc;
#endif

	(void)signal(SIGALRM, timedout);
	(void)alarm((u_int)timeout);
	(void)signal(SIGQUIT, SIG_IGN);
	(void)signal(SIGINT, SIG_IGN);
	(void)setpriority(PRIO_PROCESS, 0, 0);

#ifdef OQUOTA
	(void)quota(Q_SETUID, 0, 0, 0);
#endif

	/*
	 * -p is used by getty to tell login not to destroy the environment
	 * -r is used by rlogind to cause the autologin protocol;
 	 * -f is used to skip a second login authentication 
	 * -e is used to skip a second login authentication, but allows
	 * 	login as root.
	 * -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
	 * -k is used by klogind to cause the Kerberos V4 autologin protocol;
	 * -K is used by klogind to cause the Kerberos V4 autologin
	 *    protocol with restricted access.
	 */
	(void)gethostname(tbuf, sizeof(tbuf));
	domain = strchr(tbuf, '.');

	fflag = hflag = pflag = rflag = kflag = Kflag = eflag = 0;
	passwd_req = 1;
	while ((ch = getopt(argc, argv, "feh:pr:k:K:")) != EOF)
		switch (ch) {
		case 'f':
			EXCL_AUTH_TEST;
			fflag = 1;
			break;

		case 'h':
			EXCL_HOST_TEST;
			if (getuid()) {
				fprintf(stderr,
				    "login: -h for super-user only.\n");
				exit(1);
			}
			hflag = 1;
			if (domain && (p = strchr(optarg, '.')) &&
			    strcmp(p, domain) == 0)
				*p = 0;
			hostname = optarg;
			break;

		case 'p':
			pflag = 1;
			break;

		case 'r':
			EXCL_AUTH_TEST;
			EXCL_HOST_TEST;
			if (getuid()) {
				fprintf(stderr,
				    "login: -r for super-user only.\n");
				exit(1);
			}
			/* "-r hostname" must be last args */
			if (optind != argc) {
				fprintf(stderr, "Syntax error.\n");
				exit(1);
			}
			rflag = 1;
			passwd_req = (doremotelogin(optarg) == -1);
			if (domain && (p = strchr(optarg, '.')) &&
			    !strcmp(p, domain))
				*p = '\0';
			hostname = optarg;
			break;
#ifdef KERBEROS
		case 'k':
		case 'K':
			EXCL_AUTH_TEST;
			EXCL_HOST_TEST;
			if (getuid()) {
				fprintf(stderr,
				    "login: -%c for super-user only.\n", ch);
				exit(1);
			}
			/* "-k hostname" must be last args */
			if (optind != argc) {
				fprintf(stderr, "Syntax error.\n");
				exit(1);
			}
			if (ch == 'K')
			    Kflag = 1;
			else
			    kflag = 1;
			passwd_req = 1;
			if (domain && (p = strchr(optarg, '.')) &&
			    !strcmp(p, domain))
				*p = '\0';
			hostname = optarg;
			break;
#endif /* KERBEROS */
		case 'e':
			EXCL_AUTH_TEST;
			if (getuid()) {
			    fprintf(stderr,
				    "login: -e for super-user only.\n");
			    exit(1);
			}
			eflag = 1;
			passwd_req = 0;
			break;

		case '?':
		default:
			fprintf(stderr, "usage: login [-fp] [username]\n");
			exit(1);
		}
	argc -= optind;
	argv += optind;
	if (*argv)
		username = *argv;

	ioctlval = 0;
	(void)ioctl(0, TIOCLSET, (char *)&ioctlval);
	(void)ioctl(0, TIOCNXCL, (char *)0);
	(void)fcntl(0, F_SETFL, ioctlval);
#ifdef POSIX
	(void)tcgetattr(0, &tc);
#else
	(void)ioctl(0, TIOCGETP, (char *)&sgttyb);
#endif

	/*
	 * If talking to an rlogin process, propagate the terminal type and
	 * baud rate across the network.
	 */
	if (eflag)
	    	lgetstr(term, sizeof(term), "Terminal type");
#ifdef POSIX
	if (rflag || kflag || Kflag || eflag)
		doremoteterm(&tc);
	tc.c_cc[VMIN] = 1;
	tc.c_cc[VTIME] = 0;
	tc.c_cc[VERASE] = CERASE;
	tc.c_cc[VKILL] = CKILL;
	tc.c_cc[VEOF] = CEOF;
	tc.c_cc[VINTR] = CINTR;
	tc.c_cc[VQUIT] = CQUIT;
	tc.c_cc[VSTART] = CSTART;
	tc.c_cc[VSTOP] = CSTOP;
	tc.c_cc[VEOL] = CNUL;
	/* The following are common extensions to POSIX */
#ifdef VEOL2
	tc.c_cc[VEOL2] = CNUL;
#endif
#ifdef VSUSP
	tc.c_cc[VSUSP] = CSUSP;
#endif
#ifdef VDSUSP
	tc.c_cc[VDSUSP] = CDSUSP;
#endif
#ifdef VLNEXT
	tc.c_cc[VLNEXT] = CLNEXT;
#endif
#ifdef VREPRINT
	tc.c_cc[VREPRINT] = CRPRNT;
#endif
#ifdef VDISCRD
	tc.c_cc[VDISCRD] = CFLUSH;
#endif
#ifdef VWERSE
	tc.c_cc[VWERSE] = CWERASE;
#endif
	tcsetattr(0, TCSANOW, &tc);
#else
	if (rflag || kflag || Kflag || eflag)
		doremoteterm(&sgttyb);
	sgttyb.sg_erase = CERASE;
	sgttyb.sg_kill = CKILL;
	(void)ioctl(0, TIOCSLTC, (char *)&ltc);
	(void)ioctl(0, TIOCSETC, (char *)&tc);
	(void)ioctl(0, TIOCSETP, (char *)&sgttyb);
#endif

	for (cnt = getdtablesize(); cnt > 2; cnt--)
		(void) close(cnt);

	ttyn = ttyname(0);
	if (ttyn == NULL || *ttyn == '\0')
		ttyn = "/dev/tty??";

	/* This allows for tty names of the form /dev/pts/4 as well */
	if ((tty = strchr(ttyn, '/')) && (tty = strchr(tty+1, '/')))
		++tty;
	else
		tty = ttyn;

	/* For kerberos tickets, extract only the last part of the ttyname */
	if (ktty = strrchr(tty, '/'))
		++ktty;
	else
		ktty = tty;

	openlog("login", LOG_ODELAY, LOG_AUTH);

#ifdef KERBEROS
	/* initialize kerberos stuff */
	krb5_init_ets();
#endif

	for (cnt = 0;; username = NULL) {
		if (username == NULL) {
			fflag = 0;
			getloginname();
		}

		if (pwd = getpwnam(username))
			salt = pwd->pw_passwd;
		else
			salt = "xx";	/* dummy */

		/* =====================================================
		 * 		LOCAL SITE POLICY DECISION
		 * you have to change the following tests to suit your
		 * site's login policy regarding local (system password)
		 * vs Kerberos passwords vs root
		 * ===================================================== */

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

		/*
		 * if 'fflag' is set to purport that the user
		 * has already been authenticated, be sure that
		 * 1) it's not root he's trying to log in as AND
		 * 2a) root is doing it, OR
		 * 2b) he's logging in as himself
		 * otherwise require a password be entered
		 */
		if (fflag && pwd) {
			int uid = (int)getuid();

			passwd_req = (pwd->pw_uid == 0)
				|| (uid && uid != pwd->pw_uid);
		}

		/*
		 * if a password isn't required (because of some other
		 * kind of authentication has reset 'passwd_req', or
		 * there's no password in the local passwd file, don't
		 * bother to prompt for a password, just procede
		 */
		if (passwd_req == 0 || (pwd && *pwd->pw_passwd == NULL))
			break;

#ifdef KERBEROS
		pwsize = sizeof(password);
		memset(password, 0, sizeof(password));
		krb5_err = krb5_read_password("Password: ",
				0, password, &pwsize);	
		if (krb5_err) {
			memset(password, 0, sizeof(password));
			krb5_free_addresses(my_addresses);
			printf("Error reading password\r\n");
			exit(1);
		}

		/*
		 * check the password against the kerberos server
		 *
		 * note that most errors are silently ignored to avoid
		 * probes of the kerberos server
		 *
		 * most of this code stolen from the KINIT program
		 */
		
		kpass_ok = 0;

		ccache = NULL;

		if (pwd == NULL)
			goto krberr;

		sprintf(tmpcf, "%s%d", TKT_RTMP, pwd->pw_uid);
		sprintf(tmpcn, "%s%d", TKT_ROOT, pwd->pw_uid);
		if (krb5_cc_resolve(tmpcf, &ccache))
			goto krberr;
		if (krb5_parse_name(username, &me))
			goto krberr;
		if (krb5_unparse_name(me, &client_name))
			goto krberr;
		if (krb5_cc_initialize(ccache, me))
			goto krberr;
		memset((char *)&my_creds, 0, sizeof(my_creds));
		my_creds.client = me;
		if ( krb5_build_principal_ext(	&server,
						krb5_princ_realm(me)->length,
						krb5_princ_realm(me)->data,
						tgtname.length,
						tgtname.data,
						krb5_princ_realm(me)->length,
						krb5_princ_realm(me)->data,
						0 ) )
			goto krberr;
		my_creds.server = server;
		if (krb5_os_localaddr(&my_addresses))
			goto krberr;
		if (krb5_timeofday(&now))
			goto krberr;

		my_creds.times.starttime = 0;
		my_creds.times.endtime = now + KRB5_DEFAULT_LIFE;
		my_creds.times.renew_till = 0;

		if ( krb5_get_in_tkt_with_password(
				KRB5_DEFAULT_OPTIONS,
				my_addresses,
				KRB5_PADATA_NONE,
				ETYPE_DES_CBC_CRC,
				KEYTYPE_DES,
				password,
				ccache,
				&my_creds, 0 ) == 0)
			{
			kpass_ok = 1;
			krb5_free_principal(server);
			krb5_free_addresses(my_addresses);
			}
		else
			{
krberr:			
			kpass_ok = 0;
			if (ccache)
				(void)krb5_cc_destroy(ccache);
			}
#else	/* !KERBEROS */
		strcpy(password, getpass("Password:"));
#endif	/* !KERBEROS */
		/* check the password against the password file */
		lpass_ok = 0;
		password[8] = 0;	/* truncate to 8 chars */
		p = crypt(password,salt);
		if (pwd && strcmp(p, pwd->pw_passwd) == 0)
			lpass_ok = 1;	/* his passwd is ok in passwd file */
		
		memset(password, 0, sizeof(password));	/* zap */

		/* SITE local vs kerberos password */
		if (pwd && (lpass_ok || kpass_ok))
			break;

		printf("Login incorrect\n");
		if (++cnt >= 2) {
			if (hostname)
			    syslog(LOG_ERR,
				"REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s",
				tty, UT_HOSTSIZE, hostname, UT_NAMESIZE,
				username);
			else
			    syslog(LOG_ERR,
				"REPEATED LOGIN FAILURES ON %s, %.*s",
				tty, UT_NAMESIZE, username);
			(void)ioctl(0, TIOCHPCL, (char *)0);
			sleepexit(1);
		}
	}

	/* 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.
	 *
	 * We allow authenticated remote root logins (except -r style)
	 */
	if (pwd->pw_uid == 0 && !rootterm(tty) && (passwd_req || rflag)) {
		if (hostname)
			syslog(LOG_ERR, "ROOT LOGIN REFUSED ON %s FROM %.*s",
			    tty, UT_HOSTSIZE, hostname);
		else
			syslog(LOG_ERR, "ROOT LOGIN REFUSED ON %s", tty);
		printf("Login incorrect\n");
		sleepexit(1);
	}

#ifdef OQUOTA
	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)");
		}
		sleepexit(0);
	}
#endif

	if (hostname)
		syslog(LOG_INFO, "%s on %s from %.*s",
		    pwd->pw_name, tty, UT_HOSTSIZE, hostname);
	else
		syslog(LOG_INFO, "%s on %s", pwd->pw_name, tty);

#ifdef KERBEROS
	/* change the scratch credentials cache to a real one */
	rename(tmpcf+5, tmpcn+5);
	(void)chown(tmpcn+5, pwd->pw_uid, pwd->pw_gid);
	unlink(tmpcf+5);
#endif
	if (chdir(pwd->pw_dir) < 0) {
		syslog(LOG_ERR, "%s no home directory %s",
			pwd->pw_name, pwd->pw_dir);
		printf("No directory %s!\n", pwd->pw_dir);
		if (chdir("/"))
			exit(0);
		pwd->pw_dir = "/";
		printf("Logging in with home = \"/\".\n");
	}

	/* nothing else left to fail -- really log in */
	{
		struct utmp utmp;

		bzero((char *)&utmp, sizeof(utmp));
		login_time = time(&utmp.ut_time);
		(void) strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
		if (hostname)
		    (void) strncpy(utmp.ut_host, hostname,
				   sizeof(utmp.ut_host));
		else
		    bzero(utmp.ut_host, sizeof(utmp.ut_host));
		(void) strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
		login(&utmp);
	}

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

	if (!hflag && !rflag && !kflag && !Kflag && !eflag) {	/* XXX */
		static struct winsize win = { 0, 0, 0, 0 };

		(void)ioctl(0, TIOCSWINSZ, (char *)&win);
	}

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

	(void)chmod(ttyn, 0620);

	(void)setgid((gid_type) pwd->pw_gid);
	(void) initgroups(username, pwd->pw_gid);

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

	/* This call MUST succeed */
	if(setuid((uid_type) pwd->pw_uid) < 0) {
	     perror("setuid");
	     sleepexit(1);
	}

	if (*pwd->pw_shell == '\0')
		pwd->pw_shell = BSHELL;
		/* turn on new line discipline for the csh */
	else if (!strcmp(pwd->pw_shell, "/bin/csh")) {
		ioctlval = NTTYDISC;
		(void)ioctl(0, TIOCSETD, (char *)&ioctlval);
	}

	/* destroy environment unless user has requested preservation */
	envinit = (char **)malloc(MAXENVIRON * sizeof(char *));
	if (envinit == 0) {
		fprintf(stderr, "Can't malloc empty environment.\n");
		sleepexit(1);
	}

	if (!pflag)
		environ = envinit;

	i = 0;

	sprintf(tbuf,"LOGNAME=%s",pwd->pw_name);
	envinit[i++] = strsave(tbuf);
	sprintf(tbuf,"LOGIN=%s",pwd->pw_name);
	envinit[i++] = strsave(tbuf);

	envinit[i++] = NULL;

	setenv("HOME", pwd->pw_dir, 0);
	setenv("PATH", DEFPATH, 0);
	setenv("USER", pwd->pw_name, 0);
	setenv("SHELL", pwd->pw_shell, 0);
	if (hostname)
		setenv("RHOST", hostname, 0);

	if (term[0] == '\0')
		(void) strncpy(term, stypeof(tty), sizeof(term));
	(void)setenv("TERM", term, 0);

	/* root logins */
	if (pwd->pw_uid == 0)
		if (hostname)
#ifdef KERBEROS
			if (kpass_ok) {
			    char buf[BUFSIZ];
			    syslog("ROOT LOGIN (krb) %s from %.*s, %s",
				   tty, UT_HOSTSIZE, hostname, client_name);
		        } else {
#endif /* KERBEROS */
			syslog(LOG_NOTICE, "ROOT LOGIN on %s FROM %.*s",
			    tty, UT_HOSTSIZE, hostname);
#ifdef KERBEROS
			}
  		else 
			if (kpass_ok) {
			    syslog(LOG_NOTICE, "ROOT LOGIN (krb) %s, %s",
				   tty, client_name);
			} 
#endif /* KERBEROS */
		else
			syslog(LOG_NOTICE, "ROOT LOGIN %s", tty);

	if (!quietlog) {
		struct stat st;

		motd();

#ifdef KERBEROS
		if (kpass_ok)
		    printf("\nKERBEROS ticket obtained.\n\n");
		else
		    printf("\nWarning: NO Kerberos tickets obtained.\n\n");
#endif /* KERBEROS */

		(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 " : "");
	}

#ifndef OQUOTA
	if (!access(QUOTAWARN, X_OK))
		(void) system(QUOTAWARN);
#endif
	(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(pwd->pw_shell, '/')) ?
	    p + 1 : pwd->pw_shell);

	execlp(pwd->pw_shell, tbuf, 0);

	syslog(LOG_ERR, "%s no shell %s", pwd->pw_name, pwd->pw_shell);
	fprintf(stderr, "login: no shell: ");
	perror(pwd->pw_shell);
	exit(0);
}

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

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

char *strsave(s)
char *s;
{
    char *ret = (char *)malloc(strlen(s) + 1);
    strcpy(ret, s);
    return(ret);
}


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

#ifdef NOTTYENT
int root_tty_security = 1;
#endif
rootterm(tty)
	char *tty;
{
#ifdef NOTTYENT
	return(root_tty_security);
#else
	struct ttyent *t;

	return((t = getttynam(tty)) && t->ty_status&TTY_SECURE);
#endif /* NOTTYENT */
}

jmp_buf motdinterrupt;

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

	if ((fd = open(MOTDFILE, O_RDONLY, 0)) < 0)
		return;
	oldint = (void *)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);
}

sigtype
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);
	}
}

dolastlog(quiet, tty)
	int quiet;
	char *tty;
{
	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);
		}
		(void)time(&ll.ll_time);
		(void) strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
		if (hostname)
		    (void) strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
		else
		    (void) bzero(ll.ll_host, sizeof(ll.ll_host));
		(void)write(fd, (char *)&ll, sizeof(ll));
		(void)close(fd);
	}
}

#undef	UNKNOWN
#define	UNKNOWN	"su"

char *
stypeof(ttyid)
	char *ttyid;
{
#ifdef NOTTYENT
	return(UNKNOWN);
#else
	struct ttyent *t;

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

doremotelogin(host)
	char *host;
{
	static char lusername[UT_NAMESIZE+1];
	char rusername[UT_NAMESIZE+1];

	lgetstr(rusername, sizeof(rusername), "Remote user");
	lgetstr(lusername, sizeof(lusername), "Local user");
	lgetstr(term, sizeof(term), "Terminal type");
	username = lusername;
	pwd = getpwnam(username);
	if (pwd == NULL)
		return(-1);
	return(ruserok(host, (pwd->pw_uid == 0), rusername, username));
}


lgetstr(buf, cnt, err)
	char *buf, *err;
	int cnt;
{
	int ocnt = cnt;
	char *obuf = buf;
	char ch;

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

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]))

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

	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 POSIX
				tp->c_cflag =
					(tp->c_cflag & ~CBAUD) | (cpp-speeds);
#else
				tp->sg_ispeed = tp->sg_ospeed = cpp-speeds;
#endif
				break;
			}
	}
#ifdef POSIX
 	/* set all standard echo, edit, and job control options */
 	tp->c_lflag = ECHO|ECHOE|ECHOK|ICANON|ISIG;
 	tp->c_iflag |= ICRNL|BRKINT;
 	tp->c_oflag |= ONLCR|OPOST|TAB3;
#else /* !POSIX */
	tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
#endif
}

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


home help back first fref pref prev next nref lref last post