Home | History | Annotate | Line # | Download | only in login
login.c revision 1.1
      1 /*-
      2  * Copyright (c) 1980, 1987, 1988, 1991 The Regents of the University
      3  * of California.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 char copyright[] =
     36 "@(#) Copyright (c) 1980, 1987, 1988, 1991 The Regents of the University of California.\n\
     37  All rights reserved.\n";
     38 #endif /* not lint */
     39 
     40 #ifndef lint
     41 static char sccsid[] = "@(#)login.c	5.73 (Berkeley) 6/29/91";
     42 #endif /* not lint */
     43 
     44 /*
     45  * login [ name ]
     46  * login -h hostname	(for telnetd, etc.)
     47  * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
     48  */
     49 
     50 #include <sys/param.h>
     51 #include <sys/stat.h>
     52 #include <sys/time.h>
     53 #include <sys/resource.h>
     54 #include <sys/file.h>
     55 
     56 #include <utmp.h>
     57 #include <signal.h>
     58 #include <errno.h>
     59 #include <ttyent.h>
     60 #include <syslog.h>
     61 #include <grp.h>
     62 #include <pwd.h>
     63 #include <setjmp.h>
     64 #include <stdio.h>
     65 #include <string.h>
     66 #include <tzfile.h>
     67 #include "pathnames.h"
     68 
     69 #define	TTYGRPNAME	"tty"		/* name of group to own ttys */
     70 
     71 /*
     72  * This bounds the time given to login.  Not a define so it can
     73  * be patched on machines where it's too small.
     74  */
     75 int	timeout = 300;
     76 int	rootlogin;
     77 #ifdef KERBEROS
     78 int	notickets = 1;
     79 char	*instance;
     80 char	*krbtkfile_env;
     81 int	authok;
     82 #endif
     83 
     84 struct	passwd *pwd;
     85 int	failures;
     86 char	term[64], *envinit[1], *hostname, *username, *tty;
     87 
     88 main(argc, argv)
     89 	int argc;
     90 	char **argv;
     91 {
     92 	extern int optind;
     93 	extern char *optarg, **environ;
     94 	struct timeval tp;
     95 	struct group *gr;
     96 	register int ch;
     97 	register char *p;
     98 	int ask, fflag, hflag, pflag, cnt, uid;
     99 	int quietlog, rval;
    100 	char *domain, *salt, *ttyn;
    101 	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
    102 	char localhost[MAXHOSTNAMELEN];
    103 	char *ctime(), *ttyname(), *stypeof(), *crypt(), *getpass();
    104 	time_t time();
    105 	off_t lseek();
    106 	void timedout();
    107 
    108 	(void)signal(SIGALRM, timedout);
    109 	(void)alarm((u_int)timeout);
    110 	(void)signal(SIGQUIT, SIG_IGN);
    111 	(void)signal(SIGINT, SIG_IGN);
    112 	(void)setpriority(PRIO_PROCESS, 0, 0);
    113 
    114 	openlog("login", LOG_ODELAY, LOG_AUTH);
    115 
    116 	/*
    117 	 * -p is used by getty to tell login not to destroy the environment
    118  	 * -f is used to skip a second login authentication
    119 	 * -h is used by other servers to pass the name of the remote
    120 	 *    host to login so that it may be placed in utmp and wtmp
    121 	 */
    122 	domain = NULL;
    123 	if (gethostname(localhost, sizeof(localhost)) < 0)
    124 		syslog(LOG_ERR, "couldn't get local hostname: %m");
    125 	else
    126 		domain = index(localhost, '.');
    127 
    128 	fflag = hflag = pflag = 0;
    129 	uid = getuid();
    130 	while ((ch = getopt(argc, argv, "fh:p")) != EOF)
    131 		switch (ch) {
    132 		case 'f':
    133 			fflag = 1;
    134 			break;
    135 		case 'h':
    136 			if (uid) {
    137 				(void)fprintf(stderr,
    138 				    "login: -h option: %s\n", strerror(EPERM));
    139 				exit(1);
    140 			}
    141 			hflag = 1;
    142 			if (domain && (p = index(optarg, '.')) &&
    143 			    strcasecmp(p, domain) == 0)
    144 				*p = 0;
    145 			hostname = optarg;
    146 			break;
    147 		case 'p':
    148 			pflag = 1;
    149 			break;
    150 		case '?':
    151 		default:
    152 			if (!uid)
    153 				syslog(LOG_ERR, "invalid flag %c", ch);
    154 			(void)fprintf(stderr,
    155 			    "usage: login [-fp] [-h hostname] [username]\n");
    156 			exit(1);
    157 		}
    158 	argc -= optind;
    159 	argv += optind;
    160 	if (*argv) {
    161 		username = *argv;
    162 		ask = 0;
    163 	} else
    164 		ask = 1;
    165 
    166 	for (cnt = getdtablesize(); cnt > 2; cnt--)
    167 		close(cnt);
    168 
    169 	ttyn = ttyname(0);
    170 	if (ttyn == NULL || *ttyn == '\0') {
    171 		(void)sprintf(tname, "%s??", _PATH_TTY);
    172 		ttyn = tname;
    173 	}
    174 	if (tty = rindex(ttyn, '/'))
    175 		++tty;
    176 	else
    177 		tty = ttyn;
    178 
    179 	for (cnt = 0;; ask = 1) {
    180 		if (ask) {
    181 			fflag = 0;
    182 			getloginname();
    183 		}
    184 #ifdef	KERBEROS
    185 		if ((instance = index(username, '.')) != NULL) {
    186 			if (strncmp(instance, ".root", 5) == 0)
    187 				rootlogin++;
    188 			*instance++ = '\0';
    189 		} else {
    190 			rootlogin = 0;
    191 			instance = "";
    192 		}
    193 #else
    194 		rootlogin = 0;
    195 #endif
    196 		if (strlen(username) > UT_NAMESIZE)
    197 			username[UT_NAMESIZE] = '\0';
    198 
    199 		/*
    200 		 * Note if trying multiple user names; log failures for
    201 		 * previous user name, but don't bother logging one failure
    202 		 * for nonexistent name (mistyped username).
    203 		 */
    204 		if (failures && strcmp(tbuf, username)) {
    205 			if (failures > (pwd ? 0 : 1))
    206 				badlogin(tbuf);
    207 			failures = 0;
    208 		}
    209 		(void)strcpy(tbuf, username);
    210 
    211 		if (pwd = getpwnam(username))
    212 			salt = pwd->pw_passwd;
    213 		else
    214 			salt = "xx";
    215 
    216 		/*
    217 		 * if we have a valid account name, and it doesn't have a
    218 		 * password, or the -f option was specified and the caller
    219 		 * is root or the caller isn't changing their uid, don't
    220 		 * authenticate.
    221 		 */
    222 		if (pwd && (*pwd->pw_passwd == '\0' ||
    223 		    fflag && (uid == 0 || uid == pwd->pw_uid)))
    224 			break;
    225 		fflag = 0;
    226 		if (pwd && pwd->pw_uid == 0)
    227 			rootlogin = 1;
    228 
    229 		(void)setpriority(PRIO_PROCESS, 0, -4);
    230 
    231 		p = getpass("Password:");
    232 
    233 		if (pwd) {
    234 #ifdef KERBEROS
    235 			rval = klogin(pwd, instance, localhost, p);
    236 			if (rval == 0)
    237 				authok = 1;
    238 			else if (rval == 1) {
    239 				if (pwd->pw_uid != 0)
    240 					rootlogin = 0;
    241 				rval = strcmp(crypt(p, salt), pwd->pw_passwd);
    242 			}
    243 #else
    244 			if (pwd->pw_uid != 0)
    245 				rootlogin = 0;
    246 #ifdef DES
    247 			rval = strcmp(crypt(p, salt), pwd->pw_passwd);
    248 #else
    249 			rval = strcmp(p, pwd->pw_passwd);
    250 #endif
    251 #endif
    252 		}
    253 		bzero(p, strlen(p));
    254 
    255 		(void)setpriority(PRIO_PROCESS, 0, 0);
    256 
    257 		/*
    258 		 * If trying to log in as root without Kerberos,
    259 		 * but with insecure terminal, refuse the login attempt.
    260 		 */
    261 #ifdef KERBEROS
    262 		if (authok == 0)
    263 #endif
    264 		if (pwd && rootlogin && !rootterm(tty)) {
    265 			(void)fprintf(stderr,
    266 			    "%s login refused on this terminal.\n",
    267 			    pwd->pw_name);
    268 			if (hostname)
    269 				syslog(LOG_NOTICE,
    270 				    "LOGIN %s REFUSED FROM %s ON TTY %s",
    271 				    pwd->pw_name, hostname, tty);
    272 			else
    273 				syslog(LOG_NOTICE,
    274 				    "LOGIN %s REFUSED ON TTY %s",
    275 				     pwd->pw_name, tty);
    276 			continue;
    277 		}
    278 
    279 		if (pwd && !rval)
    280 			break;
    281 
    282 		(void)printf("Login incorrect\n");
    283 		failures++;
    284 		/* we allow 10 tries, but after 3 we start backing off */
    285 		if (++cnt > 3) {
    286 			if (cnt >= 10) {
    287 				badlogin(username);
    288 				sleepexit(1);
    289 			}
    290 			sleep((u_int)((cnt - 3) * 5));
    291 		}
    292 	}
    293 
    294 	/* committed to login -- turn off timeout */
    295 	(void)alarm((u_int)0);
    296 
    297 	endpwent();
    298 
    299 	/* if user not super-user, check for disabled logins */
    300 	if (!rootlogin)
    301 		checknologin();
    302 
    303 	if (chdir(pwd->pw_dir) < 0) {
    304 		(void)printf("No home directory %s!\n", pwd->pw_dir);
    305 		if (chdir("/"))
    306 			exit(0);
    307 		pwd->pw_dir = "/";
    308 		(void)printf("Logging in with home = \"/\".\n");
    309 	}
    310 
    311 	quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
    312 
    313 	if (pwd->pw_change || pwd->pw_expire)
    314 		(void)gettimeofday(&tp, (struct timezone *)NULL);
    315 	if (pwd->pw_change)
    316 		if (tp.tv_sec >= pwd->pw_change) {
    317 			(void)printf("Sorry -- your password has expired.\n");
    318 			sleepexit(1);
    319 		} else if (pwd->pw_change - tp.tv_sec <
    320 		    2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
    321 			(void)printf("Warning: your password expires on %s",
    322 			    ctime(&pwd->pw_expire));
    323 	if (pwd->pw_expire)
    324 		if (tp.tv_sec >= pwd->pw_expire) {
    325 			(void)printf("Sorry -- your account has expired.\n");
    326 			sleepexit(1);
    327 		} else if (pwd->pw_expire - tp.tv_sec <
    328 		    2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
    329 			(void)printf("Warning: your account expires on %s",
    330 			    ctime(&pwd->pw_expire));
    331 
    332 	/* nothing else left to fail -- really log in */
    333 	{
    334 		struct utmp utmp;
    335 
    336 		bzero((void *)&utmp, sizeof(utmp));
    337 		(void)time(&utmp.ut_time);
    338 		strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
    339 		if (hostname)
    340 			strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
    341 		strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
    342 		login(&utmp);
    343 	}
    344 
    345 	dolastlog(quietlog);
    346 
    347 	(void)chown(ttyn, pwd->pw_uid,
    348 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
    349 	(void)setgid(pwd->pw_gid);
    350 
    351 	initgroups(username, pwd->pw_gid);
    352 
    353 	if (*pwd->pw_shell == '\0')
    354 		pwd->pw_shell = _PATH_BSHELL;
    355 
    356 	/* destroy environment unless user has requested preservation */
    357 	if (!pflag)
    358 		environ = envinit;
    359 	(void)setenv("HOME", pwd->pw_dir, 1);
    360 	(void)setenv("SHELL", pwd->pw_shell, 1);
    361 	if (term[0] == '\0')
    362 		strncpy(term, stypeof(tty), sizeof(term));
    363 	(void)setenv("TERM", term, 0);
    364 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    365 	(void)setenv("USER", pwd->pw_name, 1);
    366 	(void)setenv("PATH", _PATH_DEFPATH, 0);
    367 #ifdef KERBEROS
    368 	if (krbtkfile_env)
    369 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
    370 #endif
    371 
    372 	if (tty[sizeof("tty")-1] == 'd')
    373 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    374 	/* if fflag is on, assume caller/authenticator has logged root login */
    375 	if (rootlogin && fflag == 0)
    376 		if (hostname)
    377 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    378 			    username, tty, hostname);
    379 		else
    380 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
    381 
    382 #ifdef KERBEROS
    383 	if (!quietlog && notickets == 1)
    384 		(void)printf("Warning: no Kerberos tickets issued.\n");
    385 #endif
    386 
    387 	if (!quietlog) {
    388 		struct stat st;
    389 
    390 		printf("%s%s",
    391 			"386BSD Release 0.1 by William and Lynne Jolitz.\n",
    392 "Copyright (c) 1989,1990,1991,1992 William F. Jolitz. All rights reserved.\n\
    393 Based in part on work by the 386BSD User Community and the\n\
    394 BSD Networking Software, Release 2 by UCB EECS Department.\n");
    395 
    396 		motd();
    397 		(void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    398 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    399 			(void)printf("You have %smail.\n",
    400 			    (st.st_mtime > st.st_atime) ? "new " : "");
    401 	}
    402 
    403 	(void)signal(SIGALRM, SIG_DFL);
    404 	(void)signal(SIGQUIT, SIG_DFL);
    405 	(void)signal(SIGINT, SIG_DFL);
    406 	(void)signal(SIGTSTP, SIG_IGN);
    407 
    408 	tbuf[0] = '-';
    409 	strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ?
    410 	    p + 1 : pwd->pw_shell);
    411 
    412 	if (setlogin(pwd->pw_name) < 0)
    413 		syslog(LOG_ERR, "setlogin() failure: %m");
    414 
    415 	/* discard permissions last so can't get killed and drop core */
    416 	if (rootlogin)
    417 		(void) setuid(0);
    418 	else
    419 		(void) setuid(pwd->pw_uid);
    420 
    421 	execlp(pwd->pw_shell, tbuf, 0);
    422 	(void)fprintf(stderr, "%s: %s\n", pwd->pw_shell, strerror(errno));
    423 	exit(1);
    424 }
    425 
    426 #ifdef	KERBEROS
    427 #define	NBUFSIZ		(UT_NAMESIZE + 1 + 5) /* .root suffix */
    428 #else
    429 #define	NBUFSIZ		(UT_NAMESIZE + 1)
    430 #endif
    431 
    432 getloginname()
    433 {
    434 	register int ch;
    435 	register char *p;
    436 	static char nbuf[NBUFSIZ];
    437 
    438 	for (;;) {
    439 		(void)printf("login: ");
    440 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    441 			if (ch == EOF) {
    442 				badlogin(username);
    443 				exit(0);
    444 			}
    445 			if (p < nbuf + (NBUFSIZ - 1))
    446 				*p++ = ch;
    447 		}
    448 		if (p > nbuf)
    449 			if (nbuf[0] == '-')
    450 				(void)fprintf(stderr,
    451 				    "login names may not start with '-'.\n");
    452 			else {
    453 				*p = '\0';
    454 				username = nbuf;
    455 				break;
    456 			}
    457 	}
    458 }
    459 
    460 void
    461 timedout()
    462 {
    463 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    464 	exit(0);
    465 }
    466 
    467 rootterm(ttyn)
    468 	char *ttyn;
    469 {
    470 	struct ttyent *t;
    471 
    472 	return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
    473 }
    474 
    475 jmp_buf motdinterrupt;
    476 
    477 motd()
    478 {
    479 	register int fd, nchars;
    480 	sig_t oldint;
    481 	void sigint();
    482 	char tbuf[8192];
    483 
    484 	if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    485 		return;
    486 	oldint = signal(SIGINT, sigint);
    487 	if (setjmp(motdinterrupt) == 0)
    488 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    489 			(void)write(fileno(stdout), tbuf, nchars);
    490 	(void)signal(SIGINT, oldint);
    491 	(void)close(fd);
    492 }
    493 
    494 void
    495 sigint()
    496 {
    497 	longjmp(motdinterrupt, 1);
    498 }
    499 
    500 checknologin()
    501 {
    502 	register int fd, nchars;
    503 	char tbuf[8192];
    504 
    505 	if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
    506 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    507 			(void)write(fileno(stdout), tbuf, nchars);
    508 		sleepexit(0);
    509 	}
    510 }
    511 
    512 dolastlog(quiet)
    513 	int quiet;
    514 {
    515 	struct lastlog ll;
    516 	int fd;
    517 	char *ctime();
    518 
    519 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
    520 		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
    521 		if (!quiet) {
    522 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
    523 			    ll.ll_time != 0) {
    524 				(void)printf("Last login: %.*s ",
    525 				    24-5, (char *)ctime(&ll.ll_time));
    526 				if (*ll.ll_host != '\0')
    527 					(void)printf("from %.*s\n",
    528 					    sizeof(ll.ll_host), ll.ll_host);
    529 				else
    530 					(void)printf("on %.*s\n",
    531 					    sizeof(ll.ll_line), ll.ll_line);
    532 			}
    533 			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
    534 		}
    535 		bzero((void *)&ll, sizeof(ll));
    536 		(void)time(&ll.ll_time);
    537 		strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
    538 		if (hostname)
    539 			strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
    540 		(void)write(fd, (char *)&ll, sizeof(ll));
    541 		(void)close(fd);
    542 	}
    543 }
    544 
    545 badlogin(name)
    546 	char *name;
    547 {
    548 	if (failures == 0)
    549 		return;
    550 	if (hostname) {
    551 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
    552 		    failures, failures > 1 ? "S" : "", hostname);
    553 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    554 		    "%d LOGIN FAILURE%s FROM %s, %s",
    555 		    failures, failures > 1 ? "S" : "", hostname, name);
    556 	} else {
    557 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
    558 		    failures, failures > 1 ? "S" : "", tty);
    559 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    560 		    "%d LOGIN FAILURE%s ON %s, %s",
    561 		    failures, failures > 1 ? "S" : "", tty, name);
    562 	}
    563 }
    564 
    565 #undef	UNKNOWN
    566 #define	UNKNOWN	"su"
    567 
    568 char *
    569 stypeof(ttyid)
    570 	char *ttyid;
    571 {
    572 	struct ttyent *t;
    573 
    574 	return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
    575 }
    576 
    577 sleepexit(eval)
    578 	int eval;
    579 {
    580 	sleep((u_int)5);
    581 	exit(eval);
    582 }
    583