From d247242f6377437279ae5b0731830626f9e687ee Mon Sep 17 00:00:00 2001 From: "Asanka C. Herath" Date: Wed, 22 Sep 2010 17:28:12 -0400 Subject: [PATCH] Windows: Registry based configuration Load configuration data in the registry into a krb5_config_section. Each registry key corresponds to a krb5_config_section and each registry value becomes a bound string value. The set of values contained in the root Heimdal registry key is treated as if they were defined in the [libdefaults] section. E.g. the configuration file: [libdefaults] foo = bar [Foo] x = y y = { baz = quux } is equivalent to the registry keys: [HKEY_CURRENT_USER\Software\Heimdal] "foo"="bar" [HKEY_CURRENT_USER\Software\Heimdal\Foo] "x"="y" [HKEY_CURRENT_USER\Software\Heimdal\Foo\y] "baz"="quux" --- lib/krb5/NTMakefile | 2 + lib/krb5/config_file.c | 2 +- lib/krb5/config_reg.c | 346 +++++++++++++++++++++++++++++++++++++++++ lib/krb5/context.c | 5 + 4 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 lib/krb5/config_reg.c diff --git a/lib/krb5/NTMakefile b/lib/krb5/NTMakefile index b55c400d7..801cb9ade 100644 --- a/lib/krb5/NTMakefile +++ b/lib/krb5/NTMakefile @@ -48,6 +48,7 @@ libkrb5_OBJS = \ $(OBJ)\changepw.obj \ $(OBJ)\codec.obj \ $(OBJ)\config_file.obj \ + $(OBJ)\config_reg.obj \ $(OBJ)\convert_creds.obj \ $(OBJ)\constants.obj \ $(OBJ)\context.obj \ @@ -190,6 +191,7 @@ dist_libkrb5_la_SOURCES = \ changepw.c \ codec.c \ config_file.c \ + config_reg.c \ convert_creds.c \ constants.c \ context.c \ diff --git a/lib/krb5/config_file.c b/lib/krb5/config_file.c index 5840b8d9b..828371cc2 100644 --- a/lib/krb5/config_file.c +++ b/lib/krb5/config_file.c @@ -84,7 +84,7 @@ static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent, const char **err_message); -static krb5_config_section * +krb5_config_section * get_entry(krb5_config_section **parent, const char *name, int type) { krb5_config_section **q; diff --git a/lib/krb5/config_reg.c b/lib/krb5/config_reg.c new file mode 100644 index 000000000..9b1b253c0 --- /dev/null +++ b/lib/krb5/config_reg.c @@ -0,0 +1,346 @@ +/*********************************************************************** + * Copyright (c) 2010, Secure Endpoints Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +#include "krb5_locl.h" + +#ifndef _WIN32 +#error config_reg.c is only for Windows +#endif + +#define REGPATH "SOFTWARE\\Heimdal" + +/** + * Parse a registry value as a configuration value + * + * The following registry value types are handled: + * + * - REG_DWORD: The decimal string representation is used as the + * value. + * + * - REG_SZ: The string is used as-is. + * + * - REG_EXPAND_SZ: Environment variables in the string are expanded + * and the result is used as the value. + * + * - REG_MULTI_SZ: The list of strings is concatenated using a ' ' as + * a separator. No quoting is performed. + * + * Any other value type is rejected. + */ +static krb5_error_code +parse_reg_value(krb5_context context, + HKEY key, const char * valuename, + DWORD type, DWORD cbdata, krb5_config_section ** parent) +{ + LONG rcode; + DWORD cb; + krb5_config_section *value; + krb5_error_code code = 0; + + BYTE static_buffer[16384]; + BYTE *pbuffer; + + /* Size adjustments */ + + switch (type) { + case REG_DWORD: + if (cbdata != sizeof(DWORD)) { + krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT, + "Unexpected size while reading registry value %s", + valuename); + return KRB5_CONFIG_BADFORMAT; + } + break; + + case REG_SZ: + case REG_EXPAND_SZ: + cbdata += sizeof(char); /* Accout for potential missing NUL + * terminator. */ + break; + + case REG_MULTI_SZ: + cbdata += sizeof(char) * 2; + break; + + default: + krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT, + "Unexpected type while reading registry value %s", + valuename); + return KRB5_CONFIG_BADFORMAT; + } + + if (cbdata <= sizeof(static_buffer)) + pbuffer = &static_buffer[0]; + else { + pbuffer = malloc(cbdata); + if (pbuffer == NULL) + return ENOMEM; + } + + cb = cbdata; + + rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb); + if (rcode != ERROR_SUCCESS) { + krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT, + "Unexpected error while reading registry value %s", + valuename); + code = KRB5_CONFIG_BADFORMAT; + goto done; + } + + if (cb > cbdata) { + krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT, + "Unexpected error while reading registry value %s", + valuename); + code = KRB5_CONFIG_BADFORMAT; + goto done; + } + + value = get_entry(parent, valuename, krb5_config_string); + if (value == NULL) { + code = ENOMEM; + goto done; + } + + if (value->u.string != NULL) { + free(value->u.string); + value->u.string = NULL; + } + + switch (type) { + case REG_DWORD: + { + asprintf(&value->u.string, "%d", *((DWORD *) pbuffer)); + } + break; + + case REG_SZ: + { + char * str = (char *) pbuffer; + + if (str[cb - 1] != '\0') + str[cb] = '\0'; + + value->u.string = strdup(str); + } + break; + + case REG_EXPAND_SZ: + { + char *str = (char *) pbuffer; + char expsz[32768]; /* Size of output buffer for + * ExpandEnvironmentStrings() is + * limited to 32K. */ + + if (str[cb - 1] != '\0') + str[cb] = '\0'; + + if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(expsz[0])) != 0) { + value->u.string = strdup(expsz); + } else { + code = KRB5_CONFIG_BADFORMAT; + krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT, + "Overflow while expanding environment strings for registry value %s", valuename); + } + } + break; + + case REG_MULTI_SZ: + { + char * str = (char *) pbuffer; + char * iter; + + str[cbdata - 1] = '\0'; + str[cbdata - 2] = '\0'; + + for (iter = str; *iter;) { + size_t len = strlen(iter); + + iter += len; + if (iter[1] != '\0') + *iter++ = ' '; + else + break; + } + + value->u.string = strdup(str); + } + break; + } + +done: + if (pbuffer != static_buffer && pbuffer != NULL) + free(pbuffer); + + return code; +} + +static krb5_error_code +parse_reg_values(krb5_context context, + HKEY key, + krb5_config_section ** parent) +{ + DWORD index; + LONG rcode; + + for (index = 0; ; index ++) { + char name[16385]; + DWORD cch = sizeof(name)/sizeof(name[0]); + DWORD type; + DWORD cbdata = 0; + krb5_error_code code; + + rcode = RegEnumValue(key, index, name, &cch, NULL, + &type, NULL, &cbdata); + if (rcode != ERROR_SUCCESS) + break; + + if (cbdata == 0) + continue; + + code = parse_reg_value(context, key, name, type, cbdata, parent); + if (code != 0) + return code; + } + + return 0; +} + +static krb5_error_code +parse_reg_subkeys(krb5_context context, + HKEY key, + krb5_config_section ** parent) +{ + DWORD index; + LONG rcode; + + for (index = 0; ; index ++) { + HKEY subkey = NULL; + char name[256]; + DWORD cch = sizeof(name)/sizeof(name[0]); + krb5_config_section *section = NULL; + krb5_error_code code; + + rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL); + if (rcode != ERROR_SUCCESS) + break; + + rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey); + if (rcode != ERROR_SUCCESS) + continue; + + section = get_entry(parent, name, krb5_config_list); + if (section == NULL) { + RegCloseKey(subkey); + return ENOMEM; + } + + code = parse_reg_values(context, subkey, §ion->u.list); + if (code) { + RegCloseKey(subkey); + return code; + } + + code = parse_reg_subkeys(context, subkey, §ion->u.list); + if (code) { + RegCloseKey(subkey); + return code; + } + + RegCloseKey(subkey); + } + + return 0; +} + +static krb5_error_code +parse_reg_root(krb5_context context, + HKEY key, + krb5_config_section ** parent) +{ + krb5_config_section *libdefaults = NULL; + krb5_error_code code = 0; + + libdefaults = get_entry(parent, "libdefaults", krb5_config_list); + if (libdefaults == NULL) { + krb5_set_error_message(context, ENOMEM, "Out of memory while parsing configuration"); + return ENOMEM; + } + + code = parse_reg_values(context, key, &libdefaults->u.list); + if (code) + return code; + + return parse_reg_subkeys(context, key, parent); +} + +/** + * Load configuration from registry + * + * The registry keys 'HKCU\Software\Heimdal' and + * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each + * registry key corresponds to a configuration section (or bound list) + * and each value in a registry key is treated as a bound value. The + * set of values that are directly under the Heimdal key are treated + * as if they were defined in the [libdefaults] section. + * + * @see parse_reg_value() for details about how each type of value is handled. + */ +krb5_error_code +krb5_load_config_from_registry(krb5_context context, + krb5_config_section ** res) +{ + HKEY key = NULL; + LONG rcode; + krb5_error_code code = 0; + + rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGPATH, 0, KEY_READ, &key); + if (rcode == ERROR_SUCCESS) { + code = parse_reg_root(context, key, res); + RegCloseKey(key); + key = NULL; + + if (code) + return code; + } + + rcode = RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH, 0, KEY_READ, &key); + if (rcode == ERROR_SUCCESS) { + code = parse_reg_root(context, key, res); + RegCloseKey(key); + key = NULL; + + if (code) + return code; + } + + return 0; +} diff --git a/lib/krb5/context.c b/lib/krb5/context.c index bab368813..5bde3d236 100644 --- a/lib/krb5/context.c +++ b/lib/krb5/context.c @@ -616,6 +616,11 @@ krb5_set_config_files(krb5_context context, char **filenames) if(tmp == NULL) return ENXIO; #endif + +#ifdef _WIN32 + krb5_load_config_from_registry(context, &tmp); +#endif + krb5_config_file_free(context, context->cf); context->cf = tmp; ret = init_context_from_config_file(context);