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