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.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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"
37 #error config_reg.c is only for Windows
43 #define MAX_DWORD 0xFFFFFFFF
46 #define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
47 #define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
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.
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
70 * . If the string is all numeric, it will be stored as a
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().
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
)
99 BYTE static_buffer
[16384];
100 BYTE
*pbuffer
= &static_buffer
[0];
105 krb5_set_error_message(context
, 0,
106 "'data' must not be NULL");
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
))
118 krb5_set_error_message(context
, 0, "cb_data too big");
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
) )
147 rcode
= RegSetValueEx(key
, valuename
, 0, type
, data
, cb_data
);
151 krb5_set_error_message(context
, 0,
152 "Unexpected error when setting registry value %s gle 0x%x",
159 if (separator
&& *separator
)
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
)
173 rcode
= RegSetValueEx(key
, valuename
, 0, type
, data
, cb_data
);
177 krb5_set_error_message(context
, 0,
178 "Unexpected error when setting registry value %s gle 0x%x",
186 if ( !StrToIntExA( data
, STIF_SUPPORT_HEX
, &dwData
) )
189 krb5_set_error_message(context
, 0,
190 "Unexpected error when parsing %s as number gle 0x%x",
195 rcode
= RegSetValueEx(key
, valuename
, 0, type
, dwData
, sizeof(DWORD
));
199 krb5_set_error_message(context
, 0,
200 "Unexpected error when setting registry value %s gle 0x%x",
214 * Parse a registry value as a string
216 * @see _krb5_parse_reg_value_as_multi_string()
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
,
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
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
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().
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
&&
283 type
!= REG_EXPAND_SZ
) || cb_data
+ 1 <= sizeof(static_buffer
)) &&
285 (type
!= REG_MULTI_SZ
|| cb_data
+ 2 <= sizeof(static_buffer
)))
288 if (rcode
!= ERROR_MORE_DATA
&& rcode
!= ERROR_SUCCESS
)
292 /* Either we don't have the data or we aren't sure of the size
293 * (due to potentially missing terminating NULs). */
297 if (cb_data
!= sizeof(DWORD
)) {
299 krb5_set_error_message(context
, 0,
300 "Unexpected size while reading registry value %s",
309 if (rcode
== ERROR_SUCCESS
&& cb_data
> 0 && pbuffer
[cb_data
- 1] == '\0')
312 cb_data
+= sizeof(char); /* Accout for potential missing NUL
318 if (rcode
== ERROR_SUCCESS
&& cb_data
> 0 && pbuffer
[cb_data
- 1] == '\0' &&
319 (cb_data
== 1 || pbuffer
[cb_data
- 2] == '\0'))
322 cb_data
+= sizeof(char) * 2; /* Potential missing double NUL
328 krb5_set_error_message(context
, 0,
329 "Unexpected type while reading registry value %s",
334 if (cb_data
<= sizeof(static_buffer
))
335 pbuffer
= &static_buffer
[0];
337 pbuffer
= malloc(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. */
353 krb5_set_error_message(context
, 0,
354 "Unexpected error while reading registry value %s",
359 if (cb_data
> cb_alloc
|| cb_data
== 0) {
361 krb5_set_error_message(context
, 0,
362 "Unexpected size while reading registry value %s",
370 asprintf(&ret_string
, "%d", *((DWORD
*) pbuffer
));
375 char * str
= (char *) pbuffer
;
377 if (str
[cb_data
- 1] != '\0') {
378 if (cb_data
< cb_alloc
)
384 if (pbuffer
!= static_buffer
) {
385 ret_string
= (char *) pbuffer
;
388 ret_string
= strdup((char *) pbuffer
);
395 char *str
= (char *) pbuffer
;
396 char expsz
[32768]; /* Size of output buffer for
397 * ExpandEnvironmentStrings() is
400 if (str
[cb_data
- 1] != '\0') {
401 if (cb_data
< cb_alloc
)
407 if (ExpandEnvironmentStrings(str
, expsz
, sizeof(expsz
)/sizeof(char)) != 0) {
408 ret_string
= strdup(expsz
);
411 krb5_set_error_message(context
, 0,
412 "Overflow while expanding environment strings "
413 "for registry value %s", valuename
);
420 char * str
= (char *) pbuffer
;
423 str
[cb_alloc
- 1] = '\0';
424 str
[cb_alloc
- 2] = '\0';
426 for (iter
= str
; *iter
;) {
427 size_t len
= strlen(iter
);
431 *iter
++ = *separator
;
436 if (pbuffer
!= static_buffer
) {
440 ret_string
= strdup(str
);
447 krb5_set_error_message(context
, 0,
448 "Unexpected type while reading registry value %s",
453 if (pbuffer
!= static_buffer
&& pbuffer
!= NULL
)
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
);
484 if (value
->u
.string
!= NULL
)
485 free(value
->u
.string
);
487 value
->u
.string
= reg_string
;
491 if (reg_string
!= NULL
)
497 static krb5_error_code
498 parse_reg_values(krb5_context context
,
500 krb5_config_section
** parent
)
505 for (index
= 0; ; index
++) {
507 DWORD cch
= sizeof(name
)/sizeof(name
[0]);
510 krb5_error_code code
;
512 rcode
= RegEnumValue(key
, index
, name
, &cch
, NULL
,
513 &type
, NULL
, &cbdata
);
514 if (rcode
!= ERROR_SUCCESS
)
520 code
= parse_reg_value(context
, key
, name
, type
, cbdata
, parent
);
528 static krb5_error_code
529 parse_reg_subkeys(krb5_context context
,
531 krb5_config_section
** parent
)
536 for (index
= 0; ; index
++) {
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
)
547 rcode
= RegOpenKeyEx(key
, name
, 0, KEY_READ
, &subkey
);
548 if (rcode
!= ERROR_SUCCESS
)
551 section
= _krb5_config_get_entry(parent
, name
, krb5_config_list
);
552 if (section
== NULL
) {
557 code
= parse_reg_values(context
, subkey
, §ion
->u
.list
);
563 code
= parse_reg_subkeys(context
, subkey
, §ion
->u
.list
);
575 static krb5_error_code
576 parse_reg_root(krb5_context context
,
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");
589 code
= parse_reg_values(context
, key
, &libdefaults
->u
.list
);
593 return parse_reg_subkeys(context
, key
, parent
);
596 static krb5_error_code
597 load_config_from_regpath(krb5_context context
,
599 const char* key_path
,
600 krb5_config_section
** res
)
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
);
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.
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
);
639 code
= load_config_from_regpath(context
, HKEY_LOCAL_MACHINE
,
640 REGPATH_HEIMDAL
, res
);
644 code
= load_config_from_regpath(context
, HKEY_CURRENT_USER
,
645 REGPATH_KERBEROS
, res
);
649 code
= load_config_from_regpath(context
, HKEY_CURRENT_USER
,
650 REGPATH_HEIMDAL
, res
);