etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / config_reg.c
blob169749fea51f37f774710e9361cef8eada6b2074
1 /* $NetBSD: config_reg.c,v 1.1.1.2 2014/04/24 12:45:49 pettai Exp $ */
3 /***********************************************************************
4 * Copyright (c) 2010, Secure Endpoints Inc.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 **********************************************************************/
34 #include "krb5_locl.h"
36 #ifndef _WIN32
37 #error config_reg.c is only for Windows
38 #endif
40 #include <shlwapi.h>
42 #ifndef MAX_DWORD
43 #define MAX_DWORD 0xFFFFFFFF
44 #endif
46 #define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
47 #define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
49 /**
50 * Store a string as a registry value of the specified type
52 * The following registry types are handled:
54 * - REG_DWORD: The string is converted to a number.
56 * - REG_SZ: The string is stored as is.
58 * - REG_EXPAND_SZ: The string is stored as is.
60 * - REG_MULTI_SZ:
62 * . If a separator is specified, the input string is broken
63 * up into multiple strings and stored as a multi-sz.
65 * . If no separator is provided, the input string is stored
66 * as a multi-sz.
68 * - REG_NONE:
70 * . If the string is all numeric, it will be stored as a
71 * REG_DWORD.
73 * . Otherwise, the string is stored as a REG_SZ.
75 * Other types are rejected.
77 * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
78 * otherwise a buffer overrun will occur.
80 * @param [in]valuename Name of the registry value to be modified or created
81 * @param [in]type Type of the value. REG_NONE if unknown
82 * @param [in]data The input string to be stored in the registry.
83 * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
84 * @param [in]separator Separator character for parsing strings.
86 * @retval 0 if success or non-zero on error.
87 * If non-zero is returned, an error message has been set using
88 * krb5_set_error_message().
91 int
92 _krb5_store_string_to_reg_value(krb5_context context,
93 HKEY key, const char * valuename,
94 DWORD type, const char *data, DWORD cb_data,
95 const char * separator)
97 LONG rcode;
98 DWORD dwData;
99 BYTE static_buffer[16384];
100 BYTE *pbuffer = &static_buffer[0];
102 if (data == NULL)
104 if (context)
105 krb5_set_error_message(context, 0,
106 "'data' must not be NULL");
107 return -1;
110 if (cb_data == MAX_DWORD)
112 cb_data = (DWORD)strlen(data) + 1;
114 else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
115 cb_data >= sizeof(static_buffer))
117 if (context)
118 krb5_set_error_message(context, 0, "cb_data too big");
119 return -1;
121 else if (data[cb_data-1] != '\0')
123 memcpy(static_buffer, data, cb_data);
124 static_buffer[cb_data++] = '\0';
125 if (type == REG_MULTI_SZ)
126 static_buffer[cb_data++] = '\0';
127 data = static_buffer;
130 if (type == REG_NONE)
133 * If input is all numeric, convert to DWORD and save as REG_DWORD.
134 * Otherwise, store as REG_SZ.
136 if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
138 type = REG_DWORD;
139 } else {
140 type = REG_SZ;
144 switch (type) {
145 case REG_SZ:
146 case REG_EXPAND_SZ:
147 rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
148 if (rcode)
150 if (context)
151 krb5_set_error_message(context, 0,
152 "Unexpected error when setting registry value %s gle 0x%x",
153 valuename,
154 GetLastError());
155 return -1;
157 break;
158 case REG_MULTI_SZ:
159 if (separator && *separator)
161 int i;
162 char *cp;
164 if (data != static_buffer)
165 static_buffer[cb_data++] = '\0';
167 for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
169 if (*cp == *separator)
170 *cp = '\0';
173 rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
174 if (rcode)
176 if (context)
177 krb5_set_error_message(context, 0,
178 "Unexpected error when setting registry value %s gle 0x%x",
179 valuename,
180 GetLastError());
181 return -1;
184 break;
185 case REG_DWORD:
186 if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
188 if (context)
189 krb5_set_error_message(context, 0,
190 "Unexpected error when parsing %s as number gle 0x%x",
191 data,
192 GetLastError());
195 rcode = RegSetValueEx(key, valuename, 0, type, dwData, sizeof(DWORD));
196 if (rcode)
198 if (context)
199 krb5_set_error_message(context, 0,
200 "Unexpected error when setting registry value %s gle 0x%x",
201 valuename,
202 GetLastError());
203 return -1;
205 break;
206 default:
207 return -1;
210 return 0;
214 * Parse a registry value as a string
216 * @see _krb5_parse_reg_value_as_multi_string()
218 char *
219 _krb5_parse_reg_value_as_string(krb5_context context,
220 HKEY key, const char * valuename,
221 DWORD type, DWORD cb_data)
223 return _krb5_parse_reg_value_as_multi_string(context, key, valuename,
224 type, cb_data, " ");
228 * Parse a registry value as a multi string
230 * The following registry value types are handled:
232 * - REG_DWORD: The decimal string representation is used as the
233 * value.
235 * - REG_SZ: The string is used as-is.
237 * - REG_EXPAND_SZ: Environment variables in the string are expanded
238 * and the result is used as the value.
240 * - REG_MULTI_SZ: The list of strings is concatenated using the
241 * separator. No quoting is performed.
243 * Any other value type is rejected.
245 * @param [in]valuename Name of the registry value to be queried
246 * @param [in]type Type of the value. REG_NONE if unknown
247 * @param [in]cbdata Size of value. 0 if unknown.
248 * @param [in]separator Separator character for concatenating strings.
250 * @a type and @a cbdata are only considered valid if both are
251 * specified.
253 * @retval The registry value string, or NULL if there was an error.
254 * If NULL is returned, an error message has been set using
255 * krb5_set_error_message().
257 char *
258 _krb5_parse_reg_value_as_multi_string(krb5_context context,
259 HKEY key, const char * valuename,
260 DWORD type, DWORD cb_data, char *separator)
262 LONG rcode = ERROR_MORE_DATA;
264 BYTE static_buffer[16384];
265 BYTE *pbuffer = &static_buffer[0];
266 DWORD cb_alloc = sizeof(static_buffer);
267 char *ret_string = NULL;
269 /* If we know a type and cb_data from a previous call to
270 * RegEnumValue(), we use it. Otherwise we use the
271 * static_buffer[] and query directly. We do this to minimize the
272 * number of queries. */
274 if (type == REG_NONE || cb_data == 0) {
276 pbuffer = &static_buffer[0];
277 cb_alloc = cb_data = sizeof(static_buffer);
278 rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
280 if (rcode == ERROR_SUCCESS &&
282 ((type != REG_SZ &&
283 type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
285 (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
286 goto have_data;
288 if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
289 return NULL;
292 /* Either we don't have the data or we aren't sure of the size
293 * (due to potentially missing terminating NULs). */
295 switch (type) {
296 case REG_DWORD:
297 if (cb_data != sizeof(DWORD)) {
298 if (context)
299 krb5_set_error_message(context, 0,
300 "Unexpected size while reading registry value %s",
301 valuename);
302 return NULL;
304 break;
306 case REG_SZ:
307 case REG_EXPAND_SZ:
309 if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
310 goto have_data;
312 cb_data += sizeof(char); /* Accout for potential missing NUL
313 * terminator. */
314 break;
316 case REG_MULTI_SZ:
318 if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
319 (cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
320 goto have_data;
322 cb_data += sizeof(char) * 2; /* Potential missing double NUL
323 * terminator. */
324 break;
326 default:
327 if (context)
328 krb5_set_error_message(context, 0,
329 "Unexpected type while reading registry value %s",
330 valuename);
331 return NULL;
334 if (cb_data <= sizeof(static_buffer))
335 pbuffer = &static_buffer[0];
336 else {
337 pbuffer = malloc(cb_data);
338 if (pbuffer == NULL)
339 return NULL;
342 cb_alloc = cb_data;
343 rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
345 if (rcode != ERROR_SUCCESS) {
347 /* This can potentially be from a race condition. I.e. some
348 * other process or thread went and modified the registry
349 * value between the time we queried its size and queried for
350 * its value. Ideally we would retry the query in a loop. */
352 if (context)
353 krb5_set_error_message(context, 0,
354 "Unexpected error while reading registry value %s",
355 valuename);
356 goto done;
359 if (cb_data > cb_alloc || cb_data == 0) {
360 if (context)
361 krb5_set_error_message(context, 0,
362 "Unexpected size while reading registry value %s",
363 valuename);
364 goto done;
367 have_data:
368 switch (type) {
369 case REG_DWORD:
370 asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
371 break;
373 case REG_SZ:
375 char * str = (char *) pbuffer;
377 if (str[cb_data - 1] != '\0') {
378 if (cb_data < cb_alloc)
379 str[cb_data] = '\0';
380 else
381 break;
384 if (pbuffer != static_buffer) {
385 ret_string = (char *) pbuffer;
386 pbuffer = NULL;
387 } else {
388 ret_string = strdup((char *) pbuffer);
391 break;
393 case REG_EXPAND_SZ:
395 char *str = (char *) pbuffer;
396 char expsz[32768]; /* Size of output buffer for
397 * ExpandEnvironmentStrings() is
398 * limited to 32K. */
400 if (str[cb_data - 1] != '\0') {
401 if (cb_data < cb_alloc)
402 str[cb_data] = '\0';
403 else
404 break;
407 if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
408 ret_string = strdup(expsz);
409 } else {
410 if (context)
411 krb5_set_error_message(context, 0,
412 "Overflow while expanding environment strings "
413 "for registry value %s", valuename);
416 break;
418 case REG_MULTI_SZ:
420 char * str = (char *) pbuffer;
421 char * iter;
423 str[cb_alloc - 1] = '\0';
424 str[cb_alloc - 2] = '\0';
426 for (iter = str; *iter;) {
427 size_t len = strlen(iter);
429 iter += len;
430 if (iter[1] != '\0')
431 *iter++ = *separator;
432 else
433 break;
436 if (pbuffer != static_buffer) {
437 ret_string = str;
438 pbuffer = NULL;
439 } else {
440 ret_string = strdup(str);
443 break;
445 default:
446 if (context)
447 krb5_set_error_message(context, 0,
448 "Unexpected type while reading registry value %s",
449 valuename);
452 done:
453 if (pbuffer != static_buffer && pbuffer != NULL)
454 free(pbuffer);
456 return ret_string;
460 * Parse a registry value as a configuration value
462 * @see parse_reg_value_as_string()
464 static krb5_error_code
465 parse_reg_value(krb5_context context,
466 HKEY key, const char * valuename,
467 DWORD type, DWORD cbdata, krb5_config_section ** parent)
469 char *reg_string = NULL;
470 krb5_config_section *value;
471 krb5_error_code code = 0;
473 reg_string = _krb5_parse_reg_value_as_string(context, key, valuename, type, cbdata);
475 if (reg_string == NULL)
476 return KRB5_CONFIG_BADFORMAT;
478 value = _krb5_config_get_entry(parent, valuename, krb5_config_string);
479 if (value == NULL) {
480 code = ENOMEM;
481 goto done;
484 if (value->u.string != NULL)
485 free(value->u.string);
487 value->u.string = reg_string;
488 reg_string = NULL;
490 done:
491 if (reg_string != NULL)
492 free(reg_string);
494 return code;
497 static krb5_error_code
498 parse_reg_values(krb5_context context,
499 HKEY key,
500 krb5_config_section ** parent)
502 DWORD index;
503 LONG rcode;
505 for (index = 0; ; index ++) {
506 char name[16385];
507 DWORD cch = sizeof(name)/sizeof(name[0]);
508 DWORD type;
509 DWORD cbdata = 0;
510 krb5_error_code code;
512 rcode = RegEnumValue(key, index, name, &cch, NULL,
513 &type, NULL, &cbdata);
514 if (rcode != ERROR_SUCCESS)
515 break;
517 if (cbdata == 0)
518 continue;
520 code = parse_reg_value(context, key, name, type, cbdata, parent);
521 if (code != 0)
522 return code;
525 return 0;
528 static krb5_error_code
529 parse_reg_subkeys(krb5_context context,
530 HKEY key,
531 krb5_config_section ** parent)
533 DWORD index;
534 LONG rcode;
536 for (index = 0; ; index ++) {
537 HKEY subkey = NULL;
538 char name[256];
539 DWORD cch = sizeof(name)/sizeof(name[0]);
540 krb5_config_section *section = NULL;
541 krb5_error_code code;
543 rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
544 if (rcode != ERROR_SUCCESS)
545 break;
547 rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
548 if (rcode != ERROR_SUCCESS)
549 continue;
551 section = _krb5_config_get_entry(parent, name, krb5_config_list);
552 if (section == NULL) {
553 RegCloseKey(subkey);
554 return ENOMEM;
557 code = parse_reg_values(context, subkey, &section->u.list);
558 if (code) {
559 RegCloseKey(subkey);
560 return code;
563 code = parse_reg_subkeys(context, subkey, &section->u.list);
564 if (code) {
565 RegCloseKey(subkey);
566 return code;
569 RegCloseKey(subkey);
572 return 0;
575 static krb5_error_code
576 parse_reg_root(krb5_context context,
577 HKEY key,
578 krb5_config_section ** parent)
580 krb5_config_section *libdefaults = NULL;
581 krb5_error_code code = 0;
583 libdefaults = _krb5_config_get_entry(parent, "libdefaults", krb5_config_list);
584 if (libdefaults == NULL) {
585 krb5_set_error_message(context, ENOMEM, "Out of memory while parsing configuration");
586 return ENOMEM;
589 code = parse_reg_values(context, key, &libdefaults->u.list);
590 if (code)
591 return code;
593 return parse_reg_subkeys(context, key, parent);
596 static krb5_error_code
597 load_config_from_regpath(krb5_context context,
598 HKEY hk_root,
599 const char* key_path,
600 krb5_config_section ** res)
602 HKEY key = NULL;
603 LONG rcode;
604 krb5_error_code code = 0;
606 rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
607 if (rcode == ERROR_SUCCESS) {
608 code = parse_reg_root(context, key, res);
609 RegCloseKey(key);
610 key = NULL;
613 return code;
617 * Load configuration from registry
619 * The registry keys 'HKCU\Software\Heimdal' and
620 * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
621 * registry key corresponds to a configuration section (or bound list)
622 * and each value in a registry key is treated as a bound value. The
623 * set of values that are directly under the Heimdal key are treated
624 * as if they were defined in the [libdefaults] section.
626 * @see parse_reg_value() for details about how each type of value is handled.
628 krb5_error_code
629 _krb5_load_config_from_registry(krb5_context context,
630 krb5_config_section ** res)
632 krb5_error_code code;
634 code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
635 REGPATH_KERBEROS, res);
636 if (code)
637 return code;
639 code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
640 REGPATH_HEIMDAL, res);
641 if (code)
642 return code;
644 code = load_config_from_regpath(context, HKEY_CURRENT_USER,
645 REGPATH_KERBEROS, res);
646 if (code)
647 return code;
649 code = load_config_from_regpath(context, HKEY_CURRENT_USER,
650 REGPATH_HEIMDAL, res);
651 if (code)
652 return code;
653 return 0;