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