2 * Retrieve configuration settings from krb5.conf.
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.
8 * Written by Russ Allbery <eagle@eyrie.org>
10 * The Board of Trustees of the Leland Stanford Junior University
12 * See LICENSE for licensing terms.
16 #include <portable/krb5.h>
17 #include <portable/system.h>
22 #include <plugin/internal.h>
23 #include <util/macros.h>
25 /* The representation of the realm differs between MIT and Kerberos. */
26 #ifdef HAVE_KRB5_REALM
27 typedef krb5_realm realm_type;
29 typedef krb5_data *realm_type;
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_*.
39 #ifdef HAVE_KRB5_REALM
42 default_realm(krb5_context ctx)
47 code = krb5_get_default_realm(ctx, &realm);
53 #else /* !HAVE_KRB5_REALM */
56 default_realm(krb5_context ctx)
60 krb5_data *realm_data;
62 realm_data = calloc(1, sizeof(krb5_data));
63 if (realm_data == NULL)
65 code = krb5_get_default_realm(ctx, &realm);
70 realm_data->magic = KV5M_DATA;
71 realm_data->data = strdup(realm);
72 if (realm_data->data == NULL) {
74 krb5_free_default_realm(ctx, realm);
77 realm_data->length = strlen(realm);
78 krb5_free_default_realm(ctx, realm);
82 #endif /* !HAVE_KRB5_REALM */
86 * Free the default realm data in whatever form it was generated for the calls
87 * to krb5_appdefault_*.
89 #ifdef HAVE_KRB5_REALM
92 free_default_realm(krb5_context ctx UNUSED, realm_type realm)
94 krb5_free_default_realm(ctx, realm);
97 #else /* !HAVE_KRB5_REALM */
100 free_default_realm(krb5_context ctx UNUSED, realm_type realm)
106 #endif /* !HAVE_KRB5_REALM */
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.
116 parse_number(const char *string, unsigned long *result, const char **end)
121 value = strtoul(string, (char **) end, 10);
122 if (errno != 0 || *end == string)
130 * Load a boolean option from Kerberos appdefaults. Takes the Kerberos
131 * context, the option, and the result location.
134 strength_config_boolean(krb5_context ctx, const char *opt, bool *result)
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.
144 realm = default_realm(ctx);
145 krb5_appdefault_boolean(ctx, "krb5-strength", realm, opt, *result, &tmp);
147 free_default_realm(ctx, realm);
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
157 static krb5_error_code
158 parse_class(krb5_context ctx, const char *spec, struct class_rule **rule)
160 struct vector *classes = NULL;
162 krb5_error_code code;
166 /* Create the basic rule structure. */
167 *rule = calloc(1, sizeof(struct class_rule));
170 * If the rule starts with a digit, it starts with a range of affected
171 * password lengths. Parse that range.
173 if (isdigit((unsigned char) *spec)) {
174 okay = parse_number(spec, &(*rule)->min, &end);
176 okay = (*end == '-');
178 okay = parse_number(end + 1, &(*rule)->max, &end);
180 okay = (*end == ':');
184 code = strength_error_config(ctx, "bad character class requirement"
185 " in configuration: %s", spec);
190 /* Parse the required classes into a vector. */
191 classes = strength_vector_split_multi(spec, ",", NULL);
192 if (classes == NULL) {
193 code = strength_error_system(ctx, "cannot allocate memory");
198 * Walk the list of required classes and set our flags, diagnosing an
199 * unknown character class.
201 for (i = 0; i < classes->count; i++) {
202 if (strcmp(classes->strings[i], "upper") == 0)
203 (*rule)->upper = true;
204 else if (strcmp(classes->strings[i], "lower") == 0)
205 (*rule)->lower = true;
206 else if (strcmp(classes->strings[i], "digit") == 0)
207 (*rule)->digit = true;
208 else if (strcmp(classes->strings[i], "symbol") == 0)
209 (*rule)->symbol = true;
211 code = strength_error_config(ctx, "unknown character class %s",
212 classes->strings[i]);
216 strength_vector_free(classes);
220 strength_vector_free(classes);
228 * Parse character class requirements from Kerberos appdefaults. Takes the
229 * Kerberos context, the option, and the place to store the linked list of
230 * class requirements.
233 strength_config_classes(krb5_context ctx, const char *opt,
234 struct class_rule **result)
236 struct vector *config = NULL;
237 struct class_rule *rules, *last, *tmp;
238 krb5_error_code code;
241 /* Get the basic configuration as a list. */
242 code = strength_config_list(ctx, opt, &config);
245 if (config == NULL || config->count == 0) {
250 /* Each word in the list will be a class rule. */
251 code = parse_class(ctx, config->strings[0], &rules);
255 for (i = 1; i < config->count; i++) {
256 code = parse_class(ctx, config->strings[i], &last->next);
262 /* Success. Free the vector and return the results. */
263 strength_vector_free(config);
269 while (last != NULL) {
274 strength_vector_free(config);
280 * Load a list option from Kerberos appdefaults. Takes the Kerberos context,
281 * the option, and the result location. The option is read as a string and
282 * the split on spaces and tabs into a list.
284 * This requires an annoying workaround because one cannot specify a default
285 * value of NULL with MIT Kerberos, since MIT Kerberos unconditionally calls
286 * strdup on the default value. There's also no way to determine if memory
287 * allocation failed while parsing or while setting the default value.
290 strength_config_list(krb5_context ctx, const char *opt,
291 struct vector **result)
296 /* Obtain the string from [appdefaults]. */
297 realm = default_realm(ctx);
298 krb5_appdefault_string(ctx, "krb5-strength", realm, opt, "", &value);
299 free_default_realm(ctx, realm);
301 /* If we got something back, store it in result. */
303 if (value[0] != '\0') {
304 *result = strength_vector_split_multi(value, " \t", *result);
306 return strength_error_system(ctx, "cannot allocate memory");
308 krb5_free_string(ctx, value);
315 * Load a number option from Kerberos appdefaults. Takes the Kerberos
316 * context, the option, and the result location. The native interface doesn't
317 * support numbers, so we actually read a string and then convert.
320 strength_config_number(krb5_context ctx, const char *opt, long *result)
327 /* Obtain the setting in string form from [appdefaults]. */
328 realm = default_realm(ctx);
329 krb5_appdefault_string(ctx, "krb5-strength", realm, opt, "", &tmp);
330 free_default_realm(ctx, realm);
333 * If we found anything, convert it to a number. Currently, we ignore
336 if (tmp != NULL && tmp[0] != '\0') {
338 value = strtol(tmp, &end, 10);
339 if (errno == 0 && *end == '\0')
343 krb5_free_string(ctx, tmp);
348 * Load a string option from Kerberos appdefaults. Takes the Kerberos
349 * context, the option, and the result location.
351 * This requires an annoying workaround because one cannot specify a default
352 * value of NULL with MIT Kerberos, since MIT Kerberos unconditionally calls
353 * strdup on the default value. There's also no way to determine if memory
354 * allocation failed while parsing or while setting the default value, so we
355 * don't return an error code.
358 strength_config_string(krb5_context ctx, const char *opt, char **result)
363 /* Obtain the string from [appdefaults]. */
364 realm = default_realm(ctx);
365 krb5_appdefault_string(ctx, "krb5-strength", realm, opt, "", &value);
366 free_default_realm(ctx, realm);
368 /* If we got something back, store it in result. */
370 if (value[0] != '\0') {
372 *result = strdup(value);
374 krb5_free_string(ctx, value);