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