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