Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(637)

Side by Side Diff: chrome_elf/nt_registry/nt_registry.cc

Issue 2345913003: [chrome_elf] NTRegistry - added wow64 redirection support. (Closed)
Patch Set: Code review fixes, part 2. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome_elf/nt_registry/nt_registry.h" 5 #include "chrome_elf/nt_registry/nt_registry.h"
6 6
7 #include <assert.h>
8 #include <stdlib.h>
9
7 namespace { 10 namespace {
8 11
9 // Function pointers used for registry access. 12 // Function pointers used for registry access.
10 RtlInitUnicodeStringFunction g_rtl_init_unicode_string = nullptr; 13 RtlInitUnicodeStringFunction g_rtl_init_unicode_string = nullptr;
11 NtCreateKeyFunction g_nt_create_key = nullptr; 14 NtCreateKeyFunction g_nt_create_key = nullptr;
12 NtDeleteKeyFunction g_nt_delete_key = nullptr; 15 NtDeleteKeyFunction g_nt_delete_key = nullptr;
13 NtOpenKeyExFunction g_nt_open_key_ex = nullptr; 16 NtOpenKeyExFunction g_nt_open_key_ex = nullptr;
14 NtCloseFunction g_nt_close = nullptr; 17 NtCloseFunction g_nt_close = nullptr;
15 NtQueryValueKeyFunction g_nt_query_value_key = nullptr; 18 NtQueryValueKeyFunction g_nt_query_value_key = nullptr;
16 NtSetValueKeyFunction g_nt_set_value_key = nullptr; 19 NtSetValueKeyFunction g_nt_set_value_key = nullptr;
17 20
18 // Lazy init. No concern about concurrency in chrome_elf. 21 // Lazy init. No concern about concurrency in chrome_elf.
19 bool g_initialized = false; 22 bool g_initialized = false;
20 bool g_system_install = false; 23 bool g_system_install = false;
24 bool g_wow64_proc = false;
21 bool g_reg_redirection = false; 25 bool g_reg_redirection = false;
22 const size_t g_kMaxPathLen = 255; 26 const size_t g_kMaxPathLen = 255;
23 wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\"; 27 wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\";
24 wchar_t g_kRegPathHKCU[g_kMaxPathLen] = L""; 28 wchar_t g_kRegPathHKCU[g_kMaxPathLen] = L"";
25 wchar_t g_current_user_sid_string[g_kMaxPathLen] = L""; 29 wchar_t g_current_user_sid_string[g_kMaxPathLen] = L"";
26 wchar_t g_override_path[g_kMaxPathLen] = L""; 30 wchar_t g_override_path[g_kMaxPathLen] = L"";
27 31
28 // Not using install_util, to prevent circular dependency. 32 //------------------------------------------------------------------------------
33 // Initialization - LOCAL
34 //------------------------------------------------------------------------------
35
36 // Not using install_static, to prevent circular dependency.
29 bool IsThisProcSystem() { 37 bool IsThisProcSystem() {
30 wchar_t program_dir[MAX_PATH] = {}; 38 wchar_t program_dir[MAX_PATH] = {};
31 wchar_t* cmd_line = GetCommandLineW(); 39 wchar_t* cmd_line = GetCommandLineW();
32 // If our command line starts with the "Program Files" or 40 // If our command line starts with the "Program Files" or
33 // "Program Files (x86)" path, we're system. 41 // "Program Files (x86)" path, we're system.
34 DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, MAX_PATH); 42 DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, MAX_PATH);
35 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret)) 43 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
36 return true; 44 return true;
37 45
38 ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, MAX_PATH); 46 ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, MAX_PATH);
39 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret)) 47 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
40 return true; 48 return true;
41 49
42 return false; 50 return false;
43 } 51 }
44 52
53 bool IsThisProcWow64() {
robertshield 2016/09/28 23:49:23 This function looks an awful lot like https://code
penny 2016/09/30 05:28:22 Commonalizified. I've exposed this information wi
54 // Using BOOL type for compat with IsWow64Process() system API.
55 BOOL is_wow64 = FALSE;
56
57 // API might not exist, so dynamic lookup.
58 using IsWow64ProcessFunction = decltype(&IsWow64Process);
59 IsWow64ProcessFunction is_wow64_process =
60 reinterpret_cast<IsWow64ProcessFunction>(::GetProcAddress(
61 ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
62 if (!is_wow64_process)
63 return false;
64 if (!is_wow64_process(::GetCurrentProcess(), &is_wow64))
65 return false;
66 return is_wow64 ? true : false;
67 }
68
45 bool InitNativeRegApi() { 69 bool InitNativeRegApi() {
46 HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll"); 70 HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
47 71
48 // Setup the global function pointers for registry access. 72 // Setup the global function pointers for registry access.
49 g_rtl_init_unicode_string = reinterpret_cast<RtlInitUnicodeStringFunction>( 73 g_rtl_init_unicode_string = reinterpret_cast<RtlInitUnicodeStringFunction>(
50 ::GetProcAddress(ntdll, "RtlInitUnicodeString")); 74 ::GetProcAddress(ntdll, "RtlInitUnicodeString"));
51 75
52 g_nt_create_key = reinterpret_cast<NtCreateKeyFunction>( 76 g_nt_create_key = reinterpret_cast<NtCreateKeyFunction>(
53 ::GetProcAddress(ntdll, "NtCreateKey")); 77 ::GetProcAddress(ntdll, "NtCreateKey"));
54 78
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 // Finish setting up global HKCU path. 115 // Finish setting up global HKCU path.
92 ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, (g_kMaxPathLen - 1)); 116 ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, (g_kMaxPathLen - 1));
93 ::wcsncat(g_kRegPathHKCU, L"\\", 117 ::wcsncat(g_kRegPathHKCU, L"\\",
94 (g_kMaxPathLen - ::wcslen(g_kRegPathHKCU) - 1)); 118 (g_kMaxPathLen - ::wcslen(g_kRegPathHKCU) - 1));
95 // Keep the sid string as well. 119 // Keep the sid string as well.
96 wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\'); 120 wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\');
97 ptr++; 121 ptr++;
98 ::wcsncpy(g_current_user_sid_string, ptr, (g_kMaxPathLen - 1)); 122 ::wcsncpy(g_current_user_sid_string, ptr, (g_kMaxPathLen - 1));
99 rtl_free_unicode_str(&current_user_reg_path); 123 rtl_free_unicode_str(&current_user_reg_path);
100 124
101 // Figure out if we're a system or user install. 125 // Figure out if this is a system or user install.
102 g_system_install = IsThisProcSystem(); 126 g_system_install = IsThisProcSystem();
103 127
128 // Figure out if this is a WOW64 process.
129 g_wow64_proc = IsThisProcWow64();
130
104 g_initialized = true; 131 g_initialized = true;
105 return true; 132 return true;
106 } 133 }
107 134
135 //------------------------------------------------------------------------------
136 // Reg WOW64 Redirection - LOCAL
137 //
138 // How registry redirection works directly calling NTDLL APIs:
139 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140 // - NOTE: On >= Win7, reflection support was removed.
141 // -
142 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384253(v=vs.85).as px
143 //
144 // - 1) 32-bit / WOW64 process:
145 // a) Default access WILL be redirected to WOW64.
146 // b) KEY_WOW64_32KEY access WILL be redirected to WOW64.
147 // c) KEY_WOW64_64KEY access will NOT be redirected to WOW64.
148 //
149 // - 2) 64-bit process:
150 // a) Default access will NOT be redirected to WOW64.
151 // b) KEY_WOW64_32KEY access will NOT be redirected to WOW64.
152 // c) KEY_WOW64_64KEY access will NOT be redirected to WOW64.
153 //
154 // - Key point from above is that NTDLL redirects and respects access
155 // overrides for WOW64 calling processes. But does NOT do any of that if the
156 // calling process is 64-bit. 2b is surprising and troublesome.
157 //
158 // How registry redirection works using these nt_registry APIs:
159 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
160 // - These APIs will behave the same as NTDLL above, EXCEPT for 2b.
161 // nt_registry APIs will respect the override access flags for all processes.
162 //
163 // - How the WOW64 redirection decision trees / Nodes work below:
164 //
165 // The HKLM and HKCU descision trees represent the information at the MSDN
robertshield 2016/09/28 23:49:23 nit: decision
penny 2016/09/30 05:28:22 Thank you. I dislike spelling or grammatical erro
166 // link above... but in a way that generates a decision about whether a
167 // registry path should be subject to WOW64 redirection. The tree is
168 // traversed as you scan along the registry path in question.
169 //
170 // - Each Node contains a chunk of registry subkey(s) to match.
171 // - If it is NOT matched, traversal is done.
172 // - If it is matched:
173 // - Current state of |redirection_type| for the whole registry path is
174 // updated.
175 // - If |next| is empty, traversal is done.
176 // - Otherwise, |next| is an array of child Nodes to try to match against.
177 // Loop.
178 //------------------------------------------------------------------------------
179
180 // This enum defines states for how to handle redirection.
181 // NOTE: When WOW64 redirection should happen, the redirect subkey can be either
182 // before or after the latest Node match. Unfortunately not consistent.
183 enum RedirectionType { SHARED = 0, REDIRECTED_BEFORE, REDIRECTED_AFTER };
184
185 struct Node {
186 template <size_t len, size_t n_len>
187 constexpr Node(const wchar_t (&wcs)[len],
188 RedirectionType rt,
189 const Node (&n)[n_len])
190 : to_match(wcs),
191 to_match_len(len - 1),
192 redirection_type(rt),
193 next(n),
194 next_len(n_len) {}
195
196 template <size_t len>
197 constexpr Node(const wchar_t (&wcs)[len], RedirectionType rt)
198 : to_match(wcs),
199 to_match_len(len - 1),
200 redirection_type(rt),
201 next(nullptr),
202 next_len(0) {}
203
204 const wchar_t* to_match;
205 size_t to_match_len;
206 // If a match, this is the new state of how to redirect.
207 RedirectionType redirection_type;
208 // |next| is nullptr or an array of Nodes of length |array_len|.
209 const Node* next;
210 size_t next_len;
211 };
212
213 // HKLM or HKCU SOFTWARE\Classes is shared by default. Specific subkeys under
214 // Classes are redirected to SOFTWARE\WOW6432Node\Classes\<subkey> though.
215 constexpr Node classes_subtree[] = {{L"CLSID", REDIRECTED_BEFORE},
grt (UTC plus 2) 2016/09/29 10:30:22 nit: kClassesSubtree
penny 2016/09/30 05:28:22 Sorry - I should have changed these at the same ti
216 {L"DirectShow", REDIRECTED_BEFORE},
217 {L"Interface", REDIRECTED_BEFORE},
218 {L"Media Type", REDIRECTED_BEFORE},
219 {L"MediaFoundation", REDIRECTED_BEFORE}};
220
221 // These specific HKLM\SOFTWARE subkeys are shared. Specific
222 // subkeys under Classes are redirected though... see classes_subtree.
223 constexpr Node hklm_software_subtree[] = {
grt (UTC plus 2) 2016/09/29 10:30:22 nit: kHklmSoftwareSubtree
penny 2016/09/30 05:28:22 Sorry - I should have changed these at the same ti
224 // TODO(pennymac): when MS fixes compiler bug, or bots are all using clang,
225 // remove the "Classes" subkeys below and replace with:
226 // {L"Classes", SHARED, classes_subtree},
227 // https://connect.microsoft.com/VisualStudio/feedback/details/3104499
228 {L"Classes\\CLSID", REDIRECTED_BEFORE},
229 {L"Classes\\DirectShow", REDIRECTED_BEFORE},
230 {L"Classes\\Interface", REDIRECTED_BEFORE},
231 {L"Classes\\Media Type", REDIRECTED_BEFORE},
232 {L"Classes\\MediaFoundation", REDIRECTED_BEFORE},
233 {L"Classes", SHARED},
234
235 {L"Clients", SHARED},
236 {L"Microsoft\\COM3", SHARED},
237 {L"Microsoft\\Cryptography\\Calais\\Current", SHARED},
238 {L"Microsoft\\Cryptography\\Calais\\Readers", SHARED},
239 {L"Microsoft\\Cryptography\\Services", SHARED},
240
241 {L"Microsoft\\CTF\\SystemShared", SHARED},
242 {L"Microsoft\\CTF\\TIP", SHARED},
243 {L"Microsoft\\DFS", SHARED},
244 {L"Microsoft\\Driver Signing", SHARED},
245 {L"Microsoft\\EnterpriseCertificates", SHARED},
246
247 {L"Microsoft\\EventSystem", SHARED},
248 {L"Microsoft\\MSMQ", SHARED},
249 {L"Microsoft\\Non-Driver Signing", SHARED},
250 {L"Microsoft\\Notepad\\DefaultFonts", SHARED},
251 {L"Microsoft\\OLE", SHARED},
252
253 {L"Microsoft\\RAS", SHARED},
254 {L"Microsoft\\RPC", SHARED},
255 {L"Microsoft\\SOFTWARE\\Microsoft\\Shared Tools\\MSInfo", SHARED},
256 {L"Microsoft\\SystemCertificates", SHARED},
257 {L"Microsoft\\TermServLicensing", SHARED},
258
259 {L"Microsoft\\Transaction Server", SHARED},
260 {L"Microsoft\\Windows\\CurrentVersion\\App Paths", SHARED},
261 {L"Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cursors\\Schemes",
262 SHARED},
263 {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers", SHARED},
264 {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons", SHARED},
265
266 {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap", SHARED},
267 {L"Microsoft\\Windows\\CurrentVersion\\Group Policy", SHARED},
268 {L"Microsoft\\Windows\\CurrentVersion\\Policies", SHARED},
269 {L"Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", SHARED},
270 {L"Microsoft\\Windows\\CurrentVersion\\Setup", SHARED},
271
272 {L"Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations", SHARED},
273 {L"Microsoft\\Windows NT\\CurrentVersion\\Console", SHARED},
274 {L"Microsoft\\Windows NT\\CurrentVersion\\FontDpi", SHARED},
275 {L"Microsoft\\Windows NT\\CurrentVersion\\FontLink", SHARED},
276 {L"Microsoft\\Windows NT\\CurrentVersion\\FontMapper", SHARED},
277
278 {L"Microsoft\\Windows NT\\CurrentVersion\\Fonts", SHARED},
279 {L"Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", SHARED},
280 {L"Microsoft\\Windows NT\\CurrentVersion\\Gre_Initialize", SHARED},
281 {L"Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options",
282 SHARED},
283 {L"Microsoft\\Windows NT\\CurrentVersion\\LanguagePack", SHARED},
284
285 {L"Microsoft\\Windows NT\\CurrentVersion\\NetworkCards", SHARED},
286 {L"Microsoft\\Windows NT\\CurrentVersion\\Perflib", SHARED},
287 {L"Microsoft\\Windows NT\\CurrentVersion\\Ports", SHARED},
288 {L"Microsoft\\Windows NT\\CurrentVersion\\Print", SHARED},
289 {L"Microsoft\\Windows NT\\CurrentVersion\\ProfileList", SHARED},
290
291 {L"Microsoft\\Windows NT\\CurrentVersion\\Time Zones", SHARED},
292 {L"Policies", SHARED},
293 {L"RegisteredApplications", SHARED}};
294
295 // HKCU is entirely shared, except for a few specific Classes subkeys which
296 // are redirected. See |classes_subtree|.
297 constexpr Node g_redirectionDecisionTreeHKCU = {L"SOFTWARE\\Classes", SHARED,
grt (UTC plus 2) 2016/09/29 10:30:22 nit: kRedirectionDecisionTreeHkcu
penny 2016/09/30 05:28:22 Done.
298 classes_subtree};
299
300 // HKLM\SOFTWARE is redirected by default to SOFTWARE\WOW6432Node. Specific
301 // subkeys under SOFTWARE are shared though... see |hklm_software_subtree|.
302 constexpr Node g_redirectionDecisionTreeHKLM = {L"SOFTWARE", REDIRECTED_AFTER,
grt (UTC plus 2) 2016/09/29 10:30:22 nit: kRedirectionDecisionTreeHklm
penny 2016/09/30 05:28:22 Done.
303 hklm_software_subtree};
304
305 // Main redirection handler function.
306 // If redirection is required, change is made to |subkey_path| in place.
307 //
308 // - This function should be called BEFORE concatenating |subkey_path| with the
309 // root hive or calling ParseFullRegPath().
310 // - Also, |subkey_path| should be passed to SanitizeSubkeyPath() before calling
grt (UTC plus 2) 2016/09/29 10:30:22 how about enforcing this by adding to the top of t
penny 2016/09/30 05:28:22 My take on this is that external APIs should be re
grt (UTC plus 2) 2016/09/30 09:32:16 Your philosophy on when to sanitize external input
penny 2016/10/01 01:44:25 I hear ya, and considered just changing ProcessRed
grt (UTC plus 2) 2016/10/02 20:10:53 Do the underlying Nt registry functions choke on m
penny 2016/10/03 19:18:48 I'm not sure at the moment. It (advapi or ntdll)
311 // this function.
312 void ProcessRedirection(nt::ROOT_KEY root,
313 ACCESS_MASK access,
314 std::wstring* subkey_path) {
315 static constexpr wchar_t kRedirectBefore[] = L"WOW6432Node\\";
316 static constexpr wchar_t kRedirectAfter[] = L"\\WOW6432Node";
317
318 if (subkey_path == nullptr || subkey_path->empty() ||
319 (access & KEY_WOW64_32KEY && access & KEY_WOW64_64KEY))
320 return;
321
322 // Convert nt::AUTO to the appropriate root key.
323 nt::ROOT_KEY temp_root = root;
324 if (root == nt::AUTO)
325 temp_root = g_system_install ? nt::HKLM : nt::HKCU;
326
327 // No redirection during testing when there's already an override.
328 // Otherwise, the testing redirect directory Software\Chromium\TempTestKeys
329 // would get WOW64 redirected if root_key == HKLM in this function.
330 if ((temp_root == nt::HKCU && *nt::HKCU_override) ||
331 (temp_root == nt::HKLM && *nt::HKLM_override))
332 return;
333
334 // WOW64 redirection only supported on x64 architecture. Return if x86.
335 SYSTEM_INFO system_info = {};
336 ::GetNativeSystemInfo(&system_info);
337 if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
338 return;
339
340 bool use_wow64 = g_wow64_proc;
341 // Consider KEY_WOW64_32KEY and KEY_WOW64_64KEY override access flags.
342 if (access & KEY_WOW64_32KEY)
343 use_wow64 = true;
344 if (access & KEY_WOW64_64KEY)
345 use_wow64 = false;
346
347 // If !use_wow64, there's nothing more to do.
348 if (!use_wow64)
349 return;
350
351 // Pick which decision tree to use.
352 const Node* current = (temp_root == nt::HKCU)
353 ? &g_redirectionDecisionTreeHKCU
354 : &g_redirectionDecisionTreeHKLM;
355
356 // The following loop works on the |subkey_path| from left to right.
357 // |position| tracks progress along |subkey_path|.
358 const wchar_t* position = subkey_path->c_str();
359 // Hold a count of chars left after position, for efficient calculations.
360 size_t chars_left = subkey_path->length();
361 // |redirect_state| holds the latest state of redirection requirement.
362 RedirectionType redirect_state = SHARED;
363 // |insertion_point| tracks latest spot for redirection subkey to be inserted.
364 const wchar_t* insertion_point = nullptr;
365 // |insert_string| tracks which redirection string would be inserted.
366 const wchar_t* insert_string = nullptr;
367
368 // The root of the tree is an array of 1.
grt (UTC plus 2) 2016/09/29 10:30:22 uber-nit: move the definition of |array_len| up so
penny 2016/09/30 05:28:22 Done.
369 size_t array_len = 1;
grt (UTC plus 2) 2016/09/29 10:30:22 nit: array_len -> node_array_len
penny 2016/09/30 05:28:23 I like this, thanks. More clear. I've also chang
370 size_t index = 0;
grt (UTC plus 2) 2016/09/29 10:30:22 nit: index -> node_index
penny 2016/09/30 05:28:23 Done.
371 while (index < array_len) {
372 size_t len = current->to_match_len;
grt (UTC plus 2) 2016/09/29 10:30:22 nit: len -> to_match_len
penny 2016/09/30 05:28:22 Done. current_to_match_len. I don't want any conf
373 // Make sure the remainder of the path is at least as long as the current
374 // subkey to match.
375 if (chars_left >= len) {
376 // Do case insensitive comparisons.
377 if (::wcsnicmp(position, current->to_match, len) == 0) {
378 // Make sure not to match on a substring.
379 if (*(position + len) == L'\\' || *(position + len) == L'\0') {
380 // MATCH!
381 // -------------------------------------------------------------------
382 // 1) Update state of redirection.
383 redirect_state = current->redirection_type;
384 // 1.5) If new state is to redirect, the new insertion point will be
385 // either right before or right after this match.
386 if (redirect_state == REDIRECTED_BEFORE) {
387 insertion_point = position;
388 insert_string = kRedirectBefore;
389 } else if (redirect_state == REDIRECTED_AFTER) {
390 insertion_point = position + len;
391 insert_string = kRedirectAfter;
392 }
393 // 2) Adjust |position| along the subkey path.
394 position += len;
395 chars_left -= len;
396 // 2.5) Increment the position, to move past path seperator(s).
397 while (*position == L'\\') {
398 ++position;
399 --chars_left;
400 }
401 // 3) Move our loop parameters to the |next| array of Nodes.
402 array_len = current->next_len;
403 current = current->next;
404 index = 0;
405 // 4) Finish this loop and start on new array.
406 continue;
407 }
408 }
409 }
410
411 // Move to the next node in the array if we didn't match this loop.
412 ++current;
413 ++index;
414 }
415
416 if (redirect_state == SHARED)
417 return;
418
419 // Insert the redirection into |subkey_path|, at |insertion_point|.
420 subkey_path->insert((insertion_point - subkey_path->data()), insert_string);
grt (UTC plus 2) 2016/09/29 10:30:22 nit: data -> c_str for consistency with the code a
penny 2016/09/30 05:28:22 Done.
421 }
422
423 //------------------------------------------------------------------------------
424 // Reg Path Utilities - LOCAL
425 //------------------------------------------------------------------------------
426
108 const wchar_t* ConvertRootKey(nt::ROOT_KEY root) { 427 const wchar_t* ConvertRootKey(nt::ROOT_KEY root) {
109 nt::ROOT_KEY key = root; 428 nt::ROOT_KEY key = root;
110 429
111 if (!root) { 430 if (!root) {
robertshield 2016/09/28 23:49:23 should this be if (root == nt::AUTO)?
grt (UTC plus 2) 2016/09/29 10:30:22 yes, please
penny 2016/09/30 05:28:22 Done. Sorry - missed this one!
112 // AUTO 431 // AUTO
113 key = g_system_install ? nt::HKLM : nt::HKCU; 432 key = g_system_install ? nt::HKLM : nt::HKCU;
114 } 433 }
115 434
116 if ((key == nt::HKCU) && (::wcslen(nt::HKCU_override) != 0)) { 435 if (key == nt::HKCU && *nt::HKCU_override) {
117 std::wstring temp(g_kRegPathHKCU); 436 std::wstring temp(g_kRegPathHKCU);
118 temp.append(nt::HKCU_override); 437 temp.append(nt::HKCU_override);
119 temp.append(L"\\"); 438 temp.append(L"\\");
120 ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1); 439 ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1);
121 g_reg_redirection = true; 440 g_reg_redirection = true;
122 return g_override_path; 441 return g_override_path;
123 } else if ((key == nt::HKLM) && (::wcslen(nt::HKLM_override) != 0)) { 442 } else if (key == nt::HKLM && *nt::HKLM_override) {
124 std::wstring temp(g_kRegPathHKCU); 443 std::wstring temp(g_kRegPathHKCU);
125 temp.append(nt::HKLM_override); 444 temp.append(nt::HKLM_override);
126 temp.append(L"\\"); 445 temp.append(L"\\");
127 ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1); 446 ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1);
128 g_reg_redirection = true; 447 g_reg_redirection = true;
129 return g_override_path; 448 return g_override_path;
130 } 449 }
131 450
132 g_reg_redirection = false; 451 g_reg_redirection = false;
133 if (key == nt::HKCU) 452 if (key == nt::HKCU)
134 return g_kRegPathHKCU; 453 return g_kRegPathHKCU;
135 else 454 else
136 return g_kRegPathHKLM; 455 return g_kRegPathHKLM;
137 } 456 }
138 457
458 // This utility should be called on an externally provided subkey path.
459 // - Ensures there are no starting or trailing backslashes.
460 // - Note from MSDN: "Key names cannot include the backslash character (\),
461 // but any other printable character can be used."
462 void SanitizeSubkeyPath(std::wstring* input) {
463 if (input == nullptr || input->empty())
grt (UTC plus 2) 2016/09/29 10:30:22 two comments here: since this is a private functi
penny 2016/09/30 05:28:22 I fully agree. Thank you.
464 return;
465
466 // Remove any trailing backslashes.
467 while (input->back() == L'\\')
grt (UTC plus 2) 2016/09/29 10:30:22 this provokes undefined behavior if the string con
penny 2016/09/30 05:28:22 TIL, don't call front/back on an empty std::wstrin
grt (UTC plus 2) 2016/09/30 09:32:16 I think it's much more clear and safe to use the s
penny 2016/10/01 01:44:25 I'm fully agreeing with you now. This is much nic
468 input->pop_back();
469
470 // Remove any starting backslashes.
471 size_t index = 0;
472 while (input->at(index) == L'\\')
grt (UTC plus 2) 2016/09/29 10:30:22 fyi: at() does bounds checking and throws exceptio
penny 2016/09/30 05:28:22 TIL. Thanks. Reading the docs, I think [] operat
473 ++index;
474 if (!index)
475 *input = input->substr(index);
476 }
477
139 // Turns a root and subkey path into the registry base hive and the rest of the 478 // Turns a root and subkey path into the registry base hive and the rest of the
140 // subkey tokens. 479 // subkey tokens.
141 // - |converted_root| should come directly out of ConvertRootKey function. 480 // - |converted_root| should come directly out of ConvertRootKey function.
481 // - |subkey_path| should be passed to SanitizeSubkeyPath() first.
142 // - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\". 482 // - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\".
143 bool ParseFullRegPath(const wchar_t* converted_root, 483 bool ParseFullRegPath(const wchar_t* converted_root,
144 const wchar_t* subkey_path, 484 const wchar_t* subkey_path,
145 std::wstring* out_base, 485 std::wstring* out_base,
146 std::vector<std::wstring>* subkeys) { 486 std::vector<std::wstring>* subkeys) {
147 out_base->clear(); 487 out_base->clear();
148 subkeys->clear(); 488 subkeys->clear();
149 std::wstring temp = L""; 489 std::wstring temp = L"";
150 490
151 if (g_reg_redirection) { 491 if (g_reg_redirection) {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 out_base->push_back(L'\\'); 531 out_base->push_back(L'\\');
192 } 532 }
193 subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens); 533 subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens);
194 } else { 534 } else {
195 out_base->assign(converted_root); 535 out_base->assign(converted_root);
196 } 536 }
197 537
198 return true; 538 return true;
199 } 539 }
200 540
541 //------------------------------------------------------------------------------
542 // Misc wrapper functions - LOCAL
543 //------------------------------------------------------------------------------
544
201 NTSTATUS CreateKeyWrapper(const std::wstring& key_path, 545 NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
202 ACCESS_MASK access, 546 ACCESS_MASK access,
203 HANDLE* out_handle, 547 HANDLE* out_handle,
204 ULONG* create_or_open OPTIONAL) { 548 ULONG* create_or_open OPTIONAL) {
205 UNICODE_STRING key_path_uni = {}; 549 UNICODE_STRING key_path_uni = {};
206 g_rtl_init_unicode_string(&key_path_uni, key_path.c_str()); 550 g_rtl_init_unicode_string(&key_path_uni, key_path.c_str());
207 551
208 OBJECT_ATTRIBUTES obj = {}; 552 OBJECT_ATTRIBUTES obj = {};
209 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL, 553 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
210 nullptr); 554 nullptr);
211 555
212 return g_nt_create_key(out_handle, access, &obj, 0, nullptr, 556 return g_nt_create_key(out_handle, access, &obj, 0, nullptr,
213 REG_OPTION_NON_VOLATILE, create_or_open); 557 REG_OPTION_NON_VOLATILE, create_or_open);
214 } 558 }
215 559
216 } // namespace 560 } // namespace
217 561
218 namespace nt { 562 namespace nt {
219 563
220 const size_t g_kRegMaxPathLen = 255; 564 const size_t g_kRegMaxPathLen = 255;
221 wchar_t HKLM_override[g_kRegMaxPathLen] = L""; 565 wchar_t HKLM_override[g_kRegMaxPathLen] = L"";
grt (UTC plus 2) 2016/09/29 10:30:22 wdyt of adding 1 to the length here and below so t
penny 2016/09/30 05:28:22 Done. Thanks. I've adjusted this everywhere to b
222 wchar_t HKCU_override[g_kRegMaxPathLen] = L""; 566 wchar_t HKCU_override[g_kRegMaxPathLen] = L"";
223 567
224 //------------------------------------------------------------------------------ 568 //------------------------------------------------------------------------------
225 // Create, open, delete, close functions 569 // Create, open, delete, close functions
226 //------------------------------------------------------------------------------ 570 //------------------------------------------------------------------------------
227 571
228 bool CreateRegKey(ROOT_KEY root, 572 bool CreateRegKey(ROOT_KEY root,
229 const wchar_t* key_path, 573 const wchar_t* key_path,
230 ACCESS_MASK access, 574 ACCESS_MASK access,
231 HANDLE* out_handle OPTIONAL) { 575 HANDLE* out_handle OPTIONAL) {
232 if (!g_initialized) 576 if (!g_initialized)
233 InitNativeRegApi(); 577 InitNativeRegApi();
234 578
579 std::wstring redirected_key_path;
580 if (key_path) {
581 redirected_key_path = key_path;
582 SanitizeSubkeyPath(&redirected_key_path);
583 ProcessRedirection(root, access, &redirected_key_path);
584 }
585
235 std::wstring current_path; 586 std::wstring current_path;
236 std::vector<std::wstring> subkeys; 587 std::vector<std::wstring> subkeys;
237 if (!ParseFullRegPath(ConvertRootKey(root), key_path, &current_path, 588 if (!ParseFullRegPath(ConvertRootKey(root), redirected_key_path.c_str(),
grt (UTC plus 2) 2016/09/29 10:30:22 pass redirected_key_path directly as a const std::
penny 2016/09/30 05:28:22 Done.
238 &subkeys)) 589 &current_path, &subkeys))
239 return false; 590 return false;
240 591
241 // Open the base hive first. It should always exist already. 592 // Open the base hive first. It should always exist already.
242 HANDLE last_handle = INVALID_HANDLE_VALUE; 593 HANDLE last_handle = INVALID_HANDLE_VALUE;
243 NTSTATUS status = 594 NTSTATUS status =
244 CreateKeyWrapper(current_path, access, &last_handle, nullptr); 595 CreateKeyWrapper(current_path, access, &last_handle, nullptr);
245 if (!NT_SUCCESS(status)) 596 if (!NT_SUCCESS(status))
246 return false; 597 return false;
247 598
248 size_t subkeys_size = subkeys.size(); 599 size_t subkeys_size = subkeys.size();
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 HANDLE* out_handle, 656 HANDLE* out_handle,
306 NTSTATUS* error_code OPTIONAL) { 657 NTSTATUS* error_code OPTIONAL) {
307 if (!g_initialized) 658 if (!g_initialized)
308 InitNativeRegApi(); 659 InitNativeRegApi();
309 660
310 NTSTATUS status = STATUS_UNSUCCESSFUL; 661 NTSTATUS status = STATUS_UNSUCCESSFUL;
311 UNICODE_STRING key_path_uni = {}; 662 UNICODE_STRING key_path_uni = {};
312 OBJECT_ATTRIBUTES obj = {}; 663 OBJECT_ATTRIBUTES obj = {};
313 *out_handle = INVALID_HANDLE_VALUE; 664 *out_handle = INVALID_HANDLE_VALUE;
314 665
315 std::wstring full_path(ConvertRootKey(root)); 666 std::wstring full_path;
316 full_path.append(key_path); 667 if (key_path) {
668 full_path = key_path;
669 SanitizeSubkeyPath(&full_path);
670 ProcessRedirection(root, access, &full_path);
671 }
672 full_path.insert(0, ConvertRootKey(root));
317 673
318 g_rtl_init_unicode_string(&key_path_uni, full_path.c_str()); 674 g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
319 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL, 675 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
320 NULL); 676 NULL);
321 677
322 status = g_nt_open_key_ex(out_handle, access, &obj, 0); 678 status = g_nt_open_key_ex(out_handle, access, &obj, 0);
323 // See if caller wants the NTSTATUS. 679 // See if caller wants the NTSTATUS.
324 if (error_code) 680 if (error_code)
325 *error_code = status; 681 *error_code = status;
326 682
(...skipping 11 matching lines...) Expand all
338 694
339 status = g_nt_delete_key(key); 695 status = g_nt_delete_key(key);
340 696
341 if (NT_SUCCESS(status)) 697 if (NT_SUCCESS(status))
342 return true; 698 return true;
343 699
344 return false; 700 return false;
345 } 701 }
346 702
347 // wrapper function 703 // wrapper function
348 bool DeleteRegKey(ROOT_KEY root, const wchar_t* key_path) { 704 bool DeleteRegKey(ROOT_KEY root,
705 WOW64_OVERRIDE wow64_override,
706 const wchar_t* key_path) {
349 HANDLE key = INVALID_HANDLE_VALUE; 707 HANDLE key = INVALID_HANDLE_VALUE;
350 708
351 if (!OpenRegKey(root, key_path, DELETE, &key, nullptr)) 709 if (!OpenRegKey(root, key_path, DELETE | wow64_override, &key, nullptr))
352 return false; 710 return false;
353 711
354 if (!DeleteRegKey(key)) { 712 if (!DeleteRegKey(key)) {
355 CloseRegKey(key); 713 CloseRegKey(key);
356 return false; 714 return false;
357 } 715 }
358 716
359 CloseRegKey(key); 717 CloseRegKey(key);
360 return true; 718 return true;
361 } 719 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
423 return false; 781 return false;
424 782
425 *out_dword = *(reinterpret_cast<DWORD*>(value_bytes)); 783 *out_dword = *(reinterpret_cast<DWORD*>(value_bytes));
426 784
427 delete[] value_bytes; 785 delete[] value_bytes;
428 return true; 786 return true;
429 } 787 }
430 788
431 // wrapper function 789 // wrapper function
432 bool QueryRegValueDWORD(ROOT_KEY root, 790 bool QueryRegValueDWORD(ROOT_KEY root,
791 WOW64_OVERRIDE wow64_override,
433 const wchar_t* key_path, 792 const wchar_t* key_path,
434 const wchar_t* value_name, 793 const wchar_t* value_name,
435 DWORD* out_dword) { 794 DWORD* out_dword) {
436 HANDLE key = INVALID_HANDLE_VALUE; 795 HANDLE key = INVALID_HANDLE_VALUE;
437 796
438 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key, 797 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
439 NULL))
440 return false; 798 return false;
441 799
442 if (!QueryRegValueDWORD(key, value_name, out_dword)) { 800 if (!QueryRegValueDWORD(key, value_name, out_dword)) {
443 CloseRegKey(key); 801 CloseRegKey(key);
444 return false; 802 return false;
445 } 803 }
446 804
447 CloseRegKey(key); 805 CloseRegKey(key);
448 return true; 806 return true;
449 } 807 }
(...skipping 11 matching lines...) Expand all
461 return false; 819 return false;
462 820
463 *out_sz = reinterpret_cast<wchar_t*>(value_bytes); 821 *out_sz = reinterpret_cast<wchar_t*>(value_bytes);
464 822
465 delete[] value_bytes; 823 delete[] value_bytes;
466 return true; 824 return true;
467 } 825 }
468 826
469 // wrapper function 827 // wrapper function
470 bool QueryRegValueSZ(ROOT_KEY root, 828 bool QueryRegValueSZ(ROOT_KEY root,
829 WOW64_OVERRIDE wow64_override,
471 const wchar_t* key_path, 830 const wchar_t* key_path,
472 const wchar_t* value_name, 831 const wchar_t* value_name,
473 std::wstring* out_sz) { 832 std::wstring* out_sz) {
474 HANDLE key = INVALID_HANDLE_VALUE; 833 HANDLE key = INVALID_HANDLE_VALUE;
475 834
476 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key, 835 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
477 NULL))
478 return false; 836 return false;
479 837
480 if (!QueryRegValueSZ(key, value_name, out_sz)) { 838 if (!QueryRegValueSZ(key, value_name, out_sz)) {
481 CloseRegKey(key); 839 CloseRegKey(key);
482 return false; 840 return false;
483 } 841 }
484 842
485 CloseRegKey(key); 843 CloseRegKey(key);
486 return true; 844 return true;
487 } 845 }
(...skipping 27 matching lines...) Expand all
515 // Handle the case of "empty multi_sz". 873 // Handle the case of "empty multi_sz".
516 if (out_multi_sz->size() == 0) 874 if (out_multi_sz->size() == 0)
517 out_multi_sz->push_back(L""); 875 out_multi_sz->push_back(L"");
518 876
519 delete[] value_bytes; 877 delete[] value_bytes;
520 return true; 878 return true;
521 } 879 }
522 880
523 // wrapper function 881 // wrapper function
524 bool QueryRegValueMULTISZ(ROOT_KEY root, 882 bool QueryRegValueMULTISZ(ROOT_KEY root,
883 WOW64_OVERRIDE wow64_override,
525 const wchar_t* key_path, 884 const wchar_t* key_path,
526 const wchar_t* value_name, 885 const wchar_t* value_name,
527 std::vector<std::wstring>* out_multi_sz) { 886 std::vector<std::wstring>* out_multi_sz) {
528 HANDLE key = INVALID_HANDLE_VALUE; 887 HANDLE key = INVALID_HANDLE_VALUE;
529 888
530 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key, 889 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
531 NULL))
532 return false; 890 return false;
533 891
534 if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) { 892 if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
535 CloseRegKey(key); 893 CloseRegKey(key);
536 return false; 894 return false;
537 } 895 }
538 896
539 CloseRegKey(key); 897 CloseRegKey(key);
540 return true; 898 return true;
541 } 899 }
(...skipping 25 matching lines...) Expand all
567 } 925 }
568 926
569 // wrapper function 927 // wrapper function
570 bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) { 928 bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
571 return SetRegKeyValue(key, value_name, REG_DWORD, 929 return SetRegKeyValue(key, value_name, REG_DWORD,
572 reinterpret_cast<BYTE*>(&value), sizeof(value)); 930 reinterpret_cast<BYTE*>(&value), sizeof(value));
573 } 931 }
574 932
575 // wrapper function 933 // wrapper function
576 bool SetRegValueDWORD(ROOT_KEY root, 934 bool SetRegValueDWORD(ROOT_KEY root,
935 WOW64_OVERRIDE wow64_override,
577 const wchar_t* key_path, 936 const wchar_t* key_path,
578 const wchar_t* value_name, 937 const wchar_t* value_name,
579 DWORD value) { 938 DWORD value) {
580 HANDLE key = INVALID_HANDLE_VALUE; 939 HANDLE key = INVALID_HANDLE_VALUE;
581 940
582 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL)) 941 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
583 return false; 942 return false;
584 943
585 if (!SetRegValueDWORD(key, value_name, value)) { 944 if (!SetRegValueDWORD(key, value_name, value)) {
586 CloseRegKey(key); 945 CloseRegKey(key);
587 return false; 946 return false;
588 } 947 }
589 948
590 return true; 949 return true;
591 } 950 }
592 951
593 // wrapper function 952 // wrapper function
594 bool SetRegValueSZ(HANDLE key, 953 bool SetRegValueSZ(HANDLE key,
595 const wchar_t* value_name, 954 const wchar_t* value_name,
596 const std::wstring& value) { 955 const std::wstring& value) {
597 // Make sure the number of bytes in |value|, including EoS, fits in a DWORD. 956 // Make sure the number of bytes in |value|, including EoS, fits in a DWORD.
598 if (std::numeric_limits<DWORD>::max() < 957 if (std::numeric_limits<DWORD>::max() <
599 ((value.length() + 1) * sizeof(wchar_t))) 958 ((value.length() + 1) * sizeof(wchar_t)))
600 return false; 959 return false;
601 960
602 DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t))); 961 DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
603 return SetRegKeyValue(key, value_name, REG_SZ, 962 return SetRegKeyValue(key, value_name, REG_SZ,
604 reinterpret_cast<const BYTE*>(value.c_str()), size); 963 reinterpret_cast<const BYTE*>(value.c_str()), size);
605 } 964 }
606 965
607 // wrapper function 966 // wrapper function
608 bool SetRegValueSZ(ROOT_KEY root, 967 bool SetRegValueSZ(ROOT_KEY root,
968 WOW64_OVERRIDE wow64_override,
609 const wchar_t* key_path, 969 const wchar_t* key_path,
610 const wchar_t* value_name, 970 const wchar_t* value_name,
611 const std::wstring& value) { 971 const std::wstring& value) {
612 HANDLE key = INVALID_HANDLE_VALUE; 972 HANDLE key = INVALID_HANDLE_VALUE;
613 973
614 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL)) 974 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
615 return false; 975 return false;
616 976
617 if (!SetRegValueSZ(key, value_name, value)) { 977 if (!SetRegValueSZ(key, value_name, value)) {
618 CloseRegKey(key); 978 CloseRegKey(key);
619 return false; 979 return false;
620 } 980 }
621 981
622 return true; 982 return true;
623 } 983 }
624 984
(...skipping 23 matching lines...) Expand all
648 if (std::numeric_limits<DWORD>::max() < builder.size()) 1008 if (std::numeric_limits<DWORD>::max() < builder.size())
649 return false; 1009 return false;
650 1010
651 return SetRegKeyValue( 1011 return SetRegKeyValue(
652 key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()), 1012 key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()),
653 (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t)); 1013 (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t));
654 } 1014 }
655 1015
656 // wrapper function 1016 // wrapper function
657 bool SetRegValueMULTISZ(ROOT_KEY root, 1017 bool SetRegValueMULTISZ(ROOT_KEY root,
1018 WOW64_OVERRIDE wow64_override,
658 const wchar_t* key_path, 1019 const wchar_t* key_path,
659 const wchar_t* value_name, 1020 const wchar_t* value_name,
660 const std::vector<std::wstring>& values) { 1021 const std::vector<std::wstring>& values) {
661 HANDLE key = INVALID_HANDLE_VALUE; 1022 HANDLE key = INVALID_HANDLE_VALUE;
662 1023
663 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL)) 1024 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
664 return false; 1025 return false;
665 1026
666 if (!SetRegValueMULTISZ(key, value_name, values)) { 1027 if (!SetRegValueMULTISZ(key, value_name, values)) {
667 CloseRegKey(key); 1028 CloseRegKey(key);
668 return false; 1029 return false;
669 } 1030 }
670 1031
671 return true; 1032 return true;
672 } 1033 }
673 1034
674 //------------------------------------------------------------------------------ 1035 //------------------------------------------------------------------------------
675 // Utils 1036 // Utils
676 //------------------------------------------------------------------------------ 1037 //------------------------------------------------------------------------------
677 1038
678 const wchar_t* GetCurrentUserSidString() { 1039 const wchar_t* GetCurrentUserSidString() {
679 if (!g_initialized) 1040 if (!g_initialized)
680 InitNativeRegApi(); 1041 InitNativeRegApi();
681 1042
682 return g_current_user_sid_string; 1043 return g_current_user_sid_string;
683 } 1044 }
684 1045
685 }; // namespace nt 1046 }; // namespace nt
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698