master
cpp 533 lines 16.5 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 registry.cpp
8
9 Abstract:
10
11 This file contains registry management helper function implementation.
12
13 --*/
14
15 #include "precomp.h"
16 #include "registry.hpp"
17 #include "svccomm.hpp"
18 #pragma hdrstop
19
20 namespace {
21 std::wstring GetKeyPath(_In_ HKEY Key)
22 {
23 if (Key == HKEY_LOCAL_MACHINE)
24 {
25 return L"HKLM";
26 }
27 else if (Key == HKEY_CLASSES_ROOT)
28 {
29 return L"HKCR";
30 }
31 else if (Key == HKEY_USERS)
32 {
33 return L"HKU";
34 }
35 else if (Key == HKEY_CURRENT_USER)
36 {
37 return L"HKCU";
38 }
39 else if (Key == HKEY_CURRENT_CONFIG)
40 {
41 return L"HKCC";
42 }
43
44 ULONG requiredSize{};
45 auto status = ZwQueryKey(Key, KeyNameInformation, nullptr, 0, &requiredSize);
46 if (status != STATUS_BUFFER_TOO_SMALL)
47 {
48 THROW_NTSTATUS(status);
49 }
50
51 std::vector<char> buffer(requiredSize, 0);
52
53 status = ZwQueryKey(Key, KeyNameInformation, buffer.data(), static_cast<ULONG>(buffer.size()), &requiredSize);
54 THROW_IF_NTSTATUS_FAILED(status);
55
56 const auto* info = reinterpret_cast<KEY_NAME_INFORMATION*>(buffer.data());
57
58 return std::wstring{info->Name, info->NameLength / sizeof(WCHAR)};
59 }
60
61 void ReportErrorIfFailed(_In_ LSTATUS Error, _In_ HKEY Key, _In_opt_ LPCWSTR Subkey, _In_opt_ LPCWSTR Value)
62 {
63 if (Error == ERROR_SUCCESS)
64 {
65 return;
66 }
67
68 const auto result = HRESULT_FROM_WIN32(Error);
69 if (Key == nullptr)
70 {
71 const auto errorString = wsl::windows::common::wslutil::GetSystemErrorString(result);
72 THROW_HR_WITH_USER_ERROR(
73 result, wsl::shared::Localization::MessageRegistryError(Subkey ? Subkey : L"[null]", errorString.c_str()).c_str());
74 }
75
76 auto path = GetKeyPath(Key);
77 if (Subkey != nullptr)
78 {
79 path += L'\\';
80 path += Subkey;
81 }
82
83 if (Value != nullptr)
84 {
85 path += L'\\';
86 path += Value;
87 }
88
89 if (wsl::windows::common::ExecutionContext::ShouldCollectErrorMessage())
90 {
91 const auto errorString = wsl::windows::common::wslutil::GetSystemErrorString(result);
92 THROW_HR_WITH_USER_ERROR(result, wsl::shared::Localization::MessageRegistryError(path.c_str(), errorString.c_str()).c_str());
93 }
94 else
95 {
96 THROW_HR_MSG(result, "An error occurred accessing the registry. Path: %ls", path.c_str());
97 }
98 }
99 } // namespace
100 void wsl::windows::common::registry::ClearSubkeys(_In_ HKEY Key)
101 {
102 for (const auto& e : EnumKeys(Key, KEY_READ))
103 {
104 DeleteKey(Key, e.first.c_str());
105 }
106 }
107
108 wil::unique_hkey wsl::windows::common::registry::CreateKey(
109 _In_ HKEY Key, _In_ LPCWSTR KeyName, _In_ REGSAM AccessMask, _Out_opt_ LPDWORD Disposition, _In_ DWORD Options)
110 {
111 wil::unique_hkey NewKey;
112 THROW_IF_WIN32_ERROR(RegCreateKeyExW(Key, KeyName, 0, nullptr, Options, AccessMask, nullptr, &NewKey, Disposition));
113
114 return NewKey;
115 }
116
117 bool wsl::windows::common::registry::DeleteKey(_In_ HKEY Key, _In_ LPCWSTR KeyName)
118 {
119 const LSTATUS Result = RegDeleteTreeW(Key, KeyName);
120 if (Result != ERROR_FILE_NOT_FOUND)
121 {
122 LOG_IF_WIN32_ERROR(Result);
123 }
124
125 return Result == NO_ERROR;
126 }
127
128 void wsl::windows::common::registry::DeleteKeyValue(_In_ HKEY Key, _In_ LPCWSTR KeyName)
129 {
130 const LSTATUS Result = RegDeleteKeyValueW(Key, nullptr, KeyName);
131 if (Result != ERROR_FILE_NOT_FOUND)
132 {
133 LOG_IF_WIN32_ERROR(Result);
134 }
135 }
136
137 void wsl::windows::common::registry::DeleteValue(_In_ HKEY Key, _In_ LPCWSTR KeyName)
138 {
139 const LSTATUS Result = RegDeleteValueW(Key, KeyName);
140 if (Result != ERROR_FILE_NOT_FOUND)
141 {
142 LOG_IF_WIN32_ERROR(Result);
143 }
144 }
145
146 std::map<std::wstring, wil::unique_hkey> wsl::windows::common::registry::EnumKeys(_In_ HKEY Key, _In_ DWORD SubkeyAccess)
147 {
148 // Get the max size of a subkey
149 DWORD MaxSubkeySize = 0;
150 QueryInfo(Key, &MaxSubkeySize);
151
152 std::map<std::wstring, wil::unique_hkey> keys;
153 for (DWORD Index = 0;; Index++)
154 {
155 std::wstring Name(MaxSubkeySize, '\0');
156 DWORD NameSize = MaxSubkeySize + 1;
157 const LSTATUS result = RegEnumKeyExW(Key, Index, Name.data(), &NameSize, nullptr, nullptr, nullptr, nullptr);
158 if (result == ERROR_NO_MORE_ITEMS)
159 {
160 break;
161 }
162
163 ReportErrorIfFailed(result, Key, nullptr, nullptr);
164
165 Name.resize(NameSize);
166
167 auto subKey = OpenKey(Key, Name.c_str(), SubkeyAccess);
168 keys.emplace(std::move(Name), std::move(subKey));
169 }
170
171 return keys;
172 }
173
174 std::vector<std::pair<GUID, std::wstring>> wsl::windows::common::registry::EnumGuidKeys(_In_ HKEY Key)
175 {
176 // Iterate through the provided keys and return a list of all sub-keys that are GUIDs.
177 WCHAR buffer[39];
178 std::vector<std::pair<GUID, std::wstring>> subKeys;
179 DWORD subKeyCount = 0;
180 QueryInfo(Key, nullptr, nullptr, nullptr, &subKeyCount);
181 subKeys.reserve(subKeyCount);
182 DWORD index = 0;
183 for (;;)
184 {
185 DWORD bufferSize = ARRAYSIZE(buffer);
186 const LSTATUS error = RegEnumKeyExW(Key, index, buffer, &bufferSize, nullptr, nullptr, nullptr, nullptr);
187 index += 1;
188 if (error == ERROR_NO_MORE_ITEMS)
189 {
190 break;
191 }
192 if ((error == ERROR_MORE_DATA) || ((error == ERROR_SUCCESS) && (bufferSize != (ARRAYSIZE(buffer) - 1))))
193 {
194 continue;
195 }
196
197 ReportErrorIfFailed(error, Key, nullptr, nullptr);
198
199 // Ignore any subkeys that are not GUIDs.
200 auto guid = wsl::shared::string::ToGuid(buffer);
201 if (!guid.has_value())
202 {
203 continue;
204 }
205
206 subKeys.emplace_back(guid.value(), std::wstring(buffer));
207 }
208
209 return subKeys;
210 }
211
212 std::vector<std::pair<std::wstring, DWORD>> wsl::windows::common::registry::EnumValues(_In_ HKEY Key)
213 {
214 std::vector<std::pair<std::wstring, DWORD>> values;
215 DWORD maxValueNameSize = 0;
216 DWORD valueCount = 0;
217 QueryInfo(Key, nullptr, &maxValueNameSize, nullptr, nullptr, &valueCount);
218 values.reserve(valueCount);
219
220 for (DWORD Index = 0;; Index++)
221 {
222 std::wstring valueName(maxValueNameSize, '\0');
223 DWORD size = maxValueNameSize + 1;
224 DWORD type = 0;
225
226 const auto error = RegEnumValueW(Key, Index, valueName.data(), &size, nullptr, &type, nullptr, nullptr);
227 if (error == ERROR_NO_MORE_ITEMS)
228 {
229 break;
230 }
231 THROW_IF_WIN32_ERROR(error);
232
233 valueName.resize(size);
234 values.emplace_back(std::move(valueName), type);
235 }
236
237 return values;
238 }
239
240 std::map<std::wstring, std::wstring> wsl::windows::common::registry::EnumStringValues(_In_ HKEY Key)
241 {
242 std::map<std::wstring, std::wstring> values;
243 for (const auto& [name, type] : EnumValues(Key))
244 {
245 // Only return string values; callers that need other types should use EnumValues() directly.
246 // REG_EXPAND_SZ values are returned with environment variables expanded (per ReadOptionalString).
247 if (type != REG_SZ && type != REG_EXPAND_SZ)
248 {
249 continue;
250 }
251
252 if (auto value = ReadOptionalString(Key, nullptr, name.c_str()))
253 {
254 values.emplace(name, std::move(*value));
255 }
256 }
257
258 return values;
259 }
260
261 bool wsl::windows::common::registry::IsKeyVolatile(_In_ HKEY Key)
262 {
263 KEY_FLAGS_INFORMATION info{};
264 DWORD resultSize{};
265 THROW_IF_NTSTATUS_FAILED(ZwQueryKey(Key, KeyFlagsInformation, &info, sizeof(info), &resultSize));
266
267 return WI_IsFlagSet(info.KeyFlags, REG_OPTION_VOLATILE);
268 }
269
270 wil::unique_hkey wsl::windows::common::registry::OpenCurrentUser(_In_ REGSAM AccessMask)
271 {
272 wil::unique_hkey UserKey;
273 THROW_IF_WIN32_ERROR(RegOpenCurrentUser(AccessMask, &UserKey));
274
275 return UserKey;
276 }
277
278 std::pair<wil::unique_hkey, HRESULT> wsl::windows::common::registry::OpenKeyNoThrow(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ REGSAM AccessMask, _In_ DWORD Options)
279 {
280 wil::unique_hkey OpenedKey;
281 const auto error = RegOpenKeyExW(Key, SubKey, Options, AccessMask, &OpenedKey);
282
283 return {std::move(OpenedKey), HRESULT_FROM_WIN32(error)};
284 }
285
286 wil::unique_hkey wsl::windows::common::registry::OpenKey(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ REGSAM AccessMask, _In_ DWORD Options)
287 {
288 auto [key, error] = OpenKeyNoThrow(Key, SubKey, AccessMask, Options);
289 ReportErrorIfFailed(error, Key, SubKey, nullptr);
290
291 return std::move(key);
292 }
293
294 wil::unique_hkey wsl::windows::common::registry::OpenLxssMachineKey(REGSAM AccessMask)
295 {
296 wil::unique_hkey LxssKey = CreateKey(HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, AccessMask);
297 THROW_LAST_ERROR_IF(!LxssKey);
298
299 return LxssKey;
300 }
301
302 wil::unique_hkey wsl::windows::common::registry::OpenLxssUserKey()
303 {
304 const wil::unique_hkey UserKey = OpenCurrentUser();
305 wil::unique_hkey LxssKey = CreateKey(UserKey.get(), LXSS_REGISTRY_PATH);
306 THROW_LAST_ERROR_IF(!LxssKey);
307
308 return LxssKey;
309 }
310
311 wil::unique_hkey wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(_In_ PSID UserSid)
312 {
313 // In this method we use the user SID to open a user specific key under HKLM
314 // The reason for not using HKCU is that lxss trusts this key and will mount
315 // all the volumes listed under it.
316 // Given that only elevated users are allowed to mount disks, using HKCU would
317 // create a security issue as non-admin users could write anything they want there.
318 std::wstring path = std::format(L"{}\\{}", LXSS_DISK_MOUNTS_REGISTRY_PATH, wsl::windows::common::wslutil::SidToString(UserSid).get());
319
320 // Create a volatile key so that disk states aren't kept after a reboot
321 return CreateKey(HKEY_LOCAL_MACHINE, path.c_str(), KEY_ALL_ACCESS, nullptr, REG_OPTION_VOLATILE);
322 }
323
324 void wsl::windows::common::registry::QueryInfo(
325 _In_ HKEY Key,
326 _In_opt_ DWORD* MaxSubKeySize,
327 _In_opt_ DWORD* MaxValueNameSize,
328 _In_opt_ DWORD* MaxValueDataSize,
329 _Out_opt_ DWORD* SubKeyCount,
330 _Out_opt_ DWORD* ValueCount)
331 {
332 const auto error = (RegQueryInfoKeyW(
333 Key, nullptr, nullptr, nullptr, SubKeyCount, MaxSubKeySize, nullptr, ValueCount, MaxValueNameSize, MaxValueDataSize, nullptr, nullptr));
334
335 ReportErrorIfFailed(error, Key, nullptr, nullptr);
336 }
337
338 DWORD
339 wsl::windows::common::registry::ReadDword(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ DWORD DefaultValue)
340 {
341 DWORD Returned = 0;
342 DWORD Size = sizeof(Returned);
343 const LONG Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_DWORD, nullptr, &Returned, &Size);
344 if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND))
345 {
346 return DefaultValue;
347 }
348
349 ReportErrorIfFailed(Result, Key, KeyName, ValueName);
350 return Returned;
351 }
352
353 ULONG64
354 wsl::windows::common::registry::ReadQword(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ ULONG64 DefaultValue)
355 {
356 ULONG64 Returned = 0;
357 DWORD Size = sizeof(Returned);
358 const LONG Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_QWORD, nullptr, &Returned, &Size);
359 if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND))
360 {
361 return DefaultValue;
362 }
363
364 ReportErrorIfFailed(Result, Key, KeyName, ValueName);
365
366 return Returned;
367 }
368
369 std::wstring wsl::windows::common::registry::ReadString(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_opt_ LPCWSTR Default)
370 {
371 auto value = ReadOptionalString(Key, KeyName, ValueName);
372 if (!value.has_value())
373 {
374 if (ARGUMENT_PRESENT(Default))
375 {
376 return Default;
377 }
378 else
379 {
380 ReportErrorIfFailed(ERROR_PATH_NOT_FOUND, Key, KeyName, ValueName);
381 }
382 }
383
384 return value.value();
385 }
386
387 std::optional<std::wstring> wsl::windows::common::registry::ReadOptionalString(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName)
388 {
389 DWORD Size = 0;
390 LONG Result = RegGetValueW(Key, KeyName, ValueName, (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ), nullptr, nullptr, &Size);
391 if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND) || (Size == 0))
392 {
393 return {};
394 }
395
396 ReportErrorIfFailed(Result, Key, KeyName, ValueName);
397
398 //
399 // Allocate a buffer and read the value of the key.
400 //
401
402 std::wstring Buffer(Size / sizeof(WCHAR), L'\0');
403 Result = RegGetValueW(Key, KeyName, ValueName, (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ), nullptr, Buffer.data(), &Size);
404 if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND) || (Size == 0))
405 {
406 return {};
407 }
408
409 ReportErrorIfFailed(Result, Key, KeyName, ValueName);
410
411 Buffer.resize(wcsnlen(Buffer.c_str(), Buffer.size()));
412 return Buffer;
413 }
414
415 std::vector<std::string> wsl::windows::common::registry::ReadStringSet(
416 _In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, const std::vector<std::string>& Default)
417 {
418 //
419 // Detect if the key exists and determine how large of a buffer is needed.
420 // If the key does not exist, return the default value.
421 //
422
423 LONG Result;
424 DWORD Size = 0;
425 Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_MULTI_SZ, nullptr, nullptr, &Size);
426 if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND) || (Size == 0))
427 {
428 //
429 // Convert the supplied string into a vector of strings.
430 //
431
432 return Default;
433 }
434
435 ReportErrorIfFailed(Result, Key, KeyName, ValueName);
436
437 //
438 // Allocate a buffer to hold the value and two NULL terminators.
439 //
440
441 std::vector<WCHAR> Buffer(Size + 2);
442
443 //
444 // Read the value.
445 //
446
447 Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_MULTI_SZ, nullptr, Buffer.data(), &Size);
448 ReportErrorIfFailed(Result, Key, KeyName, ValueName);
449
450 //
451 // Convert the reg value into a vector of strings.
452 //
453
454 std::vector<std::string> Values{};
455 for (auto Current = Buffer.data(); UNICODE_NULL != *Current; Current += wcslen(Current) + 1)
456 {
457 Values.push_back(wsl::shared::string::WideToMultiByte(Current));
458 }
459
460 return Values;
461 }
462
463 void wsl::windows::common::registry::WriteDword(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ DWORD Value)
464 {
465 const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_DWORD, &Value, sizeof(Value));
466 ReportErrorIfFailed(Result, Key, SubKey, ValueName);
467 }
468
469 void wsl::windows::common::registry::WriteQword(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ ULONG64 Value)
470 {
471 const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_QWORD, &Value, sizeof(Value));
472 ReportErrorIfFailed(Result, Key, SubKey, ValueName);
473 }
474
475 void wsl::windows::common::registry::WriteDefaultString(_In_ HKEY Key, _In_ LPCWSTR Value)
476 {
477 SIZE_T StringLength = wcslen(Value);
478 THROW_IF_FAILED(SizeTAdd(StringLength, 1, &StringLength));
479 THROW_IF_FAILED(SizeTMult(StringLength, sizeof(WCHAR), &StringLength));
480
481 THROW_HR_IF(E_INVALIDARG, (StringLength > (SIZE_T)DWORD_MAX));
482
483 const auto Result = RegSetValueExW(Key, NULL, 0, REG_SZ, reinterpret_cast<const BYTE*>(Value), static_cast<DWORD>(StringLength));
484
485 ReportErrorIfFailed(Result, Key, nullptr, nullptr);
486 }
487
488 void wsl::windows::common::registry::WriteString(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ LPCWSTR Value)
489 {
490 SIZE_T StringLength = wcslen(Value);
491 THROW_IF_FAILED(SizeTAdd(StringLength, 1, &StringLength));
492 THROW_IF_FAILED(SizeTMult(StringLength, sizeof(WCHAR), &StringLength));
493
494 THROW_HR_IF(E_INVALIDARG, (StringLength > (SIZE_T)DWORD_MAX));
495
496 const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_SZ, Value, static_cast<DWORD>(StringLength));
497 ReportErrorIfFailed(Result, Key, SubKey, ValueName);
498 }
499
500 void wsl::windows::common::registry::WriteStringSet(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ const std::vector<std::wstring>& StringSet)
501 {
502 THROW_HR_IF(E_INVALIDARG, (StringSet.size() == 0));
503
504 //
505 // Combine each element into a NULL-separated string ending with two NULL
506 // terminators.
507 //
508
509 std::wstring Value;
510 for (SIZE_T Index = 0; Index < StringSet.size(); Index += 1)
511 {
512 Value += StringSet[Index] + UNICODE_NULL;
513 }
514
515 Value += UNICODE_NULL;
516
517 //
518 // Ensure the wstring ends with two NULL terminators.
519 //
520
521 WI_ASSERT((Value.size() >= 2) && (Value.at(Value.size() - 1) == UNICODE_NULL) && (Value.at(Value.size() - 2) == UNICODE_NULL));
522
523 //
524 // Store the value in the registry.
525 //
526
527 SIZE_T ValueSize;
528 THROW_IF_FAILED(SizeTMult(Value.size(), sizeof(WCHAR), &ValueSize));
529 THROW_HR_IF(E_INVALIDARG, (ValueSize > (SIZE_T)DWORD_MAX));
530
531 const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_MULTI_SZ, Value.c_str(), static_cast<DWORD>(ValueSize));
532 ReportErrorIfFailed(Result, Key, SubKey, ValueName);
533 }