4 * This program is derived from 4.3 BSD software and is subject to the
5 * copyright notice below.
7 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
8 * Rewritten to PAM-only version.
10 * Michael Glad (glad@daimi.dk)
11 * Computer Science Department, Aarhus University, Denmark
14 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
15 * All rights reserved.
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.
29 #include <sys/param.h>
38 #include <sys/resource.h>
42 #include <sys/ioctl.h>
56 #include <sys/syslog.h>
58 #ifdef HAVE_LINUX_MAJOR_H
59 # include <linux/major.h>
63 #include <security/pam_appl.h>
65 #ifdef HAVE_SECURITY_PAM_MISC_H
66 # include <security/pam_misc.h>
67 #elif defined(HAVE_SECURITY_OPENPAM_H)
68 # include <security/openpam.h>
72 # include <libaudit.h>
76 #include "pathnames.h"
82 #include "fileutils.h"
83 #include "timeutils.h"
87 #include "logindefs.h"
89 #define LOGIN_MAX_TRIES 3
90 #define LOGIN_EXIT_TIMEOUT 5
91 #define LOGIN_TIMEOUT 60
94 static size_t argv_lth
;
96 #define VCS_PATH_MAX 64
98 #if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT)
100 # define MOTDDIR_SUPPORT
101 # define MOTDDIR_EXT ".motd"
102 # define MOTDDIR_EXTSIZ (sizeof(MOTDDIR_EXT) - 1)
106 * Login control struct
108 struct login_context
{
109 const char *tty_path
; /* ttyname() return value */
110 const char *tty_name
; /* tty_path without /dev prefix */
111 const char *tty_number
; /* end of the tty_path */
112 mode_t tty_mode
; /* chmod() mode */
114 const char *username
; /* points to PAM, pwd or cmd_username */
115 char *cmd_username
; /* username specified on command line */
117 struct passwd
*pwd
; /* user info */
118 char *pwdbuf
; /* pwd strings */
120 pam_handle_t
*pamh
; /* PAM handler */
121 struct pam_conv conv
; /* PAM conversation */
123 #ifdef LOGIN_CHOWN_VCS
124 char vcsn
[VCS_PATH_MAX
]; /* virtual console name */
125 char vcsan
[VCS_PATH_MAX
];
128 char *thishost
; /* this machine */
129 char *thisdomain
; /* this machine's domain */
130 char *hostname
; /* remote machine */
131 char hostaddress
[16]; /* remote address */
135 unsigned int quiet
:1, /* hush file exists */
136 remote
:1, /* login -h */
137 nohost
:1, /* login -H */
138 noauth
:1, /* login -f */
139 keep_env
:1; /* login -p */
142 static pid_t child_pid
= 0;
143 static volatile sig_atomic_t got_sig
= 0;
144 static char *timeout_msg
;
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
)
152 if ((fstat(fd
, &stb
) >= 0)
153 && (major(stb
.st_rdev
) == TTY_MAJOR
)
154 && (minor(stb
.st_rdev
) < 64)) {
162 * Robert Ambrose writes:
163 * A couple of my users have a problem with login processes hanging around
164 * soaking up pts's. What they seem to hung up on is trying to write out the
165 * message 'Login timed out after %d seconds' when the connection has already
167 * What I did was add a second timeout while trying to write the message, so
168 * the process just exits if the second timeout expires.
170 static void __attribute__((__noreturn__
))
171 timedout2(int sig
__attribute__((__unused__
)))
176 if (tcgetattr(0, &ti
) >= 0) {
178 tcsetattr(0, TCSANOW
, &ti
);
180 _exit(EXIT_SUCCESS
); /* %% */
183 static void timedout(int sig
__attribute__((__unused__
)))
185 signal(SIGALRM
, timedout2
);
188 ignore_result( write(STDERR_FILENO
, timeout_msg
, strlen(timeout_msg
)) );
189 signal(SIGALRM
, SIG_IGN
);
195 * This handler can be used to inform a shell about signals to login. If you have
196 * (root) permissions, you can kill all login children by one signal to the
199 * Also, a parent who is session leader is able (before setsid() in the child)
200 * to inform the child when the controlling tty goes away (e.g. modem hangup).
202 static void sig_handler(int signal
)
205 kill(-child_pid
, signal
);
206 if (signal
== SIGTERM
)
207 kill(-child_pid
, SIGHUP
); /* because the shell often ignores SIGTERM */
213 * Let us delay all exit() calls when the user is not authenticated
214 * or the session not fully initialized (loginpam_session()).
216 static void __attribute__((__noreturn__
)) sleepexit(int eval
)
218 sleep((unsigned int)getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT
));
222 static void process_title_init(int argc
, char **argv
)
225 char **envp
= environ
;
228 * Move the environment so we can reuse the memory.
229 * (Code borrowed from sendmail.)
230 * WARNING: ugly assumptions on memory layout here;
231 * if this ever causes problems, #undef DO_PS_FIDDLING
233 for (i
= 0; envp
[i
] != NULL
; i
++)
236 environ
= xmalloc(sizeof(char *) * (i
+ 1));
238 for (i
= 0; envp
[i
] != NULL
; i
++)
239 environ
[i
] = xstrdup(envp
[i
]);
243 argv_lth
= envp
[i
- 1] + strlen(envp
[i
- 1]) - argv
[0];
245 argv_lth
= argv
[argc
- 1] + strlen(argv
[argc
- 1]) - argv
[0];
250 static void process_title_update(const char *username
)
253 const char prefix
[] = "login -- ";
254 char buf
[sizeof(prefix
) + LOGIN_NAME_MAX
];
259 if (sizeof(buf
) < (sizeof(prefix
) + strlen(username
) + 1))
262 snprintf(buf
, sizeof(buf
), "%s%s", prefix
, username
);
265 if (i
> argv_lth
- 2) {
269 memset(argv0
[0], '\0', argv_lth
); /* clear the memory area */
270 strcpy(argv0
[0], buf
);
275 static const char *get_thishost(struct login_context
*cxt
, const char **domain
)
277 if (!cxt
->thishost
) {
278 cxt
->thishost
= xgethostname();
279 if (!cxt
->thishost
) {
284 cxt
->thisdomain
= strchr(cxt
->thishost
, '.');
286 *cxt
->thisdomain
++ = '\0';
290 *domain
= cxt
->thisdomain
;
291 return cxt
->thishost
;
294 #ifdef MOTDDIR_SUPPORT
295 static int motddir_filter(const struct dirent
*d
)
299 #ifdef _DIRENT_HAVE_D_TYPE
300 if (d
->d_type
!= DT_UNKNOWN
&& d
->d_type
!= DT_REG
&&
304 if (*d
->d_name
== '.')
307 namesz
= strlen(d
->d_name
);
308 if (!namesz
|| namesz
< MOTDDIR_EXTSIZ
+ 1 ||
309 strcmp(d
->d_name
+ (namesz
- MOTDDIR_EXTSIZ
), MOTDDIR_EXT
) != 0)
312 return 1; /* accept */
315 static int motddir(const char *dirname
)
317 int dd
, nfiles
, i
, done
= 0;
318 struct dirent
**namelist
= NULL
;
320 dd
= open(dirname
, O_RDONLY
| O_CLOEXEC
| O_DIRECTORY
);
324 nfiles
= scandirat(dd
, ".", &namelist
, motddir_filter
, versionsort
);
328 for (i
= 0; i
< nfiles
; i
++) {
329 struct dirent
*d
= namelist
[i
];
332 fd
= openat(dd
, d
->d_name
, O_RDONLY
| O_CLOEXEC
);
334 ul_copy_file(fd
, fileno(stdout
));
340 for (i
= 0; i
< nfiles
; i
++)
347 #endif /* MOTDDIR_SUPPORT */
350 * Output the /etc/motd file.
352 * It determines the name of a login announcement file/dir and outputs it to the
353 * user's terminal at login time. The MOTD_FILE configuration option is a
354 * colon-delimited list of filenames or directories. An empty option disables
355 * message-of-the-day printing completely.
357 static void motd(void)
361 int firstonly
, done
= 0;
363 firstonly
= getlogindefs_bool("MOTD_FIRSTONLY", 0);
365 mb
= getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE
);
371 for (file
= strtok(list
, ":"); file
; file
= strtok(NULL
, ":")) {
374 if (stat(file
, &st
) < 0)
376 #ifdef MOTDDIR_SUPPORT
377 if (S_ISDIR(st
.st_mode
))
378 done
+= motddir(file
);
380 if (S_ISREG(st
.st_mode
) && st
.st_size
> 0) {
381 int fd
= open(file
, O_RDONLY
, 0);
383 ul_copy_file(fd
, fileno(stdout
));
388 if (firstonly
&& done
)
395 * Display message of the day and you have mail notifications
397 static void display_login_messages(void)
401 #ifdef LOGIN_STAT_MAIL
403 * This turns out to be a bad idea: when the mail spool
404 * is NFS mounted, and the NFS connection hangs, the
405 * login hangs, even root cannot login.
406 * Checking for mail should be done from the shell.
412 mail
= getenv("MAIL");
413 if (mail
&& stat(mail
, &st
) == 0 && st
.st_size
!= 0) {
414 if (st
.st_mtime
> st
.st_atime
)
415 printf(_("You have new mail.\n"));
417 printf(_("You have mail.\n"));
424 * Nice and simple code provided by Linus Torvalds 16-Feb-93.
425 * Non-blocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
427 * He writes: "Login performs open() on a tty in a blocking mode.
428 * In some cases it may make login wait in open() for carrier infinitely,
429 * for example if the line is a simplistic case of a three-wire serial
430 * connection. I believe login should open the line in non-blocking mode,
431 * leaving the decision to make a connection to getty (where it actually
434 static void open_tty(const char *tty
)
438 fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
440 syslog(LOG_ERR
, _("FATAL: can't reopen tty: %m"));
441 sleepexit(EXIT_FAILURE
);
446 syslog(LOG_ERR
, _("FATAL: %s is not a terminal"), tty
);
447 sleepexit(EXIT_FAILURE
);
450 flags
= fcntl(fd
, F_GETFL
);
451 flags
&= ~O_NONBLOCK
;
452 fcntl(fd
, F_SETFL
, flags
);
454 for (i
= 0; i
< fd
; i
++)
456 for (i
= 0; i
< 3; i
++)
463 static inline void chown_err(const char *what
, uid_t uid
, gid_t gid
)
465 syslog(LOG_ERR
, _("chown (%s, %u, %u) failed: %m"), what
, uid
, gid
);
468 static inline void chmod_err(const char *what
, mode_t mode
)
470 syslog(LOG_ERR
, _("chmod (%s, %u) failed: %m"), what
, mode
);
473 static void chown_tty(struct login_context
*cxt
)
476 uid_t uid
= cxt
->pwd
->pw_uid
;
477 gid_t gid
= cxt
->pwd
->pw_gid
;
479 grname
= getlogindefs_str("TTYGROUP", TTYGRPNAME
);
480 if (grname
&& *grname
) {
481 struct group
*gr
= getgrnam(grname
);
482 if (gr
) /* group by name */
484 else /* group by ID */
485 gid
= (gid_t
) getlogindefs_num("TTYGROUP", gid
);
487 if (fchown(0, uid
, gid
)) /* tty */
488 chown_err(cxt
->tty_name
, uid
, gid
);
489 if (fchmod(0, cxt
->tty_mode
))
490 chmod_err(cxt
->tty_name
, cxt
->tty_mode
);
492 #ifdef LOGIN_CHOWN_VCS
493 if (is_consoletty(0)) {
494 if (chown(cxt
->vcsn
, uid
, gid
)) /* vcs */
495 chown_err(cxt
->vcsn
, uid
, gid
);
496 if (chmod(cxt
->vcsn
, cxt
->tty_mode
))
497 chmod_err(cxt
->vcsn
, cxt
->tty_mode
);
499 if (chown(cxt
->vcsan
, uid
, gid
)) /* vcsa */
500 chown_err(cxt
->vcsan
, uid
, gid
);
501 if (chmod(cxt
->vcsan
, cxt
->tty_mode
))
502 chmod_err(cxt
->vcsan
, cxt
->tty_mode
);
508 * Reads the current terminal path and initializes cxt->tty_* variables.
510 static void init_tty(struct login_context
*cxt
)
513 struct termios tt
, ttt
= { 0 };
514 struct winsize ws
= { 0 };
517 cxt
->tty_mode
= (mode_t
) getlogindefs_num("TTYPERM", TTY_MODE
);
519 get_terminal_name(&cxt
->tty_path
, &cxt
->tty_name
, &cxt
->tty_number
);
520 fd
= get_terminal_stdfd();
523 * In case login is suid it was possible to use a hardlink as stdin
524 * and exploit races for a local root exploit. (Wojciech Purczynski).
526 * More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
527 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
528 * All of this is a problem only when login is suid, which it isn't.
530 if (!cxt
->tty_path
|| !*cxt
->tty_path
||
531 lstat(cxt
->tty_path
, &st
) != 0 || !S_ISCHR(st
.st_mode
) ||
532 (st
.st_nlink
> 1 && strncmp(cxt
->tty_path
, "/dev/", 5) != 0) ||
533 access(cxt
->tty_path
, R_OK
| W_OK
) != 0 || fd
== -EINVAL
) {
535 syslog(LOG_ERR
, _("FATAL: bad tty"));
536 sleepexit(EXIT_FAILURE
);
539 #ifdef LOGIN_CHOWN_VCS
540 if (cxt
->tty_number
) {
541 /* find names of Virtual Console devices, for later mode change */
542 snprintf(cxt
->vcsn
, sizeof(cxt
->vcsn
), "/dev/vcs%s", cxt
->tty_number
);
543 snprintf(cxt
->vcsan
, sizeof(cxt
->vcsan
), "/dev/vcsa%s", cxt
->tty_number
);
547 /* The TTY size might be reset to 0x0 by the kernel when we close the stdin/stdout/stderr file
548 * descriptors so let's save the size now so we can reapply it later */
549 if (ioctl(fd
, TIOCGWINSZ
, &ws
) < 0) {
550 syslog(LOG_WARNING
, _("TIOCGWINSZ ioctl failed: %m"));
555 if (tcgetattr(fd
, &tt
) >= 0) {
557 ttt
.c_cflag
&= ~HUPCL
;
562 if ((fchown(fd
, 0, 0) || fchmod(fd
, cxt
->tty_mode
)) && errno
!= EROFS
) {
564 syslog(LOG_ERR
, _("FATAL: %s: change permissions failed: %m"),
566 sleepexit(EXIT_FAILURE
);
569 /* Kill processes left on this tty */
570 if ((ttt
.c_cflag
& HUPCL
) == 0)
571 tcsetattr(fd
, TCSANOW
, &ttt
);
574 * Let's close file descriptors before vhangup
575 * https://lkml.org/lkml/2012/6/5/145
578 close(STDOUT_FILENO
);
579 close(STDERR_FILENO
);
581 signal(SIGHUP
, SIG_IGN
); /* so vhangup() won't kill us */
583 signal(SIGHUP
, SIG_DFL
);
585 /* open stdin,stdout,stderr to the tty */
586 open_tty(cxt
->tty_path
);
588 /* restore tty modes */
589 if ((ttt
.c_cflag
& HUPCL
) == 0)
590 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tt
);
592 /* Restore tty size */
593 if ((ws
.ws_row
> 0 || ws
.ws_col
> 0)
594 && ioctl(STDIN_FILENO
, TIOCSWINSZ
, &ws
) < 0)
595 syslog(LOG_WARNING
, _("TIOCSWINSZ ioctl failed: %m"));
599 * Logs failed login attempts in _PATH_BTMP, if it exists.
600 * Must be called only with username the name of an actual user.
601 * The most common login failure is to give password instead of username.
603 static void log_btmp(struct login_context
*cxt
)
608 memset(&ut
, 0, sizeof(ut
));
610 str2memcpy(ut
.ut_user
,
611 cxt
->username
? cxt
->username
: "(unknown)",
615 str2memcpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
617 str2memcpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
619 gettimeofday(&tv
, NULL
);
620 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
621 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
623 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
624 ut
.ut_pid
= cxt
->pid
;
627 str2memcpy(ut
.ut_host
, cxt
->hostname
, sizeof(ut
.ut_host
));
628 if (*cxt
->hostaddress
)
629 memcpy(&ut
.ut_addr_v6
, cxt
->hostaddress
,
630 sizeof(ut
.ut_addr_v6
));
633 updwtmpx(_PATH_BTMP
, &ut
);
637 static void log_audit(struct login_context
*cxt
, int status
)
640 struct passwd
*pwd
= cxt
->pwd
;
642 audit_fd
= audit_open();
645 if (!pwd
&& cxt
->username
)
646 pwd
= getpwnam(cxt
->username
);
648 ignore_result( audit_log_acct_message(audit_fd
,
652 cxt
->username
? cxt
->username
: "(unknown)",
653 pwd
? pwd
->pw_uid
: (unsigned int)-1,
661 #else /* !HAVE_LIBAUDIT */
662 # define log_audit(cxt, status)
663 #endif /* HAVE_LIBAUDIT */
665 #ifdef USE_LOGIN_LASTLOG
666 static void log_lastlog(struct login_context
*cxt
)
668 struct sigaction sa
, oldsa_xfsz
;
677 if (cxt
->pwd
->pw_uid
> (uid_t
) getlogindefs_num("LASTLOG_UID_MAX", ULONG_MAX
))
680 /* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
681 memset(&sa
, 0, sizeof(sa
));
682 sa
.sa_handler
= SIG_IGN
;
683 sigaction(SIGXFSZ
, &sa
, &oldsa_xfsz
);
685 fd
= open(_PATH_LASTLOG
, O_RDWR
, 0);
688 offset
= cxt
->pwd
->pw_uid
* sizeof(ll
);
691 * Print last log message.
694 if ((pread(fd
, (void *)&ll
, sizeof(ll
), offset
) == sizeof(ll
)) &&
696 char time_string
[CTIME_BUFSIZ
];
697 char buf
[sizeof(ll
.ll_host
) + 1];
699 time_t ll_time
= (time_t)ll
.ll_time
;
701 ctime_r(&ll_time
, time_string
);
702 printf(_("Last login: %.*s "), 24 - 5, time_string
);
704 if (*ll
.ll_host
!= '\0') {
705 mem2strcpy(buf
, ll
.ll_host
, sizeof(ll
.ll_host
), sizeof(buf
));
706 printf(_("from %s\n"), buf
);
708 mem2strcpy(buf
, ll
.ll_line
, sizeof(ll
.ll_line
), sizeof(buf
));
709 printf(_("on %s\n"), buf
);
714 memset((char *)&ll
, 0, sizeof(ll
));
717 ll
.ll_time
= t
; /* ll_time is always 32bit */
720 str2memcpy(ll
.ll_line
, cxt
->tty_name
, sizeof(ll
.ll_line
));
722 str2memcpy(ll
.ll_host
, cxt
->hostname
, sizeof(ll
.ll_host
));
724 if (pwrite(fd
, (void *)&ll
, sizeof(ll
), offset
) != sizeof(ll
))
725 warn(_("write lastlog failed"));
730 sigaction(SIGXFSZ
, &oldsa_xfsz
, NULL
); /* restore original setting */
733 # define log_lastlog(cxt)
734 #endif /* USE_LOGIN_LASTLOG */
737 * Update wtmp and utmp logs.
739 static void log_utmp(struct login_context
*cxt
)
741 struct utmpx ut
= { 0 };
742 struct utmpx
*utp
= NULL
;
743 struct timeval tv
= { 0 };
745 utmpxname(_PATH_UTMP
);
750 * login sometimes overwrites the runlevel entry in /var/run/utmp,
751 * confusing sysvinit. I added a test for the entry type, and the
752 * problem was gone. (In a runlevel entry, st_pid is not really a pid
753 * but some number calculated from the previous and current runlevel.)
754 * -- Michael Riepe <michael@stud.uni-hannover.de>
756 while ((utp
= getutxent()))
757 if (utp
->ut_pid
== cxt
->pid
758 && utp
->ut_type
>= INIT_PROCESS
759 && utp
->ut_type
<= DEAD_PROCESS
)
762 /* If we can't find a pre-existing entry by pid, try by line.
763 * BSD network daemons may rely on this. */
764 if (utp
== NULL
&& cxt
->tty_name
) {
766 ut
.ut_type
= LOGIN_PROCESS
;
767 str2memcpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
768 utp
= getutxline(&ut
);
771 /* If we can't find a pre-existing entry by pid and line, try it by id.
772 * Very stupid telnetd daemons don't set up utmp at all. (kzak) */
773 if (utp
== NULL
&& cxt
->tty_number
) {
775 ut
.ut_type
= DEAD_PROCESS
;
776 str2memcpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
781 memcpy(&ut
, utp
, sizeof(ut
));
783 /* some gettys/telnetds don't initialize utmp... */
784 memset(&ut
, 0, sizeof(ut
));
786 if (cxt
->tty_number
&& ut
.ut_id
[0] == 0)
787 str2memcpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
789 str2memcpy(ut
.ut_user
, cxt
->username
, sizeof(ut
.ut_user
));
791 str2memcpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
793 gettimeofday(&tv
, NULL
);
794 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
795 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
796 ut
.ut_type
= USER_PROCESS
;
797 ut
.ut_pid
= cxt
->pid
;
799 str2memcpy(ut
.ut_host
, cxt
->hostname
, sizeof(ut
.ut_host
));
800 if (*cxt
->hostaddress
)
801 memcpy(&ut
.ut_addr_v6
, cxt
->hostaddress
,
802 sizeof(ut
.ut_addr_v6
));
808 updwtmpx(_PATH_WTMP
, &ut
);
811 static void log_syslog(struct login_context
*cxt
)
813 struct passwd
*pwd
= cxt
->pwd
;
818 if (!strncmp(cxt
->tty_name
, "ttyS", 4))
819 syslog(LOG_INFO
, _("DIALUP AT %s BY %s"),
820 cxt
->tty_name
, pwd
->pw_name
);
824 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s FROM %s"),
825 cxt
->tty_name
, cxt
->hostname
);
827 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s"), cxt
->tty_name
);
830 syslog(LOG_INFO
, _("LOGIN ON %s BY %s FROM %s"),
831 cxt
->tty_name
, pwd
->pw_name
, cxt
->hostname
);
833 syslog(LOG_INFO
, _("LOGIN ON %s BY %s"), cxt
->tty_name
,
838 /* encapsulate stupid "void **" pam_get_item() API */
839 static int loginpam_get_username(pam_handle_t
*pamh
, const char **name
)
841 const void *item
= (const void *)*name
;
844 rc
= pam_get_item(pamh
, PAM_USER
, &item
);
845 *name
= (const char *)item
;
849 static void loginpam_err(pam_handle_t
*pamh
, int retcode
)
851 const char *msg
= pam_strerror(pamh
, retcode
);
854 fprintf(stderr
, "\n%s\n", msg
);
855 syslog(LOG_ERR
, "%s", msg
);
857 pam_end(pamh
, retcode
);
858 sleepexit(EXIT_FAILURE
);
862 * Composes "<host> login: " string; or returns "login: " if -H is given or
863 * LOGIN_PLAIN_PROMPT=yes configured.
865 static const char *loginpam_get_prompt(struct login_context
*cxt
)
868 char *prompt
= NULL
, *dflt_prompt
= _("login: ");
871 return dflt_prompt
; /* -H on command line */
873 if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
876 if (!(host
= get_thishost(cxt
, NULL
)))
879 xasprintf(&prompt
, "%s %s", host
, dflt_prompt
);
884 static inline int is_pam_failure(int rc
)
886 return rc
!= PAM_SUCCESS
;
889 static pam_handle_t
*init_loginpam(struct login_context
*cxt
)
891 pam_handle_t
*pamh
= NULL
;
895 * username is initialized to NULL and if specified on the command line
896 * it is set. Therefore, we are safe not setting it to anything.
898 rc
= pam_start(cxt
->remote
? "remote" : "login",
899 cxt
->username
, &cxt
->conv
, &pamh
);
900 if (rc
!= PAM_SUCCESS
) {
901 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh
, rc
));
902 syslog(LOG_ERR
, _("Couldn't initialize PAM: %s"),
903 pam_strerror(pamh
, rc
));
904 sleepexit(EXIT_FAILURE
);
907 /* hostname & tty are either set to NULL or their correct values,
908 * depending on how much we know. */
909 rc
= pam_set_item(pamh
, PAM_RHOST
, cxt
->hostname
);
910 if (is_pam_failure(rc
))
911 loginpam_err(pamh
, rc
);
914 rc
= pam_set_item(pamh
, PAM_TTY
, cxt
->tty_path
);
915 if (is_pam_failure(rc
))
916 loginpam_err(pamh
, rc
);
920 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
921 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
922 * an interface to specify the "Password: " string (yet).
924 rc
= pam_set_item(pamh
, PAM_USER_PROMPT
, loginpam_get_prompt(cxt
));
925 if (is_pam_failure(rc
))
926 loginpam_err(pamh
, rc
);
928 /* We don't need the original username. We have to follow PAM. */
929 cxt
->username
= NULL
;
935 static void loginpam_auth(struct login_context
*cxt
)
937 int rc
, show_unknown
, keep_username
;
938 unsigned int retries
, failcount
= 0;
939 const char *hostname
= cxt
->hostname
? cxt
->hostname
:
940 cxt
->tty_name
? cxt
->tty_name
: "<unknown>";
941 pam_handle_t
*pamh
= cxt
->pamh
;
943 /* if we didn't get a user on the command line, set it to NULL */
944 loginpam_get_username(pamh
, &cxt
->username
);
946 show_unknown
= getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
947 retries
= getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES
);
948 keep_username
= getlogindefs_bool("LOGIN_KEEP_USERNAME", 0);
951 * There may be better ways to deal with some of these conditions, but
952 * at least this way I don't think we'll be giving away information...
954 * Perhaps someday we can trust that all PAM modules will pay attention
955 * to failure count and get rid of LOGIN_MAX_TRIES?
957 rc
= pam_authenticate(pamh
, 0);
959 while ((++failcount
< retries
) &&
960 ((rc
== PAM_AUTH_ERR
) ||
961 (rc
== PAM_USER_UNKNOWN
) ||
962 (rc
== PAM_CRED_INSUFFICIENT
) ||
963 (rc
== PAM_AUTHINFO_UNAVAIL
))) {
965 if (rc
== PAM_USER_UNKNOWN
&& !show_unknown
)
967 * Logging unknown usernames may be a security issue if
968 * a user enters their password instead of their login name.
970 cxt
->username
= NULL
;
972 loginpam_get_username(pamh
, &cxt
->username
);
975 _("FAILED LOGIN %u FROM %s FOR %s, %s"),
977 cxt
->username
? cxt
->username
: "(unknown)",
978 pam_strerror(pamh
, rc
));
983 if (!keep_username
|| rc
== PAM_USER_UNKNOWN
) {
984 pam_set_item(pamh
, PAM_USER
, NULL
);
985 fprintf(stderr
, _("Login incorrect\n\n"));
987 fprintf(stderr
, _("Password incorrect\n\n"));
989 rc
= pam_authenticate(pamh
, 0);
992 if (is_pam_failure(rc
)) {
994 if (rc
== PAM_USER_UNKNOWN
&& !show_unknown
)
995 cxt
->username
= NULL
;
997 loginpam_get_username(pamh
, &cxt
->username
);
999 if (rc
== PAM_MAXTRIES
)
1001 _("TOO MANY LOGIN TRIES (%u) FROM %s FOR %s, %s"),
1002 failcount
, hostname
,
1003 cxt
->username
? cxt
->username
: "(unknown)",
1004 pam_strerror(pamh
, rc
));
1007 _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
1009 cxt
->username
? cxt
->username
: "(unknown)",
1010 pam_strerror(pamh
, rc
));
1015 fprintf(stderr
, _("\nLogin incorrect\n"));
1017 sleepexit(EXIT_SUCCESS
);
1021 static void loginpam_acct(struct login_context
*cxt
)
1024 pam_handle_t
*pamh
= cxt
->pamh
;
1026 rc
= pam_acct_mgmt(pamh
, 0);
1028 if (rc
== PAM_NEW_AUTHTOK_REQD
)
1029 rc
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
1031 if (is_pam_failure(rc
))
1032 loginpam_err(pamh
, rc
);
1035 * First get the username that we are actually using, though.
1037 rc
= loginpam_get_username(pamh
, &cxt
->username
);
1038 if (is_pam_failure(rc
))
1039 loginpam_err(pamh
, rc
);
1041 if (!cxt
->username
|| !*cxt
->username
) {
1042 warnx(_("\nSession setup problem, abort."));
1043 syslog(LOG_ERR
, _("NULL user name. Abort."));
1044 pam_end(pamh
, PAM_SYSTEM_ERR
);
1045 sleepexit(EXIT_FAILURE
);
1050 * Note that the position of the pam_setcred() call is discussable:
1052 * - the PAM docs recommend pam_setcred() before pam_open_session()
1053 * - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
1054 * uses pam_setcred() after pam_open_session()
1056 * The old login versions (before year 2011) followed the RFC. This is probably
1057 * not optimal, because there could be a dependence between some session modules
1058 * and the user's credentials.
1060 * The best is probably to follow openssh and call pam_setcred() before and
1061 * after pam_open_session(). -- kzak@redhat.com (18-Nov-2011)
1064 static void loginpam_session(struct login_context
*cxt
)
1067 pam_handle_t
*pamh
= cxt
->pamh
;
1069 rc
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
1070 if (is_pam_failure(rc
))
1071 loginpam_err(pamh
, rc
);
1073 rc
= pam_open_session(pamh
, cxt
->quiet
? PAM_SILENT
: 0);
1074 if (is_pam_failure(rc
)) {
1075 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1076 loginpam_err(pamh
, rc
);
1079 rc
= pam_setcred(pamh
, PAM_REINITIALIZE_CRED
);
1080 if (is_pam_failure(rc
)) {
1081 pam_close_session(pamh
, 0);
1082 loginpam_err(pamh
, rc
);
1087 * Detach the controlling terminal, fork, restore syslog stuff, and create
1090 static void fork_session(struct login_context
*cxt
)
1092 struct sigaction sa
, oldsa_hup
, oldsa_term
;
1094 signal(SIGALRM
, SIG_DFL
);
1095 signal(SIGQUIT
, SIG_DFL
);
1096 signal(SIGTSTP
, SIG_IGN
);
1098 memset(&sa
, 0, sizeof(sa
));
1099 sa
.sa_handler
= SIG_IGN
;
1100 sigaction(SIGINT
, &sa
, NULL
);
1102 sigaction(SIGHUP
, &sa
, &oldsa_hup
); /* ignore when TIOCNOTTY */
1105 * Detach the controlling tty.
1106 * We don't need the tty in a parent who only waits for a child.
1107 * The child calls setsid() that detaches from the tty as well.
1109 ioctl(0, TIOCNOTTY
, NULL
);
1112 * We have to beware of SIGTERM, because leaving a PAM session
1113 * without pam_close_session() is a pretty bad thing.
1115 sa
.sa_handler
= sig_handler
;
1116 sigaction(SIGHUP
, &sa
, NULL
);
1117 sigaction(SIGTERM
, &sa
, &oldsa_term
);
1122 * We must fork before setuid(), because we need to call
1123 * pam_close_session() as root.
1126 if (child_pid
< 0) {
1127 warn(_("fork failed"));
1129 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1130 pam_end(cxt
->pamh
, pam_close_session(cxt
->pamh
, 0));
1131 sleepexit(EXIT_FAILURE
);
1136 * parent - wait for child to finish, then clean up session
1138 close(STDIN_FILENO
);
1139 close(STDOUT_FILENO
);
1140 close(STDERR_FILENO
);
1141 free_getlogindefs_data();
1143 sa
.sa_handler
= SIG_IGN
;
1144 sigaction(SIGQUIT
, &sa
, NULL
);
1145 sigaction(SIGINT
, &sa
, NULL
);
1147 /* wait as long as any child is there */
1148 while (wait(NULL
) == -1 && errno
== EINTR
) ;
1149 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
1151 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1152 pam_end(cxt
->pamh
, pam_close_session(cxt
->pamh
, 0));
1159 sigaction(SIGHUP
, &oldsa_hup
, NULL
); /* restore old state */
1160 sigaction(SIGTERM
, &oldsa_term
, NULL
);
1165 * Problem: if the user's shell is a shell like ash that doesn't do
1166 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1167 * process in the pgrp, will kill us.
1170 /* start new session */
1173 /* make sure we have a controlling tty */
1174 open_tty(cxt
->tty_path
);
1175 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
); /* reopen */
1178 * TIOCSCTTY: steal tty from other process group.
1180 if (ioctl(0, TIOCSCTTY
, 1))
1181 syslog(LOG_ERR
, _("TIOCSCTTY failed: %m"));
1182 signal(SIGINT
, SIG_DFL
);
1186 * Initialize $TERM, $HOME, ...
1188 static void init_environ(struct login_context
*cxt
)
1190 struct passwd
*pwd
= cxt
->pwd
;
1191 char *termenv
, **env
;
1195 termenv
= getenv("TERM");
1197 termenv
= xstrdup(termenv
);
1199 /* destroy environment unless user has requested preservation (-p) */
1201 environ
= xcalloc(1, sizeof(char *));
1203 xsetenv("HOME", pwd
->pw_dir
, 0); /* legal to override */
1204 xsetenv("USER", pwd
->pw_name
, 1);
1205 xsetenv("SHELL", pwd
->pw_shell
, 1);
1206 xsetenv("TERM", termenv
? termenv
: "dumb", 1);
1210 if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH
) != 0)
1211 err(EXIT_FAILURE
, _("failed to set the %s environment variable"), "PATH");
1213 } else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL
) != 0 &&
1214 logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT
) != 0) {
1215 err(EXIT_FAILURE
, _("failed to set the %s environment variable"), "PATH");
1218 /* mailx will give a funny error msg if you forget this one */
1219 len
= snprintf(tmp
, sizeof(tmp
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
1220 if (len
> 0 && (size_t)len
< sizeof(tmp
))
1221 xsetenv("MAIL", tmp
, 0);
1223 /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1224 * not allow modifying it.
1226 xsetenv("LOGNAME", pwd
->pw_name
, 1);
1228 env
= pam_getenvlist(cxt
->pamh
);
1229 for (i
= 0; env
&& env
[i
]; i
++)
1234 * This is called for the -h option, initializes cxt->{hostname,hostaddress}.
1236 static void init_remote_info(struct login_context
*cxt
, char *remotehost
)
1240 struct addrinfo hints
, *info
= NULL
;
1244 get_thishost(cxt
, &domain
);
1246 if (domain
&& (p
= strchr(remotehost
, '.')) &&
1247 strcasecmp(p
+ 1, domain
) == 0)
1250 cxt
->hostname
= xstrdup(remotehost
);
1252 memset(&hints
, 0, sizeof(hints
));
1253 hints
.ai_flags
= AI_ADDRCONFIG
;
1254 cxt
->hostaddress
[0] = 0;
1256 if (getaddrinfo(cxt
->hostname
, NULL
, &hints
, &info
) == 0 && info
) {
1257 if (info
->ai_family
== AF_INET
) {
1258 struct sockaddr_in
*sa
=
1259 (struct sockaddr_in
*)info
->ai_addr
;
1261 memcpy(cxt
->hostaddress
, &(sa
->sin_addr
), sizeof(sa
->sin_addr
));
1263 } else if (info
->ai_family
== AF_INET6
) {
1264 struct sockaddr_in6
*sa
=
1265 (struct sockaddr_in6
*)info
->ai_addr
;
1266 #ifdef IN6_IS_ADDR_V4MAPPED
1267 if (IN6_IS_ADDR_V4MAPPED(&sa
->sin6_addr
)) {
1268 const uint8_t *bytes
= sa
->sin6_addr
.s6_addr
;
1269 struct in_addr addr
= { *(const in_addr_t
*)(bytes
+ 12) };
1271 memcpy(cxt
->hostaddress
, &addr
, sizeof(struct in_addr
));
1274 memcpy(cxt
->hostaddress
, &(sa
->sin6_addr
), sizeof(sa
->sin6_addr
));
1280 static void __attribute__((__noreturn__
)) usage(void)
1282 fputs(USAGE_HEADER
, stdout
);
1283 printf(_(" %s [-p] [-h <host>] [-H] [[-f] <username>]\n"), program_invocation_short_name
);
1284 fputs(USAGE_SEPARATOR
, stdout
);
1285 fputs(_("Begin a session on the system.\n"), stdout
);
1287 fputs(USAGE_OPTIONS
, stdout
);
1288 puts(_(" -p do not destroy the environment"));
1289 puts(_(" -f skip a login authentication"));
1290 puts(_(" -h <host> hostname to be used for utmp logging"));
1291 puts(_(" -H suppress hostname in the login prompt"));
1292 printf(" --help %s\n", USAGE_OPTSTR_HELP
);
1293 printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION
);
1294 printf(USAGE_MAN_TAIL("login(1)"));
1298 static void load_credentials(struct login_context
*cxt
) {
1299 char str
[32] = { 0 };
1301 struct path_cxt
*pc
;
1303 env
= safe_getenv("CREDENTIALS_DIRECTORY");
1307 pc
= ul_new_path("%s", env
);
1309 syslog(LOG_WARNING
, _("failed to initialize path context"));
1313 if (ul_path_read_buffer(pc
, str
, sizeof(str
), "login.noauth") > 0
1314 && *str
&& strcmp(str
, "yes") == 0)
1320 static void initialize(int argc
, char **argv
, struct login_context
*cxt
)
1323 unsigned int timeout
;
1324 struct sigaction act
;
1326 /* the only two longopts to satisfy UL standards */
1327 enum { HELP_OPTION
= CHAR_MAX
+ 1 };
1328 const struct option longopts
[] = {
1329 {"help", no_argument
, NULL
, HELP_OPTION
},
1330 {"version", no_argument
, NULL
, 'V'},
1335 * This bounds the time given to login. Not a define, so it can
1336 * be patched on machines where it's too small.
1338 timeout
= (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT
);
1340 /* TRANSLATORS: The standard value for %u is 60. */
1341 xasprintf(&timeout_msg
, _("%s: timed out after %u seconds"),
1342 program_invocation_short_name
, timeout
);
1344 signal(SIGALRM
, timedout
);
1345 sigaction(SIGALRM
, NULL
, &act
);
1346 act
.sa_flags
&= ~SA_RESTART
;
1347 sigaction(SIGALRM
, &act
, NULL
);
1349 signal(SIGQUIT
, SIG_IGN
);
1350 signal(SIGINT
, SIG_IGN
);
1352 setpriority(PRIO_PROCESS
, 0, 0);
1353 process_title_init(argc
, argv
);
1355 load_credentials(cxt
);
1357 while ((c
= getopt_long(argc
, argv
, "fHh:pV", longopts
, NULL
)) != -1)
1370 _("login: -h is for superuser only\n"));
1373 init_remote_info(cxt
, optarg
);
1381 print_version(EXIT_SUCCESS
);
1385 errtryhelp(EXIT_FAILURE
);
1393 /* username from command line */
1394 cxt
->cmd_username
= xstrdup(p
);
1395 /* used temporary, it'll be replaced by username from PAM or/and cxt->pwd */
1396 cxt
->username
= cxt
->cmd_username
;
1398 /* Wipe the name - some people mistype their password here. */
1399 /* (Of course we are too late, but perhaps this helps a little...) */
1400 #ifdef HAVE_EXPLICIT_BZERO
1401 explicit_bzero(p
, strlen(p
));
1407 #ifdef HAVE_CLOSE_RANGE
1408 if (close_range(STDERR_FILENO
+ 1, ~0U, 0) < 0)
1410 ul_close_all_fds(STDERR_FILENO
+ 1, ~0U);
1413 int main(int argc
, char **argv
)
1415 char *child_argv
[10];
1418 struct login_context cxt
= {
1419 .tty_mode
= TTY_MODE
, /* tty chmod() */
1420 .pid
= getpid(), /* PID */
1421 #ifdef HAVE_SECURITY_PAM_MISC_H
1422 .conv
= { misc_conv
, NULL
} /* Linux-PAM conversation function */
1423 #elif defined(HAVE_SECURITY_OPENPAM_H)
1424 .conv
= { openpam_ttyconv
, NULL
} /* OpenPAM conversation function */
1429 setlocale(LC_ALL
, "");
1430 bindtextdomain(PACKAGE
, LOCALEDIR
);
1431 textdomain(PACKAGE
);
1433 initialize(argc
, argv
, &cxt
);
1435 setpgrp(); /* set pgid to pid this means that setsid() will fail */
1438 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
1440 init_loginpam(&cxt
);
1442 /* login -f, then the user has already been authenticated */
1443 cxt
.noauth
= cxt
.noauth
&& getuid() == 0 ? 1 : 0;
1446 loginpam_auth(&cxt
);
1449 * Authentication may be skipped (for example, during krlogin, rlogin,
1450 * etc...), but it doesn't mean that we can skip other account checks.
1451 * The account could be disabled or the password has expired (although
1452 * the kerberos ticket is valid). -- kzak@redhat.com (22-Feb-2006)
1454 loginpam_acct(&cxt
);
1456 cxt
.pwd
= xgetpwnam(cxt
.username
, &cxt
.pwdbuf
);
1458 warnx(_("\nSession setup problem, abort."));
1459 syslog(LOG_ERR
, _("Invalid user name \"%s\". Abort."),
1461 pam_end(cxt
.pamh
, PAM_SYSTEM_ERR
);
1462 sleepexit(EXIT_FAILURE
);
1466 cxt
.username
= pwd
->pw_name
;
1469 * Initialize the supplementary group list. This should be done before
1470 * pam_setcred, because PAM modules might add groups during that call.
1472 * For root we don't call initgroups, instead we call setgroups with
1473 * group 0. This avoids the need to step through the whole group file,
1474 * which can cause problems if NIS, NIS+, LDAP or something similar
1475 * is used and the machine has network problems.
1480 retcode
= pwd
->pw_uid
? initgroups(cxt
.username
, pwd
->pw_gid
) : /* user */
1481 setgroups(0, NULL
); /* root */
1483 syslog(LOG_ERR
, _("groups initialization failed: %m"));
1484 warnx(_("\nSession setup problem, abort."));
1485 pam_end(cxt
.pamh
, PAM_SYSTEM_ERR
);
1486 sleepexit(EXIT_FAILURE
);
1490 cxt
.quiet
= get_hushlogin_status(pwd
, 1) == 1 ? 1 : 0;
1493 * Open PAM session (after successful authentication and account check).
1495 loginpam_session(&cxt
);
1497 /* committed to login -- turn off timeout */
1498 alarm((unsigned int)0);
1510 if (setgid(pwd
->pw_gid
) < 0 && pwd
->pw_gid
) {
1511 syslog(LOG_ALERT
, _("setgid() failed"));
1515 if (pwd
->pw_shell
== NULL
|| *pwd
->pw_shell
== '\0')
1516 pwd
->pw_shell
= _PATH_BSHELL
;
1518 init_environ(&cxt
); /* init $HOME, $TERM ... */
1520 process_title_update(cxt
.username
);
1525 display_login_messages();
1528 * Detach the controlling terminal, fork, and create a new session
1529 * and reinitialize syslog stuff.
1533 /* discard permissions last so we can't get killed and drop core */
1534 if (setuid(pwd
->pw_uid
) < 0 && pwd
->pw_uid
) {
1535 syslog(LOG_ALERT
, _("setuid() failed"));
1539 /* wait until here to change directory! */
1540 if (chdir(pwd
->pw_dir
) < 0) {
1541 warn(_("%s: change directory failed"), pwd
->pw_dir
);
1543 if (!getlogindefs_bool("DEFAULT_HOME", 1))
1548 printf(_("Logging in with home = \"/\".\n"));
1551 /* if the shell field has a space: treat it like a shell script */
1552 script
= strchr(pwd
->pw_shell
, ' ') != NULL
;
1557 xasprintf(&buff
, "exec %s", pwd
->pw_shell
);
1558 child_argv
[child_argc
++] = "/bin/sh";
1559 child_argv
[child_argc
++] = "-sh";
1560 child_argv
[child_argc
++] = "-c";
1561 child_argv
[child_argc
++] = buff
;
1565 xasprintf(&buff
, "-%.*s", PATH_MAX
,
1566 ((p
= strrchr(pwd
->pw_shell
, '/')) ?
1567 p
+ 1 : pwd
->pw_shell
));
1568 child_argv
[child_argc
++] = pwd
->pw_shell
;
1569 child_argv
[child_argc
++] = buff
;
1572 child_argv
[child_argc
++] = NULL
;
1574 /* http://www.linux-pam.org/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_end */
1575 (void) pam_end(cxt
.pamh
, PAM_SUCCESS
|PAM_DATA_SILENT
);
1577 execvp(child_argv
[0], child_argv
+ 1);
1580 warn(_("couldn't exec shell script"));
1582 warn(_("no shell"));