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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 #include <sys/param.h>
38 #include <sys/resource.h>
42 #include <sys/ioctl.h>
50 #include <sys/syslog.h>
51 #include <sys/sysmacros.h>
52 #ifdef HAVE_LINUX_MAJOR_H
53 # include <linux/major.h>
57 #include <security/pam_appl.h>
58 #include <security/pam_misc.h>
59 #include <sys/sendfile.h>
62 # include <libaudit.h>
66 #include "setproctitle.h"
67 #include "pathnames.h"
72 #include "fileutils.h"
74 #include "logindefs.h"
76 #define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
78 #define LOGIN_MAX_TRIES 3
79 #define LOGIN_EXIT_TIMEOUT 5
80 #define LOGIN_TIMEOUT 60
83 # define TTY_MODE 0620
85 # define TTY_MODE 0600
88 #define TTYGRPNAME "tty" /* name of group to own ttys */
89 #define VCS_PATH_MAX 64
92 * Login control struct
94 struct login_context
{
95 const char *tty_path
; /* ttyname() return value */
96 const char *tty_name
; /* tty_path without /dev prefix */
97 const char *tty_number
; /* end of the tty_path */
98 mode_t tty_mode
; /* chmod() mode */
100 char *username
; /* from command line or PAM */
102 struct passwd
*pwd
; /* user info */
104 pam_handle_t
*pamh
; /* PAM handler */
105 struct pam_conv conv
; /* PAM conversation */
107 #ifdef LOGIN_CHOWN_VCS
108 char vcsn
[VCS_PATH_MAX
]; /* virtual console name */
109 char vcsan
[VCS_PATH_MAX
];
112 char thishost
[MAXHOSTNAMELEN
+ 1]; /* this machine */
113 char *thisdomain
; /* this machine domain */
114 char *hostname
; /* remote machine */
115 char hostaddress
[16]; /* remote address */
118 int quiet
; /* 1 is hush file exists */
120 unsigned int remote
:1, /* login -h */
121 nohost
:1, /* login -H */
122 noauth
:1, /* login -f */
123 keep_env
:1; /* login -p */
127 * This bounds the time given to login. Not a define so it can
128 * be patched on machines where it's too small.
130 static int timeout
= LOGIN_TIMEOUT
;
131 static int child_pid
= 0;
132 static volatile int got_sig
= 0;
134 #ifdef LOGIN_CHOWN_VCS
135 /* true if the filedescriptor fd is a console tty, very Linux specific */
136 static int is_consoletty(int fd
)
140 if ((fstat(fd
, &stb
) >= 0)
141 && (major(stb
.st_rdev
) == TTY_MAJOR
)
142 && (minor(stb
.st_rdev
) < 64)) {
151 * Robert Ambrose writes:
152 * A couple of my users have a problem with login processes hanging around
153 * soaking up pts's. What they seem to hung up on is trying to write out the
154 * message 'Login timed out after %d seconds' when the connection has already
156 * What I did was add a second timeout while trying to write the message so
157 * the process just exits if the second timeout expires.
159 static void __attribute__ ((__noreturn__
))
160 timedout2(int sig
__attribute__ ((__unused__
)))
167 tcsetattr(0, TCSANOW
, &ti
);
168 exit(EXIT_SUCCESS
); /* %% */
171 static void timedout(int sig
__attribute__ ((__unused__
)))
173 signal(SIGALRM
, timedout2
);
175 /* TRANSLATORS: The standard value for %d is 60. */
176 warnx(_("timed out after %d seconds"), timeout
);
177 signal(SIGALRM
, SIG_IGN
);
183 * This handler allows to inform a shell about signals to login. If you have
184 * (root) permissions you can kill all login childrent by one signal to login
187 * Also, parent who is session leader is able (before setsid() in child) to
188 * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
190 static void sig_handler(int signal
)
193 kill(-child_pid
, signal
);
196 if (signal
== SIGTERM
)
197 kill(-child_pid
, SIGHUP
); /* because the shell often ignores SIGTERM */
201 * Let use delay for all exit() calls when user is not authenticated or
202 * session fully initialized (loginpam_session()).
204 static void __attribute__ ((__noreturn__
)) sleepexit(int eval
)
206 sleep(getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT
));
210 static const char *get_thishost(struct login_context
*cxt
, const char **domain
)
212 if (!*cxt
->thishost
) {
213 if (gethostname(cxt
->thishost
, sizeof(cxt
->thishost
))) {
218 cxt
->thishost
[sizeof(cxt
->thishost
) -1] = '\0';
219 cxt
->thisdomain
= strchr(cxt
->thishost
, '.');
221 *cxt
->thisdomain
++ = '\0';
225 *domain
= cxt
->thisdomain
;
226 return cxt
->thishost
;
230 * Output the /etc/motd file
232 * motd() determines the name of a login announcement file and outputs it to
233 * the user's terminal at login time. The MOTD_FILE configuration option is a
234 * colon-delimited list of filenames. The empty MOTD_FILE option disables motd
237 static void motd(void)
239 char *motdlist
, *motdfile
;
242 mb
= getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE
);
246 motdlist
= xstrdup(mb
);
248 for (motdfile
= strtok(motdlist
, ":"); motdfile
;
249 motdfile
= strtok(NULL
, ":")) {
254 if (stat(motdfile
, &st
) || !st
.st_size
)
256 fd
= open(motdfile
, O_RDONLY
, 0);
260 sendfile(fileno(stdout
), fd
, NULL
, st
.st_size
);
268 * Nice and simple code provided by Linus Torvalds 16-Feb-93
269 * Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
271 * He writes: "Login performs open() on a tty in a blocking mode.
272 * In some cases it may make login wait in open() for carrier infinitely,
273 * for example if the line is a simplistic case of a three-wire serial
274 * connection. I believe login should open the line in the non-blocking mode
275 * leaving the decision to make a connection to getty (where it actually
278 static void open_tty(const char *tty
)
282 fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
284 syslog(LOG_ERR
, _("FATAL: can't reopen tty: %m"));
285 sleepexit(EXIT_FAILURE
);
290 syslog(LOG_ERR
, _("FATAL: %s is not a terminal"), tty
);
291 sleepexit(EXIT_FAILURE
);
294 flags
= fcntl(fd
, F_GETFL
);
295 flags
&= ~O_NONBLOCK
;
296 fcntl(fd
, F_SETFL
, flags
);
298 for (i
= 0; i
< fd
; i
++)
300 for (i
= 0; i
< 3; i
++)
307 #define chown_err(_what, _uid, _gid) \
308 syslog(LOG_ERR, _("chown (%s, %lu, %lu) failed: %m"), \
309 (_what), (unsigned long) (_uid), (unsigned long) (_gid))
311 #define chmod_err(_what, _mode) \
312 syslog(LOG_ERR, _("chmod (%s, %u) failed: %m"), (_what), (_mode))
314 static void chown_tty(struct login_context
*cxt
)
317 uid_t uid
= cxt
->pwd
->pw_uid
;
318 gid_t gid
= cxt
->pwd
->pw_gid
;
320 grname
= getlogindefs_str("TTYGROUP", TTYGRPNAME
);
321 if (grname
&& *grname
) {
322 if (*grname
>= 0 && *grname
<= 9) /* group by ID */
323 gid
= getlogindefs_num("TTYGROUP", gid
);
324 else { /* group by name */
325 struct group
*gr
= getgrnam(grname
);
331 if (fchown(0, uid
, gid
)) /* tty */
332 chown_err(cxt
->tty_name
, uid
, gid
);
333 if (fchmod(0, cxt
->tty_mode
))
334 chmod_err(cxt
->tty_name
, cxt
->tty_mode
);
336 #ifdef LOGIN_CHOWN_VCS
337 if (is_consoletty(0)) {
338 if (chown(cxt
->vcsn
, uid
, gid
)) /* vcs */
339 chown_err(cxt
->vcsn
, uid
, gid
);
340 if (chmod(cxt
->vcsn
, cxt
->tty_mode
))
341 chmod_err(cxt
->vcsn
, cxt
->tty_mode
);
343 if (chown(cxt
->vcsan
, uid
, gid
)) /* vcsa */
344 chown_err(cxt
->vcsan
, uid
, gid
);
345 if (chmod(cxt
->vcsan
, cxt
->tty_mode
))
346 chmod_err(cxt
->vcsan
, cxt
->tty_mode
);
352 * Reads the currect terminal path and initialize cxt->tty_* variables.
354 static void init_tty(struct login_context
*cxt
)
358 struct termios tt
, ttt
;
360 cxt
->tty_mode
= (mode_t
) getlogindefs_num("TTYPERM", TTY_MODE
);
362 cxt
->tty_path
= ttyname(0); /* libc calls istty() here */
365 * In case login is suid it was possible to use a hardlink as stdin
366 * and exploit races for a local root exploit. (Wojciech Purczynski).
368 * More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
369 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
370 * All of this is a problem only when login is suid, which it isnt.
372 if (!cxt
->tty_path
|| !*cxt
->tty_path
||
373 lstat(cxt
->tty_path
, &st
) != 0 || !S_ISCHR(st
.st_mode
) ||
374 (st
.st_nlink
> 1 && strncmp(cxt
->tty_path
, "/dev/", 5)) ||
375 access(cxt
->tty_path
, R_OK
| W_OK
) != 0) {
377 syslog(LOG_ERR
, _("FATAL: bad tty"));
378 sleepexit(EXIT_FAILURE
);
381 if (strncmp(cxt
->tty_path
, "/dev/", 5) == 0)
382 cxt
->tty_name
= cxt
->tty_path
+ 5;
384 cxt
->tty_name
= cxt
->tty_path
;
386 for (p
= cxt
->tty_name
; p
&& *p
; p
++) {
393 #ifdef LOGIN_CHOWN_VCS
394 if (cxt
->tty_number
) {
395 /* find names of Virtual Console devices, for later mode change */
396 snprintf(cxt
->vcsn
, sizeof(cxt
->vcsn
), "/dev/vcs%s", cxt
->tty_number
);
397 snprintf(cxt
->vcsan
, sizeof(cxt
->vcsan
), "/dev/vcsa%s", cxt
->tty_number
);
403 ttt
.c_cflag
&= ~HUPCL
;
405 if ((fchown(0, 0, 0) || fchmod(0, cxt
->tty_mode
)) && errno
!= EROFS
) {
407 syslog(LOG_ERR
, _("FATAL: %s: change permissions failed: %m"),
409 sleepexit(EXIT_FAILURE
);
412 /* Kill processes left on this tty */
413 tcsetattr(0, TCSAFLUSH
, &ttt
);
415 signal(SIGHUP
, SIG_IGN
); /* so vhangup() wont kill us */
417 signal(SIGHUP
, SIG_DFL
);
419 /* open stdin,stdout,stderr to the tty */
420 open_tty(cxt
->tty_path
);
422 /* restore tty modes */
423 tcsetattr(0, TCSAFLUSH
, &tt
);
428 * Log failed login attempts in _PATH_BTMP if that exists.
429 * Must be called only with username the name of an actual user.
430 * The most common login failure is to give password instead of username.
432 static void log_btmp(struct login_context
*cxt
)
437 memset(&ut
, 0, sizeof(ut
));
440 cxt
->username
? cxt
->username
: "(unknown)",
444 strncpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
446 xstrncpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
448 #if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
449 gettimeofday(&tv
, NULL
);
450 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
451 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
456 ut
.ut_time
= t
; /* ut_time is not always a time_t */
460 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
461 ut
.ut_pid
= cxt
->pid
;
464 xstrncpy(ut
.ut_host
, cxt
->hostname
, sizeof(ut
.ut_host
));
465 if (*cxt
->hostaddress
)
466 memcpy(&ut
.ut_addr_v6
, cxt
->hostaddress
,
467 sizeof(ut
.ut_addr_v6
));
470 updwtmp(_PATH_BTMP
, &ut
);
475 static void log_audit(struct login_context
*cxt
, int status
)
478 struct passwd
*pwd
= cxt
->pwd
;
480 audit_fd
= audit_open();
483 if (!pwd
&& cxt
->username
)
484 pwd
= getpwnam(cxt
->username
);
486 audit_log_acct_message(audit_fd
,
490 cxt
->username
? cxt
->username
: "(unknown)",
491 pwd
? pwd
->pw_uid
: (unsigned int) -1,
499 #else /* !HAVE_LIBAUDIT */
500 # define log_audit(cxt, status)
501 #endif /* HAVE_LIBAUDIT */
503 static void log_lastlog(struct login_context
*cxt
)
512 fd
= open(_PATH_LASTLOG
, O_RDWR
, 0);
516 lseek(fd
, (off_t
) cxt
->pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
519 * Print last log message
522 if (read(fd
, (char *)&ll
, sizeof(ll
)) == sizeof(ll
) &&
524 time_t ll_time
= (time_t) ll
.ll_time
;
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
);
531 printf(_("on %.*s\n"),
532 (int)sizeof(ll
.ll_line
), ll
.ll_line
);
534 lseek(fd
, (off_t
) cxt
->pwd
->pw_uid
* sizeof(ll
), SEEK_SET
);
537 memset((char *)&ll
, 0, sizeof(ll
));
540 ll
.ll_time
= t
; /* ll_time is always 32bit */
543 xstrncpy(ll
.ll_line
, cxt
->tty_name
, sizeof(ll
.ll_line
));
545 xstrncpy(ll
.ll_host
, cxt
->hostname
, sizeof(ll
.ll_host
));
547 if (write_all(fd
, (char *)&ll
, sizeof(ll
)))
548 warn(_("write lastlog failed"));
554 * Update wtmp and utmp logs
556 static void log_utmp(struct login_context
*cxt
)
562 utmpname(_PATH_UTMP
);
567 * login sometimes overwrites the runlevel entry in /var/run/utmp,
568 * confusing sysvinit. I added a test for the entry type, and the
569 * problem was gone. (In a runlevel entry, st_pid is not really a pid
570 * but some number calculated from the previous and current runlevel).
571 * -- Michael Riepe <michael@stud.uni-hannover.de>
573 while ((utp
= getutent()))
574 if (utp
->ut_pid
== cxt
->pid
575 && utp
->ut_type
>= INIT_PROCESS
576 && utp
->ut_type
<= DEAD_PROCESS
)
579 /* If we can't find a pre-existing entry by pid, try by line.
580 * BSD network daemons may rely on this.
584 ut
.ut_type
= LOGIN_PROCESS
;
586 strncpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
587 utp
= getutline(&ut
);
591 memcpy(&ut
, utp
, sizeof(ut
));
593 /* some gettys/telnetds don't initialize utmp... */
594 memset(&ut
, 0, sizeof(ut
));
596 if (cxt
->tty_number
&& ut
.ut_id
[0] == 0)
597 strncpy(ut
.ut_id
, cxt
->tty_number
, sizeof(ut
.ut_id
));
599 strncpy(ut
.ut_user
, cxt
->username
, sizeof(ut
.ut_user
));
601 xstrncpy(ut
.ut_line
, cxt
->tty_name
, sizeof(ut
.ut_line
));
603 #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
604 gettimeofday(&tv
, NULL
);
605 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
606 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
611 ut
.ut_time
= t
; /* ut_time is not always a time_t */
612 /* glibc2 #defines it as ut_tv.tv_sec */
615 ut
.ut_type
= USER_PROCESS
;
616 ut
.ut_pid
= cxt
->pid
;
618 xstrncpy(ut
.ut_host
, cxt
->hostname
, sizeof(ut
.ut_host
));
619 if (*cxt
->hostaddress
)
620 memcpy(&ut
.ut_addr_v6
, cxt
->hostaddress
,
621 sizeof(ut
.ut_addr_v6
));
627 updwtmp(_PATH_WTMP
, &ut
);
630 static void log_syslog(struct login_context
*cxt
)
632 struct passwd
*pwd
= cxt
->pwd
;
637 if (!strncmp(cxt
->tty_name
, "ttyS", 4))
638 syslog(LOG_INFO
, _("DIALUP AT %s BY %s"),
639 cxt
->tty_name
, pwd
->pw_name
);
643 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s FROM %s"),
644 cxt
->tty_name
, cxt
->hostname
);
646 syslog(LOG_NOTICE
, _("ROOT LOGIN ON %s"), cxt
->tty_name
);
649 syslog(LOG_INFO
, _("LOGIN ON %s BY %s FROM %s"),
650 cxt
->tty_name
, pwd
->pw_name
, cxt
->hostname
);
652 syslog(LOG_INFO
, _("LOGIN ON %s BY %s"), cxt
->tty_name
,
657 static struct passwd
*get_passwd_entry(const char *username
,
661 struct passwd
*res
= NULL
;
665 if (!pwdbuf
|| !username
)
668 #ifdef _SC_GETPW_R_SIZE_MAX
670 long xsz
= sysconf(_SC_GETPW_R_SIZE_MAX
);
675 *pwdbuf
= xrealloc(*pwdbuf
, sz
);
677 x
= getpwnam_r(username
, pwd
, *pwdbuf
, sz
, &res
);
685 /* encapsulate stupid "void **" pam_get_item() API */
686 static int loginpam_get_username(pam_handle_t
*pamh
, char **name
)
688 const void *item
= (void *)*name
;
690 rc
= pam_get_item(pamh
, PAM_USER
, &item
);
691 *name
= (char *)item
;
695 static void loginpam_err(pam_handle_t
*pamh
, int retcode
)
697 const char *msg
= pam_strerror(pamh
, retcode
);
700 fprintf(stderr
, "\n%s\n", msg
);
701 syslog(LOG_ERR
, "%s", msg
);
703 pam_end(pamh
, retcode
);
704 sleepexit(EXIT_FAILURE
);
708 * Composes "<host> login: " string; or returns "login: " is -H is given
710 static const char *loginpam_get_prompt(struct login_context
*cxt
)
713 char *prompt
, *dflt_prompt
= _("login: ");
716 if (cxt
->nohost
|| !(host
= get_thishost(cxt
, NULL
)))
719 sz
= strlen(host
) + 1 + strlen(dflt_prompt
) + 1;
721 prompt
= xmalloc(sz
);
722 snprintf(prompt
, sz
, "%s %s", host
, dflt_prompt
);
727 static pam_handle_t
*init_loginpam(struct login_context
*cxt
)
729 pam_handle_t
*pamh
= NULL
;
733 * username is initialized to NULL and if specified on the command line
734 * it is set. Therefore, we are safe not setting it to anything
736 rc
= pam_start(cxt
->remote
? "remote" : "login",
737 cxt
->username
, &cxt
->conv
, &pamh
);
738 if (rc
!= PAM_SUCCESS
) {
739 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh
, rc
));
740 syslog(LOG_ERR
, _("Couldn't initialize PAM: %s"),
741 pam_strerror(pamh
, rc
));
742 sleepexit(EXIT_FAILURE
);
745 /* hostname & tty are either set to NULL or their correct values,
746 * depending on how much we know
748 rc
= pam_set_item(pamh
, PAM_RHOST
, cxt
->hostname
);
749 if (is_pam_failure(rc
))
750 loginpam_err(pamh
, rc
);
752 rc
= pam_set_item(pamh
, PAM_TTY
, cxt
->tty_name
);
753 if (is_pam_failure(rc
))
754 loginpam_err(pamh
, rc
);
757 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
758 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
759 * an interface to specify the "Password: " string (yet).
761 rc
= pam_set_item(pamh
, PAM_USER_PROMPT
, loginpam_get_prompt(cxt
));
762 if (is_pam_failure(rc
))
763 loginpam_err(pamh
, rc
);
765 /* we need't the original username. We have to follow PAM. */
767 cxt
->username
= NULL
;
773 static void loginpam_auth(struct login_context
*cxt
)
775 int rc
, failcount
= 0, show_unknown
, retries
;
776 const char *hostname
= cxt
->hostname
? cxt
->hostname
:
777 cxt
->tty_name
? cxt
->tty_name
: "<unknown>";
778 pam_handle_t
*pamh
= cxt
->pamh
;
780 /* if we didn't get a user on the command line, set it to NULL */
781 loginpam_get_username(pamh
, &cxt
->username
);
783 show_unknown
= getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
784 retries
= getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES
);
787 * There may be better ways to deal with some of these conditions, but
788 * at least this way I don't think we'll be giving away information...
790 * Perhaps someday we can trust that all PAM modules will pay attention
791 * to failure count and get rid of LOGIN_MAX_TRIES?
793 rc
= pam_authenticate(pamh
, 0);
795 while ((++failcount
< retries
) &&
796 ((rc
== PAM_AUTH_ERR
) ||
797 (rc
== PAM_USER_UNKNOWN
) ||
798 (rc
== PAM_CRED_INSUFFICIENT
) ||
799 (rc
== PAM_AUTHINFO_UNAVAIL
))) {
801 if (rc
== PAM_USER_UNKNOWN
&& !show_unknown
)
803 * logging unknown usernames may be a security issue if
804 * an user enter her password instead of her login name
806 cxt
->username
= NULL
;
808 loginpam_get_username(pamh
, &cxt
->username
);
811 _("FAILED LOGIN %d FROM %s FOR %s, %s"),
813 cxt
->username
? cxt
->username
: "(unknown)",
814 pam_strerror(pamh
, rc
));
819 fprintf(stderr
, _("Login incorrect\n\n"));
821 pam_set_item(pamh
, PAM_USER
, NULL
);
822 rc
= pam_authenticate(pamh
, 0);
825 if (is_pam_failure(rc
)) {
827 if (rc
== PAM_USER_UNKNOWN
&& !show_unknown
)
828 cxt
->username
= NULL
;
830 loginpam_get_username(pamh
, &cxt
->username
);
832 if (rc
== PAM_MAXTRIES
)
834 _("TOO MANY LOGIN TRIES (%d) FROM %s FOR %s, %s"),
836 cxt
->username
? cxt
->username
: "(unknown)",
837 pam_strerror(pamh
, rc
));
840 _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
842 cxt
->username
? cxt
->username
: "(unknown)",
843 pam_strerror(pamh
, rc
));
848 fprintf(stderr
, _("\nLogin incorrect\n"));
850 sleepexit(EXIT_SUCCESS
);
854 static void loginpam_acct(struct login_context
*cxt
)
857 pam_handle_t
*pamh
= cxt
->pamh
;
859 rc
= pam_acct_mgmt(pamh
, 0);
861 if (rc
== PAM_NEW_AUTHTOK_REQD
)
862 rc
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
864 if (is_pam_failure(rc
))
865 loginpam_err(pamh
, rc
);
868 * Grab the user information out of the password file for future usage
869 * First get the username that we are actually using, though.
871 rc
= loginpam_get_username(pamh
, &cxt
->username
);
872 if (is_pam_failure(rc
))
873 loginpam_err(pamh
, rc
);
875 if (!cxt
->username
|| !*cxt
->username
) {
876 warnx(_("\nSession setup problem, abort."));
877 syslog(LOG_ERR
, _("NULL user name in %s:%d. Abort."),
878 __FUNCTION__
, __LINE__
);
879 pam_end(pamh
, PAM_SYSTEM_ERR
);
880 sleepexit(EXIT_FAILURE
);
885 * Note that position of the pam_setcred() call is discussable:
887 * - the PAM docs recommends pam_setcred() before pam_open_session()
888 * - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
889 * uses pam_setcred() after pam_open_session()
891 * The old login versions (before year 2011) followed the RFC. This is probably
892 * not optimal, because there could be dependence between some session modules
893 * and user's credentials.
895 * The best is probably to follow openssh and call pam_setcred() before and
896 * after pam_open_session(). -- kzak@redhat.com (18-Nov-2011)
899 static void loginpam_session(struct login_context
*cxt
)
902 pam_handle_t
*pamh
= cxt
->pamh
;
904 rc
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
);
905 if (is_pam_failure(rc
))
906 loginpam_err(pamh
, rc
);
908 rc
= pam_open_session(pamh
, 0);
909 if (is_pam_failure(rc
)) {
910 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
911 loginpam_err(pamh
, rc
);
914 rc
= pam_setcred(pamh
, PAM_REINITIALIZE_CRED
);
915 if (is_pam_failure(rc
)) {
916 pam_close_session(pamh
, 0);
917 loginpam_err(pamh
, rc
);
922 * We need to check effective UID/GID. For example $HOME could be on root
923 * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
924 * The open(2) seems as the surest solution.
925 * -- kzak@redhat.com (10-Apr-2009)
927 static int effective_access(const char *path
, int mode
)
929 int fd
= open(path
, mode
);
932 return fd
== -1 ? -1 : 0;
936 * Check per accout or global hush-login setting.
938 * Hushed mode is enabled:
940 * a) if global (e.g. /etc/hushlogins) hush file exists:
941 * 1) for ALL ACCOUNTS if the file is empty
942 * 2) for the current user if the username or shell are found in the file
944 * b) if ~/.hushlogin file exists
946 * The ~/.hushlogin is ignored if the global hush file exists.
948 * The HUSHLOGIN_FILE login.def variable overwrites the default hush filename.
950 * Note that shadow-utils login(1) does not support "a1)". The "a1)" is
951 * necessary if you want to use PAM for "Last login" message.
953 * -- Karel Zak <kzak@redhat.com> (26-Aug-2011)
956 * Per-account check requires some explanation: As root we may not be able to
957 * read the directory of the user if it is on an NFS mounted filesystem. We
958 * temporarily set our effective uid to the user-uid making sure that we keep
959 * root privs. in the real uid.
961 * A portable solution would require a fork(), but we rely on Linux having the
964 static int get_hushlogin_status(struct passwd
*pwd
)
966 const char *files
[] = { _PATH_HUSHLOGINS
, _PATH_HUSHLOGIN
, NULL
};
971 file
= getlogindefs_str("HUSHLOGIN_FILE", NULL
);
974 return 0; /* empty HUSHLOGIN_FILE defined */
980 for (i
= 0; files
[i
]; i
++) {
985 /* Global hush-file*/
990 if (stat(file
, &st
) != 0)
991 continue; /* file does not exist */
994 return 1; /* for all accounts */
996 f
= fopen(file
, "r");
998 continue; /* ignore errors... */
1000 while (ok
== 0 && fgets(buf
, sizeof(buf
), f
)) {
1001 buf
[strlen(buf
) - 1] = '\0';
1002 ok
= !strcmp(buf
, *buf
== '/' ? pwd
->pw_shell
:
1007 return 1; /* found username/shell */
1009 return 0; /* ignore per-account files */
1012 /* Per-account setting */
1013 if (strlen(pwd
->pw_dir
) + sizeof(file
) + 2 > sizeof(buf
))
1016 uid_t ruid
= getuid();
1017 gid_t egid
= getegid();
1019 sprintf(buf
, "%s/%s", pwd
->pw_dir
, file
);
1020 setregid(-1, pwd
->pw_gid
);
1021 setreuid(0, pwd
->pw_uid
);
1022 ok
= effective_access(buf
, O_RDONLY
) == 0;
1023 setuid(0); /* setreuid doesn't do it alone! */
1028 return 1; /* enabled by user */
1036 * Detach the controlling terminal, fork, restore syslog stuff and create a new
1039 static void fork_session(struct login_context
*cxt
)
1041 struct sigaction sa
, oldsa_hup
, oldsa_term
;
1043 signal(SIGALRM
, SIG_DFL
);
1044 signal(SIGQUIT
, SIG_DFL
);
1045 signal(SIGTSTP
, SIG_IGN
);
1047 memset(&sa
, 0, sizeof(sa
));
1048 sa
.sa_handler
= SIG_IGN
;
1049 sigaction(SIGINT
, &sa
, NULL
);
1051 sigaction(SIGHUP
, &sa
, &oldsa_hup
); /* ignore when TIOCNOTTY */
1054 * detach the controlling tty
1055 * -- we needn't the tty in parent who waits for child only.
1056 * The child calls setsid() that detach from the tty as well.
1058 ioctl(0, TIOCNOTTY
, NULL
);
1061 * We have care about SIGTERM, because leave PAM session without
1062 * pam_close_session() is pretty bad thing.
1064 sa
.sa_handler
= sig_handler
;
1065 sigaction(SIGHUP
, &sa
, NULL
);
1066 sigaction(SIGTERM
, &sa
, &oldsa_term
);
1071 * We must fork before setuid() because we need to call
1072 * pam_close_session() as root.
1075 if (child_pid
< 0) {
1079 warn(_("fork failed"));
1081 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1082 pam_end(cxt
->pamh
, pam_close_session(cxt
->pamh
, 0));
1083 sleepexit(EXIT_FAILURE
);
1088 * parent - wait for child to finish, then cleanup session
1093 sa
.sa_handler
= SIG_IGN
;
1094 sigaction(SIGQUIT
, &sa
, NULL
);
1095 sigaction(SIGINT
, &sa
, NULL
);
1097 /* wait as long as any child is there */
1098 while (wait(NULL
) == -1 && errno
== EINTR
) ;
1099 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
1101 pam_setcred(cxt
->pamh
, PAM_DELETE_CRED
);
1102 pam_end(cxt
->pamh
, pam_close_session(cxt
->pamh
, 0));
1109 sigaction(SIGHUP
, &oldsa_hup
, NULL
); /* restore old state */
1110 sigaction(SIGTERM
, &oldsa_term
, NULL
);
1115 * Problem: if the user's shell is a shell like ash that doesn't do
1116 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1117 * process in the pgrp, will kill us.
1120 /* start new session */
1123 /* make sure we have a controlling tty */
1124 open_tty(cxt
->tty_path
);
1125 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
); /* reopen */
1128 * TIOCSCTTY: steal tty from other process group.
1130 if (ioctl(0, TIOCSCTTY
, 1))
1131 syslog(LOG_ERR
, _("TIOCSCTTY failed: %m"));
1132 signal(SIGINT
, SIG_DFL
);
1136 * Initialize $TERM, $HOME, ...
1138 static void init_environ(struct login_context
*cxt
)
1140 struct passwd
*pwd
= cxt
->pwd
;
1141 char *termenv
= NULL
, **env
;
1145 termenv
= getenv("TERM");
1146 termenv
= termenv
? xstrdup(termenv
) : "dumb";
1148 /* destroy environment unless user has requested preservation (-p) */
1149 if (!cxt
->keep_env
) {
1150 environ
= (char **) xmalloc(sizeof(char *));
1151 memset(environ
, 0, sizeof(char *));
1154 setenv("HOME", pwd
->pw_dir
, 0); /* legal to override */
1155 setenv("USER", pwd
->pw_name
, 1);
1156 setenv("SHELL", pwd
->pw_shell
, 1);
1157 setenv("TERM", termenv
, 1);
1160 logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH
);
1162 else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL
) != 0)
1163 logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT
);
1165 /* mailx will give a funny error msg if you forget this one */
1166 len
= snprintf(tmp
, sizeof(tmp
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
1167 if (len
> 0 && (size_t) len
+ 1 <= sizeof(tmp
))
1168 setenv("MAIL", tmp
, 0);
1170 /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1171 * not allow modifying it.
1173 setenv("LOGNAME", pwd
->pw_name
, 1);
1175 env
= pam_getenvlist(cxt
->pamh
);
1176 for (i
= 0; env
&& env
[i
]; i
++)
1181 * Called for -h option, initialize cxt->{hostname,hostaddress}
1183 static void init_remote_info(struct login_context
*cxt
, char *remotehost
)
1187 struct addrinfo hints
, *info
= NULL
;
1191 get_thishost(cxt
, &domain
);
1193 if (domain
&& (p
= strchr(remotehost
, '.')) &&
1194 strcasecmp(p
+ 1, domain
) == 0)
1197 cxt
->hostname
= xstrdup(remotehost
);
1199 memset(&hints
, 0, sizeof(hints
));
1200 hints
.ai_flags
= AI_ADDRCONFIG
;
1201 cxt
->hostaddress
[0] = 0;
1203 if (getaddrinfo(cxt
->hostname
, NULL
, &hints
, &info
) == 0 && info
) {
1204 if (info
->ai_family
== AF_INET
) {
1205 struct sockaddr_in
*sa
=
1206 (struct sockaddr_in
*) info
->ai_addr
;
1208 memcpy(cxt
->hostaddress
, &(sa
->sin_addr
), sizeof(sa
->sin_addr
));
1210 } else if (info
->ai_family
== AF_INET6
) {
1211 struct sockaddr_in6
*sa
=
1212 (struct sockaddr_in6
*) info
->ai_addr
;
1214 memcpy(cxt
->hostaddress
, &(sa
->sin6_addr
), sizeof(sa
->sin6_addr
));
1220 int main(int argc
, char **argv
)
1224 char *childArgv
[10];
1229 char *pwdbuf
= NULL
;
1230 struct passwd
*pwd
= NULL
, _pwd
;
1232 struct login_context cxt
= {
1233 .tty_mode
= TTY_MODE
, /* tty chmod() */
1234 .pid
= getpid(), /* PID */
1235 .conv
= { misc_conv
, NULL
} /* PAM conversation function */
1238 timeout
= getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT
);
1240 signal(SIGALRM
, timedout
);
1241 siginterrupt(SIGALRM
, 1); /* we have to interrupt syscalls like ioclt() */
1242 alarm((unsigned int)timeout
);
1243 signal(SIGQUIT
, SIG_IGN
);
1244 signal(SIGINT
, SIG_IGN
);
1246 setlocale(LC_ALL
, "");
1247 bindtextdomain(PACKAGE
, LOCALEDIR
);
1248 textdomain(PACKAGE
);
1250 setpriority(PRIO_PROCESS
, 0, 0);
1251 initproctitle(argc
, argv
);
1254 * -p is used by getty to tell login not to destroy the environment
1255 * -f is used to skip a second login authentication
1256 * -h is used by other servers to pass the name of the remote
1257 * host to login so that it may be placed in utmp and wtmp
1259 while ((c
= getopt(argc
, argv
, "fHh:pV")) != -1)
1272 _("login: -h for super-user only.\n"));
1275 init_remote_info(&cxt
, optarg
);
1283 printf(UTIL_LINUX_VERSION
);
1284 return EXIT_SUCCESS
;
1287 fprintf(stderr
, _("usage: login [ -p ] [ -h host ] [ -H ] [ -f username | username ]\n"));
1295 cxt
.username
= xstrdup(p
);
1297 /* wipe name - some people mistype their password here */
1298 /* (of course we are too late, but perhaps this helps a little ..) */
1303 for (cnt
= get_fd_tabsize() - 1; cnt
> 2; cnt
--)
1306 setpgrp(); /* set pgid to pid this means that setsid() will fail */
1308 openlog("login", LOG_ODELAY
, LOG_AUTHPRIV
);
1311 init_loginpam(&cxt
);
1313 /* login -f, then the user has already been authenticated */
1314 cxt
.noauth
= cxt
.noauth
&& getuid() == 0 ? 1 : 0;
1317 loginpam_auth(&cxt
);
1320 * Authentication may be skipped (for example, during krlogin, rlogin,
1321 * etc...), but it doesn't mean that we can skip other account checks.
1322 * The account could be disabled or password expired (althought
1323 * kerberos ticket is valid). -- kzak@redhat.com (22-Feb-2006)
1325 loginpam_acct(&cxt
);
1327 if (!(cxt
.pwd
= get_passwd_entry(cxt
.username
, &pwdbuf
, &_pwd
))) {
1328 warnx(_("\nSession setup problem, abort."));
1329 syslog(LOG_ERR
, _("Invalid user name \"%s\" in %s:%d. Abort."),
1330 cxt
.username
, __FUNCTION__
, __LINE__
);
1331 pam_end(cxt
.pamh
, PAM_SYSTEM_ERR
);
1332 sleepexit(EXIT_FAILURE
);
1336 cxt
.username
= pwd
->pw_name
;
1339 * Initialize the supplementary group list. This should be done before
1340 * pam_setcred because the PAM modules might add groups during
1343 * For root we don't call initgroups, instead we call setgroups with
1344 * group 0. This avoids the need to step through the whole group file,
1345 * which can cause problems if NIS, NIS+, LDAP or something similar
1346 * is used and the machine has network problems.
1348 retcode
= pwd
->pw_uid
? initgroups(cxt
.username
, pwd
->pw_gid
) : /* user */
1349 setgroups(0, NULL
); /* root */
1351 syslog(LOG_ERR
, _("groups initialization failed: %m"));
1352 warnx(_("\nSession setup problem, abort."));
1353 pam_end(cxt
.pamh
, PAM_SYSTEM_ERR
);
1354 sleepexit(EXIT_FAILURE
);
1358 * Open PAM session (after successful authentication and account check)
1360 loginpam_session(&cxt
);
1362 /* committed to login -- turn off timeout */
1363 alarm((unsigned int)0);
1367 cxt
.quiet
= get_hushlogin_status(pwd
);
1375 if (setgid(pwd
->pw_gid
) < 0 && pwd
->pw_gid
) {
1376 syslog(LOG_ALERT
, _("setgid() failed"));
1380 if (pwd
->pw_shell
== NULL
|| *pwd
->pw_shell
== '\0')
1381 pwd
->pw_shell
= _PATH_BSHELL
;
1383 init_environ(&cxt
); /* init $HOME, $TERM ... */
1385 setproctitle("login", cxt
.username
);
1392 #ifdef LOGIN_STAT_MAIL
1394 * This turns out to be a bad idea: when the mail spool
1395 * is NFS mounted, and the NFS connection hangs, the
1396 * login hangs, even root cannot login.
1397 * Checking for mail should be done from the shell.
1403 mail
= getenv("MAIL");
1404 if (mail
&& stat(mail
, &st
) == 0 && st
.st_size
!= 0) {
1405 if (st
.st_mtime
> st
.st_atime
)
1406 printf(_("You have new mail.\n"));
1408 printf(_("You have mail.\n"));
1415 * Detach the controlling terminal, fork() and create, new session
1416 * and reinilizalize syslog stuff.
1420 /* discard permissions last so can't get killed and drop core */
1421 if (setuid(pwd
->pw_uid
) < 0 && pwd
->pw_uid
) {
1422 syslog(LOG_ALERT
, _("setuid() failed"));
1426 /* wait until here to change directory! */
1427 if (chdir(pwd
->pw_dir
) < 0) {
1428 warn(_("%s: change directory failed"), pwd
->pw_dir
);
1430 if (!getlogindefs_bool("DEFAULT_HOME", 1))
1435 printf(_("Logging in with home = \"/\".\n"));
1438 /* if the shell field has a space: treat it like a shell script */
1439 if (strchr(pwd
->pw_shell
, ' ')) {
1440 buff
= xmalloc(strlen(pwd
->pw_shell
) + 6);
1442 strcpy(buff
, "exec ");
1443 strcat(buff
, pwd
->pw_shell
);
1444 childArgv
[childArgc
++] = "/bin/sh";
1445 childArgv
[childArgc
++] = "-sh";
1446 childArgv
[childArgc
++] = "-c";
1447 childArgv
[childArgc
++] = buff
;
1449 char tbuf
[PATH_MAX
+ 2], *p
;
1452 xstrncpy(tbuf
+ 1, ((p
= strrchr(pwd
->pw_shell
, '/')) ?
1453 p
+ 1 : pwd
->pw_shell
), sizeof(tbuf
) - 1);
1455 childArgv
[childArgc
++] = pwd
->pw_shell
;
1456 childArgv
[childArgc
++] = xstrdup(tbuf
);
1459 childArgv
[childArgc
++] = NULL
;
1461 execvp(childArgv
[0], childArgv
+ 1);
1463 if (!strcmp(childArgv
[0], "/bin/sh"))
1464 warn(_("couldn't exec shell script"));
1466 warn(_("no shell"));