]> granicus.if.org Git - linux-pam/blob - modules/pam_lastlog/pam_lastlog.c
pam_lastlog: prevent crash due to reduced 'fsize' limit
[linux-pam] / modules / pam_lastlog / pam_lastlog.c
1 /* pam_lastlog module */
2
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 /* XXX - time before ignoring lock. Is 1 sec enough? */
61 #define LASTLOG_IGNORE_LOCK_TIME     1
62
63 #define DEFAULT_HOST     ""  /* "[no.where]" */
64 #define DEFAULT_TERM     ""  /* "tt???" */
65
66 #define DEFAULT_INACTIVE_DAYS 90
67 #define MAX_INACTIVE_DAYS 100000
68
69 /*
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.
74  */
75
76 #define PAM_SM_SESSION
77 #define PAM_SM_AUTH
78 #define PAM_SM_ACCOUNT
79
80 #include <security/pam_modules.h>
81 #include <security/_pam_macros.h>
82 #include <security/pam_modutil.h>
83 #include <security/pam_ext.h>
84
85 /* argument parsing */
86
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) */
97
98 static int
99 _pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv,
100     time_t *inactive)
101 {
102     int ctrl = 0;
103
104     *inactive = DEFAULT_INACTIVE_DAYS;
105
106     /* does the appliction require quiet? */
107     if (flags & PAM_SILENT) {
108         ctrl |= LASTLOG_QUIET;
109     }
110
111     /* step through arguments */
112     for (; argc-- > 0; ++argv) {
113         char *ep = NULL;
114         long l;
115
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)
123                 *inactive = l;
124             else {
125                 pam_syslog(pamh, LOG_ERR, "bad option value: %s", *argv);
126             }
127         } else {
128             pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
129         }
130     }
131
132     D(("ctrl = %o", ctrl));
133     return ctrl;
134 }
135
136 static int
137 _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
138 {
139     int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
140
141     /* step through arguments */
142     for (; argc-- > 0; ++argv) {
143
144         /* generic options */
145
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;
166         } else {
167             pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
168         }
169     }
170
171     /* does the appliction require quiet? */
172     if (flags & PAM_SILENT) {
173         ctrl |= LASTLOG_QUIET;
174         ctrl &= ~LASTLOG_BTMP;
175     }
176
177     D(("ctrl = %o", ctrl));
178     return ctrl;
179 }
180
181 static const char *
182 get_tty(pam_handle_t *pamh)
183 {
184     const void *void_terminal_line = NULL;
185     const char *terminal_line;
186
187     if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS
188         || void_terminal_line == NULL) {
189         terminal_line = DEFAULT_TERM;
190     } else {
191         terminal_line = void_terminal_line;
192     }
193     if (!strncmp("/dev/", terminal_line, 5)) {
194         /* strip leading "/dev/" from tty. */
195         terminal_line += 5;
196     }
197     D(("terminal = %s", terminal_line));
198     return terminal_line;
199 }
200
201 #define MAX_UID_VALUE 0xFFFFFFFFUL
202
203 static uid_t
204 get_lastlog_uid_max(pam_handle_t *pamh)
205 {
206     uid_t uid_max = MAX_UID_VALUE;
207     unsigned long ul;
208     char *s, *ep;
209
210     s = pam_modutil_search_key(pamh, PATH_LOGIN_DEFS, "LASTLOG_UID_MAX");
211     if (s == NULL)
212         return uid_max;
213
214     ep = s + strlen(s);
215     while (ep > s && isspace(*(--ep))) {
216         *ep = '\0';
217     }
218     errno = 0;
219     ul = strtoul(s, &ep, 10);
220     if (!(ul >= MAX_UID_VALUE
221         || (uid_t)ul >= MAX_UID_VALUE
222         || (errno != 0 && ul == 0)
223         || s == ep
224         || *ep != '\0')) {
225         uid_max = (uid_t)ul;
226     }
227     free(s);
228
229     return uid_max;
230 }
231
232 static int
233 last_login_open(pam_handle_t *pamh, int announce, uid_t uid)
234 {
235     int last_fd;
236
237     /* obtain the last login date and all the relevant info */
238     last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
239     if (last_fd < 0) {
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);
243              if (last_fd < 0) {
244                   pam_syslog(pamh, LOG_ERR,
245                              "unable to create %s: %m", _PATH_LASTLOG);
246                   D(("unable to create %s file", _PATH_LASTLOG));
247                   return -1;
248              }
249              pam_syslog(pamh, LOG_NOTICE,
250                         "file %s created", _PATH_LASTLOG);
251              D(("file %s created", _PATH_LASTLOG));
252         } else {
253           pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
254           D(("unable to open %s file", _PATH_LASTLOG));
255           return -1;
256         }
257     }
258
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));
262         close(last_fd);
263         return -1;
264     }
265
266     return last_fd;
267 }
268
269
270 static int
271 last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
272 {
273     struct flock last_lock;
274     struct lastlog last_login;
275     int retval = PAM_SUCCESS;
276     char the_time[256];
277     char *date = NULL;
278     char *host = NULL;
279     char *line = NULL;
280
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);
286
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);
292     }
293
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));
297     }
298
299     last_lock.l_type = F_UNLCK;
300     (void) fcntl(last_fd, F_SETLK, &last_lock);        /* unlock */
301
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);
308         }
309     }
310
311     if (!(announce & LASTLOG_QUIET)) {
312
313         if (last_login.ll_time) {
314
315             /* we want the date? */
316             if (announce & LASTLOG_DATE) {
317                 struct tm *tm, tm_buf;
318                 time_t ll_time;
319
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);
325                         date = the_time;
326                 }
327             }
328
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;
337                     goto cleanup;
338                 }
339             }
340
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;
349                     goto cleanup;
350                 }
351             }
352
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"),
356                               date ? date : "",
357                               host ? host : "",
358                               line ? line : "");
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!"));
362         }
363     }
364
365     /* cleanup */
366  cleanup:
367     memset(&last_login, 0, sizeof(last_login));
368     _pam_overwrite(date);
369     _pam_overwrite(host);
370     _pam_drop(host);
371     _pam_overwrite(line);
372     _pam_drop(line);
373
374     return retval;
375 }
376
377 static int
378 last_login_write(pam_handle_t *pamh, int announce, int last_fd,
379                  uid_t uid, const char *user)
380 {
381     static struct rlimit no_limit = {
382         RLIM_INFINITY,
383         RLIM_INFINITY
384     };
385     struct rlimit old_limit;
386     int setrlimit_res;
387     struct flock last_lock;
388     struct lastlog last_login;
389     time_t ll_time;
390     const void *void_remote_host = NULL;
391     const char *remote_host;
392     const char *terminal_line;
393     int retval = PAM_SUCCESS;
394
395     /* rewind */
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;
399     }
400
401     memset(&last_login, 0, sizeof(last_login));
402
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;
407
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;
412     } else {
413         remote_host = void_remote_host;
414     }
415
416     /* copy to last_login */
417     strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1);
418
419     /* set the terminal line */
420     terminal_line = get_tty(pamh);
421
422     /* copy to last_login */
423     strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1);
424     terminal_line = NULL;
425
426     D(("locking lastlog file"));
427
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);
434
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);
439     }
440
441     /*
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.
444      */
445     D(("setting limit for 'fsize'"));
446
447     if ((announce & LASTLOG_UNLIMITED) == 0) {    /* don't set to unlimted */
448         setrlimit_res = -1;
449     } else if (getrlimit(RLIMIT_FSIZE, &old_limit) == 0) {
450         if (old_limit.rlim_cur == RLIM_INFINITY) {    /* already unlimited */
451             setrlimit_res = -1;
452         } else {
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");
456         }
457     } else {
458         setrlimit_res = -1;
459         if (errno == EINVAL) {
460             pam_syslog(pamh, LOG_INFO, "Limit for 'fsize' not supported: %m");
461         } else {
462             pam_syslog(pamh, LOG_WARNING, "Could not get limit for 'fsize': %m");
463         }
464     }
465
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;
471     }
472
473     /*
474      * Failing to restore the 'fsize' limit is a fatal error.
475      */
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;
482         }
483     }
484
485     last_lock.l_type = F_UNLCK;
486     (void) fcntl(last_fd, F_SETLK, &last_lock);        /* unlock */
487     D(("unlocked"));
488
489     if (announce & LASTLOG_WTMP) {
490         /* write wtmp entry for user */
491         logwtmp(last_login.ll_line, user, remote_host);
492     }
493
494     /* cleanup */
495     memset(&last_login, 0, sizeof(last_login));
496
497     return retval;
498 }
499
500 static int
501 last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime)
502 {
503     int retval;
504     int last_fd;
505
506     if (uid > get_lastlog_uid_max(pamh)) {
507         return PAM_SUCCESS;
508     }
509
510     /* obtain the last login date and all the relevant info */
511     last_fd = last_login_open(pamh, announce, uid);
512     if (last_fd < 0) {
513         return PAM_SERVICE_ERR;
514     }
515
516     retval = last_login_read(pamh, announce, last_fd, uid, lltime);
517     if (retval != PAM_SUCCESS)
518       {
519         close(last_fd);
520         D(("error while reading lastlog file"));
521         return retval;
522       }
523
524     if (announce & LASTLOG_UPDATE) {
525         retval = last_login_write(pamh, announce, last_fd, uid, user);
526     }
527
528     close(last_fd);
529     D(("all done with last login"));
530
531     return retval;
532 }
533
534 static int
535 last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
536 {
537     int retval;
538     int fd;
539     struct utmp ut;
540     struct utmp utuser;
541     int failed = 0;
542     char the_time[256];
543     char *date = NULL;
544     char *host = NULL;
545     char *line = NULL;
546
547     if (strlen(user) > UT_NAMESIZE) {
548         pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
549     }
550
551     /* obtain the failed login attempt records from btmp */
552     fd = open(_PATH_BTMP, O_RDONLY);
553     if (fd < 0) {
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)
558           return PAM_SUCCESS;
559         else
560           return PAM_SERVICE_ERR;
561     }
562
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));
567             failed++;
568         }
569     }
570
571     if (retval != 0)
572         pam_syslog(pamh, LOG_ERR, "corruption detected in %s", _PATH_BTMP);
573     retval = PAM_SUCCESS;
574
575     if (failed) {
576         /* we want the date? */
577         if (announce & LASTLOG_DATE) {
578             struct tm *tm, tm_buf;
579             time_t lf_time;
580
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);
586
587             date = the_time;
588         }
589
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;
598                 goto cleanup;
599             }
600         }
601
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;
610                 goto cleanup;
611             }
612         }
613
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"),
617                               date ? date : "",
618                               host ? host : "",
619                               line ? line : "");
620         }
621
622         _pam_drop(line);
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.",
627                 failed),
628             failed);
629 #else
630         if (failed == 1)
631             retval = asprintf(&line,
632                 _("There was %d failed login attempt since the last successful login."),
633                 failed);
634         else
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."),
638                 failed);
639 #endif
640         if (retval >= 0)
641                 retval = pam_info(pamh, "%s", line);
642         else {
643                 retval = PAM_BUF_ERR;
644                 line = NULL;
645         }
646     }
647
648 cleanup:
649     free(host);
650     free(line);
651     close(fd);
652     D(("all done with btmp"));
653
654     return retval;
655 }
656
657 /* --- authentication (locking out inactive users) functions --- */
658 int
659 pam_sm_authenticate(pam_handle_t *pamh, int flags,
660                     int argc, const char **argv)
661 {
662     int retval, ctrl;
663     const char *user = NULL;
664     const struct passwd *pwd;
665     uid_t uid;
666     time_t lltime = 0;
667     time_t inactive_days = 0;
668     int last_fd;
669
670     /*
671      * Lock out the user if he did not login recently enough.
672      */
673
674     ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days);
675
676     /* which user? */
677
678     if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
679         || *user == '\0') {
680         pam_syslog(pamh, LOG_ERR, "cannot determine the user's name");
681         return PAM_USER_UNKNOWN;
682     }
683
684     /* what uid? */
685
686     pwd = pam_modutil_getpwnam (pamh, user);
687     if (pwd == NULL) {
688         pam_syslog(pamh, LOG_ERR, "user unknown");
689         return PAM_USER_UNKNOWN;
690     }
691     uid = pwd->pw_uid;
692     pwd = NULL;                                         /* tidy up */
693
694     if (uid == 0 || uid > get_lastlog_uid_max(pamh))
695         return PAM_SUCCESS;
696
697     /* obtain the last login date and all the relevant info */
698     last_fd = last_login_open(pamh, ctrl, uid);
699     if (last_fd < 0) {
700         return PAM_IGNORE;
701     }
702
703     retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime);
704     close(last_fd);
705
706     if (retval != PAM_SUCCESS) {
707         D(("error while reading lastlog file"));
708         return PAM_IGNORE;
709     }
710
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");
714         return PAM_SUCCESS;
715     }
716
717     lltime = (time(NULL) - lltime) / (24*60*60);
718
719     if (lltime > inactive_days) {
720         pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied",
721                    user, (long) lltime);
722         return PAM_AUTH_ERR;
723     }
724
725     return PAM_SUCCESS;
726 }
727
728 int
729 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
730                     int argc UNUSED, const char **argv UNUSED)
731 {
732     return PAM_SUCCESS;
733 }
734
735 int
736 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
737                     int argc, const char **argv)
738 {
739     return pam_sm_authenticate(pamh, flags, argc, argv);
740 }
741
742 /* --- session management functions --- */
743
744 int
745 pam_sm_open_session(pam_handle_t *pamh, int flags,
746                     int argc, const char **argv)
747 {
748     int retval, ctrl;
749     const void *user;
750     const struct passwd *pwd;
751     uid_t uid;
752     time_t lltime = 0;
753
754     /*
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.
757      */
758
759     ctrl = _pam_session_parse(pamh, flags, argc, argv);
760
761     /* which user? */
762
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;
767     }
768
769     /* what uid? */
770
771     pwd = pam_modutil_getpwnam (pamh, user);
772     if (pwd == NULL) {
773         D(("couldn't identify user %s", user));
774         return PAM_USER_UNKNOWN;
775     }
776     uid = pwd->pw_uid;
777     pwd = NULL;                                         /* tidy up */
778
779     /* process the current login attempt (indicate last) */
780
781     retval = last_login_date(pamh, ctrl, uid, user, &lltime);
782
783     if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
784             retval = last_login_failed(pamh, ctrl, user, lltime);
785     }
786
787     /* indicate success or failure */
788
789     uid = -1;                                           /* forget this */
790
791     return retval;
792 }
793
794 int
795 pam_sm_close_session (pam_handle_t *pamh, int flags,
796                       int argc, const char **argv)
797 {
798     const char *terminal_line;
799
800     if (!(_pam_session_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
801         return PAM_SUCCESS;
802
803     terminal_line = get_tty(pamh);
804
805     /* Wipe out utmp logout entry */
806     logwtmp(terminal_line, "", "");
807
808     return PAM_SUCCESS;
809 }
810
811 /* end of module definition */