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>
53 #include <sys/syslog.h>
54 #ifdef HAVE_LINUX_MAJOR_H
55 # include <linux/major.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>
64 #include <sys/sendfile.h>
67 # include <libaudit.h>
71 #include "setproctitle.h"
72 #include "pathnames.h"
78 #include "fileutils.h"
79 #include "timeutils.h"
83 #include "logindefs.h"
85 #define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
87 #define LOGIN_MAX_TRIES 3
88 #define LOGIN_EXIT_TIMEOUT 5
89 #define LOGIN_TIMEOUT 60
92 # define TTY_MODE 0620
94 # define TTY_MODE 0600
97 #define TTYGRPNAME "tty" /* name of group to own ttys */
98 #define VCS_PATH_MAX 64
100 #if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT)
102 # include "fileutils.h"
103 # define MOTDDIR_SUPPORT
104 # define MOTDDIR_EXT ".motd"
105 # define MOTDDIR_EXTSIZ (sizeof(MOTDDIR_EXT) - 1)
109 * Login control struct
111 struct login_context
{
112 const char *tty_path
; /* ttyname() return value */
113 const char *tty_name
; /* tty_path without /dev prefix */
114 const char *tty_number
; /* end of the tty_path */
115 mode_t tty_mode
; /* chmod() mode */
117 const char *username
; /* points to PAM, pwd or cmd_username */
118 char *cmd_username
; /* username specified on command line */
121 struct passwd
*pwd
; /* user info */
122 char *pwdbuf
; /* pwd strings */
124 pam_handle_t
*pamh
; /* PAM handler */
125 struct pam_conv conv
; /* PAM conversation */
127 #ifdef LOGIN_CHOWN_VCS
128 char vcsn
[VCS_PATH_MAX
]; /* virtual console name */
129 char vcsan
[VCS_PATH_MAX
];
132 char *thishost
; /* this machine */
133 char *thisdomain
; /* this machine's domain */
134 char *hostname
; /* remote machine */
135 char hostaddress
[16]; /* remote address */
138 int quiet
; /* 1 if hush file exists */
140 unsigned int remote
:1, /* login -h */
141 nohost
:1, /* login -H */
142 noauth
:1, /* login -f */
143 keep_env
:1; /* login -p */
147 * This bounds the time given to login. Not a define, so it can
148 * be patched on machines where it's too small.
150 static unsigned int timeout
= LOGIN_TIMEOUT
;
151 static int child_pid
= 0;
152 static volatile int got_sig
= 0;
153 static char timeout_msg
[128];
155 #ifdef LOGIN_CHOWN_VCS
156 /* true if the filedescriptor fd is a console tty, very Linux specific */
157 static int is_consoletty(int fd
)
161 if ((fstat(fd
, &stb
) >= 0)
162 && (major(stb
.st_rdev
) == TTY_MAJOR
)
163 && (minor(stb
.st_rdev
) < 64)) {
172 * Robert Ambrose writes:
173 * A couple of my users have a problem with login processes hanging around
174 * soaking up pts's. What they seem to hung up on is trying to write out the
175 * message 'Login timed out after %d seconds' when the connection has already
177 * What I did was add a second timeout while trying to write the message, so
178 * the process just exits if the second timeout expires.
180 static void __attribute__ ((__noreturn__
))
181 timedout2(int sig
__attribute__ ((__unused__
)))
188 tcsetattr(0, TCSANOW
, &ti
);
189 _exit(EXIT_SUCCESS
); /* %% */
192 static void timedout(int sig
__attribute__ ((__unused__
)))
194 signal(SIGALRM
, timedout2
);
196 ignore_result( write(STDERR_FILENO
, timeout_msg
, strlen(timeout_msg
)) );
197 signal(SIGALRM
, SIG_IGN
);
203 * This handler allows to inform a shell about signals to login. If you have
204 * (root) permissions, you can kill all login children by one signal to the
207 * Also, a parent who is session leader is able (before setsid() in the child)
208 * to inform the child when the controlling tty goes away (e.g. modem hangup).
210 static void sig_handler(int signal
)
213 kill(-child_pid
, signal
);
216 if (signal
== SIGTERM
)
217 kill(-child_pid
, SIGHUP
); /* because the shell often ignores SIGTERM */
221 * Let us delay all exit() calls when the user is not authenticated
222 * or the session not fully initialized (loginpam_session()).
224 static void __attribute__ ((__noreturn__
)) sleepexit(int eval
)
226 sleep((unsigned int)getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT
));
230 static const char *get_thishost(struct login_context
*cxt
, const char **domain
)
232 if (!cxt
->thishost
) {
233 cxt
->thishost
= xgethostname();
234 if (!cxt
->thishost
) {
239 cxt
->thisdomain
= strchr(cxt
->thishost
, '.');
241 *cxt
->thisdomain
++ = '\0';
245 *domain
= cxt
->thisdomain
;
246 return cxt
->thishost
;
249 #ifdef MOTDDIR_SUPPORT
250 static int motddir_filter(const struct dirent
*d
)
254 #ifdef _DIRENT_HAVE_D_TYPE
255 if (d
->d_type
!= DT_UNKNOWN
&& d
->d_type
!= DT_REG
&&
259 if (*d
->d_name
== '.')
262 namesz
= strlen(d
->d_name
);
263 if (!namesz
|| namesz
< MOTDDIR_EXTSIZ
+ 1 ||
264 strcmp(d
->d_name
+ (namesz
- MOTDDIR_EXTSIZ
), MOTDDIR_EXT
) != 0)
267 return 1; /* accept */
270 static int motddir(const char *dirname
)
272 int dd
, nfiles
, i
, done
= 0;
273 struct dirent
**namelist
= NULL
;
275 dd
= open(dirname
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
279 nfiles
= scandirat(dd
, ".", &namelist
, motddir_filter
, versionsort
);
283 for (i
= 0; i
< nfiles
; i
++) {
284 struct dirent
*d
= namelist
[i
];
287 fd
= openat(dd
, d
->d_name
, O_RDONLY
|O_CLOEXEC
);
290 if (fstat(fd
, &st
) == 0 && st
.st_size
> 0)
291 sendfile(fileno(stdout
), fd
, NULL
, st
.st_size
);
297 for (i
= 0; i
< nfiles
; i
++)
304 #endif /* MOTDDIR_SUPPORT */
307 * Output the /etc/motd file.
309 * It determines the name of a login announcement file/dir and outputs it to the
310 * user's terminal at login time. The MOTD_FILE configuration option is a
311 * colon-delimited list of filenames or directories. An empty option disables
312 * message-of-the-day printing completely.
314 static void motd(void)
318 int firstonly
, done
= 0;
320 firstonly
= getlogindefs_bool("MOTD_FIRSTONLY", 0);
322 mb
= getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE
);
328 for (file
= strtok(list
, ":"); file
; file
= strtok(NULL
, ":")) {
331 if (stat(file
, &st
) < 0)
333 #ifdef MOTDDIR_SUPPORT
334 if (S_ISDIR(st
.st_mode
))
335 done
+= motddir(file
);
337 if (S_ISREG(st
.st_mode
) && st
.st_size
> 0) {
338 int fd
= open(file
, O_RDONLY
, 0);
340 sendfile(fileno(stdout
), fd
, NULL
, st
.st_size
);
344 if (firstonly
&& done
)
351 * Nice and simple code provided by Linus Torvalds 16-Feb-93.
352 * Non-blocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
354 * He writes: "Login performs open() on a tty in a blocking mode.
355 * In some cases it may make login wait in open() for carrier infinitely,
356 * for example if the line is a simplistic case of a three-wire serial
357 * connection. I believe login should open the line in non-blocking mode,
358 * leaving the decision to make a connection to getty (where it actually
361 static void open_tty(const char *tty
)
365 fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
367 syslog(LOG_ERR
, _("FATAL: can't reopen tty: %m"));
368 sleepexit(EXIT_FAILURE
);
373 syslog(LOG_ERR
, _("FATAL: %s is not a terminal"), tty
);
374 sleepexit(EXIT_FAILURE
);
377 flags
= fcntl(fd
, F_GETFL
);
378 flags
&= ~O_NONBLOCK
;
379 fcntl(fd
, F_SETFL
, flags
);
381 for (i
= 0; i
< fd
; i
++)
383 for (i
= 0; i
< 3; i
++)
390 #define chown_err(_what, _uid, _gid) \
391 syslog(LOG_ERR, _("chown (%s, %lu, %lu) failed: %m"), \
392 (_what), (unsigned long) (_uid), (unsigned long) (_gid))
394 #define chmod_err(_what, _mode) \
395 syslog(LOG_ERR, _("chmod (%s, %u) failed: %m"), (_what), (_mode))
397 static void chown_tty(struct login_context
*cxt
)
400 uid_t uid
= cxt
->pwd
->pw_uid
;
401 gid_t gid
= cxt
->pwd
->pw_gid
;
403 grname
= getlogindefs_str("TTYGROUP", TTYGRPNAME
);
404 if (grname
&& *grname
) {
405 struct group
*gr
= getgrnam(grname
);
406 if (gr
) /* group by name */
408 else /* group by ID */
409 gid
= (gid_t
) getlogindefs_num("TTYGROUP", gid
);
411 if (fchown(0, uid
, gid
)) /* tty */
412 chown_err(cxt
->tty_name
, uid
, gid
);
413 if (fchmod(0, cxt
->tty_mode
))
414 chmod_err(cxt
->tty_name
, cxt
->tty_mode
);
416 #ifdef LOGIN_CHOWN_VCS
417 if (is_consoletty(0)) {
418 if (chown(cxt
->vcsn
, uid
, gid
)) /* vcs */
419 chown_err(cxt
->vcsn
, uid
, gid
);
420 if (chmod(cxt
->vcsn
, cxt
->tty_mode
))
421 chmod_err(cxt
->vcsn
, cxt
->tty_mode
);
423 if (chown(cxt
->vcsan
, uid
, gid
)) /* vcsa */
424 chown_err(cxt
->vcsan
, uid
, gid
);
425 if (chmod(cxt
->vcsan
, cxt
->tty_mode
))
426 chmod_err(cxt
->vcsan
, cxt
->tty_mode
);
432 * Reads the current terminal path and initializes cxt->tty_* variables.
434 static void init_tty(struct login_context
*cxt
)
437 struct termios tt
, ttt
;
439 cxt
->tty_mode
= (mode_t
) getlogindefs_num("TTYPERM", TTY_MODE
);
441 get_terminal_name(&cxt
->tty_path
, &cxt
->tty_name
, &cxt
->tty_number
);
444 * In case login is suid it was possible to use a hardlink as stdin
445 * and exploit races for a local root exploit. (Wojciech Purczynski).
447 * More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
448 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
449 * All of this is a problem only when login is suid, which it isn't.
451 if (!cxt
->tty_path
|| !*cxt
->tty_path
||
452 lstat(cxt
->tty_path
, &st
) != 0 || !S_ISCHR(st
.st_mode
) ||
453 (st
.st_nlink
> 1 && strncmp(cxt
->tty_path
, "/dev/", 5) != 0) ||
454 access(cxt
->tty_path
, R_OK
| W_OK
) != 0) {
456 syslog(LOG_ERR
, _("FATAL: bad tty"));
457 sleepexit(EXIT_FAILURE
);
460 #ifdef LOGIN_CHOWN_VCS
461 if (cxt
->tty_number
) {
462 /* find names of Virtual Console devices, for later mode change */
463 snprintf(cxt
->vcsn
, sizeof(cxt
->vcsn
), "/dev/vcs%s", cxt
->tty_number
);
464 snprintf(cxt
->vcsan
, sizeof(cxt
->vcsan
), "/dev/vcsa%s", cxt
->tty_number
);
470 ttt
.c_cflag
&= ~HUPCL
;
472 if ((fchown(0, 0, 0) || fchmod(0, cxt
->tty_mode
)) && errno
!= EROFS
) {
474 syslog(LOG_ERR
, _("FATAL: %s: change permissions failed: %m"),
476 sleepexit(EXIT_FAILURE
);
479 /* Kill processes left on this tty */
480 tcsetattr(0, TCSANOW
, &ttt
);
483 * Let's close file descriptors before vhangup
484 * https://lkml.org/lkml/2012/6/5/145
487 close(STDOUT_FILENO
);
488 close(STDERR_FILENO
);
490 signal(SIGHUP
, SIG_IGN
); /* so vhangup() won't kill us */
492 signal(SIGHUP
, SIG_DFL
);
494 /* open stdin,stdout,stderr to the tty */
495 open_tty(cxt
->tty_path
);
497 /* restore tty modes */
498 tcsetattr(0, TCSAFLUSH
, &tt
);
503 * Logs failed login attempts in _PATH_BTMP, if it exists.
504 * Must be called only with username the name of an actual user.
505 * The most common login failure is to give password instead of username.
507 static void log_btmp(struct login_context
*cxt
)
512 memset(&ut
, 0, sizeof(ut
));
514 str2memcpy(ut
.ut_user
,
515 cxt
->username
? cxt
->username
: "(unknown)",
519 str2memcpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
521 str2memcpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
523 gettimeofday(&tv
, NULL
);
524 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
525 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
527 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
528 ut
.ut_pid
= cxt
->pid
;
531 str2memcpy(ut
.ut_host
, cxt
->hostname
, sizeof(ut
.ut_host
));
532 if (*cxt
->hostaddress
)
533 memcpy(&ut
.ut_addr_v6
, cxt
->hostaddress
,
534 sizeof(ut
.ut_addr_v6
));
537 updwtmpx(_PATH_BTMP
, &ut
);
542 static void log_audit(struct login_context
*cxt
, int status
)
545 struct passwd
*pwd
= cxt
->pwd
;
547 audit_fd
= audit_open();
550 if (!pwd
&& cxt
->username
)
551 pwd
= getpwnam(cxt
->username
);
553 audit_log_acct_message(audit_fd
,
557 cxt
->username
? cxt
->username
: "(unknown)",
558 pwd
? pwd
->pw_uid
: (unsigned int) -1,
566 #else /* !HAVE_LIBAUDIT */
567 # define log_audit(cxt, status)
568 #endif /* HAVE_LIBAUDIT */
570 static void log_lastlog(struct login_context
*cxt
)
572 struct sigaction sa
, oldsa_xfsz
;
581 if (cxt
->pwd
->pw_uid
> (uid_t
) getlogindefs_num("LASTLOG_UID_MAX", ULONG_MAX
))
584 /* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
585 memset(&sa
, 0, sizeof(sa
));
586 sa
.sa_handler
= SIG_IGN
;
587 sigaction(SIGXFSZ
, &sa
, &oldsa_xfsz
);
589 fd
= open(_PATH_LASTLOG
, O_RDWR
, 0);
592 offset
= cxt
->pwd
->pw_uid
* sizeof(ll
);
595 * Print last log message.
598 if ((pread(fd
, (void *)&ll
, sizeof(ll
), offset
) == sizeof(ll
)) &&
600 char time_string
[CTIME_BUFSIZ
];
602 time_t ll_time
= (time_t) ll
.ll_time
;
604 ctime_r(&ll_time
, time_string
);
605 printf(_("Last login: %.*s "), 24 - 5, time_string
);
606 if (*ll
.ll_host
!= '\0')
607 printf(_("from %.*s\n"),
608 (int)sizeof(ll
.ll_host
), ll
.ll_host
);
610 printf(_("on %.*s\n"),
611 (int)sizeof(ll
.ll_line
), ll
.ll_line
);
615 memset((char *)&ll
, 0, sizeof(ll
));
618 ll
.ll_time
= t
; /* ll_time is always 32bit */
621 str2memcpy(ll
.ll_line
, cxt
->tty_name
, sizeof(ll
.ll_line
));
623 str2memcpy(ll
.ll_host
, cxt
->hostname
, sizeof(ll
.ll_host
));
625 if (pwrite(fd
, (void *)&ll
, sizeof(ll
), offset
) != sizeof(ll
))
626 warn(_("write lastlog failed"));
631 sigaction(SIGXFSZ
, &oldsa_xfsz
, NULL
); /* restore original setting */
635 * Update wtmp and utmp logs.
637 static void log_utmp(struct login_context
*cxt
)
643 utmpxname(_PATH_UTMP
);
648 * login sometimes overwrites the runlevel entry in /var/run/utmp,
649 * confusing sysvinit. I added a test for the entry type, and the
650 * problem was gone. (In a runlevel entry, st_pid is not really a pid
651 * but some number calculated from the previous and current runlevel.)
652 * -- Michael Riepe <michael@stud.uni-hannover.de>
654 while ((utp
= getutxent()))
655 if (utp
->ut_pid
== cxt
->pid
656 && utp
->ut_type
>= INIT_PROCESS
657 && utp
->ut_type
<= DEAD_PROCESS
)
660 /* If we can't find a pre-existing entry by pid, try by line.
661 * BSD network daemons may rely on this. */
662 if (utp
== NULL
&& cxt
->tty_name
) {
664 ut
.ut_type
= LOGIN_PROCESS
;
665 str2memcpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
666 utp
= getutxline(&ut
);
669 /* If we can't find a pre-existing entry by pid and line, try it by id.
670 * Very stupid telnetd daemons don't set up utmp at all. (kzak) */
671 if (utp
== NULL
&& cxt
->tty_number
) {
673 ut
.ut_type
= DEAD_PROCESS
;
674 str2memcpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
679 memcpy(&ut
, utp
, sizeof(ut
));
681 /* some gettys/telnetds don't initialize utmp... */
682 memset(&ut
, 0, sizeof(ut
));
684 if (cxt
->tty_number
&& ut
.ut_id
[0] == 0)
685 str2memcpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
687 str2memcpy(ut
.ut_user
, cxt
->username
, sizeof(ut
.ut_user
));
689 str2memcpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
691 gettimeofday(&tv
, NULL
);
692 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
693 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
694 ut
.ut_type
= USER_PROCESS
;
695 ut
.ut_pid
= cxt
->pid
;
697 str2memcpy(ut
.ut_host
, cxt
->hostname
, sizeof(ut
.ut_host
));
698 if (*cxt
->hostaddress
)
699 memcpy(&ut
.ut_addr_v6
, cxt
->hostaddress
,
700 sizeof(ut
.ut_addr_v6
));
706 updwtmpx(_PATH_WTMP
, &ut
);
709 static void log_syslog(struct login_context
*cxt
)
711 struct passwd
*pwd
= cxt
->pwd
;
716 if (!strncmp(cxt
->tty_name
, "ttyS", 4))
717 syslog(LOG_INFO
, _("DIALUP AT %s BY %s"),
718 cxt
->tty_name
, pwd
->pw_name
);
722 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s FROM %s"),
723 cxt
->tty_name
, cxt
->hostname
);
725 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s"), cxt
->tty_name
);
728 syslog(LOG_INFO
, _("LOGIN ON %s BY %s FROM %s"),
729 cxt
->tty_name
, pwd
->pw_name
, cxt
->hostname
);
731 syslog(LOG_INFO
, _("LOGIN ON %s BY %s"), cxt
->tty_name
,
736 /* encapsulate stupid "void **" pam_get_item() API */
737 static int loginpam_get_username(pam_handle_t
*pamh
, const char **name
)
739 const void *item
= (const void *)*name
;
741 rc
= pam_get_item(pamh
, PAM_USER
, &item
);
742 *name
= (const char *)item
;
746 static void loginpam_err(pam_handle_t
*pamh
, int retcode
)
748 const char *msg
= pam_strerror(pamh
, retcode
);
751 fprintf(stderr
, "\n%s\n", msg
);
752 syslog(LOG_ERR
, "%s", msg
);
754 pam_end(pamh
, retcode
);
755 sleepexit(EXIT_FAILURE
);
759 * Composes "<host> login: " string; or returns "login: " if -H is given or
760 * LOGIN_PLAIN_PROMPT=yes configured.
762 static const char *loginpam_get_prompt(struct login_context
*cxt
)
765 char *prompt
, *dflt_prompt
= _("login: ");
769 return dflt_prompt
; /* -H on command line */
771 if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
774 if (!(host
= get_thishost(cxt
, NULL
)))
777 sz
= strlen(host
) + 1 + strlen(dflt_prompt
) + 1;
778 prompt
= xmalloc(sz
);
779 snprintf(prompt
, sz
, "%s %s", host
, dflt_prompt
);
784 static pam_handle_t
*init_loginpam(struct login_context
*cxt
)
786 pam_handle_t
*pamh
= NULL
;
790 * username is initialized to NULL and if specified on the command line
791 * it is set. Therefore, we are safe not setting it to anything.
793 rc
= pam_start(cxt
->remote
? "remote" : "login",
794 cxt
->username
, &cxt
->conv
, &pamh
);
795 if (rc
!= PAM_SUCCESS
) {
796 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh
, rc
));
797 syslog(LOG_ERR
, _("Couldn't initialize PAM: %s"),
798 pam_strerror(pamh
, rc
));
799 sleepexit(EXIT_FAILURE
);
802 /* hostname & tty are either set to NULL or their correct values,
803 * depending on how much we know. */
804 rc
= pam_set_item(pamh
, PAM_RHOST
, cxt
->hostname
);
805 if (is_pam_failure(rc
))
806 loginpam_err(pamh
, rc
);
808 rc
= pam_set_item(pamh
, PAM_TTY
, cxt
->tty_name
);
809 if (is_pam_failure(rc
))
810 loginpam_err(pamh
, rc
);
813 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
814 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
815 * an interface to specify the "Password: " string (yet).
817 rc
= pam_set_item(pamh
, PAM_USER_PROMPT
, loginpam_get_prompt(cxt
));
818 if (is_pam_failure(rc
))
819 loginpam_err(pamh
, rc
);
821 /* We don't need the original username. We have to follow PAM. */
822 cxt
->username
= NULL
;
828 static void loginpam_auth(struct login_context
*cxt
)
830 int rc
, show_unknown
;
831 unsigned int retries
, failcount
= 0;
832 const char *hostname
= cxt
->hostname
? cxt
->hostname
:
833 cxt
->tty_name
? cxt
->tty_name
: "<unknown>";
834 pam_handle_t
*pamh
= cxt
->pamh
;
836 /* if we didn't get a user on the command line, set it to NULL */
837 loginpam_get_username(pamh
, &cxt
->username
);
839 show_unknown
= getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
840 retries
= getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES
);
843 * There may be better ways to deal with some of these conditions, but
844 * at least this way I don't think we'll be giving away information...
846 * Perhaps someday we can trust that all PAM modules will pay attention
847 * to failure count and get rid of LOGIN_MAX_TRIES?
849 rc
= pam_authenticate(pamh
, 0);
851 while ((++failcount
< retries
) &&
852 ((rc
== PAM_AUTH_ERR
) ||
853 (rc
== PAM_USER_UNKNOWN
) ||
854 (rc
== PAM_CRED_INSUFFICIENT
) ||
855 (rc
== PAM_AUTHINFO_UNAVAIL
))) {
857 if (rc
== PAM_USER_UNKNOWN
&& !show_unknown
)
859 * Logging unknown usernames may be a security issue if
860 * a user enters her password instead of her login name.
862 cxt
->username
= NULL
;
864 loginpam_get_username(pamh
, &cxt
->username
);
867 _("FAILED LOGIN %u FROM %s FOR %s, %s"),
869 cxt
->username
? cxt
->username
: "(unknown)",
870 pam_strerror(pamh
, rc
));
875 fprintf(stderr
, _("Login incorrect\n\n"));
877 pam_set_item(pamh
, PAM_USER
, NULL
);
878 rc
= pam_authenticate(pamh
, 0);
881 if (is_pam_failure(rc
)) {
883 if (rc
== PAM_USER_UNKNOWN
&& !show_unknown
)
884 cxt
->username
= NULL
;
886 loginpam_get_username(pamh
, &cxt
->username
);
888 if (rc
== PAM_MAXTRIES
)
890 _("TOO MANY LOGIN TRIES (%u) FROM %s FOR %s, %s"),
892 cxt
->username
? cxt
->username
: "(unknown)",
893 pam_strerror(pamh
, rc
));
896 _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
898 cxt
->username
? cxt
->username
: "(unknown)",
899 pam_strerror(pamh
, rc
));
904 fprintf(stderr
, _("\nLogin incorrect\n"));
906 sleepexit(EXIT_SUCCESS
);
910 static void loginpam_acct(struct login_context
*cxt
)
913 pam_handle_t
*pamh
= cxt
->pamh
;
915 rc
= pam_acct_mgmt(pamh
, 0);
917 if (rc
== PAM_NEW_AUTHTOK_REQD
)
918 rc
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
920 if (is_pam_failure(rc
))
921 loginpam_err(pamh
, rc
);
924 * Grab the user information out of the password file for future use.
925 * First get the username that we are actually using, though.
927 rc
= loginpam_get_username(pamh
, &cxt
->username
);
928 if (is_pam_failure(rc
))
929 loginpam_err(pamh
, rc
);
931 if (!cxt
->username
|| !*cxt
->username
) {
932 warnx(_("\nSession setup problem, abort."));
933 syslog(LOG_ERR
, _("NULL user name. Abort."));
934 pam_end(pamh
, PAM_SYSTEM_ERR
);
935 sleepexit(EXIT_FAILURE
);
940 * Note that the position of the pam_setcred() call is discussable:
942 * - the PAM docs recommend pam_setcred() before pam_open_session()
943 * - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
944 * uses pam_setcred() after pam_open_session()
946 * The old login versions (before year 2011) followed the RFC. This is probably
947 * not optimal, because there could be a dependence between some session modules
948 * and the user's credentials.
950 * The best is probably to follow openssh and call pam_setcred() before and
951 * after pam_open_session(). -- kzak@redhat.com (18-Nov-2011)
954 static void loginpam_session(struct login_context
*cxt
)
957 pam_handle_t
*pamh
= cxt
->pamh
;
959 rc
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
960 if (is_pam_failure(rc
))
961 loginpam_err(pamh
, rc
);
963 rc
= pam_open_session(pamh
, 0);
964 if (is_pam_failure(rc
)) {
965 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
966 loginpam_err(pamh
, rc
);
969 rc
= pam_setcred(pamh
, PAM_REINITIALIZE_CRED
);
970 if (is_pam_failure(rc
)) {
971 pam_close_session(pamh
, 0);
972 loginpam_err(pamh
, rc
);
977 * Detach the controlling terminal, fork, restore syslog stuff, and create
980 static void fork_session(struct login_context
*cxt
)
982 struct sigaction sa
, oldsa_hup
, oldsa_term
;
984 signal(SIGALRM
, SIG_DFL
);
985 signal(SIGQUIT
, SIG_DFL
);
986 signal(SIGTSTP
, SIG_IGN
);
988 memset(&sa
, 0, sizeof(sa
));
989 sa
.sa_handler
= SIG_IGN
;
990 sigaction(SIGINT
, &sa
, NULL
);
992 sigaction(SIGHUP
, &sa
, &oldsa_hup
); /* ignore when TIOCNOTTY */
995 * Detach the controlling tty.
996 * We don't need the tty in a parent who only waits for a child.
997 * The child calls setsid() that detaches from the tty as well.
999 ioctl(0, TIOCNOTTY
, NULL
);
1002 * We have to beware of SIGTERM, because leaving a PAM session
1003 * without pam_close_session() is a pretty bad thing.
1005 sa
.sa_handler
= sig_handler
;
1006 sigaction(SIGHUP
, &sa
, NULL
);
1007 sigaction(SIGTERM
, &sa
, &oldsa_term
);
1012 * We must fork before setuid(), because we need to call
1013 * pam_close_session() as root.
1016 if (child_pid
< 0) {
1017 warn(_("fork failed"));
1019 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1020 pam_end(cxt
->pamh
, pam_close_session(cxt
->pamh
, 0));
1021 sleepexit(EXIT_FAILURE
);
1026 * parent - wait for child to finish, then clean up session
1031 free_getlogindefs_data();
1033 sa
.sa_handler
= SIG_IGN
;
1034 sigaction(SIGQUIT
, &sa
, NULL
);
1035 sigaction(SIGINT
, &sa
, NULL
);
1037 /* wait as long as any child is there */
1038 while (wait(NULL
) == -1 && errno
== EINTR
) ;
1039 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
1041 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1042 pam_end(cxt
->pamh
, pam_close_session(cxt
->pamh
, 0));
1049 sigaction(SIGHUP
, &oldsa_hup
, NULL
); /* restore old state */
1050 sigaction(SIGTERM
, &oldsa_term
, NULL
);
1055 * Problem: if the user's shell is a shell like ash that doesn't do
1056 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1057 * process in the pgrp, will kill us.
1060 /* start new session */
1063 /* make sure we have a controlling tty */
1064 open_tty(cxt
->tty_path
);
1065 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
); /* reopen */
1068 * TIOCSCTTY: steal tty from other process group.
1070 if (ioctl(0, TIOCSCTTY
, 1))
1071 syslog(LOG_ERR
, _("TIOCSCTTY failed: %m"));
1072 signal(SIGINT
, SIG_DFL
);
1076 * Initialize $TERM, $HOME, ...
1078 static void init_environ(struct login_context
*cxt
)
1080 struct passwd
*pwd
= cxt
->pwd
;
1081 char *termenv
, **env
;
1085 termenv
= getenv("TERM");
1087 termenv
= xstrdup(termenv
);
1089 /* destroy environment unless user has requested preservation (-p) */
1090 if (!cxt
->keep_env
) {
1091 environ
= xmalloc(sizeof(char *));
1092 memset(environ
, 0, sizeof(char *));
1095 xsetenv("HOME", pwd
->pw_dir
, 0); /* legal to override */
1096 xsetenv("USER", pwd
->pw_name
, 1);
1097 xsetenv("SHELL", pwd
->pw_shell
, 1);
1098 xsetenv("TERM", termenv
? termenv
: "dumb", 1);
1102 if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH
) != 0)
1103 err(EXIT_FAILURE
, _("failed to set the %s environment variable"), "PATH");
1105 } else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL
) != 0 &&
1106 logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT
) != 0) {
1107 err(EXIT_FAILURE
, _("failed to set the %s environment variable"), "PATH");
1110 /* mailx will give a funny error msg if you forget this one */
1111 len
= snprintf(tmp
, sizeof(tmp
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
1112 if (len
> 0 && (size_t) len
< sizeof(tmp
))
1113 xsetenv("MAIL", tmp
, 0);
1115 /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1116 * not allow modifying it.
1118 xsetenv("LOGNAME", pwd
->pw_name
, 1);
1120 env
= pam_getenvlist(cxt
->pamh
);
1121 for (i
= 0; env
&& env
[i
]; i
++)
1126 * This is called for the -h option, initializes cxt->{hostname,hostaddress}.
1128 static void init_remote_info(struct login_context
*cxt
, char *remotehost
)
1132 struct addrinfo hints
, *info
= NULL
;
1136 get_thishost(cxt
, &domain
);
1138 if (domain
&& (p
= strchr(remotehost
, '.')) &&
1139 strcasecmp(p
+ 1, domain
) == 0)
1142 cxt
->hostname
= xstrdup(remotehost
);
1144 memset(&hints
, 0, sizeof(hints
));
1145 hints
.ai_flags
= AI_ADDRCONFIG
;
1146 cxt
->hostaddress
[0] = 0;
1148 if (getaddrinfo(cxt
->hostname
, NULL
, &hints
, &info
) == 0 && info
) {
1149 if (info
->ai_family
== AF_INET
) {
1150 struct sockaddr_in
*sa
=
1151 (struct sockaddr_in
*) info
->ai_addr
;
1153 memcpy(cxt
->hostaddress
, &(sa
->sin_addr
), sizeof(sa
->sin_addr
));
1155 } else if (info
->ai_family
== AF_INET6
) {
1156 struct sockaddr_in6
*sa
=
1157 (struct sockaddr_in6
*) info
->ai_addr
;
1158 #ifdef IN6_IS_ADDR_V4MAPPED
1159 if (IN6_IS_ADDR_V4MAPPED(&sa
->sin6_addr
)) {
1160 const uint8_t *bytes
= sa
->sin6_addr
.s6_addr
;
1161 struct in_addr addr
= { *(const in_addr_t
*) (bytes
+ 12) };
1163 memcpy(cxt
->hostaddress
, &addr
, sizeof(struct in_addr
));
1166 memcpy(cxt
->hostaddress
, &(sa
->sin6_addr
), sizeof(sa
->sin6_addr
));
1172 static void __attribute__((__noreturn__
)) usage(void)
1174 fputs(USAGE_HEADER
, stdout
);
1175 printf(_(" %s [-p] [-h <host>] [-H] [[-f] <username>]\n"), program_invocation_short_name
);
1176 fputs(USAGE_SEPARATOR
, stdout
);
1177 fputs(_("Begin a session on the system.\n"), stdout
);
1179 fputs(USAGE_OPTIONS
, stdout
);
1180 puts(_(" -p do not destroy the environment"));
1181 puts(_(" -f skip a login authentication"));
1182 puts(_(" -h <host> hostname to be used for utmp logging"));
1183 puts(_(" -H suppress hostname in the login prompt"));
1184 printf(" --help %s\n", USAGE_OPTSTR_HELP
);
1185 printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION
);
1186 printf(USAGE_MAN_TAIL("login(1)"));
1190 int main(int argc
, char **argv
)
1193 char *childArgv
[10];
1197 struct sigaction act
;
1199 static const int wanted_fds
[] = {
1200 STDIN_FILENO
, STDOUT_FILENO
, STDERR_FILENO
1202 struct login_context cxt
= {
1203 .tty_mode
= TTY_MODE
, /* tty chmod() */
1204 .pid
= getpid(), /* PID */
1205 #ifdef HAVE_SECURITY_PAM_MISC_H
1206 .conv
= { misc_conv
, NULL
} /* Linux-PAM conversation function */
1207 #elif defined(HAVE_SECURITY_OPENPAM_H)
1208 .conv
= { openpam_ttyconv
, NULL
} /* OpenPAM conversation function */
1213 /* the only two longopts to satisfy UL standards */
1214 enum { HELP_OPTION
= CHAR_MAX
+ 1 };
1215 static const struct option longopts
[] = {
1216 {"help", no_argument
, NULL
, HELP_OPTION
},
1217 {"version", no_argument
, NULL
, 'V'},
1221 timeout
= (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT
);
1223 setlocale(LC_ALL
, "");
1224 bindtextdomain(PACKAGE
, LOCALEDIR
);
1225 textdomain(PACKAGE
);
1227 /* TRANSLATORS: The standard value for %u is 60. */
1228 snprintf(timeout_msg
, sizeof(timeout_msg
),
1229 _("%s: timed out after %u seconds"),
1230 program_invocation_short_name
, timeout
);
1232 signal(SIGALRM
, timedout
);
1233 (void) sigaction(SIGALRM
, NULL
, &act
);
1234 act
.sa_flags
&= ~SA_RESTART
;
1235 sigaction(SIGALRM
, &act
, NULL
);
1237 signal(SIGQUIT
, SIG_IGN
);
1238 signal(SIGINT
, SIG_IGN
);
1240 setpriority(PRIO_PROCESS
, 0, 0);
1241 initproctitle(argc
, argv
);
1243 while ((c
= getopt_long(argc
, argv
, "fHh:pV", longopts
, NULL
)) != -1)
1256 _("login: -h is for superuser only\n"));
1259 init_remote_info(&cxt
, optarg
);
1267 print_version(EXIT_SUCCESS
);
1271 errtryhelp(EXIT_FAILURE
);
1279 /* username from command line */
1280 cxt
.cmd_username
= xstrdup(p
);
1281 /* used temporary, it'll be replaced by username from PAM or/and cxt->pwd */
1282 cxt
.username
= cxt
.cmd_username
;
1284 /* Wipe the name - some people mistype their password here. */
1285 /* (Of course we are too late, but perhaps this helps a little...) */
1290 close_all_fds(wanted_fds
, ARRAY_SIZE(wanted_fds
));
1292 setpgrp(); /* set pgid to pid this means that setsid() will fail */
1295 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
1297 init_loginpam(&cxt
);
1299 /* login -f, then the user has already been authenticated */
1300 cxt
.noauth
= cxt
.noauth
&& getuid() == 0 ? 1 : 0;
1303 loginpam_auth(&cxt
);
1306 * Authentication may be skipped (for example, during krlogin, rlogin,
1307 * etc...), but it doesn't mean that we can skip other account checks.
1308 * The account could be disabled or the password has expired (although
1309 * the kerberos ticket is valid). -- kzak@redhat.com (22-Feb-2006)
1311 loginpam_acct(&cxt
);
1313 cxt
.pwd
= xgetpwnam(cxt
.username
, &cxt
.pwdbuf
);
1315 warnx(_("\nSession setup problem, abort."));
1316 syslog(LOG_ERR
, _("Invalid user name \"%s\". Abort."),
1318 pam_end(cxt
.pamh
, PAM_SYSTEM_ERR
);
1319 sleepexit(EXIT_FAILURE
);
1323 cxt
.username
= pwd
->pw_name
;
1326 * Initialize the supplementary group list. This should be done before
1327 * pam_setcred, because PAM modules might add groups during that call.
1329 * For root we don't call initgroups, instead we call setgroups with
1330 * group 0. This avoids the need to step through the whole group file,
1331 * which can cause problems if NIS, NIS+, LDAP or something similar
1332 * is used and the machine has network problems.
1334 retcode
= pwd
->pw_uid
? initgroups(cxt
.username
, pwd
->pw_gid
) : /* user */
1335 setgroups(0, NULL
); /* root */
1337 syslog(LOG_ERR
, _("groups initialization failed: %m"));
1338 warnx(_("\nSession setup problem, abort."));
1339 pam_end(cxt
.pamh
, PAM_SYSTEM_ERR
);
1340 sleepexit(EXIT_FAILURE
);
1344 * Open PAM session (after successful authentication and account check).
1346 loginpam_session(&cxt
);
1348 /* committed to login -- turn off timeout */
1349 alarm((unsigned int)0);
1353 cxt
.quiet
= get_hushlogin_status(pwd
, 1);
1361 if (setgid(pwd
->pw_gid
) < 0 && pwd
->pw_gid
) {
1362 syslog(LOG_ALERT
, _("setgid() failed"));
1366 if (pwd
->pw_shell
== NULL
|| *pwd
->pw_shell
== '\0')
1367 pwd
->pw_shell
= _PATH_BSHELL
;
1369 init_environ(&cxt
); /* init $HOME, $TERM ... */
1371 setproctitle("login", cxt
.username
);
1378 #ifdef LOGIN_STAT_MAIL
1380 * This turns out to be a bad idea: when the mail spool
1381 * is NFS mounted, and the NFS connection hangs, the
1382 * login hangs, even root cannot login.
1383 * Checking for mail should be done from the shell.
1389 mail
= getenv("MAIL");
1390 if (mail
&& stat(mail
, &st
) == 0 && st
.st_size
!= 0) {
1391 if (st
.st_mtime
> st
.st_atime
)
1392 printf(_("You have new mail.\n"));
1394 printf(_("You have mail.\n"));
1401 * Detach the controlling terminal, fork, and create a new session
1402 * and reinitialize syslog stuff.
1406 /* discard permissions last so we can't get killed and drop core */
1407 if (setuid(pwd
->pw_uid
) < 0 && pwd
->pw_uid
) {
1408 syslog(LOG_ALERT
, _("setuid() failed"));
1412 /* wait until here to change directory! */
1413 if (chdir(pwd
->pw_dir
) < 0) {
1414 warn(_("%s: change directory failed"), pwd
->pw_dir
);
1416 if (!getlogindefs_bool("DEFAULT_HOME", 1))
1421 printf(_("Logging in with home = \"/\".\n"));
1424 /* if the shell field has a space: treat it like a shell script */
1425 if (strchr(pwd
->pw_shell
, ' ')) {
1426 xasprintf(&buff
, "exec %s", pwd
->pw_shell
);
1427 childArgv
[childArgc
++] = "/bin/sh";
1428 childArgv
[childArgc
++] = "-sh";
1429 childArgv
[childArgc
++] = "-c";
1430 childArgv
[childArgc
++] = buff
;
1432 char tbuf
[PATH_MAX
+ 2], *p
;
1435 xstrncpy(tbuf
+ 1, ((p
= strrchr(pwd
->pw_shell
, '/')) ?
1436 p
+ 1 : pwd
->pw_shell
), sizeof(tbuf
) - 1);
1438 childArgv
[childArgc
++] = pwd
->pw_shell
;
1439 childArgv
[childArgc
++] = xstrdup(tbuf
);
1442 childArgv
[childArgc
++] = NULL
;
1444 execvp(childArgv
[0], childArgv
+ 1);
1446 if (!strcmp(childArgv
[0], "/bin/sh"))
1447 warn(_("couldn't exec shell script"));
1449 warn(_("no shell"));