From 7f16b85d54f469c6c504a0f86636bb32b6a4c5d8 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 9 May 2012 11:58:27 +0200 Subject: [PATCH] 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 deletion(-) create mode 100644 pam-1.1.5-cracklib-gecoscheck.patch create mode 100644 pam-1.1.5-lastlog-inactive.patch create mode 100644 pam-1.1.5-unix-crypt.patch create mode 100644 pam-1.1.5-unix-no-fallback.patch create mode 100644 pam-1.1.5-unix-remember.patch 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 +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 @@ + + Is the new password too much like the old one? + This is primarily controlled by one argument, +- 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. +- +- +- To avoid the lockup associated with trying to change a long and +- complicated password, is available. +- This argument can be used to specify the minimum length a new +- password needs to be before the value is +- ignored. The default value for is 23. ++ 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. + + + +@@ -96,7 +89,8 @@ + + + Is the new password too small? +- This is controlled by 5 arguments , ++ This is controlled by 6 arguments , ++ , + , , + , and . See the section + on the arguments for the details of how these work and there defaults. +@@ -204,24 +198,9 @@ + + + This argument will change the default of +- 5 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. +- +- +- +- +- +- +- +- +- +- +- How many characters should the password have before +- difok will be ignored. The default is +- 23. ++ 5 for the number of character ++ changes in the new password that differentiate it ++ from the old password. + + + +@@ -370,6 +349,19 @@ + + + ++ ++ ++ ++ ++ Reject passwords which contain more than N consecutive ++ characters of the same class. The default is 0 which means ++ that this check is disabled. ++ ++ ++ ++ ++ ++ + + + +@@ -383,6 +375,20 @@ + + + ++ ++ ++ ++ ++ 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. ++ ++ ++ ++ ++ ++ + + + +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 + #include + #include ++#include ++#include + + #ifdef HAVE_CRACK_H + #include +@@ -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 @@ + + + pam_lastlog +- PAM module to display date of last login ++ PAM module to display date of last login and perform inactive account lock out + + + +@@ -45,6 +45,9 @@ + + showfailed + ++ ++ inactive=<days> ++ + + + +@@ -61,6 +64,12 @@ + Some applications may perform this function themselves. In such + cases, this module is not necessary. + ++ ++ 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. ++ + + + +@@ -165,13 +174,30 @@ + + + ++ ++ ++ ++ ++ ++ ++ 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. ++ ++ ++ + + + + + MODULE TYPES PROVIDED + +- Only the module type is provided. ++ The and module type ++ allows to lock out users which did not login recently enough. ++ The module type is provided for displaying ++ the information about the last login and/or updating the lastlog and ++ wtmp files. + + + +@@ -207,6 +233,27 @@ + + + ++ ++ PAM_AUTH_ERR ++ ++ ++ User locked out in the auth or account phase due to ++ inactivity. ++ ++ ++ ++ ++ ++ PAM_IGNORE ++ ++ ++ There was an error during reading the lastlog file ++ in the auth or account phase and thus inactivity ++ of the user cannot be determined. ++ ++ ++ ++ + + + +@@ -220,6 +267,13 @@ + + session required pam_lastlog.so nowtmp + ++ ++ To reject the user if he did not login during the previous 50 days ++ the following line can be used: ++ ++ ++ auth required pam_lastlog.so inactive=50 ++ + + + +@@ -254,6 +308,9 @@ + + pam_lastlog was written by Andrew G. Morgan <morgan@kernel.org>. + ++ ++ Inactive account lock out added by Tomáš Mráz <tm@t8m.info>. ++ + + + +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 + #include +@@ -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 +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 @@ + + + When a user changes their password next, +- encrypt it with the SHA256 algorithm. If the +- SHA256 algorithm is not known to the ++ encrypt it with the SHA256 algorithm. The ++ SHA256 algorithm must be supported by the + crypt3 +- function, +- fall back to MD5. ++ function. + + + +@@ -280,11 +279,10 @@ + + + When a user changes their password next, +- encrypt it with the SHA512 algorithm. If the +- SHA512 algorithm is not known to the ++ encrypt it with the SHA512 algorithm. The ++ SHA512 algorithm must be supported by the + crypt3 +- function, +- fall back to MD5. ++ function. + + + +@@ -295,11 +293,10 @@ + + + When a user changes their password next, +- encrypt it with the blowfish algorithm. If the +- blowfish algorithm is not known to the ++ encrypt it with the blowfish algorithm. The ++ blowfish algorithm must be supported by the + crypt3 +- function, +- fall back to MD5. ++ function. + + + +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 +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 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 1.1.5-6 - add pam_systemd to session modules