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.
26 #include <sys/types.h>
30 #if defined(hpux) || defined(sunos) || defined(solaris)
31 # ifndef _PATH_LASTLOG
32 # define _PATH_LASTLOG "/usr/adm/lastlog"
33 # endif /* _PATH_LASTLOG */
35 # define UT_HOSTSIZE 16
36 # endif /* UT_HOSTSIZE */
38 # define UT_LINESIZE 12
39 # endif /* UT_LINESIZE */
44 char ll_line[UT_LINESIZE];
45 char ll_host[UT_HOSTSIZE]; /* same as in utmp */
50 # define _PATH_BTMP "/var/log/btmp"
53 /* XXX - time before ignoring lock. Is 1 sec enough? */
54 #define LASTLOG_IGNORE_LOCK_TIME 1
56 #define DEFAULT_HOST "" /* "[no.where]" */
57 #define DEFAULT_TERM "" /* "tt???" */
60 * here, we make a definition for the externally accessible function
61 * in this file (this definition is required for static a module
62 * but strongly encouraged generally) it is used to instruct the
63 * modules include file to define the function prototypes.
66 #define PAM_SM_SESSION
68 #include <security/pam_modules.h>
69 #include <security/_pam_macros.h>
70 #include <security/pam_modutil.h>
71 #include <security/pam_ext.h>
73 /* argument parsing */
75 #define LASTLOG_DATE 01 /* display the date of the last login */
76 #define LASTLOG_HOST 02 /* display the last host used (if set) */
77 #define LASTLOG_LINE 04 /* display the last terminal used */
78 #define LASTLOG_NEVER 010 /* display a welcome message for first login */
79 #define LASTLOG_DEBUG 020 /* send info to syslog(3) */
80 #define LASTLOG_QUIET 040 /* keep quiet about things */
81 #define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */
82 #define LASTLOG_BTMP 0200 /* display failed login info from btmp */
83 #define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */
86 _pam_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
88 int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
90 /* does the appliction require quiet? */
91 if (flags & PAM_SILENT) {
92 ctrl |= LASTLOG_QUIET;
95 /* step through arguments */
96 for (; argc-- > 0; ++argv) {
100 if (!strcmp(*argv,"debug")) {
101 ctrl |= LASTLOG_DEBUG;
102 } else if (!strcmp(*argv,"nodate")) {
103 ctrl &= ~LASTLOG_DATE;
104 } else if (!strcmp(*argv,"noterm")) {
105 ctrl &= ~LASTLOG_LINE;
106 } else if (!strcmp(*argv,"nohost")) {
107 ctrl &= ~LASTLOG_HOST;
108 } else if (!strcmp(*argv,"silent")) {
109 ctrl |= LASTLOG_QUIET;
110 } else if (!strcmp(*argv,"never")) {
111 ctrl |= LASTLOG_NEVER;
112 } else if (!strcmp(*argv,"nowtmp")) {
113 ctrl &= ~LASTLOG_WTMP;
114 } else if (!strcmp(*argv,"noupdate")) {
115 ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE);
116 } else if (!strcmp(*argv,"showfailed")) {
117 ctrl |= LASTLOG_BTMP;
119 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
123 D(("ctrl = %o", ctrl));
128 get_tty(pam_handle_t *pamh)
130 const void *void_terminal_line = NULL;
131 const char *terminal_line;
133 if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS
134 || void_terminal_line == NULL) {
135 terminal_line = DEFAULT_TERM;
137 terminal_line = void_terminal_line;
139 if (!strncmp("/dev/", terminal_line, 5)) {
140 /* strip leading "/dev/" from tty. */
143 D(("terminal = %s", terminal_line));
144 return terminal_line;
148 last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
150 struct flock last_lock;
151 struct lastlog last_login;
152 int retval = PAM_SUCCESS;
158 memset(&last_lock, 0, sizeof(last_lock));
159 last_lock.l_type = F_RDLCK;
160 last_lock.l_whence = SEEK_SET;
161 last_lock.l_start = sizeof(last_login) * (off_t) uid;
162 last_lock.l_len = sizeof(last_login);
164 if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
165 D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
166 pam_syslog(pamh, LOG_WARNING,
167 "file %s is locked/read", _PATH_LASTLOG);
168 sleep(LASTLOG_IGNORE_LOCK_TIME);
171 if (pam_modutil_read(last_fd, (char *) &last_login,
172 sizeof(last_login)) != sizeof(last_login)) {
173 memset(&last_login, 0, sizeof(last_login));
176 last_lock.l_type = F_UNLCK;
177 (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
179 *lltime = last_login.ll_time;
180 if (!last_login.ll_time) {
181 if (announce & LASTLOG_DEBUG) {
182 pam_syslog(pamh, LOG_DEBUG,
183 "first login for user with uid %lu",
184 (unsigned long int)uid);
188 if (!(announce & LASTLOG_QUIET)) {
190 if (last_login.ll_time) {
192 /* we want the date? */
193 if (announce & LASTLOG_DATE) {
194 struct tm *tm, tm_buf;
197 ll_time = last_login.ll_time;
198 tm = localtime_r (&ll_time, &tm_buf);
199 strftime (the_time, sizeof (the_time),
200 /* TRANSLATORS: "strftime options for date of last login" */
201 _(" %a %b %e %H:%M:%S %Z %Y"), tm);
206 /* we want & have the host? */
207 if ((announce & LASTLOG_HOST)
208 && (last_login.ll_host[0] != '\0')) {
209 /* TRANSLATORS: " from <host>" */
210 if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
211 last_login.ll_host) < 0) {
212 pam_syslog(pamh, LOG_ERR, "out of memory");
213 retval = PAM_BUF_ERR;
218 /* we want and have the terminal? */
219 if ((announce & LASTLOG_LINE)
220 && (last_login.ll_line[0] != '\0')) {
221 /* TRANSLATORS: " on <terminal>" */
222 if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
223 last_login.ll_line) < 0) {
224 pam_syslog(pamh, LOG_ERR, "out of memory");
225 retval = PAM_BUF_ERR;
230 if (date != NULL || host != NULL || line != NULL)
231 /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
232 retval = pam_info(pamh, _("Last login:%s%s%s"),
236 } else if (announce & LASTLOG_NEVER) {
237 D(("this is the first time this user has logged in"));
238 retval = pam_info(pamh, "%s", _("Welcome to your new account!"));
244 memset(&last_login, 0, sizeof(last_login));
245 _pam_overwrite(date);
246 _pam_overwrite(host);
248 _pam_overwrite(line);
255 last_login_write(pam_handle_t *pamh, int announce, int last_fd,
256 uid_t uid, const char *user)
258 struct flock last_lock;
259 struct lastlog last_login;
261 const void *void_remote_host = NULL;
262 const char *remote_host;
263 const char *terminal_line;
264 int retval = PAM_SUCCESS;
267 if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) {
268 pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
269 return PAM_SERVICE_ERR;
272 /* set this login date */
273 D(("set the most recent login time"));
274 (void) time(&ll_time); /* set the time */
275 last_login.ll_time = ll_time;
277 /* set the remote host */
278 if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS
279 || void_remote_host == NULL) {
280 remote_host = DEFAULT_HOST;
282 remote_host = void_remote_host;
285 /* copy to last_login */
286 last_login.ll_host[0] = '\0';
287 strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1);
289 /* set the terminal line */
290 terminal_line = get_tty(pamh);
292 /* copy to last_login */
293 last_login.ll_line[0] = '\0';
294 strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1);
295 terminal_line = NULL;
297 D(("locking lastlog file"));
299 /* now we try to lock this file-record exclusively; non-blocking */
300 memset(&last_lock, 0, sizeof(last_lock));
301 last_lock.l_type = F_WRLCK;
302 last_lock.l_whence = SEEK_SET;
303 last_lock.l_start = sizeof(last_login) * (off_t) uid;
304 last_lock.l_len = sizeof(last_login);
306 if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
307 D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
308 pam_syslog(pamh, LOG_WARNING, "file %s is locked/write", _PATH_LASTLOG);
309 sleep(LASTLOG_IGNORE_LOCK_TIME);
312 D(("writing to the lastlog file"));
313 if (pam_modutil_write (last_fd, (char *) &last_login,
314 sizeof (last_login)) != sizeof(last_login)) {
315 pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG);
316 retval = PAM_SERVICE_ERR;
319 last_lock.l_type = F_UNLCK;
320 (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
323 if (announce & LASTLOG_WTMP) {
324 /* write wtmp entry for user */
325 logwtmp(last_login.ll_line, user, remote_host);
329 memset(&last_login, 0, sizeof(last_login));
335 last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime)
340 /* obtain the last login date and all the relevant info */
341 last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
343 if (errno == ENOENT) {
344 last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
345 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
347 pam_syslog(pamh, LOG_ERR,
348 "unable to create %s: %m", _PATH_LASTLOG);
349 D(("unable to create %s file", _PATH_LASTLOG));
350 return PAM_SERVICE_ERR;
352 pam_syslog(pamh, LOG_WARNING,
353 "file %s created", _PATH_LASTLOG);
354 D(("file %s created", _PATH_LASTLOG));
356 pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
357 D(("unable to open %s file", _PATH_LASTLOG));
358 return PAM_SERVICE_ERR;
362 if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) {
363 pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
364 D(("unable to lseek %s file", _PATH_LASTLOG));
365 return PAM_SERVICE_ERR;
368 retval = last_login_read(pamh, announce, last_fd, uid, lltime);
369 if (retval != PAM_SUCCESS)
372 D(("error while reading lastlog file"));
376 if (announce & LASTLOG_UPDATE) {
377 retval = last_login_write(pamh, announce, last_fd, uid, user);
381 D(("all done with last login"));
387 last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
399 if (strlen(user) > UT_NAMESIZE) {
400 pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
403 /* obtain the failed login attempt records from btmp */
404 fd = open(_PATH_BTMP, O_RDONLY);
406 int save_errno = errno;
407 pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
408 D(("unable to open %s file", _PATH_BTMP));
409 if (save_errno == ENOENT)
412 return PAM_SERVICE_ERR;
415 while ((retval=pam_modutil_read(fd, (void *)&ut,
416 sizeof(ut))) == sizeof(ut)) {
417 if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) {
418 memcpy(&utuser, &ut, sizeof(utuser));
424 /* we want the date? */
425 if (announce & LASTLOG_DATE) {
426 struct tm *tm, tm_buf;
429 lf_time = utuser.ut_tv.tv_sec;
430 tm = localtime_r (&lf_time, &tm_buf);
431 strftime (the_time, sizeof (the_time),
432 /* TRANSLATORS: "strftime options for date of last login" */
433 _(" %a %b %e %H:%M:%S %Z %Y"), tm);
438 /* we want & have the host? */
439 if ((announce & LASTLOG_HOST)
440 && (utuser.ut_host[0] != '\0')) {
441 /* TRANSLATORS: " from <host>" */
442 if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
443 utuser.ut_host) < 0) {
444 pam_syslog(pamh, LOG_ERR, "out of memory");
445 retval = PAM_BUF_ERR;
450 /* we want and have the terminal? */
451 if ((announce & LASTLOG_LINE)
452 && (utuser.ut_line[0] != '\0')) {
453 /* TRANSLATORS: " on <terminal>" */
454 if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
455 utuser.ut_line) < 0) {
456 pam_syslog(pamh, LOG_ERR, "out of memory");
457 retval = PAM_BUF_ERR;
462 if (line != NULL || date != NULL || host != NULL) {
463 /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */
464 pam_info(pamh, _("Last failed login:%s%s%s"),
471 #if defined HAVE_DNGETTEXT && defined ENABLE_NLS
472 retval = asprintf (&line, dngettext(PACKAGE,
473 "There was %d failed login attempt since the last successful login.",
474 "There were %d failed login attempts since the last successful login.",
479 retval = asprintf(&line,
480 _("There was %d failed login attempt since the last successful login."),
483 retval = asprintf(&line,
484 /* TRANSLATORS: only used if dngettext is not supported */
485 _("There were %d failed login attempts since the last successful login."),
489 retval = pam_info(pamh, "%s", line);
491 retval = PAM_BUF_ERR;
500 D(("all done with btmp"));
505 /* --- authentication management functions (only) --- */
508 pam_sm_open_session(pam_handle_t *pamh, int flags,
509 int argc, const char **argv)
513 const struct passwd *pwd;
518 * this module gets the uid of the PAM_USER. Uses it to display
519 * last login info and then updates the lastlog for that user.
522 ctrl = _pam_parse(pamh, flags, argc, argv);
526 retval = pam_get_item(pamh, PAM_USER, &user);
527 if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') {
528 pam_syslog(pamh, LOG_NOTICE, "user unknown");
529 return PAM_USER_UNKNOWN;
534 pwd = pam_modutil_getpwnam (pamh, user);
536 D(("couldn't identify user %s", user));
537 return PAM_USER_UNKNOWN;
540 pwd = NULL; /* tidy up */
542 /* process the current login attempt (indicate last) */
544 retval = last_login_date(pamh, ctrl, uid, user, &lltime);
546 if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
547 retval = last_login_failed(pamh, ctrl, user, lltime);
550 /* indicate success or failure */
552 uid = -1; /* forget this */
558 pam_sm_close_session (pam_handle_t *pamh, int flags,
559 int argc, const char **argv)
561 const char *terminal_line;
563 if (!(_pam_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
566 terminal_line = get_tty(pamh);
568 /* Wipe out utmp logout entry */
569 logwtmp(terminal_line, "", "");
576 /* static module data */
578 struct pam_module _pam_lastlog_modstruct = {
584 pam_sm_close_session,
590 /* end of module definition */