xref: /reactos/base/system/smss/sminit.c (revision 8a92b556)
1 /*
2  * PROJECT:         ReactOS Windows-Compatible Session Manager
3  * LICENSE:         BSD 2-Clause License
4  * FILE:            base/system/smss/sminit.c
5  * PURPOSE:         Main SMSS Code
6  * PROGRAMMERS:     Alex Ionescu
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "smss.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ********************************************************************/
17 
18 UNICODE_STRING SmpSubsystemName, PosixName, Os2Name;
19 LIST_ENTRY SmpBootExecuteList, SmpSetupExecuteList, SmpPagingFileList;
20 LIST_ENTRY SmpDosDevicesList, SmpFileRenameList, SmpKnownDllsList;
21 LIST_ENTRY SmpExcludeKnownDllsList, SmpSubSystemList, SmpSubSystemsToLoad;
22 LIST_ENTRY SmpSubSystemsToDefer, SmpExecuteList, NativeProcessList;
23 
24 PVOID SmpHeap;
25 ULONG SmBaseTag;
26 HANDLE SmpDebugPort, SmpDosDevicesObjectDirectory;
27 PWCHAR SmpDefaultEnvironment, SmpDefaultLibPathBuffer;
28 UNICODE_STRING SmpKnownDllPath, SmpDefaultLibPath;
29 ULONG SmpCalledConfigEnv;
30 
31 ULONG SmpInitProgressByLine;
32 NTSTATUS SmpInitReturnStatus;
33 PVOID SmpInitLastCall;
34 
35 SECURITY_DESCRIPTOR SmpPrimarySDBody, SmpLiberalSDBody, SmpKnownDllsSDBody;
36 SECURITY_DESCRIPTOR SmpApiPortSDBody;
37 PISECURITY_DESCRIPTOR SmpPrimarySecurityDescriptor, SmpLiberalSecurityDescriptor;
38 PISECURITY_DESCRIPTOR SmpKnownDllsSecurityDescriptor, SmpApiPortSecurityDescriptor;
39 
40 ULONG SmpAllowProtectedRenames, SmpProtectionMode = 1;
41 BOOLEAN MiniNTBoot = FALSE;
42 
43 #define SMSS_CHECKPOINT(x, y)           \
44 {                                       \
45     SmpInitProgressByLine = __LINE__;   \
46     SmpInitReturnStatus = (y);          \
47     SmpInitLastCall = (x);              \
48 }
49 
50 /* REGISTRY CONFIGURATION *****************************************************/
51 
52 NTSTATUS
53 NTAPI
54 SmpSaveRegistryValue(IN PLIST_ENTRY ListAddress,
55                      IN PWSTR Name,
56                      IN PWCHAR Value,
57                      IN BOOLEAN Flags)
58 {
59     PSMP_REGISTRY_VALUE RegEntry;
60     UNICODE_STRING NameString, ValueString;
61     ANSI_STRING AnsiValueString;
62     PLIST_ENTRY NextEntry;
63 
64     /* Convert to unicode strings */
65     RtlInitUnicodeString(&NameString, Name);
66     RtlInitUnicodeString(&ValueString, Value);
67 
68     /* In case this is the first value, initialize a new list/structure */
69     RegEntry = NULL;
70 
71     /* Check if we should do a duplicate check */
72     if (Flags)
73     {
74         /* Loop the current list */
75         NextEntry = ListAddress->Flink;
76         while (NextEntry != ListAddress)
77         {
78             /* Get each entry */
79             RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
80 
81             /* Check if the value name matches */
82             if (!RtlCompareUnicodeString(&RegEntry->Name, &NameString, TRUE))
83             {
84                 /* Check if the value is the exact same thing */
85                 if (!RtlCompareUnicodeString(&RegEntry->Value, &ValueString, TRUE))
86                 {
87                     /* Fail -- the same setting is being set twice */
88                     return STATUS_OBJECT_NAME_EXISTS;
89                 }
90 
91                 /* We found the list, and this isn't a duplicate value */
92                 break;
93             }
94 
95             /* This wasn't a match, keep going */
96             NextEntry = NextEntry->Flink;
97             RegEntry = NULL;
98         }
99     }
100 
101     /* Are we adding on, or creating a new entry */
102     if (!RegEntry)
103     {
104         /* A new entry -- allocate it */
105         RegEntry = RtlAllocateHeap(RtlGetProcessHeap(),
106                                    SmBaseTag,
107                                    sizeof(SMP_REGISTRY_VALUE) +
108                                    NameString.MaximumLength);
109         if (!RegEntry) return STATUS_NO_MEMORY;
110 
111         /* Initialize the list and set all values to NULL */
112         InitializeListHead(&RegEntry->Entry);
113         RegEntry->AnsiValue = NULL;
114         RegEntry->Value.Buffer = NULL;
115 
116         /* Copy and initialize the value name */
117         RegEntry->Name.Buffer = (PWCHAR)(RegEntry + 1);
118         RegEntry->Name.Length = NameString.Length;
119         RegEntry->Name.MaximumLength = NameString.MaximumLength;
120         RtlCopyMemory(RegEntry->Name.Buffer,
121                       NameString.Buffer,
122                       NameString.MaximumLength);
123 
124         /* Add this entry into the list */
125         InsertTailList(ListAddress, &RegEntry->Entry);
126     }
127 
128     /* Did we have an old value buffer? */
129     if (RegEntry->Value.Buffer)
130     {
131         /* Free it */
132         ASSERT(RegEntry->Value.Length != 0);
133         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
134     }
135 
136     /* Is there no value associated? */
137     if (!Value)
138     {
139         /* We're done here */
140         RtlInitUnicodeString(&RegEntry->Value, NULL);
141         return STATUS_SUCCESS;
142     }
143 
144     /* There is a value, so allocate a buffer for it */
145     RegEntry->Value.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
146                                              SmBaseTag,
147                                              ValueString.MaximumLength);
148     if (!RegEntry->Value.Buffer)
149     {
150         /* Out of memory, undo */
151         RemoveEntryList(&RegEntry->Entry);
152         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
153         return STATUS_NO_MEMORY;
154     }
155 
156     /* Copy the value into the entry */
157     RegEntry->Value.Length = ValueString.Length;
158     RegEntry->Value.MaximumLength = ValueString.MaximumLength;
159     RtlCopyMemory(RegEntry->Value.Buffer,
160                   ValueString.Buffer,
161                   ValueString.MaximumLength);
162 
163     /* Now allocate memory for an ANSI copy of it */
164     RegEntry->AnsiValue = RtlAllocateHeap(RtlGetProcessHeap(),
165                                           SmBaseTag,
166                                           (ValueString.Length / sizeof(WCHAR)) +
167                                           sizeof(ANSI_NULL));
168     if (!RegEntry->AnsiValue)
169     {
170         /* Out of memory, undo */
171         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
172         RemoveEntryList(&RegEntry->Entry);
173         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
174         return STATUS_NO_MEMORY;
175     }
176 
177     /* Convert the Unicode value string and return success */
178     RtlInitEmptyAnsiString(&AnsiValueString,
179                            RegEntry->AnsiValue,
180                            (ValueString.Length / sizeof(WCHAR)) +
181                            sizeof(ANSI_NULL));
182     RtlUnicodeStringToAnsiString(&AnsiValueString, &ValueString, FALSE);
183     return STATUS_SUCCESS;
184 }
185 
186 PSMP_REGISTRY_VALUE
187 NTAPI
188 SmpFindRegistryValue(IN PLIST_ENTRY List,
189                      IN PWSTR ValueName)
190 {
191     PSMP_REGISTRY_VALUE RegEntry;
192     UNICODE_STRING ValueString;
193     PLIST_ENTRY NextEntry;
194 
195     /* Initialize the value name sting */
196     RtlInitUnicodeString(&ValueString, ValueName);
197 
198     /* Loop the list */
199     NextEntry = List->Flink;
200     while (NextEntry != List)
201     {
202         /* Get each entry */
203         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
204 
205         /* Check if the value name matches */
206         if (!RtlCompareUnicodeString(&RegEntry->Name, &ValueString, TRUE)) break;
207 
208         /* It doesn't, move on */
209         NextEntry = NextEntry->Flink;
210     }
211 
212     /* If we looped back, return NULL, otherwise return the entry we found */
213     if (NextEntry == List) RegEntry = NULL;
214     return RegEntry;
215 }
216 
217 NTSTATUS
218 NTAPI
219 SmpConfigureProtectionMode(IN PWSTR ValueName,
220                            IN ULONG ValueType,
221                            IN PVOID ValueData,
222                            IN ULONG ValueLength,
223                            IN PVOID Context,
224                            IN PVOID EntryContext)
225 {
226     /* Make sure the value is valid */
227     if (ValueLength == sizeof(ULONG))
228     {
229         /* Read it */
230         SmpProtectionMode = *(PULONG)ValueData;
231     }
232     else
233     {
234         /* Default is to protect stuff */
235         SmpProtectionMode = 1;
236     }
237 
238     /* Recreate the security descriptors to take into account security mode */
239     SmpCreateSecurityDescriptors(FALSE);
240     DPRINT("SmpProtectionMode: %lu\n", SmpProtectionMode);
241     return STATUS_SUCCESS;
242 }
243 
244 NTSTATUS
245 NTAPI
246 SmpConfigureAllowProtectedRenames(IN PWSTR ValueName,
247                                   IN ULONG ValueType,
248                                   IN PVOID ValueData,
249                                   IN ULONG ValueLength,
250                                   IN PVOID Context,
251                                   IN PVOID EntryContext)
252 {
253     /* Make sure the value is valid */
254     if (ValueLength == sizeof(ULONG))
255     {
256         /* Read it */
257         SmpAllowProtectedRenames = *(PULONG)ValueData;
258     }
259     else
260     {
261         /* Default is to not allow protected renames */
262         SmpAllowProtectedRenames = 0;
263     }
264 
265     DPRINT("SmpAllowProtectedRenames: %lu\n", SmpAllowProtectedRenames);
266     return STATUS_SUCCESS;
267 }
268 
269 NTSTATUS
270 NTAPI
271 SmpConfigureObjectDirectories(IN PWSTR ValueName,
272                               IN ULONG ValueType,
273                               IN PVOID ValueData,
274                               IN ULONG ValueLength,
275                               IN PVOID Context,
276                               IN PVOID EntryContext)
277 {
278     PISECURITY_DESCRIPTOR SecDescriptor;
279     NTSTATUS Status;
280     OBJECT_ATTRIBUTES ObjectAttributes;
281     HANDLE DirHandle;
282     UNICODE_STRING RpcString, WindowsString, SearchString;
283     PWCHAR SourceString = ValueData;
284 
285     /* Initialize the two strings we will be looking for */
286     RtlInitUnicodeString(&RpcString, L"\\RPC Control");
287     RtlInitUnicodeString(&WindowsString, L"\\Windows");
288 
289     /* Loop the registry data we received */
290     while (*SourceString)
291     {
292         /* Assume primary SD for most objects */
293         RtlInitUnicodeString(&SearchString, SourceString);
294         SecDescriptor = SmpPrimarySecurityDescriptor;
295 
296         /* But for these two always set the liberal descriptor */
297         if ((RtlEqualUnicodeString(&SearchString, &RpcString, TRUE)) ||
298             (RtlEqualUnicodeString(&SearchString, &WindowsString, TRUE)))
299         {
300             SecDescriptor = SmpLiberalSecurityDescriptor;
301         }
302 
303         /* Create the requested directory with the requested descriptor */
304         InitializeObjectAttributes(&ObjectAttributes,
305                                    &SearchString,
306                                    OBJ_CASE_INSENSITIVE |
307                                    OBJ_OPENIF |
308                                    OBJ_PERMANENT,
309                                    NULL,
310                                    SecDescriptor);
311         DPRINT("Creating: %wZ directory\n", &SearchString);
312         Status = NtCreateDirectoryObject(&DirHandle,
313                                          DIRECTORY_ALL_ACCESS,
314                                          &ObjectAttributes);
315         if (!NT_SUCCESS(Status))
316         {
317             /* Failure case */
318             DPRINT1("SMSS: Unable to create %wZ object directory - Status == %lx\n",
319                     &SearchString, Status);
320         }
321         else
322         {
323             /* It worked, now close the handle */
324             NtClose(DirHandle);
325         }
326 
327         /* Move to the next requested object */
328         SourceString += wcslen(SourceString) + 1;
329     }
330 
331     /* All done */
332     return STATUS_SUCCESS;
333 }
334 
335 NTSTATUS
336 NTAPI
337 SmpConfigureMemoryMgmt(IN PWSTR ValueName,
338                        IN ULONG ValueType,
339                        IN PVOID ValueData,
340                        IN ULONG ValueLength,
341                        IN PVOID Context,
342                        IN PVOID EntryContext)
343 {
344     /* Save this is into a list */
345     return SmpSaveRegistryValue(EntryContext, ValueData, NULL, TRUE);
346 }
347 
348 NTSTATUS
349 NTAPI
350 SmpConfigureFileRenames(IN PWSTR ValueName,
351                         IN ULONG ValueType,
352                         IN PVOID ValueData,
353                         IN ULONG ValueLength,
354                         IN PVOID Context,
355                         IN PVOID EntryContext)
356 {
357     NTSTATUS Status;
358     static PWCHAR Canary = NULL;
359 
360     /* Check if this is the second call */
361     if (Canary)
362     {
363         /* Save the data into the list */
364         DPRINT("Renamed file: '%S' - '%S'\n", Canary, ValueData);
365         Status = SmpSaveRegistryValue(EntryContext, Canary, ValueData, FALSE);
366         Canary = NULL;
367     }
368     else
369     {
370         /* This it the first call, do nothing until we get the second call */
371         Canary = ValueData;
372         Status = STATUS_SUCCESS;
373     }
374 
375     /* Return the status */
376     return Status;
377 }
378 
379 NTSTATUS
380 NTAPI
381 SmpConfigureExcludeKnownDlls(IN PWSTR ValueName,
382                              IN ULONG ValueType,
383                              IN PVOID ValueData,
384                              IN ULONG ValueLength,
385                              IN PVOID Context,
386                              IN PVOID EntryContext)
387 {
388     PWCHAR DllName;
389     NTSTATUS Status;
390 
391     /* Make sure the value type is valid */
392     if ((ValueType == REG_MULTI_SZ) || (ValueType == REG_SZ))
393     {
394         /* Keep going for each DLL in the list */
395         DllName = ValueData;
396         while (*DllName)
397         {
398             /* Add this to the linked list */
399             DPRINT("Excluded DLL: %S\n", DllName);
400             Status = SmpSaveRegistryValue(EntryContext, DllName, NULL, TRUE);
401 
402             /* Bail out on failure or if only one DLL name was present */
403             if (!(NT_SUCCESS(Status)) || (ValueType == REG_SZ)) return Status;
404 
405             /* Otherwise, move to the next DLL name */
406             DllName += wcslen(DllName) + 1;
407         }
408     }
409 
410     /* All done */
411     return STATUS_SUCCESS;
412 }
413 
414 NTSTATUS
415 NTAPI
416 SmpConfigureDosDevices(IN PWSTR ValueName,
417                        IN ULONG ValueType,
418                        IN PVOID ValueData,
419                        IN ULONG ValueLength,
420                        IN PVOID Context,
421                        IN PVOID EntryContext)
422 {
423     /* Save into linked list */
424     return SmpSaveRegistryValue(EntryContext, ValueName, ValueData, TRUE);
425 }
426 
427 NTSTATUS
428 NTAPI
429 SmpInitializeKnownDllPath(IN PUNICODE_STRING DllPath,
430                           IN PWCHAR Buffer,
431                           IN ULONG Length)
432 {
433     NTSTATUS Status;
434 
435     /* Allocate the buffer */
436     DllPath->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), SmBaseTag, Length);
437     if (DllPath->Buffer)
438     {
439         /* Fill out the rest of the string */
440         DllPath->MaximumLength = (USHORT)Length;
441         DllPath->Length = (USHORT)Length - sizeof(UNICODE_NULL);
442 
443         /* Copy the actual path and return success */
444         RtlCopyMemory(DllPath->Buffer, Buffer, Length);
445         Status = STATUS_SUCCESS;
446     }
447     else
448     {
449         /* Fail with out of memory code */
450         Status = STATUS_NO_MEMORY;
451     }
452 
453     /* Return result */
454     return Status;
455 }
456 
457 NTSTATUS
458 NTAPI
459 SmpConfigureKnownDlls(IN PWSTR ValueName,
460                       IN ULONG ValueType,
461                       IN PVOID ValueData,
462                       IN ULONG ValueLength,
463                       IN PVOID Context,
464                       IN PVOID EntryContext)
465 {
466     /* Check which value is being set */
467     if (_wcsicmp(ValueName, L"DllDirectory") == 0)
468     {
469         /* This is the directory, initialize it */
470         DPRINT("KnownDll Path: %S\n", ValueData);
471         return SmpInitializeKnownDllPath(&SmpKnownDllPath, ValueData, ValueLength);
472     }
473     else
474     {
475         /* Add to the linked list -- this is a file */
476         return SmpSaveRegistryValue(EntryContext, ValueName, ValueData, TRUE);
477     }
478 }
479 
480 NTSTATUS
481 NTAPI
482 SmpConfigureEnvironment(IN PWSTR ValueName,
483                         IN ULONG ValueType,
484                         IN PVOID ValueData,
485                         IN ULONG ValueLength,
486                         IN PVOID Context,
487                         IN PVOID EntryContext)
488 {
489     NTSTATUS Status;
490     UNICODE_STRING ValueString, DataString;
491 
492     /* Convert the strings into UNICODE_STRING and set the variable defined */
493     RtlInitUnicodeString(&ValueString, ValueName);
494     RtlInitUnicodeString(&DataString, ValueData);
495     DPRINT("Setting %wZ = %wZ\n", &ValueString, &DataString);
496     Status = RtlSetEnvironmentVariable(0, &ValueString, &DataString);
497     if (!NT_SUCCESS(Status))
498     {
499         DPRINT1("SMSS: 'SET %wZ = %wZ' failed - Status == %lx\n",
500                 &ValueString, &DataString, Status);
501         return Status;
502     }
503 
504     /* Check if the path is being set, and wait for the second instantiation */
505     if ((_wcsicmp(ValueName, L"Path") == 0) && (++SmpCalledConfigEnv == 2))
506     {
507         /* Allocate the path buffer */
508         SmpDefaultLibPathBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
509                                                   SmBaseTag,
510                                                   ValueLength);
511         if (!SmpDefaultLibPathBuffer) return STATUS_NO_MEMORY;
512 
513         /* Copy the data into it and create the UNICODE_STRING to hold it */
514         RtlCopyMemory(SmpDefaultLibPathBuffer, ValueData, ValueLength);
515         RtlInitUnicodeString(&SmpDefaultLibPath, SmpDefaultLibPathBuffer);
516     }
517 
518     /* All good */
519     return STATUS_SUCCESS;
520 }
521 
522 NTSTATUS
523 NTAPI
524 SmpConfigureSubSystems(IN PWSTR ValueName,
525                        IN ULONG ValueType,
526                        IN PVOID ValueData,
527                        IN ULONG ValueLength,
528                        IN PVOID Context,
529                        IN PVOID EntryContext)
530 {
531     PSMP_REGISTRY_VALUE RegEntry;
532     PWCHAR SubsystemName;
533 
534     /* Is this a required or optional subsystem? */
535     if ((_wcsicmp(ValueName, L"Required") != 0) &&
536         (_wcsicmp(ValueName, L"Optional") != 0))
537     {
538         /* It isn't, is this the PSI flag? */
539         if ((_wcsicmp(ValueName, L"PosixSingleInstance") != 0) ||
540             (ValueType != REG_DWORD))
541         {
542             /* It isn't, must be a subsystem entry, add it to the list */
543             DPRINT("Subsystem entry: %S-%S\n", ValueName, ValueData);
544             return SmpSaveRegistryValue(EntryContext, ValueName, ValueData, TRUE);
545         }
546 
547         /* This was the PSI flag, save it and exit */
548         RegPosixSingleInstance = TRUE;
549         return STATUS_SUCCESS;
550     }
551 
552     /* This should be one of the required/optional lists. Is the type valid? */
553     if (ValueType == REG_MULTI_SZ)
554     {
555         /* It is, get the first subsystem */
556         SubsystemName = ValueData;
557         while (*SubsystemName)
558         {
559             /* We should have already put it into the list when we found it */
560             DPRINT("Found subsystem: %S\n", SubsystemName);
561             RegEntry = SmpFindRegistryValue(EntryContext, SubsystemName);
562             if (!RegEntry)
563             {
564                 /* This subsystem doesn't exist, so skip it */
565                 DPRINT1("SMSS: Invalid subsystem name - %ws\n", SubsystemName);
566             }
567             else
568             {
569                 /* Found it -- remove it from the main list */
570                 RemoveEntryList(&RegEntry->Entry);
571 
572                 /* Figure out which list to put it in */
573                 if (_wcsicmp(ValueName, L"Required") == 0)
574                 {
575                     /* Put it into the required list */
576                     DPRINT("Required\n");
577                     InsertTailList(&SmpSubSystemsToLoad, &RegEntry->Entry);
578                 }
579                 else
580                 {
581                     /* Put it into the optional list */
582                     DPRINT("Optional\n");
583                     InsertTailList(&SmpSubSystemsToDefer, &RegEntry->Entry);
584                 }
585             }
586 
587             /* Move to the next name */
588             SubsystemName += wcslen(SubsystemName) + 1;
589         }
590     }
591 
592     /* All done! */
593     return STATUS_SUCCESS;
594 }
595 
596 RTL_QUERY_REGISTRY_TABLE
597 SmpRegistryConfigurationTable[] =
598 {
599     {
600         SmpConfigureProtectionMode,
601         0,
602         L"ProtectionMode",
603         NULL,
604         REG_DWORD,
605         NULL,
606         0
607     },
608 
609     {
610         SmpConfigureAllowProtectedRenames,
611         RTL_QUERY_REGISTRY_DELETE,
612         L"AllowProtectedRenames",
613         NULL,
614         REG_DWORD,
615         NULL,
616         0
617     },
618 
619     {
620         SmpConfigureObjectDirectories,
621         0,
622         L"ObjectDirectories",
623         NULL,
624         REG_MULTI_SZ,
625         L"\\Windows\0\\RPC Control\0",
626         0
627     },
628 
629     {
630         SmpConfigureMemoryMgmt,
631         0,
632         L"BootExecute",
633         &SmpBootExecuteList,
634         REG_MULTI_SZ,
635         L"autocheck AutoChk.exe *\0",
636         0
637     },
638 
639     {
640         SmpConfigureMemoryMgmt,
641         RTL_QUERY_REGISTRY_TOPKEY,
642         L"SetupExecute",
643         &SmpSetupExecuteList,
644         REG_NONE,
645         NULL,
646         0
647     },
648 
649     {
650         SmpConfigureFileRenames,
651         RTL_QUERY_REGISTRY_DELETE,
652         L"PendingFileRenameOperations",
653         &SmpFileRenameList,
654         REG_NONE,
655         NULL,
656         0
657     },
658 
659     {
660         SmpConfigureFileRenames,
661         RTL_QUERY_REGISTRY_DELETE,
662         L"PendingFileRenameOperations2",
663         &SmpFileRenameList,
664         REG_NONE,
665         NULL,
666         0
667     },
668 
669     {
670         SmpConfigureExcludeKnownDlls,
671         0,
672         L"ExcludeFromKnownDlls",
673         &SmpExcludeKnownDllsList,
674         REG_MULTI_SZ,
675         L"\0",
676         0
677     },
678 
679     {
680         NULL,
681         RTL_QUERY_REGISTRY_SUBKEY,
682         L"Memory Management",
683         NULL,
684         REG_NONE,
685         NULL,
686         0
687     },
688 
689     {
690         SmpConfigureMemoryMgmt,
691         0,
692         L"PagingFiles",
693         &SmpPagingFileList,
694         REG_MULTI_SZ,
695         L"?:\\pagefile.sys\0",
696         0
697     },
698 
699     {
700         SmpConfigureDosDevices,
701         RTL_QUERY_REGISTRY_SUBKEY,
702         L"DOS Devices",
703         &SmpDosDevicesList,
704         REG_NONE,
705         NULL,
706         0
707     },
708 
709     {
710         SmpConfigureKnownDlls,
711         RTL_QUERY_REGISTRY_SUBKEY,
712         L"KnownDlls",
713         &SmpKnownDllsList,
714         REG_NONE,
715         NULL,
716         0
717     },
718 
719     {
720         SmpConfigureEnvironment,
721         RTL_QUERY_REGISTRY_SUBKEY,
722         L"Environment",
723         NULL,
724         REG_NONE,
725         NULL,
726         0
727     },
728 
729     {
730         SmpConfigureSubSystems,
731         RTL_QUERY_REGISTRY_SUBKEY,
732         L"SubSystems",
733         &SmpSubSystemList,
734         REG_NONE,
735         NULL,
736         0
737     },
738 
739     {
740         SmpConfigureSubSystems,
741         RTL_QUERY_REGISTRY_NOEXPAND,
742         L"Required",
743         &SmpSubSystemList,
744         REG_MULTI_SZ,
745         L"Debug\0Windows\0",
746         0
747     },
748 
749     {
750         SmpConfigureSubSystems,
751         RTL_QUERY_REGISTRY_NOEXPAND,
752         L"Optional",
753         &SmpSubSystemList,
754         REG_NONE,
755         NULL,
756         0
757     },
758 
759     {
760         SmpConfigureSubSystems,
761         0,
762         L"Kmode",
763         &SmpSubSystemList,
764         REG_NONE,
765         NULL,
766         0
767     },
768 
769     {
770         SmpConfigureMemoryMgmt,
771         RTL_QUERY_REGISTRY_TOPKEY,
772         L"Execute",
773         &SmpExecuteList,
774         REG_NONE,
775         NULL,
776         0
777     },
778 
779     {0},
780 };
781 
782 /* FUNCTIONS ******************************************************************/
783 
784 VOID
785 NTAPI
786 SmpTranslateSystemPartitionInformation(VOID)
787 {
788     NTSTATUS Status;
789     UNICODE_STRING UnicodeString, LinkTarget, SearchString, SystemPartition;
790     OBJECT_ATTRIBUTES ObjectAttributes;
791     HANDLE KeyHandle, LinkHandle;
792     ULONG Length, Context;
793     size_t StrLength;
794     WCHAR LinkBuffer[MAX_PATH];
795     CHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512];
796     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
797     CHAR DirInfoBuffer[sizeof(OBJECT_DIRECTORY_INFORMATION) + 512];
798     POBJECT_DIRECTORY_INFORMATION DirInfo = (PVOID)DirInfoBuffer;
799 
800     /* Open the setup key */
801     RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\Setup");
802     InitializeObjectAttributes(&ObjectAttributes,
803                                &UnicodeString,
804                                OBJ_CASE_INSENSITIVE,
805                                NULL,
806                                NULL);
807     Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
808     if (!NT_SUCCESS(Status))
809     {
810         DPRINT1("SMSS: Cannot open system setup key for reading: 0x%x\n", Status);
811         return;
812     }
813 
814     /* Query the system partition */
815     RtlInitUnicodeString(&UnicodeString, L"SystemPartition");
816     Status = NtQueryValueKey(KeyHandle,
817                              &UnicodeString,
818                              KeyValuePartialInformation,
819                              PartialInfo,
820                              sizeof(ValueBuffer),
821                              &Length);
822     NtClose(KeyHandle);
823     if (!NT_SUCCESS(Status) ||
824         ((PartialInfo->Type != REG_SZ) && (PartialInfo->Type != REG_EXPAND_SZ)))
825     {
826         DPRINT1("SMSS: Cannot query SystemPartition value (Type %lu, Status 0x%x)\n",
827                 PartialInfo->Type, Status);
828         return;
829     }
830 
831     /* Initialize the system partition string */
832     RtlInitEmptyUnicodeString(&SystemPartition,
833                               (PWCHAR)PartialInfo->Data,
834                               PartialInfo->DataLength);
835     RtlStringCbLengthW(SystemPartition.Buffer,
836                        SystemPartition.MaximumLength,
837                        &StrLength);
838     SystemPartition.Length = (USHORT)StrLength;
839 
840     /* Enumerate the directory looking for the symbolic link string */
841     RtlInitUnicodeString(&SearchString, L"SymbolicLink");
842     RtlInitEmptyUnicodeString(&LinkTarget, LinkBuffer, sizeof(LinkBuffer));
843     Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory,
844                                     DirInfo,
845                                     sizeof(DirInfoBuffer),
846                                     TRUE,
847                                     TRUE,
848                                     &Context,
849                                     NULL);
850     if (!NT_SUCCESS(Status))
851     {
852         DPRINT1("SMSS: Cannot find drive letter for system partition\n");
853         return;
854     }
855 
856     /* Keep searching until we find it */
857     do
858     {
859         /* Is this it? */
860         if ((RtlEqualUnicodeString(&DirInfo->TypeName, &SearchString, TRUE)) &&
861             (DirInfo->Name.Length == 2 * sizeof(WCHAR)) &&
862             (DirInfo->Name.Buffer[1] == L':'))
863         {
864             /* Looks like we found it, open the link to get its target */
865             InitializeObjectAttributes(&ObjectAttributes,
866                                        &DirInfo->Name,
867                                        OBJ_CASE_INSENSITIVE,
868                                        SmpDosDevicesObjectDirectory,
869                                        NULL);
870             Status = NtOpenSymbolicLinkObject(&LinkHandle,
871                                               SYMBOLIC_LINK_ALL_ACCESS,
872                                               &ObjectAttributes);
873             if (NT_SUCCESS(Status))
874             {
875                 /* Open worked, query the target now */
876                 Status = NtQuerySymbolicLinkObject(LinkHandle,
877                                                    &LinkTarget,
878                                                    NULL);
879                 NtClose(LinkHandle);
880 
881                 /* Check if it matches the string we had found earlier */
882                 if ((NT_SUCCESS(Status)) &&
883                     ((RtlEqualUnicodeString(&SystemPartition,
884                                             &LinkTarget,
885                                             TRUE)) ||
886                     ((RtlPrefixUnicodeString(&SystemPartition,
887                                              &LinkTarget,
888                                              TRUE)) &&
889                      (LinkTarget.Buffer[SystemPartition.Length / sizeof(WCHAR)] == L'\\'))))
890                 {
891                     /* All done */
892                     break;
893                 }
894             }
895         }
896 
897         /* Couldn't find it, try again */
898         Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory,
899                                         DirInfo,
900                                         sizeof(DirInfoBuffer),
901                                         TRUE,
902                                         FALSE,
903                                         &Context,
904                                         NULL);
905     } while (NT_SUCCESS(Status));
906     if (!NT_SUCCESS(Status))
907     {
908         DPRINT1("SMSS: Cannot find drive letter for system partition\n");
909         return;
910     }
911 
912     /* Open the setup key again, for full access this time */
913     RtlInitUnicodeString(&UnicodeString,
914                          L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\Setup");
915     InitializeObjectAttributes(&ObjectAttributes,
916                                &UnicodeString,
917                                OBJ_CASE_INSENSITIVE,
918                                NULL,
919                                NULL);
920     Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
921     if (!NT_SUCCESS(Status))
922     {
923         DPRINT1("SMSS: Cannot open software setup key for writing: 0x%x\n",
924                 Status);
925         return;
926     }
927 
928     /* Wrap up the end of the link buffer */
929     wcsncpy(LinkBuffer, DirInfo->Name.Buffer, 2);
930     LinkBuffer[2] = L'\\';
931     LinkBuffer[3] = L'\0';
932 
933     /* Now set this as the "BootDir" */
934     RtlInitUnicodeString(&UnicodeString, L"BootDir");
935     Status = NtSetValueKey(KeyHandle,
936                            &UnicodeString,
937                            0,
938                            REG_SZ,
939                            LinkBuffer,
940                            4 * sizeof(WCHAR));
941     if (!NT_SUCCESS(Status))
942     {
943         DPRINT1("SMSS: couldn't write BootDir value: 0x%x\n", Status);
944     }
945     NtClose(KeyHandle);
946 }
947 
948 NTSTATUS
949 NTAPI
950 SmpCreateSecurityDescriptors(IN BOOLEAN InitialCall)
951 {
952     NTSTATUS Status;
953     PSID WorldSid = NULL, AdminSid = NULL, SystemSid = NULL;
954     PSID RestrictedSid = NULL, OwnerSid = NULL;
955     SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
956     SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
957     SID_IDENTIFIER_AUTHORITY CreatorAuthority = {SECURITY_CREATOR_SID_AUTHORITY};
958     ULONG AclLength, SidLength;
959     PACL Acl;
960     PACE_HEADER Ace;
961     BOOLEAN ProtectionRequired = FALSE;
962 
963     /* Check if this is the first call */
964     if (InitialCall)
965     {
966         /* Create and set the primary descriptor */
967         SmpPrimarySecurityDescriptor = &SmpPrimarySDBody;
968         Status = RtlCreateSecurityDescriptor(SmpPrimarySecurityDescriptor,
969                                              SECURITY_DESCRIPTOR_REVISION);
970         ASSERT(NT_SUCCESS(Status));
971         Status = RtlSetDaclSecurityDescriptor(SmpPrimarySecurityDescriptor,
972                                               TRUE,
973                                               NULL,
974                                               FALSE);
975         ASSERT(NT_SUCCESS(Status));
976 
977         /* Create and set the liberal descriptor */
978         SmpLiberalSecurityDescriptor = &SmpLiberalSDBody;
979         Status = RtlCreateSecurityDescriptor(SmpLiberalSecurityDescriptor,
980                                              SECURITY_DESCRIPTOR_REVISION);
981         ASSERT(NT_SUCCESS(Status));
982         Status = RtlSetDaclSecurityDescriptor(SmpLiberalSecurityDescriptor,
983                                               TRUE,
984                                               NULL,
985                                               FALSE);
986         ASSERT(NT_SUCCESS(Status));
987 
988         /* Create and set the \KnownDlls descriptor */
989         SmpKnownDllsSecurityDescriptor = &SmpKnownDllsSDBody;
990         Status = RtlCreateSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
991                                              SECURITY_DESCRIPTOR_REVISION);
992         ASSERT(NT_SUCCESS(Status));
993         Status = RtlSetDaclSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
994                                               TRUE,
995                                               NULL,
996                                               FALSE);
997         ASSERT(NT_SUCCESS(Status));
998 
999         /* Create and Set the \ApiPort descriptor */
1000         SmpApiPortSecurityDescriptor = &SmpApiPortSDBody;
1001         Status = RtlCreateSecurityDescriptor(SmpApiPortSecurityDescriptor,
1002                                              SECURITY_DESCRIPTOR_REVISION);
1003         ASSERT(NT_SUCCESS(Status));
1004         Status = RtlSetDaclSecurityDescriptor(SmpApiPortSecurityDescriptor,
1005                                               TRUE,
1006                                               NULL,
1007                                               FALSE);
1008         ASSERT(NT_SUCCESS(Status));
1009     }
1010 
1011     /* Check if protection was requested in the registry (on by default) */
1012     if (SmpProtectionMode & 1) ProtectionRequired = TRUE;
1013 
1014     /* Exit if there's nothing to do */
1015     if (!(InitialCall || ProtectionRequired)) return STATUS_SUCCESS;
1016 
1017     /* Build the world SID */
1018     Status = RtlAllocateAndInitializeSid(&WorldAuthority, 1,
1019                                          SECURITY_WORLD_RID,
1020                                          0, 0, 0, 0, 0, 0, 0,
1021                                          &WorldSid);
1022     if (!NT_SUCCESS(Status))
1023     {
1024         WorldSid = NULL;
1025         goto Quickie;
1026     }
1027 
1028     /* Build the admin SID */
1029     Status = RtlAllocateAndInitializeSid(&NtAuthority, 2,
1030                                          SECURITY_BUILTIN_DOMAIN_RID,
1031                                          DOMAIN_ALIAS_RID_ADMINS,
1032                                          0, 0, 0, 0, 0, 0,
1033                                          &AdminSid);
1034     if (!NT_SUCCESS(Status))
1035     {
1036         AdminSid = NULL;
1037         goto Quickie;
1038     }
1039 
1040     /* Build the owner SID */
1041     Status = RtlAllocateAndInitializeSid(&CreatorAuthority, 1,
1042                                          SECURITY_CREATOR_OWNER_RID,
1043                                          0, 0, 0, 0, 0, 0, 0,
1044                                          &OwnerSid);
1045     if (!NT_SUCCESS(Status))
1046     {
1047         OwnerSid = NULL;
1048         goto Quickie;
1049     }
1050 
1051     /* Build the restricted SID */
1052     Status = RtlAllocateAndInitializeSid(&NtAuthority, 1,
1053                                          SECURITY_RESTRICTED_CODE_RID,
1054                                          0, 0, 0, 0, 0, 0, 0,
1055                                          &RestrictedSid);
1056     if (!NT_SUCCESS(Status))
1057     {
1058         RestrictedSid = NULL;
1059         goto Quickie;
1060     }
1061 
1062     /* Build the system SID */
1063     Status = RtlAllocateAndInitializeSid(&NtAuthority, 1,
1064                                          SECURITY_LOCAL_SYSTEM_RID,
1065                                          0, 0, 0, 0, 0, 0, 0,
1066                                          &SystemSid);
1067     if (!NT_SUCCESS(Status))
1068     {
1069         SystemSid = NULL;
1070         goto Quickie;
1071     }
1072 
1073     /* Now check if we're creating the core descriptors */
1074     if (!InitialCall)
1075     {
1076         /* We're skipping NextAcl so we have to do this here */
1077         SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(RestrictedSid) + RtlLengthSid(AdminSid);
1078         SidLength *= 2;
1079         goto NotInitial;
1080     }
1081 
1082     /* Allocate an ACL with two ACEs with two SIDs each */
1083     SidLength = RtlLengthSid(SystemSid) + RtlLengthSid(AdminSid);
1084     AclLength = sizeof(ACL) + 2 * sizeof(ACCESS_ALLOWED_ACE) + SidLength;
1085     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1086     if (!Acl) Status = STATUS_NO_MEMORY;
1087     if (!NT_SUCCESS(Status)) goto NextAcl;
1088 
1089     /* Now build the ACL and add the two ACEs */
1090     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1091     ASSERT(NT_SUCCESS(Status));
1092     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1093     ASSERT(NT_SUCCESS(Status));
1094     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, SystemSid);
1095     ASSERT(NT_SUCCESS(Status));
1096 
1097     /* Set this as the DACL */
1098     Status = RtlSetDaclSecurityDescriptor(SmpApiPortSecurityDescriptor,
1099                                           TRUE,
1100                                           Acl,
1101                                           FALSE);
1102     ASSERT(NT_SUCCESS(Status));
1103 
1104 NextAcl:
1105     /* Allocate an ACL with 6 ACEs, two ACEs per SID */
1106     SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(RestrictedSid) + RtlLengthSid(AdminSid);
1107     SidLength *= 2;
1108     AclLength = sizeof(ACL) + 6 * sizeof(ACCESS_ALLOWED_ACE) + SidLength;
1109     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1110     if (!Acl) Status = STATUS_NO_MEMORY;
1111     if (!NT_SUCCESS(Status)) goto NotInitial;
1112 
1113     /* Now build the ACL and add the six ACEs */
1114     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1115     ASSERT(NT_SUCCESS(Status));
1116     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE, WorldSid);
1117     ASSERT(NT_SUCCESS(Status));
1118     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE, RestrictedSid);
1119     ASSERT(NT_SUCCESS(Status));
1120     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1121     ASSERT(NT_SUCCESS(Status));
1122     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
1123     ASSERT(NT_SUCCESS(Status));
1124     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
1125     ASSERT(NT_SUCCESS(Status));
1126     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1127     ASSERT(NT_SUCCESS(Status));
1128 
1129     /* Now edit the last three ACEs and make them inheritable */
1130     Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
1131     ASSERT(NT_SUCCESS(Status));
1132     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1133     Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
1134     ASSERT(NT_SUCCESS(Status));
1135     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1136     Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
1137     ASSERT(NT_SUCCESS(Status));
1138     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1139 
1140     /* Set this as the DACL */
1141     Status = RtlSetDaclSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
1142                                           TRUE,
1143                                           Acl,
1144                                           FALSE);
1145     ASSERT(NT_SUCCESS(Status));
1146 
1147 NotInitial:
1148     /* The initial ACLs have been created, are we also protecting objects? */
1149     if (!ProtectionRequired) goto Quickie;
1150 
1151     /* Allocate an ACL with 7 ACEs, two ACEs per SID, and one final owner ACE */
1152     SidLength += RtlLengthSid(OwnerSid);
1153     AclLength = sizeof(ACL) + 7 * sizeof (ACCESS_ALLOWED_ACE) + 2 * SidLength;
1154     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1155     if (!Acl) Status = STATUS_NO_MEMORY;
1156     if (!NT_SUCCESS(Status)) goto Quickie;
1157 
1158     /* Build the ACL and add the seven ACEs */
1159     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1160     ASSERT(NT_SUCCESS(Status));
1161     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, WorldSid);
1162     ASSERT(NT_SUCCESS(Status));
1163     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, RestrictedSid);
1164     ASSERT(NT_SUCCESS(Status));
1165     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1166     ASSERT(NT_SUCCESS(Status));
1167     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, WorldSid);
1168     ASSERT(NT_SUCCESS(Status));
1169     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, RestrictedSid);
1170     ASSERT(NT_SUCCESS(Status));
1171     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1172     ASSERT(NT_SUCCESS(Status));
1173     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, OwnerSid);
1174     ASSERT(NT_SUCCESS(Status));
1175 
1176     /* Edit the last 4 ACEs to make then inheritable */
1177     Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
1178     ASSERT(NT_SUCCESS(Status));
1179     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1180     Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
1181     ASSERT(NT_SUCCESS(Status));
1182     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1183     Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
1184     ASSERT(NT_SUCCESS(Status));
1185     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1186     Status = RtlGetAce(Acl, 6, (PVOID)&Ace);
1187     ASSERT(NT_SUCCESS(Status));
1188     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1189 
1190     /* Set this as the DACL for the primary SD */
1191     Status = RtlSetDaclSecurityDescriptor(SmpPrimarySecurityDescriptor,
1192                                           TRUE,
1193                                           Acl,
1194                                           FALSE);
1195     ASSERT(NT_SUCCESS(Status));
1196 
1197     /* Allocate an ACL with 7 ACEs, two ACEs per SID, and one final owner ACE */
1198     AclLength = sizeof(ACL) + 7 * sizeof (ACCESS_ALLOWED_ACE) + 2 * SidLength;
1199     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1200     if (!Acl) Status = STATUS_NO_MEMORY;
1201     if (!NT_SUCCESS(Status)) goto Quickie;
1202 
1203     /* Build the ACL and add the seven ACEs */
1204     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1205     ASSERT(NT_SUCCESS(Status));
1206     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
1207     ASSERT(NT_SUCCESS(Status));
1208     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
1209     ASSERT(NT_SUCCESS(Status));
1210     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1211     ASSERT(NT_SUCCESS(Status));
1212     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
1213     ASSERT(NT_SUCCESS(Status));
1214     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
1215     ASSERT(NT_SUCCESS(Status));
1216     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1217     ASSERT(NT_SUCCESS(Status));
1218     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, OwnerSid);
1219     ASSERT(NT_SUCCESS(Status));
1220 
1221     /* Edit the last 4 ACEs to make then inheritable */
1222     Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
1223     ASSERT(NT_SUCCESS(Status));
1224     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1225     Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
1226     ASSERT(NT_SUCCESS(Status));
1227     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1228     Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
1229     ASSERT(NT_SUCCESS(Status));
1230     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1231     Status = RtlGetAce(Acl, 6, (PVOID)&Ace);
1232     ASSERT(NT_SUCCESS(Status));
1233     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1234 
1235     /* Now set this as the DACL for the liberal SD */
1236     Status = RtlSetDaclSecurityDescriptor(SmpLiberalSecurityDescriptor,
1237                                           TRUE,
1238                                           Acl,
1239                                           FALSE);
1240     ASSERT(NT_SUCCESS(Status));
1241 
1242 Quickie:
1243     /* Cleanup the SIDs */
1244     if (OwnerSid) RtlFreeHeap(RtlGetProcessHeap(), 0, OwnerSid);
1245     if (AdminSid) RtlFreeHeap(RtlGetProcessHeap(), 0, AdminSid);
1246     if (WorldSid) RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
1247     if (SystemSid) RtlFreeHeap(RtlGetProcessHeap(), 0, SystemSid);
1248     if (RestrictedSid) RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedSid);
1249     return Status;
1250 }
1251 
1252 NTSTATUS
1253 NTAPI
1254 SmpInitializeDosDevices(VOID)
1255 {
1256     NTSTATUS Status;
1257     PSMP_REGISTRY_VALUE RegEntry;
1258     SECURITY_DESCRIPTOR_CONTROL OldFlag = 0;
1259     OBJECT_ATTRIBUTES ObjectAttributes;
1260     UNICODE_STRING DestinationString;
1261     HANDLE DirHandle;
1262     PLIST_ENTRY NextEntry, Head;
1263 
1264     /* Open the GLOBAL?? directory */
1265     RtlInitUnicodeString(&DestinationString, L"\\??");
1266     InitializeObjectAttributes(&ObjectAttributes,
1267                                &DestinationString,
1268                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1269                                NULL,
1270                                NULL);
1271     Status = NtOpenDirectoryObject(&SmpDosDevicesObjectDirectory,
1272                                    DIRECTORY_ALL_ACCESS,
1273                                    &ObjectAttributes);
1274     if (!NT_SUCCESS(Status))
1275     {
1276         DPRINT1("SMSS: Unable to open %wZ directory - Status == %lx\n",
1277                 &DestinationString, Status);
1278         return Status;
1279     }
1280 
1281     /* Loop the DOS devices */
1282     Head = &SmpDosDevicesList;
1283     while (!IsListEmpty(Head))
1284     {
1285         /* Get the entry and remove it */
1286         NextEntry = RemoveHeadList(Head);
1287         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
1288 
1289         /* Initialize the attributes, and see which descriptor is being used */
1290         InitializeObjectAttributes(&ObjectAttributes,
1291                                    &RegEntry->Name,
1292                                    OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1293                                    SmpDosDevicesObjectDirectory,
1294                                    SmpPrimarySecurityDescriptor);
1295         if (SmpPrimarySecurityDescriptor)
1296         {
1297             /* Save the old flag and set it while we create this link */
1298             OldFlag = SmpPrimarySecurityDescriptor->Control;
1299             SmpPrimarySecurityDescriptor->Control |= SE_DACL_DEFAULTED;
1300         }
1301 
1302         /* Create the symbolic link */
1303         DPRINT("Creating symlink for %wZ to %wZ\n", &RegEntry->Name, &RegEntry->Value);
1304         Status = NtCreateSymbolicLinkObject(&DirHandle,
1305                                             SYMBOLIC_LINK_ALL_ACCESS,
1306                                             &ObjectAttributes,
1307                                             &RegEntry->Value);
1308         if (Status == STATUS_OBJECT_NAME_EXISTS)
1309         {
1310             /* Make it temporary and get rid of the handle */
1311             NtMakeTemporaryObject(DirHandle);
1312             NtClose(DirHandle);
1313 
1314             /* Treat this as success, and see if we got a name back */
1315             Status = STATUS_SUCCESS;
1316             if (RegEntry->Value.Length)
1317             {
1318                 /* Create it now with this name */
1319                 ObjectAttributes.Attributes &= ~OBJ_OPENIF;
1320                 Status = NtCreateSymbolicLinkObject(&DirHandle,
1321                                                     SYMBOLIC_LINK_ALL_ACCESS,
1322                                                     &ObjectAttributes,
1323                                                     &RegEntry->Value);
1324             }
1325         }
1326 
1327         /* If we were using a security descriptor, restore the non-defaulted flag */
1328         if (ObjectAttributes.SecurityDescriptor)
1329         {
1330             SmpPrimarySecurityDescriptor->Control = OldFlag;
1331         }
1332 
1333         /* Print a failure if we failed to create the symbolic link */
1334         if (!NT_SUCCESS(Status))
1335         {
1336             DPRINT1("SMSS: Unable to create %wZ => %wZ symbolic link object - Status == 0x%lx\n",
1337                     &RegEntry->Name,
1338                     &RegEntry->Value,
1339                     Status);
1340             break;
1341         }
1342 
1343         /* Close the handle */
1344         NtClose(DirHandle);
1345 
1346         /* Free this entry */
1347         if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
1348         if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
1349         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
1350     }
1351 
1352     /* Return the status */
1353     return Status;
1354 }
1355 
1356 VOID
1357 NTAPI
1358 SmpProcessModuleImports(IN PVOID Unused,
1359                         IN PCHAR ImportName)
1360 {
1361     ULONG Length = 0;
1362     WCHAR Buffer[MAX_PATH];
1363     PWCHAR DllName, DllValue;
1364     ANSI_STRING ImportString;
1365     UNICODE_STRING ImportUnicodeString;
1366     NTSTATUS Status;
1367 
1368     /* Skip NTDLL since it's already always mapped */
1369     if (!_stricmp(ImportName, "ntdll.dll")) return;
1370 
1371     /* Initialize our strings */
1372     RtlInitAnsiString(&ImportString, ImportName);
1373     RtlInitEmptyUnicodeString(&ImportUnicodeString, Buffer, sizeof(Buffer));
1374     Status = RtlAnsiStringToUnicodeString(&ImportUnicodeString, &ImportString, FALSE);
1375     if (!NT_SUCCESS(Status)) return;
1376 
1377     /* Loop to find the DLL file extension */
1378     while (Length < ImportUnicodeString.Length)
1379     {
1380         if (ImportUnicodeString.Buffer[Length / sizeof(WCHAR)] == L'.') break;
1381         Length += sizeof(WCHAR);
1382     }
1383 
1384     /*
1385      * Break up the values as needed; the buffer acquires the form:
1386      * "dll_name.dll\0dll_name\0"
1387      */
1388     DllValue = ImportUnicodeString.Buffer;
1389     DllName = &ImportUnicodeString.Buffer[(ImportUnicodeString.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR)];
1390     RtlStringCbCopyNW(DllName,
1391                       ImportUnicodeString.MaximumLength - (ImportUnicodeString.Length + sizeof(UNICODE_NULL)),
1392                       ImportUnicodeString.Buffer, Length);
1393 
1394     /* Add the DLL to the list */
1395     SmpSaveRegistryValue(&SmpKnownDllsList, DllName, DllValue, TRUE);
1396 }
1397 
1398 NTSTATUS
1399 NTAPI
1400 SmpInitializeKnownDllsInternal(IN PUNICODE_STRING Directory,
1401                                IN PUNICODE_STRING Path)
1402 {
1403     HANDLE DirFileHandle, DirHandle, SectionHandle, FileHandle, LinkHandle;
1404     UNICODE_STRING NtPath, DestinationString;
1405     OBJECT_ATTRIBUTES ObjectAttributes;
1406     NTSTATUS Status, Status1;
1407     PLIST_ENTRY NextEntry;
1408     PSMP_REGISTRY_VALUE RegEntry;
1409     ULONG_PTR ErrorParameters[3];
1410     UNICODE_STRING ErrorResponse;
1411     IO_STATUS_BLOCK IoStatusBlock;
1412     SECURITY_DESCRIPTOR_CONTROL OldFlag = 0;
1413     USHORT ImageCharacteristics;
1414 
1415     /* Initialize to NULL */
1416     DirFileHandle = NULL;
1417     DirHandle = NULL;
1418     NtPath.Buffer = NULL;
1419 
1420     /* Create the \KnownDLLs directory */
1421     InitializeObjectAttributes(&ObjectAttributes,
1422                                Directory,
1423                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1424                                NULL,
1425                                SmpKnownDllsSecurityDescriptor);
1426     Status = NtCreateDirectoryObject(&DirHandle,
1427                                      DIRECTORY_ALL_ACCESS,
1428                                      &ObjectAttributes);
1429     if (!NT_SUCCESS(Status))
1430     {
1431         /* Handle failure */
1432         DPRINT1("SMSS: Unable to create %wZ directory - Status == %lx\n",
1433                 Directory, Status);
1434         return Status;
1435     }
1436 
1437     /* Convert the path to native format */
1438     if (!RtlDosPathNameToNtPathName_U(Path->Buffer, &NtPath, NULL, NULL))
1439     {
1440         /* Fail if this didn't work */
1441         DPRINT1("SMSS: Unable to to convert %wZ to an Nt path\n", Path);
1442         Status = STATUS_OBJECT_NAME_INVALID;
1443         goto Quickie;
1444     }
1445 
1446     /* Open the path that was specified, which should be a directory */
1447     InitializeObjectAttributes(&ObjectAttributes,
1448                                &NtPath,
1449                                OBJ_CASE_INSENSITIVE,
1450                                NULL,
1451                                NULL);
1452     Status = NtOpenFile(&DirFileHandle,
1453                         FILE_LIST_DIRECTORY | SYNCHRONIZE,
1454                         &ObjectAttributes,
1455                         &IoStatusBlock,
1456                         FILE_SHARE_READ | FILE_SHARE_WRITE,
1457                         FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
1458     if (!NT_SUCCESS(Status))
1459     {
1460         /* Fail if we couldn't open it */
1461         DPRINT1("SMSS: Unable to open a handle to the KnownDll directory (%wZ)"
1462                 "- Status == %lx\n",
1463                 Path,
1464                 Status);
1465         FileHandle = NULL;
1466         goto Quickie;
1467     }
1468 
1469     /* Temporarily hack the SD to use a default DACL for this symbolic link */
1470     if (SmpPrimarySecurityDescriptor)
1471     {
1472         OldFlag = SmpPrimarySecurityDescriptor->Control;
1473         SmpPrimarySecurityDescriptor->Control |= SE_DACL_DEFAULTED;
1474     }
1475 
1476     /* Create a symbolic link to the directory in the object manager */
1477     RtlInitUnicodeString(&DestinationString, L"KnownDllPath");
1478     InitializeObjectAttributes(&ObjectAttributes,
1479                                &DestinationString,
1480                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1481                                DirHandle,
1482                                SmpPrimarySecurityDescriptor);
1483     Status = NtCreateSymbolicLinkObject(&LinkHandle,
1484                                         SYMBOLIC_LINK_ALL_ACCESS,
1485                                         &ObjectAttributes,
1486                                         Path);
1487 
1488     /* Undo the hack */
1489     if (SmpPrimarySecurityDescriptor) SmpPrimarySecurityDescriptor->Control = OldFlag;
1490 
1491     /* Check if the symlink was created */
1492     if (!NT_SUCCESS(Status))
1493     {
1494         /* It wasn't, so bail out since the OS needs it to exist */
1495         DPRINT1("SMSS: Unable to create %wZ symbolic link - Status == %lx\n",
1496                 &DestinationString, Status);
1497         LinkHandle = NULL;
1498         goto Quickie;
1499     }
1500 
1501     /* We created it permanent, we can go ahead and close the handle now */
1502     Status1 = NtClose(LinkHandle);
1503     ASSERT(NT_SUCCESS(Status1));
1504 
1505     /* Now loop the known DLLs */
1506     NextEntry = SmpKnownDllsList.Flink;
1507     while (NextEntry != &SmpKnownDllsList)
1508     {
1509         /* Get the entry and move on */
1510         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
1511         NextEntry = NextEntry->Flink;
1512 
1513         DPRINT("Processing known DLL: %wZ-%wZ\n", &RegEntry->Name, &RegEntry->Value);
1514 
1515         /* Skip the entry if it's in the excluded list */
1516         if ((SmpFindRegistryValue(&SmpExcludeKnownDllsList,
1517                                   RegEntry->Name.Buffer)) ||
1518             (SmpFindRegistryValue(&SmpExcludeKnownDllsList,
1519                                   RegEntry->Value.Buffer)))
1520         {
1521             continue;
1522         }
1523 
1524         /* Open the actual file */
1525         InitializeObjectAttributes(&ObjectAttributes,
1526                                    &RegEntry->Value,
1527                                    OBJ_CASE_INSENSITIVE,
1528                                    DirFileHandle,
1529                                    NULL);
1530         Status1 = NtOpenFile(&FileHandle,
1531                              SYNCHRONIZE | FILE_EXECUTE,
1532                              &ObjectAttributes,
1533                              &IoStatusBlock,
1534                              FILE_SHARE_READ | FILE_SHARE_DELETE,
1535                              FILE_NON_DIRECTORY_FILE |
1536                              FILE_SYNCHRONOUS_IO_NONALERT);
1537         /* If we failed, skip it */
1538         if (!NT_SUCCESS(Status1)) continue;
1539 
1540         /* Checksum it */
1541         Status = LdrVerifyImageMatchesChecksum((HANDLE)((ULONG_PTR)FileHandle | 1),
1542                                                SmpProcessModuleImports,
1543                                                RegEntry,
1544                                                &ImageCharacteristics);
1545         if (!NT_SUCCESS(Status))
1546         {
1547             /* Checksum failed, so don't even try going further -- kill SMSS */
1548             RtlInitUnicodeString(&ErrorResponse,
1549                                  L"Verification of a KnownDLL failed.");
1550             ErrorParameters[0] = (ULONG_PTR)&ErrorResponse;
1551             ErrorParameters[1] = Status;
1552             ErrorParameters[2] = (ULONG_PTR)&RegEntry->Value;
1553             SmpTerminate(ErrorParameters, 5, RTL_NUMBER_OF(ErrorParameters));
1554         }
1555         else if (!(ImageCharacteristics & IMAGE_FILE_DLL))
1556         {
1557             /* An invalid known DLL entry will also kill SMSS */
1558             RtlInitUnicodeString(&ErrorResponse,
1559                                  L"Non-DLL file included in KnownDLL list.");
1560             ErrorParameters[0] = (ULONG_PTR)&ErrorResponse;
1561             ErrorParameters[1] = STATUS_INVALID_IMPORT_OF_NON_DLL;
1562             ErrorParameters[2] = (ULONG_PTR)&RegEntry->Value;
1563             SmpTerminate(ErrorParameters, 5, RTL_NUMBER_OF(ErrorParameters));
1564         }
1565 
1566         /* Temporarily hack the SD to use a default DACL for this section */
1567         if (SmpLiberalSecurityDescriptor)
1568         {
1569             OldFlag = SmpLiberalSecurityDescriptor->Control;
1570             SmpLiberalSecurityDescriptor->Control |= SE_DACL_DEFAULTED;
1571         }
1572 
1573         /* Create the section for this known DLL */
1574         InitializeObjectAttributes(&ObjectAttributes,
1575                                    &RegEntry->Value,
1576                                    OBJ_PERMANENT,
1577                                    DirHandle,
1578                                    SmpLiberalSecurityDescriptor)
1579         Status = NtCreateSection(&SectionHandle,
1580                                  SECTION_ALL_ACCESS,
1581                                  &ObjectAttributes,
1582                                  0,
1583                                  PAGE_EXECUTE,
1584                                  SEC_IMAGE,
1585                                  FileHandle);
1586 
1587         /* Undo the hack */
1588         if (SmpLiberalSecurityDescriptor) SmpLiberalSecurityDescriptor->Control = OldFlag;
1589 
1590         /* Check if we created the section okay */
1591         if (NT_SUCCESS(Status))
1592         {
1593             /* We can close it now, since it's marked permanent */
1594             Status1 = NtClose(SectionHandle);
1595             ASSERT(NT_SUCCESS(Status1));
1596         }
1597         else
1598         {
1599             /* If we couldn't make it "known", that's fine and keep going */
1600             DPRINT1("SMSS: CreateSection for KnownDll %wZ failed - Status == %lx\n",
1601                     &RegEntry->Value, Status);
1602         }
1603 
1604         /* Close the file since we can move on to the next one */
1605         Status1 = NtClose(FileHandle);
1606         ASSERT(NT_SUCCESS(Status1));
1607     }
1608 
1609 Quickie:
1610     /* Close both handles and free the NT path buffer */
1611     if (DirHandle)
1612     {
1613         Status1 = NtClose(DirHandle);
1614         ASSERT(NT_SUCCESS(Status1));
1615     }
1616     if (DirFileHandle)
1617     {
1618         Status1 = NtClose(DirFileHandle);
1619         ASSERT(NT_SUCCESS(Status1));
1620     }
1621     if (NtPath.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, NtPath.Buffer);
1622     return Status;
1623 }
1624 
1625 NTSTATUS
1626 NTAPI
1627 SmpInitializeKnownDlls(VOID)
1628 {
1629     NTSTATUS Status;
1630     PSMP_REGISTRY_VALUE RegEntry;
1631     UNICODE_STRING DestinationString;
1632     PLIST_ENTRY Head, NextEntry;
1633 
1634     /* Call the internal function */
1635     RtlInitUnicodeString(&DestinationString, L"\\KnownDlls");
1636     Status = SmpInitializeKnownDllsInternal(&DestinationString, &SmpKnownDllPath);
1637 
1638     /* Wipe out the list regardless of success */
1639     Head = &SmpKnownDllsList;
1640     while (!IsListEmpty(Head))
1641     {
1642         /* Remove this entry */
1643         NextEntry = RemoveHeadList(Head);
1644 
1645         /* Free it */
1646         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
1647         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
1648         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
1649         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
1650     }
1651 
1652     /* All done */
1653     return Status;
1654 }
1655 
1656 NTSTATUS
1657 NTAPI
1658 SmpCreateDynamicEnvironmentVariables(VOID)
1659 {
1660     NTSTATUS Status;
1661     SYSTEM_BASIC_INFORMATION BasicInfo;
1662     SYSTEM_PROCESSOR_INFORMATION ProcessorInfo;
1663     OBJECT_ATTRIBUTES ObjectAttributes;
1664     UNICODE_STRING ValueName, DestinationString;
1665     HANDLE KeyHandle, KeyHandle2;
1666     PWCHAR ValueData;
1667     ULONG ResultLength;
1668     size_t StrLength;
1669     WCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512];
1670     WCHAR ValueBuffer2[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512];
1671     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
1672     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo2 = (PVOID)ValueBuffer2;
1673 
1674     /* Get system basic information -- we'll need the CPU count */
1675     Status = NtQuerySystemInformation(SystemBasicInformation,
1676                                       &BasicInfo,
1677                                       sizeof(BasicInfo),
1678                                       NULL);
1679     if (!NT_SUCCESS(Status))
1680     {
1681         /* Bail out on failure */
1682         DPRINT1("SMSS: Unable to query system basic information - %x\n", Status);
1683         return Status;
1684     }
1685 
1686     /* Get the processor information, we'll query a bunch of revision info */
1687     Status = NtQuerySystemInformation(SystemProcessorInformation,
1688                                       &ProcessorInfo,
1689                                       sizeof(ProcessorInfo),
1690                                       NULL);
1691     if (!NT_SUCCESS(Status))
1692     {
1693         /* Bail out on failure */
1694         DPRINT1("SMSS: Unable to query system processor information - %x\n", Status);
1695         return Status;
1696     }
1697 
1698     /* We'll be writing all these environment variables over here */
1699     RtlInitUnicodeString(&DestinationString,
1700                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
1701                          L"Control\\Session Manager\\Environment");
1702     InitializeObjectAttributes(&ObjectAttributes,
1703                                &DestinationString,
1704                                OBJ_CASE_INSENSITIVE,
1705                                NULL,
1706                                NULL);
1707     Status = NtOpenKey(&KeyHandle, GENERIC_WRITE, &ObjectAttributes);
1708     if (!NT_SUCCESS(Status))
1709     {
1710         /* Bail out on failure */
1711         DPRINT1("SMSS: Unable to open %wZ - %x\n", &DestinationString, Status);
1712         return Status;
1713     }
1714 
1715     /* First let's write the OS variable */
1716     RtlInitUnicodeString(&ValueName, L"OS");
1717     ValueData = L"Windows_NT";
1718     DPRINT("Setting %wZ to %S\n", &ValueName, ValueData);
1719     Status = NtSetValueKey(KeyHandle,
1720                            &ValueName,
1721                            0,
1722                            REG_SZ,
1723                            ValueData,
1724                            (wcslen(ValueData) + 1) * sizeof(WCHAR));
1725     if (!NT_SUCCESS(Status))
1726     {
1727         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1728                 &ValueName, Status);
1729         NtClose(KeyHandle);
1730         return Status;
1731     }
1732 
1733     /* Next, let's write the CPU architecture variable */
1734     RtlInitUnicodeString(&ValueName, L"PROCESSOR_ARCHITECTURE");
1735     switch (ProcessorInfo.ProcessorArchitecture)
1736     {
1737         /* Pick the correct string that matches the architecture */
1738         case PROCESSOR_ARCHITECTURE_INTEL:
1739             ValueData = L"x86";
1740             break;
1741 
1742         case PROCESSOR_ARCHITECTURE_AMD64:
1743             ValueData = L"AMD64";
1744             break;
1745 
1746         case PROCESSOR_ARCHITECTURE_IA64:
1747             ValueData = L"IA64";
1748             break;
1749 
1750         default:
1751             ValueData = L"Unknown";
1752             break;
1753     }
1754 
1755     /* Set it */
1756     DPRINT("Setting %wZ to %S\n", &ValueName, ValueData);
1757     Status = NtSetValueKey(KeyHandle,
1758                            &ValueName,
1759                            0,
1760                            REG_SZ,
1761                            ValueData,
1762                            (wcslen(ValueData) + 1) * sizeof(WCHAR));
1763     if (!NT_SUCCESS(Status))
1764     {
1765         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1766                 &ValueName, Status);
1767         NtClose(KeyHandle);
1768         return Status;
1769     }
1770 
1771     /* And now let's write the processor level */
1772     RtlInitUnicodeString(&ValueName, L"PROCESSOR_LEVEL");
1773     swprintf(ValueBuffer, L"%u", ProcessorInfo.ProcessorLevel);
1774     DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1775     Status = NtSetValueKey(KeyHandle,
1776                            &ValueName,
1777                            0,
1778                            REG_SZ,
1779                            ValueBuffer,
1780                            (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1781     if (!NT_SUCCESS(Status))
1782     {
1783         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1784                 &ValueName, Status);
1785         NtClose(KeyHandle);
1786         return Status;
1787     }
1788 
1789     /* Now open the hardware CPU key */
1790     RtlInitUnicodeString(&DestinationString,
1791                          L"\\Registry\\Machine\\Hardware\\Description\\System\\"
1792                          L"CentralProcessor\\0");
1793     InitializeObjectAttributes(&ObjectAttributes,
1794                                &DestinationString,
1795                                OBJ_CASE_INSENSITIVE,
1796                                NULL,
1797                                NULL);
1798     Status = NtOpenKey(&KeyHandle2, KEY_READ, &ObjectAttributes);
1799     if (!NT_SUCCESS(Status))
1800     {
1801         DPRINT1("SMSS: Unable to open %wZ - %x\n", &DestinationString, Status);
1802         NtClose(KeyHandle);
1803         return Status;
1804     }
1805 
1806     /* So that we can read the identifier out of it... */
1807     RtlInitUnicodeString(&ValueName, L"Identifier");
1808     Status = NtQueryValueKey(KeyHandle2,
1809                              &ValueName,
1810                              KeyValuePartialInformation,
1811                              PartialInfo,
1812                              sizeof(ValueBuffer),
1813                              &ResultLength);
1814     if (!NT_SUCCESS(Status) ||
1815         ((PartialInfo->Type != REG_SZ) && (PartialInfo->Type != REG_EXPAND_SZ)))
1816     {
1817         NtClose(KeyHandle2);
1818         NtClose(KeyHandle);
1819         DPRINT1("SMSS: Unable to read %wZ\\%wZ (Type %lu, Status 0x%x)\n",
1820                 &DestinationString, &ValueName, PartialInfo->Type, Status);
1821         return Status;
1822     }
1823 
1824     /* Initialize the string so that it can be large enough
1825      * to contain both the identifier and the vendor strings. */
1826     RtlInitEmptyUnicodeString(&DestinationString,
1827                               (PWCHAR)PartialInfo->Data,
1828                               sizeof(ValueBuffer) -
1829                                 FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
1830     RtlStringCbLengthW(DestinationString.Buffer,
1831                        PartialInfo->DataLength,
1832                        &StrLength);
1833     DestinationString.Length = (USHORT)StrLength;
1834 
1835     /* As well as the vendor... */
1836     RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
1837     Status = NtQueryValueKey(KeyHandle2,
1838                              &ValueName,
1839                              KeyValuePartialInformation,
1840                              PartialInfo2,
1841                              sizeof(ValueBuffer2),
1842                              &ResultLength);
1843     NtClose(KeyHandle2);
1844     if (NT_SUCCESS(Status) &&
1845         ((PartialInfo2->Type == REG_SZ) || (PartialInfo2->Type == REG_EXPAND_SZ)))
1846     {
1847         /* To combine it into a single string */
1848         RtlStringCbPrintfW(DestinationString.Buffer + DestinationString.Length / sizeof(WCHAR),
1849                            DestinationString.MaximumLength - DestinationString.Length,
1850                            L", %.*s",
1851                            PartialInfo2->DataLength / sizeof(WCHAR),
1852                            (PWCHAR)PartialInfo2->Data);
1853         DestinationString.Length = (USHORT)(wcslen(DestinationString.Buffer) * sizeof(WCHAR));
1854     }
1855 
1856     /* So that we can set this as the PROCESSOR_IDENTIFIER variable */
1857     RtlInitUnicodeString(&ValueName, L"PROCESSOR_IDENTIFIER");
1858     DPRINT("Setting %wZ to %wZ\n", &ValueName, &DestinationString);
1859     Status = NtSetValueKey(KeyHandle,
1860                            &ValueName,
1861                            0,
1862                            REG_SZ,
1863                            DestinationString.Buffer,
1864                            DestinationString.Length + sizeof(UNICODE_NULL));
1865     if (!NT_SUCCESS(Status))
1866     {
1867         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1868                 &ValueName, Status);
1869         NtClose(KeyHandle);
1870         return Status;
1871     }
1872 
1873     /* Now let's get the processor architecture */
1874     RtlInitUnicodeString(&ValueName, L"PROCESSOR_REVISION");
1875     switch (ProcessorInfo.ProcessorArchitecture)
1876     {
1877         /* Check if this is an older Intel CPU */
1878         case PROCESSOR_ARCHITECTURE_INTEL:
1879             if ((ProcessorInfo.ProcessorRevision >> 8) == 0xFF)
1880             {
1881                 /* These guys used a revision + stepping, so get the rev only */
1882                 swprintf(ValueBuffer, L"%02x", ProcessorInfo.ProcessorRevision & 0xFF);
1883                 _wcsupr(ValueBuffer);
1884                 break;
1885             }
1886 
1887         /* Modern Intel, as well as 64-bit CPUs use a revision without stepping */
1888         case PROCESSOR_ARCHITECTURE_IA64:
1889         case PROCESSOR_ARCHITECTURE_AMD64:
1890             swprintf(ValueBuffer, L"%04x", ProcessorInfo.ProcessorRevision);
1891             break;
1892 
1893         /* And anything else we'll just read the whole revision identifier */
1894         default:
1895             swprintf(ValueBuffer, L"%u", ProcessorInfo.ProcessorRevision);
1896             break;
1897     }
1898 
1899     /* Write the revision to the registry */
1900     DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1901     Status = NtSetValueKey(KeyHandle,
1902                            &ValueName,
1903                            0,
1904                            REG_SZ,
1905                            ValueBuffer,
1906                            (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1907     if (!NT_SUCCESS(Status))
1908     {
1909         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1910                 &ValueName, Status);
1911         NtClose(KeyHandle);
1912         return Status;
1913     }
1914 
1915     /* And finally, write the number of CPUs */
1916     RtlInitUnicodeString(&ValueName, L"NUMBER_OF_PROCESSORS");
1917     swprintf(ValueBuffer, L"%d", BasicInfo.NumberOfProcessors);
1918     DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1919     Status = NtSetValueKey(KeyHandle,
1920                            &ValueName,
1921                            0,
1922                            REG_SZ,
1923                            ValueBuffer,
1924                            (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1925     if (!NT_SUCCESS(Status))
1926     {
1927         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1928                 &ValueName, Status);
1929         NtClose(KeyHandle);
1930         return Status;
1931     }
1932 
1933     /* Now we need to write the safeboot option key in a different format */
1934     RtlInitUnicodeString(&DestinationString,
1935                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
1936                          L"Control\\Safeboot\\Option");
1937     InitializeObjectAttributes(&ObjectAttributes,
1938                                &DestinationString,
1939                                OBJ_CASE_INSENSITIVE,
1940                                NULL,
1941                                NULL);
1942     Status = NtOpenKey(&KeyHandle2, KEY_ALL_ACCESS, &ObjectAttributes);
1943     if (NT_SUCCESS(Status))
1944     {
1945         /* This was indeed a safeboot, so check what kind of safeboot it was */
1946         RtlInitUnicodeString(&ValueName, L"OptionValue");
1947         Status = NtQueryValueKey(KeyHandle2,
1948                                  &ValueName,
1949                                  KeyValuePartialInformation,
1950                                  PartialInfo,
1951                                  sizeof(ValueBuffer),
1952                                  &ResultLength);
1953         NtClose(KeyHandle2);
1954         if (NT_SUCCESS(Status) &&
1955             (PartialInfo->Type == REG_DWORD) &&
1956             (PartialInfo->DataLength >= sizeof(ULONG)))
1957         {
1958             /* Convert from the integer value to the correct specifier */
1959             RtlInitUnicodeString(&ValueName, L"SAFEBOOT_OPTION");
1960             switch (*(PULONG)PartialInfo->Data)
1961             {
1962                 case 1:
1963                     wcscpy(ValueBuffer, L"MINIMAL");
1964                     break;
1965                 case 2:
1966                     wcscpy(ValueBuffer, L"NETWORK");
1967                     break;
1968                 case 3:
1969                     wcscpy(ValueBuffer, L"DSREPAIR");
1970                     break;
1971             }
1972 
1973             /* And write it in the environment! */
1974             DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1975             Status = NtSetValueKey(KeyHandle,
1976                                    &ValueName,
1977                                    0,
1978                                    REG_SZ,
1979                                    ValueBuffer,
1980                                    (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1981             if (!NT_SUCCESS(Status))
1982             {
1983                 DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1984                         &ValueName, Status);
1985                 NtClose(KeyHandle);
1986                 return Status;
1987             }
1988         }
1989         else
1990         {
1991             DPRINT1("SMSS: Failed to query SAFEBOOT option (Type %lu, Status 0x%x)\n",
1992                     PartialInfo->Type, Status);
1993         }
1994     }
1995 
1996     /* We are all done now */
1997     NtClose(KeyHandle);
1998     return STATUS_SUCCESS;
1999 }
2000 
2001 NTSTATUS
2002 NTAPI
2003 SmpProcessFileRenames(VOID)
2004 {
2005     BOOLEAN OldState, HavePrivilege = FALSE;
2006     NTSTATUS Status;
2007     HANDLE FileHandle, OtherFileHandle;
2008     FILE_INFORMATION_CLASS InformationClass;
2009     OBJECT_ATTRIBUTES ObjectAttributes;
2010     IO_STATUS_BLOCK IoStatusBlock;
2011     UNICODE_STRING FileString;
2012     FILE_BASIC_INFORMATION BasicInfo;
2013     FILE_DISPOSITION_INFORMATION DeleteInformation;
2014     PFILE_RENAME_INFORMATION Buffer;
2015     PLIST_ENTRY Head, NextEntry;
2016     PSMP_REGISTRY_VALUE RegEntry;
2017     PWCHAR FileName;
2018     ULONG ValueLength, Length;
2019 
2020     /* Give us access to restore any files we want */
2021     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OldState);
2022     if (NT_SUCCESS(Status)) HavePrivilege = TRUE;
2023 
2024     // FIXME: Handle SFC-protected file renames!
2025     if (SmpAllowProtectedRenames)
2026         DPRINT1("SMSS: FIXME: Handle SFC-protected file renames!\n");
2027 
2028     /* Process pending files to rename */
2029     Head = &SmpFileRenameList;
2030     while (!IsListEmpty(Head))
2031     {
2032         /* Get this entry */
2033         NextEntry = RemoveHeadList(Head);
2034         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
2035         DPRINT("Processing PFRO: '%wZ' / '%wZ'\n", &RegEntry->Value, &RegEntry->Name);
2036 
2037         /* Skip past the '@' marker */
2038         if (!(RegEntry->Value.Length) && (*RegEntry->Name.Buffer == L'@'))
2039         {
2040             RegEntry->Name.Length -= sizeof(UNICODE_NULL);
2041             RegEntry->Name.Buffer++;
2042         }
2043 
2044         /* Open the file for delete access */
2045         InitializeObjectAttributes(&ObjectAttributes,
2046                                    &RegEntry->Name,
2047                                    OBJ_CASE_INSENSITIVE,
2048                                    NULL,
2049                                    NULL);
2050         Status = NtOpenFile(&OtherFileHandle,
2051                             DELETE | SYNCHRONIZE,
2052                             &ObjectAttributes,
2053                             &IoStatusBlock,
2054                             FILE_SHARE_READ | FILE_SHARE_WRITE,
2055                             FILE_SYNCHRONOUS_IO_NONALERT);
2056         if (!NT_SUCCESS(Status)) goto Quickie;
2057 
2058         /* Check if it's a rename or just a delete */
2059         ValueLength = RegEntry->Value.Length;
2060         if (!ValueLength)
2061         {
2062             /* Just a delete, set up the class, length and buffer */
2063             InformationClass = FileDispositionInformation;
2064             Length = sizeof(DeleteInformation);
2065             Buffer = (PFILE_RENAME_INFORMATION)&DeleteInformation;
2066 
2067             /* Set the delete disposition */
2068             DeleteInformation.DeleteFile = TRUE;
2069         }
2070         else
2071         {
2072             /* This is a rename, setup the class and length */
2073             InformationClass = FileRenameInformation;
2074             Length = ValueLength + sizeof(FILE_RENAME_INFORMATION);
2075 
2076             /* Skip past the special markers */
2077             FileName = RegEntry->Value.Buffer;
2078             if ((*FileName == L'!') || (*FileName == L'@'))
2079             {
2080                 FileName++;
2081                 Length -= sizeof(UNICODE_NULL);
2082             }
2083 
2084             /* Now allocate the buffer for the rename information */
2085             Buffer = RtlAllocateHeap(RtlGetProcessHeap(), SmBaseTag, Length);
2086             if (Buffer)
2087             {
2088                 /* Setup the buffer to point to the filename, and copy it */
2089                 Buffer->RootDirectory = NULL;
2090                 Buffer->FileNameLength = Length - sizeof(FILE_RENAME_INFORMATION);
2091                 Buffer->ReplaceIfExists = FileName != RegEntry->Value.Buffer;
2092                 RtlCopyMemory(Buffer->FileName, FileName, Buffer->FileNameLength);
2093             }
2094             else
2095             {
2096                 /* Fail */
2097                 Status = STATUS_NO_MEMORY;
2098             }
2099         }
2100 
2101         /* Check if everything is okay till here */
2102         if (NT_SUCCESS(Status))
2103         {
2104             /* Now either rename or delete the file as requested */
2105             Status = NtSetInformationFile(OtherFileHandle,
2106                                           &IoStatusBlock,
2107                                           Buffer,
2108                                           Length,
2109                                           InformationClass);
2110 
2111             /* Check if we seem to have failed because the file was readonly */
2112             if (!NT_SUCCESS(Status) &&
2113                 (InformationClass == FileRenameInformation) &&
2114                 (Status == STATUS_OBJECT_NAME_COLLISION) &&
2115                 Buffer->ReplaceIfExists)
2116             {
2117                 /* Open the file for write attribute access this time... */
2118                 DPRINT("\nSMSS: '%wZ' => '%wZ' failed - Status == %x, Possible readonly target\n",
2119                         &RegEntry->Name,
2120                         &RegEntry->Value,
2121                         STATUS_OBJECT_NAME_COLLISION);
2122                 FileString.Length = RegEntry->Value.Length - sizeof(WCHAR);
2123                 FileString.MaximumLength = RegEntry->Value.MaximumLength - sizeof(WCHAR);
2124                 FileString.Buffer = FileName;
2125                 InitializeObjectAttributes(&ObjectAttributes,
2126                                            &FileString,
2127                                            OBJ_CASE_INSENSITIVE,
2128                                            NULL,
2129                                            NULL);
2130                 Status = NtOpenFile(&FileHandle,
2131                                     FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
2132                                     &ObjectAttributes,
2133                                     &IoStatusBlock,
2134                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
2135                                     FILE_SYNCHRONOUS_IO_NONALERT);
2136                 if (!NT_SUCCESS(Status))
2137                 {
2138                     /* That didn't work, so bail out */
2139                     DPRINT1("     SMSS: Open Existing file Failed - Status == %x\n",
2140                             Status);
2141                 }
2142                 else
2143                 {
2144                     /* Now remove the read-only attribute from the file */
2145                     DPRINT("     SMSS: Open Existing Success\n");
2146                     RtlZeroMemory(&BasicInfo, sizeof(BasicInfo));
2147                     BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2148                     Status = NtSetInformationFile(FileHandle,
2149                                                   &IoStatusBlock,
2150                                                   &BasicInfo,
2151                                                   sizeof(BasicInfo),
2152                                                   FileBasicInformation);
2153                     NtClose(FileHandle);
2154                     if (!NT_SUCCESS(Status))
2155                     {
2156                         /* That didn't work, bail out */
2157                         DPRINT1("     SMSS: Set To NORMAL Failed - Status == %x\n",
2158                                 Status);
2159                     }
2160                     else
2161                     {
2162                         /* Now that the file is no longer read-only, delete! */
2163                         DPRINT("     SMSS: Set To NORMAL OK\n");
2164                         Status = NtSetInformationFile(OtherFileHandle,
2165                                                       &IoStatusBlock,
2166                                                       Buffer,
2167                                                       Length,
2168                                                       FileRenameInformation);
2169                         if (!NT_SUCCESS(Status))
2170                         {
2171                             /* That failed too! */
2172                             DPRINT1("     SMSS: Re-Rename Failed - Status == %x\n",
2173                                     Status);
2174                         }
2175                         else
2176                         {
2177                             /* Everything ok */
2178                             DPRINT("     SMSS: Re-Rename Worked OK\n");
2179                         }
2180                     }
2181                 }
2182             }
2183         }
2184 
2185         /* Close the file handle and check the operation result */
2186         NtClose(OtherFileHandle);
2187 Quickie:
2188         if (!NT_SUCCESS(Status))
2189         {
2190             /* We totally failed */
2191             DPRINT1("SMSS: '%wZ' => '%wZ' failed - Status == %x\n",
2192                     &RegEntry->Name, &RegEntry->Value, Status);
2193         }
2194         else if (RegEntry->Value.Length)
2195         {
2196             /* We succeed with a rename */
2197             DPRINT("SMSS: '%wZ' (renamed to) '%wZ'\n", &RegEntry->Name, &RegEntry->Value);
2198         }
2199         else
2200         {
2201             /* We succeeded with a delete */
2202             DPRINT("SMSS: '%wZ' (deleted)\n", &RegEntry->Name);
2203         }
2204 
2205         /* Now free this entry and keep going */
2206         if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
2207         if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
2208         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
2209     }
2210 
2211     /* Put back the restore privilege if we had requested it, and return */
2212     if (HavePrivilege) RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, FALSE, FALSE, &OldState);
2213     return Status;
2214 }
2215 
2216 NTSTATUS
2217 NTAPI
2218 SmpLoadDataFromRegistry(OUT PUNICODE_STRING InitialCommand)
2219 {
2220     NTSTATUS Status;
2221     PLIST_ENTRY Head, NextEntry;
2222     PSMP_REGISTRY_VALUE RegEntry;
2223     PVOID OriginalEnvironment;
2224     ULONG MuSessionId = 0;
2225     OBJECT_ATTRIBUTES ObjectAttributes;
2226     HANDLE KeyHandle;
2227     UNICODE_STRING DestinationString;
2228 
2229     /* Initialize the keywords we'll be looking for */
2230     RtlInitUnicodeString(&SmpDebugKeyword, L"debug");
2231     RtlInitUnicodeString(&SmpASyncKeyword, L"async");
2232     RtlInitUnicodeString(&SmpAutoChkKeyword, L"autocheck");
2233 
2234     /* Initialize all the registry-associated list heads */
2235     InitializeListHead(&SmpBootExecuteList);
2236     InitializeListHead(&SmpSetupExecuteList);
2237     InitializeListHead(&SmpPagingFileList);
2238     InitializeListHead(&SmpDosDevicesList);
2239     InitializeListHead(&SmpFileRenameList);
2240     InitializeListHead(&SmpKnownDllsList);
2241     InitializeListHead(&SmpExcludeKnownDllsList);
2242     InitializeListHead(&SmpSubSystemList);
2243     InitializeListHead(&SmpSubSystemsToLoad);
2244     InitializeListHead(&SmpSubSystemsToDefer);
2245     InitializeListHead(&SmpExecuteList);
2246     SmpPagingFileInitialize();
2247 
2248     /* Initialize the SMSS environment */
2249     Status = RtlCreateEnvironment(TRUE, &SmpDefaultEnvironment);
2250     if (!NT_SUCCESS(Status))
2251     {
2252         /* Fail if there was a problem */
2253         DPRINT1("SMSS: Unable to allocate default environment - Status == %X\n",
2254                 Status);
2255         SMSS_CHECKPOINT(RtlCreateEnvironment, Status);
2256         return Status;
2257     }
2258 
2259     /* Check if we were booted in PE mode (LiveCD should have this) */
2260     RtlInitUnicodeString(&DestinationString,
2261                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
2262                          L"Control\\MiniNT");
2263     InitializeObjectAttributes(&ObjectAttributes,
2264                                &DestinationString,
2265                                OBJ_CASE_INSENSITIVE,
2266                                NULL,
2267                                NULL);
2268     Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
2269     if (NT_SUCCESS(Status))
2270     {
2271         /* If the key exists, we were */
2272         NtClose(KeyHandle);
2273         MiniNTBoot = TRUE;
2274     }
2275 
2276     /* Print out if this is the case */
2277     if (MiniNTBoot) DPRINT("SMSS: !!! MiniNT Boot !!!\n");
2278 
2279     /* Open the environment key to see if we are booted in safe mode */
2280     RtlInitUnicodeString(&DestinationString,
2281                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
2282                          L"Control\\Session Manager\\Environment");
2283     InitializeObjectAttributes(&ObjectAttributes,
2284                                &DestinationString,
2285                                OBJ_CASE_INSENSITIVE,
2286                                NULL,
2287                                NULL);
2288     Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
2289     if (NT_SUCCESS(Status))
2290     {
2291         /* Delete the value if we found it */
2292         RtlInitUnicodeString(&DestinationString, L"SAFEBOOT_OPTION");
2293         NtDeleteValueKey(KeyHandle, &DestinationString);
2294         NtClose(KeyHandle);
2295     }
2296 
2297     /* Switch environments, then query the registry for all needed settings */
2298     OriginalEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
2299     NtCurrentPeb()->ProcessParameters->Environment = SmpDefaultEnvironment;
2300     Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
2301                                     L"Session Manager",
2302                                     SmpRegistryConfigurationTable,
2303                                     NULL,
2304                                     NULL);
2305     SmpDefaultEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
2306     NtCurrentPeb()->ProcessParameters->Environment = OriginalEnvironment;
2307     if (!NT_SUCCESS(Status))
2308     {
2309         /* We failed somewhere in registry initialization, which is bad... */
2310         DPRINT1("SMSS: RtlQueryRegistryValues failed - Status == %lx\n", Status);
2311         SMSS_CHECKPOINT(RtlQueryRegistryValues, Status);
2312         return Status;
2313     }
2314 
2315     /* Now we can start acting on the registry settings. First to DOS devices */
2316     Status = SmpInitializeDosDevices();
2317     if (!NT_SUCCESS(Status))
2318     {
2319         /* Failed */
2320         DPRINT1("SMSS: Unable to initialize DosDevices configuration - Status == %lx\n",
2321                 Status);
2322         SMSS_CHECKPOINT(SmpInitializeDosDevices, Status);
2323         return Status;
2324     }
2325 
2326     /* Next create the session directory... */
2327     RtlInitUnicodeString(&DestinationString, L"\\Sessions");
2328     InitializeObjectAttributes(&ObjectAttributes,
2329                                &DestinationString,
2330                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
2331                                NULL,
2332                                SmpPrimarySecurityDescriptor);
2333     Status = NtCreateDirectoryObject(&SmpSessionsObjectDirectory,
2334                                      DIRECTORY_ALL_ACCESS,
2335                                      &ObjectAttributes);
2336     if (!NT_SUCCESS(Status))
2337     {
2338         /* Fail */
2339         DPRINT1("SMSS: Unable to create %wZ object directory - Status == %lx\n",
2340                 &DestinationString, Status);
2341         SMSS_CHECKPOINT(NtCreateDirectoryObject, Status);
2342         return Status;
2343     }
2344 
2345     /* Next loop all the boot execute binaries */
2346     Head = &SmpBootExecuteList;
2347     while (!IsListEmpty(Head))
2348     {
2349         /* Remove each one from the list */
2350         NextEntry = RemoveHeadList(Head);
2351 
2352         /* Execute it */
2353         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
2354         SmpExecuteCommand(&RegEntry->Name, 0, NULL, 0);
2355 
2356         /* And free it */
2357         if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
2358         if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
2359         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
2360     }
2361 
2362     /* Now do any pending file rename operations... */
2363     if (!MiniNTBoot) SmpProcessFileRenames();
2364 
2365     /* And initialize known DLLs... */
2366     Status = SmpInitializeKnownDlls();
2367     if (!NT_SUCCESS(Status))
2368     {
2369         /* Fail if that didn't work */
2370         DPRINT1("SMSS: Unable to initialize KnownDll configuration - Status == %lx\n",
2371                 Status);
2372         SMSS_CHECKPOINT(SmpInitializeKnownDlls, Status);
2373         return Status;
2374     }
2375 
2376     /* Create the needed page files */
2377     if (!MiniNTBoot)
2378     {
2379         /* Loop every page file */
2380         Head = &SmpPagingFileList;
2381         while (!IsListEmpty(Head))
2382         {
2383             /* Remove each one from the list */
2384             NextEntry = RemoveHeadList(Head);
2385 
2386             /* Create the descriptor for it */
2387             RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
2388             SmpCreatePagingFileDescriptor(&RegEntry->Name);
2389 
2390             /* And free it */
2391             if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
2392             if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
2393             RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
2394         }
2395 
2396         /* Now create all the paging files for the descriptors that we have */
2397         SmpCreatePagingFiles();
2398     }
2399 
2400     /* Tell Cm it's now safe to fully enable write access to the registry */
2401     NtInitializeRegistry(CM_BOOT_FLAG_SMSS);
2402 
2403     /* Create all the system-based environment variables for later inheriting */
2404     Status = SmpCreateDynamicEnvironmentVariables();
2405     if (!NT_SUCCESS(Status))
2406     {
2407         /* Handle failure */
2408         SMSS_CHECKPOINT(SmpCreateDynamicEnvironmentVariables, Status);
2409         return Status;
2410     }
2411 
2412     /* And finally load all the subsystems for our first session! */
2413     Status = SmpLoadSubSystemsForMuSession(&MuSessionId,
2414                                            &SmpWindowsSubSysProcessId,
2415                                            InitialCommand);
2416     ASSERT(MuSessionId == 0);
2417     if (!NT_SUCCESS(Status)) SMSS_CHECKPOINT(SmpLoadSubSystemsForMuSession, Status);
2418     return Status;
2419 }
2420 
2421 NTSTATUS
2422 NTAPI
2423 SmpInit(IN PUNICODE_STRING InitialCommand,
2424         OUT PHANDLE ProcessHandle)
2425 {
2426     NTSTATUS Status, Status2;
2427     OBJECT_ATTRIBUTES ObjectAttributes;
2428     UNICODE_STRING PortName, EventName;
2429     HANDLE EventHandle, PortHandle;
2430     ULONG HardErrorMode;
2431 
2432     /* Create the SMSS Heap */
2433     SmBaseTag = RtlCreateTagHeap(RtlGetProcessHeap(),
2434                                  0,
2435                                  L"SMSS!",
2436                                  L"INIT");
2437     SmpHeap = RtlGetProcessHeap();
2438 
2439     /* Enable hard errors */
2440     HardErrorMode = TRUE;
2441     NtSetInformationProcess(NtCurrentProcess(),
2442                             ProcessDefaultHardErrorMode,
2443                             &HardErrorMode,
2444                             sizeof(HardErrorMode));
2445 
2446     /* Initialize the subsystem list and the session list, plus their locks */
2447     RtlInitializeCriticalSection(&SmpKnownSubSysLock);
2448     InitializeListHead(&SmpKnownSubSysHead);
2449     RtlInitializeCriticalSection(&SmpSessionListLock);
2450     InitializeListHead(&SmpSessionListHead);
2451 
2452     /* Initialize the process list */
2453     InitializeListHead(&NativeProcessList);
2454 
2455     /* Initialize session parameters */
2456     SmpNextSessionId = 1;
2457     SmpNextSessionIdScanMode = 0;
2458     SmpDbgSsLoaded = FALSE;
2459 
2460     /* Create the initial security descriptors */
2461     Status = SmpCreateSecurityDescriptors(TRUE);
2462     if (!NT_SUCCESS(Status))
2463     {
2464         /* Fail */
2465         SMSS_CHECKPOINT(SmpCreateSecurityDescriptors, Status);
2466         return Status;
2467     }
2468 
2469     /* Initialize subsystem names */
2470     RtlInitUnicodeString(&SmpSubsystemName, L"NT-Session Manager");
2471     RtlInitUnicodeString(&PosixName, L"POSIX");
2472     RtlInitUnicodeString(&Os2Name, L"OS2");
2473 
2474     /* Create the SM API Port */
2475     RtlInitUnicodeString(&PortName, L"\\SmApiPort");
2476     InitializeObjectAttributes(&ObjectAttributes, &PortName, 0, NULL, SmpApiPortSecurityDescriptor);
2477     Status = NtCreatePort(&PortHandle,
2478                           &ObjectAttributes,
2479                           sizeof(SB_CONNECTION_INFO),
2480                           sizeof(SM_API_MSG),
2481                           sizeof(SB_API_MSG) * 32);
2482     ASSERT(NT_SUCCESS(Status));
2483     SmpDebugPort = PortHandle;
2484 
2485     /* Create two SM API threads */
2486     Status = RtlCreateUserThread(NtCurrentProcess(),
2487                                  NULL,
2488                                  FALSE,
2489                                  0,
2490                                  0,
2491                                  0,
2492                                  SmpApiLoop,
2493                                  PortHandle,
2494                                  NULL,
2495                                  NULL);
2496     ASSERT(NT_SUCCESS(Status));
2497     Status = RtlCreateUserThread(NtCurrentProcess(),
2498                                  NULL,
2499                                  FALSE,
2500                                  0,
2501                                  0,
2502                                  0,
2503                                  SmpApiLoop,
2504                                  PortHandle,
2505                                  NULL,
2506                                  NULL);
2507     ASSERT(NT_SUCCESS(Status));
2508 
2509     /* Create the write event that autochk can set after running */
2510     RtlInitUnicodeString(&EventName, L"\\Device\\VolumesSafeForWriteAccess");
2511     InitializeObjectAttributes(&ObjectAttributes,
2512                                &EventName,
2513                                OBJ_PERMANENT,
2514                                NULL,
2515                                NULL);
2516     Status2 = NtCreateEvent(&EventHandle,
2517                             EVENT_ALL_ACCESS,
2518                             &ObjectAttributes,
2519                             0,
2520                             0);
2521     if (!NT_SUCCESS(Status2))
2522     {
2523         /* Should never really fail */
2524         DPRINT1("SMSS: Unable to create %wZ event - Status == %lx\n",
2525                 &EventName, Status2);
2526         ASSERT(NT_SUCCESS(Status2));
2527     }
2528 
2529     /* Now initialize everything else based on the registry parameters */
2530     Status = SmpLoadDataFromRegistry(InitialCommand);
2531     if (NT_SUCCESS(Status))
2532     {
2533         /* Autochk should've run now. Set the event and save the CSRSS handle */
2534         *ProcessHandle = SmpWindowsSubSysProcess;
2535         NtSetEvent(EventHandle, 0);
2536         NtClose(EventHandle);
2537     }
2538 
2539     /* All done */
2540     return Status;
2541 }
2542