1 /*
2  * login(1)
3  *
4  * This program is derived from 4.3 BSD software and is subject to the
5  * copyright notice below.
6  *
7  * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
8  * Rewritten to PAM-only version.
9  *
10  * Michael Glad (glad@daimi.dk)
11  * Computer Science Department, Aarhus University, Denmark
12  * 1990-07-04
13  *
14  * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
15  * All rights reserved.
16  *
17  * Redistribution and use in source and binary forms are permitted
18  * provided that the above copyright notice and this paragraph are
19  * duplicated in all such forms and that any documentation,
20  * advertising materials, and other materials related to such
21  * distribution and use acknowledge that the software was developed
22  * by the University of California, Berkeley.  The name of the
23  * University may not be used to endorse or promote products derived
24  * from this software without specific prior written permission.
25  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
27  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28  */
29 #include <sys/param.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <getopt.h>
34 #include <memory.h>
35 #include <time.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/resource.h>
39 #include <sys/file.h>
40 #include <termios.h>
41 #include <string.h>
42 #include <sys/ioctl.h>
43 #include <sys/wait.h>
44 #include <signal.h>
45 #include <errno.h>
46 #include <grp.h>
47 #include <pwd.h>
48 #include <utmpx.h>
49 #ifdef HAVE_LASTLOG_H
50 # include <lastlog.h>
51 #endif
52 #include <stdlib.h>
53 #include <sys/syslog.h>
54 #ifdef HAVE_LINUX_MAJOR_H
55 # include <linux/major.h>
56 #endif
57 #include <netdb.h>
58 #include <security/pam_appl.h>
59 #ifdef HAVE_SECURITY_PAM_MISC_H
60 # include <security/pam_misc.h>
61 #elif defined(HAVE_SECURITY_OPENPAM_H)
62 # include <security/openpam.h>
63 #endif
64 #include <sys/sendfile.h>
65 
66 #ifdef HAVE_LIBAUDIT
67 # include <libaudit.h>
68 #endif
69 
70 #include "c.h"
71 #include "setproctitle.h"
72 #include "pathnames.h"
73 #include "strutils.h"
74 #include "nls.h"
75 #include "env.h"
76 #include "xalloc.h"
77 #include "all-io.h"
78 #include "fileutils.h"
79 #include "timeutils.h"
80 #include "ttyutils.h"
81 #include "pwdutils.h"
82 
83 #include "logindefs.h"
84 
85 #define is_pam_failure(_rc)	((_rc) != PAM_SUCCESS)
86 
87 #define LOGIN_MAX_TRIES        3
88 #define LOGIN_EXIT_TIMEOUT     5
89 #define LOGIN_TIMEOUT          60
90 
91 #ifdef USE_TTY_GROUP
92 # define TTY_MODE 0620
93 #else
94 # define TTY_MODE 0600
95 #endif
96 
97 #define	TTYGRPNAME	"tty"	/* name of group to own ttys */
98 #define VCS_PATH_MAX	64
99 
100 #if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT)
101 # include <dirent.h>
102 # define MOTDDIR_SUPPORT
103 # define MOTDDIR_EXT	".motd"
104 # define MOTDDIR_EXTSIZ	(sizeof(MOTDDIR_EXT) - 1)
105 #endif
106 
107 /*
108  * Login control struct
109  */
110 struct login_context {
111 	const char	*tty_path;	/* ttyname() return value */
112 	const char	*tty_name;	/* tty_path without /dev prefix */
113 	const char	*tty_number;	/* end of the tty_path */
114 	mode_t		tty_mode;	/* chmod() mode */
115 
116 	const char	*username;	/* points to PAM, pwd or cmd_username */
117 	char            *cmd_username;	/* username specified on command line */
118 
119 
120 	struct passwd	*pwd;		/* user info */
121 	char		*pwdbuf;	/* pwd strings */
122 
123 	pam_handle_t	*pamh;		/* PAM handler */
124 	struct pam_conv	conv;		/* PAM conversation */
125 
126 #ifdef LOGIN_CHOWN_VCS
127 	char		vcsn[VCS_PATH_MAX];	/* virtual console name */
128 	char		vcsan[VCS_PATH_MAX];
129 #endif
130 
131 	char		*thishost;		/* this machine */
132 	char		*thisdomain;		/* this machine's domain */
133 	char		*hostname;		/* remote machine */
134 	char		hostaddress[16];	/* remote address */
135 
136 	pid_t		pid;
137 
138 	unsigned int	quiet:1,        /* hush file exists */
139 			remote:1,	/* login -h */
140 			nohost:1,	/* login -H */
141 			noauth:1,	/* login -f */
142 			keep_env:1;	/* login -p */
143 };
144 
145 /*
146  * This bounds the time given to login.  Not a define, so it can
147  * be patched on machines where it's too small.
148  */
149 static unsigned int timeout = LOGIN_TIMEOUT;
150 static int child_pid = 0;
151 static volatile int got_sig = 0;
152 static char timeout_msg[128];
153 
154 #ifdef LOGIN_CHOWN_VCS
155 /* true if the filedescriptor fd is a console tty, very Linux specific */
is_consoletty(int fd)156 static int is_consoletty(int fd)
157 {
158 	struct stat stb;
159 
160 	if ((fstat(fd, &stb) >= 0)
161 	    && (major(stb.st_rdev) == TTY_MAJOR)
162 	    && (minor(stb.st_rdev) < 64)) {
163 		return 1;
164 	}
165 	return 0;
166 }
167 #endif
168 
169 
170 /*
171  * Robert Ambrose writes:
172  * A couple of my users have a problem with login processes hanging around
173  * soaking up pts's.  What they seem to hung up on is trying to write out the
174  * message 'Login timed out after %d seconds' when the connection has already
175  * been dropped.
176  * What I did was add a second timeout while trying to write the message, so
177  * the process just exits if the second timeout expires.
178  */
179 static void __attribute__ ((__noreturn__))
timedout2(int sig)180 timedout2(int sig __attribute__ ((__unused__)))
181 {
182 	struct termios ti;
183 
184 	/* reset echo */
185 	tcgetattr(0, &ti);
186 	ti.c_lflag |= ECHO;
187 	tcsetattr(0, TCSANOW, &ti);
188 	_exit(EXIT_SUCCESS);	/* %% */
189 }
190 
timedout(int sig)191 static void timedout(int sig __attribute__ ((__unused__)))
192 {
193 	signal(SIGALRM, timedout2);
194 	alarm(10);
195 	ignore_result( write(STDERR_FILENO, timeout_msg, strlen(timeout_msg)) );
196 	signal(SIGALRM, SIG_IGN);
197 	alarm(0);
198 	timedout2(0);
199 }
200 
201 /*
202  * This handler can be used to inform a shell about signals to login. If you have
203  * (root) permissions, you can kill all login children by one signal to the
204  * login process.
205  *
206  * Also, a parent who is session leader is able (before setsid() in the child)
207  * to inform the child when the controlling tty goes away (e.g. modem hangup).
208  */
sig_handler(int signal)209 static void sig_handler(int signal)
210 {
211 	if (child_pid)
212 		kill(-child_pid, signal);
213 	else
214 		got_sig = 1;
215 	if (signal == SIGTERM)
216 		kill(-child_pid, SIGHUP);	/* because the shell often ignores SIGTERM */
217 }
218 
219 /*
220  * Let us delay all exit() calls when the user is not authenticated
221  * or the session not fully initialized (loginpam_session()).
222  */
sleepexit(int eval)223 static void __attribute__ ((__noreturn__)) sleepexit(int eval)
224 {
225 	sleep((unsigned int)getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT));
226 	exit(eval);
227 }
228 
get_thishost(struct login_context * cxt,const char ** domain)229 static const char *get_thishost(struct login_context *cxt, const char **domain)
230 {
231 	if (!cxt->thishost) {
232 		cxt->thishost = xgethostname();
233 		if (!cxt->thishost) {
234 			if (domain)
235 				*domain = NULL;
236 			return NULL;
237 		}
238 		cxt->thisdomain = strchr(cxt->thishost, '.');
239 		if (cxt->thisdomain)
240 			*cxt->thisdomain++ = '\0';
241 	}
242 
243 	if (domain)
244 		*domain = cxt->thisdomain;
245 	return cxt->thishost;
246 }
247 
248 #ifdef MOTDDIR_SUPPORT
motddir_filter(const struct dirent * d)249 static int motddir_filter(const struct dirent *d)
250 {
251 	size_t namesz;
252 
253 #ifdef _DIRENT_HAVE_D_TYPE
254 	if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG &&
255 	    d->d_type != DT_LNK)
256 		return 0;
257 #endif
258 	if (*d->d_name == '.')
259 		return 0;
260 
261 	namesz = strlen(d->d_name);
262 	if (!namesz || namesz < MOTDDIR_EXTSIZ + 1 ||
263 	    strcmp(d->d_name + (namesz - MOTDDIR_EXTSIZ), MOTDDIR_EXT) != 0)
264 		return 0;
265 
266 	return 1; /* accept */
267 }
268 
motddir(const char * dirname)269 static int motddir(const char *dirname)
270 {
271         int dd, nfiles, i, done = 0;
272         struct dirent **namelist = NULL;
273 
274 	dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
275 	if (dd < 0)
276 		return 0;
277 
278 	nfiles = scandirat(dd, ".", &namelist, motddir_filter, versionsort);
279 	if (nfiles <= 0)
280 		goto done;
281 
282 	for (i = 0; i < nfiles; i++) {
283 		struct dirent *d = namelist[i];
284 		int fd;
285 
286 		fd = openat(dd, d->d_name, O_RDONLY|O_CLOEXEC);
287 		if (fd >= 0) {
288 			struct stat st;
289 			if (fstat(fd, &st) == 0 && st.st_size > 0)
290 				sendfile(fileno(stdout), fd, NULL, st.st_size);
291 			close(fd);
292 			done++;
293 		}
294 	}
295 
296 	for (i = 0; i < nfiles; i++)
297 		free(namelist[i]);
298 	free(namelist);
299 done:
300 	close(dd);
301 	return done;
302 }
303 #endif /* MOTDDIR_SUPPORT */
304 
305 /*
306  * Output the /etc/motd file.
307  *
308  * It determines the name of a login announcement file/dir and outputs it to the
309  * user's terminal at login time.  The MOTD_FILE configuration option is a
310  * colon-delimited list of filenames or directories.  An empty option disables
311  * message-of-the-day printing completely.
312  */
motd(void)313 static void motd(void)
314 {
315 	const char *mb;
316 	char *file, *list;
317 	int firstonly, done = 0;
318 
319 	firstonly = getlogindefs_bool("MOTD_FIRSTONLY", 0);
320 
321 	mb = getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE);
322 	if (!mb || !*mb)
323 		return;
324 
325 	list = xstrdup(mb);
326 
327 	for (file = strtok(list, ":"); file; file = strtok(NULL, ":")) {
328 		struct stat st;
329 
330 		if (stat(file, &st) < 0)
331 			continue;
332 #ifdef MOTDDIR_SUPPORT
333 		if (S_ISDIR(st.st_mode))
334 			done += motddir(file);
335 #endif
336 		if (S_ISREG(st.st_mode) && st.st_size > 0) {
337 			int fd = open(file, O_RDONLY, 0);
338 			if (fd >= 0)
339 				sendfile(fileno(stdout), fd, NULL, st.st_size);
340 			close(fd);
341 			done++;
342 		}
343 		if (firstonly && done)
344 			break;
345 	}
346 	free(list);
347 }
348 
349 /*
350  * Nice and simple code provided by Linus Torvalds 16-Feb-93.
351  * Non-blocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
352  *
353  * He writes: "Login performs open() on a tty in a blocking mode.
354  * In some cases it may make login wait in open() for carrier infinitely,
355  * for example if the line is a simplistic case of a three-wire serial
356  * connection. I believe login should open the line in non-blocking mode,
357  * leaving the decision to make a connection to getty (where it actually
358  * belongs)."
359  */
open_tty(const char * tty)360 static void open_tty(const char *tty)
361 {
362 	int i, fd, flags;
363 
364 	fd = open(tty, O_RDWR | O_NONBLOCK);
365 	if (fd == -1) {
366 		syslog(LOG_ERR, _("FATAL: can't reopen tty: %m"));
367 		sleepexit(EXIT_FAILURE);
368 	}
369 
370 	if (!isatty(fd)) {
371 		close(fd);
372 		syslog(LOG_ERR, _("FATAL: %s is not a terminal"), tty);
373 		sleepexit(EXIT_FAILURE);
374 	}
375 
376 	flags = fcntl(fd, F_GETFL);
377 	flags &= ~O_NONBLOCK;
378 	fcntl(fd, F_SETFL, flags);
379 
380 	for (i = 0; i < fd; i++)
381 		close(i);
382 	for (i = 0; i < 3; i++)
383 		if (fd != i)
384 			dup2(fd, i);
385 	if (fd >= 3)
386 		close(fd);
387 }
388 
389 #define chown_err(_what, _uid, _gid) \
390 		syslog(LOG_ERR, _("chown (%s, %lu, %lu) failed: %m"), \
391 			(_what), (unsigned long) (_uid), (unsigned long) (_gid))
392 
393 #define chmod_err(_what, _mode) \
394 		syslog(LOG_ERR, _("chmod (%s, %u) failed: %m"), (_what), (_mode))
395 
chown_tty(struct login_context * cxt)396 static void chown_tty(struct login_context *cxt)
397 {
398 	const char *grname;
399 	uid_t uid = cxt->pwd->pw_uid;
400 	gid_t gid = cxt->pwd->pw_gid;
401 
402 	grname = getlogindefs_str("TTYGROUP", TTYGRPNAME);
403 	if (grname && *grname) {
404 		struct group *gr = getgrnam(grname);
405 		if (gr)	/* group by name */
406 			gid = gr->gr_gid;
407 		else	/* group by ID */
408 			gid = (gid_t) getlogindefs_num("TTYGROUP", gid);
409 	}
410 	if (fchown(0, uid, gid))				/* tty */
411 		chown_err(cxt->tty_name, uid, gid);
412 	if (fchmod(0, cxt->tty_mode))
413 		chmod_err(cxt->tty_name, cxt->tty_mode);
414 
415 #ifdef LOGIN_CHOWN_VCS
416 	if (is_consoletty(0)) {
417 		if (chown(cxt->vcsn, uid, gid))			/* vcs */
418 			chown_err(cxt->vcsn, uid, gid);
419 		if (chmod(cxt->vcsn, cxt->tty_mode))
420 			chmod_err(cxt->vcsn, cxt->tty_mode);
421 
422 		if (chown(cxt->vcsan, uid, gid))		/* vcsa */
423 			chown_err(cxt->vcsan, uid, gid);
424 		if (chmod(cxt->vcsan, cxt->tty_mode))
425 			chmod_err(cxt->vcsan, cxt->tty_mode);
426 	}
427 #endif
428 }
429 
430 /*
431  * Reads the current terminal path and initializes cxt->tty_* variables.
432  */
init_tty(struct login_context * cxt)433 static void init_tty(struct login_context *cxt)
434 {
435 	struct stat st;
436 	struct termios tt, ttt;
437 
438 	cxt->tty_mode = (mode_t) getlogindefs_num("TTYPERM", TTY_MODE);
439 
440 	get_terminal_name(&cxt->tty_path, &cxt->tty_name, &cxt->tty_number);
441 
442 	/*
443 	 * In case login is suid it was possible to use a hardlink as stdin
444 	 * and exploit races for a local root exploit. (Wojciech Purczynski).
445 	 *
446 	 * More precisely, the problem is  ttyn := ttyname(0); ...; chown(ttyn);
447 	 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
448 	 * All of this is a problem only when login is suid, which it isn't.
449 	 */
450 	if (!cxt->tty_path || !*cxt->tty_path ||
451 	    lstat(cxt->tty_path, &st) != 0 || !S_ISCHR(st.st_mode) ||
452 	    (st.st_nlink > 1 && strncmp(cxt->tty_path, "/dev/", 5) != 0) ||
453 	    access(cxt->tty_path, R_OK | W_OK) != 0) {
454 
455 		syslog(LOG_ERR, _("FATAL: bad tty"));
456 		sleepexit(EXIT_FAILURE);
457 	}
458 
459 #ifdef LOGIN_CHOWN_VCS
460 	if (cxt->tty_number) {
461 		/* find names of Virtual Console devices, for later mode change */
462 		snprintf(cxt->vcsn, sizeof(cxt->vcsn), "/dev/vcs%s", cxt->tty_number);
463 		snprintf(cxt->vcsan, sizeof(cxt->vcsan), "/dev/vcsa%s", cxt->tty_number);
464 	}
465 #endif
466 
467 	tcgetattr(0, &tt);
468 	ttt = tt;
469 	ttt.c_cflag &= ~HUPCL;
470 
471 	if ((fchown(0, 0, 0) || fchmod(0, cxt->tty_mode)) && errno != EROFS) {
472 
473 		syslog(LOG_ERR, _("FATAL: %s: change permissions failed: %m"),
474 				cxt->tty_path);
475 		sleepexit(EXIT_FAILURE);
476 	}
477 
478 	/* Kill processes left on this tty */
479 	tcsetattr(0, TCSANOW, &ttt);
480 
481 	/*
482 	 * Let's close file descriptors before vhangup
483 	 * https://lkml.org/lkml/2012/6/5/145
484 	 */
485 	close(STDIN_FILENO);
486 	close(STDOUT_FILENO);
487 	close(STDERR_FILENO);
488 
489 	signal(SIGHUP, SIG_IGN);	/* so vhangup() won't kill us */
490 	vhangup();
491 	signal(SIGHUP, SIG_DFL);
492 
493 	/* open stdin,stdout,stderr to the tty */
494 	open_tty(cxt->tty_path);
495 
496 	/* restore tty modes */
497 	tcsetattr(0, TCSAFLUSH, &tt);
498 }
499 
500 
501 /*
502  * Logs failed login attempts in _PATH_BTMP, if it exists.
503  * Must be called only with username the name of an actual user.
504  * The most common login failure is to give password instead of username.
505  */
log_btmp(struct login_context * cxt)506 static void log_btmp(struct login_context *cxt)
507 {
508 	struct utmpx ut;
509 	struct timeval tv;
510 
511 	memset(&ut, 0, sizeof(ut));
512 
513 	str2memcpy(ut.ut_user,
514 		cxt->username ? cxt->username : "(unknown)",
515 		sizeof(ut.ut_user));
516 
517 	if (cxt->tty_number)
518 		str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
519 	if (cxt->tty_name)
520 		str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
521 
522 	gettimeofday(&tv, NULL);
523 	ut.ut_tv.tv_sec = tv.tv_sec;
524 	ut.ut_tv.tv_usec = tv.tv_usec;
525 
526 	ut.ut_type = LOGIN_PROCESS;	/* XXX doesn't matter */
527 	ut.ut_pid = cxt->pid;
528 
529 	if (cxt->hostname) {
530 		str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
531 		if (*cxt->hostaddress)
532 			memcpy(&ut.ut_addr_v6, cxt->hostaddress,
533 			       sizeof(ut.ut_addr_v6));
534 	}
535 
536 	updwtmpx(_PATH_BTMP, &ut);
537 }
538 
539 
540 #ifdef HAVE_LIBAUDIT
log_audit(struct login_context * cxt,int status)541 static void log_audit(struct login_context *cxt, int status)
542 {
543 	int audit_fd;
544 	struct passwd *pwd = cxt->pwd;
545 
546 	audit_fd = audit_open();
547 	if (audit_fd == -1)
548 		return;
549 	if (!pwd && cxt->username)
550 		pwd = getpwnam(cxt->username);
551 
552 	audit_log_acct_message(audit_fd,
553 			       AUDIT_USER_LOGIN,
554 			       NULL,
555 			       "login",
556 			       cxt->username ? cxt->username : "(unknown)",
557 			       pwd ? pwd->pw_uid : (unsigned int) -1,
558 			       cxt->hostname,
559 			       NULL,
560 			       cxt->tty_name,
561 			       status);
562 
563 	close(audit_fd);
564 }
565 #else				/* !HAVE_LIBAUDIT */
566 # define log_audit(cxt, status)
567 #endif				/* HAVE_LIBAUDIT */
568 
log_lastlog(struct login_context * cxt)569 static void log_lastlog(struct login_context *cxt)
570 {
571 	struct sigaction sa, oldsa_xfsz;
572 	struct lastlog ll;
573 	off_t offset;
574 	time_t t;
575 	int fd;
576 
577 	if (!cxt->pwd)
578 		return;
579 
580 	if (cxt->pwd->pw_uid > (uid_t) getlogindefs_num("LASTLOG_UID_MAX", ULONG_MAX))
581 		return;
582 
583 	/* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
584 	memset(&sa, 0, sizeof(sa));
585 	sa.sa_handler = SIG_IGN;
586 	sigaction(SIGXFSZ, &sa, &oldsa_xfsz);
587 
588 	fd = open(_PATH_LASTLOG, O_RDWR, 0);
589 	if (fd < 0)
590 		goto done;
591 	offset = cxt->pwd->pw_uid * sizeof(ll);
592 
593 	/*
594 	 * Print last log message.
595 	 */
596 	if (!cxt->quiet) {
597 		if ((pread(fd, (void *)&ll, sizeof(ll), offset) == sizeof(ll)) &&
598 		    ll.ll_time != 0) {
599 			char time_string[CTIME_BUFSIZ];
600 
601 			time_t ll_time = (time_t) ll.ll_time;
602 
603 			ctime_r(&ll_time, time_string);
604 			printf(_("Last login: %.*s "), 24 - 5, time_string);
605 			if (*ll.ll_host != '\0')
606 				printf(_("from %.*s\n"),
607 				       (int)sizeof(ll.ll_host), ll.ll_host);
608 			else
609 				printf(_("on %.*s\n"),
610 				       (int)sizeof(ll.ll_line), ll.ll_line);
611 		}
612 	}
613 
614 	memset((char *)&ll, 0, sizeof(ll));
615 
616 	time(&t);
617 	ll.ll_time = t;		/* ll_time is always 32bit */
618 
619 	if (cxt->tty_name)
620 		str2memcpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
621 	if (cxt->hostname)
622 		str2memcpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));
623 
624 	if (pwrite(fd, (void *)&ll, sizeof(ll), offset) != sizeof(ll))
625 		warn(_("write lastlog failed"));
626 done:
627 	if (fd >= 0)
628 		close(fd);
629 
630 	sigaction(SIGXFSZ, &oldsa_xfsz, NULL);		/* restore original setting */
631 }
632 
633 /*
634  * Update wtmp and utmp logs.
635  */
log_utmp(struct login_context * cxt)636 static void log_utmp(struct login_context *cxt)
637 {
638 	struct utmpx ut;
639 	struct utmpx *utp;
640 	struct timeval tv;
641 
642 	utmpxname(_PATH_UTMP);
643 	setutxent();
644 
645 	/* Find pid in utmp.
646 	 *
647 	 * login sometimes overwrites the runlevel entry in /var/run/utmp,
648 	 * confusing sysvinit. I added a test for the entry type, and the
649 	 * problem was gone. (In a runlevel entry, st_pid is not really a pid
650 	 * but some number calculated from the previous and current runlevel.)
651 	 * -- Michael Riepe <michael@stud.uni-hannover.de>
652 	 */
653 	while ((utp = getutxent()))
654 		if (utp->ut_pid == cxt->pid
655 		    && utp->ut_type >= INIT_PROCESS
656 		    && utp->ut_type <= DEAD_PROCESS)
657 			break;
658 
659 	/* If we can't find a pre-existing entry by pid, try by line.
660 	 * BSD network daemons may rely on this. */
661 	if (utp == NULL && cxt->tty_name) {
662 		setutxent();
663 		ut.ut_type = LOGIN_PROCESS;
664 		str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
665 		utp = getutxline(&ut);
666 	}
667 
668 	/* If we can't find a pre-existing entry by pid and line, try it by id.
669 	 * Very stupid telnetd daemons don't set up utmp at all. (kzak) */
670 	if (utp == NULL && cxt->tty_number) {
671 	     setutxent();
672 	     ut.ut_type = DEAD_PROCESS;
673 	     str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
674 	     utp = getutxid(&ut);
675 	}
676 
677 	if (utp)
678 		memcpy(&ut, utp, sizeof(ut));
679 	else
680 		/* some gettys/telnetds don't initialize utmp... */
681 		memset(&ut, 0, sizeof(ut));
682 
683 	if (cxt->tty_number && ut.ut_id[0] == 0)
684 		str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
685 	if (cxt->username)
686 		str2memcpy(ut.ut_user, cxt->username, sizeof(ut.ut_user));
687 	if (cxt->tty_name)
688 		str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
689 
690 	gettimeofday(&tv, NULL);
691 	ut.ut_tv.tv_sec = tv.tv_sec;
692 	ut.ut_tv.tv_usec = tv.tv_usec;
693 	ut.ut_type = USER_PROCESS;
694 	ut.ut_pid = cxt->pid;
695 	if (cxt->hostname) {
696 		str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
697 		if (*cxt->hostaddress)
698 			memcpy(&ut.ut_addr_v6, cxt->hostaddress,
699 			       sizeof(ut.ut_addr_v6));
700 	}
701 
702 	pututxline(&ut);
703 	endutxent();
704 
705 	updwtmpx(_PATH_WTMP, &ut);
706 }
707 
log_syslog(struct login_context * cxt)708 static void log_syslog(struct login_context *cxt)
709 {
710 	struct passwd *pwd = cxt->pwd;
711 
712 	if (!cxt->tty_name)
713 		return;
714 
715 	if (!strncmp(cxt->tty_name, "ttyS", 4))
716 		syslog(LOG_INFO, _("DIALUP AT %s BY %s"),
717 		       cxt->tty_name, pwd->pw_name);
718 
719 	if (!pwd->pw_uid) {
720 		if (cxt->hostname)
721 			syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
722 			       cxt->tty_name, cxt->hostname);
723 		else
724 			syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), cxt->tty_name);
725 	} else {
726 		if (cxt->hostname)
727 			syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"),
728 			       cxt->tty_name, pwd->pw_name, cxt->hostname);
729 		else
730 			syslog(LOG_INFO, _("LOGIN ON %s BY %s"), cxt->tty_name,
731 			       pwd->pw_name);
732 	}
733 }
734 
735 /* encapsulate stupid "void **" pam_get_item() API */
loginpam_get_username(pam_handle_t * pamh,const char ** name)736 static int loginpam_get_username(pam_handle_t *pamh, const char **name)
737 {
738 	const void *item = (const void *)*name;
739 	int rc;
740 	rc = pam_get_item(pamh, PAM_USER, &item);
741 	*name = (const char *)item;
742 	return rc;
743 }
744 
loginpam_err(pam_handle_t * pamh,int retcode)745 static void loginpam_err(pam_handle_t *pamh, int retcode)
746 {
747 	const char *msg = pam_strerror(pamh, retcode);
748 
749 	if (msg) {
750 		fprintf(stderr, "\n%s\n", msg);
751 		syslog(LOG_ERR, "%s", msg);
752 	}
753 	pam_end(pamh, retcode);
754 	sleepexit(EXIT_FAILURE);
755 }
756 
757 /*
758  * Composes "<host> login: " string; or returns "login: " if -H is given or
759  * LOGIN_PLAIN_PROMPT=yes configured.
760  */
loginpam_get_prompt(struct login_context * cxt)761 static const char *loginpam_get_prompt(struct login_context *cxt)
762 {
763 	const char *host;
764 	char *prompt, *dflt_prompt = _("login: ");
765 	size_t sz;
766 
767 	if (cxt->nohost)
768 		return dflt_prompt;	/* -H on command line */
769 
770 	if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
771 		return dflt_prompt;
772 
773 	if (!(host = get_thishost(cxt, NULL)))
774 		return dflt_prompt;
775 
776 	sz = strlen(host) + 1 + strlen(dflt_prompt) + 1;
777 	prompt = xmalloc(sz);
778 	snprintf(prompt, sz, "%s %s", host, dflt_prompt);
779 
780 	return prompt;
781 }
782 
init_loginpam(struct login_context * cxt)783 static pam_handle_t *init_loginpam(struct login_context *cxt)
784 {
785 	pam_handle_t *pamh = NULL;
786 	int rc;
787 
788 	/*
789 	 * username is initialized to NULL and if specified on the command line
790 	 * it is set.  Therefore, we are safe not setting it to anything.
791 	 */
792 	rc = pam_start(cxt->remote ? "remote" : "login",
793 		       cxt->username, &cxt->conv, &pamh);
794 	if (rc != PAM_SUCCESS) {
795 		warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, rc));
796 		syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
797 		       pam_strerror(pamh, rc));
798 		sleepexit(EXIT_FAILURE);
799 	}
800 
801 	/* hostname & tty are either set to NULL or their correct values,
802 	 * depending on how much we know. */
803 	rc = pam_set_item(pamh, PAM_RHOST, cxt->hostname);
804 	if (is_pam_failure(rc))
805 		loginpam_err(pamh, rc);
806 
807 	rc = pam_set_item(pamh, PAM_TTY, cxt->tty_name);
808 	if (is_pam_failure(rc))
809 		loginpam_err(pamh, rc);
810 
811 	/*
812 	 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
813 	 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
814 	 * an interface to specify the "Password: " string (yet).
815 	 */
816 	rc = pam_set_item(pamh, PAM_USER_PROMPT, loginpam_get_prompt(cxt));
817 	if (is_pam_failure(rc))
818 		loginpam_err(pamh, rc);
819 
820 	/* We don't need the original username. We have to follow PAM. */
821 	cxt->username = NULL;
822 	cxt->pamh = pamh;
823 
824 	return pamh;
825 }
826 
loginpam_auth(struct login_context * cxt)827 static void loginpam_auth(struct login_context *cxt)
828 {
829 	int rc, show_unknown;
830 	unsigned int retries, failcount = 0;
831 	const char *hostname = cxt->hostname ? cxt->hostname :
832 			       cxt->tty_name ? cxt->tty_name : "<unknown>";
833 	pam_handle_t *pamh = cxt->pamh;
834 
835 	/* if we didn't get a user on the command line, set it to NULL */
836 	loginpam_get_username(pamh, &cxt->username);
837 
838 	show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
839 	retries = getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES);
840 
841 	/*
842 	 * There may be better ways to deal with some of these conditions, but
843 	 * at least this way I don't think we'll be giving away information...
844 	 *
845 	 * Perhaps someday we can trust that all PAM modules will pay attention
846 	 * to failure count and get rid of LOGIN_MAX_TRIES?
847 	 */
848 	rc = pam_authenticate(pamh, 0);
849 
850 	while ((++failcount < retries) &&
851 	       ((rc == PAM_AUTH_ERR) ||
852 		(rc == PAM_USER_UNKNOWN) ||
853 		(rc == PAM_CRED_INSUFFICIENT) ||
854 		(rc == PAM_AUTHINFO_UNAVAIL))) {
855 
856 		if (rc == PAM_USER_UNKNOWN && !show_unknown)
857 			/*
858 			 * Logging unknown usernames may be a security issue if
859 			 * a user enters her password instead of her login name.
860 			 */
861 			cxt->username = NULL;
862 		else
863 			loginpam_get_username(pamh, &cxt->username);
864 
865 		syslog(LOG_NOTICE,
866 		       _("FAILED LOGIN %u FROM %s FOR %s, %s"),
867 		       failcount, hostname,
868 		       cxt->username ? cxt->username : "(unknown)",
869 		       pam_strerror(pamh, rc));
870 
871 		log_btmp(cxt);
872 		log_audit(cxt, 0);
873 
874 		fprintf(stderr, _("Login incorrect\n\n"));
875 
876 		pam_set_item(pamh, PAM_USER, NULL);
877 		rc = pam_authenticate(pamh, 0);
878 	}
879 
880 	if (is_pam_failure(rc)) {
881 
882 		if (rc == PAM_USER_UNKNOWN && !show_unknown)
883 			cxt->username = NULL;
884 		else
885 			loginpam_get_username(pamh, &cxt->username);
886 
887 		if (rc == PAM_MAXTRIES)
888 			syslog(LOG_NOTICE,
889 			       _("TOO MANY LOGIN TRIES (%u) FROM %s FOR %s, %s"),
890 			       failcount, hostname,
891 			       cxt->username ? cxt->username : "(unknown)",
892 			       pam_strerror(pamh, rc));
893 		else
894 			syslog(LOG_NOTICE,
895 			       _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
896 			       hostname,
897 			       cxt->username ? cxt->username : "(unknown)",
898 			       pam_strerror(pamh, rc));
899 
900 		log_btmp(cxt);
901 		log_audit(cxt, 0);
902 
903 		fprintf(stderr, _("\nLogin incorrect\n"));
904 		pam_end(pamh, rc);
905 		sleepexit(EXIT_SUCCESS);
906 	}
907 }
908 
loginpam_acct(struct login_context * cxt)909 static void loginpam_acct(struct login_context *cxt)
910 {
911 	int rc;
912 	pam_handle_t *pamh = cxt->pamh;
913 
914 	rc = pam_acct_mgmt(pamh, 0);
915 
916 	if (rc == PAM_NEW_AUTHTOK_REQD)
917 		rc = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
918 
919 	if (is_pam_failure(rc))
920 		loginpam_err(pamh, rc);
921 
922 	/*
923 	 * Grab the user information out of the password file for future use.
924 	 * First get the username that we are actually using, though.
925 	 */
926 	rc = loginpam_get_username(pamh, &cxt->username);
927 	if (is_pam_failure(rc))
928 		loginpam_err(pamh, rc);
929 
930 	if (!cxt->username || !*cxt->username) {
931 		warnx(_("\nSession setup problem, abort."));
932 		syslog(LOG_ERR, _("NULL user name. Abort."));
933 		pam_end(pamh, PAM_SYSTEM_ERR);
934 		sleepexit(EXIT_FAILURE);
935 	}
936 }
937 
938 /*
939  * Note that the position of the pam_setcred() call is discussable:
940  *
941  *  - the PAM docs recommend pam_setcred() before pam_open_session()
942  *  - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
943  *    uses pam_setcred() after pam_open_session()
944  *
945  * The old login versions (before year 2011) followed the RFC. This is probably
946  * not optimal, because there could be a dependence between some session modules
947  * and the user's credentials.
948  *
949  * The best is probably to follow openssh and call pam_setcred() before and
950  * after pam_open_session().                -- kzak@redhat.com (18-Nov-2011)
951  *
952  */
loginpam_session(struct login_context * cxt)953 static void loginpam_session(struct login_context *cxt)
954 {
955 	int rc;
956 	pam_handle_t *pamh = cxt->pamh;
957 
958 	rc = pam_setcred(pamh, PAM_ESTABLISH_CRED);
959 	if (is_pam_failure(rc))
960 		loginpam_err(pamh, rc);
961 
962 	rc = pam_open_session(pamh, cxt->quiet ? PAM_SILENT : 0);
963 	if (is_pam_failure(rc)) {
964 		pam_setcred(cxt->pamh, PAM_DELETE_CRED);
965 		loginpam_err(pamh, rc);
966 	}
967 
968 	rc = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
969 	if (is_pam_failure(rc)) {
970 		pam_close_session(pamh, 0);
971 		loginpam_err(pamh, rc);
972 	}
973 }
974 
975 /*
976  * Detach the controlling terminal, fork, restore syslog stuff, and create
977  * a new session.
978  */
fork_session(struct login_context * cxt)979 static void fork_session(struct login_context *cxt)
980 {
981 	struct sigaction sa, oldsa_hup, oldsa_term;
982 
983 	signal(SIGALRM, SIG_DFL);
984 	signal(SIGQUIT, SIG_DFL);
985 	signal(SIGTSTP, SIG_IGN);
986 
987 	memset(&sa, 0, sizeof(sa));
988 	sa.sa_handler = SIG_IGN;
989 	sigaction(SIGINT, &sa, NULL);
990 
991 	sigaction(SIGHUP, &sa, &oldsa_hup);	/* ignore when TIOCNOTTY */
992 
993 	/*
994 	 * Detach the controlling tty.
995 	 * We don't need the tty in a parent who only waits for a child.
996 	 * The child calls setsid() that detaches from the tty as well.
997 	 */
998 	ioctl(0, TIOCNOTTY, NULL);
999 
1000 	/*
1001 	 * We have to beware of SIGTERM, because leaving a PAM session
1002 	 * without pam_close_session() is a pretty bad thing.
1003 	 */
1004 	sa.sa_handler = sig_handler;
1005 	sigaction(SIGHUP, &sa, NULL);
1006 	sigaction(SIGTERM, &sa, &oldsa_term);
1007 
1008 	closelog();
1009 
1010 	/*
1011 	 * We must fork before setuid(), because we need to call
1012 	 * pam_close_session() as root.
1013 	 */
1014 	child_pid = fork();
1015 	if (child_pid < 0) {
1016 		warn(_("fork failed"));
1017 
1018 		pam_setcred(cxt->pamh, PAM_DELETE_CRED);
1019 		pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
1020 		sleepexit(EXIT_FAILURE);
1021 	}
1022 
1023 	if (child_pid) {
1024 		/*
1025 		 * parent - wait for child to finish, then clean up session
1026 		 */
1027 		close(0);
1028 		close(1);
1029 		close(2);
1030 		free_getlogindefs_data();
1031 
1032 		sa.sa_handler = SIG_IGN;
1033 		sigaction(SIGQUIT, &sa, NULL);
1034 		sigaction(SIGINT, &sa, NULL);
1035 
1036 		/* wait as long as any child is there */
1037 		while (wait(NULL) == -1 && errno == EINTR) ;
1038 		openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1039 
1040 		pam_setcred(cxt->pamh, PAM_DELETE_CRED);
1041 		pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
1042 		exit(EXIT_SUCCESS);
1043 	}
1044 
1045 	/*
1046 	 * child
1047 	 */
1048 	sigaction(SIGHUP, &oldsa_hup, NULL);		/* restore old state */
1049 	sigaction(SIGTERM, &oldsa_term, NULL);
1050 	if (got_sig)
1051 		exit(EXIT_FAILURE);
1052 
1053 	/*
1054 	 * Problem: if the user's shell is a shell like ash that doesn't do
1055 	 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1056 	 * process in the pgrp, will kill us.
1057 	 */
1058 
1059 	/* start new session */
1060 	setsid();
1061 
1062 	/* make sure we have a controlling tty */
1063 	open_tty(cxt->tty_path);
1064 	openlog("login", LOG_ODELAY, LOG_AUTHPRIV);	/* reopen */
1065 
1066 	/*
1067 	 * TIOCSCTTY: steal tty from other process group.
1068 	 */
1069 	if (ioctl(0, TIOCSCTTY, 1))
1070 		syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
1071 	signal(SIGINT, SIG_DFL);
1072 }
1073 
1074 /*
1075  * Initialize $TERM, $HOME, ...
1076  */
init_environ(struct login_context * cxt)1077 static void init_environ(struct login_context *cxt)
1078 {
1079 	struct passwd *pwd = cxt->pwd;
1080 	char *termenv, **env;
1081 	char tmp[PATH_MAX];
1082 	int len, i;
1083 
1084 	termenv = getenv("TERM");
1085 	if (termenv)
1086 		termenv = xstrdup(termenv);
1087 
1088 	/* destroy environment unless user has requested preservation (-p) */
1089 	if (!cxt->keep_env) {
1090 		environ = xmalloc(sizeof(char *));
1091 		memset(environ, 0, sizeof(char *));
1092 	}
1093 
1094 	xsetenv("HOME", pwd->pw_dir, 0);	/* legal to override */
1095 	xsetenv("USER", pwd->pw_name, 1);
1096 	xsetenv("SHELL", pwd->pw_shell, 1);
1097 	xsetenv("TERM", termenv ? termenv : "dumb", 1);
1098 	free(termenv);
1099 
1100 	if (pwd->pw_uid) {
1101 		if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH) != 0)
1102 			err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
1103 
1104 	} else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0 &&
1105 		   logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT) != 0) {
1106 			err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
1107 	}
1108 
1109 	/* mailx will give a funny error msg if you forget this one */
1110 	len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1111 	if (len > 0 && (size_t) len < sizeof(tmp))
1112 		xsetenv("MAIL", tmp, 0);
1113 
1114 	/* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1115 	 * not allow modifying it.
1116 	 */
1117 	xsetenv("LOGNAME", pwd->pw_name, 1);
1118 
1119 	env = pam_getenvlist(cxt->pamh);
1120 	for (i = 0; env && env[i]; i++)
1121 		putenv(env[i]);
1122 }
1123 
1124 /*
1125  * This is called for the -h option, initializes cxt->{hostname,hostaddress}.
1126  */
init_remote_info(struct login_context * cxt,char * remotehost)1127 static void init_remote_info(struct login_context *cxt, char *remotehost)
1128 {
1129 	const char *domain;
1130 	char *p;
1131 	struct addrinfo hints, *info = NULL;
1132 
1133 	cxt->remote = 1;
1134 
1135 	get_thishost(cxt, &domain);
1136 
1137 	if (domain && (p = strchr(remotehost, '.')) &&
1138 	    strcasecmp(p + 1, domain) == 0)
1139 		*p = '\0';
1140 
1141 	cxt->hostname = xstrdup(remotehost);
1142 
1143 	memset(&hints, 0, sizeof(hints));
1144 	hints.ai_flags = AI_ADDRCONFIG;
1145 	cxt->hostaddress[0] = 0;
1146 
1147 	if (getaddrinfo(cxt->hostname, NULL, &hints, &info) == 0 && info) {
1148 		if (info->ai_family == AF_INET) {
1149 			struct sockaddr_in *sa =
1150 				    (struct sockaddr_in *) info->ai_addr;
1151 
1152 			memcpy(cxt->hostaddress, &(sa->sin_addr), sizeof(sa->sin_addr));
1153 
1154 		} else if (info->ai_family == AF_INET6) {
1155 			struct sockaddr_in6 *sa =
1156 				     (struct sockaddr_in6 *) info->ai_addr;
1157 #ifdef IN6_IS_ADDR_V4MAPPED
1158 			if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) {
1159 				const uint8_t *bytes = sa->sin6_addr.s6_addr;
1160 				struct in_addr addr = { *(const in_addr_t *) (bytes + 12) };
1161 
1162 				memcpy(cxt->hostaddress, &addr, sizeof(struct in_addr));
1163 			} else
1164 #endif
1165 				memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr));
1166 		}
1167 		freeaddrinfo(info);
1168 	}
1169 }
1170 
usage(void)1171 static void __attribute__((__noreturn__)) usage(void)
1172 {
1173 	fputs(USAGE_HEADER, stdout);
1174 	printf(_(" %s [-p] [-h <host>] [-H] [[-f] <username>]\n"), program_invocation_short_name);
1175 	fputs(USAGE_SEPARATOR, stdout);
1176 	fputs(_("Begin a session on the system.\n"), stdout);
1177 
1178 	fputs(USAGE_OPTIONS, stdout);
1179 	puts(_(" -p             do not destroy the environment"));
1180 	puts(_(" -f             skip a login authentication"));
1181 	puts(_(" -h <host>      hostname to be used for utmp logging"));
1182 	puts(_(" -H             suppress hostname in the login prompt"));
1183 	printf("     --help     %s\n", USAGE_OPTSTR_HELP);
1184 	printf(" -V, --version  %s\n", USAGE_OPTSTR_VERSION);
1185 	printf(USAGE_MAN_TAIL("login(1)"));
1186 	exit(EXIT_SUCCESS);
1187 }
1188 
main(int argc,char ** argv)1189 int main(int argc, char **argv)
1190 {
1191 	int c;
1192 	char *childArgv[10];
1193 	char *buff;
1194 	int childArgc = 0;
1195 	int retcode;
1196 	struct sigaction act;
1197 	struct passwd *pwd;
1198 	static const int wanted_fds[] = {
1199 		STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO
1200 	};
1201 	struct login_context cxt = {
1202 		.tty_mode = TTY_MODE,		  /* tty chmod() */
1203 		.pid = getpid(),		  /* PID */
1204 #ifdef HAVE_SECURITY_PAM_MISC_H
1205 		.conv = { misc_conv, NULL }	  /* Linux-PAM conversation function */
1206 #elif defined(HAVE_SECURITY_OPENPAM_H)
1207 		.conv = { openpam_ttyconv, NULL } /* OpenPAM conversation function */
1208 #endif
1209 
1210 	};
1211 
1212 	/* the only two longopts to satisfy UL standards */
1213 	enum { HELP_OPTION = CHAR_MAX + 1 };
1214 	static const struct option longopts[] = {
1215 		{"help", no_argument, NULL, HELP_OPTION},
1216 		{"version", no_argument, NULL, 'V'},
1217 		{NULL, 0, NULL, 0}
1218 	};
1219 
1220 	timeout = (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);
1221 
1222 	setlocale(LC_ALL, "");
1223 	bindtextdomain(PACKAGE, LOCALEDIR);
1224 	textdomain(PACKAGE);
1225 
1226 	/* TRANSLATORS: The standard value for %u is 60. */
1227 	snprintf(timeout_msg, sizeof(timeout_msg),
1228 	    _("%s: timed out after %u seconds"),
1229 	    program_invocation_short_name, timeout);
1230 
1231 	signal(SIGALRM, timedout);
1232 	(void) sigaction(SIGALRM, NULL, &act);
1233 	act.sa_flags &= ~SA_RESTART;
1234 	sigaction(SIGALRM, &act, NULL);
1235 	alarm(timeout);
1236 	signal(SIGQUIT, SIG_IGN);
1237 	signal(SIGINT, SIG_IGN);
1238 
1239 	setpriority(PRIO_PROCESS, 0, 0);
1240 	initproctitle(argc, argv);
1241 
1242 	while ((c = getopt_long(argc, argv, "fHh:pV", longopts, NULL)) != -1)
1243 		switch (c) {
1244 		case 'f':
1245 			cxt.noauth = 1;
1246 			break;
1247 
1248 		case 'H':
1249 			cxt.nohost = 1;
1250 			break;
1251 
1252 		case 'h':
1253 			if (getuid()) {
1254 				fprintf(stderr,
1255 					_("login: -h is for superuser only\n"));
1256 				exit(EXIT_FAILURE);
1257 			}
1258 			init_remote_info(&cxt, optarg);
1259 			break;
1260 
1261 		case 'p':
1262 			cxt.keep_env = 1;
1263 			break;
1264 
1265 		case 'V':
1266 			print_version(EXIT_SUCCESS);
1267 		case HELP_OPTION:
1268 			usage();
1269 		default:
1270 			errtryhelp(EXIT_FAILURE);
1271 		}
1272 	argc -= optind;
1273 	argv += optind;
1274 
1275 	if (*argv) {
1276 		char *p = *argv;
1277 
1278 		/* username from command line */
1279 		cxt.cmd_username = xstrdup(p);
1280 		/* used temporary, it'll be replaced by username from PAM or/and cxt->pwd */
1281 		cxt.username = cxt.cmd_username;
1282 
1283 		/* Wipe the name - some people mistype their password here. */
1284 		/* (Of course we are too late, but perhaps this helps a little...) */
1285 		while (*p)
1286 			*p++ = ' ';
1287 	}
1288 
1289 	close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds));
1290 
1291 	setpgrp();	 /* set pgid to pid this means that setsid() will fail */
1292 	init_tty(&cxt);
1293 
1294 	openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1295 
1296 	init_loginpam(&cxt);
1297 
1298 	/* login -f, then the user has already been authenticated */
1299 	cxt.noauth = cxt.noauth && getuid() == 0 ? 1 : 0;
1300 
1301 	if (!cxt.noauth)
1302 		loginpam_auth(&cxt);
1303 
1304 	/*
1305 	 * Authentication may be skipped (for example, during krlogin, rlogin,
1306 	 * etc...), but it doesn't mean that we can skip other account checks.
1307 	 * The account could be disabled or the password has expired (although
1308 	 * the kerberos ticket is valid).      -- kzak@redhat.com (22-Feb-2006)
1309 	 */
1310 	loginpam_acct(&cxt);
1311 
1312 	cxt.pwd = xgetpwnam(cxt.username, &cxt.pwdbuf);
1313 	if (!cxt.pwd) {
1314 		warnx(_("\nSession setup problem, abort."));
1315 		syslog(LOG_ERR, _("Invalid user name \"%s\". Abort."),
1316 		       cxt.username);
1317 		pam_end(cxt.pamh, PAM_SYSTEM_ERR);
1318 		sleepexit(EXIT_FAILURE);
1319 	}
1320 
1321 	pwd = cxt.pwd;
1322 	cxt.username = pwd->pw_name;
1323 
1324 	/*
1325 	 * Initialize the supplementary group list. This should be done before
1326 	 * pam_setcred, because PAM modules might add groups during that call.
1327 	 *
1328 	 * For root we don't call initgroups, instead we call setgroups with
1329 	 * group 0. This avoids the need to step through the whole group file,
1330 	 * which can cause problems if NIS, NIS+, LDAP or something similar
1331 	 * is used and the machine has network problems.
1332 	 */
1333 	retcode = pwd->pw_uid ? initgroups(cxt.username, pwd->pw_gid) :	/* user */
1334 			        setgroups(0, NULL);			/* root */
1335 	if (retcode < 0) {
1336 		syslog(LOG_ERR, _("groups initialization failed: %m"));
1337 		warnx(_("\nSession setup problem, abort."));
1338 		pam_end(cxt.pamh, PAM_SYSTEM_ERR);
1339 		sleepexit(EXIT_FAILURE);
1340 	}
1341 
1342 	cxt.quiet = get_hushlogin_status(pwd, 1) == 1 ? 1 : 0;
1343 
1344 	/*
1345 	 * Open PAM session (after successful authentication and account check).
1346 	 */
1347 	loginpam_session(&cxt);
1348 
1349 	/* committed to login -- turn off timeout */
1350 	alarm((unsigned int)0);
1351 
1352 	endpwent();
1353 
1354 	log_utmp(&cxt);
1355 	log_audit(&cxt, 1);
1356 	log_lastlog(&cxt);
1357 
1358 	chown_tty(&cxt);
1359 
1360 	if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
1361 		syslog(LOG_ALERT, _("setgid() failed"));
1362 		exit(EXIT_FAILURE);
1363 	}
1364 
1365 	if (pwd->pw_shell == NULL || *pwd->pw_shell == '\0')
1366 		pwd->pw_shell = _PATH_BSHELL;
1367 
1368 	init_environ(&cxt);		/* init $HOME, $TERM ... */
1369 
1370 	setproctitle("login", cxt.username);
1371 
1372 	log_syslog(&cxt);
1373 
1374 	if (!cxt.quiet) {
1375 		motd();
1376 
1377 #ifdef LOGIN_STAT_MAIL
1378 		/*
1379 		 * This turns out to be a bad idea: when the mail spool
1380 		 * is NFS mounted, and the NFS connection hangs, the
1381 		 * login hangs, even root cannot login.
1382 		 * Checking for mail should be done from the shell.
1383 		 */
1384 		{
1385 			struct stat st;
1386 			char *mail;
1387 
1388 			mail = getenv("MAIL");
1389 			if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1390 				if (st.st_mtime > st.st_atime)
1391 					printf(_("You have new mail.\n"));
1392 				else
1393 					printf(_("You have mail.\n"));
1394 			}
1395 		}
1396 #endif
1397 	}
1398 
1399 	/*
1400 	 * Detach the controlling terminal, fork, and create a new session
1401 	 * and reinitialize syslog stuff.
1402 	 */
1403 	fork_session(&cxt);
1404 
1405 	/* discard permissions last so we can't get killed and drop core */
1406 	if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1407 		syslog(LOG_ALERT, _("setuid() failed"));
1408 		exit(EXIT_FAILURE);
1409 	}
1410 
1411 	/* wait until here to change directory! */
1412 	if (chdir(pwd->pw_dir) < 0) {
1413 		warn(_("%s: change directory failed"), pwd->pw_dir);
1414 
1415 		if (!getlogindefs_bool("DEFAULT_HOME", 1))
1416 			exit(0);
1417 		if (chdir("/"))
1418 			exit(EXIT_FAILURE);
1419 		pwd->pw_dir = "/";
1420 		printf(_("Logging in with home = \"/\".\n"));
1421 	}
1422 
1423 	/* if the shell field has a space: treat it like a shell script */
1424 	if (strchr(pwd->pw_shell, ' ')) {
1425 		xasprintf(&buff, "exec %s", pwd->pw_shell);
1426 		childArgv[childArgc++] = "/bin/sh";
1427 		childArgv[childArgc++] = "-sh";
1428 		childArgv[childArgc++] = "-c";
1429 		childArgv[childArgc++] = buff;
1430 	} else {
1431 		char tbuf[PATH_MAX + 2], *p;
1432 
1433 		tbuf[0] = '-';
1434 		xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1435 				    p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);
1436 
1437 		childArgv[childArgc++] = pwd->pw_shell;
1438 		childArgv[childArgc++] = xstrdup(tbuf);
1439 	}
1440 
1441 	childArgv[childArgc++] = NULL;
1442 
1443 	execvp(childArgv[0], childArgv + 1);
1444 
1445 	if (!strcmp(childArgv[0], "/bin/sh"))
1446 		warn(_("couldn't exec shell script"));
1447 	else
1448 		warn(_("no shell"));
1449 
1450 	exit(EXIT_SUCCESS);
1451 }
1452