xref: /reactos/base/setup/lib/registry.c (revision 6c2d3dee)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS Setup Library
22  * FILE:            base/setup/lib/registry.c
23  * PURPOSE:         Registry creation functions
24  * PROGRAMMERS:     ...
25  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include "precomp.h"
31 #include "filesup.h"
32 #include "infsupp.h"
33 #include "regutil.h"
34 
35 #include "registry.h"
36 
37 #define NDEBUG
38 #include <debug.h>
39 
40 
41 // #ifdef __REACTOS__
42 #if 1 // FIXME: Disable if setupapi.h is included in the code...
43 #define FLG_ADDREG_BINVALUETYPE           0x00000001
44 #define FLG_ADDREG_NOCLOBBER              0x00000002
45 #define FLG_ADDREG_DELVAL                 0x00000004
46 #define FLG_ADDREG_APPEND                 0x00000008
47 #define FLG_ADDREG_KEYONLY                0x00000010
48 #define FLG_ADDREG_OVERWRITEONLY          0x00000020
49 #define FLG_ADDREG_TYPE_SZ                0x00000000
50 #define FLG_ADDREG_TYPE_MULTI_SZ          0x00010000
51 #define FLG_ADDREG_TYPE_EXPAND_SZ         0x00020000
52 #define FLG_ADDREG_TYPE_BINARY           (0x00000000 | FLG_ADDREG_BINVALUETYPE)
53 #define FLG_ADDREG_TYPE_DWORD            (0x00010000 | FLG_ADDREG_BINVALUETYPE)
54 #define FLG_ADDREG_TYPE_NONE             (0x00020000 | FLG_ADDREG_BINVALUETYPE)
55 #define FLG_ADDREG_TYPE_MASK             (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
56 #endif
57 
58 #ifdef _M_IX86
59 #define Architecture L"x86"
60 #elif defined(_M_AMD64)
61 #define Architecture L"amd64"
62 #elif defined(_M_IA64)
63 #define Architecture L"ia64"
64 #elif defined(_M_ARM)
65 #define Architecture L"arm"
66 #elif defined(_M_PPC)
67 #define Architecture L"ppc"
68 #endif
69 
70 /* FUNCTIONS ****************************************************************/
71 
72 #define REGISTRY_SETUP_MACHINE  L"\\Registry\\Machine\\SYSTEM\\USetup_Machine\\"
73 #define REGISTRY_SETUP_USER     L"\\Registry\\Machine\\SYSTEM\\USetup_User\\"
74 
75 typedef struct _ROOT_KEY
76 {
77     PCWSTR Name;
78     PCWSTR MountPoint;
79     HANDLE Handle;
80 } ROOT_KEY, *PROOT_KEY;
81 
82 ROOT_KEY RootKeys[] =
83 {
84     { L"HKCR", REGISTRY_SETUP_MACHINE L"SOFTWARE\\Classes\\", NULL },   /* "\\Registry\\Machine\\SOFTWARE\\Classes\\" */ // HKEY_CLASSES_ROOT
85     { L"HKCU", REGISTRY_SETUP_USER    L".DEFAULT\\"         , NULL },   /* "\\Registry\\User\\.DEFAULT\\" */             // HKEY_CURRENT_USER
86     { L"HKLM", REGISTRY_SETUP_MACHINE                       , NULL },   /* "\\Registry\\Machine\\"        */             // HKEY_LOCAL_MACHINE
87     { L"HKU" , REGISTRY_SETUP_USER                          , NULL },   /* "\\Registry\\User\\"           */             // HKEY_USERS
88 #if 0
89     { L"HKR", NULL, NULL },
90 #endif
91 };
92 
93 #define IsPredefKey(HKey)       \
94     (((ULONG_PTR)(HKey) & 0xF0000000) == 0x80000000)
95 
96 #define GetPredefKeyIndex(HKey) \
97     ((ULONG_PTR)(HKey) & 0x0FFFFFFF)
98 
99 HANDLE
100 GetRootKeyByPredefKey(
101     IN HANDLE KeyHandle,
102     OUT PCWSTR* RootKeyMountPoint OPTIONAL)
103 {
104     ULONG_PTR Index = GetPredefKeyIndex(KeyHandle);
105 
106     if (!IsPredefKey(KeyHandle))
107         return NULL;
108     if (Index >= ARRAYSIZE(RootKeys))
109         return NULL;
110 
111     if (RootKeyMountPoint)
112         *RootKeyMountPoint = RootKeys[Index].MountPoint;
113     return RootKeys[Index].Handle;
114 }
115 
116 HANDLE
117 GetRootKeyByName(
118     IN PCWSTR RootKeyName,
119     OUT PCWSTR* RootKeyMountPoint OPTIONAL)
120 {
121     UCHAR i;
122 
123     for (i = 0; i < ARRAYSIZE(RootKeys); ++i)
124     {
125         if (!_wcsicmp(RootKeyName, RootKeys[i].Name))
126         {
127             if (RootKeyMountPoint)
128                 *RootKeyMountPoint = RootKeys[i].MountPoint;
129             return RootKeys[i].Handle;
130         }
131     }
132 
133     return NULL;
134 }
135 
136 
137 /***********************************************************************
138  *            append_multi_sz_value
139  *
140  * Append a multisz string to a multisz registry value.
141  */
142 // NOTE: Synced with setupapi/install.c ; see also mkhive/reginf.c
143 #if 0
144 static void
145 append_multi_sz_value (HANDLE hkey,
146                        const WCHAR *value,
147                        const WCHAR *strings,
148                        DWORD str_size )
149 {
150     DWORD size, type, total;
151     WCHAR *buffer, *p;
152 
153     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
154     if (type != REG_MULTI_SZ) return;
155 
156     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + str_size * sizeof(WCHAR) ))) return;
157     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
158 
159     /* compare each string against all the existing ones */
160     total = size;
161     while (*strings)
162     {
163         int len = strlenW(strings) + 1;
164 
165         for (p = buffer; *p; p += strlenW(p) + 1)
166             if (!strcmpiW( p, strings )) break;
167 
168         if (!*p)  /* not found, need to append it */
169         {
170             memcpy( p, strings, len * sizeof(WCHAR) );
171             p[len] = 0;
172             total += len;
173         }
174         strings += len;
175     }
176     if (total != size)
177     {
178         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
179         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
180     }
181  done:
182     HeapFree( GetProcessHeap(), 0, buffer );
183 }
184 #endif
185 
186 /***********************************************************************
187  *            delete_multi_sz_value
188  *
189  * Remove a string from a multisz registry value.
190  */
191 #if 0
192 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
193 {
194     DWORD size, type;
195     WCHAR *buffer, *src, *dst;
196 
197     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
198     if (type != REG_MULTI_SZ) return;
199     /* allocate double the size, one for value before and one for after */
200     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
201     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
202     src = buffer;
203     dst = buffer + size;
204     while (*src)
205     {
206         int len = strlenW(src) + 1;
207         if (strcmpiW( src, string ))
208         {
209             memcpy( dst, src, len * sizeof(WCHAR) );
210             dst += len;
211         }
212         src += len;
213     }
214     *dst++ = 0;
215     if (dst != buffer + 2*size)  /* did we remove something? */
216     {
217         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
218         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
219                         (BYTE *)(buffer + size), dst - (buffer + size) );
220     }
221  done:
222     HeapFree( GetProcessHeap(), 0, buffer );
223 }
224 #endif
225 
226 /***********************************************************************
227  *            do_reg_operation
228  *
229  * Perform an add/delete registry operation depending on the flags.
230  */
231 static BOOLEAN
232 do_reg_operation(HANDLE KeyHandle,
233                  PUNICODE_STRING ValueName,
234                  PINFCONTEXT Context,
235                  ULONG Flags)
236 {
237   WCHAR EmptyStr = 0;
238   ULONG Type;
239   ULONG Size;
240 
241   if (Flags & FLG_ADDREG_DELVAL)  /* deletion */
242     {
243 #if 0
244       if (ValueName)
245         {
246           RegDeleteValueW( KeyHandle, ValueName );
247         }
248       else
249         {
250           RegDeleteKeyW( KeyHandle, NULL );
251         }
252 #endif
253       return TRUE;
254     }
255 
256   if (Flags & FLG_ADDREG_KEYONLY)
257     return TRUE;
258 
259 #if 0
260   if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
261     {
262       BOOL exists = !RegQueryValueExW( hkey, ValueName, NULL, NULL, NULL, NULL );
263       if (exists && (flags & FLG_ADDREG_NOCLOBBER))
264         return TRUE;
265       if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY))
266         return TRUE;
267     }
268 #endif
269 
270   switch (Flags & FLG_ADDREG_TYPE_MASK)
271     {
272       case FLG_ADDREG_TYPE_SZ:
273         Type = REG_SZ;
274         break;
275 
276       case FLG_ADDREG_TYPE_MULTI_SZ:
277         Type = REG_MULTI_SZ;
278         break;
279 
280       case FLG_ADDREG_TYPE_EXPAND_SZ:
281         Type = REG_EXPAND_SZ;
282         break;
283 
284       case FLG_ADDREG_TYPE_BINARY:
285         Type = REG_BINARY;
286         break;
287 
288       case FLG_ADDREG_TYPE_DWORD:
289         Type = REG_DWORD;
290         break;
291 
292       case FLG_ADDREG_TYPE_NONE:
293         Type = REG_NONE;
294         break;
295 
296       default:
297         Type = Flags >> 16;
298         break;
299     }
300 
301   if (!(Flags & FLG_ADDREG_BINVALUETYPE) ||
302       (Type == REG_DWORD && SetupGetFieldCount (Context) == 5))
303     {
304       PWCHAR Str = NULL;
305 
306       if (Type == REG_MULTI_SZ)
307         {
308           if (!SetupGetMultiSzFieldW (Context, 5, NULL, 0, &Size))
309             Size = 0;
310 
311           if (Size)
312             {
313               Str = (WCHAR*) RtlAllocateHeap(ProcessHeap, 0, Size * sizeof(WCHAR));
314               if (Str == NULL)
315                 return FALSE;
316 
317               SetupGetMultiSzFieldW (Context, 5, Str, Size, NULL);
318             }
319 
320           if (Flags & FLG_ADDREG_APPEND)
321             {
322               if (Str == NULL)
323                 return TRUE;
324 
325               DPRINT1("append_multi_sz_value '%S' commented out, WHY??\n", ValueName);
326 //            append_multi_sz_value( hkey, value, str, size );
327 
328               RtlFreeHeap (ProcessHeap, 0, Str);
329               return TRUE;
330             }
331           /* else fall through to normal string handling */
332         }
333       else
334         {
335           if (!SetupGetStringFieldW(Context, 5, NULL, 0, &Size))
336             Size = 0;
337 
338           if (Size)
339             {
340               Str = (WCHAR*)RtlAllocateHeap(ProcessHeap, 0, Size * sizeof(WCHAR));
341               if (Str == NULL)
342                 return FALSE;
343 
344               SetupGetStringFieldW(Context, 5, Str, Size, NULL);
345             }
346         }
347 
348       if (Type == REG_DWORD)
349         {
350           ULONG dw = Str ? wcstoul (Str, NULL, 0) : 0;
351 
352           DPRINT("setting dword %wZ to %lx\n", ValueName, dw);
353 
354           NtSetValueKey (KeyHandle,
355                          ValueName,
356                          0,
357                          Type,
358                          (PVOID)&dw,
359                          sizeof(ULONG));
360         }
361       else
362         {
363           DPRINT("setting value %wZ to %S\n", ValueName, Str);
364 
365           if (Str)
366             {
367               NtSetValueKey (KeyHandle,
368                              ValueName,
369                              0,
370                              Type,
371                              (PVOID)Str,
372                              Size * sizeof(WCHAR));
373             }
374           else
375             {
376               NtSetValueKey (KeyHandle,
377                              ValueName,
378                              0,
379                              Type,
380                              (PVOID)&EmptyStr,
381                              sizeof(WCHAR));
382             }
383         }
384       RtlFreeHeap (ProcessHeap, 0, Str);
385     }
386   else  /* get the binary data */
387     {
388       PUCHAR Data = NULL;
389 
390       if (!SetupGetBinaryField (Context, 5, NULL, 0, &Size))
391         Size = 0;
392 
393       if (Size)
394         {
395           Data = (unsigned char*) RtlAllocateHeap(ProcessHeap, 0, Size);
396           if (Data == NULL)
397             return FALSE;
398 
399           DPRINT("setting binary data %wZ len %lu\n", ValueName, Size);
400           SetupGetBinaryField (Context, 5, Data, Size, NULL);
401         }
402 
403       NtSetValueKey (KeyHandle,
404                      ValueName,
405                      0,
406                      Type,
407                      (PVOID)Data,
408                      Size);
409 
410       RtlFreeHeap (ProcessHeap, 0, Data);
411     }
412 
413   return TRUE;
414 }
415 
416 /***********************************************************************
417  *            registry_callback
418  *
419  * Called once for each AddReg and DelReg entry in a given section.
420  */
421 static BOOLEAN
422 registry_callback(HINF hInf, PCWSTR Section, BOOLEAN Delete)
423 {
424     NTSTATUS Status;
425     OBJECT_ATTRIBUTES ObjectAttributes;
426     UNICODE_STRING Name, Value;
427     PUNICODE_STRING ValuePtr;
428     UINT Flags;
429     WCHAR Buffer[MAX_INF_STRING_LENGTH];
430 
431     INFCONTEXT Context;
432     PCWSTR RootKeyName;
433     HANDLE RootKeyHandle, KeyHandle;
434     BOOLEAN Ok;
435 
436     Ok = SetupFindFirstLineW(hInf, Section, NULL, &Context);
437     if (!Ok)
438         return TRUE; /* Don't fail if the section isn't present */
439 
440     for (;Ok; Ok = SetupFindNextLine(&Context, &Context))
441     {
442         /* get root */
443         if (!SetupGetStringFieldW(&Context, 1, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
444             continue;
445         RootKeyHandle = GetRootKeyByName(Buffer, &RootKeyName);
446         if (!RootKeyHandle)
447             continue;
448 
449         /* get key */
450         if (!SetupGetStringFieldW(&Context, 2, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
451             *Buffer = 0;
452 
453         DPRINT("KeyName: <%S\\%S>\n", RootKeyName, Buffer);
454 
455         /* get flags */
456         if (!SetupGetIntField(&Context, 4, (PINT)&Flags))
457             Flags = 0;
458 
459         DPRINT("Flags: %lx\n", Flags);
460 
461         RtlInitUnicodeString(&Name, Buffer);
462         InitializeObjectAttributes(&ObjectAttributes,
463                                    &Name,
464                                    OBJ_CASE_INSENSITIVE,
465                                    RootKeyHandle,
466                                    NULL);
467 
468         if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
469         {
470             Status = NtOpenKey(&KeyHandle,
471                                KEY_ALL_ACCESS,
472                                &ObjectAttributes);
473             if (!NT_SUCCESS(Status))
474             {
475                 DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
476                 continue;  /* ignore if it doesn't exist */
477             }
478         }
479         else
480         {
481             Status = CreateNestedKey(&KeyHandle,
482                                      KEY_ALL_ACCESS,
483                                      &ObjectAttributes,
484                                      REG_OPTION_NON_VOLATILE);
485             if (!NT_SUCCESS(Status))
486             {
487                 DPRINT1("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
488                 continue;
489             }
490         }
491 
492         /* get value name */
493         if (SetupGetStringFieldW(&Context, 3, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
494         {
495             RtlInitUnicodeString(&Value, Buffer);
496             ValuePtr = &Value;
497         }
498         else
499         {
500             ValuePtr = NULL;
501         }
502 
503         /* and now do it */
504         if (!do_reg_operation(KeyHandle, ValuePtr, &Context, Flags))
505         {
506             NtClose(KeyHandle);
507             return FALSE;
508         }
509 
510         NtClose(KeyHandle);
511     }
512 
513     return TRUE;
514 }
515 
516 BOOLEAN
517 ImportRegistryFile(
518     IN PCWSTR SourcePath,
519     IN PCWSTR FileName,
520     IN PCWSTR Section,
521     IN LCID LocaleId,
522     IN BOOLEAN Delete)
523 {
524     HINF hInf;
525     UINT ErrorLine;
526     WCHAR FileNameBuffer[MAX_PATH];
527 
528     /* Load the INF file from the installation media */
529     CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
530                  SourcePath, FileName);
531 
532     hInf = SetupOpenInfFileExW(FileNameBuffer,
533                                NULL,
534                                INF_STYLE_WIN4,
535                                LocaleId,
536                                &ErrorLine);
537     if (hInf == INVALID_HANDLE_VALUE)
538     {
539         DPRINT1("SetupOpenInfFileEx() failed\n");
540         return FALSE;
541     }
542 
543 #if 0
544     if (!registry_callback(hInf, L"DelReg", FALSE))
545     {
546         DPRINT1("registry_callback() failed\n");
547         SetupCloseInfFile(hInf);
548         return FALSE;
549     }
550 #endif
551 
552     if (!registry_callback(hInf, L"AddReg", FALSE))
553     {
554         DPRINT1("registry_callback() failed\n");
555         SetupCloseInfFile(hInf);
556         return FALSE;
557     }
558 
559     if (!registry_callback(hInf, L"AddReg.NT" Architecture, FALSE))
560     {
561         DPRINT1("registry_callback() failed\n");
562         SetupCloseInfFile(hInf);
563         return FALSE;
564     }
565 
566     SetupCloseInfFile(hInf);
567     return TRUE;
568 }
569 
570 
571 typedef enum _HIVE_UPDATE_STATE
572 {
573     Create, // Create a new hive file and save possibly existing old one with a .old extension.
574     Repair, // Re-create a new hive file and save possibly existing old one with a .brk extension.
575     Update  // Hive update, do not need to be recreated.
576 } HIVE_UPDATE_STATE;
577 
578 typedef struct _HIVE_LIST_ENTRY
579 {
580     PCWSTR HiveName;            // HiveFileName;
581     PCWSTR HiveRegistryPath;    // HiveRegMountPoint;
582     HANDLE PredefKeyHandle;
583     PCWSTR RegSymLink;
584     HIVE_UPDATE_STATE State;
585     // PUCHAR SecurityDescriptor;
586     // ULONG  SecurityDescriptorLength;
587 } HIVE_LIST_ENTRY, *PHIVE_LIST_ENTRY;
588 
589 #define NUMBER_OF_STANDARD_REGISTRY_HIVES   3
590 
591 HIVE_LIST_ENTRY RegistryHives[/*NUMBER_OF_STANDARD_REGISTRY_HIVES*/] =
592 {
593     { L"SYSTEM"  , L"\\Registry\\Machine\\USetup_SYSTEM"  , HKEY_LOCAL_MACHINE, L"SYSTEM"  , Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
594     { L"SOFTWARE", L"\\Registry\\Machine\\USetup_SOFTWARE", HKEY_LOCAL_MACHINE, L"SOFTWARE", Create /* , SoftwareSecurity, sizeof(SoftwareSecurity) */ },
595     { L"DEFAULT" , L"\\Registry\\User\\USetup_DEFAULT"    , HKEY_USERS        , L".DEFAULT", Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
596 
597 //  { L"BCD"     , L"\\Registry\\Machine\\USetup_BCD", HKEY_LOCAL_MACHINE, L"BCD00000000", Create /* , BcdSecurity     , sizeof(BcdSecurity)      */ },
598 };
599 C_ASSERT(_countof(RegistryHives) == NUMBER_OF_STANDARD_REGISTRY_HIVES);
600 
601 #define NUMBER_OF_SECURITY_REGISTRY_HIVES   2
602 
603 /** These hives are created by LSASS during 2nd stage setup */
604 HIVE_LIST_ENTRY SecurityRegistryHives[/*NUMBER_OF_SECURITY_REGISTRY_HIVES*/] =
605 {
606     { L"SAM"     , L"\\Registry\\Machine\\USetup_SAM"     , HKEY_LOCAL_MACHINE, L"SAM"     , Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
607     { L"SECURITY", L"\\Registry\\Machine\\USetup_SECURITY", HKEY_LOCAL_MACHINE, L"SECURITY", Create /* , NULL            , 0                        */ },
608 };
609 C_ASSERT(_countof(SecurityRegistryHives) == NUMBER_OF_SECURITY_REGISTRY_HIVES);
610 
611 
612 NTSTATUS
613 VerifyRegistryHives(
614     IN PUNICODE_STRING NtSystemRoot,
615     OUT PBOOLEAN ShouldRepairRegistry)
616 {
617     NTSTATUS Status;
618     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
619     UINT i;
620 
621     /* Suppose first the registry hives do not have to be fully recreated */
622     *ShouldRepairRegistry = FALSE;
623 
624     /* Acquire restore privilege */
625     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
626     if (!NT_SUCCESS(Status))
627     {
628         DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
629         /* Exit prematurely here.... */
630         return Status;
631     }
632 
633     /* Acquire backup privilege */
634     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
635     if (!NT_SUCCESS(Status))
636     {
637         DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
638         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
639         /* Exit prematurely here.... */
640         return Status;
641     }
642 
643     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
644     {
645         Status = VerifyRegistryHive(NtSystemRoot, RegistryHives[i].HiveName);
646         if (!NT_SUCCESS(Status))
647         {
648             DPRINT1("Registry hive '%S' needs repair!\n", RegistryHives[i].HiveName);
649             RegistryHives[i].State = Repair;
650             *ShouldRepairRegistry = TRUE;
651         }
652         else
653         {
654             RegistryHives[i].State = Update;
655         }
656     }
657 
658     /** These hives are created by LSASS during 2nd stage setup */
659     for (i = 0; i < ARRAYSIZE(SecurityRegistryHives); ++i)
660     {
661         Status = VerifyRegistryHive(NtSystemRoot, SecurityRegistryHives[i].HiveName);
662         if (!NT_SUCCESS(Status))
663         {
664             DPRINT1("Registry hive '%S' needs repair!\n", SecurityRegistryHives[i].HiveName);
665             SecurityRegistryHives[i].State = Repair;
666             /*
667              * Note that it's not the role of the 1st-stage installer to fix
668              * the security hives. This should be done at 2nd-stage installation
669              * by LSASS.
670              */
671         }
672         else
673         {
674             SecurityRegistryHives[i].State = Update;
675         }
676     }
677 
678     /* Reset the status (we succeeded in checking all the hives) */
679     Status = STATUS_SUCCESS;
680 
681     /* Remove restore and backup privileges */
682     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
683     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
684 
685     return Status;
686 }
687 
688 NTSTATUS
689 RegInitializeRegistry(
690     IN PUNICODE_STRING NtSystemRoot)
691 {
692     NTSTATUS Status;
693     HANDLE KeyHandle;
694     UNICODE_STRING KeyName;
695     OBJECT_ATTRIBUTES ObjectAttributes;
696     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
697     ULONG Disposition;
698     UINT i;
699 
700     /* Acquire restore privilege */
701     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
702     if (!NT_SUCCESS(Status))
703     {
704         DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
705         /* Exit prematurely here.... */
706         return Status;
707     }
708 
709     /* Acquire backup privilege */
710     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
711     if (!NT_SUCCESS(Status))
712     {
713         DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
714         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
715         /* Exit prematurely here.... */
716         return Status;
717     }
718 
719     /*
720      * Create the template proto-hive.
721      *
722      * Use a dummy root key name:
723      * - On 2k/XP/2k3, this is "$$$PROTO.HIV"
724      * - On Vista+, this is "CMI-CreateHive{guid}"
725      * See https://github.com/libyal/winreg-kb/blob/master/documentation/Registry%20files.asciidoc
726      * for more information.
727      */
728     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
729     InitializeObjectAttributes(&ObjectAttributes,
730                                &KeyName,
731                                OBJ_CASE_INSENSITIVE,
732                                NULL,
733                                NULL);
734     Status = NtCreateKey(&KeyHandle,
735                          KEY_ALL_ACCESS,
736                          &ObjectAttributes,
737                          0,
738                          NULL,
739                          REG_OPTION_NON_VOLATILE,
740                          NULL);
741     if (!NT_SUCCESS(Status))
742     {
743         DPRINT1("NtCreateKey() failed to create the proto-hive (Status %lx)\n", Status);
744         goto Quit;
745     }
746     NtFlushKey(KeyHandle);
747 
748     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
749     {
750         if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
751             continue;
752 
753         Status = CreateRegistryFile(NtSystemRoot,
754                                     RegistryHives[i].HiveName,
755                                     RegistryHives[i].State != Repair, // RegistryHives[i].State == Create,
756                                     KeyHandle);
757         if (!NT_SUCCESS(Status))
758         {
759             DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
760             /* Exit prematurely here.... */
761             /* That is now done, remove the proto-hive */
762             NtDeleteKey(KeyHandle);
763             NtClose(KeyHandle);
764             goto Quit;
765         }
766     }
767 
768     /* That is now done, remove the proto-hive */
769     NtDeleteKey(KeyHandle);
770     NtClose(KeyHandle);
771 
772 
773     /*
774      * Prepare the registry root keys. Since we cannot create real registry keys
775      * inside the master keys (\Registry, \Registry\Machine or \Registry\User),
776      * we need to perform some SymLink tricks instead.
777      */
778 
779     /* Our offline HKLM '\Registry\Machine' is inside '\Registry\Machine\SYSTEM\USetup_Machine' */
780     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].MountPoint);
781     InitializeObjectAttributes(&ObjectAttributes,
782                                &KeyName,
783                                OBJ_CASE_INSENSITIVE,
784                                NULL,
785                                NULL);
786     KeyHandle = NULL;
787     Status = NtCreateKey(&KeyHandle,
788                          KEY_ALL_ACCESS,
789                          &ObjectAttributes,
790                          0,
791                          NULL,
792                          REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE, // FIXME!
793                          &Disposition);
794     if (!NT_SUCCESS(Status))
795     {
796         DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
797         // return Status;
798     }
799     RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle = KeyHandle;
800 
801     /* Our offline HKU '\Registry\User' is inside '\Registry\Machine\SYSTEM\USetup_User' */
802     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_USERS)].MountPoint);
803     InitializeObjectAttributes(&ObjectAttributes,
804                                &KeyName,
805                                OBJ_CASE_INSENSITIVE,
806                                NULL,
807                                NULL);
808     KeyHandle = NULL;
809     Status = NtCreateKey(&KeyHandle,
810                          KEY_ALL_ACCESS,
811                          &ObjectAttributes,
812                          0,
813                          NULL,
814                          REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE, // FIXME!
815                          &Disposition);
816     if (!NT_SUCCESS(Status))
817     {
818         DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
819         // return Status;
820     }
821     RootKeys[GetPredefKeyIndex(HKEY_USERS)].Handle = KeyHandle;
822 
823 
824     /*
825      * Now properly mount the offline hive files
826      */
827     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
828     {
829         // if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
830             // continue;
831 
832         if (RegistryHives[i].State == Create || RegistryHives[i].State == Repair)
833         {
834             Status = ConnectRegistry(NULL,
835                                      RegistryHives[i].HiveRegistryPath,
836                                      NtSystemRoot,
837                                      RegistryHives[i].HiveName
838                                      /* SystemSecurity, sizeof(SystemSecurity) */);
839             if (!NT_SUCCESS(Status))
840             {
841                 DPRINT1("ConnectRegistry(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
842             }
843 
844             /* Create the registry symlink to this key */
845             if (!CmpLinkKeyToHive(RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
846                                   RegistryHives[i].RegSymLink,
847                                   RegistryHives[i].HiveRegistryPath))
848             {
849                 DPRINT1("CmpLinkKeyToHive(%S) failed!\n", RegistryHives[i].HiveName);
850             }
851         }
852         else
853         {
854             /* Create *DUMMY* volatile hives just to make the update procedure working */
855 
856             RtlInitUnicodeString(&KeyName, RegistryHives[i].RegSymLink);
857             InitializeObjectAttributes(&ObjectAttributes,
858                                        &KeyName,
859                                        OBJ_CASE_INSENSITIVE,
860                                        RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
861                                        NULL);
862             KeyHandle = NULL;
863             Status = NtCreateKey(&KeyHandle,
864                                  KEY_ALL_ACCESS,
865                                  &ObjectAttributes,
866                                  0,
867                                  NULL,
868                                  REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE, // FIXME!
869                                  &Disposition);
870             if (!NT_SUCCESS(Status))
871             {
872                 DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
873                 // return Status;
874             }
875             NtClose(KeyHandle);
876         }
877     }
878 
879 
880     /* HKCU is a handle to 'HKU\.DEFAULT' */
881 #if 0
882     RtlInitUnicodeString(&KeyName, L".DEFAULT");
883     InitializeObjectAttributes(&ObjectAttributes,
884                                &KeyName,
885                                OBJ_CASE_INSENSITIVE,
886                                RootKeys[GetPredefKeyIndex(HKEY_USERS)].Handle,
887                                NULL);
888 #else
889     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_CURRENT_USER)].MountPoint);
890     InitializeObjectAttributes(&ObjectAttributes,
891                                &KeyName,
892                                OBJ_CASE_INSENSITIVE,
893                                NULL,
894                                NULL);
895 #endif
896     KeyHandle = NULL;
897     Status = NtOpenKey(&KeyHandle,
898                        KEY_ALL_ACCESS,
899                        &ObjectAttributes);
900     if (!NT_SUCCESS(Status))
901     {
902         DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
903     }
904     RootKeys[GetPredefKeyIndex(HKEY_CURRENT_USER)].Handle = KeyHandle;
905 
906 
907     /* HKCR is a handle to 'HKLM\Software\Classes' */
908 #if 0
909     RtlInitUnicodeString(&KeyName, L"Software\\Classes");
910     InitializeObjectAttributes(&ObjectAttributes,
911                                &KeyName,
912                                OBJ_CASE_INSENSITIVE,
913                                RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
914                                NULL);
915 #else
916     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_CLASSES_ROOT)].MountPoint);
917     InitializeObjectAttributes(&ObjectAttributes,
918                                &KeyName,
919                                OBJ_CASE_INSENSITIVE,
920                                NULL,
921                                NULL);
922 #endif
923     KeyHandle = NULL;
924     /* We use NtCreateKey instead of NtOpenKey because Software\Classes doesn't exist originally */
925     Status = NtCreateKey(&KeyHandle,
926                          KEY_ALL_ACCESS,
927                          &ObjectAttributes,
928                          0,
929                          NULL,
930                          REG_OPTION_NON_VOLATILE,
931                          &Disposition);
932     if (!NT_SUCCESS(Status))
933     {
934         DPRINT1("NtCreateKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
935     }
936     else
937     {
938         DPRINT1("NtCreateKey() succeeded to %s the %wZ key (Status %lx)\n",
939                 Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
940                 &KeyName, Status);
941     }
942     RootKeys[GetPredefKeyIndex(HKEY_CLASSES_ROOT)].Handle = KeyHandle;
943 
944 
945     Status = STATUS_SUCCESS;
946 
947 
948     /* Create the 'HKLM\SYSTEM\ControlSet001' key */
949     // REGISTRY_SETUP_MACHINE L"SYSTEM\\ControlSet001"
950     RtlInitUnicodeString(&KeyName, L"SYSTEM\\ControlSet001");
951     InitializeObjectAttributes(&ObjectAttributes,
952                                &KeyName,
953                                OBJ_CASE_INSENSITIVE,
954                                RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
955                                NULL);
956     Status = NtCreateKey(&KeyHandle,
957                          KEY_ALL_ACCESS,
958                          &ObjectAttributes,
959                          0,
960                          NULL,
961                          REG_OPTION_NON_VOLATILE,
962                          &Disposition);
963     if (!NT_SUCCESS(Status))
964     {
965         DPRINT1("NtCreateKey() failed to create the ControlSet001 key (Status %lx)\n", Status);
966         // return Status;
967     }
968     else
969     {
970         DPRINT1("NtCreateKey() succeeded to %s the ControlSet001 key (Status %lx)\n",
971                 Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
972                 Status);
973     }
974     NtClose(KeyHandle);
975 
976     /* Create the 'HKLM\SYSTEM\CurrentControlSet' symlink */
977     if (!CmpLinkKeyToHive(RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
978                           L"SYSTEM\\CurrentControlSet",
979                           REGISTRY_SETUP_MACHINE L"SYSTEM\\ControlSet001"))
980     {
981         DPRINT1("CmpLinkKeyToHive(CurrentControlSet) failed!\n");
982     }
983 
984 
985     Status = STATUS_SUCCESS;
986 
987 
988 Quit:
989     /* Remove restore and backup privileges */
990     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
991     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
992 
993     return Status;
994 }
995 
996 VOID
997 RegCleanupRegistry(
998     IN PUNICODE_STRING NtSystemRoot)
999 {
1000     NTSTATUS Status;
1001     HANDLE KeyHandle;
1002     UNICODE_STRING KeyName;
1003     OBJECT_ATTRIBUTES ObjectAttributes;
1004     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
1005     UINT i;
1006     WCHAR SrcPath[MAX_PATH];
1007     WCHAR DstPath[MAX_PATH];
1008 
1009     /* Acquire restore privilege */
1010     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
1011     if (!NT_SUCCESS(Status))
1012     {
1013         DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
1014         /* Exit prematurely here.... */
1015         return;
1016     }
1017 
1018     /* Acquire backup privilege */
1019     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
1020     if (!NT_SUCCESS(Status))
1021     {
1022         DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
1023         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
1024         /* Exit prematurely here.... */
1025         return;
1026     }
1027 
1028     /*
1029      * Note that we don't need to explicitly remove the symlinks we have created
1030      * since they are created volatile, inside registry keys that will be however
1031      * removed explictly in the following.
1032      */
1033 
1034     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
1035     {
1036         if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
1037         {
1038             RtlInitUnicodeString(&KeyName, RegistryHives[i].RegSymLink);
1039             InitializeObjectAttributes(&ObjectAttributes,
1040                                        &KeyName,
1041                                        OBJ_CASE_INSENSITIVE,
1042                                        RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
1043                                        NULL);
1044             KeyHandle = NULL;
1045             Status = NtOpenKey(&KeyHandle,
1046                                DELETE,
1047                                &ObjectAttributes);
1048             if (!NT_SUCCESS(Status))
1049             {
1050                 DPRINT1("NtOpenKey(%wZ) failed, Status 0x%08lx\n", &KeyName, Status);
1051                 // return;
1052             }
1053 
1054             NtDeleteKey(KeyHandle);
1055             NtClose(KeyHandle);
1056         }
1057         else
1058         {
1059             Status = DisconnectRegistry(NULL,
1060                                         RegistryHives[i].HiveRegistryPath,
1061                                         1 /* REG_FORCE_UNLOAD */);
1062             DPRINT1("Unmounting '%S' %s\n", RegistryHives[i].HiveRegistryPath, NT_SUCCESS(Status) ? "succeeded" : "failed");
1063 
1064             /* Switch the hive state to 'Update' */
1065             RegistryHives[i].State = Update;
1066         }
1067     }
1068 
1069     /*
1070      * FIXME: Once force-unloading keys is correctly fixed, I'll fix
1071      * this code that closes some of the registry keys that were opened
1072      * inside the hives we've just unmounted above...
1073      */
1074 
1075     /* Remove the registry root keys */
1076     for (i = 0; i < ARRAYSIZE(RootKeys); ++i)
1077     {
1078         if (RootKeys[i].Handle)
1079         {
1080             /**/NtFlushKey(RootKeys[i].Handle);/**/ // FIXME: Why does it hang? Answer: because we have some problems in CMAPI!
1081             NtDeleteKey(RootKeys[i].Handle);
1082             NtClose(RootKeys[i].Handle);
1083             RootKeys[i].Handle = NULL;
1084         }
1085     }
1086 
1087     //
1088     // RegBackupRegistry()
1089     //
1090     /* Now backup the hives into .sav files */
1091     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
1092     {
1093         if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
1094             continue;
1095 
1096         CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 3,
1097                      NtSystemRoot->Buffer, L"System32\\config", RegistryHives[i].HiveName);
1098         RtlStringCchCopyW(DstPath, ARRAYSIZE(DstPath), SrcPath);
1099         RtlStringCchCatW(DstPath, ARRAYSIZE(DstPath), L".sav");
1100 
1101         DPRINT1("Copy hive: %S ==> %S\n", SrcPath, DstPath);
1102         Status = SetupCopyFile(SrcPath, DstPath, FALSE);
1103         if (!NT_SUCCESS(Status))
1104         {
1105             DPRINT1("SetupCopyFile() failed (Status %lx)\n", Status);
1106             // return Status;
1107         }
1108     }
1109 
1110     /* Remove restore and backup privileges */
1111     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
1112     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
1113 }
1114 
1115 /* EOF */
1116