/* * This wordlist check is now the least important given the checks above * and the support for passphrases (which are based on dictionary words, * and checked by other means). It is still useful to trap simple short * passwords (if short passwords are allowed) that are word-based, but * passed the other checks due to uncommon capitalization, digits, and * special characters. We (mis)use the same set of words that are used * to generate random passwords. This list is much smaller than those * used for password crackers, and it doesn't contain common passwords * that aren't short English words. Perhaps support for large wordlists * should still be added, even though this is now of little importance. */ static const char *is_word_based(const passwdqc_params_qc_t *params, const char *needle, const char *original, int is_reversed) { char word[7]; char *unified; unsigned int i; int length; int mode; if (!params->match_length) /* disabled */ return NULL; mode = is_reversed | 2; for (i = 0; i < sizeof(seq) / sizeof(seq[0]); i++) { unified = unify(NULL, seq[i]); if (!unified) return REASON_ERROR; if (is_based(params, unified, needle, original, mode)) { free(unified); return REASON_SEQ; } free(unified); } mode = is_reversed | 1; word[6] = '\0'; for (i = 0; i < 0x1000; i++) { if (params->lang == 0) { memcpy(word, _passwdqc_wordset_4k[i], 6); } else { memcpy(word, _passwdqc_wordset_4k_es[i], 6); } length = strlen(word); if (length < params->match_length) continue; if (params->lang == 0 && i < 0xfff && !memcmp(word, _passwdqc_wordset_4k[i + 1], length)) continue; if (params->lang == 1 && i < 0xfff && !memcmp(word, _passwdqc_wordset_4k_es[i + 1], length)) continue; unify(word, word); if (is_based(params, word, needle, original, mode)) return REASON_WORD; } mode = is_reversed | 2; if (params->match_length <= 4) for (i = 1900; i <= 2039; i++) { sprintf(word, "%u", i); if (is_based(params, word, needle, original, mode)) return REASON_SEQ; } return NULL; }
/* * This wordlist check is now the least important given the checks above * and the support for passphrases (which are based on dictionary words, * and checked by other means). It is still useful to trap simple short * passwords (if short passwords are allowed) that are word-based, but * passed the other checks due to uncommon capitalization, digits, and * special characters. We (mis)use the same set of words that are used * to generate random passwords. This list is much smaller than those * used for password crackers, and it doesn't contain common passwords * that aren't short English words. Perhaps support for large wordlists * should still be added, even though this is now of little importance. */ static int is_word_based(passwdqc_params_t *params, const char *needle, const char *original) { char word[7]; char *unified; int i; word[6] = '\0'; for (i = 0; i < 0x1000; i++) { memcpy(word, _passwdqc_wordset_4k[i], 6); if ((int)strlen(word) < params->match_length) continue; unified = unify(word); if (is_based(params, unified, needle, original)) { clean(unified); return 1; } clean(unified); } return 0; }
const char *_passwdqc_check(passwdqc_params_t *params, const char *newpass, const char *oldpass, struct passwd *pw) { char truncated[9], *reversed; char *u_newpass, *u_reversed; char *u_oldpass; char *u_name, *u_gecos; const char *reason; int length; reversed = NULL; u_newpass = u_reversed = NULL; u_oldpass = NULL; u_name = u_gecos = NULL; reason = NULL; if (oldpass && !strcmp(oldpass, newpass)) reason = REASON_SAME; length = strlen(newpass); if (!reason && length < params->min[4]) reason = REASON_SHORT; if (!reason && length > params->max) { if (params->max == 8) { truncated[0] = '\0'; strncat(truncated, newpass, 8); newpass = truncated; if (oldpass && !strncmp(oldpass, newpass, 8)) reason = REASON_SAME; } else reason = REASON_LONG; } if (!reason && is_simple(params, newpass)) { if (length < params->min[1] && params->min[1] <= params->max) reason = REASON_SIMPLESHORT; else reason = REASON_SIMPLE; } if (!reason) { if ((reversed = reverse(newpass))) { u_newpass = unify(newpass); u_reversed = unify(reversed); if (oldpass) u_oldpass = unify(oldpass); if (pw) { u_name = unify(pw->pw_name); u_gecos = unify(pw->pw_gecos); } } if (!reversed || !u_newpass || !u_reversed || (oldpass && !u_oldpass) || (pw && (!u_name || !u_gecos))) reason = REASON_ERROR; } if (!reason && oldpass && params->similar_deny && (is_based(params, u_oldpass, u_newpass, newpass) || is_based(params, u_oldpass, u_reversed, reversed))) reason = REASON_SIMILAR; if (!reason && pw && (is_based(params, u_name, u_newpass, newpass) || is_based(params, u_name, u_reversed, reversed) || is_based(params, u_gecos, u_newpass, newpass) || is_based(params, u_gecos, u_reversed, reversed))) reason = REASON_PERSONAL; if (!reason && (int)strlen(newpass) < params->min[2] && (is_word_based(params, u_newpass, newpass) || is_word_based(params, u_reversed, reversed))) reason = REASON_WORD; memset(truncated, 0, sizeof(truncated)); clean(reversed); clean(u_newpass); clean(u_reversed); clean(u_oldpass); clean(u_name); clean(u_gecos); return reason; }
const char *passwdqc_check(const passwdqc_params_qc_t *params, const char *newpass, const char *oldpass, const struct passwd *pw) { char truncated[9]; char *u_newpass, *u_reversed; char *u_oldpass; char *u_name, *u_gecos, *u_dir; const char *reason; int length; u_newpass = u_reversed = NULL; u_oldpass = NULL; u_name = u_gecos = u_dir = NULL; reason = REASON_ERROR; if (oldpass && !strcmp(oldpass, newpass)) { reason = REASON_SAME; goto out; } length = strlen(newpass); if (length < params->min[4]) { reason = REASON_SHORT; goto out; } if (length > params->max) { if (params->max == 8) { truncated[0] = '\0'; strncat(truncated, newpass, 8); newpass = truncated; if (oldpass && !strncmp(oldpass, newpass, 8)) { reason = REASON_SAME; goto out; } } else { reason = REASON_LONG; goto out; } } if (is_simple(params, newpass, 0)) { reason = REASON_SIMPLE; if (length < params->min[1] && params->min[1] <= params->max) reason = REASON_SIMPLESHORT; goto out; } if (!(u_newpass = unify(NULL, newpass))) goto out; /* REASON_ERROR */ if (!(u_reversed = reverse(u_newpass))) goto out; if (oldpass && !(u_oldpass = unify(NULL, oldpass))) goto out; if (pw) { if (!(u_name = unify(NULL, pw->pw_name)) || !(u_gecos = unify(NULL, pw->pw_gecos)) || !(u_dir = unify(NULL, pw->pw_dir))) goto out; } if (oldpass && params->similar_deny && (is_based(params, u_oldpass, u_newpass, newpass, 0) || is_based(params, u_oldpass, u_reversed, newpass, 0x100))) { reason = REASON_SIMILAR; goto out; } if (pw && (is_based(params, u_name, u_newpass, newpass, 0) || is_based(params, u_name, u_reversed, newpass, 0x100) || is_based(params, u_gecos, u_newpass, newpass, 0) || is_based(params, u_gecos, u_reversed, newpass, 0x100) || is_based(params, u_dir, u_newpass, newpass, 0) || is_based(params, u_dir, u_reversed, newpass, 0x100))) { reason = REASON_PERSONAL; goto out; } reason = is_word_based(params, u_newpass, newpass, 0); if (!reason) reason = is_word_based(params, u_reversed, newpass, 0x100); out: memset(truncated, 0, sizeof(truncated)); clean(u_newpass); clean(u_reversed); clean(u_oldpass); clean(u_name); clean(u_gecos); clean(u_dir); return reason; }