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