1 /* registry.c - Registry routines
2    SPDX-FileCopyrightText: 2005, 2007 g 10 Code GmbH
3 
4    This file is part of GpgEX.
5 
6    SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8 
9 /* keep this in sync with svn://cvs.gnupg.org/gpgex/trunk/src/registry.c (last checked against rev. 32) */
10 
11 #include <config-kleopatra.h>
12 
13 #if 0 /* We don't have a config.h in ONLY_KLEO, fix if needed */
14 #if HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 #endif
18 
19 #include <unistd.h>
20 #include <windows.h>
21 
22 #include <shlobj.h>
23 #ifndef CSIDL_APPDATA
24 #define CSIDL_APPDATA 0x001a
25 #endif
26 #ifndef CSIDL_LOCAL_APPDATA
27 #define CSIDL_LOCAL_APPDATA 0x001c
28 #endif
29 #ifndef CSIDL_FLAG_CREATE
30 #define CSIDL_FLAG_CREATE 0x8000
31 #endif
32 
33 #include "gnupg-registry.h"
34 
35 /* This is a helper function to load a Windows function from either of
36    one DLLs. */
37 HRESULT
w32_shgetfolderpath(HWND a,int b,HANDLE c,DWORD d,LPSTR e)38 w32_shgetfolderpath(HWND a, int b, HANDLE c, DWORD d, LPSTR e)
39 {
40     static int initialized;
41     static HRESULT(WINAPI * func)(HWND, int, HANDLE, DWORD, LPSTR);
42 
43     if (!initialized) {
44         static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
45         void *handle;
46         int i;
47 
48         initialized = 1;
49 
50         for (i = 0, handle = NULL; !handle && dllnames[i]; i++) {
51             handle = LoadLibraryA(dllnames[i]);
52             if (handle) {
53                 func = (HRESULT(WINAPI *)(HWND, int, HANDLE, DWORD, LPSTR))
54                        GetProcAddress(handle, "SHGetFolderPathA");
55                 if (!func) {
56                     FreeLibrary(handle);
57                     handle = NULL;
58                 }
59             }
60         }
61     }
62 
63     if (func) {
64         return func(a, b, c, d, e);
65     } else {
66         return -1;
67     }
68 }
69 
70 /* Helper for read_w32_registry_string(). */
71 static HKEY
get_root_key(const char * root)72 get_root_key(const char *root)
73 {
74     HKEY root_key;
75 
76     if (!root) {
77         root_key = HKEY_CURRENT_USER;
78     } else if (!strcmp(root, "HKEY_CLASSES_ROOT")) {
79         root_key = HKEY_CLASSES_ROOT;
80     } else if (!strcmp(root, "HKEY_CURRENT_USER")) {
81         root_key = HKEY_CURRENT_USER;
82     } else if (!strcmp(root, "HKEY_LOCAL_MACHINE")) {
83         root_key = HKEY_LOCAL_MACHINE;
84     } else if (!strcmp(root, "HKEY_USERS")) {
85         root_key = HKEY_USERS;
86     } else if (!strcmp(root, "HKEY_PERFORMANCE_DATA")) {
87         root_key = HKEY_PERFORMANCE_DATA;
88     } else if (!strcmp(root, "HKEY_CURRENT_CONFIG")) {
89         root_key = HKEY_CURRENT_CONFIG;
90     } else {
91         return NULL;
92     }
93     return root_key;
94 }
95 
96 /* Return a string from the Win32 Registry or NULL in case of error.
97    Caller must release the return value.  A NULL for root is an alias
98    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.  */
99 char *
read_w32_registry_string(const char * root,const char * dir,const char * name)100 read_w32_registry_string(const char *root, const char *dir, const char *name)
101 {
102     HKEY root_key, key_handle;
103     DWORD n1, nbytes, type;
104     char *result = NULL;
105 
106     if (!(root_key = get_root_key(root))) {
107         return NULL;
108     }
109 
110     if (RegOpenKeyExA(root_key, dir, 0, KEY_READ, &key_handle)) {
111         if (root) {
112             return NULL;    /* no need for a RegClose, so return direct */
113         }
114         /* It seems to be common practice to fall back to HKLM. */
115         if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) {
116             return NULL;    /* still no need for a RegClose, so return direct */
117         }
118     }
119 
120     nbytes = 1;
121     if (RegQueryValueExA(key_handle, name, 0, NULL, NULL, &nbytes)) {
122         if (root) {
123             goto leave;
124         }
125         /* Try to fallback to HKLM also vor a missing value.  */
126         RegCloseKey(key_handle);
127         if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) {
128             return NULL;    /* Nope.  */
129         }
130         if (RegQueryValueExA(key_handle, name, 0, NULL, NULL, &nbytes)) {
131             goto leave;
132         }
133     }
134     result = malloc((n1 = nbytes + 1));
135     if (!result) {
136         goto leave;
137     }
138     if (RegQueryValueExA(key_handle, name, 0, &type, result, &n1)) {
139         free(result); result = NULL;
140         goto leave;
141     }
142     result[nbytes] = 0; /* make sure it is really a string  */
143     if (type == REG_EXPAND_SZ && strchr(result, '%')) {
144         char *tmp;
145 
146         n1 += 1000;
147         tmp = malloc(n1 + 1);
148         if (!tmp) {
149             goto leave;
150         }
151         nbytes = ExpandEnvironmentStringsA(result, tmp, n1);
152         if (nbytes && nbytes > n1) {
153             free(tmp);
154             n1 = nbytes;
155             tmp = malloc(n1 + 1);
156             if (!tmp) {
157                 goto leave;
158             }
159             nbytes = ExpandEnvironmentStringsA(result, tmp, n1);
160             if (nbytes && nbytes > n1) {
161                 free(tmp);  /* oops - truncated, better don't expand at all */
162                 goto leave;
163             }
164             tmp[nbytes] = 0;
165             free(result);
166             result = tmp;
167         } else if (nbytes) { /* okay, reduce the length */
168             tmp[nbytes] = 0;
169             free(result);
170             result = malloc(strlen(tmp) + 1);
171             if (!result) {
172                 result = tmp;
173             } else {
174                 strcpy(result, tmp);
175                 free(tmp);
176             }
177         } else { /* error - don't expand */
178             free(tmp);
179         }
180     }
181 
182 leave:
183     RegCloseKey(key_handle);
184     return result;
185 }
186