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; }; @@ -100,6 +101,7 @@ struct cracklib_options { #define CO_LOW_CREDIT 1 #define CO_OTH_CREDIT 1 #define CO_MIN_WORD_LENGTH 4 +#define CO_MIN_WORD_LENGTH 4 static int _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt, @@ -185,6 +187,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 +531,54 @@ static int wordcheck(const char *new, ch return 0; } -static int usercheck(struct cracklib_options *opt, const char *new, +/* + * RETURNS: True if the password is unacceptable, else false + */ +static int usersubstr(pam_handle_t *pamh, 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(pam_handle_t *pamh, 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(pamh, opt->user_substr, new, user); - return wordcheck(new, user); + return bad; } static char * str_lower(char *string) @@ -646,7 +693,7 @@ static const char *password_check(pam_ha if (!msg && sequence(opt, new)) msg = _("contains too long of a monotonic character sequence"); - if (!msg && (usercheck(opt, newmono, usermono) || gecoscheck(pamh, opt, newmono, user))) + if (!msg && (usercheck(pamh, opt, newmono, usermono) || gecoscheck(pamh, opt, newmono, user))) msg = _("contains the user name in some form"); free(usermono);