]> granicus.if.org Git - linux-pam/commitdiff
Relevant BUGIDs:
authorTomas Mraz <tm@t8m.info>
Tue, 30 Sep 2008 14:40:39 +0000 (14:40 +0000)
committerTomas Mraz <tm@t8m.info>
Tue, 30 Sep 2008 14:40:39 +0000 (14:40 +0000)
Purpose of commit: new feature

Commit summary:
---------------
2008-09-30  Tomas Mraz <t8m@centrum.cz>

        * modules/pam_lastlog/pam_lastlog.8.xml: Document new options
        noupdate and showfailed.
        * modules/pam_lastlog/pam_lastlog.c(pam_parse): Recognize the new
        options.
        (last_login_read): New output parameter lltime. Do not display
        the last login message if it would be empty.
        (last_login_date): New output parameter lltime. Do not write the
        last login info when LASTLOG_UPDATE is not set.
        (last_login_failed): New function to display the last bad login
        attempt from btmp.
        (pam_sm_open_session): Obtain lltime from last_login_date() and
        call last_login_failed() when appropriate.

ChangeLog
NEWS
modules/pam_lastlog/pam_lastlog.8.xml
modules/pam_lastlog/pam_lastlog.c

index 5e25c9378ad99d03f1b1070fe67f48f74111e194..021a9f1968f91cdaf1ace6d743b8fea846c9f802 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2008-09-30  Tomas Mraz <t8m@centrum.cz>
+
+       * modules/pam_lastlog/pam_lastlog.8.xml: Document new options
+       noupdate and showfailed.
+       * modules/pam_lastlog/pam_lastlog.c(pam_parse): Recognize the new
+       options.
+       (last_login_read): New output parameter lltime. Do not display
+       the last login message if it would be empty.
+       (last_login_date): New output parameter lltime. Do not write the
+       last login info when LASTLOG_UPDATE is not set.
+       (last_login_failed): New function to display the last bad login
+       attempt from btmp.
+       (pam_sm_open_session): Obtain lltime from last_login_date() and
+       call last_login_failed() when appropriate.
+
 2008-09-29  Thorsten Kukuk  <kukuk@thkukuk.de>
 
        * modules/pam_echo/pam_echo.8.xml: Fix format error.
diff --git a/NEWS b/NEWS
index 5d9927b7b3b2feaf6a15fdd28efd899a98b26b7e..d3e18f77838b58ca4516bd5cedb6f8357238658a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,11 +7,13 @@ Release 1.0.90
 * Make pam_namespace to work safe on child directories of parent directories
   owned by users
 * Redefine LOCAL keyword of pam_access configuration file
-* Add support fro try_first_pass and use_first_pass to pam_cracklib
+* Add support for try_first_pass and use_first_pass to pam_cracklib
 * Print informative messages for rejected login and add silent and
   no_log_info options to pam_tally
 * Add support for passing PAM_AUTHTOK to stdin of helpers from pam_exec
 * New password quality tests in pam_cracklib
+* New options for pam_lastlog to show last failed login attempt and
+  to disable lastlog update
 
 Release 1.0.1
 
index f066ac6a147075349d264fe4ab4af7b276174678..f1fffa89b1aeb89c102c5caf1911a235ff0f2b8e 100644 (file)
       <arg choice="opt">
         nowtmp
       </arg>
+      <arg choice="opt">
+        noupdate
+      </arg>
+      <arg choice="opt">
+        showfailed
+      </arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term>
+          <option>noupdate</option>
+        </term>
+        <listitem>
+          <para>
+            Don't update any file.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <option>showfailed</option>
+        </term>
+        <listitem>
+          <para>
+            Display number of failed login attempts and the date of the
+            last failed attempt from btmp. The date is not displayed
+            when <option>nodate</option> is specified.
+          </para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index a75e1ce7a4129de031e71634795125df14bba8b1..8af6b9ebe0faead68150ae560e8d1b1a153aff8b 100644 (file)
@@ -46,6 +46,10 @@ struct lastlog {
 };
 #endif /* hpux */
 
+#ifndef _PATH_BTMP
+# define _PATH_BTMP "/var/log/btmp"
+#endif
+
 /* XXX - time before ignoring lock. Is 1 sec enough? */
 #define LASTLOG_IGNORE_LOCK_TIME     1
 
@@ -75,11 +79,13 @@ struct lastlog {
 #define LASTLOG_DEBUG      020  /* send info to syslog(3) */
 #define LASTLOG_QUIET      040  /* keep quiet about things */
 #define LASTLOG_WTMP      0100  /* log to wtmp as well as lastlog */
+#define LASTLOG_BTMP      0200  /* display failed login info from btmp */
+#define LASTLOG_UPDATE    0400  /* update the lastlog and wtmp files (default) */
 
 static int
 _pam_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
 {
-    int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP);
+    int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
 
     /* does the appliction require quiet? */
     if (flags & PAM_SILENT) {
@@ -105,6 +111,10 @@ _pam_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
            ctrl |= LASTLOG_NEVER;
        } else if (!strcmp(*argv,"nowtmp")) {
            ctrl &= ~LASTLOG_WTMP;
+       } else if (!strcmp(*argv,"noupdate")) {
+           ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE);
+       } else if (!strcmp(*argv,"showfailed")) {
+           ctrl |= LASTLOG_BTMP;
        } else {
            pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
        }
@@ -135,7 +145,7 @@ get_tty(pam_handle_t *pamh)
 }
 
 static int
-last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid)
+last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
 {
     struct flock last_lock;
     struct lastlog last_login;
@@ -166,6 +176,7 @@ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid)
     last_lock.l_type = F_UNLCK;
     (void) fcntl(last_fd, F_SETLK, &last_lock);        /* unlock */
 
+    *lltime = last_login.ll_time;
     if (!last_login.ll_time) {
         if (announce & LASTLOG_DEBUG) {
            pam_syslog(pamh, LOG_DEBUG,
@@ -216,8 +227,9 @@ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid)
                }
            }
 
-               /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
-           retval = pam_info(pamh, _("Last login:%s%s%s"),
+           if (date != NULL || host != NULL || line != NULL)
+                   /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
+                   retval = pam_info(pamh, _("Last login:%s%s%s"),
                              date ? date : "",
                              host ? host : "",
                              line ? line : "");
@@ -320,13 +332,13 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd,
 }
 
 static int
-last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user)
+last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime)
 {
     int retval;
     int last_fd;
 
     /* obtain the last login date and all the relevant info */
-    last_fd = open(_PATH_LASTLOG, O_RDWR);
+    last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
     if (last_fd < 0) {
         if (errno == ENOENT) {
             last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
@@ -353,7 +365,7 @@ last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user)
        return PAM_SERVICE_ERR;
     }
 
-    retval = last_login_read(pamh, announce, last_fd, uid);
+    retval = last_login_read(pamh, announce, last_fd, uid, lltime);
     if (retval != PAM_SUCCESS)
       {
        close(last_fd);
@@ -361,7 +373,9 @@ last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user)
        return retval;
       }
 
-    retval = last_login_write(pamh, announce, last_fd, uid, user);
+    if (announce & LASTLOG_UPDATE) {
+       retval = last_login_write(pamh, announce, last_fd, uid, user);
+    }
 
     close(last_fd);
     D(("all done with last login"));
@@ -369,6 +383,121 @@ last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user)
     return retval;
 }
 
+static int
+last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
+{
+    int retval;
+    int fd;
+    struct utmp ut;
+    struct utmp utuser;
+    int failed = 0;
+    char the_time[256];
+    char *date = NULL;
+    char *host = NULL;
+    char *line = NULL;
+
+    if (strlen(user) > UT_NAMESIZE) {
+       pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
+    }
+
+    /* obtain the failed login attempt records from btmp */
+    fd = open(_PATH_BTMP, O_RDONLY);
+    if (fd < 0) {
+       pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
+       D(("unable to open %s file", _PATH_BTMP));
+       return PAM_SERVICE_ERR;
+    }
+
+    while ((retval=pam_modutil_read(fd, (void *)&ut,
+                        sizeof(ut))) == sizeof(ut)) {
+       if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) {
+           memcpy(&utuser, &ut, sizeof(utuser));
+           failed++;
+       }
+    }
+
+    if (failed) {
+       /* we want the date? */
+       if (announce & LASTLOG_DATE) {
+           struct tm *tm, tm_buf;
+           time_t lf_time;
+
+           lf_time = utuser.ut_tv.tv_sec;
+           tm = localtime_r (&lf_time, &tm_buf);
+           strftime (the_time, sizeof (the_time),
+               /* TRANSLATORS: "strftime options for date of last login" */
+               _(" %a %b %e %H:%M:%S %Z %Y"), tm);
+
+           date = the_time;
+       }
+
+       /* we want & have the host? */
+       if ((announce & LASTLOG_HOST)
+               && (utuser.ut_host[0] != '\0')) {
+           /* TRANSLATORS: " from <host>" */
+           if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
+                   utuser.ut_host) < 0) {
+               pam_syslog(pamh, LOG_ERR, "out of memory");
+               retval = PAM_BUF_ERR;
+               goto cleanup;
+           }
+       }
+
+       /* we want and have the terminal? */
+       if ((announce & LASTLOG_LINE)
+               && (utuser.ut_line[0] != '\0')) {
+           /* TRANSLATORS: " on <terminal>" */
+           if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
+                       utuser.ut_line) < 0) {
+               pam_syslog(pamh, LOG_ERR, "out of memory");
+               retval = PAM_BUF_ERR;
+               goto cleanup;
+           }
+       }
+       
+       if (line != NULL || date != NULL || host != NULL) {
+           /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */
+           pam_info(pamh, _("Last failed login:%s%s%s"),
+                             date ? date : "",
+                             host ? host : "",
+                             line ? line : "");
+       }
+
+       _pam_drop(line);
+#if defined HAVE_DNGETTEXT && defined ENABLE_NLS
+        retval = asprintf (&line, dngettext(PACKAGE,
+               "There was %d failed login attempt since the last successful login.",
+               "There were %d failed login attempts since the last successful login.",
+               failed),
+           failed);
+#else
+       if (daysleft == 1)
+           retval = asprintf(&line,
+               _("There was %d failed login attempt since the last successful login."),
+               failed);
+       else
+           retval = asprintf(&line,
+               /* TRANSLATORS: only used if dngettext is not supported */
+               _("There were %d failed login attempts since the last successful login."),
+               failed);
+#endif
+       if (retval >= 0)
+               retval = pam_info(pamh, "%s", line);
+       else {
+               retval = PAM_BUF_ERR;
+               line = NULL;
+       }
+    }
+
+cleanup:
+    free(host);
+    free(line);
+    close(fd);
+    D(("all done with btmp"));
+
+    return retval;
+}
+
 /* --- authentication management functions (only) --- */
 
 PAM_EXTERN int
@@ -379,6 +508,7 @@ pam_sm_open_session(pam_handle_t *pamh, int flags,
     const void *user;
     const struct passwd *pwd;
     uid_t uid;
+    time_t lltime = 0;
 
     /*
      * this module gets the uid of the PAM_USER. Uses it to display
@@ -407,7 +537,11 @@ pam_sm_open_session(pam_handle_t *pamh, int flags,
 
     /* process the current login attempt (indicate last) */
 
-    retval = last_login_date(pamh, ctrl, uid, user);
+    retval = last_login_date(pamh, ctrl, uid, user, &lltime);
+
+    if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
+           retval = last_login_failed(pamh, ctrl, user, lltime);
+    }
 
     /* indicate success or failure */