2 * Password strength checks based on the principal.
4 * Performs various checks of the password against the principal for which the
5 * password is being changed, trying to detect and reject passwords based on
6 * components of the principal.
8 * Developed by Derrick Brashear and Ken Hornstein of Sine Nomine Associates,
9 * on behalf of Stanford University
10 * Extensive modifications by Russ Allbery <eagle@eyrie.org>
11 * Copyright 2006, 2007, 2009, 2012, 2013
12 * The Board of Trustees of the Leland Stanford Junior University
14 * See LICENSE for licensing terms.
18 #include <portable/system.h>
22 #include <plugin/internal.h>
23 #include <util/macros.h>
27 * Given a string taken from the principal, check if the password matches that
28 * string or is that string with leading or trailing digits added. If so,
29 * sets the Kerberos error and returns a non-zero error code. Otherwise,
32 static krb5_error_code
33 check_component(krb5_context ctx, const char *component, const char *password)
36 size_t i, j, complength, passlength;
39 /* Check if the password is a simple match for the component. */
40 if (strcasecmp(component, password) == 0)
41 return strength_error_generic(ctx, ERROR_USERNAME);
44 * If the length of the password matches the length of the component,
45 * check for a reversed match.
47 complength = strlen(component);
48 passlength = strlen(password);
49 if (complength == passlength) {
50 copy = strdup(component);
52 return strength_error_system(ctx, "cannot allocate memory");
53 for (i = 0, j = complength - 1; i < j; i++, j--) {
58 if (strcasecmp(copy, password) == 0) {
60 return strength_error_generic(ctx, ERROR_USERNAME);
66 * We've checked everything we care about unless the password is longer
69 if (passlength <= complength)
73 * Check whether the user just added leading or trailing digits to the
74 * component of the principal to form the password.
76 for (i = 0; i <= passlength - complength; i++) {
77 if (strncasecmp(password + i, component, complength) != 0)
81 * For this to be a match, all characters from 0 to i - 1 must be
82 * digits, and all characters from strlen(component) + i to
83 * strlen(password) - 1 must be digits.
85 for (j = 0; j < i; j++)
86 if (!isdigit((unsigned char) password[j]))
88 for (j = complength + i; j < passlength; j++)
89 if (!isdigit((unsigned char) password[j]))
92 /* The password was formed by adding digits to this component. */
93 return strength_error_generic(ctx, ERROR_USERNAME);
96 /* No similarity to component detected. */
102 * Returns true if a given character is a separator character for forming
103 * components, and false otherwise.
106 is_separator(unsigned char c)
108 if (c == '-' || c == '_')
117 * Check whether the password is based in some way on the principal. We do
118 * this by scanning the principal (in string form) and checking both each
119 * component of that password (defined as the alphanumeric, hyphen, and
120 * underscore bits between other characters) and the remaining principal from
121 * that point forward (to catch, for example, the entire realm). Returns 0 if
122 * it is not and some non-zero error code if it appears to be.
125 strength_check_principal(krb5_context ctx, krb5_pwqual_moddata data UNUSED,
126 const char *principal, const char *password)
128 krb5_error_code code;
133 if (principal == NULL)
136 /* Start with checking the entire principal. */
137 code = check_component(ctx, principal, password);
142 * Make a copy of the principal and scan forward past any leading
145 length = strlen(principal);
146 copy = strdup(principal);
148 return strength_error_system(ctx, "cannot allocate memory");
150 while (copy[i] != '\0' && is_separator(copy[i]))
154 * Now loop for each component. At the start of each loop, check against
155 * the component formed by the rest of the principal string.
159 code = check_component(ctx, copy + i, password);
166 /* Set the component start and then scan for a separator. */
168 while (i < length && !is_separator(copy[i]))
171 /* At end of string or a separator. Truncate the component. */
174 /* Check the current component. */
175 code = check_component(ctx, start, password);
181 /* Scan forward past any more separators. */
182 while (i < length && is_separator(copy[i]))
184 } while (i < length);
186 /* Password does not appear to be based on the principal. */