[pam: 2/2] multiple backported fixes

Tomáš Mráz tmraz at fedoraproject.org
Wed May 9 09:58:36 UTC 2012


commit 7f16b85d54f469c6c504a0f86636bb32b6a4c5d8
Author: Tomas Mraz <tmraz at fedoraproject.org>
Date:   Wed May 9 11:58:27 2012 +0200

    multiple backported fixes
    
    - add inactive account lock out functionality to pam_lastlog
    - fix pam_unix remember user name matching
    - add gecoscheck and maxclassrepeat functionality to pam_cracklib
    - correctly check for crypt() returning NULL in pam_unix
    - pam_unix - do not fallback to MD5 on password change
      if requested algorithm not supported by crypt() (#818741)

 pam-1.1.5-cracklib-gecoscheck.patch |  373 +++++++++++++++++++++++++++++++++
 pam-1.1.5-lastlog-inactive.patch    |  391 +++++++++++++++++++++++++++++++++++
 pam-1.1.5-unix-crypt.patch          |   53 +++++
 pam-1.1.5-unix-no-fallback.patch    |   69 ++++++
 pam-1.1.5-unix-remember.patch       |   59 ++++++
 pam.spec                            |   25 +++-
 6 files changed, 969 insertions(+), 1 deletions(-)
---
diff --git a/pam-1.1.5-cracklib-gecoscheck.patch b/pam-1.1.5-cracklib-gecoscheck.patch
new file mode 100644
index 0000000..be55f3a
--- /dev/null
+++ b/pam-1.1.5-cracklib-gecoscheck.patch
@@ -0,0 +1,373 @@
+From 422c19520fb814cfd8edd84d7989f4c52acbfa03 Mon Sep 17 00:00:00 2001
+From: Tomas Mraz <tmraz at fedoraproject.org>
+Date: Mon, 30 Apr 2012 15:03:32 +0200
+Subject: [PATCH] pam_cracklib: Add maxclassrepeat, gecoscheck checks and
+ remove unused difignore.
+
+modules/pam_cracklib/pam_cracklib.c (_pam_parse): Recognize the maxclassrepeat, gecoscheck options. Ignore difignore option.
+(simple): Add the check for the same class repetition.
+(usercheck): Refactor into wordcheck().
+(gecoscheck): New test for words from the GECOS field.
+(password_check): Call the gecoscheck().
+(pam_sm_chauthtok): Drop the diff_ignore from options struct.
+modules/pam_cracklib/pam_cracklib.8.xml: Document the maxclassrepeat and gecoscheck checks, update the documentation of the difok test.
+---
+ modules/pam_cracklib/pam_cracklib.8.xml |   66 +++++++++-------
+ modules/pam_cracklib/pam_cracklib.c     |  129 +++++++++++++++++++++++++------
+ 2 files changed, 142 insertions(+), 53 deletions(-)
+
+diff --git a/modules/pam_cracklib/pam_cracklib.8.xml b/modules/pam_cracklib/pam_cracklib.8.xml
+index 29e00c0..5022c75 100644
+--- a/modules/pam_cracklib/pam_cracklib.8.xml
++++ b/modules/pam_cracklib/pam_cracklib.8.xml
+@@ -77,17 +77,10 @@
+           <para>
+             Is the new password too much like the old one?
+             This is primarily controlled by one argument,
+-            <option>difok</option> which is a number of characters
+-            that if different between the old and new are enough to accept
+-            the new password, this defaults to 10 or 1/2 the size of the
+-            new password whichever is smaller.
+-          </para>
+-          <para>
+-            To avoid the lockup associated with trying to change a long and
+-            complicated password, <option>difignore</option> is available.
+-            This argument can be used to specify the minimum length a new
+-            password needs to be before the <option>difok</option> value is
+-            ignored. The default value for <option>difignore</option> is 23.
++            <option>difok</option> which is a number of character changes
++            (inserts, removals, or replacements) between the old and new
++            password that are enough to accept the new password.
++            This defaults to 5 changes.
+           </para>
+         </listitem>
+       </varlistentry>
+@@ -96,7 +89,8 @@
+         <listitem>
+           <para>
+              Is the new password too small?
+-             This is controlled by 5 arguments <option>minlen</option>,
++             This is controlled by 6 arguments <option>minlen</option>,
++             <option>maxclassrepeat</option>,
+              <option>dcredit</option>, <option>ucredit</option>,
+              <option>lcredit</option>, and <option>ocredit</option>. See the section
+              on the arguments for the details of how these work and there defaults.
+@@ -204,24 +198,9 @@
+           <listitem>
+             <para>
+               This argument will change the default of
+-              <emphasis>5</emphasis> for the number of characters in
+-              the new password that must not be present in the old
+-              password. In addition, if 1/2 of the characters in the
+-              new password are different then the new password will
+-              be accepted anyway.
+-            </para>
+-          </listitem>
+-        </varlistentry>
+-
+-        <varlistentry>
+-          <term>
+-            <option>difignore=<replaceable>N</replaceable></option>
+-          </term>
+-          <listitem>
+-            <para>
+-              How many characters should the password have before
+-              difok will be ignored. The default is
+-              <emphasis>23</emphasis>.
++              <emphasis>5</emphasis> for the number of character
++              changes in the new password that differentiate it
++              from the old password.
+             </para>
+           </listitem>
+         </varlistentry>
+@@ -370,6 +349,19 @@
+ 
+         <varlistentry>
+           <term>
++            <option>maxclassrepeat=<replaceable>N</replaceable></option>
++          </term>
++          <listitem>
++            <para>
++              Reject passwords which contain more than N consecutive
++              characters of the same class. The default is 0 which means
++              that this check is disabled.
++            </para>
++          </listitem>
++        </varlistentry>
++
++        <varlistentry>
++          <term>
+             <option>reject_username</option>
+           </term>
+           <listitem>
+@@ -383,6 +375,20 @@
+ 
+         <varlistentry>
+           <term>
++            <option>gecoscheck</option>
++          </term>
++          <listitem>
++            <para>
++              Check whether the words from the GECOS field (usualy full name
++              of the user) longer than 3 characters in straight or reversed
++              form are contained in the new password. If any such word is
++              found the new password is rejected.
++            </para>
++          </listitem>
++        </varlistentry>
++
++        <varlistentry>
++          <term>
+             <option>use_authtok</option>
+           </term>
+           <listitem>
+diff --git a/modules/pam_cracklib/pam_cracklib.c b/modules/pam_cracklib/pam_cracklib.c
+index 1955b83..96ee995 100644
+--- a/modules/pam_cracklib/pam_cracklib.c
++++ b/modules/pam_cracklib/pam_cracklib.c
+@@ -51,6 +51,8 @@
+ #include <sys/stat.h>
+ #include <ctype.h>
+ #include <limits.h>
++#include <pwd.h>
++#include <security/pam_modutil.h>
+ 
+ #ifdef HAVE_CRACK_H
+ #include <crack.h>
+@@ -92,7 +94,6 @@ extern char *FascistCheck(char *pw, const char *dictpath);
+ struct cracklib_options {
+ 	int retry_times;
+ 	int diff_ok;
+-	int diff_ignore;
+ 	int min_length;
+ 	int dig_credit;
+ 	int up_credit;
+@@ -100,19 +101,21 @@ struct cracklib_options {
+ 	int oth_credit;
+         int min_class;
+ 	int max_repeat;
++        int max_class_repeat;
+ 	int reject_user;
++        int gecos_check;
+         const char *cracklib_dictpath;
+ };
+ 
+ #define CO_RETRY_TIMES  1
+ #define CO_DIFF_OK      5
+-#define CO_DIFF_IGNORE  23
+ #define CO_MIN_LENGTH   9
+ # define CO_MIN_LENGTH_BASE 5
+ #define CO_DIG_CREDIT   1
+ #define CO_UP_CREDIT    1
+ #define CO_LOW_CREDIT   1
+ #define CO_OTH_CREDIT   1
++#define CO_MIN_WORD_LENGTH 4
+ 
+ static int
+ _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
+@@ -139,9 +142,7 @@ _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
+ 	     if (!ep || (opt->diff_ok < 0))
+ 		 opt->diff_ok = CO_DIFF_OK;
+ 	 } else if (!strncmp(*argv,"difignore=",10)) {
+-	     opt->diff_ignore = strtol(*argv+10,&ep,10);
+-	     if (!ep || (opt->diff_ignore < 0))
+-		 opt->diff_ignore = CO_DIFF_IGNORE;
++		/* just ignore */
+ 	 } else if (!strncmp(*argv,"minlen=",7)) {
+ 	     opt->min_length = strtol(*argv+7,&ep,10);
+ 	     if (!ep || (opt->min_length < CO_MIN_LENGTH_BASE))
+@@ -172,8 +173,14 @@ _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
+              opt->max_repeat = strtol(*argv+10,&ep,10);
+              if (!ep)
+                  opt->max_repeat = 0;
++         } else if (!strncmp(*argv,"maxclassrepeat=",15)) {
++             opt->max_class_repeat = strtol(*argv+15,&ep,10);
++             if (!ep)
++                 opt->max_class_repeat = 0;
+ 	 } else if (!strncmp(*argv,"reject_username",15)) {
+ 		 opt->reject_user = 1;
++	 } else if (!strncmp(*argv,"gecoscheck",10)) {
++		 opt->gecos_check = 1;
+ 	 } else if (!strncmp(*argv,"authtok_type",12)) {
+ 	   /* for pam_get_authtok, ignore */;
+ 	 } else if (!strncmp(*argv,"use_authtok",11)) {
+@@ -357,16 +364,45 @@ static int simple(struct cracklib_options *opt, const char *new)
+     int	others = 0;
+     int	size;
+     int	i;
++    enum { NONE, DIGIT, UCASE, LCASE, OTHER } prevclass = NONE;
++    int sameclass = 0;
+ 
+     for (i = 0;new[i];i++) {
+-	if (isdigit (new[i]))
++	if (isdigit (new[i])) {
+ 	    digits++;
+-	else if (isupper (new[i]))
++            if (prevclass != DIGIT) {
++                prevclass = DIGIT;
++                sameclass = 1;
++            } else
++                sameclass++;
++        }
++	else if (isupper (new[i])) {
+ 	    uppers++;
+-	else if (islower (new[i]))
++            if (prevclass != UCASE) {
++                prevclass = UCASE;
++                sameclass = 1;
++            } else
++                sameclass++;
++        }
++	else if (islower (new[i])) {
+ 	    lowers++;
+-	else
++            if (prevclass != LCASE) {
++                prevclass = LCASE;
++                sameclass = 1;
++            } else
++                sameclass++;
++        }
++	else {
+ 	    others++;
++            if (prevclass != OTHER) {
++                prevclass = OTHER;
++                sameclass = 1;
++            } else
++                sameclass++;
++        }
++        if (opt->max_class_repeat > 1 && sameclass > opt->max_class_repeat) {
++                return 1;
++        }
+     }
+ 
+     /*
+@@ -439,21 +475,17 @@ static int consecutive(struct cracklib_options *opt, const char *new)
+     return 0;
+ }
+ 
+-static int usercheck(struct cracklib_options *opt, const char *new,
+-		     char *user)
++static int wordcheck(const char *new, char *word)
+ {
+     char *f, *b;
+ 
+-    if (!opt->reject_user)
+-	return 0;
+-
+-    if (strstr(new, user) != NULL)
++    if (strstr(new, word) != NULL)
+ 	return 1;
+ 
+-    /* now reverse the username, we can do that in place
++    /* now reverse the word, we can do that in place
+        as it is strdup-ed */
+-    f = user;
+-    b = user+strlen(user)-1;
++    f = word;
++    b = word+strlen(word)-1;
+     while (f < b) {
+ 	char c;
+ 
+@@ -464,11 +496,20 @@ static int usercheck(struct cracklib_options *opt, const char *new,
+ 	++f;
+     }
+ 
+-    if (strstr(new, user) != NULL)
++    if (strstr(new, word) != NULL)
+ 	return 1;
+     return 0;
+ }
+ 
++static int usercheck(struct cracklib_options *opt, const char *new,
++		     char *user)
++{
++    if (!opt->reject_user)
++        return 0;
++
++    return wordcheck(new, user);
++}
++
+ static char * str_lower(char *string)
+ {
+ 	char *cp;
+@@ -481,7 +522,50 @@ static char * str_lower(char *string)
+ 	return string;
+ }
+ 
+-static const char *password_check(struct cracklib_options *opt,
++static int gecoscheck(pam_handle_t *pamh, struct cracklib_options *opt, const char *new,
++		     const char *user)
++{
++    struct passwd *pwd;
++    char *list;
++    char *p;
++    char *next;
++
++    if (!opt->gecos_check)
++        return 0;
++
++    if ((pwd = pam_modutil_getpwnam(pamh, user)) == NULL) {
++        return 0;
++    }
++
++    list = strdup(pwd->pw_gecos);
++
++    if (list == NULL || *list == '\0') {
++        free(list);
++        return 0;
++    }
++
++    for (p = list;;p = next + 1) {
++         next = strchr(p, ' ');
++         if (next)
++             *next = '\0';
++
++         if (strlen(p) >= CO_MIN_WORD_LENGTH) {
++             str_lower(p);
++             if (wordcheck(new, p)) {
++                 free(list);
++                 return 1;
++             }
++         }
++
++         if (!next)
++             break;
++    }
++
++    free(list);
++    return 0;
++}
++
++static const char *password_check(pam_handle_t *pamh, struct cracklib_options *opt,
+ 				  const char *old, const char *new,
+ 				  const char *user)
+ {
+@@ -535,7 +619,7 @@ static const char *password_check(struct cracklib_options *opt,
+ 	if (!msg && consecutive(opt, new))
+ 	        msg = _("contains too many same characters consecutively");
+ 
+-	if (!msg && usercheck(opt, newmono, usermono))
++	if (!msg && (usercheck(opt, newmono, usermono) || gecoscheck(pamh, opt, newmono, user)))
+ 	        msg = _("contains the user name in some form");
+ 
+ 	free(usermono);
+@@ -584,7 +668,7 @@ static int _pam_unix_approve_pass(pam_handle_t *pamh,
+      * if one wanted to hardwire authentication token strength
+      * checking this would be the place
+      */
+-    msg = password_check(opt, pass_old, pass_new, user);
++    msg = password_check(pamh, opt, pass_old, pass_new, user);
+ 
+     if (msg) {
+         if (ctrl & PAM_DEBUG_ARG)
+@@ -611,7 +695,6 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+     memset(&options, 0, sizeof(options));
+     options.retry_times = CO_RETRY_TIMES;
+     options.diff_ok = CO_DIFF_OK;
+-    options.diff_ignore = CO_DIFF_IGNORE;
+     options.min_length = CO_MIN_LENGTH;
+     options.dig_credit = CO_DIG_CREDIT;
+     options.up_credit = CO_UP_CREDIT;
+-- 
+1.7.7.6
+
diff --git a/pam-1.1.5-lastlog-inactive.patch b/pam-1.1.5-lastlog-inactive.patch
new file mode 100644
index 0000000..da44070
--- /dev/null
+++ b/pam-1.1.5-lastlog-inactive.patch
@@ -0,0 +1,391 @@
+diff -up Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.8.xml.inactive Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.8.xml
+--- Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.8.xml.inactive	2011-06-21 11:04:56.000000000 +0200
++++ Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.8.xml	2012-05-09 11:35:42.810209582 +0200
+@@ -12,7 +12,7 @@
+ 
+   <refnamediv id="pam_lastlog-name">
+     <refname>pam_lastlog</refname>
+-    <refpurpose>PAM module to display date of last login</refpurpose>
++    <refpurpose>PAM module to display date of last login and perform inactive account lock out</refpurpose>
+   </refnamediv>
+ 
+   <refsynopsisdiv>
+@@ -45,6 +45,9 @@
+       <arg choice="opt">
+         showfailed
+       </arg>
++      <arg choice="opt">
++        inactive=&lt;days&gt;
++      </arg>
+     </cmdsynopsis>
+   </refsynopsisdiv>
+ 
+@@ -61,6 +64,12 @@
+       Some applications may perform this function themselves. In such
+       cases, this module is not necessary.
+     </para>
++    <para>
++      If the module is called in the auth or account phase, the accounts that
++      were not used recently enough will be disallowed to log in. The
++      check is not performed for the root account so the root is never
++      locked out.
++    </para>
+   </refsect1>
+ 
+   <refsect1 id="pam_lastlog-options">
+@@ -165,13 +174,30 @@
+           </para>
+         </listitem>
+       </varlistentry>
++      <varlistentry>
++        <term>
++          <option>inactive=&lt;days&gt;</option>
++        </term>
++        <listitem>
++          <para>
++            This option is specific for the auth or account phase. It
++            specifies the number of days after the last login of the user
++            when the user will be locked out by the module. The default
++            value is 90.
++          </para>
++        </listitem>
++      </varlistentry>
+     </variablelist>
+   </refsect1>
+ 
+   <refsect1 id="pam_lastlog-types">
+     <title>MODULE TYPES PROVIDED</title>
+     <para>
+-      Only the <option>session</option> module type is provided.
++      The <option>auth</option> and <option>account</option> module type
++      allows to lock out users which did not login recently enough.
++      The <option>session</option> module type is provided for displaying
++      the information about the last login and/or updating the lastlog and
++      wtmp files.
+     </para>
+   </refsect1>
+ 
+@@ -207,6 +233,27 @@
+           </listitem>
+         </varlistentry>
+ 
++        <varlistentry>
++          <term>PAM_AUTH_ERR</term>
++          <listitem>
++            <para>
++	      User locked out in the auth or account phase due to
++	      inactivity.
++            </para>
++          </listitem>
++        </varlistentry>
++
++        <varlistentry>
++          <term>PAM_IGNORE</term>
++          <listitem>
++            <para>
++	      There was an error during reading the lastlog file
++	      in the auth or account phase and thus inactivity
++	      of the user cannot be determined.
++            </para>
++          </listitem>
++        </varlistentry>
++
+       </variablelist>
+     </para>
+   </refsect1>
+@@ -220,6 +267,13 @@
+       <programlisting>
+     session  required  pam_lastlog.so nowtmp
+       </programlisting>
++    <para>
++     To reject the user if he did not login during the previous 50 days
++     the following line can be used:
++    </para>
++      <programlisting>
++    auth  required  pam_lastlog.so inactive=50
++      </programlisting>
+   </refsect1>
+ 
+   <refsect1 id="pam_lastlog-files">
+@@ -254,6 +308,9 @@
+       <para>
+         pam_lastlog was written by Andrew G. Morgan &lt;morgan at kernel.org&gt;.
+       </para>
++      <para>
++        Inactive account lock out added by Tomáš Mráz &lt;tm at t8m.info&gt;.
++      </para>
+   </refsect1>
+ 
+ </refentry>
+diff -up Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.c.inactive Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.c
+--- Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.c.inactive	2011-06-21 11:04:56.000000000 +0200
++++ Linux-PAM-1.1.5/modules/pam_lastlog/pam_lastlog.c	2012-05-09 11:35:22.363759805 +0200
+@@ -56,6 +56,9 @@ struct lastlog {
+ #define DEFAULT_HOST     ""  /* "[no.where]" */
+ #define DEFAULT_TERM     ""  /* "tt???" */
+ 
++#define DEFAULT_INACTIVE_DAYS 90
++#define MAX_INACTIVE_DAYS 100000
++
+ /*
+  * here, we make a definition for the externally accessible function
+  * in this file (this definition is required for static a module
+@@ -64,6 +67,8 @@ struct lastlog {
+  */
+ 
+ #define PAM_SM_SESSION
++#define PAM_SM_AUTH
++#define PAM_SM_ACCOUNT
+ 
+ #include <security/pam_modules.h>
+ #include <security/_pam_macros.h>
+@@ -83,7 +88,45 @@ struct lastlog {
+ #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)
++_pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv,
++    time_t *inactive)
++{
++    int ctrl = 0;
++
++    *inactive = DEFAULT_INACTIVE_DAYS;
++
++    /* does the appliction require quiet? */
++    if (flags & PAM_SILENT) {
++	ctrl |= LASTLOG_QUIET;
++    }
++
++    /* step through arguments */
++    for (; argc-- > 0; ++argv) {
++        char *ep = NULL;
++        long l;
++
++	if (!strcmp(*argv,"debug")) {
++	    ctrl |= LASTLOG_DEBUG;
++	} else if (!strcmp(*argv,"silent")) {
++	    ctrl |= LASTLOG_QUIET;
++	} else if (!strncmp(*argv,"inactive=", 9)) {
++            l = strtol(*argv+9, &ep, 10);
++            if (ep != *argv+9 && l > 0 && l < MAX_INACTIVE_DAYS)
++                *inactive = l;
++            else {
++                pam_syslog(pamh, LOG_ERR, "bad option value: %s", *argv);
++            }
++	} else {
++	    pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
++	}
++    }
++
++    D(("ctrl = %o", ctrl));
++    return ctrl;
++}
++
++static int
++_pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
+ {
+     int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
+ 
+@@ -145,6 +188,44 @@ get_tty(pam_handle_t *pamh)
+ }
+ 
+ static int
++last_login_open(pam_handle_t *pamh, int announce, uid_t uid)
++{
++    int last_fd;
++
++    /* obtain the last login date and all the relevant info */
++    last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
++    if (last_fd < 0) {
++        if (errno == ENOENT && (announce & LASTLOG_UPDATE)) {
++	     last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
++                            S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
++             if (last_fd < 0) {
++	          pam_syslog(pamh, LOG_ERR,
++                             "unable to create %s: %m", _PATH_LASTLOG);
++		  D(("unable to create %s file", _PATH_LASTLOG));
++		  return -1;
++	     }
++	     pam_syslog(pamh, LOG_WARNING,
++			"file %s created", _PATH_LASTLOG);
++	     D(("file %s created", _PATH_LASTLOG));
++	} else {
++	  pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
++	  D(("unable to open %s file", _PATH_LASTLOG));
++	  return -1;
++	}
++    }
++
++    if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) {
++	pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
++	D(("unable to lseek %s file", _PATH_LASTLOG));
++        close(last_fd);
++	return -1;
++    }
++
++    return last_fd;
++}
++
++
++static int
+ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
+ {
+     struct flock last_lock;
+@@ -338,31 +419,9 @@ last_login_date(pam_handle_t *pamh, int
+     int last_fd;
+ 
+     /* obtain the last login date and all the relevant info */
+-    last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
++    last_fd = last_login_open(pamh, announce, uid);
+     if (last_fd < 0) {
+-        if (errno == ENOENT) {
+-	     last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
+-                            S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+-             if (last_fd < 0) {
+-	          pam_syslog(pamh, LOG_ERR,
+-                             "unable to create %s: %m", _PATH_LASTLOG);
+-		  D(("unable to create %s file", _PATH_LASTLOG));
+-		  return PAM_SERVICE_ERR;
+-	     }
+-	     pam_syslog(pamh, LOG_WARNING,
+-			"file %s created", _PATH_LASTLOG);
+-	     D(("file %s created", _PATH_LASTLOG));
+-	} else {
+-	  pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
+-	  D(("unable to open %s file", _PATH_LASTLOG));
+-	  return PAM_SERVICE_ERR;
+-	}
+-    }
+-
+-    if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) {
+-	pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
+-	D(("unable to lseek %s file", _PATH_LASTLOG));
+-	return PAM_SERVICE_ERR;
++        return PAM_SERVICE_ERR;
+     }
+ 
+     retval = last_login_read(pamh, announce, last_fd, uid, lltime);
+@@ -502,7 +561,91 @@ cleanup:
+     return retval;
+ }
+ 
+-/* --- authentication management functions (only) --- */
++/* --- authentication (locking out inactive users) functions --- */
++PAM_EXTERN int
++pam_sm_authenticate(pam_handle_t *pamh, int flags,
++		    int argc, const char **argv)
++{
++    int retval, ctrl;
++    const char *user = NULL;
++    const struct passwd *pwd;
++    uid_t uid;
++    time_t lltime = 0;
++    time_t inactive_days = 0;
++    int last_fd;
++
++    /*
++     * Lock out the user if he did not login recently enough.
++     */
++
++    ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days);
++
++    /* which user? */
++
++    if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
++        || *user == '\0') {
++        pam_syslog(pamh, LOG_ERR, "cannot determine the user's name");
++        return PAM_USER_UNKNOWN;
++    }
++
++    /* what uid? */
++
++    pwd = pam_modutil_getpwnam (pamh, user);
++    if (pwd == NULL) {
++        pam_syslog(pamh, LOG_ERR, "user unknown");
++	return PAM_USER_UNKNOWN;
++    }
++    uid = pwd->pw_uid;
++    pwd = NULL;                                         /* tidy up */
++
++    if (uid == 0)
++	return PAM_SUCCESS;
++
++    /* obtain the last login date and all the relevant info */
++    last_fd = last_login_open(pamh, ctrl, uid);
++    if (last_fd < 0) {
++	return PAM_IGNORE;
++    }
++
++    retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime);
++    close(last_fd);
++
++    if (retval != PAM_SUCCESS) {
++	D(("error while reading lastlog file"));
++	return PAM_IGNORE;
++    }
++
++    if (lltime == 0) { /* user never logged in before */
++        if (ctrl & LASTLOG_DEBUG)
++            pam_syslog(pamh, LOG_DEBUG, "user never logged in - pass");
++        return PAM_SUCCESS;
++    }
++
++    lltime = (time(NULL) - lltime) / (24*60*60);
++
++    if (lltime > inactive_days) {
++        pam_syslog(pamh, LOG_INFO, "user %s inactive for %d days - denied", user, lltime);
++        return PAM_AUTH_ERR;
++    }
++
++    return PAM_SUCCESS;
++}
++
++PAM_EXTERN int
++pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
++		    int argc UNUSED, const char **argv UNUSED)
++{
++    return PAM_SUCCESS;
++}
++
++PAM_EXTERN int
++pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
++		    int argc, const char **argv)
++{
++    return pam_sm_authenticate(pamh, flags, argc, argv);
++}
++
++/* --- session management functions --- */
+ 
+ PAM_EXTERN int
+ pam_sm_open_session(pam_handle_t *pamh, int flags,
+@@ -519,7 +662,7 @@ pam_sm_open_session(pam_handle_t *pamh,
+      * last login info and then updates the lastlog for that user.
+      */
+ 
+-    ctrl = _pam_parse(pamh, flags, argc, argv);
++    ctrl = _pam_session_parse(pamh, flags, argc, argv);
+ 
+     /* which user? */
+ 
+@@ -560,7 +703,7 @@ pam_sm_close_session (pam_handle_t *pamh
+ {
+     const char *terminal_line;
+ 
+-    if (!(_pam_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
++    if (!(_pam_session_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
+ 	return PAM_SUCCESS;
+ 
+     terminal_line = get_tty(pamh);
+@@ -577,9 +720,9 @@ pam_sm_close_session (pam_handle_t *pamh
+ 
+ struct pam_module _pam_lastlog_modstruct = {
+      "pam_lastlog",
+-     NULL,
+-     NULL,
+-     NULL,
++     pam_sm_authenticate,
++     pam_sm_setcred,
++     pam_sm_acct_mgmt,
+      pam_sm_open_session,
+      pam_sm_close_session,
+      NULL,
diff --git a/pam-1.1.5-unix-crypt.patch b/pam-1.1.5-unix-crypt.patch
new file mode 100644
index 0000000..a10ba89
--- /dev/null
+++ b/pam-1.1.5-unix-crypt.patch
@@ -0,0 +1,53 @@
+From 1329c68b19daa6d5793dd672db73ebe85465eea9 Mon Sep 17 00:00:00 2001
+From: Paul Wouters <pwouters at redhat.com>
+Date: Wed, 11 Apr 2012 21:13:14 +0200
+Subject: [PATCH] Check for crypt() failure returning NULL.
+
+* modules/pam_unix/pam_unix_passwd.c (pam_sm_chauthtok): Adjust syslog message.
+* modules/pam_unix/passverify.c (create_password_hash): Check for crypt()
+returning NULL.
+---
+ modules/pam_unix/pam_unix_passwd.c |    2 +-
+ modules/pam_unix/passverify.c      |    6 ++++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
+index e9059d3..9e1302d 100644
+--- a/modules/pam_unix/pam_unix_passwd.c
++++ b/modules/pam_unix/pam_unix_passwd.c
+@@ -800,7 +800,7 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
+ 		tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
+ 		if (tpass == NULL) {
+ 			pam_syslog(pamh, LOG_CRIT,
+-				"out of memory for password");
++				"crypt() failure or out of memory for password");
+ 			pass_new = pass_old = NULL;	/* tidy up */
+ 			unlock_pwdf();
+ 			return PAM_BUF_ERR;
+diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
+index 5289955..4840bb2 100644
+--- a/modules/pam_unix/passverify.c
++++ b/modules/pam_unix/passverify.c
+@@ -424,7 +424,7 @@ PAMH_ARG_DECL(char * create_password_hash,
+ 	}
+ #endif
+ 	sp = crypt(password, salt);
+-	if (strncmp(algoid, sp, strlen(algoid)) != 0) {
++	if (!sp || strncmp(algoid, sp, strlen(algoid)) != 0) {
+ 		/* libxcrypt/libc doesn't know the algorithm, use MD5 */
+ 		pam_syslog(pamh, LOG_ERR,
+ 			   "Algo %s not supported by the crypto backend, "
+@@ -432,7 +432,9 @@ PAMH_ARG_DECL(char * create_password_hash,
+ 			   on(UNIX_BLOWFISH_PASS, ctrl) ? "blowfish" :
+ 			   on(UNIX_SHA256_PASS, ctrl) ? "sha256" :
+ 			   on(UNIX_SHA512_PASS, ctrl) ? "sha512" : algoid);
+-		memset(sp, '\0', strlen(sp));
++		if(sp) {
++		   memset(sp, '\0', strlen(sp));
++		}
+ 		return crypt_md5_wrapper(password);
+ 	}
+ 
+-- 
+1.7.7.6
+
diff --git a/pam-1.1.5-unix-no-fallback.patch b/pam-1.1.5-unix-no-fallback.patch
new file mode 100644
index 0000000..7857196
--- /dev/null
+++ b/pam-1.1.5-unix-no-fallback.patch
@@ -0,0 +1,69 @@
+diff -up Linux-PAM-1.1.5/modules/pam_unix/pam_unix.8.xml.no-fallback Linux-PAM-1.1.5/modules/pam_unix/pam_unix.8.xml
+--- Linux-PAM-1.1.5/modules/pam_unix/pam_unix.8.xml.no-fallback	2011-06-21 11:04:56.000000000 +0200
++++ Linux-PAM-1.1.5/modules/pam_unix/pam_unix.8.xml	2012-05-09 11:54:34.442036404 +0200
+@@ -265,11 +265,10 @@
+         <listitem>
+           <para>
+             When a user changes their password next,
+-            encrypt it with the SHA256 algorithm. If the
+-            SHA256 algorithm is not known to the <citerefentry>
++            encrypt it with the SHA256 algorithm. The
++            SHA256 algorithm must be supported by the <citerefentry>
+ 	    <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum>
+-            </citerefentry> function,
+-            fall back to MD5.
++            </citerefentry> function.
+           </para>
+         </listitem>
+       </varlistentry>
+@@ -280,11 +279,10 @@
+         <listitem>
+           <para>
+             When a user changes their password next,
+-            encrypt it with the SHA512 algorithm. If the
+-            SHA512 algorithm is not known to the <citerefentry>
++            encrypt it with the SHA512 algorithm. The
++            SHA512 algorithm must be supported by the <citerefentry>
+ 	    <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum>
+-            </citerefentry> function,
+-            fall back to MD5.
++            </citerefentry> function.
+           </para>
+         </listitem>
+       </varlistentry>
+@@ -295,11 +293,10 @@
+         <listitem>
+           <para>
+             When a user changes their password next,
+-            encrypt it with the blowfish algorithm. If the
+-            blowfish algorithm is not known to the <citerefentry>
++            encrypt it with the blowfish algorithm. The
++            blowfish algorithm must be supported by the <citerefentry>
+ 	    <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum>
+-            </citerefentry> function,
+-            fall back to MD5.
++            </citerefentry> function.
+           </para>
+         </listitem>
+       </varlistentry>
+diff -up Linux-PAM-1.1.5/modules/pam_unix/passverify.c.no-fallback Linux-PAM-1.1.5/modules/pam_unix/passverify.c
+--- Linux-PAM-1.1.5/modules/pam_unix/passverify.c.no-fallback	2012-05-09 11:48:12.409632377 +0200
++++ Linux-PAM-1.1.5/modules/pam_unix/passverify.c	2012-05-09 11:48:36.953172291 +0200
+@@ -427,15 +427,14 @@ PAMH_ARG_DECL(char * create_password_has
+ 	if (!sp || strncmp(algoid, sp, strlen(algoid)) != 0) {
+ 		/* libxcrypt/libc doesn't know the algorithm, use MD5 */
+ 		pam_syslog(pamh, LOG_ERR,
+-			   "Algo %s not supported by the crypto backend, "
+-			   "falling back to MD5\n",
++			   "Algo %s not supported by the crypto backend.\n",
+ 			   on(UNIX_BLOWFISH_PASS, ctrl) ? "blowfish" :
+ 			   on(UNIX_SHA256_PASS, ctrl) ? "sha256" :
+ 			   on(UNIX_SHA512_PASS, ctrl) ? "sha512" : algoid);
+ 		if(sp) {
+ 		   memset(sp, '\0', strlen(sp));
+ 		}
+-		return crypt_md5_wrapper(password);
++		return NULL;
+ 	}
+ 
+ 	return x_strdup(sp);
diff --git a/pam-1.1.5-unix-remember.patch b/pam-1.1.5-unix-remember.patch
new file mode 100644
index 0000000..de34428
--- /dev/null
+++ b/pam-1.1.5-unix-remember.patch
@@ -0,0 +1,59 @@
+From 0baf28fa03dfa46482e13390fd9a7545c30ccd7f Mon Sep 17 00:00:00 2001
+From: Tomas Mraz <tmraz at fedoraproject.org>
+Date: Tue, 3 Jan 2012 12:30:43 +0100
+Subject: [PATCH] Fix matching of usernames in the pam_unix remember feature.
+
+* modules/pam_unix/pam_unix_passwd.c (check_old_password): Make
+sure we match only the whole username in opasswd entry.
+* modules/pam_unix/passverify.c (save_old_password): Likewise make
+sure we match only the whole username in opasswd entry.
+---
+ modules/pam_unix/pam_unix_passwd.c |    4 +++-
+ modules/pam_unix/passverify.c      |    3 ++-
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
+index 6ba2c2e..498a81c 100644
+--- a/modules/pam_unix/pam_unix_passwd.c
++++ b/modules/pam_unix/pam_unix_passwd.c
+@@ -280,13 +280,15 @@ static int check_old_password(const char *forwho, const char *newpass)
+ 	char *s_luser, *s_uid, *s_npas, *s_pas;
+ 	int retval = PAM_SUCCESS;
+ 	FILE *opwfile;
++	size_t len = strlen(forwho);
+ 
+ 	opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ 	if (opwfile == NULL)
+ 		return PAM_ABORT;
+ 
+ 	while (fgets(buf, 16380, opwfile)) {
+-		if (!strncmp(buf, forwho, strlen(forwho))) {
++		if (!strncmp(buf, forwho, len) && (buf[len] == ':' ||
++			buf[len] == ',')) {
+ 			char *sptr;
+ 			buf[strlen(buf) - 1] = '\0';
+ 			s_luser = strtok_r(buf, ":,", &sptr);
+diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
+index 089f4b8..5289955 100644
+--- a/modules/pam_unix/passverify.c
++++ b/modules/pam_unix/passverify.c
+@@ -562,6 +562,7 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
+     int found = 0;
+     struct passwd *pwd = NULL;
+     struct stat st;
++    size_t len = strlen(forwho);
+ #ifdef WITH_SELINUX
+     security_context_t prev_context=NULL;
+ #endif
+@@ -629,7 +630,7 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
+     }
+ 
+     while (fgets(buf, 16380, opwfile)) {
+-	if (!strncmp(buf, forwho, strlen(forwho))) {
++	if (!strncmp(buf, forwho, len) && strchr(":,\n", buf[len]) != NULL) {
+ 	    char *sptr = NULL;
+ 	    found = 1;
+ 	    if (howmany == 0)
+-- 
+1.7.7.6
+
diff --git a/pam.spec b/pam.spec
index 3826403..24f7934 100644
--- a/pam.spec
+++ b/pam.spec
@@ -3,7 +3,7 @@
 Summary: An extensible library which provides authentication for applications
 Name: pam
 Version: 1.1.5
-Release: 6%{?dist}
+Release: 7%{?dist}
 # The library is BSD licensed with option to relicense as GPLv2+
 # - this option is redundant as the BSD license allows that anyway.
 # pam_timestamp, pam_loginuid, and pam_console modules are GPLv2+.
@@ -42,6 +42,16 @@ Patch13: pam-1.1.5-limits-user.patch
 Patch14: pam-1.1.5-namespace-rslave.patch
 # Committed to upstream git
 Patch15: pam-1.1.5-namespace-no-unmount.patch
+# Committed to upstream git
+Patch16: pam-1.1.5-lastlog-inactive.patch
+# Committed to upstream git
+Patch17: pam-1.1.5-cracklib-gecoscheck.patch
+# Committed to upstream git
+Patch18: pam-1.1.5-unix-remember.patch
+# Committed to upstream git
+Patch19: pam-1.1.5-unix-crypt.patch
+# FIPS related - non upstreamable
+Patch20: pam-1.1.5-unix-no-fallback.patch
 
 %define _sbindir /sbin
 %define _moduledir /%{_lib}/security
@@ -116,6 +126,11 @@ mv pam-redhat-%{pam_redhat_version}/* modules
 %patch13 -p1 -b .limits
 %patch14 -p1 -b .rslave
 %patch15 -p1 -b .no-unmount
+%patch16 -p1 -b .inactive
+%patch17 -p1 -b .gecoscheck
+%patch18 -p1 -b .remember
+%patch19 -p1 -b .crypt
+%patch20 -p1 -b .no-fallback
 
 libtoolize -f
 autoreconf
@@ -370,6 +385,14 @@ fi
 %doc doc/adg/*.txt doc/adg/html
 
 %changelog
+* Mon May  9 2012 Tomas Mraz <tmraz at redhat.com> 1.1.5-7
+- add inactive account lock out functionality to pam_lastlog
+- fix pam_unix remember user name matching
+- add gecoscheck and maxclassrepeat functionality to pam_cracklib
+- correctly check for crypt() returning NULL in pam_unix
+- pam_unix - do not fallback to MD5 on password change
+  if requested algorithm not supported by crypt() (#818741)
+
 * Mon May  9 2012 Tomas Mraz <tmraz at redhat.com> 1.1.5-6
 - add pam_systemd to session modules
 


More information about the scm-commits mailing list