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