1 /*
2 * pam_lastlog module
3 *
4 * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
5 *
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.
9 */
10
11 #include "config.h"
12
13 #include <fcntl.h>
14 #include <time.h>
15 #include <errno.h>
16 #ifdef HAVE_UTMP_H
17 # include <utmp.h>
18 #else
19 # include <lastlog.h>
20 #endif
21 #include <pwd.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/resource.h>
30 #include <syslog.h>
31 #include <unistd.h>
32
33 #if defined(hpux) || defined(sunos) || defined(solaris)
34 # ifndef _PATH_LASTLOG
35 # define _PATH_LASTLOG "/usr/adm/lastlog"
36 # endif /* _PATH_LASTLOG */
37 # ifndef UT_HOSTSIZE
38 # define UT_HOSTSIZE 16
39 # endif /* UT_HOSTSIZE */
40 # ifndef UT_LINESIZE
41 # define UT_LINESIZE 12
42 # endif /* UT_LINESIZE */
43 #endif
44 #if defined(hpux)
45 struct lastlog {
46 time_t ll_time;
47 char ll_line[UT_LINESIZE];
48 char ll_host[UT_HOSTSIZE]; /* same as in utmp */
49 };
50 #endif /* hpux */
51
52 #ifndef _PATH_BTMP
53 # define _PATH_BTMP "/var/log/btmp"
54 #endif
55
56 #ifndef PATH_LOGIN_DEFS
57 # define PATH_LOGIN_DEFS "/etc/login.defs"
58 #endif
59
60 #define DEFAULT_HOST "" /* "[no.where]" */
61 #define DEFAULT_TERM "" /* "tt???" */
62
63 #define DEFAULT_INACTIVE_DAYS 90
64 #define MAX_INACTIVE_DAYS 100000
65 #define LOCK_RETRIES 3 /* number of file lock retries */
66 #define LOCK_RETRY_DELAY 1 /* seconds to wait between lock attempts */
67
68 #include <security/pam_modules.h>
69 #include <security/_pam_macros.h>
70 #include <security/pam_modutil.h>
71 #include <security/pam_ext.h>
72 #include "pam_inline.h"
73
74 /* argument parsing */
75
76 #define LASTLOG_DATE 01 /* display the date of the last login */
77 #define LASTLOG_HOST 02 /* display the last host used (if set) */
78 #define LASTLOG_LINE 04 /* display the last terminal used */
79 #define LASTLOG_NEVER 010 /* display a welcome message for first login */
80 #define LASTLOG_DEBUG 020 /* send info to syslog(3) */
81 #define LASTLOG_QUIET 040 /* keep quiet about things */
82 #define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */
83 #define LASTLOG_BTMP 0200 /* display failed login info from btmp */
84 #define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */
85 #define LASTLOG_UNLIMITED 01000 /* unlimited file size (ignore 'fsize' limit) */
86
87 static int
88 _pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv,
89 time_t *inactive)
90 {
91 int ctrl = 0;
92
93 *inactive = DEFAULT_INACTIVE_DAYS;
94
95 /* does the application require quiet? */
96 if (flags & PAM_SILENT) {
97 ctrl |= LASTLOG_QUIET;
98 }
99
100 /* step through arguments */
101 for (; argc-- > 0; ++argv) {
102 const char *str;
103 char *ep = NULL;
104 long l;
105
106 if (!strcmp(*argv,"debug")) {
107 ctrl |= LASTLOG_DEBUG;
108 } else if (!strcmp(*argv,"silent")) {
109 ctrl |= LASTLOG_QUIET;
110 } else if ((str = pam_str_skip_prefix(*argv, "inactive=")) != NULL) {
111 l = strtol(str, &ep, 10);
112 if (ep != str && l > 0 && l < MAX_INACTIVE_DAYS)
113 *inactive = l;
114 else {
115 pam_syslog(pamh, LOG_ERR, "bad option value: %s", *argv);
116 }
117 } else {
118 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
119 }
120 }
121
122 D(("ctrl = %o", ctrl));
123 return ctrl;
124 }
125
126 static int
127 _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
128 {
129 int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
130
131 /* step through arguments */
132 for (; argc-- > 0; ++argv) {
133
134 /* generic options */
135
136 if (!strcmp(*argv,"debug")) {
137 ctrl |= LASTLOG_DEBUG;
138 } else if (!strcmp(*argv,"nodate")) {
139 ctrl &= ~LASTLOG_DATE;
140 } else if (!strcmp(*argv,"noterm")) {
141 ctrl &= ~LASTLOG_LINE;
142 } else if (!strcmp(*argv,"nohost")) {
143 ctrl &= ~LASTLOG_HOST;
144 } else if (!strcmp(*argv,"silent")) {
145 ctrl |= LASTLOG_QUIET;
146 } else if (!strcmp(*argv,"never")) {
147 ctrl |= LASTLOG_NEVER;
148 } else if (!strcmp(*argv,"nowtmp")) {
149 ctrl &= ~LASTLOG_WTMP;
150 } else if (!strcmp(*argv,"noupdate")) {
151 ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE);
152 } else if (!strcmp(*argv,"showfailed")) {
153 ctrl |= LASTLOG_BTMP;
154 } else if (!strcmp(*argv,"unlimited")) {
155 ctrl |= LASTLOG_UNLIMITED;
156 } else {
157 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
158 }
159 }
160
161 /* does the application require quiet? */
162 if (flags & PAM_SILENT) {
163 ctrl |= LASTLOG_QUIET;
164 ctrl &= ~LASTLOG_BTMP;
165 }
166
167 D(("ctrl = %o", ctrl));
168 return ctrl;
169 }
170
171 static const char *
172 get_tty(pam_handle_t *pamh)
173 {
174 const void *void_terminal_line = NULL;
175 const char *terminal_line;
176 const char *str;
177
178 if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS
179 || void_terminal_line == NULL) {
180 terminal_line = DEFAULT_TERM;
181 } else {
182 terminal_line = void_terminal_line;
183 }
184
185 /* strip leading "/dev/" from tty. */
186 str = pam_str_skip_prefix(terminal_line, "/dev/");
187 if (str != NULL)
188 terminal_line = str;
189
190 D(("terminal = %s", terminal_line));
191 return terminal_line;
192 }
193
194 #define MAX_UID_VALUE 0xFFFFFFFFUL
195
196 static uid_t
197 get_lastlog_uid_max(pam_handle_t *pamh)
198 {
199 uid_t uid_max = MAX_UID_VALUE;
200 unsigned long ul;
201 char *s, *ep;
202
203 s = pam_modutil_search_key(pamh, PATH_LOGIN_DEFS, "LASTLOG_UID_MAX");
204 if (s == NULL)
205 return uid_max;
206
207 ep = s + strlen(s);
208 while (ep > s && isspace(*(--ep))) {
209 *ep = '\0';
210 }
211 errno = 0;
212 ul = strtoul(s, &ep, 10);
213 if (!(ul >= MAX_UID_VALUE
214 || (uid_t)ul >= MAX_UID_VALUE
215 || (errno != 0 && ul == 0)
216 || s == ep
217 || *ep != '\0')) {
218 uid_max = (uid_t)ul;
219 }
220 free(s);
221
222 return uid_max;
223 }
224
225 static int
226 last_login_open(pam_handle_t *pamh, int announce, uid_t uid)
227 {
228 int last_fd;
229
230 /* obtain the last login date and all the relevant info */
231 last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
232 if (last_fd < 0) {
233 if (errno == ENOENT && (announce & LASTLOG_UPDATE)) {
234 last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
235 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
236 if (last_fd < 0) {
237 pam_syslog(pamh, LOG_ERR,
238 "unable to create %s: %m", _PATH_LASTLOG);
239 D(("unable to create %s file", _PATH_LASTLOG));
240 return -1;
241 }
242 pam_syslog(pamh, LOG_NOTICE,
243 "file %s created", _PATH_LASTLOG);
244 D(("file %s created", _PATH_LASTLOG));
245 } else {
246 pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
247 D(("unable to open %s file", _PATH_LASTLOG));
248 return -1;
249 }
250 }
251
252 if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) {
253 pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
254 D(("unable to lseek %s file", _PATH_LASTLOG));
255 close(last_fd);
256 return -1;
257 }
258
259 return last_fd;
260 }
261
262
263 static int
264 last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
265 {
266 struct flock last_lock;
267 struct lastlog last_login;
268 int lock_retries = LOCK_RETRIES;
269 int retval = PAM_SUCCESS;
270 char the_time[256];
271 char *date = NULL;
272 char *host = NULL;
273 char *line = NULL;
274
275 memset(&last_lock, 0, sizeof(last_lock));
276 last_lock.l_type = F_RDLCK;
277 last_lock.l_whence = SEEK_SET;
278 last_lock.l_start = sizeof(last_login) * (off_t) uid;
279 last_lock.l_len = sizeof(last_login);
280
281 while (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
282 if (0 == --lock_retries) {
283 /* read lock failed, proceed anyway to avoid possible DoS */
284 D(("locking %s failed", _PATH_LASTLOG));
285 pam_syslog(pamh, LOG_INFO,
286 "file %s is locked/read, proceeding anyway",
287 _PATH_LASTLOG);
288 break;
289 }
290 D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
291 pam_syslog(pamh, LOG_INFO,
292 "file %s is locked/read, retrying", _PATH_LASTLOG);
293 sleep(LOCK_RETRY_DELAY);
294 }
295
296 if (pam_modutil_read(last_fd, (char *) &last_login,
297 sizeof(last_login)) != sizeof(last_login)) {
298 memset(&last_login, 0, sizeof(last_login));
299 }
300
301 last_lock.l_type = F_UNLCK;
302 (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
303
304 *lltime = last_login.ll_time;
305 if (!last_login.ll_time) {
306 if (announce & LASTLOG_DEBUG) {
307 pam_syslog(pamh, LOG_DEBUG,
308 "first login for user with uid %lu",
309 (unsigned long int)uid);
310 }
311 }
312
313 if (!(announce & LASTLOG_QUIET)) {
314
315 if (last_login.ll_time) {
316
317 /* we want the date? */
318 if (announce & LASTLOG_DATE) {
319 struct tm *tm, tm_buf;
320 time_t ll_time;
321
322 ll_time = last_login.ll_time;
323 if ((tm = localtime_r (&ll_time, &tm_buf)) != NULL) {
324 strftime (the_time, sizeof (the_time),
325 /* TRANSLATORS: "strftime options for date of last login" */
326 _(" %a %b %e %H:%M:%S %Z %Y"), tm);
327 date = the_time;
328 }
329 }
330
331 /* we want & have the host? */
332 if ((announce & LASTLOG_HOST)
333 && (last_login.ll_host[0] != '\0')) {
334 /* TRANSLATORS: " from <host>" */
335 if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
336 last_login.ll_host) < 0) {
337 pam_syslog(pamh, LOG_CRIT, "out of memory");
338 retval = PAM_BUF_ERR;
339 goto cleanup;
340 }
341 }
342
343 /* we want and have the terminal? */
344 if ((announce & LASTLOG_LINE)
345 && (last_login.ll_line[0] != '\0')) {
346 /* TRANSLATORS: " on <terminal>" */
347 if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
348 last_login.ll_line) < 0) {
349 pam_syslog(pamh, LOG_CRIT, "out of memory");
350 retval = PAM_BUF_ERR;
351 goto cleanup;
352 }
353 }
354
355 if (date != NULL || host != NULL || line != NULL)
356 /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
357 retval = pam_info(pamh, _("Last login:%s%s%s"),
358 date ? date : "",
359 host ? host : "",
360 line ? line : "");
361 } else if (announce & LASTLOG_NEVER) {
362 D(("this is the first time this user has logged in"));
363 retval = pam_info(pamh, "%s", _("Welcome to your new account!"));
364 }
365 }
366
367 /* cleanup */
368 cleanup:
369 pam_overwrite_object(&last_login);
370 pam_overwrite_string(date);
371 pam_overwrite_string(host);
372 _pam_drop(host);
373 pam_overwrite_string(line);
374 _pam_drop(line);
375
376 return retval;
377 }
378
379 static int
380 last_login_write(pam_handle_t *pamh, int announce, int last_fd,
381 uid_t uid, const char *user)
382 {
383 static struct rlimit no_limit = {
384 RLIM_INFINITY,
385 RLIM_INFINITY
386 };
387 struct rlimit old_limit;
388 int setrlimit_res;
389 struct flock last_lock;
390 struct lastlog last_login;
391 int lock_retries = LOCK_RETRIES;
392 time_t ll_time;
393 const void *void_remote_host = NULL;
394 const char *remote_host;
395 const char *terminal_line;
396 int retval = PAM_SUCCESS;
397
398 /* rewind */
399 if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) {
400 pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
401 return PAM_SERVICE_ERR;
402 }
403
404 memset(&last_login, 0, sizeof(last_login));
405
406 /* set this login date */
407 D(("set the most recent login time"));
408 (void) time(&ll_time); /* set the time */
409 last_login.ll_time = ll_time;
410
411 /* set the remote host */
412 if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS
413 || void_remote_host == NULL) {
414 remote_host = DEFAULT_HOST;
415 } else {
416 remote_host = void_remote_host;
417 }
418
419 /* copy to last_login */
420 strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1);
421
422 /* set the terminal line */
423 terminal_line = get_tty(pamh);
424
425 /* copy to last_login */
426 strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1);
427 terminal_line = NULL;
428
429 D(("locking lastlog file"));
430
431 /* now we try to lock this file-record exclusively; non-blocking */
432 memset(&last_lock, 0, sizeof(last_lock));
433 last_lock.l_type = F_WRLCK;
434 last_lock.l_whence = SEEK_SET;
435 last_lock.l_start = sizeof(last_login) * (off_t) uid;
436 last_lock.l_len = sizeof(last_login);
437
438 while (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
439 if (0 == --lock_retries) {
440 D(("locking %s failed", _PATH_LASTLOG));
441 pam_syslog(pamh, LOG_ERR,
442 "file %s is locked/write", _PATH_LASTLOG);
443 return PAM_SERVICE_ERR;
444 }
445 D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
446 pam_syslog(pamh, LOG_INFO,
447 "file %s is locked/write, retrying", _PATH_LASTLOG);
448 sleep(LOCK_RETRY_DELAY);
449 }
450
451 /*
452 * Failing to set the 'fsize' limit is not a fatal error. We try to write
453 * lastlog anyway, under the risk of dying due to a SIGXFSZ.
454 */
455 D(("setting limit for 'fsize'"));
456
457 if ((announce & LASTLOG_UNLIMITED) == 0) { /* don't set to unlimited */
458 setrlimit_res = -1;
459 } else if (getrlimit(RLIMIT_FSIZE, &old_limit) == 0) {
460 if (old_limit.rlim_cur == RLIM_INFINITY) { /* already unlimited */
461 setrlimit_res = -1;
462 } else {
463 setrlimit_res = setrlimit(RLIMIT_FSIZE, &no_limit);
464 if (setrlimit_res != 0)
465 pam_syslog(pamh, LOG_WARNING, "Could not set limit for 'fsize': %m");
466 }
467 } else {
468 setrlimit_res = -1;
469 if (errno == EINVAL) {
470 pam_syslog(pamh, LOG_INFO, "Limit for 'fsize' not supported: %m");
471 } else {
472 pam_syslog(pamh, LOG_WARNING, "Could not get limit for 'fsize': %m");
473 }
474 }
475
476 D(("writing to the lastlog file"));
477 if (pam_modutil_write (last_fd, (char *) &last_login,
478 sizeof (last_login)) != sizeof(last_login)) {
479 pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG);
480 retval = PAM_SERVICE_ERR;
481 }
482
483 /*
484 * Failing to restore the 'fsize' limit is a fatal error.
485 */
486 D(("restoring limit for 'fsize'"));
487 if (setrlimit_res == 0) {
488 setrlimit_res = setrlimit(RLIMIT_FSIZE, &old_limit);
489 if (setrlimit_res != 0) {
490 pam_syslog(pamh, LOG_ERR, "Could not restore limit for 'fsize': %m");
491 retval = PAM_SERVICE_ERR;
492 }
493 }
494
495 last_lock.l_type = F_UNLCK;
496 (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
497 D(("unlocked"));
498
499 if (announce & LASTLOG_WTMP) {
500 /* write wtmp entry for user */
501 logwtmp(last_login.ll_line, user, remote_host);
502 }
503
504 /* cleanup */
505 pam_overwrite_object(&last_login);
506
507 return retval;
508 }
509
510 static int
511 last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime)
512 {
513 int retval;
514 int last_fd;
515
516 if (uid > get_lastlog_uid_max(pamh)) {
517 return PAM_SUCCESS;
518 }
519
520 /* obtain the last login date and all the relevant info */
521 last_fd = last_login_open(pamh, announce, uid);
522 if (last_fd < 0) {
523 return PAM_SERVICE_ERR;
524 }
525
526 retval = last_login_read(pamh, announce, last_fd, uid, lltime);
527 if (retval != PAM_SUCCESS)
528 {
529 close(last_fd);
530 D(("error while reading lastlog file"));
531 return retval;
532 }
533
534 if (announce & LASTLOG_UPDATE) {
535 retval = last_login_write(pamh, announce, last_fd, uid, user);
536 }
537
538 close(last_fd);
539 D(("all done with last login"));
540
541 return retval;
542 }
543
544 static int
545 last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
546 {
547 int retval;
548 int fd;
549 struct utmp ut;
550 struct utmp utuser;
551 int failed = 0;
552 char the_time[256];
553 char *date = NULL;
554 char *host = NULL;
555 char *line = NULL;
556
557 if (strlen(user) > UT_NAMESIZE) {
558 pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
559 }
560
561 /* obtain the failed login attempt records from btmp */
562 fd = open(_PATH_BTMP, O_RDONLY);
563 if (fd < 0) {
564 int save_errno = errno;
565 pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
566 D(("unable to open %s file", _PATH_BTMP));
567 if (save_errno == ENOENT)
568 return PAM_SUCCESS;
569 else
570 return PAM_SERVICE_ERR;
571 }
572
573 while ((retval=pam_modutil_read(fd, (void *)&ut,
574 sizeof(ut))) == sizeof(ut)) {
575 if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) {
576 memcpy(&utuser, &ut, sizeof(utuser));
577 failed++;
578 }
579 }
580
581 if (retval != 0)
582 pam_syslog(pamh, LOG_ERR, "corruption detected in %s", _PATH_BTMP);
583 retval = PAM_SUCCESS;
584
585 if (failed) {
586 /* we want the date? */
587 if (announce & LASTLOG_DATE) {
588 struct tm *tm, tm_buf;
589 time_t lf_time;
590
591 lf_time = utuser.ut_tv.tv_sec;
592 if ((tm = localtime_r (&lf_time, &tm_buf)) != NULL) {
593 strftime (the_time, sizeof (the_time),
594 /* TRANSLATORS: "strftime options for date of last login" */
595 _(" %a %b %e %H:%M:%S %Z %Y"), tm);
596 date = the_time;
597 }
598 }
599
600 /* we want & have the host? */
601 if ((announce & LASTLOG_HOST)
602 && (utuser.ut_host[0] != '\0')) {
603 /* TRANSLATORS: " from <host>" */
604 if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
605 utuser.ut_host) < 0) {
606 pam_syslog(pamh, LOG_CRIT, "out of memory");
607 retval = PAM_BUF_ERR;
608 goto cleanup;
609 }
610 }
611
612 /* we want and have the terminal? */
613 if ((announce & LASTLOG_LINE)
614 && (utuser.ut_line[0] != '\0')) {
615 /* TRANSLATORS: " on <terminal>" */
616 if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
617 utuser.ut_line) < 0) {
618 pam_syslog(pamh, LOG_CRIT, "out of memory");
619 retval = PAM_BUF_ERR;
620 goto cleanup;
621 }
622 }
623
624 if (line != NULL || date != NULL || host != NULL) {
625 /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */
626 pam_info(pamh, _("Last failed login:%s%s%s"),
627 date ? date : "",
628 host ? host : "",
629 line ? line : "");
630 }
631
632 _pam_drop(line);
633 #if defined HAVE_DNGETTEXT && defined ENABLE_NLS
634 retval = asprintf (&line, dngettext(PACKAGE,
635 "There was %d failed login attempt since the last successful login.",
636 "There were %d failed login attempts since the last successful login.",
637 failed),
638 failed);
639 #else
640 if (failed == 1)
641 retval = asprintf(&line,
642 _("There was %d failed login attempt since the last successful login."),
643 failed);
644 else
645 retval = asprintf(&line,
646 /* TRANSLATORS: only used if dngettext is not supported */
647 _("There were %d failed login attempts since the last successful login."),
648 failed);
649 #endif
650 if (retval >= 0)
651 retval = pam_info(pamh, "%s", line);
652 else {
653 retval = PAM_BUF_ERR;
654 line = NULL;
655 }
656 }
657
658 cleanup:
659 free(host);
660 free(line);
661 close(fd);
662 D(("all done with btmp"));
663
664 return retval;
665 }
666
667 /* --- authentication (locking out inactive users) functions --- */
668 int
669 pam_sm_authenticate(pam_handle_t *pamh, int flags,
670 int argc, const char **argv)
671 {
672 int retval, ctrl;
673 const char *user = NULL;
674 const struct passwd *pwd;
675 uid_t uid;
676 time_t lltime = 0;
677 time_t inactive_days = 0;
678 int last_fd;
679
680 /*
681 * Lock out the user if he did not login recently enough.
682 */
683
684 ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days);
685
686 /* which user? */
687
688 if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
689 pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
690 return PAM_USER_UNKNOWN;
691 }
692
693 /* what uid? */
694
695 pwd = pam_modutil_getpwnam (pamh, user);
696 if (pwd == NULL) {
697 pam_syslog(pamh, LOG_NOTICE, "user unknown");
698 return PAM_USER_UNKNOWN;
699 }
700 uid = pwd->pw_uid;
701 pwd = NULL; /* tidy up */
702
703 if (uid == 0 || uid > get_lastlog_uid_max(pamh))
704 return PAM_SUCCESS;
705
706 /* obtain the last login date and all the relevant info */
707 last_fd = last_login_open(pamh, ctrl, uid);
708 if (last_fd < 0) {
709 return PAM_IGNORE;
710 }
711
712 retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime);
713 close(last_fd);
714
715 if (retval != PAM_SUCCESS) {
716 D(("error while reading lastlog file"));
717 return PAM_IGNORE;
718 }
719
720 if (lltime == 0) { /* user never logged in before */
721 if (ctrl & LASTLOG_DEBUG)
722 pam_syslog(pamh, LOG_DEBUG, "user never logged in - pass");
723 return PAM_SUCCESS;
724 }
725
726 lltime = (time(NULL) - lltime) / (24*60*60);
727
728 if (lltime > inactive_days) {
729 pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied",
730 user, (long) lltime);
731 return PAM_AUTH_ERR;
732 }
733
734 return PAM_SUCCESS;
735 }
736
737 int
738 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
739 int argc UNUSED, const char **argv UNUSED)
740 {
741 return PAM_SUCCESS;
742 }
743
744 int
745 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
746 int argc, const char **argv)
747 {
748 return pam_sm_authenticate(pamh, flags, argc, argv);
749 }
750
751 /* --- session management functions --- */
752
753 int
754 pam_sm_open_session(pam_handle_t *pamh, int flags,
755 int argc, const char **argv)
756 {
757 int retval, ctrl;
758 const void *user;
759 const struct passwd *pwd;
760 uid_t uid;
761 time_t lltime = 0;
762
763 /*
764 * this module gets the uid of the PAM_USER. Uses it to display
765 * last login info and then updates the lastlog for that user.
766 */
767
768 ctrl = _pam_session_parse(pamh, flags, argc, argv);
769
770 /* which user? */
771
772 retval = pam_get_item(pamh, PAM_USER, &user);
773 if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') {
774 pam_syslog(pamh, LOG_NOTICE, "user unknown");
775 return PAM_USER_UNKNOWN;
776 }
777
778 /* what uid? */
779
780 pwd = pam_modutil_getpwnam (pamh, user);
781 if (pwd == NULL) {
782 D(("couldn't identify user %s", user));
783 return PAM_USER_UNKNOWN;
784 }
785 uid = pwd->pw_uid;
786 pwd = NULL; /* tidy up */
787
788 /* process the current login attempt (indicate last) */
789
790 retval = last_login_date(pamh, ctrl, uid, user, &lltime);
791
792 if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
793 retval = last_login_failed(pamh, ctrl, user, lltime);
794 }
795
796 /* indicate success or failure */
797
798 uid = -1; /* forget this */
799
800 return retval;
801 }
802
803 int
804 pam_sm_close_session (pam_handle_t *pamh, int flags,
805 int argc, const char **argv)
806 {
807 const char *terminal_line;
808
809 if (!(_pam_session_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
810 return PAM_SUCCESS;
811
812 terminal_line = get_tty(pamh);
813
814 /* Wipe out utmp logout entry */
815 logwtmp(terminal_line, "", "");
816
817 return PAM_SUCCESS;
818 }
819
820 /* end of module definition */