/* * A password is too simple if it is too short for its class, or doesn't * contain enough different characters for its class, or doesn't contain * enough words for a passphrase. */ static int is_simple(passwdqc_params_t *params, const char *newpass) { int length, classes, words, chars; int digits, lowers, uppers, others, unknowns; int c, p; length = classes = words = chars = 0; digits = lowers = uppers = others = unknowns = 0; p = ' '; while ((c = (unsigned char)newpass[length])) { length++; if (!isascii(c)) unknowns++; else if (isdigit(c)) digits++; else if (islower(c)) lowers++; else if (isupper(c)) uppers++; else others++; if (isascii(c) && isalpha(c) && isascii(p) && !isalpha(p)) words++; p = c; if (!strchr(&newpass[length], c)) chars++; } if (!length) return 1; /* Upper case characters and digits used in common ways don't increase the * strength of a password */ c = (unsigned char)newpass[0]; if (uppers && isascii(c) && isupper(c)) uppers--; c = (unsigned char)newpass[length - 1]; if (digits && isascii(c) && isdigit(c)) digits--; /* Count the number of different character classes we've seen. We assume * that there're no non-ASCII characters for digits. */ classes = 0; if (digits) classes++; if (lowers) classes++; if (uppers) classes++; if (others) classes++; if (unknowns && (!classes || (digits && classes == 1))) classes++; for (; classes > 0; classes--) switch (classes) { case 1: if (length >= params->min[0] && chars >= expected_different(10, params->min[0]) - 1) return 0; return 1; case 2: if (length >= params->min[1] && chars >= expected_different(36, params->min[1]) - 1) return 0; if (!params->passphrase_words || words < params->passphrase_words) continue; if (length >= params->min[2] && chars >= expected_different(27, params->min[2]) - 1) return 0; continue; case 3: if (length >= params->min[3] && chars >= expected_different(62, params->min[3]) - 1) return 0; continue; case 4: if (length >= params->min[4] && chars >= expected_different(95, params->min[4]) - 1) return 0; continue; } return 1; }
/* * A password is too simple if it is too short for its class, or doesn't * contain enough different characters for its class, or doesn't contain * enough words for a passphrase. * * The bias may be positive or negative. It is added to the length, * except that a negative bias is not considered in the passphrase * length check because a passphrase is expected to contain words. * The bias does not apply to the number of different characters; the * actual number is used in all checks. */ static int is_simple(const passwdqc_params_qc_t *params, const char *newpass, int bias) { int length, classes, words, chars; int digits, lowers, uppers, others, unknowns; int c, p; length = classes = words = chars = 0; digits = lowers = uppers = others = unknowns = 0; p = ' '; while ((c = (unsigned char)newpass[length])) { length++; if (!isascii(c)) unknowns++; else if (isdigit(c)) digits++; else if (islower(c)) lowers++; else if (isupper(c)) uppers++; else others++; /* A word starts when a letter follows a non-letter or when a non-ASCII * character follows a space character. We treat all non-ASCII characters * as non-spaces, which is not entirely correct (there's the non-breaking * space character at 0xa0, 0x9a, or 0xff), but it should not hurt. */ if (isascii(p)) { if (isascii(c)) { if (isalpha(c) && !isalpha(p)) words++; } else if (isspace(p)) words++; } p = c; /* Count this character just once: when we're not going to see it anymore */ if (!strchr(&newpass[length], c)) chars++; } if (!length) return 1; /* Upper case characters and digits used in common ways don't increase the * strength of a password */ c = (unsigned char)newpass[0]; if (uppers && isascii(c) && isupper(c)) uppers--; c = (unsigned char)newpass[length - 1]; if (digits && isascii(c) && isdigit(c)) digits--; /* Count the number of different character classes we've seen. We assume * that there are no non-ASCII characters for digits. */ classes = 0; if (digits) classes++; if (lowers) classes++; if (uppers) classes++; if (others) classes++; if (unknowns && classes <= 1 && (!classes || digits || words >= 2)) classes++; for (; classes > 0; classes--) switch (classes) { case 1: if (length + bias >= params->min[0] && chars >= expected_different(10, params->min[0]) - 1) return 0; return 1; case 2: if (length + bias >= params->min[1] && chars >= expected_different(36, params->min[1]) - 1) return 0; if (!params->passphrase_words || words < params->passphrase_words) continue; if (length + (bias > 0 ? bias : 0) >= params->min[2] && chars >= expected_different(27, params->min[2]) - 1) return 0; continue; case 3: if (length + bias >= params->min[3] && chars >= expected_different(62, params->min[3]) - 1) return 0; continue; case 4: if (length + bias >= params->min[4] && chars >= expected_different(95, params->min[4]) - 1) return 0; continue; } return 1; }