base: rename heim_base_atomic_{max,type} to ...integer_{max,type}
[heimdal.git] / lib / base / config_reg.c
blobcb24e5043d64b9561ca23f1204bd2d13c7801a88
1 /***********************************************************************
2 * Copyright (c) 2010, Secure Endpoints Inc.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 **********************************************************************/
32 #include "baselocl.h"
34 #ifndef _WIN32
35 #error config_reg.c is only for Windows
36 #endif
38 #include <shlwapi.h>
40 #ifndef MAX_DWORD
41 #define MAX_DWORD 0xFFFFFFFF
42 #endif
44 /**
45 * Store a string as a registry value of the specified type
47 * The following registry types are handled:
49 * - REG_DWORD: The string is converted to a number.
51 * - REG_SZ: The string is stored as is.
53 * - REG_EXPAND_SZ: The string is stored as is.
55 * - REG_MULTI_SZ:
57 * . If a separator is specified, the input string is broken
58 * up into multiple strings and stored as a multi-sz.
60 * . If no separator is provided, the input string is stored
61 * as a multi-sz.
63 * - REG_NONE:
65 * . If the string is all numeric, it will be stored as a
66 * REG_DWORD.
68 * . Otherwise, the string is stored as a REG_SZ.
70 * Other types are rejected.
72 * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
73 * otherwise a buffer overrun will occur.
75 * @param [in]valuename Name of the registry value to be modified or created
76 * @param [in]type Type of the value. REG_NONE if unknown
77 * @param [in]data The input string to be stored in the registry.
78 * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
79 * @param [in]separator Separator character for parsing strings.
81 * @retval 0 if success or non-zero on error.
82 * If non-zero is returned, an error message has been set using
83 * heim_set_error_message().
86 int
87 heim_store_string_to_reg_value(heim_context context,
88 HKEY key, const char *valuename,
89 DWORD type, const char *data, DWORD cb_data,
90 const char *separator)
92 LONG rcode;
93 int dwData;
94 BYTE static_buffer[16384];
96 if (data == NULL)
98 if (context)
99 heim_set_error_message(context, 0,
100 "'data' must not be NULL");
101 return -1;
104 if (cb_data == MAX_DWORD)
106 cb_data = (DWORD)strlen(data) + 1;
108 else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
109 cb_data >= sizeof(static_buffer))
111 if (context)
112 heim_set_error_message(context, 0, "cb_data too big");
113 return -1;
115 else if (data[cb_data-1] != '\0')
117 memcpy(static_buffer, data, cb_data);
118 static_buffer[cb_data++] = '\0';
119 if (type == REG_MULTI_SZ)
120 static_buffer[cb_data++] = '\0';
121 data = static_buffer;
124 if (type == REG_NONE)
127 * If input is all numeric, convert to DWORD and save as REG_DWORD.
128 * Otherwise, store as REG_SZ.
130 if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
132 type = REG_DWORD;
133 } else {
134 type = REG_SZ;
138 switch (type) {
139 case REG_SZ:
140 case REG_EXPAND_SZ:
141 rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
142 if (rcode)
144 if (context)
145 heim_set_error_message(context, 0,
146 "Unexpected error when setting registry value %s gle 0x%x",
147 valuename,
148 GetLastError());
149 return -1;
151 break;
152 case REG_MULTI_SZ:
153 if (separator && *separator)
155 char *cp;
157 if (data != static_buffer)
158 static_buffer[cb_data++] = '\0';
160 for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
162 if (*cp == *separator)
163 *cp = '\0';
166 rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
167 if (rcode)
169 if (context)
170 heim_set_error_message(context, 0,
171 "Unexpected error when setting registry value %s gle 0x%x",
172 valuename,
173 GetLastError());
174 return -1;
177 break;
178 case REG_DWORD:
179 if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
181 if (context)
182 heim_set_error_message(context, 0,
183 "Unexpected error when parsing %s as number gle 0x%x",
184 data,
185 GetLastError());
188 rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD));
189 if (rcode)
191 if (context)
192 heim_set_error_message(context, 0,
193 "Unexpected error when setting registry value %s gle 0x%x",
194 valuename,
195 GetLastError());
196 return -1;
198 break;
199 default:
200 return -1;
203 return 0;
207 * Parse a registry value as a string
209 * @see heim_parse_reg_value_as_multi_string()
211 char *
212 heim_parse_reg_value_as_string(heim_context context,
213 HKEY key, const char * valuename,
214 DWORD type, DWORD cb_data)
216 return heim_parse_reg_value_as_multi_string(context, key, valuename,
217 type, cb_data, " ");
221 * Parse a registry value as a multi string
223 * The following registry value types are handled:
225 * - REG_DWORD: The decimal string representation is used as the
226 * value.
228 * - REG_SZ: The string is used as-is.
230 * - REG_EXPAND_SZ: Environment variables in the string are expanded
231 * and the result is used as the value.
233 * - REG_MULTI_SZ: The list of strings is concatenated using the
234 * separator. No quoting is performed.
236 * Any other value type is rejected.
238 * @param [in]valuename Name of the registry value to be queried
239 * @param [in]type Type of the value. REG_NONE if unknown
240 * @param [in]cbdata Size of value. 0 if unknown.
241 * @param [in]separator Separator character for concatenating strings.
243 * @a type and @a cbdata are only considered valid if both are
244 * specified.
246 * @retval The registry value string, or NULL if there was an error.
247 * If NULL is returned, an error message has been set using
248 * heim_set_error_message().
250 char *
251 heim_parse_reg_value_as_multi_string(heim_context context,
252 HKEY key, const char * valuename,
253 DWORD type, DWORD cb_data, char *separator)
255 LONG rcode = ERROR_MORE_DATA;
257 BYTE static_buffer[16384];
258 BYTE *pbuffer = &static_buffer[0];
259 DWORD cb_alloc = sizeof(static_buffer);
260 char *ret_string = NULL;
262 /* If we know a type and cb_data from a previous call to
263 * RegEnumValue(), we use it. Otherwise we use the
264 * static_buffer[] and query directly. We do this to minimize the
265 * number of queries. */
267 if (type == REG_NONE || cb_data == 0) {
269 pbuffer = &static_buffer[0];
270 cb_alloc = cb_data = sizeof(static_buffer);
271 rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
273 if (rcode == ERROR_SUCCESS &&
275 ((type != REG_SZ &&
276 type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
278 (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
279 goto have_data;
281 if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
282 return NULL;
285 /* Either we don't have the data or we aren't sure of the size
286 * (due to potentially missing terminating NULs). */
288 switch (type) {
289 case REG_DWORD:
290 if (cb_data != sizeof(DWORD)) {
291 if (context)
292 heim_set_error_message(context, 0,
293 "Unexpected size while reading registry value %s",
294 valuename);
295 return NULL;
297 break;
299 case REG_SZ:
300 case REG_EXPAND_SZ:
302 if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
303 goto have_data;
305 cb_data += sizeof(char); /* Accout for potential missing NUL
306 * terminator. */
307 break;
309 case REG_MULTI_SZ:
311 if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
312 (cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
313 goto have_data;
315 cb_data += sizeof(char) * 2; /* Potential missing double NUL
316 * terminator. */
317 break;
319 default:
320 if (context)
321 heim_set_error_message(context, 0,
322 "Unexpected type while reading registry value %s",
323 valuename);
324 return NULL;
327 if (cb_data <= sizeof(static_buffer))
328 pbuffer = &static_buffer[0];
329 else {
330 pbuffer = malloc(cb_data);
331 if (pbuffer == NULL)
332 return NULL;
335 cb_alloc = cb_data;
336 rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
338 if (rcode != ERROR_SUCCESS) {
340 /* This can potentially be from a race condition. I.e. some
341 * other process or thread went and modified the registry
342 * value between the time we queried its size and queried for
343 * its value. Ideally we would retry the query in a loop. */
345 if (context)
346 heim_set_error_message(context, 0,
347 "Unexpected error while reading registry value %s",
348 valuename);
349 goto done;
352 if (cb_data > cb_alloc || cb_data == 0) {
353 if (context)
354 heim_set_error_message(context, 0,
355 "Unexpected size while reading registry value %s",
356 valuename);
357 goto done;
360 have_data:
361 switch (type) {
362 case REG_DWORD:
363 asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
364 break;
366 case REG_SZ:
368 char * str = (char *) pbuffer;
370 if (str[cb_data - 1] != '\0') {
371 if (cb_data < cb_alloc)
372 str[cb_data] = '\0';
373 else
374 break;
377 if (pbuffer != static_buffer) {
378 ret_string = (char *) pbuffer;
379 pbuffer = NULL;
380 } else {
381 ret_string = strdup((char *) pbuffer);
384 break;
386 case REG_EXPAND_SZ:
388 char *str = (char *) pbuffer;
389 char expsz[32768]; /* Size of output buffer for
390 * ExpandEnvironmentStrings() is
391 * limited to 32K. */
393 if (str[cb_data - 1] != '\0') {
394 if (cb_data < cb_alloc)
395 str[cb_data] = '\0';
396 else
397 break;
400 if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
401 ret_string = strdup(expsz);
402 } else {
403 if (context)
404 heim_set_error_message(context, 0,
405 "Overflow while expanding environment strings "
406 "for registry value %s", valuename);
409 break;
411 case REG_MULTI_SZ:
413 char * str = (char *) pbuffer;
414 char * iter;
416 str[cb_alloc - 1] = '\0';
417 str[cb_alloc - 2] = '\0';
419 for (iter = str; *iter;) {
420 size_t len = strlen(iter);
422 iter += len;
423 if (iter[1] != '\0')
424 *iter++ = *separator;
425 else
426 break;
429 if (pbuffer != static_buffer) {
430 ret_string = str;
431 pbuffer = NULL;
432 } else {
433 ret_string = strdup(str);
436 break;
438 default:
439 if (context)
440 heim_set_error_message(context, 0,
441 "Unexpected type while reading registry value %s",
442 valuename);
445 done:
446 if (pbuffer != static_buffer && pbuffer != NULL)
447 free(pbuffer);
449 return ret_string;
453 * Parse a registry value as a configuration value
455 * @see parse_reg_value_as_string()
457 static heim_error_code
458 parse_reg_value(heim_context context,
459 HKEY key, const char * valuename,
460 DWORD type, DWORD cbdata, heim_config_section ** parent)
462 char *reg_string = NULL;
463 heim_config_section *value;
464 heim_error_code code = 0;
466 reg_string = heim_parse_reg_value_as_string(context, key, valuename, type, cbdata);
468 if (reg_string == NULL)
469 return HEIM_ERR_CONFIG_BADFORMAT;
471 value = heim_config_get_entry(parent, valuename, heim_config_string);
472 if (value == NULL) {
473 code = ENOMEM;
474 goto done;
477 if (value->u.string != NULL)
478 free(value->u.string);
480 value->u.string = reg_string;
481 reg_string = NULL;
483 done:
484 if (reg_string != NULL)
485 free(reg_string);
487 return code;
490 static heim_error_code
491 parse_reg_values(heim_context context,
492 HKEY key,
493 heim_config_section ** parent)
495 DWORD index;
496 LONG rcode;
498 for (index = 0; ; index ++) {
499 char name[16385];
500 DWORD cch = sizeof(name)/sizeof(name[0]);
501 DWORD type;
502 DWORD cbdata = 0;
503 heim_error_code code;
505 rcode = RegEnumValue(key, index, name, &cch, NULL,
506 &type, NULL, &cbdata);
507 if (rcode != ERROR_SUCCESS)
508 break;
510 if (cbdata == 0)
511 continue;
513 code = parse_reg_value(context, key, name, type, cbdata, parent);
514 if (code != 0)
515 return code;
518 return 0;
521 static heim_error_code
522 parse_reg_subkeys(heim_context context,
523 HKEY key,
524 heim_config_section ** parent)
526 DWORD index;
527 LONG rcode;
529 for (index = 0; ; index ++) {
530 HKEY subkey = NULL;
531 char name[256];
532 DWORD cch = sizeof(name)/sizeof(name[0]);
533 heim_config_section *section = NULL;
534 heim_error_code code;
536 rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
537 if (rcode != ERROR_SUCCESS)
538 break;
540 rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
541 if (rcode != ERROR_SUCCESS)
542 continue;
544 section = heim_config_get_entry(parent, name, heim_config_list);
545 if (section == NULL) {
546 RegCloseKey(subkey);
547 return ENOMEM;
550 code = parse_reg_values(context, subkey, &section->u.list);
551 if (code) {
552 RegCloseKey(subkey);
553 return code;
556 code = parse_reg_subkeys(context, subkey, &section->u.list);
557 if (code) {
558 RegCloseKey(subkey);
559 return code;
562 RegCloseKey(subkey);
565 return 0;
568 static heim_error_code
569 parse_reg_root(heim_context context,
570 HKEY key,
571 heim_config_section ** parent)
573 heim_config_section *libdefaults = NULL;
574 heim_error_code code = 0;
576 libdefaults = heim_config_get_entry(parent, "libdefaults", heim_config_list);
577 if (libdefaults == NULL)
578 return heim_enomem(context);
580 code = parse_reg_values(context, key, &libdefaults->u.list);
581 if (code)
582 return code;
584 return parse_reg_subkeys(context, key, parent);
587 static heim_error_code
588 load_config_from_regpath(heim_context context,
589 HKEY hk_root,
590 const char* key_path,
591 heim_config_section ** res)
593 HKEY key = NULL;
594 LONG rcode;
595 heim_error_code code = 0;
597 rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
598 if (rcode == ERROR_SUCCESS) {
599 code = parse_reg_root(context, key, res);
600 RegCloseKey(key);
601 key = NULL;
604 return code;
608 * Load configuration from registry
610 * The registry keys 'HKCU\Software\Heimdal' and
611 * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
612 * registry key corresponds to a configuration section (or bound list)
613 * and each value in a registry key is treated as a bound value. The
614 * set of values that are directly under the Heimdal key are treated
615 * as if they were defined in the [libdefaults] section.
617 * @see parse_reg_value() for details about how each type of value is handled.
619 heim_error_code
620 heim_load_config_from_registry(heim_context context,
621 const char *path0,
622 const char *path1,
623 heim_config_section **res)
625 heim_error_code code;
627 if (!path0 && !path1)
628 return EINVAL;
630 if (path0) {
631 code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
632 path0, res);
633 if (code)
634 return code;
637 if (path1) {
638 code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
639 path1, res);
640 if (code)
641 return code;
644 if (path0) {
645 code = load_config_from_regpath(context, HKEY_CURRENT_USER,
646 path0, res);
647 if (code)
648 return code;
651 if (path0) {
652 code = load_config_from_regpath(context, HKEY_CURRENT_USER,
653 path1, res);
654 if (code)
655 return code;
657 return 0;