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
3 // found in the LICENSE file.
4 
5 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
6 
7 #include <assert.h>
8 #include <stdlib.h>
9 
10 #include <memory>
11 #include <string>
12 
13 namespace {
14 
15 // Function pointers used for registry access.
16 RtlInitUnicodeStringFunction g_rtl_init_unicode_string = nullptr;
17 NtCreateKeyFunction g_nt_create_key = nullptr;
18 NtDeleteKeyFunction g_nt_delete_key = nullptr;
19 NtOpenKeyExFunction g_nt_open_key_ex = nullptr;
20 NtCloseFunction g_nt_close = nullptr;
21 NtQueryKeyFunction g_nt_query_key = nullptr;
22 NtEnumerateKeyFunction g_nt_enumerate_key = nullptr;
23 NtQueryValueKeyFunction g_nt_query_value_key = nullptr;
24 NtSetValueKeyFunction g_nt_set_value_key = nullptr;
25 
26 // Lazy init.  No concern about concurrency in chrome_elf.
27 bool g_initialized = false;
28 bool g_system_install = false;
29 bool g_wow64_proc = false;
30 wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\";
31 wchar_t g_kRegPathHKCU[nt::g_kRegMaxPathLen + 1] = L"";
32 wchar_t g_current_user_sid_string[nt::g_kRegMaxPathLen + 1] = L"";
33 
34 // Max number of tries for system API calls when STATUS_BUFFER_OVERFLOW or
35 // STATUS_BUFFER_TOO_SMALL can be returned.
36 enum { kMaxTries = 5 };
37 
38 // For testing only.
39 wchar_t g_HKLM_override[nt::g_kRegMaxPathLen + 1] = L"";
40 wchar_t g_HKCU_override[nt::g_kRegMaxPathLen + 1] = L"";
41 
42 //------------------------------------------------------------------------------
43 // Initialization - LOCAL
44 //------------------------------------------------------------------------------
45 
46 // Not using install_static, to prevent circular dependency.
IsThisProcSystem()47 bool IsThisProcSystem() {
48   wchar_t program_dir[MAX_PATH] = {};
49   wchar_t* cmd_line = GetCommandLineW();
50   // If our command line starts with the "Program Files" or
51   // "Program Files (x86)" path, we're system.
52   DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, MAX_PATH);
53   if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
54     return true;
55 
56   ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, MAX_PATH);
57   if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
58     return true;
59 
60   return false;
61 }
62 
IsThisProcWow64()63 bool IsThisProcWow64() {
64   // Using BOOL type for compat with IsWow64Process() system API.
65   BOOL is_wow64 = FALSE;
66 
67   // API might not exist, so dynamic lookup.
68   using IsWow64ProcessFunction = decltype(&IsWow64Process);
69   IsWow64ProcessFunction is_wow64_process =
70       reinterpret_cast<IsWow64ProcessFunction>(::GetProcAddress(
71           ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
72   if (!is_wow64_process)
73     return false;
74   if (!is_wow64_process(::GetCurrentProcess(), &is_wow64))
75     return false;
76   return is_wow64 ? true : false;
77 }
78 
InitNativeRegApi()79 bool InitNativeRegApi() {
80   HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
81 
82   // Setup the global function pointers for registry access.
83   g_rtl_init_unicode_string = reinterpret_cast<RtlInitUnicodeStringFunction>(
84       ::GetProcAddress(ntdll, "RtlInitUnicodeString"));
85 
86   g_nt_create_key = reinterpret_cast<NtCreateKeyFunction>(
87       ::GetProcAddress(ntdll, "NtCreateKey"));
88 
89   g_nt_delete_key = reinterpret_cast<NtDeleteKeyFunction>(
90       ::GetProcAddress(ntdll, "NtDeleteKey"));
91 
92   g_nt_open_key_ex = reinterpret_cast<NtOpenKeyExFunction>(
93       ::GetProcAddress(ntdll, "NtOpenKeyEx"));
94 
95   g_nt_close =
96       reinterpret_cast<NtCloseFunction>(::GetProcAddress(ntdll, "NtClose"));
97 
98   g_nt_query_key = reinterpret_cast<NtQueryKeyFunction>(
99       ::GetProcAddress(ntdll, "NtQueryKey"));
100 
101   g_nt_enumerate_key = reinterpret_cast<NtEnumerateKeyFunction>(
102       ::GetProcAddress(ntdll, "NtEnumerateKey"));
103 
104   g_nt_query_value_key = reinterpret_cast<NtQueryValueKeyFunction>(
105       ::GetProcAddress(ntdll, "NtQueryValueKey"));
106 
107   g_nt_set_value_key = reinterpret_cast<NtSetValueKeyFunction>(
108       ::GetProcAddress(ntdll, "NtSetValueKey"));
109 
110   if (!g_rtl_init_unicode_string || !g_nt_create_key || !g_nt_open_key_ex ||
111       !g_nt_delete_key || !g_nt_close || !g_nt_query_key ||
112       !g_nt_enumerate_key || !g_nt_query_value_key || !g_nt_set_value_key)
113     return false;
114 
115   // We need to set HKCU based on the sid of the current user account.
116   RtlFormatCurrentUserKeyPathFunction rtl_current_user_string =
117       reinterpret_cast<RtlFormatCurrentUserKeyPathFunction>(
118           ::GetProcAddress(ntdll, "RtlFormatCurrentUserKeyPath"));
119 
120   RtlFreeUnicodeStringFunction rtl_free_unicode_str =
121       reinterpret_cast<RtlFreeUnicodeStringFunction>(
122           ::GetProcAddress(ntdll, "RtlFreeUnicodeString"));
123 
124   if (!rtl_current_user_string || !rtl_free_unicode_str)
125     return false;
126 
127   UNICODE_STRING current_user_reg_path;
128   if (!NT_SUCCESS(rtl_current_user_string(&current_user_reg_path)))
129     return false;
130 
131   // Finish setting up global HKCU path.
132   ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, nt::g_kRegMaxPathLen);
133   ::wcsncat(g_kRegPathHKCU, L"\\",
134             (nt::g_kRegMaxPathLen - ::wcslen(g_kRegPathHKCU)));
135   // Keep the sid string as well.
136   wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\');
137   ptr++;
138   ::wcsncpy(g_current_user_sid_string, ptr, nt::g_kRegMaxPathLen);
139   rtl_free_unicode_str(&current_user_reg_path);
140 
141   // Figure out if this is a system or user install.
142   g_system_install = IsThisProcSystem();
143 
144   // Figure out if this is a WOW64 process.
145   g_wow64_proc = IsThisProcWow64();
146 
147   g_initialized = true;
148   return true;
149 }
150 
151 //------------------------------------------------------------------------------
152 // Reg WOW64 Redirection - LOCAL
153 //
154 // How registry redirection works directly calling NTDLL APIs:
155 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
156 // - NOTE: On >= Win7, reflection support was removed.
157 // -
158 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384253(v=vs.85).aspx
159 //
160 // - 1) 32-bit / WOW64 process:
161 //     a) Default access WILL be redirected to WOW64.
162 //     b) KEY_WOW64_32KEY access WILL be redirected to WOW64.
163 //     c) KEY_WOW64_64KEY access will NOT be redirected to WOW64.
164 //
165 // - 2) 64-bit process:
166 //     a) Default access will NOT be redirected to WOW64.
167 //     b) KEY_WOW64_32KEY access will NOT be redirected to WOW64.
168 //     c) KEY_WOW64_64KEY access will NOT be redirected to WOW64.
169 //
170 // - Key point from above is that NTDLL redirects and respects access
171 //   overrides for WOW64 calling processes.  But does NOT do any of that if the
172 //   calling process is 64-bit.  2b is surprising and troublesome.
173 //
174 // How registry redirection works using these nt_registry APIs:
175 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
176 // - These APIs will behave the same as NTDLL above, EXCEPT for 2b.
177 //   nt_registry APIs will respect the override access flags for all processes.
178 //
179 // - How the WOW64 redirection decision trees / Nodes work below:
180 //
181 //   The HKLM and HKCU decision trees represent the information at the MSDN
182 //   link above... but in a way that generates a decision about whether a
183 //   registry path should be subject to WOW64 redirection.  The tree is
184 //   traversed as you scan along the registry path in question.
185 //
186 //    - Each Node contains a chunk of registry subkey(s) to match.
187 //    - If it is NOT matched, traversal is done.
188 //    - If it is matched:
189 //       - Current state of |redirection_type| for the whole registry path is
190 //         updated.
191 //       - If |next| is empty, traversal is done.
192 //       - Otherwise, |next| is an array of child Nodes to try to match against.
193 //         Loop.
194 //------------------------------------------------------------------------------
195 
196 // This enum defines states for how to handle redirection.
197 // NOTE: When WOW64 redirection should happen, the redirect subkey can be either
198 //       before or after the latest Node match.  Unfortunately not consistent.
199 enum RedirectionType { SHARED = 0, REDIRECTED_BEFORE, REDIRECTED_AFTER };
200 
201 struct Node {
202   template <size_t len, size_t n_len>
Node__anon2330ca6c0111::Node203   constexpr Node(const wchar_t (&wcs)[len],
204                  RedirectionType rt,
205                  const Node (&n)[n_len])
206       : to_match(wcs),
207         to_match_len(len - 1),
208         redirection_type(rt),
209         next(n),
210         next_len(n_len) {}
211 
212   template <size_t len>
Node__anon2330ca6c0111::Node213   constexpr Node(const wchar_t (&wcs)[len], RedirectionType rt)
214       : to_match(wcs),
215         to_match_len(len - 1),
216         redirection_type(rt),
217         next(nullptr),
218         next_len(0) {}
219 
220   const wchar_t* to_match;
221   size_t to_match_len;
222   // If a match, this is the new state of how to redirect.
223   RedirectionType redirection_type;
224   // |next| is nullptr or an array of Nodes of length |array_len|.
225   const Node* next;
226   size_t next_len;
227 };
228 
229 // HKLM or HKCU SOFTWARE\Classes is shared by default.  Specific subkeys under
230 // Classes are redirected to SOFTWARE\WOW6432Node\Classes\<subkey> though.
231 constexpr Node kClassesSubtree[] = {{L"CLSID", REDIRECTED_BEFORE},
232                                     {L"DirectShow", REDIRECTED_BEFORE},
233                                     {L"Interface", REDIRECTED_BEFORE},
234                                     {L"Media Type", REDIRECTED_BEFORE},
235                                     {L"MediaFoundation", REDIRECTED_BEFORE}};
236 
237 // These specific HKLM\SOFTWARE subkeys are shared.  Specific
238 // subkeys under Classes are redirected though... see classes_subtree.
239 constexpr Node kHklmSoftwareSubtree[] = {
240     // TODO(pennymac): when MS fixes compiler bug, or bots are all using clang,
241     // remove the "Classes" subkeys below and replace with:
242     // {L"Classes", SHARED, kClassesSubtree},
243     // https://connect.microsoft.com/VisualStudio/feedback/details/3104499
244     {L"Classes\\CLSID", REDIRECTED_BEFORE},
245     {L"Classes\\DirectShow", REDIRECTED_BEFORE},
246     {L"Classes\\Interface", REDIRECTED_BEFORE},
247     {L"Classes\\Media Type", REDIRECTED_BEFORE},
248     {L"Classes\\MediaFoundation", REDIRECTED_BEFORE},
249     {L"Classes", SHARED},
250 
251     {L"Clients", SHARED},
252     {L"Microsoft\\COM3", SHARED},
253     {L"Microsoft\\Cryptography\\Calais\\Current", SHARED},
254     {L"Microsoft\\Cryptography\\Calais\\Readers", SHARED},
255     {L"Microsoft\\Cryptography\\Services", SHARED},
256 
257     {L"Microsoft\\CTF\\SystemShared", SHARED},
258     {L"Microsoft\\CTF\\TIP", SHARED},
259     {L"Microsoft\\DFS", SHARED},
260     {L"Microsoft\\Driver Signing", SHARED},
261     {L"Microsoft\\EnterpriseCertificates", SHARED},
262 
263     {L"Microsoft\\EventSystem", SHARED},
264     {L"Microsoft\\MSMQ", SHARED},
265     {L"Microsoft\\Non-Driver Signing", SHARED},
266     {L"Microsoft\\Notepad\\DefaultFonts", SHARED},
267     {L"Microsoft\\OLE", SHARED},
268 
269     {L"Microsoft\\RAS", SHARED},
270     {L"Microsoft\\RPC", SHARED},
271     {L"Microsoft\\SOFTWARE\\Microsoft\\Shared Tools\\MSInfo", SHARED},
272     {L"Microsoft\\SystemCertificates", SHARED},
273     {L"Microsoft\\TermServLicensing", SHARED},
274 
275     {L"Microsoft\\Transaction Server", SHARED},
276     {L"Microsoft\\Windows\\CurrentVersion\\App Paths", SHARED},
277     {L"Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cursors\\Schemes",
278      SHARED},
279     {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers", SHARED},
280     {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons", SHARED},
281 
282     {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap", SHARED},
283     {L"Microsoft\\Windows\\CurrentVersion\\Group Policy", SHARED},
284     {L"Microsoft\\Windows\\CurrentVersion\\Policies", SHARED},
285     {L"Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", SHARED},
286     {L"Microsoft\\Windows\\CurrentVersion\\Setup", SHARED},
287 
288     {L"Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations", SHARED},
289     {L"Microsoft\\Windows NT\\CurrentVersion\\Console", SHARED},
290     {L"Microsoft\\Windows NT\\CurrentVersion\\FontDpi", SHARED},
291     {L"Microsoft\\Windows NT\\CurrentVersion\\FontLink", SHARED},
292     {L"Microsoft\\Windows NT\\CurrentVersion\\FontMapper", SHARED},
293 
294     {L"Microsoft\\Windows NT\\CurrentVersion\\Fonts", SHARED},
295     {L"Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", SHARED},
296     {L"Microsoft\\Windows NT\\CurrentVersion\\Gre_Initialize", SHARED},
297     {L"Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options",
298      SHARED},
299     {L"Microsoft\\Windows NT\\CurrentVersion\\LanguagePack", SHARED},
300 
301     {L"Microsoft\\Windows NT\\CurrentVersion\\NetworkCards", SHARED},
302     {L"Microsoft\\Windows NT\\CurrentVersion\\Perflib", SHARED},
303     {L"Microsoft\\Windows NT\\CurrentVersion\\Ports", SHARED},
304     {L"Microsoft\\Windows NT\\CurrentVersion\\Print", SHARED},
305     {L"Microsoft\\Windows NT\\CurrentVersion\\ProfileList", SHARED},
306 
307     {L"Microsoft\\Windows NT\\CurrentVersion\\Time Zones", SHARED},
308     {L"Policies", SHARED},
309     {L"RegisteredApplications", SHARED}};
310 
311 // HKCU is entirely shared, except for a few specific Classes subkeys which
312 // are redirected.  See |classes_subtree|.
313 constexpr Node kRedirectionDecisionTreeHkcu = {L"SOFTWARE\\Classes", SHARED,
314                                                kClassesSubtree};
315 
316 // HKLM\SOFTWARE is redirected by default to SOFTWARE\WOW6432Node.  Specific
317 // subkeys under SOFTWARE are shared though... see |hklm_software_subtree|.
318 constexpr Node kRedirectionDecisionTreeHklm = {L"SOFTWARE", REDIRECTED_AFTER,
319                                                kHklmSoftwareSubtree};
320 
321 // Main redirection handler function.
322 // If redirection is required, change is made to |subkey_path| in place.
323 //
324 // - This function should be called BEFORE concatenating |subkey_path| with the
325 //   root hive or calling ParseFullRegPath().
326 // - Also, |subkey_path| should be passed to SanitizeSubkeyPath() before calling
327 //   this function.
ProcessRedirection(nt::ROOT_KEY root,ACCESS_MASK access,std::wstring * subkey_path)328 void ProcessRedirection(nt::ROOT_KEY root,
329                         ACCESS_MASK access,
330                         std::wstring* subkey_path) {
331   static constexpr wchar_t kRedirectBefore[] = L"WOW6432Node\\";
332   static constexpr wchar_t kRedirectAfter[] = L"\\WOW6432Node";
333 
334   assert(subkey_path);
335   assert(subkey_path->empty() || subkey_path->front() != L'\\');
336   assert(subkey_path->empty() || subkey_path->back() != L'\\');
337   assert(root != nt::AUTO);
338 
339   // |subkey_path| could legitimately be empty.
340   if (subkey_path->empty() ||
341       (access & KEY_WOW64_32KEY && access & KEY_WOW64_64KEY))
342     return;
343 
344   // No redirection during testing when there's already an override.
345   // Otherwise, the testing redirect directory Software\Chromium\TempTestKeys
346   // would get WOW64 redirected if root_key == HKLM in this function.
347   if (root == nt::HKCU ? *g_HKCU_override : *g_HKLM_override)
348     return;
349 
350   // WOW64 redirection only supported on x64 architecture.  Return if x86.
351   SYSTEM_INFO system_info = {};
352   ::GetNativeSystemInfo(&system_info);
353   if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
354     return;
355 
356   bool use_wow64 = g_wow64_proc;
357   // Consider KEY_WOW64_32KEY and KEY_WOW64_64KEY override access flags.
358   if (access & KEY_WOW64_32KEY)
359     use_wow64 = true;
360   if (access & KEY_WOW64_64KEY)
361     use_wow64 = false;
362 
363   // If !use_wow64, there's nothing more to do.
364   if (!use_wow64)
365     return;
366 
367   // The root of the decision trees are an array of 1.
368   size_t node_array_len = 1;
369   // Pick which decision tree to use.
370   const Node* current_node = (root == nt::HKCU) ? &kRedirectionDecisionTreeHkcu
371                                                 : &kRedirectionDecisionTreeHklm;
372 
373   // The following loop works on the |subkey_path| from left to right.
374   // |position| tracks progress along |subkey_path|.
375   const wchar_t* position = subkey_path->c_str();
376   // Hold a count of chars left after position, for efficient calculations.
377   size_t chars_left = subkey_path->length();
378   // |redirect_state| holds the latest state of redirection requirement.
379   RedirectionType redirect_state = SHARED;
380   // |insertion_point| tracks latest spot for redirection subkey to be inserted.
381   const wchar_t* insertion_point = nullptr;
382   // |insert_string| tracks which redirection string would be inserted.
383   const wchar_t* insert_string = nullptr;
384 
385   size_t node_index = 0;
386   while (node_index < node_array_len) {
387     size_t current_to_match_len = current_node->to_match_len;
388     // Make sure the remainder of the path is at least as long as the current
389     // subkey to match.
390     if (chars_left >= current_to_match_len) {
391       // Do case insensitive comparisons.
392       if (!::wcsnicmp(position, current_node->to_match, current_to_match_len)) {
393         // Make sure not to match on a substring.
394         if (*(position + current_to_match_len) == L'\\' ||
395             *(position + current_to_match_len) == L'\0') {
396           // MATCH!
397           // -------------------------------------------------------------------
398           // 1) Update state of redirection.
399           redirect_state = current_node->redirection_type;
400           // 1.5) If new state is to redirect, the new insertion point will be
401           //      either right before or right after this match.
402           if (redirect_state == REDIRECTED_BEFORE) {
403             insertion_point = position;
404             insert_string = kRedirectBefore;
405           } else if (redirect_state == REDIRECTED_AFTER) {
406             insertion_point = position + current_to_match_len;
407             insert_string = kRedirectAfter;
408           }
409           // 2) Adjust |position| along the subkey path.
410           position += current_to_match_len;
411           chars_left -= current_to_match_len;
412           // 2.5) Increment the position, to move past path seperator(s).
413           while (*position == L'\\') {
414             ++position;
415             --chars_left;
416           }
417           // 3) Move our loop parameters to the |next| array of Nodes.
418           node_array_len = current_node->next_len;
419           current_node = current_node->next;
420           node_index = 0;
421           // 4) Finish this loop and start on new array.
422           continue;
423         }
424       }
425     }
426 
427     // Move to the next node in the array if we didn't match this loop.
428     ++current_node;
429     ++node_index;
430   }
431 
432   if (redirect_state == SHARED)
433     return;
434 
435   // Insert the redirection into |subkey_path|, at |insertion_point|.
436   subkey_path->insert((insertion_point - subkey_path->c_str()), insert_string);
437 }
438 
439 //------------------------------------------------------------------------------
440 // Reg Path Utilities - LOCAL
441 //------------------------------------------------------------------------------
442 
ConvertRootKey(nt::ROOT_KEY root)443 std::wstring ConvertRootKey(nt::ROOT_KEY root) {
444   assert(root != nt::AUTO);
445 
446   if (root == nt::HKCU && *g_HKCU_override) {
447     std::wstring temp = g_kRegPathHKCU;
448     temp.append(g_HKCU_override);
449     temp.append(L"\\");
450     return temp;
451   } else if (root == nt::HKLM && *g_HKLM_override) {
452     // Yes, HKLM override goes into HKCU.  This is not a typo.
453     std::wstring temp = g_kRegPathHKCU;
454     temp.append(g_HKLM_override);
455     temp.append(L"\\");
456     return temp;
457   }
458 
459   return (root == nt::HKCU) ? g_kRegPathHKCU : g_kRegPathHKLM;
460 }
461 
462 // This utility should be called on an externally provided subkey path.
463 // - Ensures there are no starting or trailing backslashes, and no more than
464 // - one backslash in a row.
465 // - Note from MSDN: "Key names cannot include the backslash character (\),
466 //   but any other printable character can be used.  Value names and data can
467 //   include the backslash character."
SanitizeSubkeyPath(std::wstring * input)468 void SanitizeSubkeyPath(std::wstring* input) {
469   assert(input);
470 
471   // Remove trailing backslashes.
472   size_t last_valid_pos = input->find_last_not_of(L'\\');
473   if (last_valid_pos == std::wstring::npos) {
474     // The string is all backslashes, or it's empty.  Clear and abort.
475     input->clear();
476     return;
477   }
478   // Chop off the trailing backslashes.
479   input->resize(last_valid_pos + 1);
480 
481   // Remove leading backslashes.
482   input->erase(0, input->find_first_not_of(L'\\'));
483 
484   // Replace any occurances of more than 1 backslash in a row with just 1.
485   size_t index = input->find_first_of(L"\\");
486   while (index != std::wstring::npos) {
487     // Remove a second consecutive backslash, and leave index where it is,
488     // or move to the next backslash in the string.
489     if ((*input)[index + 1] == L'\\')
490       input->erase(index + 1, 1);
491     else
492       index = input->find_first_of(L"\\", index + 1);
493   }
494 }
495 
496 // Turns a root and subkey path into the registry base hive and the rest of the
497 // subkey tokens.
498 // - |converted_root| should come directly out of ConvertRootKey function.
499 // - |subkey_path| should be passed to SanitizeSubkeyPath() first.
500 // - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\".
ParseFullRegPath(const std::wstring & converted_root,const std::wstring & subkey_path,std::wstring * out_base,std::vector<std::wstring> * subkeys)501 bool ParseFullRegPath(const std::wstring& converted_root,
502                       const std::wstring& subkey_path,
503                       std::wstring* out_base,
504                       std::vector<std::wstring>* subkeys) {
505   out_base->clear();
506   subkeys->clear();
507   std::wstring temp_path;
508 
509   // Special case if there is testing redirection set up.
510   if (*g_HKCU_override || *g_HKLM_override) {
511     // Why process |converted_root|?  To handle reg redirection used by tests.
512     // E.g.:
513     // |converted_root| = "\REGISTRY\USER\S-1-5-21-39260824-743453154-142223018-
514     // 716772\Software\Chromium\TempTestKeys\13110669370890870$94c6ed9d-bc34-
515     // 44f3-a0b3-9eee2d3f2f82\".
516     // |subkey_path| = "SOFTWARE\Google\Chrome\BrowserSec".
517     //
518     // Note: bypassing the starting backslash in the |converted_root|.
519     temp_path.append(converted_root, 1, converted_root.size() - 1);
520   }
521   temp_path.append(subkey_path);
522 
523   // Tokenize the full path.
524   size_t find_start = 0;
525   size_t delimiter = temp_path.find_first_of(L'\\');
526   while (delimiter != std::wstring::npos) {
527     subkeys->emplace_back(temp_path, find_start, delimiter - find_start);
528     // Move past the backslash.
529     find_start = delimiter + 1;
530     delimiter = temp_path.find_first_of(L'\\', find_start);
531   }
532   // Get the last token if there is one.
533   if (!temp_path.empty())
534     subkeys->emplace_back(temp_path, find_start);
535 
536   // Special case if there is testing redirection set up.
537   if (*g_HKCU_override || *g_HKLM_override) {
538     // The base hive for HKCU needs to include the user SID.
539     uint32_t num_base_tokens = 2;
540     if (0 == temp_path.compare(0, 14, L"REGISTRY\\USER\\"))
541       num_base_tokens = 3;
542 
543     if (subkeys->size() < num_base_tokens)
544       return false;
545 
546     // Pull out the base hive tokens.
547     out_base->push_back(L'\\');
548     for (size_t i = 0; i < num_base_tokens; ++i) {
549       out_base->append((*subkeys)[i]);
550       out_base->push_back(L'\\');
551     }
552     subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens);
553   } else {
554     out_base->assign(converted_root);
555   }
556 
557   return true;
558 }
559 
560 // String safety.
561 // - NOTE: only working with wchar_t here.
562 // - Also ensures the content of |value_bytes| is at least a terminator.
563 // - Pass "true" for |multi| for MULTISZ.
EnsureTerminatedSZ(std::vector<BYTE> * value_bytes,bool multi)564 void EnsureTerminatedSZ(std::vector<BYTE>* value_bytes, bool multi) {
565   DWORD terminator_size = sizeof(wchar_t);
566 
567   if (multi)
568     terminator_size = 2 * sizeof(wchar_t);
569 
570   // Ensure content is at least the size of a terminator.
571   if (value_bytes->size() < terminator_size) {
572     value_bytes->insert(value_bytes->end(),
573                         terminator_size - value_bytes->size(), 0);
574   }
575 
576   // Sanity check content size based on character size.
577   DWORD modulo = value_bytes->size() % sizeof(wchar_t);
578   value_bytes->insert(value_bytes->end(), modulo, 0);
579 
580   // Now finally check for trailing terminator.
581   bool terminated = true;
582   size_t last_element = value_bytes->size() - 1;
583   for (size_t i = 0; i < terminator_size; i++) {
584     if ((*value_bytes)[last_element - i] != 0) {
585       terminated = false;
586       break;
587     }
588   }
589 
590   if (terminated)
591     return;
592 
593   // Append a full terminator to be safe.
594   value_bytes->insert(value_bytes->end(), terminator_size, 0);
595 
596   return;
597 }
598 
599 //------------------------------------------------------------------------------
600 // Misc wrapper functions - LOCAL
601 //------------------------------------------------------------------------------
602 
CreateKeyWrapper(const std::wstring & key_path,ACCESS_MASK access,HANDLE * out_handle,ULONG * create_or_open OPTIONAL)603 NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
604                           ACCESS_MASK access,
605                           HANDLE* out_handle,
606                           ULONG* create_or_open OPTIONAL) {
607   UNICODE_STRING key_path_uni = {};
608   g_rtl_init_unicode_string(&key_path_uni, key_path.c_str());
609 
610   OBJECT_ATTRIBUTES obj = {};
611   InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
612                              nullptr);
613 
614   return g_nt_create_key(out_handle, access, &obj, 0, nullptr,
615                          REG_OPTION_NON_VOLATILE, create_or_open);
616 }
617 
618 }  // namespace
619 
620 namespace nt {
621 
622 //------------------------------------------------------------------------------
623 // Create, open, delete, close functions
624 //------------------------------------------------------------------------------
625 
CreateRegKey(ROOT_KEY root,const wchar_t * key_path,ACCESS_MASK access,HANDLE * out_handle OPTIONAL)626 bool CreateRegKey(ROOT_KEY root,
627                   const wchar_t* key_path,
628                   ACCESS_MASK access,
629                   HANDLE* out_handle OPTIONAL) {
630   // |key_path| can be null or empty, but it can't be longer than
631   // |g_kRegMaxPathLen| at this point.
632   if (key_path &&
633       ::wcsnlen(key_path, g_kRegMaxPathLen + 1) == g_kRegMaxPathLen + 1)
634     return false;
635 
636   if (!g_initialized && !InitNativeRegApi())
637     return false;
638 
639   if (root == nt::AUTO)
640     root = g_system_install ? nt::HKLM : nt::HKCU;
641 
642   std::wstring redirected_key_path;
643   if (key_path) {
644     redirected_key_path = key_path;
645     SanitizeSubkeyPath(&redirected_key_path);
646     ProcessRedirection(root, access, &redirected_key_path);
647   }
648 
649   std::wstring current_path;
650   std::vector<std::wstring> subkeys;
651   if (!ParseFullRegPath(ConvertRootKey(root), redirected_key_path,
652                         &current_path, &subkeys))
653     return false;
654 
655   // Open the base hive first.  It should always exist already.
656   HANDLE last_handle = INVALID_HANDLE_VALUE;
657   NTSTATUS status =
658       CreateKeyWrapper(current_path, access, &last_handle, nullptr);
659   if (!NT_SUCCESS(status))
660     return false;
661 
662   size_t subkeys_size = subkeys.size();
663   if (subkeys_size != 0)
664     g_nt_close(last_handle);
665 
666   // Recursively open/create each subkey.
667   std::vector<HANDLE> rollback;
668   bool success = true;
669   for (size_t i = 0; i < subkeys_size; i++) {
670     current_path.append(subkeys[i]);
671     current_path.push_back(L'\\');
672 
673     // Process the latest subkey.
674     ULONG created = 0;
675     HANDLE key_handle = INVALID_HANDLE_VALUE;
676     status =
677         CreateKeyWrapper(current_path.c_str(), access, &key_handle, &created);
678     if (!NT_SUCCESS(status)) {
679       success = false;
680       break;
681     }
682 
683     if (i == subkeys_size - 1) {
684       last_handle = key_handle;
685     } else {
686       // Save any subkey handle created, in case of rollback.
687       if (created == REG_CREATED_NEW_KEY)
688         rollback.push_back(key_handle);
689       else
690         g_nt_close(key_handle);
691     }
692   }
693 
694   if (!success) {
695     // Delete any subkeys created.
696     for (HANDLE handle : rollback) {
697       g_nt_delete_key(handle);
698     }
699   }
700   for (HANDLE handle : rollback) {
701     // Close the rollback handles, on success or failure.
702     g_nt_close(handle);
703   }
704   if (!success)
705     return false;
706 
707   // See if caller wants the handle left open.
708   if (out_handle)
709     *out_handle = last_handle;
710   else
711     g_nt_close(last_handle);
712 
713   return true;
714 }
715 
OpenRegKey(ROOT_KEY root,const wchar_t * key_path,ACCESS_MASK access,HANDLE * out_handle,NTSTATUS * error_code OPTIONAL)716 bool OpenRegKey(ROOT_KEY root,
717                 const wchar_t* key_path,
718                 ACCESS_MASK access,
719                 HANDLE* out_handle,
720                 NTSTATUS* error_code OPTIONAL) {
721   // |key_path| can be null or empty, but it can't be longer than
722   // |g_kRegMaxPathLen| at this point.
723   if (key_path &&
724       ::wcsnlen(key_path, g_kRegMaxPathLen + 1) == g_kRegMaxPathLen + 1)
725     return false;
726 
727   if (!g_initialized && !InitNativeRegApi())
728     return false;
729 
730   NTSTATUS status = STATUS_UNSUCCESSFUL;
731   UNICODE_STRING key_path_uni = {};
732   OBJECT_ATTRIBUTES obj = {};
733   *out_handle = INVALID_HANDLE_VALUE;
734 
735   if (root == nt::AUTO)
736     root = g_system_install ? nt::HKLM : nt::HKCU;
737 
738   std::wstring full_path;
739   if (key_path) {
740     full_path = key_path;
741     SanitizeSubkeyPath(&full_path);
742     ProcessRedirection(root, access, &full_path);
743   }
744   full_path.insert(0, ConvertRootKey(root));
745 
746   g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
747   InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
748                              NULL);
749 
750   status = g_nt_open_key_ex(out_handle, access, &obj, 0);
751   // See if caller wants the NTSTATUS.
752   if (error_code)
753     *error_code = status;
754 
755   if (NT_SUCCESS(status))
756     return true;
757 
758   return false;
759 }
760 
DeleteRegKey(HANDLE key)761 bool DeleteRegKey(HANDLE key) {
762   if (!g_initialized && !InitNativeRegApi())
763     return false;
764 
765   NTSTATUS status = g_nt_delete_key(key);
766 
767   return NT_SUCCESS(status);
768 }
769 
770 // wrapper function
DeleteRegKey(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path)771 bool DeleteRegKey(ROOT_KEY root,
772                   WOW64_OVERRIDE wow64_override,
773                   const wchar_t* key_path) {
774   HANDLE key = INVALID_HANDLE_VALUE;
775 
776   if (!OpenRegKey(root, key_path, DELETE | wow64_override, &key, nullptr))
777     return false;
778 
779   if (!DeleteRegKey(key)) {
780     CloseRegKey(key);
781     return false;
782   }
783 
784   CloseRegKey(key);
785   return true;
786 }
787 
CloseRegKey(HANDLE key)788 void CloseRegKey(HANDLE key) {
789   if (!g_initialized)
790     InitNativeRegApi();
791   g_nt_close(key);
792 }
793 
794 //------------------------------------------------------------------------------
795 // Getter functions
796 //------------------------------------------------------------------------------
797 
QueryRegKeyValue(HANDLE key,const wchar_t * value_name,ULONG * out_type,std::vector<BYTE> * out_buffer)798 bool QueryRegKeyValue(HANDLE key,
799                       const wchar_t* value_name,
800                       ULONG* out_type,
801                       std::vector<BYTE>* out_buffer) {
802   if (!g_initialized && !InitNativeRegApi())
803     return false;
804 
805   UNICODE_STRING value_uni = {};
806   g_rtl_init_unicode_string(&value_uni, value_name);
807 
808   // Use a loop here, to be a little more tolerant of concurrent registry
809   // changes.
810   NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
811   int tries = 0;
812   KEY_VALUE_FULL_INFORMATION* value_info = nullptr;
813   DWORD size_needed = sizeof(*value_info);
814   std::vector<BYTE> buffer(size_needed);
815   do {
816     buffer.resize(size_needed);
817     value_info = reinterpret_cast<KEY_VALUE_FULL_INFORMATION*>(buffer.data());
818 
819     ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
820                                     value_info, size_needed, &size_needed);
821   } while ((ntstatus == STATUS_BUFFER_OVERFLOW ||
822             ntstatus == STATUS_BUFFER_TOO_SMALL) &&
823            ++tries < kMaxTries);
824 
825   if (!NT_SUCCESS(ntstatus))
826     return false;
827 
828   *out_type = value_info->Type;
829   DWORD data_size = value_info->DataLength;
830 
831   if (data_size) {
832     // Move the data into |out_buffer| vector.
833     BYTE* data = reinterpret_cast<BYTE*>(value_info) + value_info->DataOffset;
834     out_buffer->assign(data, data + data_size);
835   } else {
836     out_buffer->clear();
837   }
838 
839   return true;
840 }
841 
842 // wrapper function
QueryRegValueDWORD(HANDLE key,const wchar_t * value_name,DWORD * out_dword)843 bool QueryRegValueDWORD(HANDLE key,
844                         const wchar_t* value_name,
845                         DWORD* out_dword) {
846   ULONG type = REG_NONE;
847   std::vector<BYTE> value_bytes;
848 
849   if (!QueryRegKeyValue(key, value_name, &type, &value_bytes) ||
850       type != REG_DWORD) {
851     return false;
852   }
853 
854   if (value_bytes.size() < sizeof(*out_dword))
855     return false;
856 
857   *out_dword = *(reinterpret_cast<DWORD*>(value_bytes.data()));
858 
859   return true;
860 }
861 
862 // wrapper function
QueryRegValueDWORD(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,DWORD * out_dword)863 bool QueryRegValueDWORD(ROOT_KEY root,
864                         WOW64_OVERRIDE wow64_override,
865                         const wchar_t* key_path,
866                         const wchar_t* value_name,
867                         DWORD* out_dword) {
868   HANDLE key = INVALID_HANDLE_VALUE;
869 
870   if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
871     return false;
872 
873   if (!QueryRegValueDWORD(key, value_name, out_dword)) {
874     CloseRegKey(key);
875     return false;
876   }
877 
878   CloseRegKey(key);
879   return true;
880 }
881 
882 // wrapper function
QueryRegValueSZ(HANDLE key,const wchar_t * value_name,std::wstring * out_sz)883 bool QueryRegValueSZ(HANDLE key,
884                      const wchar_t* value_name,
885                      std::wstring* out_sz) {
886   std::vector<BYTE> value_bytes;
887   ULONG type = REG_NONE;
888 
889   if (!QueryRegKeyValue(key, value_name, &type, &value_bytes) ||
890       (type != REG_SZ && type != REG_EXPAND_SZ)) {
891     return false;
892   }
893 
894   EnsureTerminatedSZ(&value_bytes, false);
895 
896   *out_sz = reinterpret_cast<wchar_t*>(value_bytes.data());
897 
898   return true;
899 }
900 
901 // wrapper function
QueryRegValueSZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,std::wstring * out_sz)902 bool QueryRegValueSZ(ROOT_KEY root,
903                      WOW64_OVERRIDE wow64_override,
904                      const wchar_t* key_path,
905                      const wchar_t* value_name,
906                      std::wstring* out_sz) {
907   HANDLE key = INVALID_HANDLE_VALUE;
908 
909   if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
910     return false;
911 
912   if (!QueryRegValueSZ(key, value_name, out_sz)) {
913     CloseRegKey(key);
914     return false;
915   }
916 
917   CloseRegKey(key);
918   return true;
919 }
920 
921 // wrapper function
QueryRegValueMULTISZ(HANDLE key,const wchar_t * value_name,std::vector<std::wstring> * out_multi_sz)922 bool QueryRegValueMULTISZ(HANDLE key,
923                           const wchar_t* value_name,
924                           std::vector<std::wstring>* out_multi_sz) {
925   std::vector<BYTE> value_bytes;
926   ULONG type = REG_NONE;
927 
928   if (!QueryRegKeyValue(key, value_name, &type, &value_bytes) ||
929       type != REG_MULTI_SZ) {
930     return false;
931   }
932 
933   EnsureTerminatedSZ(&value_bytes, true);
934 
935   // Make sure the out vector is empty to start.
936   out_multi_sz->clear();
937 
938   wchar_t* pointer = reinterpret_cast<wchar_t*>(value_bytes.data());
939   std::wstring temp = pointer;
940   // Loop.  Each string is separated by '\0'.  Another '\0' at very end (so 2 in
941   // a row).
942   while (!temp.empty()) {
943     pointer += temp.length() + 1;
944     out_multi_sz->push_back(std::move(temp));
945     temp = pointer;
946   }
947 
948   return true;
949 }
950 
951 // wrapper function
QueryRegValueMULTISZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,std::vector<std::wstring> * out_multi_sz)952 bool QueryRegValueMULTISZ(ROOT_KEY root,
953                           WOW64_OVERRIDE wow64_override,
954                           const wchar_t* key_path,
955                           const wchar_t* value_name,
956                           std::vector<std::wstring>* out_multi_sz) {
957   HANDLE key = INVALID_HANDLE_VALUE;
958 
959   if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
960     return false;
961 
962   if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
963     CloseRegKey(key);
964     return false;
965   }
966 
967   CloseRegKey(key);
968   return true;
969 }
970 
971 //------------------------------------------------------------------------------
972 // Setter functions
973 //------------------------------------------------------------------------------
974 
SetRegKeyValue(HANDLE key,const wchar_t * value_name,ULONG type,const BYTE * data,DWORD data_size)975 bool SetRegKeyValue(HANDLE key,
976                     const wchar_t* value_name,
977                     ULONG type,
978                     const BYTE* data,
979                     DWORD data_size) {
980   if (!g_initialized && !InitNativeRegApi())
981     return false;
982 
983   NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
984   UNICODE_STRING value_uni = {};
985   g_rtl_init_unicode_string(&value_uni, value_name);
986 
987   BYTE* non_const_data = const_cast<BYTE*>(data);
988   ntstatus =
989       g_nt_set_value_key(key, &value_uni, 0, type, non_const_data, data_size);
990 
991   if (NT_SUCCESS(ntstatus))
992     return true;
993 
994   return false;
995 }
996 
997 // wrapper function
SetRegValueDWORD(HANDLE key,const wchar_t * value_name,DWORD value)998 bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
999   return SetRegKeyValue(key, value_name, REG_DWORD,
1000                         reinterpret_cast<BYTE*>(&value), sizeof(value));
1001 }
1002 
1003 // wrapper function
SetRegValueDWORD(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,DWORD value)1004 bool SetRegValueDWORD(ROOT_KEY root,
1005                       WOW64_OVERRIDE wow64_override,
1006                       const wchar_t* key_path,
1007                       const wchar_t* value_name,
1008                       DWORD value) {
1009   HANDLE key = INVALID_HANDLE_VALUE;
1010 
1011   if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
1012     return false;
1013 
1014   if (!SetRegValueDWORD(key, value_name, value)) {
1015     CloseRegKey(key);
1016     return false;
1017   }
1018 
1019   return true;
1020 }
1021 
1022 // wrapper function
SetRegValueSZ(HANDLE key,const wchar_t * value_name,const std::wstring & value)1023 bool SetRegValueSZ(HANDLE key,
1024                    const wchar_t* value_name,
1025                    const std::wstring& value) {
1026   // Make sure the number of bytes in |value|, including EoS, fits in a DWORD.
1027   if (std::numeric_limits<DWORD>::max() <
1028       ((value.length() + 1) * sizeof(wchar_t)))
1029     return false;
1030 
1031   DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
1032   return SetRegKeyValue(key, value_name, REG_SZ,
1033                         reinterpret_cast<const BYTE*>(value.c_str()), size);
1034 }
1035 
1036 // wrapper function
SetRegValueSZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,const std::wstring & value)1037 bool SetRegValueSZ(ROOT_KEY root,
1038                    WOW64_OVERRIDE wow64_override,
1039                    const wchar_t* key_path,
1040                    const wchar_t* value_name,
1041                    const std::wstring& value) {
1042   HANDLE key = INVALID_HANDLE_VALUE;
1043 
1044   if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
1045     return false;
1046 
1047   if (!SetRegValueSZ(key, value_name, value)) {
1048     CloseRegKey(key);
1049     return false;
1050   }
1051 
1052   return true;
1053 }
1054 
1055 // wrapper function
SetRegValueMULTISZ(HANDLE key,const wchar_t * value_name,const std::vector<std::wstring> & values)1056 bool SetRegValueMULTISZ(HANDLE key,
1057                         const wchar_t* value_name,
1058                         const std::vector<std::wstring>& values) {
1059   std::vector<wchar_t> builder;
1060 
1061   for (auto& string : values) {
1062     // Just in case someone is passing in an illegal empty string
1063     // (not allowed in REG_MULTI_SZ), ignore it.
1064     if (!string.empty()) {
1065       for (const wchar_t& w : string) {
1066         builder.push_back(w);
1067       }
1068       builder.push_back(L'\0');
1069     }
1070   }
1071   // Add second null terminator to end REG_MULTI_SZ.
1072   builder.push_back(L'\0');
1073   // Handle rare case where the vector passed in was empty,
1074   // or only had an empty string.
1075   if (builder.size() == 1)
1076     builder.push_back(L'\0');
1077 
1078   if (std::numeric_limits<DWORD>::max() < builder.size())
1079     return false;
1080 
1081   return SetRegKeyValue(
1082       key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()),
1083       (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t));
1084 }
1085 
1086 // wrapper function
SetRegValueMULTISZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,const std::vector<std::wstring> & values)1087 bool SetRegValueMULTISZ(ROOT_KEY root,
1088                         WOW64_OVERRIDE wow64_override,
1089                         const wchar_t* key_path,
1090                         const wchar_t* value_name,
1091                         const std::vector<std::wstring>& values) {
1092   HANDLE key = INVALID_HANDLE_VALUE;
1093 
1094   if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
1095     return false;
1096 
1097   if (!SetRegValueMULTISZ(key, value_name, values)) {
1098     CloseRegKey(key);
1099     return false;
1100   }
1101 
1102   return true;
1103 }
1104 
1105 //------------------------------------------------------------------------------
1106 // Enumeration Support
1107 //------------------------------------------------------------------------------
1108 
QueryRegEnumerationInfo(HANDLE key,ULONG * out_subkey_count)1109 bool QueryRegEnumerationInfo(HANDLE key, ULONG* out_subkey_count) {
1110   if (!g_initialized && !InitNativeRegApi())
1111     return false;
1112 
1113   // Use a loop here, to be a little more tolerant of concurrent registry
1114   // changes.
1115   NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
1116   int tries = 0;
1117   // Start with sizeof the structure.  It's very common for the variable sized
1118   // "Class" element to be of length 0.
1119   KEY_FULL_INFORMATION* key_info = nullptr;
1120   DWORD size_needed = sizeof(*key_info);
1121   std::vector<BYTE> buffer(size_needed);
1122   do {
1123     buffer.resize(size_needed);
1124     key_info = reinterpret_cast<KEY_FULL_INFORMATION*>(buffer.data());
1125 
1126     ntstatus = g_nt_query_key(key, KeyFullInformation, key_info, size_needed,
1127                               &size_needed);
1128   } while ((ntstatus == STATUS_BUFFER_OVERFLOW ||
1129             ntstatus == STATUS_BUFFER_TOO_SMALL) &&
1130            ++tries < kMaxTries);
1131 
1132   if (!NT_SUCCESS(ntstatus))
1133     return false;
1134 
1135   // Move desired information to out variables.
1136   *out_subkey_count = key_info->SubKeys;
1137 
1138   return true;
1139 }
1140 
QueryRegSubkey(HANDLE key,ULONG subkey_index,std::wstring * out_subkey_name)1141 bool QueryRegSubkey(HANDLE key,
1142                     ULONG subkey_index,
1143                     std::wstring* out_subkey_name) {
1144   if (!g_initialized && !InitNativeRegApi())
1145     return false;
1146 
1147   // Use a loop here, to be a little more tolerant of concurrent registry
1148   // changes.
1149   NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
1150   int tries = 0;
1151   // Start with sizeof the structure, plus 12 characters.  It's very common for
1152   // key names to be < 12 characters (without being inefficient as an initial
1153   // allocation).
1154   KEY_BASIC_INFORMATION* subkey_info = nullptr;
1155   DWORD size_needed = sizeof(*subkey_info) + (12 * sizeof(wchar_t));
1156   std::vector<BYTE> buffer(size_needed);
1157   do {
1158     buffer.resize(size_needed);
1159     subkey_info = reinterpret_cast<KEY_BASIC_INFORMATION*>(buffer.data());
1160 
1161     ntstatus = g_nt_enumerate_key(key, subkey_index, KeyBasicInformation,
1162                                   subkey_info, size_needed, &size_needed);
1163   } while ((ntstatus == STATUS_BUFFER_OVERFLOW ||
1164             ntstatus == STATUS_BUFFER_TOO_SMALL) &&
1165            ++tries < kMaxTries);
1166 
1167   if (!NT_SUCCESS(ntstatus))
1168     return false;
1169 
1170   // Move desired information to out variables.
1171   // NOTE: NameLength is size of Name array in bytes.  Name array is also
1172   //       NOT null terminated!
1173   BYTE* name = reinterpret_cast<BYTE*>(subkey_info->Name);
1174   std::vector<BYTE> content(name, name + subkey_info->NameLength);
1175   EnsureTerminatedSZ(&content, false);
1176   out_subkey_name->assign(reinterpret_cast<wchar_t*>(content.data()));
1177 
1178   return true;
1179 }
1180 
1181 //------------------------------------------------------------------------------
1182 // Utils
1183 //------------------------------------------------------------------------------
1184 
GetCurrentUserSidString()1185 const wchar_t* GetCurrentUserSidString() {
1186   if (!g_initialized && !InitNativeRegApi())
1187     return nullptr;
1188 
1189   return g_current_user_sid_string;
1190 }
1191 
IsCurrentProcWow64()1192 bool IsCurrentProcWow64() {
1193   if (!g_initialized && !InitNativeRegApi())
1194     return false;
1195 
1196   return g_wow64_proc;
1197 }
1198 
SetTestingOverride(ROOT_KEY root,const std::wstring & new_path)1199 bool SetTestingOverride(ROOT_KEY root, const std::wstring& new_path) {
1200   if (!g_initialized && !InitNativeRegApi())
1201     return false;
1202 
1203   std::wstring sani_new_path = new_path;
1204   SanitizeSubkeyPath(&sani_new_path);
1205   if (sani_new_path.length() > g_kRegMaxPathLen)
1206     return false;
1207 
1208   if (root == HKCU || (root == AUTO && !g_system_install))
1209     ::wcsncpy(g_HKCU_override, sani_new_path.c_str(), nt::g_kRegMaxPathLen);
1210   else
1211     ::wcsncpy(g_HKLM_override, sani_new_path.c_str(), nt::g_kRegMaxPathLen);
1212 
1213   return true;
1214 }
1215 
GetTestingOverride(ROOT_KEY root)1216 std::wstring GetTestingOverride(ROOT_KEY root) {
1217   if (!g_initialized && !InitNativeRegApi())
1218     return std::wstring();
1219 
1220   if (root == HKCU || (root == AUTO && !g_system_install))
1221     return g_HKCU_override;
1222 
1223   return g_HKLM_override;
1224 }
1225 
1226 }  // namespace nt
1227