1*1c9681d1Schristos /*	$NetBSD: config_reg.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2f59d82ffSelric 
3f59d82ffSelric /***********************************************************************
4f59d82ffSelric  * Copyright (c) 2010, Secure Endpoints Inc.
5f59d82ffSelric  * All rights reserved.
6f59d82ffSelric  *
7f59d82ffSelric  * Redistribution and use in source and binary forms, with or without
8f59d82ffSelric  * modification, are permitted provided that the following conditions
9f59d82ffSelric  * are met:
10f59d82ffSelric  *
11f59d82ffSelric  * - Redistributions of source code must retain the above copyright
12f59d82ffSelric  *   notice, this list of conditions and the following disclaimer.
13f59d82ffSelric  *
14f59d82ffSelric  * - Redistributions in binary form must reproduce the above copyright
15f59d82ffSelric  *   notice, this list of conditions and the following disclaimer in
16f59d82ffSelric  *   the documentation and/or other materials provided with the
17f59d82ffSelric  *   distribution.
18f59d82ffSelric  *
19f59d82ffSelric  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20f59d82ffSelric  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21f59d82ffSelric  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22f59d82ffSelric  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23f59d82ffSelric  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24f59d82ffSelric  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25f59d82ffSelric  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26f59d82ffSelric  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27f59d82ffSelric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28f59d82ffSelric  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29f59d82ffSelric  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30f59d82ffSelric  * OF THE POSSIBILITY OF SUCH DAMAGE.
31f59d82ffSelric  *
32f59d82ffSelric  **********************************************************************/
33f59d82ffSelric 
34f59d82ffSelric #include "krb5_locl.h"
35f59d82ffSelric 
36f59d82ffSelric #ifndef _WIN32
37f59d82ffSelric #error  config_reg.c is only for Windows
38f59d82ffSelric #endif
39f59d82ffSelric 
40603f2576Spettai #include <shlwapi.h>
41603f2576Spettai 
42603f2576Spettai #ifndef MAX_DWORD
43603f2576Spettai #define MAX_DWORD 0xFFFFFFFF
44603f2576Spettai #endif
45603f2576Spettai 
46603f2576Spettai #define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
47603f2576Spettai #define REGPATH_HEIMDAL  "SOFTWARE\\Heimdal"
48603f2576Spettai 
49603f2576Spettai /**
50603f2576Spettai  * Store a string as a registry value of the specified type
51603f2576Spettai  *
52603f2576Spettai  * The following registry types are handled:
53603f2576Spettai  *
54603f2576Spettai  * - REG_DWORD: The string is converted to a number.
55603f2576Spettai  *
56603f2576Spettai  * - REG_SZ: The string is stored as is.
57603f2576Spettai  *
58603f2576Spettai  * - REG_EXPAND_SZ: The string is stored as is.
59603f2576Spettai  *
60603f2576Spettai  * - REG_MULTI_SZ:
61603f2576Spettai  *
62603f2576Spettai  *   . If a separator is specified, the input string is broken
63603f2576Spettai  *     up into multiple strings and stored as a multi-sz.
64603f2576Spettai  *
65603f2576Spettai  *   . If no separator is provided, the input string is stored
66603f2576Spettai  *     as a multi-sz.
67603f2576Spettai  *
68603f2576Spettai  * - REG_NONE:
69603f2576Spettai  *
70603f2576Spettai  *   . If the string is all numeric, it will be stored as a
71603f2576Spettai  *     REG_DWORD.
72603f2576Spettai  *
73603f2576Spettai  *   . Otherwise, the string is stored as a REG_SZ.
74603f2576Spettai  *
75603f2576Spettai  * Other types are rejected.
76603f2576Spettai  *
77603f2576Spettai  * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
78603f2576Spettai  * otherwise a buffer overrun will occur.
79603f2576Spettai  *
80603f2576Spettai  * @param [in]valuename Name of the registry value to be modified or created
81603f2576Spettai  * @param [in]type      Type of the value. REG_NONE if unknown
82603f2576Spettai  * @param [in]data      The input string to be stored in the registry.
83603f2576Spettai  * @param [in]cb_data   Size of the input string in bytes. MAX_DWORD if unknown.
84603f2576Spettai  * @param [in]separator Separator character for parsing strings.
85603f2576Spettai  *
86603f2576Spettai  * @retval 0 if success or non-zero on error.
87603f2576Spettai  * If non-zero is returned, an error message has been set using
88603f2576Spettai  * krb5_set_error_message().
89603f2576Spettai  *
90603f2576Spettai  */
91e0895134Schristos KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_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)92603f2576Spettai _krb5_store_string_to_reg_value(krb5_context context,
93603f2576Spettai                                 HKEY key, const char * valuename,
94603f2576Spettai                                 DWORD type, const char *data, DWORD cb_data,
95603f2576Spettai                                 const char * separator)
96603f2576Spettai {
97603f2576Spettai     LONG        rcode;
98603f2576Spettai     DWORD       dwData;
99603f2576Spettai     BYTE        static_buffer[16384];
100603f2576Spettai     BYTE        *pbuffer = &static_buffer[0];
101603f2576Spettai 
102603f2576Spettai     if (data == NULL)
103603f2576Spettai     {
104603f2576Spettai         if (context)
105603f2576Spettai             krb5_set_error_message(context, 0,
106603f2576Spettai                                    "'data' must not be NULL");
107603f2576Spettai         return -1;
108603f2576Spettai     }
109603f2576Spettai 
110603f2576Spettai     if (cb_data == MAX_DWORD)
111603f2576Spettai     {
112603f2576Spettai         cb_data = (DWORD)strlen(data) + 1;
113603f2576Spettai     }
114603f2576Spettai     else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
115603f2576Spettai              cb_data >= sizeof(static_buffer))
116603f2576Spettai     {
117603f2576Spettai         if (context)
118603f2576Spettai             krb5_set_error_message(context, 0, "cb_data too big");
119603f2576Spettai         return -1;
120603f2576Spettai     }
121603f2576Spettai     else if (data[cb_data-1] != '\0')
122603f2576Spettai     {
123603f2576Spettai         memcpy(static_buffer, data, cb_data);
124603f2576Spettai         static_buffer[cb_data++] = '\0';
125603f2576Spettai         if (type == REG_MULTI_SZ)
126603f2576Spettai             static_buffer[cb_data++] = '\0';
127603f2576Spettai         data = static_buffer;
128603f2576Spettai     }
129603f2576Spettai 
130603f2576Spettai     if (type == REG_NONE)
131603f2576Spettai     {
132603f2576Spettai         /*
133603f2576Spettai          * If input is all numeric, convert to DWORD and save as REG_DWORD.
134603f2576Spettai          * Otherwise, store as REG_SZ.
135603f2576Spettai          */
136603f2576Spettai         if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
137603f2576Spettai         {
138603f2576Spettai             type = REG_DWORD;
139603f2576Spettai         } else {
140603f2576Spettai             type = REG_SZ;
141603f2576Spettai         }
142603f2576Spettai     }
143603f2576Spettai 
144603f2576Spettai     switch (type) {
145603f2576Spettai     case REG_SZ:
146603f2576Spettai     case REG_EXPAND_SZ:
147603f2576Spettai         rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
148603f2576Spettai         if (rcode)
149603f2576Spettai         {
150603f2576Spettai             if (context)
151603f2576Spettai                 krb5_set_error_message(context, 0,
152603f2576Spettai                                        "Unexpected error when setting registry value %s gle 0x%x",
153603f2576Spettai                                        valuename,
154603f2576Spettai                                        GetLastError());
155603f2576Spettai             return -1;
156603f2576Spettai         }
157603f2576Spettai         break;
158603f2576Spettai     case REG_MULTI_SZ:
159603f2576Spettai         if (separator && *separator)
160603f2576Spettai         {
161603f2576Spettai             char *cp;
162603f2576Spettai 
163603f2576Spettai             if (data != static_buffer)
164603f2576Spettai                 static_buffer[cb_data++] = '\0';
165603f2576Spettai 
166603f2576Spettai             for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
167603f2576Spettai             {
168603f2576Spettai                 if (*cp == *separator)
169603f2576Spettai                     *cp = '\0';
170603f2576Spettai             }
171603f2576Spettai 
172603f2576Spettai             rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
173603f2576Spettai             if (rcode)
174603f2576Spettai             {
175603f2576Spettai                 if (context)
176603f2576Spettai                     krb5_set_error_message(context, 0,
177603f2576Spettai                                            "Unexpected error when setting registry value %s gle 0x%x",
178603f2576Spettai                                            valuename,
179603f2576Spettai                                            GetLastError());
180603f2576Spettai                 return -1;
181603f2576Spettai             }
182603f2576Spettai         }
183603f2576Spettai         break;
184603f2576Spettai     case REG_DWORD:
185603f2576Spettai         if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
186603f2576Spettai         {
187603f2576Spettai             if (context)
188603f2576Spettai                 krb5_set_error_message(context, 0,
189603f2576Spettai                                        "Unexpected error when parsing %s as number gle 0x%x",
190603f2576Spettai                                        data,
191603f2576Spettai                                        GetLastError());
192603f2576Spettai         }
193603f2576Spettai 
194e0895134Schristos 	rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD));
195603f2576Spettai         if (rcode)
196603f2576Spettai         {
197603f2576Spettai             if (context)
198603f2576Spettai                 krb5_set_error_message(context, 0,
199603f2576Spettai                                        "Unexpected error when setting registry value %s gle 0x%x",
200603f2576Spettai                                        valuename,
201603f2576Spettai                                        GetLastError());
202603f2576Spettai             return -1;
203603f2576Spettai         }
204603f2576Spettai         break;
205603f2576Spettai     default:
206603f2576Spettai         return -1;
207603f2576Spettai     }
208603f2576Spettai 
209603f2576Spettai     return 0;
210603f2576Spettai }
211f59d82ffSelric 
212f59d82ffSelric /**
213f59d82ffSelric  * Parse a registry value as a string
214f59d82ffSelric  *
215f59d82ffSelric  * @see _krb5_parse_reg_value_as_multi_string()
216f59d82ffSelric  */
217e0895134Schristos KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
_krb5_parse_reg_value_as_string(krb5_context context,HKEY key,const char * valuename,DWORD type,DWORD cb_data)218f59d82ffSelric _krb5_parse_reg_value_as_string(krb5_context context,
219f59d82ffSelric                                 HKEY key, const char * valuename,
220f59d82ffSelric                                 DWORD type, DWORD cb_data)
221f59d82ffSelric {
222f59d82ffSelric     return _krb5_parse_reg_value_as_multi_string(context, key, valuename,
223f59d82ffSelric                                                  type, cb_data, " ");
224f59d82ffSelric }
225f59d82ffSelric 
226f59d82ffSelric /**
227f59d82ffSelric  * Parse a registry value as a multi string
228f59d82ffSelric  *
229f59d82ffSelric  * The following registry value types are handled:
230f59d82ffSelric  *
231f59d82ffSelric  * - REG_DWORD: The decimal string representation is used as the
232f59d82ffSelric  *   value.
233f59d82ffSelric  *
234f59d82ffSelric  * - REG_SZ: The string is used as-is.
235f59d82ffSelric  *
236f59d82ffSelric  * - REG_EXPAND_SZ: Environment variables in the string are expanded
237f59d82ffSelric  *   and the result is used as the value.
238f59d82ffSelric  *
239f59d82ffSelric  * - REG_MULTI_SZ: The list of strings is concatenated using the
240f59d82ffSelric  *   separator.  No quoting is performed.
241f59d82ffSelric  *
242f59d82ffSelric  * Any other value type is rejected.
243f59d82ffSelric  *
244f59d82ffSelric  * @param [in]valuename Name of the registry value to be queried
245f59d82ffSelric  * @param [in]type      Type of the value. REG_NONE if unknown
246f59d82ffSelric  * @param [in]cbdata    Size of value. 0 if unknown.
247f59d82ffSelric  * @param [in]separator Separator character for concatenating strings.
248f59d82ffSelric  *
249f59d82ffSelric  * @a type and @a cbdata are only considered valid if both are
250f59d82ffSelric  * specified.
251f59d82ffSelric  *
252f59d82ffSelric  * @retval The registry value string, or NULL if there was an error.
253f59d82ffSelric  * If NULL is returned, an error message has been set using
254f59d82ffSelric  * krb5_set_error_message().
255f59d82ffSelric  */
256e0895134Schristos KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
_krb5_parse_reg_value_as_multi_string(krb5_context context,HKEY key,const char * valuename,DWORD type,DWORD cb_data,char * separator)257f59d82ffSelric _krb5_parse_reg_value_as_multi_string(krb5_context context,
258f59d82ffSelric                                       HKEY key, const char * valuename,
259f59d82ffSelric                                       DWORD type, DWORD cb_data, char *separator)
260f59d82ffSelric {
261f59d82ffSelric     LONG                rcode = ERROR_MORE_DATA;
262f59d82ffSelric 
263f59d82ffSelric     BYTE                static_buffer[16384];
264f59d82ffSelric     BYTE                *pbuffer = &static_buffer[0];
265f59d82ffSelric     DWORD               cb_alloc = sizeof(static_buffer);
266f59d82ffSelric     char                *ret_string = NULL;
267f59d82ffSelric 
268f59d82ffSelric     /* If we know a type and cb_data from a previous call to
269f59d82ffSelric      * RegEnumValue(), we use it.  Otherwise we use the
270f59d82ffSelric      * static_buffer[] and query directly.  We do this to minimize the
271f59d82ffSelric      * number of queries. */
272f59d82ffSelric 
273f59d82ffSelric     if (type == REG_NONE || cb_data == 0) {
274f59d82ffSelric 
275f59d82ffSelric         pbuffer = &static_buffer[0];
276f59d82ffSelric         cb_alloc = cb_data = sizeof(static_buffer);
277f59d82ffSelric         rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
278f59d82ffSelric 
279f59d82ffSelric         if (rcode == ERROR_SUCCESS &&
280f59d82ffSelric 
281f59d82ffSelric             ((type != REG_SZ &&
282f59d82ffSelric               type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
283f59d82ffSelric 
284f59d82ffSelric             (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
285f59d82ffSelric             goto have_data;
286f59d82ffSelric 
287f59d82ffSelric         if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
288f59d82ffSelric             return NULL;
289f59d82ffSelric     }
290f59d82ffSelric 
291f59d82ffSelric     /* Either we don't have the data or we aren't sure of the size
292f59d82ffSelric      * (due to potentially missing terminating NULs). */
293f59d82ffSelric 
294f59d82ffSelric     switch (type) {
295f59d82ffSelric     case REG_DWORD:
296f59d82ffSelric         if (cb_data != sizeof(DWORD)) {
297f59d82ffSelric             if (context)
298f59d82ffSelric                 krb5_set_error_message(context, 0,
299f59d82ffSelric                                        "Unexpected size while reading registry value %s",
300f59d82ffSelric                                        valuename);
301f59d82ffSelric             return NULL;
302f59d82ffSelric         }
303f59d82ffSelric         break;
304f59d82ffSelric 
305f59d82ffSelric     case REG_SZ:
306f59d82ffSelric     case REG_EXPAND_SZ:
307f59d82ffSelric 
308f59d82ffSelric         if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
309f59d82ffSelric             goto have_data;
310f59d82ffSelric 
311f59d82ffSelric         cb_data += sizeof(char); /* Accout for potential missing NUL
312f59d82ffSelric                                   * terminator. */
313f59d82ffSelric         break;
314f59d82ffSelric 
315f59d82ffSelric     case REG_MULTI_SZ:
316f59d82ffSelric 
317f59d82ffSelric         if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
318f59d82ffSelric             (cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
319f59d82ffSelric             goto have_data;
320f59d82ffSelric 
321f59d82ffSelric         cb_data += sizeof(char) * 2; /* Potential missing double NUL
322f59d82ffSelric                                       * terminator. */
323f59d82ffSelric         break;
324f59d82ffSelric 
325f59d82ffSelric     default:
326f59d82ffSelric         if (context)
327f59d82ffSelric             krb5_set_error_message(context, 0,
328f59d82ffSelric                                    "Unexpected type while reading registry value %s",
329f59d82ffSelric                                    valuename);
330f59d82ffSelric         return NULL;
331f59d82ffSelric     }
332f59d82ffSelric 
333f59d82ffSelric     if (cb_data <= sizeof(static_buffer))
334f59d82ffSelric         pbuffer = &static_buffer[0];
335f59d82ffSelric     else {
336f59d82ffSelric         pbuffer = malloc(cb_data);
337f59d82ffSelric         if (pbuffer == NULL)
338f59d82ffSelric             return NULL;
339f59d82ffSelric     }
340f59d82ffSelric 
341f59d82ffSelric     cb_alloc = cb_data;
342f59d82ffSelric     rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
343f59d82ffSelric 
344f59d82ffSelric     if (rcode != ERROR_SUCCESS) {
345f59d82ffSelric 
346f59d82ffSelric         /* This can potentially be from a race condition. I.e. some
347f59d82ffSelric          * other process or thread went and modified the registry
348f59d82ffSelric          * value between the time we queried its size and queried for
349f59d82ffSelric          * its value.  Ideally we would retry the query in a loop. */
350f59d82ffSelric 
351f59d82ffSelric         if (context)
352f59d82ffSelric             krb5_set_error_message(context, 0,
353f59d82ffSelric                                    "Unexpected error while reading registry value %s",
354f59d82ffSelric                                    valuename);
355f59d82ffSelric         goto done;
356f59d82ffSelric     }
357f59d82ffSelric 
358f59d82ffSelric     if (cb_data > cb_alloc || cb_data == 0) {
359f59d82ffSelric         if (context)
360f59d82ffSelric             krb5_set_error_message(context, 0,
361f59d82ffSelric                                    "Unexpected size while reading registry value %s",
362f59d82ffSelric                                    valuename);
363f59d82ffSelric         goto done;
364f59d82ffSelric     }
365f59d82ffSelric 
366f59d82ffSelric have_data:
367f59d82ffSelric     switch (type) {
368f59d82ffSelric     case REG_DWORD:
369f59d82ffSelric         asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
370f59d82ffSelric         break;
371f59d82ffSelric 
372f59d82ffSelric     case REG_SZ:
373f59d82ffSelric     {
374f59d82ffSelric         char * str = (char *) pbuffer;
375f59d82ffSelric 
376f59d82ffSelric         if (str[cb_data - 1] != '\0') {
377f59d82ffSelric             if (cb_data < cb_alloc)
378f59d82ffSelric                 str[cb_data] = '\0';
379f59d82ffSelric             else
380f59d82ffSelric                 break;
381f59d82ffSelric         }
382f59d82ffSelric 
383f59d82ffSelric         if (pbuffer != static_buffer) {
384f59d82ffSelric             ret_string = (char *) pbuffer;
385f59d82ffSelric             pbuffer = NULL;
386f59d82ffSelric         } else {
387f59d82ffSelric             ret_string = strdup((char *) pbuffer);
388f59d82ffSelric         }
389f59d82ffSelric     }
390f59d82ffSelric     break;
391f59d82ffSelric 
392f59d82ffSelric     case REG_EXPAND_SZ:
393f59d82ffSelric     {
394f59d82ffSelric         char    *str = (char *) pbuffer;
395f59d82ffSelric         char    expsz[32768];   /* Size of output buffer for
396f59d82ffSelric                                  * ExpandEnvironmentStrings() is
397f59d82ffSelric                                  * limited to 32K. */
398f59d82ffSelric 
399f59d82ffSelric         if (str[cb_data - 1] != '\0') {
400f59d82ffSelric             if (cb_data < cb_alloc)
401f59d82ffSelric                 str[cb_data] = '\0';
402f59d82ffSelric             else
403f59d82ffSelric                 break;
404f59d82ffSelric         }
405f59d82ffSelric 
406f59d82ffSelric         if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
407f59d82ffSelric             ret_string = strdup(expsz);
408f59d82ffSelric         } else {
409f59d82ffSelric             if (context)
410f59d82ffSelric                 krb5_set_error_message(context, 0,
411f59d82ffSelric                                        "Overflow while expanding environment strings "
412f59d82ffSelric                                        "for registry value %s", valuename);
413f59d82ffSelric         }
414f59d82ffSelric     }
415f59d82ffSelric     break;
416f59d82ffSelric 
417f59d82ffSelric     case REG_MULTI_SZ:
418f59d82ffSelric     {
419f59d82ffSelric         char * str = (char *) pbuffer;
420f59d82ffSelric         char * iter;
421f59d82ffSelric 
422f59d82ffSelric         str[cb_alloc - 1] = '\0';
423f59d82ffSelric         str[cb_alloc - 2] = '\0';
424f59d82ffSelric 
425f59d82ffSelric         for (iter = str; *iter;) {
426f59d82ffSelric             size_t len = strlen(iter);
427f59d82ffSelric 
428f59d82ffSelric             iter += len;
429f59d82ffSelric             if (iter[1] != '\0')
430f59d82ffSelric                 *iter++ = *separator;
431f59d82ffSelric             else
432f59d82ffSelric                 break;
433f59d82ffSelric         }
434f59d82ffSelric 
435f59d82ffSelric         if (pbuffer != static_buffer) {
436f59d82ffSelric             ret_string = str;
437f59d82ffSelric             pbuffer = NULL;
438f59d82ffSelric         } else {
439f59d82ffSelric             ret_string = strdup(str);
440f59d82ffSelric         }
441f59d82ffSelric     }
442f59d82ffSelric     break;
443f59d82ffSelric 
444f59d82ffSelric     default:
445f59d82ffSelric         if (context)
446f59d82ffSelric             krb5_set_error_message(context, 0,
447f59d82ffSelric                                    "Unexpected type while reading registry value %s",
448f59d82ffSelric                                    valuename);
449f59d82ffSelric     }
450f59d82ffSelric 
451f59d82ffSelric done:
452f59d82ffSelric     if (pbuffer != static_buffer && pbuffer != NULL)
453f59d82ffSelric         free(pbuffer);
454f59d82ffSelric 
455f59d82ffSelric     return ret_string;
456f59d82ffSelric }
457f59d82ffSelric 
458f59d82ffSelric /**
459f59d82ffSelric  * Parse a registry value as a configuration value
460f59d82ffSelric  *
461f59d82ffSelric  * @see parse_reg_value_as_string()
462f59d82ffSelric  */
463f59d82ffSelric static krb5_error_code
parse_reg_value(krb5_context context,HKEY key,const char * valuename,DWORD type,DWORD cbdata,krb5_config_section ** parent)464f59d82ffSelric parse_reg_value(krb5_context context,
465f59d82ffSelric                 HKEY key, const char * valuename,
466f59d82ffSelric                 DWORD type, DWORD cbdata, krb5_config_section ** parent)
467f59d82ffSelric {
468f59d82ffSelric     char                *reg_string = NULL;
469f59d82ffSelric     krb5_config_section *value;
470f59d82ffSelric     krb5_error_code     code = 0;
471f59d82ffSelric 
472f59d82ffSelric     reg_string = _krb5_parse_reg_value_as_string(context, key, valuename, type, cbdata);
473f59d82ffSelric 
474f59d82ffSelric     if (reg_string == NULL)
475f59d82ffSelric         return KRB5_CONFIG_BADFORMAT;
476f59d82ffSelric 
477f59d82ffSelric     value = _krb5_config_get_entry(parent, valuename, krb5_config_string);
478f59d82ffSelric     if (value == NULL) {
479f59d82ffSelric         code = ENOMEM;
480f59d82ffSelric         goto done;
481f59d82ffSelric     }
482f59d82ffSelric 
483f59d82ffSelric     if (value->u.string != NULL)
484f59d82ffSelric         free(value->u.string);
485f59d82ffSelric 
486f59d82ffSelric     value->u.string = reg_string;
487f59d82ffSelric     reg_string = NULL;
488f59d82ffSelric 
489f59d82ffSelric done:
490f59d82ffSelric     if (reg_string != NULL)
491f59d82ffSelric         free(reg_string);
492f59d82ffSelric 
493f59d82ffSelric     return code;
494f59d82ffSelric }
495f59d82ffSelric 
496f59d82ffSelric static krb5_error_code
parse_reg_values(krb5_context context,HKEY key,krb5_config_section ** parent)497f59d82ffSelric parse_reg_values(krb5_context context,
498f59d82ffSelric                  HKEY key,
499f59d82ffSelric                  krb5_config_section ** parent)
500f59d82ffSelric {
501f59d82ffSelric     DWORD index;
502f59d82ffSelric     LONG  rcode;
503f59d82ffSelric 
504f59d82ffSelric     for (index = 0; ; index ++) {
505f59d82ffSelric         char    name[16385];
506f59d82ffSelric         DWORD   cch = sizeof(name)/sizeof(name[0]);
507f59d82ffSelric         DWORD   type;
508f59d82ffSelric         DWORD   cbdata = 0;
509f59d82ffSelric         krb5_error_code code;
510f59d82ffSelric 
511f59d82ffSelric         rcode = RegEnumValue(key, index, name, &cch, NULL,
512f59d82ffSelric                              &type, NULL, &cbdata);
513f59d82ffSelric         if (rcode != ERROR_SUCCESS)
514f59d82ffSelric             break;
515f59d82ffSelric 
516f59d82ffSelric         if (cbdata == 0)
517f59d82ffSelric             continue;
518f59d82ffSelric 
519f59d82ffSelric         code = parse_reg_value(context, key, name, type, cbdata, parent);
520f59d82ffSelric         if (code != 0)
521f59d82ffSelric             return code;
522f59d82ffSelric     }
523f59d82ffSelric 
524f59d82ffSelric     return 0;
525f59d82ffSelric }
526f59d82ffSelric 
527f59d82ffSelric static krb5_error_code
parse_reg_subkeys(krb5_context context,HKEY key,krb5_config_section ** parent)528f59d82ffSelric parse_reg_subkeys(krb5_context context,
529f59d82ffSelric                   HKEY key,
530f59d82ffSelric                   krb5_config_section ** parent)
531f59d82ffSelric {
532f59d82ffSelric     DWORD index;
533f59d82ffSelric     LONG  rcode;
534f59d82ffSelric 
535f59d82ffSelric     for (index = 0; ; index ++) {
536f59d82ffSelric         HKEY    subkey = NULL;
537f59d82ffSelric         char    name[256];
538f59d82ffSelric         DWORD   cch = sizeof(name)/sizeof(name[0]);
539f59d82ffSelric         krb5_config_section     *section = NULL;
540f59d82ffSelric         krb5_error_code         code;
541f59d82ffSelric 
542f59d82ffSelric         rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
543f59d82ffSelric         if (rcode != ERROR_SUCCESS)
544f59d82ffSelric             break;
545f59d82ffSelric 
546f59d82ffSelric         rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
547f59d82ffSelric         if (rcode != ERROR_SUCCESS)
548f59d82ffSelric             continue;
549f59d82ffSelric 
550f59d82ffSelric         section = _krb5_config_get_entry(parent, name, krb5_config_list);
551f59d82ffSelric         if (section == NULL) {
552f59d82ffSelric             RegCloseKey(subkey);
553f59d82ffSelric             return ENOMEM;
554f59d82ffSelric         }
555f59d82ffSelric 
556f59d82ffSelric         code = parse_reg_values(context, subkey, &section->u.list);
557f59d82ffSelric         if (code) {
558f59d82ffSelric             RegCloseKey(subkey);
559f59d82ffSelric             return code;
560f59d82ffSelric         }
561f59d82ffSelric 
562f59d82ffSelric         code = parse_reg_subkeys(context, subkey, &section->u.list);
563f59d82ffSelric         if (code) {
564f59d82ffSelric             RegCloseKey(subkey);
565f59d82ffSelric             return code;
566f59d82ffSelric         }
567f59d82ffSelric 
568f59d82ffSelric         RegCloseKey(subkey);
569f59d82ffSelric     }
570f59d82ffSelric 
571f59d82ffSelric     return 0;
572f59d82ffSelric }
573f59d82ffSelric 
574f59d82ffSelric static krb5_error_code
parse_reg_root(krb5_context context,HKEY key,krb5_config_section ** parent)575f59d82ffSelric parse_reg_root(krb5_context context,
576f59d82ffSelric                HKEY key,
577f59d82ffSelric                krb5_config_section ** parent)
578f59d82ffSelric {
579f59d82ffSelric     krb5_config_section *libdefaults = NULL;
580f59d82ffSelric     krb5_error_code     code = 0;
581f59d82ffSelric 
582f59d82ffSelric     libdefaults = _krb5_config_get_entry(parent, "libdefaults", krb5_config_list);
583e0895134Schristos     if (libdefaults == NULL)
584e0895134Schristos         return krb5_enomem(context);
585f59d82ffSelric 
586f59d82ffSelric     code = parse_reg_values(context, key, &libdefaults->u.list);
587f59d82ffSelric     if (code)
588f59d82ffSelric         return code;
589f59d82ffSelric 
590f59d82ffSelric     return parse_reg_subkeys(context, key, parent);
591f59d82ffSelric }
592f59d82ffSelric 
593603f2576Spettai static krb5_error_code
load_config_from_regpath(krb5_context context,HKEY hk_root,const char * key_path,krb5_config_section ** res)594603f2576Spettai load_config_from_regpath(krb5_context context,
595603f2576Spettai                          HKEY hk_root,
596603f2576Spettai                          const char* key_path,
597603f2576Spettai                          krb5_config_section ** res)
598603f2576Spettai {
599603f2576Spettai     HKEY            key  = NULL;
600603f2576Spettai     LONG            rcode;
601603f2576Spettai     krb5_error_code code = 0;
602603f2576Spettai 
603603f2576Spettai     rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
604603f2576Spettai     if (rcode == ERROR_SUCCESS) {
605603f2576Spettai         code = parse_reg_root(context, key, res);
606603f2576Spettai         RegCloseKey(key);
607603f2576Spettai         key = NULL;
608603f2576Spettai     }
609603f2576Spettai 
610603f2576Spettai     return code;
611603f2576Spettai }
612603f2576Spettai 
613f59d82ffSelric /**
614f59d82ffSelric  * Load configuration from registry
615f59d82ffSelric  *
616f59d82ffSelric  * The registry keys 'HKCU\Software\Heimdal' and
617f59d82ffSelric  * 'HKLM\Software\Heimdal' are treated as krb5.conf files.  Each
618f59d82ffSelric  * registry key corresponds to a configuration section (or bound list)
619f59d82ffSelric  * and each value in a registry key is treated as a bound value.  The
620f59d82ffSelric  * set of values that are directly under the Heimdal key are treated
621f59d82ffSelric  * as if they were defined in the [libdefaults] section.
622f59d82ffSelric  *
623f59d82ffSelric  * @see parse_reg_value() for details about how each type of value is handled.
624f59d82ffSelric  */
625e0895134Schristos KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_load_config_from_registry(krb5_context context,krb5_config_section ** res)626f59d82ffSelric _krb5_load_config_from_registry(krb5_context context,
627f59d82ffSelric                                 krb5_config_section ** res)
628f59d82ffSelric {
629603f2576Spettai     krb5_error_code code;
630f59d82ffSelric 
631603f2576Spettai     code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
632603f2576Spettai                                     REGPATH_KERBEROS, res);
633f59d82ffSelric     if (code)
634f59d82ffSelric         return code;
635f59d82ffSelric 
636603f2576Spettai     code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
637603f2576Spettai                                     REGPATH_HEIMDAL, res);
638f59d82ffSelric     if (code)
639f59d82ffSelric         return code;
640f59d82ffSelric 
641603f2576Spettai     code = load_config_from_regpath(context, HKEY_CURRENT_USER,
642603f2576Spettai                                     REGPATH_KERBEROS, res);
643603f2576Spettai     if (code)
644603f2576Spettai         return code;
645603f2576Spettai 
646603f2576Spettai     code = load_config_from_regpath(context, HKEY_CURRENT_USER,
647603f2576Spettai                                     REGPATH_HEIMDAL, res);
648603f2576Spettai     if (code)
649603f2576Spettai         return code;
650f59d82ffSelric     return 0;
651f59d82ffSelric }
652