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)
This commit is contained in:
Tomas Mraz 2012-05-09 11:58:27 +02:00
parent 882ad81ab3
commit 7f16b85d54
6 changed files with 969 additions and 1 deletions

View File

@ -0,0 +1,373 @@
From 422c19520fb814cfd8edd84d7989f4c52acbfa03 Mon Sep 17 00:00:00 2001
From: Tomas Mraz <tmraz@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

View File

@ -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@kernel.org&gt;.
</para>
+ <para>
+ Inactive account lock out added by Tomáš Mráz &lt;tm@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,

View File

@ -0,0 +1,53 @@
From 1329c68b19daa6d5793dd672db73ebe85465eea9 Mon Sep 17 00:00:00 2001
From: Paul Wouters <pwouters@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

View File

@ -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);

View File

@ -0,0 +1,59 @@
From 0baf28fa03dfa46482e13390fd9a7545c30ccd7f Mon Sep 17 00:00:00 2001
From: Tomas Mraz <tmraz@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

View File

@ -3,7 +3,7 @@
Summary: An extensible library which provides authentication for applications Summary: An extensible library which provides authentication for applications
Name: pam Name: pam
Version: 1.1.5 Version: 1.1.5
Release: 6%{?dist} Release: 7%{?dist}
# The library is BSD licensed with option to relicense as GPLv2+ # The library is BSD licensed with option to relicense as GPLv2+
# - this option is redundant as the BSD license allows that anyway. # - this option is redundant as the BSD license allows that anyway.
# pam_timestamp, pam_loginuid, and pam_console modules are GPLv2+. # 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 Patch14: pam-1.1.5-namespace-rslave.patch
# Committed to upstream git # Committed to upstream git
Patch15: pam-1.1.5-namespace-no-unmount.patch 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 _sbindir /sbin
%define _moduledir /%{_lib}/security %define _moduledir /%{_lib}/security
@ -116,6 +126,11 @@ mv pam-redhat-%{pam_redhat_version}/* modules
%patch13 -p1 -b .limits %patch13 -p1 -b .limits
%patch14 -p1 -b .rslave %patch14 -p1 -b .rslave
%patch15 -p1 -b .no-unmount %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 libtoolize -f
autoreconf autoreconf
@ -370,6 +385,14 @@ fi
%doc doc/adg/*.txt doc/adg/html %doc doc/adg/*.txt doc/adg/html
%changelog %changelog
* Mon May 9 2012 Tomas Mraz <tmraz@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@redhat.com> 1.1.5-6 * Mon May 9 2012 Tomas Mraz <tmraz@redhat.com> 1.1.5-6
- add pam_systemd to session modules - add pam_systemd to session modules