1 /* pam_lastlog module */
4 * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
6 * This module does the necessary work to display the last login
7 * time+date for this user, it then updates this entry for the
8 * present (login) service.
27 #include <sys/types.h>
29 #include <sys/resource.h>
33 #if defined(hpux) || defined(sunos) || defined(solaris)
34 # ifndef _PATH_LASTLOG
35 # define _PATH_LASTLOG "/usr/adm/lastlog"
36 # endif /* _PATH_LASTLOG */
38 # define UT_HOSTSIZE 16
39 # endif /* UT_HOSTSIZE */
41 # define UT_LINESIZE 12
42 # endif /* UT_LINESIZE */
47 char ll_line[UT_LINESIZE];
48 char ll_host[UT_HOSTSIZE]; /* same as in utmp */
53 # define _PATH_BTMP "/var/log/btmp"
56 #ifndef PATH_LOGIN_DEFS
57 # define PATH_LOGIN_DEFS "/etc/login.defs"
60 /* XXX - time before ignoring lock. Is 1 sec enough? */
61 #define LASTLOG_IGNORE_LOCK_TIME 1
63 #define DEFAULT_HOST "" /* "[no.where]" */
64 #define DEFAULT_TERM "" /* "tt???" */
66 #define DEFAULT_INACTIVE_DAYS 90
67 #define MAX_INACTIVE_DAYS 100000
70 * here, we make a definition for the externally accessible function
71 * in this file (this definition is required for static a module
72 * but strongly encouraged generally) it is used to instruct the
73 * modules include file to define the function prototypes.
76 #define PAM_SM_SESSION
78 #define PAM_SM_ACCOUNT
80 #include <security/pam_modules.h>
81 #include <security/_pam_macros.h>
82 #include <security/pam_modutil.h>
83 #include <security/pam_ext.h>
85 /* argument parsing */
87 #define LASTLOG_DATE 01 /* display the date of the last login */
88 #define LASTLOG_HOST 02 /* display the last host used (if set) */
89 #define LASTLOG_LINE 04 /* display the last terminal used */
90 #define LASTLOG_NEVER 010 /* display a welcome message for first login */
91 #define LASTLOG_DEBUG 020 /* send info to syslog(3) */
92 #define LASTLOG_QUIET 040 /* keep quiet about things */
93 #define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */
94 #define LASTLOG_BTMP 0200 /* display failed login info from btmp */
95 #define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */
96 #define LASTLOG_UNLIMITED 01000 /* unlimited file size (ignore 'fsize' limit) */
99 _pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv,
104 *inactive = DEFAULT_INACTIVE_DAYS;
106 /* does the appliction require quiet? */
107 if (flags & PAM_SILENT) {
108 ctrl |= LASTLOG_QUIET;
111 /* step through arguments */
112 for (; argc-- > 0; ++argv) {
116 if (!strcmp(*argv,"debug")) {
117 ctrl |= LASTLOG_DEBUG;
118 } else if (!strcmp(*argv,"silent")) {
119 ctrl |= LASTLOG_QUIET;
120 } else if (!strncmp(*argv,"inactive=", 9)) {
121 l = strtol(*argv+9, &ep, 10);
122 if (ep != *argv+9 && l > 0 && l < MAX_INACTIVE_DAYS)
125 pam_syslog(pamh, LOG_ERR, "bad option value: %s", *argv);
128 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
132 D(("ctrl = %o", ctrl));
137 _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
139 int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
141 /* step through arguments */
142 for (; argc-- > 0; ++argv) {
144 /* generic options */
146 if (!strcmp(*argv,"debug")) {
147 ctrl |= LASTLOG_DEBUG;
148 } else if (!strcmp(*argv,"nodate")) {
149 ctrl &= ~LASTLOG_DATE;
150 } else if (!strcmp(*argv,"noterm")) {
151 ctrl &= ~LASTLOG_LINE;
152 } else if (!strcmp(*argv,"nohost")) {
153 ctrl &= ~LASTLOG_HOST;
154 } else if (!strcmp(*argv,"silent")) {
155 ctrl |= LASTLOG_QUIET;
156 } else if (!strcmp(*argv,"never")) {
157 ctrl |= LASTLOG_NEVER;
158 } else if (!strcmp(*argv,"nowtmp")) {
159 ctrl &= ~LASTLOG_WTMP;
160 } else if (!strcmp(*argv,"noupdate")) {
161 ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE);
162 } else if (!strcmp(*argv,"showfailed")) {
163 ctrl |= LASTLOG_BTMP;
164 } else if (!strcmp(*argv,"unlimited")) {
165 ctrl |= LASTLOG_UNLIMITED;
167 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
171 /* does the appliction require quiet? */
172 if (flags & PAM_SILENT) {
173 ctrl |= LASTLOG_QUIET;
174 ctrl &= ~LASTLOG_BTMP;
177 D(("ctrl = %o", ctrl));
182 get_tty(pam_handle_t *pamh)
184 const void *void_terminal_line = NULL;
185 const char *terminal_line;
187 if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS
188 || void_terminal_line == NULL) {
189 terminal_line = DEFAULT_TERM;
191 terminal_line = void_terminal_line;
193 if (!strncmp("/dev/", terminal_line, 5)) {
194 /* strip leading "/dev/" from tty. */
197 D(("terminal = %s", terminal_line));
198 return terminal_line;
201 #define MAX_UID_VALUE 0xFFFFFFFFUL
204 get_lastlog_uid_max(pam_handle_t *pamh)
206 uid_t uid_max = MAX_UID_VALUE;
210 s = pam_modutil_search_key(pamh, PATH_LOGIN_DEFS, "LASTLOG_UID_MAX");
215 while (ep > s && isspace(*(--ep))) {
219 ul = strtoul(s, &ep, 10);
220 if (!(ul >= MAX_UID_VALUE
221 || (uid_t)ul >= MAX_UID_VALUE
222 || (errno != 0 && ul == 0)
233 last_login_open(pam_handle_t *pamh, int announce, uid_t uid)
237 /* obtain the last login date and all the relevant info */
238 last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
240 if (errno == ENOENT && (announce & LASTLOG_UPDATE)) {
241 last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
242 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
244 pam_syslog(pamh, LOG_ERR,
245 "unable to create %s: %m", _PATH_LASTLOG);
246 D(("unable to create %s file", _PATH_LASTLOG));
249 pam_syslog(pamh, LOG_NOTICE,
250 "file %s created", _PATH_LASTLOG);
251 D(("file %s created", _PATH_LASTLOG));
253 pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
254 D(("unable to open %s file", _PATH_LASTLOG));
259 if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) {
260 pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
261 D(("unable to lseek %s file", _PATH_LASTLOG));
271 last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
273 struct flock last_lock;
274 struct lastlog last_login;
275 int retval = PAM_SUCCESS;
281 memset(&last_lock, 0, sizeof(last_lock));
282 last_lock.l_type = F_RDLCK;
283 last_lock.l_whence = SEEK_SET;
284 last_lock.l_start = sizeof(last_login) * (off_t) uid;
285 last_lock.l_len = sizeof(last_login);
287 if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
288 D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
289 pam_syslog(pamh, LOG_WARNING,
290 "file %s is locked/read", _PATH_LASTLOG);
291 sleep(LASTLOG_IGNORE_LOCK_TIME);
294 if (pam_modutil_read(last_fd, (char *) &last_login,
295 sizeof(last_login)) != sizeof(last_login)) {
296 memset(&last_login, 0, sizeof(last_login));
299 last_lock.l_type = F_UNLCK;
300 (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
302 *lltime = last_login.ll_time;
303 if (!last_login.ll_time) {
304 if (announce & LASTLOG_DEBUG) {
305 pam_syslog(pamh, LOG_DEBUG,
306 "first login for user with uid %lu",
307 (unsigned long int)uid);
311 if (!(announce & LASTLOG_QUIET)) {
313 if (last_login.ll_time) {
315 /* we want the date? */
316 if (announce & LASTLOG_DATE) {
317 struct tm *tm, tm_buf;
320 ll_time = last_login.ll_time;
321 if ((tm = localtime_r (&ll_time, &tm_buf)) != NULL) {
322 strftime (the_time, sizeof (the_time),
323 /* TRANSLATORS: "strftime options for date of last login" */
324 _(" %a %b %e %H:%M:%S %Z %Y"), tm);
329 /* we want & have the host? */
330 if ((announce & LASTLOG_HOST)
331 && (last_login.ll_host[0] != '\0')) {
332 /* TRANSLATORS: " from <host>" */
333 if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
334 last_login.ll_host) < 0) {
335 pam_syslog(pamh, LOG_CRIT, "out of memory");
336 retval = PAM_BUF_ERR;
341 /* we want and have the terminal? */
342 if ((announce & LASTLOG_LINE)
343 && (last_login.ll_line[0] != '\0')) {
344 /* TRANSLATORS: " on <terminal>" */
345 if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
346 last_login.ll_line) < 0) {
347 pam_syslog(pamh, LOG_CRIT, "out of memory");
348 retval = PAM_BUF_ERR;
353 if (date != NULL || host != NULL || line != NULL)
354 /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
355 retval = pam_info(pamh, _("Last login:%s%s%s"),
359 } else if (announce & LASTLOG_NEVER) {
360 D(("this is the first time this user has logged in"));
361 retval = pam_info(pamh, "%s", _("Welcome to your new account!"));
367 memset(&last_login, 0, sizeof(last_login));
368 _pam_overwrite(date);
369 _pam_overwrite(host);
371 _pam_overwrite(line);
378 last_login_write(pam_handle_t *pamh, int announce, int last_fd,
379 uid_t uid, const char *user)
381 static struct rlimit no_limit = {
385 struct rlimit old_limit;
387 struct flock last_lock;
388 struct lastlog last_login;
390 const void *void_remote_host = NULL;
391 const char *remote_host;
392 const char *terminal_line;
393 int retval = PAM_SUCCESS;
396 if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) {
397 pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
398 return PAM_SERVICE_ERR;
401 memset(&last_login, 0, sizeof(last_login));
403 /* set this login date */
404 D(("set the most recent login time"));
405 (void) time(&ll_time); /* set the time */
406 last_login.ll_time = ll_time;
408 /* set the remote host */
409 if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS
410 || void_remote_host == NULL) {
411 remote_host = DEFAULT_HOST;
413 remote_host = void_remote_host;
416 /* copy to last_login */
417 strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1);
419 /* set the terminal line */
420 terminal_line = get_tty(pamh);
422 /* copy to last_login */
423 strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1);
424 terminal_line = NULL;
426 D(("locking lastlog file"));
428 /* now we try to lock this file-record exclusively; non-blocking */
429 memset(&last_lock, 0, sizeof(last_lock));
430 last_lock.l_type = F_WRLCK;
431 last_lock.l_whence = SEEK_SET;
432 last_lock.l_start = sizeof(last_login) * (off_t) uid;
433 last_lock.l_len = sizeof(last_login);
435 if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
436 D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
437 pam_syslog(pamh, LOG_WARNING, "file %s is locked/write", _PATH_LASTLOG);
438 sleep(LASTLOG_IGNORE_LOCK_TIME);
442 * Failing to set the 'fsize' limit is not a fatal error. We try to write
443 * lastlog anyway, under the risk of dying due to a SIGXFSZ.
445 D(("setting limit for 'fsize'"));
447 if ((announce & LASTLOG_UNLIMITED) == 0) { /* don't set to unlimted */
449 } else if (getrlimit(RLIMIT_FSIZE, &old_limit) == 0) {
450 if (old_limit.rlim_cur == RLIM_INFINITY) { /* already unlimited */
453 setrlimit_res = setrlimit(RLIMIT_FSIZE, &no_limit);
454 if (setrlimit_res != 0)
455 pam_syslog(pamh, LOG_WARNING, "Could not set limit for 'fsize': %m");
459 if (errno == EINVAL) {
460 pam_syslog(pamh, LOG_INFO, "Limit for 'fsize' not supported: %m");
462 pam_syslog(pamh, LOG_WARNING, "Could not get limit for 'fsize': %m");
466 D(("writing to the lastlog file"));
467 if (pam_modutil_write (last_fd, (char *) &last_login,
468 sizeof (last_login)) != sizeof(last_login)) {
469 pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG);
470 retval = PAM_SERVICE_ERR;
474 * Failing to restore the 'fsize' limit is a fatal error.
476 D(("restoring limit for 'fsize'"));
477 if (setrlimit_res == 0) {
478 setrlimit_res = setrlimit(RLIMIT_FSIZE, &old_limit);
479 if (setrlimit_res != 0) {
480 pam_syslog(pamh, LOG_ERR, "Could not restore limit for 'fsize': %m");
481 retval = PAM_SERVICE_ERR;
485 last_lock.l_type = F_UNLCK;
486 (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
489 if (announce & LASTLOG_WTMP) {
490 /* write wtmp entry for user */
491 logwtmp(last_login.ll_line, user, remote_host);
495 memset(&last_login, 0, sizeof(last_login));
501 last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime)
506 if (uid > get_lastlog_uid_max(pamh)) {
510 /* obtain the last login date and all the relevant info */
511 last_fd = last_login_open(pamh, announce, uid);
513 return PAM_SERVICE_ERR;
516 retval = last_login_read(pamh, announce, last_fd, uid, lltime);
517 if (retval != PAM_SUCCESS)
520 D(("error while reading lastlog file"));
524 if (announce & LASTLOG_UPDATE) {
525 retval = last_login_write(pamh, announce, last_fd, uid, user);
529 D(("all done with last login"));
535 last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
547 if (strlen(user) > UT_NAMESIZE) {
548 pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
551 /* obtain the failed login attempt records from btmp */
552 fd = open(_PATH_BTMP, O_RDONLY);
554 int save_errno = errno;
555 pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
556 D(("unable to open %s file", _PATH_BTMP));
557 if (save_errno == ENOENT)
560 return PAM_SERVICE_ERR;
563 while ((retval=pam_modutil_read(fd, (void *)&ut,
564 sizeof(ut))) == sizeof(ut)) {
565 if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) {
566 memcpy(&utuser, &ut, sizeof(utuser));
572 pam_syslog(pamh, LOG_ERR, "corruption detected in %s", _PATH_BTMP);
573 retval = PAM_SUCCESS;
576 /* we want the date? */
577 if (announce & LASTLOG_DATE) {
578 struct tm *tm, tm_buf;
581 lf_time = utuser.ut_tv.tv_sec;
582 tm = localtime_r (&lf_time, &tm_buf);
583 strftime (the_time, sizeof (the_time),
584 /* TRANSLATORS: "strftime options for date of last login" */
585 _(" %a %b %e %H:%M:%S %Z %Y"), tm);
590 /* we want & have the host? */
591 if ((announce & LASTLOG_HOST)
592 && (utuser.ut_host[0] != '\0')) {
593 /* TRANSLATORS: " from <host>" */
594 if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
595 utuser.ut_host) < 0) {
596 pam_syslog(pamh, LOG_CRIT, "out of memory");
597 retval = PAM_BUF_ERR;
602 /* we want and have the terminal? */
603 if ((announce & LASTLOG_LINE)
604 && (utuser.ut_line[0] != '\0')) {
605 /* TRANSLATORS: " on <terminal>" */
606 if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
607 utuser.ut_line) < 0) {
608 pam_syslog(pamh, LOG_CRIT, "out of memory");
609 retval = PAM_BUF_ERR;
614 if (line != NULL || date != NULL || host != NULL) {
615 /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */
616 pam_info(pamh, _("Last failed login:%s%s%s"),
623 #if defined HAVE_DNGETTEXT && defined ENABLE_NLS
624 retval = asprintf (&line, dngettext(PACKAGE,
625 "There was %d failed login attempt since the last successful login.",
626 "There were %d failed login attempts since the last successful login.",
631 retval = asprintf(&line,
632 _("There was %d failed login attempt since the last successful login."),
635 retval = asprintf(&line,
636 /* TRANSLATORS: only used if dngettext is not supported */
637 _("There were %d failed login attempts since the last successful login."),
641 retval = pam_info(pamh, "%s", line);
643 retval = PAM_BUF_ERR;
652 D(("all done with btmp"));
657 /* --- authentication (locking out inactive users) functions --- */
659 pam_sm_authenticate(pam_handle_t *pamh, int flags,
660 int argc, const char **argv)
663 const char *user = NULL;
664 const struct passwd *pwd;
667 time_t inactive_days = 0;
671 * Lock out the user if he did not login recently enough.
674 ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days);
678 if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
680 pam_syslog(pamh, LOG_ERR, "cannot determine the user's name");
681 return PAM_USER_UNKNOWN;
686 pwd = pam_modutil_getpwnam (pamh, user);
688 pam_syslog(pamh, LOG_ERR, "user unknown");
689 return PAM_USER_UNKNOWN;
692 pwd = NULL; /* tidy up */
694 if (uid == 0 || uid > get_lastlog_uid_max(pamh))
697 /* obtain the last login date and all the relevant info */
698 last_fd = last_login_open(pamh, ctrl, uid);
703 retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime);
706 if (retval != PAM_SUCCESS) {
707 D(("error while reading lastlog file"));
711 if (lltime == 0) { /* user never logged in before */
712 if (ctrl & LASTLOG_DEBUG)
713 pam_syslog(pamh, LOG_DEBUG, "user never logged in - pass");
717 lltime = (time(NULL) - lltime) / (24*60*60);
719 if (lltime > inactive_days) {
720 pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied",
721 user, (long) lltime);
729 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
730 int argc UNUSED, const char **argv UNUSED)
736 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
737 int argc, const char **argv)
739 return pam_sm_authenticate(pamh, flags, argc, argv);
742 /* --- session management functions --- */
745 pam_sm_open_session(pam_handle_t *pamh, int flags,
746 int argc, const char **argv)
750 const struct passwd *pwd;
755 * this module gets the uid of the PAM_USER. Uses it to display
756 * last login info and then updates the lastlog for that user.
759 ctrl = _pam_session_parse(pamh, flags, argc, argv);
763 retval = pam_get_item(pamh, PAM_USER, &user);
764 if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') {
765 pam_syslog(pamh, LOG_NOTICE, "user unknown");
766 return PAM_USER_UNKNOWN;
771 pwd = pam_modutil_getpwnam (pamh, user);
773 D(("couldn't identify user %s", user));
774 return PAM_USER_UNKNOWN;
777 pwd = NULL; /* tidy up */
779 /* process the current login attempt (indicate last) */
781 retval = last_login_date(pamh, ctrl, uid, user, &lltime);
783 if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
784 retval = last_login_failed(pamh, ctrl, user, lltime);
787 /* indicate success or failure */
789 uid = -1; /* forget this */
795 pam_sm_close_session (pam_handle_t *pamh, int flags,
796 int argc, const char **argv)
798 const char *terminal_line;
800 if (!(_pam_session_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
803 terminal_line = get_tty(pamh);
805 /* Wipe out utmp logout entry */
806 logwtmp(terminal_line, "", "");
811 /* end of module definition */