]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/principal.c
Refactor checking for passwords based on principals
[kerberos/krb5-strength.git] / plugin / principal.c
1 /*
2  * Password strength checks based on the principal.
3  *
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.
7  *
8  * Developed by Derrick Brashear and Ken Hornstein of Sine Nomine Associates,
9  *     on behalf of Stanford University.
10  * Extensive modifications by Russ Allbery <rra@stanford.edu>
11  * Copyright 2006, 2007, 2009, 2012, 2013
12  *     The Board of Trustees of the Leland Stanford Junior Unversity
13  *
14  * See LICENSE for licensing terms.
15  */
16
17 #include <config.h>
18 #include <portable/system.h>
19
20 #include <ctype.h>
21
22 #include <plugin/internal.h>
23 #include <util/macros.h>
24
25
26 /*
27  * Check whether the password is based in some way on the principal.  Returns
28  * 0 if it is not and some non-zero error code if it appears to be.
29  */
30 krb5_error_code
31 strength_check_principal(krb5_context ctx, krb5_pwqual_moddata data UNUSED,
32                          const char *principal, const char *password)
33 {
34     char *user, *p;
35     const char *q;
36     size_t i, j;
37     char c;
38
39     /*
40      * We get the principal (in krb5_unparse_name format) and we want to be
41      * sure that the password doesn't match the username, the username
42      * reversed, or the username with trailing digits.  We therefore have to
43      * copy the string so that we can manipulate it a bit.
44      */
45     if (strcasecmp(password, principal) == 0)
46         return strength_error_generic(ctx, ERROR_USERNAME);
47     user = strdup(principal);
48     if (user == NULL)
49         return strength_error_system(ctx, "cannot allocate memory");
50
51     /* Strip the realm off of the principal. */
52     for (p = user; p[0] != '\0'; p++) {
53         if (p[0] == '\\' && p[1] != '\0') {
54             p++;
55             continue;
56         }
57         if (p[0] == '@') {
58             p[0] = '\0';
59             break;
60         }
61     }
62
63     /*
64      * If the length of the password matches the length of the local portion
65      * of the principal, check for exact matches or reversed matches.
66      */
67     if (strlen(password) == strlen(user)) {
68         if (strcasecmp(password, user) == 0) {
69             free(user);
70             return strength_error_generic(ctx, ERROR_USERNAME);
71         }
72
73         /* Check against the reversed username. */
74         for (i = 0, j = strlen(user) - 1; i < j; i++, j--) {
75             c = user[i];
76             user[i] = user[j];
77             user[j] = c;
78         }
79         if (strcasecmp(password, user) == 0) {
80             free(user);
81             return strength_error_generic(ctx, ERROR_USERNAME);
82         }
83     }
84
85     /*
86      * If the length is greater, check whether the user just added trailing
87      * digits to the local portion of the principal to form the password.
88      */
89     if (strlen(password) > strlen(user))
90         if (strncasecmp(password, user, strlen(user)) == 0) {
91             q = password + strlen(user);
92             while (isdigit((unsigned char) *q))
93                 q++;
94             if (*q == '\0') {
95                 free(user);
96                 return strength_error_generic(ctx, ERROR_USERNAME);
97             }
98         }
99
100     /* Password does not appear to be based on the principal. */
101     free(user);
102     return 0;
103 }