#include "krb5_locl.h"
#ifndef _WIN32
#error config_reg.c is only for Windows
#endif
#include <shlwapi.h>
#ifndef MAX_DWORD
#define MAX_DWORD 0xFFFFFFFF
#endif
#define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
#define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
int
_krb5_store_string_to_reg_value(krb5_context context,
HKEY key, const char * valuename,
DWORD type, const char *data, DWORD cb_data,
const char * separator)
{
LONG rcode;
DWORD dwData;
BYTE static_buffer[16384];
BYTE *pbuffer = &static_buffer[0];
if (data == NULL)
{
if (context)
krb5_set_error_message(context, 0,
"'data' must not be NULL");
return -1;
}
if (cb_data == MAX_DWORD)
{
cb_data = (DWORD)strlen(data) + 1;
}
else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
cb_data >= sizeof(static_buffer))
{
if (context)
krb5_set_error_message(context, 0, "cb_data too big");
return -1;
}
else if (data[cb_data-1] != '\0')
{
memcpy(static_buffer, data, cb_data);
static_buffer[cb_data++] = '\0';
if (type == REG_MULTI_SZ)
static_buffer[cb_data++] = '\0';
data = static_buffer;
}
if (type == REG_NONE)
{
if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
{
type = REG_DWORD;
} else {
type = REG_SZ;
}
}
switch (type) {
case REG_SZ:
case REG_EXPAND_SZ:
rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
if (rcode)
{
if (context)
krb5_set_error_message(context, 0,
"Unexpected error when setting registry value %s gle 0x%x",
valuename,
GetLastError());
return -1;
}
break;
case REG_MULTI_SZ:
if (separator && *separator)
{
int i;
char *cp;
if (data != static_buffer)
static_buffer[cb_data++] = '\0';
for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
{
if (*cp == *separator)
*cp = '\0';
}
rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
if (rcode)
{
if (context)
krb5_set_error_message(context, 0,
"Unexpected error when setting registry value %s gle 0x%x",
valuename,
GetLastError());
return -1;
}
}
break;
case REG_DWORD:
if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
{
if (context)
krb5_set_error_message(context, 0,
"Unexpected error when parsing %s as number gle 0x%x",
data,
GetLastError());
}
rcode = RegSetValueEx(key, valuename, 0, type, dwData, sizeof(DWORD));
if (rcode)
{
if (context)
krb5_set_error_message(context, 0,
"Unexpected error when setting registry value %s gle 0x%x",
valuename,
GetLastError());
return -1;
}
break;
default:
return -1;
}
return 0;
}
char *
_krb5_parse_reg_value_as_string(krb5_context context,
HKEY key, const char * valuename,
DWORD type, DWORD cb_data)
{
return _krb5_parse_reg_value_as_multi_string(context, key, valuename,
type, cb_data, " ");
}
char *
_krb5_parse_reg_value_as_multi_string(krb5_context context,
HKEY key, const char * valuename,
DWORD type, DWORD cb_data, char *separator)
{
LONG rcode = ERROR_MORE_DATA;
BYTE static_buffer[16384];
BYTE *pbuffer = &static_buffer[0];
DWORD cb_alloc = sizeof(static_buffer);
char *ret_string = NULL;
if (type == REG_NONE || cb_data == 0) {
pbuffer = &static_buffer[0];
cb_alloc = cb_data = sizeof(static_buffer);
rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
if (rcode == ERROR_SUCCESS &&
((type != REG_SZ &&
type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
(type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
goto have_data;
if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
return NULL;
}
switch (type) {
case REG_DWORD:
if (cb_data != sizeof(DWORD)) {
if (context)
krb5_set_error_message(context, 0,
"Unexpected size while reading registry value %s",
valuename);
return NULL;
}
break;
case REG_SZ:
case REG_EXPAND_SZ:
if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
goto have_data;
cb_data += sizeof(char);
break;
case REG_MULTI_SZ:
if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
(cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
goto have_data;
cb_data += sizeof(char) * 2;
break;
default:
if (context)
krb5_set_error_message(context, 0,
"Unexpected type while reading registry value %s",
valuename);
return NULL;
}
if (cb_data <= sizeof(static_buffer))
pbuffer = &static_buffer[0];
else {
pbuffer = malloc(cb_data);
if (pbuffer == NULL)
return NULL;
}
cb_alloc = cb_data;
rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
if (rcode != ERROR_SUCCESS) {
if (context)
krb5_set_error_message(context, 0,
"Unexpected error while reading registry value %s",
valuename);
goto done;
}
if (cb_data > cb_alloc || cb_data == 0) {
if (context)
krb5_set_error_message(context, 0,
"Unexpected size while reading registry value %s",
valuename);
goto done;
}
have_data:
switch (type) {
case REG_DWORD:
asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
break;
case REG_SZ:
{
char * str = (char *) pbuffer;
if (str[cb_data - 1] != '\0') {
if (cb_data < cb_alloc)
str[cb_data] = '\0';
else
break;
}
if (pbuffer != static_buffer) {
ret_string = (char *) pbuffer;
pbuffer = NULL;
} else {
ret_string = strdup((char *) pbuffer);
}
}
break;
case REG_EXPAND_SZ:
{
char *str = (char *) pbuffer;
char expsz[32768];
if (str[cb_data - 1] != '\0') {
if (cb_data < cb_alloc)
str[cb_data] = '\0';
else
break;
}
if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
ret_string = strdup(expsz);
} else {
if (context)
krb5_set_error_message(context, 0,
"Overflow while expanding environment strings "
"for registry value %s", valuename);
}
}
break;
case REG_MULTI_SZ:
{
char * str = (char *) pbuffer;
char * iter;
str[cb_alloc - 1] = '\0';
str[cb_alloc - 2] = '\0';
for (iter = str; *iter;) {
size_t len = strlen(iter);
iter += len;
if (iter[1] != '\0')
*iter++ = *separator;
else
break;
}
if (pbuffer != static_buffer) {
ret_string = str;
pbuffer = NULL;
} else {
ret_string = strdup(str);
}
}
break;
default:
if (context)
krb5_set_error_message(context, 0,
"Unexpected type while reading registry value %s",
valuename);
}
done:
if (pbuffer != static_buffer && pbuffer != NULL)
free(pbuffer);
return ret_string;
}
static krb5_error_code
parse_reg_value(krb5_context context,
HKEY key, const char * valuename,
DWORD type, DWORD cbdata, krb5_config_section ** parent)
{
char *reg_string = NULL;
krb5_config_section *value;
krb5_error_code code = 0;
reg_string = _krb5_parse_reg_value_as_string(context, key, valuename, type, cbdata);
if (reg_string == NULL)
return KRB5_CONFIG_BADFORMAT;
value = _krb5_config_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 = reg_string;
reg_string = NULL;
done:
if (reg_string != NULL)
free(reg_string);
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 = _krb5_config_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 = _krb5_config_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);
}
static krb5_error_code
load_config_from_regpath(krb5_context context,
HKEY hk_root,
const char* key_path,
krb5_config_section ** res)
{
HKEY key = NULL;
LONG rcode;
krb5_error_code code = 0;
rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
if (rcode == ERROR_SUCCESS) {
code = parse_reg_root(context, key, res);
RegCloseKey(key);
key = NULL;
}
return code;
}
krb5_error_code
_krb5_load_config_from_registry(krb5_context context,
krb5_config_section ** res)
{
krb5_error_code code;
code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
REGPATH_KERBEROS, res);
if (code)
return code;
code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
REGPATH_HEIMDAL, res);
if (code)
return code;
code = load_config_from_regpath(context, HKEY_CURRENT_USER,
REGPATH_KERBEROS, res);
if (code)
return code;
code = load_config_from_regpath(context, HKEY_CURRENT_USER,
REGPATH_HEIMDAL, res);
if (code)
return code;
return 0;
}