diff -up Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.8.xml.pwquality Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.8.xml --- Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.8.xml.pwquality 2007-11-06 15:58:54.000000000 +0100 +++ Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.8.xml 2008-09-23 15:06:40.000000000 +0200 @@ -59,7 +59,7 @@ Palindrome - Is the new password a palindrome of the old one? + Is the new password a palindrome? @@ -120,6 +120,23 @@ + + Same consecutive characters + + + Optional check for same consecutive characters. + + + + + Contains user name + + + Optional check whether the password contains the user's name + in some form. + + + This module with no arguments will work well for standard unix @@ -281,7 +298,7 @@ than 10. - (N > 0) This is the minimum number of upper + (N < 0) This is the minimum number of upper case letters that must be met for a new password. @@ -349,6 +366,50 @@ + + + + + The minimum number of required classes of characters for + the new password. The default number is zero. The four + classes are digits, upper and lower letters and other + characters. + The difference to the check is + that a specific class if of characters is not required. + Instead N out of four of the + classes are required. + + + + + + + + + + + Reject passwords which contain more than N same consecutive + characters. The default is 0 which means that this check + is disabled. + + + + + + + + + + + Check whether the name of the user in straight or reversed + form is contained in the new password. If it is found the + new password is rejected. + + + + + + @@ -495,7 +556,7 @@ password required pam_unix.so use_autht pam.conf5 , - pam.d8 + pam.d5 , pam8 diff -up Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.c.pwquality Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.c --- Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.c.pwquality 2008-09-23 15:06:40.000000000 +0200 +++ Linux-PAM-1.0.2/modules/pam_cracklib/pam_cracklib.c 2008-09-23 15:10:14.000000000 +0200 @@ -99,6 +99,8 @@ struct cracklib_options { int min_class; int use_authtok; int try_first_pass; + int max_repeat; + int reject_user; char prompt_type[BUFSIZ]; const char *cracklib_dictpath; }; @@ -166,8 +168,14 @@ _pam_parse (pam_handle_t *pamh, struct c opt->min_class = strtol(*argv+9,&ep,10); if (!ep) opt->min_class = 0; - if (opt->min_class > 4) - opt->min_class = 4 ; + if (opt->min_class > 4) + opt->min_class = 4; + } else if (!strncmp(*argv,"maxrepeat=",10)) { + opt->max_repeat = strtol(*argv+10,&ep,10); + if (!ep) + opt->max_repeat = 0; + } else if (!strncmp(*argv,"reject_username",15)) { + opt->reject_user = 1; } else if (!strncmp(*argv,"use_authtok",11)) { opt->use_authtok = 1; } else if (!strncmp(*argv,"use_first_pass",14)) { @@ -418,6 +426,58 @@ static int simple(struct cracklib_option return 1; } +static int consecutive(struct cracklib_options *opt, const char *new) +{ + char c; + int i; + int same; + + if (opt->max_repeat == 0) + return 0; + + for (i = 0; new[i]; i++) { + if (i > 0 && new[i] == c) { + ++same; + if (same > opt->max_repeat) + return 1; + } else { + c = new[i]; + same = 1; + } + } + return 0; +} + +static int usercheck(struct cracklib_options *opt, const char *new, + char *user) +{ + char *f, *b; + + if (!opt->reject_user) + return 0; + + if (strstr(new, user) != NULL) + return 1; + + /* now reverse the username, we can do that in place + as it is strdup-ed */ + f = user; + b = user+strlen(user)-1; + while (f < b) { + char c; + + c = *f; + *f = *b; + *b = c; + --b; + ++f; + } + + if (strstr(new, user) != NULL) + return 1; + return 0; +} + static char * str_lower(char *string) { char *cp; @@ -428,10 +488,12 @@ static char * str_lower(char *string) } static const char *password_check(struct cracklib_options *opt, - const char *old, const char *new) + const char *old, const char *new, + const char *user) { const char *msg = NULL; char *oldmono = NULL, *newmono, *wrapped = NULL; + char *usermono = NULL; if (old && strcmp(new, old) == 0) { msg = _("is the same as the old one"); @@ -439,6 +501,7 @@ static const char *password_check(struct } newmono = str_lower(x_strdup(new)); + usermono = str_lower(x_strdup(user)); if (old) { oldmono = str_lower(x_strdup(old)); wrapped = malloc(strlen(oldmono) * 2 + 1); @@ -464,8 +527,15 @@ static const char *password_check(struct if (!msg && minclass (opt, new)) msg = _("not enough character classes"); + if (!msg && consecutive(opt, new)) + msg = _("contains too many same characters consecutively"); + + if (!msg && usercheck(opt, newmono, usermono)) + msg = _("contains the user name in some form"); + memset(newmono, 0, strlen(newmono)); free(newmono); + free(usermono); if (old) { memset(oldmono, 0, strlen(oldmono)); memset(wrapped, 0, strlen(wrapped)); @@ -532,18 +602,18 @@ static int _pam_unix_approve_pass(pam_ha return PAM_AUTHTOK_ERR; } + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh,LOG_ERR,"Can not get username"); + return PAM_AUTHTOK_ERR; + } /* * if one wanted to hardwire authentication token strength * checking this would be the place */ - msg = password_check(opt, pass_old, pass_new); + msg = password_check(opt, pass_old, pass_new, user); if (!msg) { - retval = pam_get_item(pamh, PAM_USER, &user); - if (retval != PAM_SUCCESS || user == NULL) { - if (ctrl & PAM_DEBUG_ARG) - pam_syslog(pamh,LOG_ERR,"Can not get username"); - return PAM_AUTHTOK_ERR; - } msg = check_old_password(user, pass_new); }