]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/api.c
Fix configuration and status codes in the MIT plugin
[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, 2013
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/kadmin.h>
18 #include <portable/krb5.h>
19 #include <portable/system.h>
20
21 #include <ctype.h>
22 #include <errno.h>
23
24 #include <plugin/api.h>
25
26 /* Heimdal doesn't define KADM5_PASS_Q_GENERIC. */
27 #ifndef KADM5_PASS_Q_GENERIC
28 # define KADM5_PASS_Q_GENERIC KADM5_PASS_Q_DICT
29 #endif
30
31 /*
32  * Used to store local state.  Currently, all we have is the dictionary path,
33  * which we get from kadmind rather than from krb5.conf since it's already a
34  * kdc.conf setting.
35  */
36 struct context {
37     char *dictionary;
38 };
39
40 /* The public function exported by the cracklib library. */
41 extern char *FascistCheck(const char *password, const char *dict);
42
43
44 /*
45  * Initialize the module.  Ensure that the dictionary file exists and is
46  * readable and store the path in the module context.  Returns 0 on success,
47  * non-zero on failure.  This function returns failure only if it could not
48  * allocate memory.
49  *
50  * The dictionary file should not include the trailing .pwd extension.
51  * Currently, we don't cope with a NULL dictionary path.
52  */
53 krb5_error_code
54 pwcheck_init(void **context, const char *dictionary)
55 {
56     char *path;
57     size_t length;
58     struct context *ctx;
59
60     if (dictionary == NULL)
61         return KADM5_MISSING_CONF_PARAMS;
62     length = strlen(dictionary) + strlen(".pwd") + 1;
63     path = malloc(length);
64     if (path == NULL)
65         return errno;
66     snprintf(path, length, "%s.pwd", dictionary);
67     if (access(path, R_OK) != 0)
68         return errno;
69     path[strlen(path) - strlen(".pwd")] = '\0';
70     ctx = malloc(sizeof(struct context));
71     if (ctx == NULL)
72         return errno;
73     ctx->dictionary = path;
74     *context = ctx;
75     return 0;
76 }
77
78
79 /*
80  * Check a given password.  Takes our local context, the password, the
81  * principal the password is for, and a buffer and buffer length into which to
82  * put any failure message.
83  */
84 krb5_error_code
85 pwcheck_check(void *context, const char *password, const char *principal,
86               char *errstr, int errstrlen)
87 {
88     char *user, *p;
89     const char *q;
90     size_t i, j;
91     char c;
92     int err;
93     const char *result;
94     struct context *ctx = context;
95
96     /*
97      * We get the principal (in krb5_unparse_name format) from kadmind and we
98      * want to be sure that the password doesn't match the username, the
99      * username reversed, or the username with trailing digits.  We therefore
100      * have to copy the string so that we can manipulate it a bit.
101      */
102     if (strcasecmp(password, principal) == 0) {
103         snprintf(errstr, errstrlen, "Password based on username");
104         return KADM5_PASS_Q_GENERIC;
105     }
106     user = strdup(principal);
107     if (user == NULL) {
108         err = errno;
109         snprintf(errstr, errstrlen, "Cannot allocate memory");
110         return err;
111     }
112     for (p = user; p[0] != '\0'; p++) {
113         if (p[0] == '\\' && p[1] != '\0') {
114             p++;
115             continue;
116         }
117         if (p[0] == '@') {
118             p[0] = '\0';
119             break;
120         }
121     }
122     if (strlen(password) == strlen(user)) {
123         if (strcasecmp(password, user) == 0) {
124             free(user);
125             snprintf(errstr, errstrlen, "Password based on username");
126             return KADM5_PASS_Q_GENERIC;
127         }
128
129         /* Check against the reversed username. */
130         for (i = 0, j = strlen(user) - 1; i < j; i++, j--) {
131             c = user[i];
132             user[i] = user[j];
133             user[j] = c;
134         }
135         if (strcasecmp(password, user) == 0) {
136             free(user);
137             snprintf(errstr, errstrlen, "Password based on username");
138             return KADM5_PASS_Q_GENERIC;
139         }
140     }
141     if (strlen(password) > strlen(user))
142         if (strncasecmp(password, user, strlen(user)) == 0) {
143             q = password + strlen(user);
144             while (isdigit((int) *q))
145                 q++;
146             if (*q == '\0') {
147                 free(user);
148                 snprintf(errstr, errstrlen, "Password based on username");
149                 return KADM5_PASS_Q_GENERIC;
150             }
151         }
152     free(user);
153     result = FascistCheck(password, ctx->dictionary);
154     if (result != NULL) {
155         strlcpy(errstr, result, errstrlen);
156         return KADM5_PASS_Q_GENERIC;
157     }
158     return 0;
159 }
160
161
162 /*
163  * Cleanly shut down the password strength plugin.  The only thing we have to
164  * do is free our context memory.
165  */
166 void
167 pwcheck_close(void *context)
168 {
169     struct context *ctx = context;
170
171     if (ctx != NULL) {
172         if (ctx->dictionary != NULL)
173             free(ctx->dictionary);
174         free(ctx);
175     }
176 }