]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/login.c
su: change error message
[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 <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 /* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
507 memset(&sa, 0, sizeof(sa));
508 sa.sa_handler = SIG_IGN;
509 sigaction(SIGXFSZ, &sa, &oldsa_xfsz);
510
511 fd = open(_PATH_LASTLOG, O_RDWR, 0);
512 if (fd < 0)
513 goto done;
514
515 if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1)
516 goto done;
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 if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1)
535 goto done;
536 }
537
538 memset((char *)&ll, 0, sizeof(ll));
539
540 time(&t);
541 ll.ll_time = t; /* ll_time is always 32bit */
542
543 if (cxt->tty_name)
544 str2memcpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
545 if (cxt->hostname)
546 str2memcpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));
547
548 if (write_all(fd, (char *)&ll, sizeof(ll)))
549 warn(_("write lastlog failed"));
550 done:
551 if (fd >= 0)
552 close(fd);
553
554 sigaction(SIGXFSZ, &oldsa_xfsz, NULL); /* restore original setting */
555 }
556
557 /*
558 * Update wtmp and utmp logs.
559 */
560 static void log_utmp(struct login_context *cxt)
561 {
562 struct utmpx ut;
563 struct utmpx *utp;
564 struct timeval tv;
565
566 utmpxname(_PATH_UTMP);
567 setutxent();
568
569 /* Find pid in utmp.
570 *
571 * login sometimes overwrites the runlevel entry in /var/run/utmp,
572 * confusing sysvinit. I added a test for the entry type, and the
573 * problem was gone. (In a runlevel entry, st_pid is not really a pid
574 * but some number calculated from the previous and current runlevel.)
575 * -- Michael Riepe <michael@stud.uni-hannover.de>
576 */
577 while ((utp = getutxent()))
578 if (utp->ut_pid == cxt->pid
579 && utp->ut_type >= INIT_PROCESS
580 && utp->ut_type <= DEAD_PROCESS)
581 break;
582
583 /* If we can't find a pre-existing entry by pid, try by line.
584 * BSD network daemons may rely on this. */
585 if (utp == NULL && cxt->tty_name) {
586 setutxent();
587 ut.ut_type = LOGIN_PROCESS;
588 str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
589 utp = getutxline(&ut);
590 }
591
592 /* If we can't find a pre-existing entry by pid and line, try it by id.
593 * Very stupid telnetd daemons don't set up utmp at all. (kzak) */
594 if (utp == NULL && cxt->tty_number) {
595 setutxent();
596 ut.ut_type = DEAD_PROCESS;
597 str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
598 utp = getutxid(&ut);
599 }
600
601 if (utp)
602 memcpy(&ut, utp, sizeof(ut));
603 else
604 /* some gettys/telnetds don't initialize utmp... */
605 memset(&ut, 0, sizeof(ut));
606
607 if (cxt->tty_number && ut.ut_id[0] == 0)
608 str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
609 if (cxt->username)
610 str2memcpy(ut.ut_user, cxt->username, sizeof(ut.ut_user));
611 if (cxt->tty_name)
612 str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
613
614 gettimeofday(&tv, NULL);
615 ut.ut_tv.tv_sec = tv.tv_sec;
616 ut.ut_tv.tv_usec = tv.tv_usec;
617 ut.ut_type = USER_PROCESS;
618 ut.ut_pid = cxt->pid;
619 if (cxt->hostname) {
620 str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
621 if (*cxt->hostaddress)
622 memcpy(&ut.ut_addr_v6, cxt->hostaddress,
623 sizeof(ut.ut_addr_v6));
624 }
625
626 pututxline(&ut);
627 endutxent();
628
629 updwtmpx(_PATH_WTMP, &ut);
630 }
631
632 static void log_syslog(struct login_context *cxt)
633 {
634 struct passwd *pwd = cxt->pwd;
635
636 if (!cxt->tty_name)
637 return;
638
639 if (!strncmp(cxt->tty_name, "ttyS", 4))
640 syslog(LOG_INFO, _("DIALUP AT %s BY %s"),
641 cxt->tty_name, pwd->pw_name);
642
643 if (!pwd->pw_uid) {
644 if (cxt->hostname)
645 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
646 cxt->tty_name, cxt->hostname);
647 else
648 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), cxt->tty_name);
649 } else {
650 if (cxt->hostname)
651 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"),
652 cxt->tty_name, pwd->pw_name, cxt->hostname);
653 else
654 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), cxt->tty_name,
655 pwd->pw_name);
656 }
657 }
658
659 /* encapsulate stupid "void **" pam_get_item() API */
660 static int loginpam_get_username(pam_handle_t *pamh, const char **name)
661 {
662 const void *item = (const void *)*name;
663 int rc;
664 rc = pam_get_item(pamh, PAM_USER, &item);
665 *name = (const char *)item;
666 return rc;
667 }
668
669 static void loginpam_err(pam_handle_t *pamh, int retcode)
670 {
671 const char *msg = pam_strerror(pamh, retcode);
672
673 if (msg) {
674 fprintf(stderr, "\n%s\n", msg);
675 syslog(LOG_ERR, "%s", msg);
676 }
677 pam_end(pamh, retcode);
678 sleepexit(EXIT_FAILURE);
679 }
680
681 /*
682 * Composes "<host> login: " string; or returns "login: " if -H is given or
683 * LOGIN_PLAIN_PROMPT=yes configured.
684 */
685 static const char *loginpam_get_prompt(struct login_context *cxt)
686 {
687 const char *host;
688 char *prompt, *dflt_prompt = _("login: ");
689 size_t sz;
690
691 if (cxt->nohost)
692 return dflt_prompt; /* -H on command line */
693
694 if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
695 return dflt_prompt;
696
697 if (!(host = get_thishost(cxt, NULL)))
698 return dflt_prompt;
699
700 sz = strlen(host) + 1 + strlen(dflt_prompt) + 1;
701 prompt = xmalloc(sz);
702 snprintf(prompt, sz, "%s %s", host, dflt_prompt);
703
704 return prompt;
705 }
706
707 static pam_handle_t *init_loginpam(struct login_context *cxt)
708 {
709 pam_handle_t *pamh = NULL;
710 int rc;
711
712 /*
713 * username is initialized to NULL and if specified on the command line
714 * it is set. Therefore, we are safe not setting it to anything.
715 */
716 rc = pam_start(cxt->remote ? "remote" : "login",
717 cxt->username, &cxt->conv, &pamh);
718 if (rc != PAM_SUCCESS) {
719 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, rc));
720 syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
721 pam_strerror(pamh, rc));
722 sleepexit(EXIT_FAILURE);
723 }
724
725 /* hostname & tty are either set to NULL or their correct values,
726 * depending on how much we know. */
727 rc = pam_set_item(pamh, PAM_RHOST, cxt->hostname);
728 if (is_pam_failure(rc))
729 loginpam_err(pamh, rc);
730
731 rc = pam_set_item(pamh, PAM_TTY, cxt->tty_name);
732 if (is_pam_failure(rc))
733 loginpam_err(pamh, rc);
734
735 /*
736 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
737 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
738 * an interface to specify the "Password: " string (yet).
739 */
740 rc = pam_set_item(pamh, PAM_USER_PROMPT, loginpam_get_prompt(cxt));
741 if (is_pam_failure(rc))
742 loginpam_err(pamh, rc);
743
744 /* We don't need the original username. We have to follow PAM. */
745 cxt->username = NULL;
746 cxt->pamh = pamh;
747
748 return pamh;
749 }
750
751 static void loginpam_auth(struct login_context *cxt)
752 {
753 int rc, show_unknown;
754 unsigned int retries, failcount = 0;
755 const char *hostname = cxt->hostname ? cxt->hostname :
756 cxt->tty_name ? cxt->tty_name : "<unknown>";
757 pam_handle_t *pamh = cxt->pamh;
758
759 /* if we didn't get a user on the command line, set it to NULL */
760 loginpam_get_username(pamh, &cxt->username);
761
762 show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
763 retries = getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES);
764
765 /*
766 * There may be better ways to deal with some of these conditions, but
767 * at least this way I don't think we'll be giving away information...
768 *
769 * Perhaps someday we can trust that all PAM modules will pay attention
770 * to failure count and get rid of LOGIN_MAX_TRIES?
771 */
772 rc = pam_authenticate(pamh, 0);
773
774 while ((++failcount < retries) &&
775 ((rc == PAM_AUTH_ERR) ||
776 (rc == PAM_USER_UNKNOWN) ||
777 (rc == PAM_CRED_INSUFFICIENT) ||
778 (rc == PAM_AUTHINFO_UNAVAIL))) {
779
780 if (rc == PAM_USER_UNKNOWN && !show_unknown)
781 /*
782 * Logging unknown usernames may be a security issue if
783 * a user enters her password instead of her login name.
784 */
785 cxt->username = NULL;
786 else
787 loginpam_get_username(pamh, &cxt->username);
788
789 syslog(LOG_NOTICE,
790 _("FAILED LOGIN %u FROM %s FOR %s, %s"),
791 failcount, hostname,
792 cxt->username ? cxt->username : "(unknown)",
793 pam_strerror(pamh, rc));
794
795 log_btmp(cxt);
796 log_audit(cxt, 0);
797
798 fprintf(stderr, _("Login incorrect\n\n"));
799
800 pam_set_item(pamh, PAM_USER, NULL);
801 rc = pam_authenticate(pamh, 0);
802 }
803
804 if (is_pam_failure(rc)) {
805
806 if (rc == PAM_USER_UNKNOWN && !show_unknown)
807 cxt->username = NULL;
808 else
809 loginpam_get_username(pamh, &cxt->username);
810
811 if (rc == PAM_MAXTRIES)
812 syslog(LOG_NOTICE,
813 _("TOO MANY LOGIN TRIES (%u) FROM %s FOR %s, %s"),
814 failcount, hostname,
815 cxt->username ? cxt->username : "(unknown)",
816 pam_strerror(pamh, rc));
817 else
818 syslog(LOG_NOTICE,
819 _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
820 hostname,
821 cxt->username ? cxt->username : "(unknown)",
822 pam_strerror(pamh, rc));
823
824 log_btmp(cxt);
825 log_audit(cxt, 0);
826
827 fprintf(stderr, _("\nLogin incorrect\n"));
828 pam_end(pamh, rc);
829 sleepexit(EXIT_SUCCESS);
830 }
831 }
832
833 static void loginpam_acct(struct login_context *cxt)
834 {
835 int rc;
836 pam_handle_t *pamh = cxt->pamh;
837
838 rc = pam_acct_mgmt(pamh, 0);
839
840 if (rc == PAM_NEW_AUTHTOK_REQD)
841 rc = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
842
843 if (is_pam_failure(rc))
844 loginpam_err(pamh, rc);
845
846 /*
847 * Grab the user information out of the password file for future use.
848 * First get the username that we are actually using, though.
849 */
850 rc = loginpam_get_username(pamh, &cxt->username);
851 if (is_pam_failure(rc))
852 loginpam_err(pamh, rc);
853
854 if (!cxt->username || !*cxt->username) {
855 warnx(_("\nSession setup problem, abort."));
856 syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
857 __FUNCTION__, __LINE__);
858 pam_end(pamh, PAM_SYSTEM_ERR);
859 sleepexit(EXIT_FAILURE);
860 }
861 }
862
863 /*
864 * Note that the position of the pam_setcred() call is discussable:
865 *
866 * - the PAM docs recommend pam_setcred() before pam_open_session()
867 * - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
868 * uses pam_setcred() after pam_open_session()
869 *
870 * The old login versions (before year 2011) followed the RFC. This is probably
871 * not optimal, because there could be a dependence between some session modules
872 * and the user's credentials.
873 *
874 * The best is probably to follow openssh and call pam_setcred() before and
875 * after pam_open_session(). -- kzak@redhat.com (18-Nov-2011)
876 *
877 */
878 static void loginpam_session(struct login_context *cxt)
879 {
880 int rc;
881 pam_handle_t *pamh = cxt->pamh;
882
883 rc = pam_setcred(pamh, PAM_ESTABLISH_CRED);
884 if (is_pam_failure(rc))
885 loginpam_err(pamh, rc);
886
887 rc = pam_open_session(pamh, 0);
888 if (is_pam_failure(rc)) {
889 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
890 loginpam_err(pamh, rc);
891 }
892
893 rc = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
894 if (is_pam_failure(rc)) {
895 pam_close_session(pamh, 0);
896 loginpam_err(pamh, rc);
897 }
898 }
899
900 /*
901 * Detach the controlling terminal, fork, restore syslog stuff, and create
902 * a new session.
903 */
904 static void fork_session(struct login_context *cxt)
905 {
906 struct sigaction sa, oldsa_hup, oldsa_term;
907
908 signal(SIGALRM, SIG_DFL);
909 signal(SIGQUIT, SIG_DFL);
910 signal(SIGTSTP, SIG_IGN);
911
912 memset(&sa, 0, sizeof(sa));
913 sa.sa_handler = SIG_IGN;
914 sigaction(SIGINT, &sa, NULL);
915
916 sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
917
918 /*
919 * Detach the controlling tty.
920 * We don't need the tty in a parent who only waits for a child.
921 * The child calls setsid() that detaches from the tty as well.
922 */
923 ioctl(0, TIOCNOTTY, NULL);
924
925 /*
926 * We have to beware of SIGTERM, because leaving a PAM session
927 * without pam_close_session() is a pretty bad thing.
928 */
929 sa.sa_handler = sig_handler;
930 sigaction(SIGHUP, &sa, NULL);
931 sigaction(SIGTERM, &sa, &oldsa_term);
932
933 closelog();
934
935 /*
936 * We must fork before setuid(), because we need to call
937 * pam_close_session() as root.
938 */
939 child_pid = fork();
940 if (child_pid < 0) {
941 warn(_("fork failed"));
942
943 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
944 pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
945 sleepexit(EXIT_FAILURE);
946 }
947
948 if (child_pid) {
949 /*
950 * parent - wait for child to finish, then clean up session
951 */
952 close(0);
953 close(1);
954 close(2);
955 free_getlogindefs_data();
956
957 sa.sa_handler = SIG_IGN;
958 sigaction(SIGQUIT, &sa, NULL);
959 sigaction(SIGINT, &sa, NULL);
960
961 /* wait as long as any child is there */
962 while (wait(NULL) == -1 && errno == EINTR) ;
963 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
964
965 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
966 pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
967 exit(EXIT_SUCCESS);
968 }
969
970 /*
971 * child
972 */
973 sigaction(SIGHUP, &oldsa_hup, NULL); /* restore old state */
974 sigaction(SIGTERM, &oldsa_term, NULL);
975 if (got_sig)
976 exit(EXIT_FAILURE);
977
978 /*
979 * Problem: if the user's shell is a shell like ash that doesn't do
980 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
981 * process in the pgrp, will kill us.
982 */
983
984 /* start new session */
985 setsid();
986
987 /* make sure we have a controlling tty */
988 open_tty(cxt->tty_path);
989 openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
990
991 /*
992 * TIOCSCTTY: steal tty from other process group.
993 */
994 if (ioctl(0, TIOCSCTTY, 1))
995 syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
996 signal(SIGINT, SIG_DFL);
997 }
998
999 /*
1000 * Initialize $TERM, $HOME, ...
1001 */
1002 static void init_environ(struct login_context *cxt)
1003 {
1004 struct passwd *pwd = cxt->pwd;
1005 char *termenv, **env;
1006 char tmp[PATH_MAX];
1007 int len, i;
1008
1009 termenv = getenv("TERM");
1010 if (termenv)
1011 termenv = xstrdup(termenv);
1012
1013 /* destroy environment unless user has requested preservation (-p) */
1014 if (!cxt->keep_env) {
1015 environ = xmalloc(sizeof(char *));
1016 memset(environ, 0, sizeof(char *));
1017 }
1018
1019 xsetenv("HOME", pwd->pw_dir, 0); /* legal to override */
1020 xsetenv("USER", pwd->pw_name, 1);
1021 xsetenv("SHELL", pwd->pw_shell, 1);
1022 xsetenv("TERM", termenv ? termenv : "dumb", 1);
1023 free(termenv);
1024
1025 if (pwd->pw_uid) {
1026 if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH) != 0)
1027 err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
1028
1029 } else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0 &&
1030 logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT) != 0) {
1031 err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
1032 }
1033
1034 /* mailx will give a funny error msg if you forget this one */
1035 len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1036 if (len > 0 && (size_t) len < sizeof(tmp))
1037 xsetenv("MAIL", tmp, 0);
1038
1039 /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1040 * not allow modifying it.
1041 */
1042 xsetenv("LOGNAME", pwd->pw_name, 1);
1043
1044 env = pam_getenvlist(cxt->pamh);
1045 for (i = 0; env && env[i]; i++)
1046 putenv(env[i]);
1047 }
1048
1049 /*
1050 * This is called for the -h option, initializes cxt->{hostname,hostaddress}.
1051 */
1052 static void init_remote_info(struct login_context *cxt, char *remotehost)
1053 {
1054 const char *domain;
1055 char *p;
1056 struct addrinfo hints, *info = NULL;
1057
1058 cxt->remote = 1;
1059
1060 get_thishost(cxt, &domain);
1061
1062 if (domain && (p = strchr(remotehost, '.')) &&
1063 strcasecmp(p + 1, domain) == 0)
1064 *p = '\0';
1065
1066 cxt->hostname = xstrdup(remotehost);
1067
1068 memset(&hints, 0, sizeof(hints));
1069 hints.ai_flags = AI_ADDRCONFIG;
1070 cxt->hostaddress[0] = 0;
1071
1072 if (getaddrinfo(cxt->hostname, NULL, &hints, &info) == 0 && info) {
1073 if (info->ai_family == AF_INET) {
1074 struct sockaddr_in *sa =
1075 (struct sockaddr_in *) info->ai_addr;
1076
1077 memcpy(cxt->hostaddress, &(sa->sin_addr), sizeof(sa->sin_addr));
1078
1079 } else if (info->ai_family == AF_INET6) {
1080 struct sockaddr_in6 *sa =
1081 (struct sockaddr_in6 *) info->ai_addr;
1082 #ifdef IN6_IS_ADDR_V4MAPPED
1083 if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) {
1084 const uint8_t *bytes = sa->sin6_addr.s6_addr;
1085 struct in_addr addr = { *(const in_addr_t *) (bytes + 12) };
1086
1087 memcpy(cxt->hostaddress, &addr, sizeof(struct in_addr));
1088 } else
1089 #endif
1090 memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr));
1091 }
1092 freeaddrinfo(info);
1093 }
1094 }
1095
1096 static void __attribute__((__noreturn__)) usage(void)
1097 {
1098 fputs(USAGE_HEADER, stdout);
1099 printf(_(" %s [-p] [-h <host>] [-H] [[-f] <username>]\n"), program_invocation_short_name);
1100 fputs(USAGE_SEPARATOR, stdout);
1101 fputs(_("Begin a session on the system.\n"), stdout);
1102
1103 fputs(USAGE_OPTIONS, stdout);
1104 puts(_(" -p do not destroy the environment"));
1105 puts(_(" -f skip a second login authentication"));
1106 puts(_(" -h <host> hostname to be used for utmp logging"));
1107 puts(_(" -H suppress hostname in the login prompt"));
1108 printf(" --help %s\n", USAGE_OPTSTR_HELP);
1109 printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION);
1110 printf(USAGE_MAN_TAIL("login(1)"));
1111 exit(EXIT_SUCCESS);
1112 }
1113
1114 int main(int argc, char **argv)
1115 {
1116 int c;
1117 int cnt;
1118 char *childArgv[10];
1119 char *buff;
1120 int childArgc = 0;
1121 int retcode;
1122 struct sigaction act;
1123 struct passwd *pwd;
1124
1125 struct login_context cxt = {
1126 .tty_mode = TTY_MODE, /* tty chmod() */
1127 .pid = getpid(), /* PID */
1128 #ifdef HAVE_SECURITY_PAM_MISC_H
1129 .conv = { misc_conv, NULL } /* Linux-PAM conversation function */
1130 #elif defined(HAVE_SECURITY_OPENPAM_H)
1131 .conv = { openpam_ttyconv, NULL } /* OpenPAM conversation function */
1132 #endif
1133
1134 };
1135
1136 /* the only two longopts to satisfy UL standards */
1137 enum { HELP_OPTION = CHAR_MAX + 1 };
1138 static const struct option longopts[] = {
1139 {"help", no_argument, NULL, HELP_OPTION},
1140 {"version", no_argument, NULL, 'V'},
1141 {NULL, 0, NULL, 0}
1142 };
1143
1144 timeout = (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);
1145
1146 setlocale(LC_ALL, "");
1147 bindtextdomain(PACKAGE, LOCALEDIR);
1148 textdomain(PACKAGE);
1149
1150 /* TRANSLATORS: The standard value for %u is 60. */
1151 snprintf(timeout_msg, sizeof(timeout_msg),
1152 _("%s: timed out after %u seconds"),
1153 program_invocation_short_name, timeout);
1154
1155 signal(SIGALRM, timedout);
1156 (void) sigaction(SIGALRM, NULL, &act);
1157 act.sa_flags &= ~SA_RESTART;
1158 sigaction(SIGALRM, &act, NULL);
1159 alarm(timeout);
1160 signal(SIGQUIT, SIG_IGN);
1161 signal(SIGINT, SIG_IGN);
1162
1163 setpriority(PRIO_PROCESS, 0, 0);
1164 initproctitle(argc, argv);
1165
1166 /*
1167 * -p is used by getty to tell login not to destroy the environment
1168 * -f is used to skip a second login authentication
1169 * -h is used by other servers to pass the name of the remote
1170 * host to login so that it may be placed in utmp and wtmp
1171 */
1172 while ((c = getopt_long(argc, argv, "fHh:pV", longopts, NULL)) != -1)
1173 switch (c) {
1174 case 'f':
1175 cxt.noauth = 1;
1176 break;
1177
1178 case 'H':
1179 cxt.nohost = 1;
1180 break;
1181
1182 case 'h':
1183 if (getuid()) {
1184 fprintf(stderr,
1185 _("login: -h is for superuser only\n"));
1186 exit(EXIT_FAILURE);
1187 }
1188 init_remote_info(&cxt, optarg);
1189 break;
1190
1191 case 'p':
1192 cxt.keep_env = 1;
1193 break;
1194
1195 case 'V':
1196 printf(UTIL_LINUX_VERSION);
1197 return EXIT_SUCCESS;
1198 case HELP_OPTION:
1199 usage();
1200 default:
1201 errtryhelp(EXIT_FAILURE);
1202 }
1203 argc -= optind;
1204 argv += optind;
1205
1206 if (*argv) {
1207 char *p = *argv;
1208
1209 /* username from command line */
1210 cxt.cmd_username = xstrdup(p);
1211 /* used temporary, it'll be replaced by username from PAM or/and cxt->pwd */
1212 cxt.username = cxt.cmd_username;
1213
1214 /* Wipe the name - some people mistype their password here. */
1215 /* (Of course we are too late, but perhaps this helps a little...) */
1216 while (*p)
1217 *p++ = ' ';
1218 }
1219
1220 for (cnt = get_fd_tabsize() - 1; cnt > 2; cnt--)
1221 close(cnt);
1222
1223 setpgrp(); /* set pgid to pid this means that setsid() will fail */
1224 init_tty(&cxt);
1225
1226 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1227
1228 init_loginpam(&cxt);
1229
1230 /* login -f, then the user has already been authenticated */
1231 cxt.noauth = cxt.noauth && getuid() == 0 ? 1 : 0;
1232
1233 if (!cxt.noauth)
1234 loginpam_auth(&cxt);
1235
1236 /*
1237 * Authentication may be skipped (for example, during krlogin, rlogin,
1238 * etc...), but it doesn't mean that we can skip other account checks.
1239 * The account could be disabled or the password has expired (although
1240 * the kerberos ticket is valid). -- kzak@redhat.com (22-Feb-2006)
1241 */
1242 loginpam_acct(&cxt);
1243
1244 cxt.pwd = xgetpwnam(cxt.username, &cxt.pwdbuf);
1245 if (!cxt.pwd) {
1246 warnx(_("\nSession setup problem, abort."));
1247 syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
1248 cxt.username, __FUNCTION__, __LINE__);
1249 pam_end(cxt.pamh, PAM_SYSTEM_ERR);
1250 sleepexit(EXIT_FAILURE);
1251 }
1252
1253 pwd = cxt.pwd;
1254 cxt.username = pwd->pw_name;
1255
1256 /*
1257 * Initialize the supplementary group list. This should be done before
1258 * pam_setcred, because PAM modules might add groups during that call.
1259 *
1260 * For root we don't call initgroups, instead we call setgroups with
1261 * group 0. This avoids the need to step through the whole group file,
1262 * which can cause problems if NIS, NIS+, LDAP or something similar
1263 * is used and the machine has network problems.
1264 */
1265 retcode = pwd->pw_uid ? initgroups(cxt.username, pwd->pw_gid) : /* user */
1266 setgroups(0, NULL); /* root */
1267 if (retcode < 0) {
1268 syslog(LOG_ERR, _("groups initialization failed: %m"));
1269 warnx(_("\nSession setup problem, abort."));
1270 pam_end(cxt.pamh, PAM_SYSTEM_ERR);
1271 sleepexit(EXIT_FAILURE);
1272 }
1273
1274 /*
1275 * Open PAM session (after successful authentication and account check).
1276 */
1277 loginpam_session(&cxt);
1278
1279 /* committed to login -- turn off timeout */
1280 alarm((unsigned int)0);
1281
1282 endpwent();
1283
1284 cxt.quiet = get_hushlogin_status(pwd, 1);
1285
1286 log_utmp(&cxt);
1287 log_audit(&cxt, 1);
1288 log_lastlog(&cxt);
1289
1290 chown_tty(&cxt);
1291
1292 if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
1293 syslog(LOG_ALERT, _("setgid() failed"));
1294 exit(EXIT_FAILURE);
1295 }
1296
1297 if (pwd->pw_shell == NULL || *pwd->pw_shell == '\0')
1298 pwd->pw_shell = _PATH_BSHELL;
1299
1300 init_environ(&cxt); /* init $HOME, $TERM ... */
1301
1302 setproctitle("login", cxt.username);
1303
1304 log_syslog(&cxt);
1305
1306 if (!cxt.quiet) {
1307 motd();
1308
1309 #ifdef LOGIN_STAT_MAIL
1310 /*
1311 * This turns out to be a bad idea: when the mail spool
1312 * is NFS mounted, and the NFS connection hangs, the
1313 * login hangs, even root cannot login.
1314 * Checking for mail should be done from the shell.
1315 */
1316 {
1317 struct stat st;
1318 char *mail;
1319
1320 mail = getenv("MAIL");
1321 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1322 if (st.st_mtime > st.st_atime)
1323 printf(_("You have new mail.\n"));
1324 else
1325 printf(_("You have mail.\n"));
1326 }
1327 }
1328 #endif
1329 }
1330
1331 /*
1332 * Detach the controlling terminal, fork, and create a new session
1333 * and reinitialize syslog stuff.
1334 */
1335 fork_session(&cxt);
1336
1337 /* discard permissions last so we can't get killed and drop core */
1338 if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1339 syslog(LOG_ALERT, _("setuid() failed"));
1340 exit(EXIT_FAILURE);
1341 }
1342
1343 /* wait until here to change directory! */
1344 if (chdir(pwd->pw_dir) < 0) {
1345 warn(_("%s: change directory failed"), pwd->pw_dir);
1346
1347 if (!getlogindefs_bool("DEFAULT_HOME", 1))
1348 exit(0);
1349 if (chdir("/"))
1350 exit(EXIT_FAILURE);
1351 pwd->pw_dir = "/";
1352 printf(_("Logging in with home = \"/\".\n"));
1353 }
1354
1355 /* if the shell field has a space: treat it like a shell script */
1356 if (strchr(pwd->pw_shell, ' ')) {
1357 buff = xmalloc(strlen(pwd->pw_shell) + 6);
1358
1359 strcpy(buff, "exec ");
1360 strcat(buff, pwd->pw_shell);
1361 childArgv[childArgc++] = "/bin/sh";
1362 childArgv[childArgc++] = "-sh";
1363 childArgv[childArgc++] = "-c";
1364 childArgv[childArgc++] = buff;
1365 } else {
1366 char tbuf[PATH_MAX + 2], *p;
1367
1368 tbuf[0] = '-';
1369 xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1370 p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);
1371
1372 childArgv[childArgc++] = pwd->pw_shell;
1373 childArgv[childArgc++] = xstrdup(tbuf);
1374 }
1375
1376 childArgv[childArgc++] = NULL;
1377
1378 execvp(childArgv[0], childArgv + 1);
1379
1380 if (!strcmp(childArgv[0], "/bin/sh"))
1381 warn(_("couldn't exec shell script"));
1382 else
1383 warn(_("no shell"));
1384
1385 exit(EXIT_SUCCESS);
1386 }