1 /***********************************************************************
2 * Copyright (c) 2010, Secure Endpoints Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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 "krb5_locl.h"
35 #error config_reg.c is only for Windows
41 #define MAX_DWORD 0xFFFFFFFF
44 #define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
45 #define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
48 * Store a string as a registry value of the specified type
50 * The following registry types are handled:
52 * - REG_DWORD: The string is converted to a number.
54 * - REG_SZ: The string is stored as is.
56 * - REG_EXPAND_SZ: The string is stored as is.
60 * . If a separator is specified, the input string is broken
61 * up into multiple strings and stored as a multi-sz.
63 * . If no separator is provided, the input string is stored
68 * . If the string is all numeric, it will be stored as a
71 * . Otherwise, the string is stored as a REG_SZ.
73 * Other types are rejected.
75 * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
76 * otherwise a buffer overrun will occur.
78 * @param [in]valuename Name of the registry value to be modified or created
79 * @param [in]type Type of the value. REG_NONE if unknown
80 * @param [in]data The input string to be stored in the registry.
81 * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
82 * @param [in]separator Separator character for parsing strings.
84 * @retval 0 if success or non-zero on error.
85 * If non-zero is returned, an error message has been set using
86 * krb5_set_error_message().
89 KRB5_LIB_FUNCTION
int KRB5_LIB_CALL
90 _krb5_store_string_to_reg_value(krb5_context context
,
91 HKEY key
, const char * valuename
,
92 DWORD type
, const char *data
, DWORD cb_data
,
93 const char * separator
)
97 BYTE static_buffer
[16384];
98 BYTE
*pbuffer
= &static_buffer
[0];
103 krb5_set_error_message(context
, 0,
104 "'data' must not be NULL");
108 if (cb_data
== MAX_DWORD
)
110 cb_data
= (DWORD
)strlen(data
) + 1;
112 else if ((type
== REG_MULTI_SZ
&& cb_data
>= sizeof(static_buffer
) - 1) ||
113 cb_data
>= sizeof(static_buffer
))
116 krb5_set_error_message(context
, 0, "cb_data too big");
119 else if (data
[cb_data
-1] != '\0')
121 memcpy(static_buffer
, data
, cb_data
);
122 static_buffer
[cb_data
++] = '\0';
123 if (type
== REG_MULTI_SZ
)
124 static_buffer
[cb_data
++] = '\0';
125 data
= static_buffer
;
128 if (type
== REG_NONE
)
131 * If input is all numeric, convert to DWORD and save as REG_DWORD.
132 * Otherwise, store as REG_SZ.
134 if ( StrToIntExA( data
, STIF_SUPPORT_HEX
, &dwData
) )
145 rcode
= RegSetValueEx(key
, valuename
, 0, type
, data
, cb_data
);
149 krb5_set_error_message(context
, 0,
150 "Unexpected error when setting registry value %s gle 0x%x",
157 if (separator
&& *separator
)
161 if (data
!= static_buffer
)
162 static_buffer
[cb_data
++] = '\0';
164 for ( cp
= static_buffer
; cp
< static_buffer
+cb_data
; cp
++)
166 if (*cp
== *separator
)
170 rcode
= RegSetValueEx(key
, valuename
, 0, type
, data
, cb_data
);
174 krb5_set_error_message(context
, 0,
175 "Unexpected error when setting registry value %s gle 0x%x",
183 if ( !StrToIntExA( data
, STIF_SUPPORT_HEX
, &dwData
) )
186 krb5_set_error_message(context
, 0,
187 "Unexpected error when parsing %s as number gle 0x%x",
192 rcode
= RegSetValueEx(key
, valuename
, 0, type
, (BYTE
*)&dwData
, sizeof(DWORD
));
196 krb5_set_error_message(context
, 0,
197 "Unexpected error when setting registry value %s gle 0x%x",
211 * Parse a registry value as a string
213 * @see _krb5_parse_reg_value_as_multi_string()
215 KRB5_LIB_FUNCTION
char * KRB5_LIB_CALL
216 _krb5_parse_reg_value_as_string(krb5_context context
,
217 HKEY key
, const char * valuename
,
218 DWORD type
, DWORD cb_data
)
220 return _krb5_parse_reg_value_as_multi_string(context
, key
, valuename
,
225 * Parse a registry value as a multi string
227 * The following registry value types are handled:
229 * - REG_DWORD: The decimal string representation is used as the
232 * - REG_SZ: The string is used as-is.
234 * - REG_EXPAND_SZ: Environment variables in the string are expanded
235 * and the result is used as the value.
237 * - REG_MULTI_SZ: The list of strings is concatenated using the
238 * separator. No quoting is performed.
240 * Any other value type is rejected.
242 * @param [in]valuename Name of the registry value to be queried
243 * @param [in]type Type of the value. REG_NONE if unknown
244 * @param [in]cbdata Size of value. 0 if unknown.
245 * @param [in]separator Separator character for concatenating strings.
247 * @a type and @a cbdata are only considered valid if both are
250 * @retval The registry value string, or NULL if there was an error.
251 * If NULL is returned, an error message has been set using
252 * krb5_set_error_message().
254 KRB5_LIB_FUNCTION
char * KRB5_LIB_CALL
255 _krb5_parse_reg_value_as_multi_string(krb5_context context
,
256 HKEY key
, const char * valuename
,
257 DWORD type
, DWORD cb_data
, char *separator
)
259 LONG rcode
= ERROR_MORE_DATA
;
261 BYTE static_buffer
[16384];
262 BYTE
*pbuffer
= &static_buffer
[0];
263 DWORD cb_alloc
= sizeof(static_buffer
);
264 char *ret_string
= NULL
;
266 /* If we know a type and cb_data from a previous call to
267 * RegEnumValue(), we use it. Otherwise we use the
268 * static_buffer[] and query directly. We do this to minimize the
269 * number of queries. */
271 if (type
== REG_NONE
|| cb_data
== 0) {
273 pbuffer
= &static_buffer
[0];
274 cb_alloc
= cb_data
= sizeof(static_buffer
);
275 rcode
= RegQueryValueExA(key
, valuename
, NULL
, &type
, pbuffer
, &cb_data
);
277 if (rcode
== ERROR_SUCCESS
&&
280 type
!= REG_EXPAND_SZ
) || cb_data
+ 1 <= sizeof(static_buffer
)) &&
282 (type
!= REG_MULTI_SZ
|| cb_data
+ 2 <= sizeof(static_buffer
)))
285 if (rcode
!= ERROR_MORE_DATA
&& rcode
!= ERROR_SUCCESS
)
289 /* Either we don't have the data or we aren't sure of the size
290 * (due to potentially missing terminating NULs). */
294 if (cb_data
!= sizeof(DWORD
)) {
296 krb5_set_error_message(context
, 0,
297 "Unexpected size while reading registry value %s",
306 if (rcode
== ERROR_SUCCESS
&& cb_data
> 0 && pbuffer
[cb_data
- 1] == '\0')
309 cb_data
+= sizeof(char); /* Accout for potential missing NUL
315 if (rcode
== ERROR_SUCCESS
&& cb_data
> 0 && pbuffer
[cb_data
- 1] == '\0' &&
316 (cb_data
== 1 || pbuffer
[cb_data
- 2] == '\0'))
319 cb_data
+= sizeof(char) * 2; /* Potential missing double NUL
325 krb5_set_error_message(context
, 0,
326 "Unexpected type while reading registry value %s",
331 if (cb_data
<= sizeof(static_buffer
))
332 pbuffer
= &static_buffer
[0];
334 pbuffer
= malloc(cb_data
);
340 rcode
= RegQueryValueExA(key
, valuename
, NULL
, NULL
, pbuffer
, &cb_data
);
342 if (rcode
!= ERROR_SUCCESS
) {
344 /* This can potentially be from a race condition. I.e. some
345 * other process or thread went and modified the registry
346 * value between the time we queried its size and queried for
347 * its value. Ideally we would retry the query in a loop. */
350 krb5_set_error_message(context
, 0,
351 "Unexpected error while reading registry value %s",
356 if (cb_data
> cb_alloc
|| cb_data
== 0) {
358 krb5_set_error_message(context
, 0,
359 "Unexpected size while reading registry value %s",
367 asprintf(&ret_string
, "%d", *((DWORD
*) pbuffer
));
372 char * str
= (char *) pbuffer
;
374 if (str
[cb_data
- 1] != '\0') {
375 if (cb_data
< cb_alloc
)
381 if (pbuffer
!= static_buffer
) {
382 ret_string
= (char *) pbuffer
;
385 ret_string
= strdup((char *) pbuffer
);
392 char *str
= (char *) pbuffer
;
393 char expsz
[32768]; /* Size of output buffer for
394 * ExpandEnvironmentStrings() is
397 if (str
[cb_data
- 1] != '\0') {
398 if (cb_data
< cb_alloc
)
404 if (ExpandEnvironmentStrings(str
, expsz
, sizeof(expsz
)/sizeof(char)) != 0) {
405 ret_string
= strdup(expsz
);
408 krb5_set_error_message(context
, 0,
409 "Overflow while expanding environment strings "
410 "for registry value %s", valuename
);
417 char * str
= (char *) pbuffer
;
420 str
[cb_alloc
- 1] = '\0';
421 str
[cb_alloc
- 2] = '\0';
423 for (iter
= str
; *iter
;) {
424 size_t len
= strlen(iter
);
428 *iter
++ = *separator
;
433 if (pbuffer
!= static_buffer
) {
437 ret_string
= strdup(str
);
444 krb5_set_error_message(context
, 0,
445 "Unexpected type while reading registry value %s",
450 if (pbuffer
!= static_buffer
&& pbuffer
!= NULL
)
457 * Parse a registry value as a configuration value
459 * @see parse_reg_value_as_string()
461 static krb5_error_code
462 parse_reg_value(krb5_context context
,
463 HKEY key
, const char * valuename
,
464 DWORD type
, DWORD cbdata
, krb5_config_section
** parent
)
466 char *reg_string
= NULL
;
467 krb5_config_section
*value
;
468 krb5_error_code code
= 0;
470 reg_string
= _krb5_parse_reg_value_as_string(context
, key
, valuename
, type
, cbdata
);
472 if (reg_string
== NULL
)
473 return KRB5_CONFIG_BADFORMAT
;
475 value
= _krb5_config_get_entry(parent
, valuename
, krb5_config_string
);
481 if (value
->u
.string
!= NULL
)
482 free(value
->u
.string
);
484 value
->u
.string
= reg_string
;
488 if (reg_string
!= NULL
)
494 static krb5_error_code
495 parse_reg_values(krb5_context context
,
497 krb5_config_section
** parent
)
502 for (index
= 0; ; index
++) {
504 DWORD cch
= sizeof(name
)/sizeof(name
[0]);
507 krb5_error_code code
;
509 rcode
= RegEnumValue(key
, index
, name
, &cch
, NULL
,
510 &type
, NULL
, &cbdata
);
511 if (rcode
!= ERROR_SUCCESS
)
517 code
= parse_reg_value(context
, key
, name
, type
, cbdata
, parent
);
525 static krb5_error_code
526 parse_reg_subkeys(krb5_context context
,
528 krb5_config_section
** parent
)
533 for (index
= 0; ; index
++) {
536 DWORD cch
= sizeof(name
)/sizeof(name
[0]);
537 krb5_config_section
*section
= NULL
;
538 krb5_error_code code
;
540 rcode
= RegEnumKeyEx(key
, index
, name
, &cch
, NULL
, NULL
, NULL
, NULL
);
541 if (rcode
!= ERROR_SUCCESS
)
544 rcode
= RegOpenKeyEx(key
, name
, 0, KEY_READ
, &subkey
);
545 if (rcode
!= ERROR_SUCCESS
)
548 section
= _krb5_config_get_entry(parent
, name
, krb5_config_list
);
549 if (section
== NULL
) {
554 code
= parse_reg_values(context
, subkey
, §ion
->u
.list
);
560 code
= parse_reg_subkeys(context
, subkey
, §ion
->u
.list
);
572 static krb5_error_code
573 parse_reg_root(krb5_context context
,
575 krb5_config_section
** parent
)
577 krb5_config_section
*libdefaults
= NULL
;
578 krb5_error_code code
= 0;
580 libdefaults
= _krb5_config_get_entry(parent
, "libdefaults", krb5_config_list
);
581 if (libdefaults
== NULL
)
582 return krb5_enomem(context
);
584 code
= parse_reg_values(context
, key
, &libdefaults
->u
.list
);
588 return parse_reg_subkeys(context
, key
, parent
);
591 static krb5_error_code
592 load_config_from_regpath(krb5_context context
,
594 const char* key_path
,
595 krb5_config_section
** res
)
599 krb5_error_code code
= 0;
601 rcode
= RegOpenKeyEx(hk_root
, key_path
, 0, KEY_READ
, &key
);
602 if (rcode
== ERROR_SUCCESS
) {
603 code
= parse_reg_root(context
, key
, res
);
612 * Load configuration from registry
614 * The registry keys 'HKCU\Software\Heimdal' and
615 * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
616 * registry key corresponds to a configuration section (or bound list)
617 * and each value in a registry key is treated as a bound value. The
618 * set of values that are directly under the Heimdal key are treated
619 * as if they were defined in the [libdefaults] section.
621 * @see parse_reg_value() for details about how each type of value is handled.
623 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
624 _krb5_load_config_from_registry(krb5_context context
,
625 krb5_config_section
** res
)
627 krb5_error_code code
;
629 code
= load_config_from_regpath(context
, HKEY_LOCAL_MACHINE
,
630 REGPATH_KERBEROS
, res
);
634 code
= load_config_from_regpath(context
, HKEY_LOCAL_MACHINE
,
635 REGPATH_HEIMDAL
, res
);
639 code
= load_config_from_regpath(context
, HKEY_CURRENT_USER
,
640 REGPATH_KERBEROS
, res
);
644 code
= load_config_from_regpath(context
, HKEY_CURRENT_USER
,
645 REGPATH_HEIMDAL
, res
);