Index: Linux-PAM-1.4.0/modules/pam_cracklib/pam_cracklib.c =================================================================== --- Linux-PAM-1.4.0.orig/modules/pam_cracklib/pam_cracklib.c +++ Linux-PAM-1.4.0/modules/pam_cracklib/pam_cracklib.c @@ -88,6 +88,7 @@ struct cracklib_options { int reject_user; int gecos_check; int enforce_for_root; + int user_substr; const char *cracklib_dictpath; }; @@ -185,6 +186,10 @@ _pam_parse (pam_handle_t *pamh, struct c if (!*(opt->cracklib_dictpath)) { opt->cracklib_dictpath = CRACKLIB_DICTS; } + } else if ((str = pam_str_skip_prefix(*argv, "usersubstr=")) != NULL) { + opt->user_substr = strtol(str, &ep, 10); + if (ep == str) + opt->user_substr = 0; } else { pam_syslog(pamh,LOG_ERR,"pam_parse: unknown option; %s",*argv); } @@ -525,13 +530,54 @@ static int wordcheck(const char *new, ch return 0; } +/* + * RETURNS: True if the password is unacceptable, else false + */ +static int usersubstr(int len, const char *new, char *user) +{ + int i, userlen; + int bad = 0; // Assume it's OK unless proven otherwise + char *subuser = calloc(len+1, sizeof(char)); + + if (subuser == NULL) { + return 1; + } + + userlen = strlen(user); + + if (len >= CO_MIN_WORD_LENGTH && + userlen > len) { + for(i = 0; !bad && (i <= userlen - len); i++) { + strncpy(subuser, user+i, len+1); + subuser[len] = '\0'; + bad = wordcheck(new, subuser); + } + } else { + // if we already tested substrings, there's no need to test + // the whole username; all substrings would've been found :) + if (!bad) + bad = wordcheck(new, user); + } + + free(subuser); + + return bad; +} + +/* + * RETURNS: True if the password is unacceptable, else false + */ static int usercheck(struct cracklib_options *opt, const char *new, char *user) { - if (!opt->reject_user) - return 0; + int bad = 0; + + if (opt->reject_user) + bad = wordcheck(new, user); + if (!bad && opt->user_substr != 0) + bad = usersubstr(opt->user_substr, new, user); - return wordcheck(new, user); + return bad; } static char * str_lower(char *string)