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