]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/config.c
70394b5093362684de8fabf989e77236ac4c2fe0
[kerberos/krb5-strength.git] / plugin / config.c
1 /*
2  * Retrieve configuration settings from krb5.conf.
3  *
4  * Provided here are functions to retrieve boolean, numeric, and string
5  * settings from krb5.conf.  This wraps the somewhat awkward
6  * krb5_appdefaults_* functions.
7  *
8  * Written by Russ Allbery <eagle@eyrie.org>
9  * Copyright 2013
10  *     The Board of Trustees of the Leland Stanford Junior University
11  *
12  * See LICENSE for licensing terms.
13  */
14
15 #include <config.h>
16 #include <portable/krb5.h>
17 #include <portable/system.h>
18
19 #include <errno.h>
20
21 #include <plugin/internal.h>
22 #include <util/macros.h>
23
24 /* The representation of the realm differs between MIT and Kerberos. */
25 #ifdef HAVE_KRB5_REALM
26 typedef krb5_realm realm_type;
27 #else
28 typedef krb5_data *realm_type;
29 #endif
30
31
32 /*
33  * Obtain the default realm and translate it into the format required by
34  * krb5_appdefault_*.  This is obnoxious for MIT Kerberos, which returns the
35  * default realm as a string but expects the realm as a krb5_data type when
36  * calling krb5_appdefault_*.
37  */
38 #ifdef HAVE_KRB5_REALM
39
40 static realm_type
41 default_realm(krb5_context ctx)
42 {
43     krb5_error_code code;
44     realm_type realm;
45
46     code = krb5_get_default_realm(ctx, &realm);
47     if (code != 0)
48         realm = NULL;
49     return realm;
50 }
51
52 #else /* !HAVE_KRB5_REALM */
53
54 static realm_type
55 default_realm(krb5_context ctx)
56 {
57     char *realm = NULL;
58     krb5_error_code code;
59     krb5_data *realm_data;
60
61     realm_data = calloc(1, sizeof(krb5_data));
62     if (realm_data == NULL)
63         return NULL;
64     code = krb5_get_default_realm(ctx, &realm);
65     if (code != 0) {
66         free(realm);
67         return NULL;
68     }
69     realm_data->magic = KV5M_DATA;
70     realm_data->data = strdup(realm);
71     if (realm_data->data == NULL) {
72         free(realm_data);
73         krb5_free_default_realm(ctx, realm);
74         return NULL;
75     }
76     realm_data->length = strlen(realm);
77     krb5_free_default_realm(ctx, realm);
78     return realm_data;
79 }
80
81 #endif /* !HAVE_KRB5_REALM */
82
83
84 /*
85  * Free the default realm data in whatever form it was generated for the calls
86  * to krb5_appdefault_*.
87  */
88 #ifdef HAVE_KRB5_REALM
89
90 static void
91 free_default_realm(krb5_context ctx UNUSED, realm_type realm)
92 {
93     krb5_free_default_realm(ctx, realm);
94 }
95
96 #else /* !HAVE_KRB5_REALM */
97
98 static void
99 free_default_realm(krb5_context ctx UNUSED, realm_type realm)
100 {
101     free(realm->data);
102     free(realm);
103 }
104
105 #endif /* !HAVE_KRB5_REALM */
106
107
108 /*
109  * Load a boolean option from Kerberos appdefaults.  Takes the Kerberos
110  * context, the option, and the result location.
111  */
112 void
113 strength_config_boolean(krb5_context ctx, const char *opt, bool *result)
114 {
115     realm_type realm;
116     int tmp;
117
118     /*
119      * The MIT version of krb5_appdefault_boolean takes an int * and the
120      * Heimdal version takes a krb5_boolean *, so hope that Heimdal always
121      * defines krb5_boolean to int or this will require more portability work.
122      */
123     realm = default_realm(ctx);
124     krb5_appdefault_boolean(ctx, "krb5-strength", realm, opt, *result, &tmp);
125     *result = tmp;
126     free_default_realm(ctx, realm);
127 }
128
129
130 /*
131  * Parse a single class specification.  Currently, this assumes that the class
132  * specification is a comma-separated list of required classes, and those
133  * classes are required for any length of password.  This will be enhanced
134  * later.
135  */
136 static krb5_error_code
137 parse_class(krb5_context ctx, const char *spec, struct class_rule **rule)
138 {
139     struct vector *classes;
140     size_t i;
141     krb5_error_code code;
142
143     /* Parse the required classes into a vector. */
144     classes = strength_vector_split_multi(spec, ",", NULL);
145     if (classes == NULL)
146         return strength_error_system(ctx, "cannot allocate memory");
147
148     /* Create the basic rule structure. */
149     *rule = calloc(1, sizeof(struct class_rule));
150     (*rule)->min = 0;
151     (*rule)->max = 0;
152
153     /*
154      * Walk the list of required classes and set our flags, diagnosing an
155      * unknown character class.
156      */
157     for (i = 0; i < classes->count; i++) {
158         if (strcmp(classes->strings[i], "upper") == 0)
159             (*rule)->upper = true;
160         else if (strcmp(classes->strings[i], "lower") == 0)
161             (*rule)->lower = true;
162         else if (strcmp(classes->strings[i], "digit") == 0)
163             (*rule)->digit = true;
164         else if (strcmp(classes->strings[i], "symbol") == 0)
165             (*rule)->symbol = true;
166         else {
167             code = strength_error_config(ctx, "unknown character class %s",
168                                          classes->strings[i]);
169             strength_vector_free(classes);
170             free(*rule);
171             *rule = NULL;
172             return code;
173         }
174     }
175     strength_vector_free(classes);
176     return 0;
177 }
178
179
180 /*
181  * Parse character class requirements from Kerberos appdefaults.  Takes the
182  * Kerberos context, the option, and the place to store the linked list of
183  * class requirements.
184  */
185 krb5_error_code
186 strength_config_classes(krb5_context ctx, const char *opt,
187                         struct class_rule **result)
188 {
189     struct vector *config;
190     struct class_rule *rules, *last, *tmp;
191     krb5_error_code code;
192     size_t i;
193
194     /* Get the basic configuration as a list. */
195     code = strength_config_list(ctx, opt, &config);
196     if (code != 0)
197         return code;
198     if (config == NULL || config->count == 0) {
199         *result = NULL;
200         return 0;
201     }
202
203     /* Each word in the list will be a class rule. */
204     code = parse_class(ctx, config->strings[0], &rules);
205     if (code != 0)
206         goto fail;
207     last = rules;
208     for (i = 1; i < config->count; i++) {
209         code = parse_class(ctx, config->strings[i], &last->next);
210         if (code != 0)
211             goto fail;
212         last = last->next;
213     }
214
215     /* Success.  Free the vector and return the results. */
216     strength_vector_free(config);
217     *result = rules;
218     return 0;
219
220 fail:
221     last = rules;
222     while (last != NULL) {
223         tmp = last;
224         last = last->next;
225         free(tmp);
226     }
227     strength_vector_free(config);
228     return code;
229 }
230
231
232 /*
233  * Load a list option from Kerberos appdefaults.  Takes the Kerberos context,
234  * the option, and the result location.  The option is read as a string and
235  * the split on spaces and tabs into a list.
236  *
237  * This requires an annoying workaround because one cannot specify a default
238  * value of NULL with MIT Kerberos, since MIT Kerberos unconditionally calls
239  * strdup on the default value.  There's also no way to determine if memory
240  * allocation failed while parsing or while setting the default value.
241  */
242 krb5_error_code
243 strength_config_list(krb5_context ctx, const char *opt,
244                      struct vector **result)
245 {
246     realm_type realm;
247     char *value = NULL;
248
249     /* Obtain the string from [appdefaults]. */
250     realm = default_realm(ctx);
251     krb5_appdefault_string(ctx, "krb5-sync", realm, opt, "", &value);
252     free_default_realm(ctx, realm);
253
254     /* If we got something back, store it in result. */
255     if (value != NULL) {
256         if (value[0] != '\0') {
257             *result = strength_vector_split_multi(value, " \t", *result);
258             if (*result == NULL)
259                 return strength_error_system(ctx, "cannot allocate memory");
260         }
261         krb5_free_string(ctx, value);
262     }
263     return 0;
264 }
265
266
267 /*
268  * Load a number option from Kerberos appdefaults.  Takes the Kerberos
269  * context, the option, and the result location.  The native interface doesn't
270  * support numbers, so we actually read a string and then convert.
271  */
272 void
273 strength_config_number(krb5_context ctx, const char *opt, long *result)
274 {
275     realm_type realm;
276     char *tmp = NULL;
277     char *end;
278     long value;
279
280     /* Obtain the setting in string form from [appdefaults]. */
281     realm = default_realm(ctx);
282     krb5_appdefault_string(ctx, "krb5-strength", realm, opt, "", &tmp);
283     free_default_realm(ctx, realm);
284
285     /*
286      * If we found anything, convert it to a number.  Currently, we ignore
287      * errors here.
288      */
289     if (tmp != NULL && tmp[0] != '\0') {
290         errno = 0;
291         value = strtol(tmp, &end, 10);
292         if (errno == 0 && *end == '\0')
293             *result = value;
294     }
295     if (tmp != NULL)
296         krb5_free_string(ctx, tmp);
297 }
298
299
300 /*
301  * Load a string option from Kerberos appdefaults.  Takes the Kerberos
302  * context, the option, and the result location.
303  *
304  * This requires an annoying workaround because one cannot specify a default
305  * value of NULL with MIT Kerberos, since MIT Kerberos unconditionally calls
306  * strdup on the default value.  There's also no way to determine if memory
307  * allocation failed while parsing or while setting the default value, so we
308  * don't return an error code.
309  */
310 void
311 strength_config_string(krb5_context ctx, const char *opt, char **result)
312 {
313     realm_type realm;
314     char *value = NULL;
315
316     /* Obtain the string from [appdefaults]. */
317     realm = default_realm(ctx);
318     krb5_appdefault_string(ctx, "krb5-strength", realm, opt, "", &value);
319     free_default_realm(ctx, realm);
320
321     /* If we got something back, store it in result. */
322     if (value != NULL) {
323         if (value[0] != '\0') {
324             free(*result);
325             *result = strdup(value);
326         }
327         krb5_free_string(ctx, value);
328     }
329 }