]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/api.c
Merge tag 'upstream/1.1' into debian
[kerberos/krb5-strength.git] / plugin / api.c
1 /*
2  * The public APIs of the password strength checking kadmind plugin.
3  *
4  * Provides the public pwcheck_init, pwcheck_check, and pwcheck_close APIs for
5  * the kadmind plugin.
6  *
7  * Developed by Derrick Brashear and Ken Hornstein of Sine Nomine Associates,
8  *     on behalf of Stanford University.
9  * Extensive modifications by Russ Allbery <rra@stanford.edu>
10  * Copyright 2006, 2007, 2009, 2012
11  *     The Board of Trustees of the Leland Stanford Junior Unversity
12  *
13  * See LICENSE for licensing terms.
14  */
15
16 #include <config.h>
17 #include <portable/system.h>
18
19 #include <ctype.h>
20
21 #include <plugin/api.h>
22
23 /*
24  * Used to store local state.  Currently, all we have is the dictionary path,
25  * which we get from kadmind rather than from krb5.conf since it's already a
26  * kdc.conf setting.
27  */
28 struct context {
29     char *dictionary;
30 };
31
32 /* The public function exported by the cracklib library. */
33 extern char *FascistCheck(const char *password, const char *dict);
34
35
36 /*
37  * Initialize the module.  Ensure that the dictionary file exists and is
38  * readable and store the path in the module context.  Returns 0 on success,
39  * non-zero on failure.  This function returns failure only if it could not
40  * allocate memory.
41  *
42  * The dictionary file should not include the trailing .pwd extension.
43  * Currently, we don't cope with a NULL dictionary path.
44  */
45 int
46 pwcheck_init(void **context, const char *dictionary)
47 {
48     char *path;
49     size_t length;
50     struct context *ctx;
51
52     if (dictionary == NULL)
53         return 1;
54     length = strlen(dictionary) + strlen(".pwd") + 1;
55     path = malloc(length);
56     if (path == NULL)
57         return 1;
58     snprintf(path, length, "%s.pwd", dictionary);
59     if (access(path, R_OK) != 0)
60         return 1;
61     path[strlen(path) - strlen(".pwd")] = '\0';
62     ctx = malloc(sizeof(struct context));
63     if (ctx == NULL)
64         return 1;
65     ctx->dictionary = path;
66     *context = ctx;
67     return 0;
68 }
69
70
71 /*
72  * Check a given password.  Takes our local context, the password, the
73  * principal the password is for, and a buffer and buffer length into which to
74  * put any failure message.
75  */
76 int
77 pwcheck_check(void *context, const char *password, const char *principal,
78               char *errstr, int errstrlen)
79 {
80     char *user, *p;
81     const char *q;
82     size_t i, j;
83     char c;
84     const char *result;
85     struct context *ctx = context;
86
87     /*
88      * We get the principal (in krb5_unparse_name format) from kadmind and we
89      * want to be sure that the password doesn't match the username, the
90      * username reversed, or the username with trailing digits.  We therefore
91      * have to copy the string so that we can manipulate it a bit.
92      */
93     if (strcasecmp(password, principal) == 0) {
94         snprintf(errstr, errstrlen, "Password based on username");
95         return 1;
96     }
97     user = strdup(principal);
98     if (user == NULL) {
99         snprintf(errstr, errstrlen, "Cannot allocate memory");
100         return 1;
101     }
102     for (p = user; p[0] != '\0'; p++) {
103         if (p[0] == '\\' && p[1] != '\0') {
104             p++;
105             continue;
106         }
107         if (p[0] == '@') {
108             p[0] = '\0';
109             break;
110         }
111     }
112     if (strlen(password) == strlen(user)) {
113         if (strcasecmp(password, user) == 0) {
114             free(user);
115             snprintf(errstr, errstrlen, "Password based on username");
116             return 1;
117         }
118
119         /* Check against the reversed username. */
120         for (i = 0, j = strlen(user) - 1; i < j; i++, j--) {
121             c = user[i];
122             user[i] = user[j];
123             user[j] = c;
124         }
125         if (strcasecmp(password, user) == 0) {
126             free(user);
127             snprintf(errstr, errstrlen, "Password based on username");
128             return 1;
129         }
130     }
131     if (strlen(password) > strlen(user))
132         if (strncasecmp(password, user, strlen(user)) == 0) {
133             q = password + strlen(user);
134             while (isdigit((int) *q))
135                 q++;
136             if (*q == '\0') {
137                 free(user);
138                 snprintf(errstr, errstrlen, "Password based on username");
139                 return 1;
140             }
141         }
142     free(user);
143     result = FascistCheck(password, ctx->dictionary);
144     if (result != NULL) {
145         strlcpy(errstr, result, errstrlen);
146         return 1;
147     }
148     return 0;
149 }
150
151
152 /*
153  * Cleanly shut down the password strength plugin.  The only thing we have to
154  * do is free our context memory.
155  */
156 void
157 pwcheck_close(void *context)
158 {
159     struct context *ctx = context;
160
161     if (ctx != NULL) {
162         if (ctx->dictionary != NULL)
163             free(ctx->dictionary);
164         free(ctx);
165     }
166 }