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