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