xref: /reactos/boot/freeldr/freeldr/ntldr/registry.c (revision 4561998a)
1 /*
2  *  FreeLoader
3  *
4  *  Copyright (C) 2014  Timo Kreuzer <timo.kreuzer@reactos.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License along
17  *  with this program; if not, write to the Free Software Foundation, Inc.,
18  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <freeldr.h>
22 #include <cmlib.h>
23 #include "registry.h"
24 
25 #include <debug.h>
26 DBG_DEFAULT_CHANNEL(REGISTRY);
27 
28 static PCMHIVE CmHive;
29 static PCM_KEY_NODE RootKeyNode;
30 static HKEY CurrentControlSetKey;
31 
32 PVOID
33 NTAPI
34 CmpAllocate(
35     IN SIZE_T Size,
36     IN BOOLEAN Paged,
37     IN ULONG Tag)
38 {
39     UNREFERENCED_PARAMETER(Paged);
40     UNREFERENCED_PARAMETER(Tag);
41 
42     return FrLdrTempAlloc(Size, Tag);
43 }
44 
45 VOID
46 NTAPI
47 CmpFree(
48     IN PVOID Ptr,
49     IN ULONG Quota)
50 {
51     UNREFERENCED_PARAMETER(Quota);
52     FrLdrTempFree(Ptr, 0);
53 }
54 
55 BOOLEAN
56 RegImportBinaryHive(
57     _In_ PVOID ChunkBase,
58     _In_ ULONG ChunkSize)
59 {
60     NTSTATUS Status;
61     TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize);
62 
63     /* Allocate and initialize the hive */
64     CmHive = CmpAllocate(sizeof(CMHIVE), FALSE, 'eviH');
65     Status = HvInitialize(&CmHive->Hive,
66                           HINIT_FLAT, // HINIT_MEMORY_INPLACE
67                           0,
68                           0,
69                           ChunkBase,
70                           CmpAllocate,
71                           CmpFree,
72                           NULL,
73                           NULL,
74                           NULL,
75                           NULL,
76                           1,
77                           NULL);
78     if (!NT_SUCCESS(Status))
79     {
80         CmpFree(CmHive, 0);
81         ERR("Corrupted hive %p!\n", ChunkBase);
82         return FALSE;
83     }
84 
85     /* Save the root key node */
86     RootKeyNode = (PCM_KEY_NODE)HvGetCell(&CmHive->Hive, CmHive->Hive.BaseBlock->RootCell);
87 
88     TRACE("RegImportBinaryHive done\n");
89     return TRUE;
90 }
91 
92 LONG
93 RegInitCurrentControlSet(
94     _In_ BOOLEAN LastKnownGood)
95 {
96     WCHAR ControlSetKeyName[80];
97     HKEY SelectKey;
98     HKEY SystemKey;
99     ULONG CurrentSet = 0;
100     ULONG DefaultSet = 0;
101     ULONG LastKnownGoodSet = 0;
102     ULONG DataSize;
103     LONG Error;
104     TRACE("RegInitCurrentControlSet\n");
105 
106     Error = RegOpenKey(NULL,
107                        L"\\Registry\\Machine\\SYSTEM\\Select",
108                        &SelectKey);
109     if (Error != ERROR_SUCCESS)
110     {
111         ERR("RegOpenKey() failed (Error %u)\n", (int)Error);
112         return Error;
113     }
114 
115     DataSize = sizeof(ULONG);
116     Error = RegQueryValue(SelectKey,
117                           L"Default",
118                           NULL,
119                           (PUCHAR)&DefaultSet,
120                           &DataSize);
121     if (Error != ERROR_SUCCESS)
122     {
123         ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error);
124         return Error;
125     }
126 
127     DataSize = sizeof(ULONG);
128     Error = RegQueryValue(SelectKey,
129                           L"LastKnownGood",
130                           NULL,
131                           (PUCHAR)&LastKnownGoodSet,
132                           &DataSize);
133     if (Error != ERROR_SUCCESS)
134     {
135         ERR("RegQueryValue('LastKnownGood') failed (Error %u)\n", (int)Error);
136         return Error;
137     }
138 
139     CurrentSet = (LastKnownGood) ? LastKnownGoodSet : DefaultSet;
140     wcscpy(ControlSetKeyName, L"ControlSet");
141     switch(CurrentSet)
142     {
143         case 1:
144             wcscat(ControlSetKeyName, L"001");
145             break;
146         case 2:
147             wcscat(ControlSetKeyName, L"002");
148             break;
149         case 3:
150             wcscat(ControlSetKeyName, L"003");
151             break;
152         case 4:
153             wcscat(ControlSetKeyName, L"004");
154             break;
155         case 5:
156             wcscat(ControlSetKeyName, L"005");
157             break;
158     }
159 
160     Error = RegOpenKey(NULL,
161                        L"\\Registry\\Machine\\SYSTEM",
162                        &SystemKey);
163     if (Error != ERROR_SUCCESS)
164     {
165         ERR("RegOpenKey(SystemKey) failed (Error %lu)\n", Error);
166         return Error;
167     }
168 
169     Error = RegOpenKey(SystemKey,
170                        ControlSetKeyName,
171                        &CurrentControlSetKey);
172     if (Error != ERROR_SUCCESS)
173     {
174         ERR("RegOpenKey(CurrentControlSetKey) failed (Error %lu)\n", Error);
175         return Error;
176     }
177 
178     TRACE("RegInitCurrentControlSet done\n");
179     return ERROR_SUCCESS;
180 }
181 
182 static
183 BOOLEAN
184 GetNextPathElement(
185     _Out_ PUNICODE_STRING NextElement,
186     _Inout_ PUNICODE_STRING RemainingPath)
187 {
188     /* Check if there are any characters left */
189     if (RemainingPath->Length < sizeof(WCHAR))
190     {
191         /* Nothing left, bail out early */
192         return FALSE;
193     }
194 
195     /* The next path elements starts with the remaining path */
196     NextElement->Buffer = RemainingPath->Buffer;
197 
198     /* Loop until the path element ends */
199     while ((RemainingPath->Length >= sizeof(WCHAR)) &&
200            (RemainingPath->Buffer[0] != '\\'))
201     {
202         /* Skip this character */
203         RemainingPath->Buffer++;
204         RemainingPath->Length -= sizeof(WCHAR);
205     }
206 
207     NextElement->Length = (USHORT)(RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR);
208     NextElement->MaximumLength = NextElement->Length;
209 
210     /* Check if the path element ended with a path separator */
211     if (RemainingPath->Length >= sizeof(WCHAR))
212     {
213         /* Skip the path separator */
214         ASSERT(RemainingPath->Buffer[0] == '\\');
215         RemainingPath->Buffer++;
216         RemainingPath->Length -= sizeof(WCHAR);
217     }
218 
219     /* Return whether we got any characters */
220     return TRUE;
221 }
222 
223 LONG
224 RegEnumKey(
225     _In_ HKEY Key,
226     _In_ ULONG Index,
227     _Out_ PWCHAR Name,
228     _Inout_ ULONG* NameSize,
229     _Out_opt_ PHKEY SubKey)
230 {
231     PHHIVE Hive = &CmHive->Hive;
232     PCM_KEY_NODE KeyNode, SubKeyNode;
233     HCELL_INDEX CellIndex;
234     USHORT NameLength;
235 
236     TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n",
237           Key, Index, Name, NameSize, NameSize ? *NameSize : 0);
238 
239     /* Get the key node */
240     KeyNode = (PCM_KEY_NODE)Key;
241     ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
242 
243     CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, Index);
244     if (CellIndex == HCELL_NIL)
245     {
246         TRACE("RegEnumKey index out of bounds (%d) in key (%.*s)\n",
247               Index, KeyNode->NameLength, KeyNode->Name);
248         return ERROR_NO_MORE_ITEMS;
249     }
250 
251     /* Get the value cell */
252     SubKeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
253     ASSERT(SubKeyNode != NULL);
254     ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE);
255 
256     if (SubKeyNode->Flags & KEY_COMP_NAME)
257     {
258         NameLength = CmpCompressedNameSize(SubKeyNode->Name, SubKeyNode->NameLength);
259 
260         /* Compressed name */
261         CmpCopyCompressedName(Name,
262                               *NameSize,
263                               SubKeyNode->Name,
264                               SubKeyNode->NameLength);
265     }
266     else
267     {
268         NameLength = SubKeyNode->NameLength;
269 
270         /* Normal name */
271         RtlCopyMemory(Name, SubKeyNode->Name,
272                       min(*NameSize, SubKeyNode->NameLength));
273     }
274 
275     if (*NameSize >= NameLength + sizeof(WCHAR))
276     {
277         Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
278     }
279 
280     *NameSize = NameLength + sizeof(WCHAR);
281 
282     /**/HvReleaseCell(Hive, CellIndex);/**/
283 
284     if (SubKey != NULL)
285         *SubKey = (HKEY)SubKeyNode;
286 
287     TRACE("RegEnumKey done -> %u, '%.*S'\n", *NameSize, *NameSize, Name);
288     return ERROR_SUCCESS;
289 }
290 
291 LONG
292 RegOpenKey(
293     _In_ HKEY ParentKey,
294     _In_z_ PCWSTR KeyName,
295     _Out_ PHKEY Key)
296 {
297     UNICODE_STRING RemainingPath, SubKeyName;
298     UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet");
299     PHHIVE Hive = &CmHive->Hive;
300     PCM_KEY_NODE KeyNode;
301     HCELL_INDEX CellIndex;
302     TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key);
303 
304     /* Initialize the remaining path name */
305     RtlInitUnicodeString(&RemainingPath, KeyName);
306 
307     /* Get the parent key node */
308     KeyNode = (PCM_KEY_NODE)ParentKey;
309 
310     /* Check if we have a parent key */
311     if (KeyNode == NULL)
312     {
313         UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3;
314         UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry");
315         UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE");
316         UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM");
317         TRACE("RegOpenKey: absolute path\n");
318 
319         if ((RemainingPath.Length < sizeof(WCHAR)) ||
320             RemainingPath.Buffer[0] != '\\')
321         {
322             /* The key path is not absolute */
323             ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
324             return ERROR_PATH_NOT_FOUND;
325         }
326 
327         /* Skip initial path separator */
328         RemainingPath.Buffer++;
329         RemainingPath.Length -= sizeof(WCHAR);
330 
331         /* Get the first 3 path elements */
332         GetNextPathElement(&SubKeyName1, &RemainingPath);
333         GetNextPathElement(&SubKeyName2, &RemainingPath);
334         GetNextPathElement(&SubKeyName3, &RemainingPath);
335         TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3);
336 
337         /* Check if we have the correct path */
338         if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) ||
339             !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) ||
340             !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE))
341         {
342             /* The key path is not inside HKLM\Machine\System */
343             ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
344             return ERROR_PATH_NOT_FOUND;
345         }
346 
347         /* Use the root key */
348         KeyNode = RootKeyNode;
349     }
350 
351     ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
352 
353     /* Check if this is the root key */
354     if (KeyNode == RootKeyNode)
355     {
356         UNICODE_STRING TempPath = RemainingPath;
357 
358         /* Get the first path element */
359         GetNextPathElement(&SubKeyName, &TempPath);
360 
361         /* Check if this is CurrentControlSet */
362         if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE))
363         {
364             /* Use the CurrentControlSetKey and update the remaining path */
365             KeyNode = (PCM_KEY_NODE)CurrentControlSetKey;
366             RemainingPath = TempPath;
367         }
368     }
369 
370     TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath);
371 
372     /* Loop while there are path elements */
373     while (GetNextPathElement(&SubKeyName, &RemainingPath))
374     {
375         TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName);
376 
377         /* Get the next sub key */
378         CellIndex = CmpFindSubKeyByName(Hive, KeyNode, &SubKeyName);
379         if (CellIndex == HCELL_NIL)
380         {
381             ERR("Did not find sub key '%wZ' (full %S)\n", &SubKeyName, KeyName);
382             return ERROR_PATH_NOT_FOUND;
383         }
384 
385         /* Get the found key */
386         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
387         ASSERT(KeyNode);
388     }
389 
390     *Key = (HKEY)KeyNode;
391 
392     TRACE("RegOpenKey done\n");
393     return ERROR_SUCCESS;
394 }
395 
396 static
397 VOID
398 RepGetValueData(
399     _In_ PHHIVE Hive,
400     _In_ PCM_KEY_VALUE ValueCell,
401     _Out_opt_ ULONG* Type,
402     _Out_opt_ PUCHAR Data,
403     _Inout_opt_ ULONG* DataSize)
404 {
405     ULONG DataLength;
406     PVOID DataCell;
407 
408     /* Does the caller want the type? */
409     if (Type != NULL)
410         *Type = ValueCell->Type;
411 
412     /* Does the caller provide DataSize? */
413     if (DataSize != NULL)
414     {
415         // NOTE: CmpValueToData doesn't support big data (the function will
416         // bugcheck if so), FreeLdr is not supposed to read such data.
417         // If big data is needed, use instead CmpGetValueData.
418         // CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...);
419         DataCell = CmpValueToData(Hive, ValueCell, &DataLength);
420 
421         /* Does the caller want the data? */
422         if ((Data != NULL) && (*DataSize != 0))
423         {
424             RtlCopyMemory(Data,
425                           DataCell,
426                           min(*DataSize, DataLength));
427         }
428 
429         /* Return the actual data length */
430         *DataSize = DataLength;
431     }
432 }
433 
434 LONG
435 RegQueryValue(
436     _In_ HKEY Key,
437     _In_z_ PCWSTR ValueName,
438     _Out_opt_ ULONG* Type,
439     _Out_opt_ PUCHAR Data,
440     _Inout_opt_ ULONG* DataSize)
441 {
442     PHHIVE Hive = &CmHive->Hive;
443     PCM_KEY_NODE KeyNode;
444     PCM_KEY_VALUE ValueCell;
445     HCELL_INDEX CellIndex;
446     UNICODE_STRING ValueNameString;
447 
448     TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n",
449           Key, ValueName, Type, Data, DataSize);
450 
451     /* Get the key node */
452     KeyNode = (PCM_KEY_NODE)Key;
453     ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
454 
455     /* Initialize value name string */
456     RtlInitUnicodeString(&ValueNameString, ValueName);
457     CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString);
458     if (CellIndex == HCELL_NIL)
459     {
460         TRACE("RegQueryValue value not found in key (%.*s)\n",
461               KeyNode->NameLength, KeyNode->Name);
462         return ERROR_FILE_NOT_FOUND;
463     }
464 
465     /* Get the value cell */
466     ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
467     ASSERT(ValueCell != NULL);
468 
469     RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
470 
471     HvReleaseCell(Hive, CellIndex);
472 
473     TRACE("RegQueryValue success\n");
474     return ERROR_SUCCESS;
475 }
476 
477 /*
478  * NOTE: This function is currently unused in FreeLdr; however it is kept here
479  * as an implementation reference of RegEnumValue using CMLIB that may be used
480  * elsewhere in ReactOS.
481  */
482 #if 0
483 LONG
484 RegEnumValue(
485     _In_ HKEY Key,
486     _In_ ULONG Index,
487     _Out_ PWCHAR ValueName,
488     _Inout_ ULONG* NameSize,
489     _Out_opt_ ULONG* Type,
490     _Out_opt_ PUCHAR Data,
491     _Inout_opt_ ULONG* DataSize)
492 {
493     PHHIVE Hive = &CmHive->Hive;
494     PCM_KEY_NODE KeyNode;
495     PCELL_DATA ValueListCell;
496     PCM_KEY_VALUE ValueCell;
497     USHORT NameLength;
498 
499     TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n",
500           Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize);
501 
502     /* Get the key node */
503     KeyNode = (PCM_KEY_NODE)Key;
504     ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
505 
506     /* Check if the index is valid */
507     if ((KeyNode->ValueList.Count == 0) ||
508         (KeyNode->ValueList.List == HCELL_NIL) ||
509         (Index >= KeyNode->ValueList.Count))
510     {
511         ERR("RegEnumValue: index invalid\n");
512         return ERROR_NO_MORE_ITEMS;
513     }
514 
515     ValueListCell = (PCELL_DATA)HvGetCell(Hive, KeyNode->ValueList.List);
516     ASSERT(ValueListCell != NULL);
517 
518     /* Get the value cell */
519     ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, ValueListCell->KeyList[Index]);
520     ASSERT(ValueCell != NULL);
521     ASSERT(ValueCell->Signature == CM_KEY_VALUE_SIGNATURE);
522 
523     if (ValueCell->Flags & VALUE_COMP_NAME)
524     {
525         NameLength = CmpCompressedNameSize(ValueCell->Name, ValueCell->NameLength);
526 
527         /* Compressed name */
528         CmpCopyCompressedName(ValueName,
529                               *NameSize,
530                               ValueCell->Name,
531                               ValueCell->NameLength);
532     }
533     else
534     {
535         NameLength = ValueCell->NameLength;
536 
537         /* Normal name */
538         RtlCopyMemory(ValueName, ValueCell->Name,
539                       min(*NameSize, ValueCell->NameLength));
540     }
541 
542     if (*NameSize >= NameLength + sizeof(WCHAR))
543     {
544         ValueName[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
545     }
546 
547     *NameSize = NameLength + sizeof(WCHAR);
548 
549     RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
550 
551     HvReleaseCell(Hive, ValueListCell->KeyList[Index]);
552     HvReleaseCell(Hive, KeyNode->ValueList.List);
553 
554     TRACE("RegEnumValue done -> %u, '%.*S'\n", *NameSize, *NameSize, ValueName);
555     return ERROR_SUCCESS;
556 }
557 #endif
558 
559 /* EOF */
560