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