xref: /reactos/base/system/smss/sminit.c (revision 31019e8c)
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     CHAR ValueBuffer[512 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
793     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
794     ULONG Length, Context;
795     CHAR DirInfoBuffer[512 + sizeof(OBJECT_DIRECTORY_INFORMATION)];
796     POBJECT_DIRECTORY_INFORMATION DirInfo = (PVOID)DirInfoBuffer;
797     WCHAR LinkBuffer[MAX_PATH];
798 
799     /* Open the setup key */
800     RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\Setup");
801     InitializeObjectAttributes(&ObjectAttributes,
802                                &UnicodeString,
803                                OBJ_CASE_INSENSITIVE,
804                                NULL,
805                                NULL);
806     Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
807     if (!NT_SUCCESS(Status))
808     {
809         DPRINT1("SMSS: can't open system setup key for reading: 0x%x\n", Status);
810         return;
811     }
812 
813     /* Query the system partition */
814     RtlInitUnicodeString(&UnicodeString, L"SystemPartition");
815     Status = NtQueryValueKey(KeyHandle,
816                              &UnicodeString,
817                              KeyValuePartialInformation,
818                              PartialInfo,
819                              sizeof(ValueBuffer),
820                              &Length);
821     NtClose(KeyHandle);
822     if (!NT_SUCCESS(Status))
823     {
824         DPRINT1("SMSS: can't query SystemPartition value: 0x%x\n", Status);
825         return;
826     }
827 
828     /* Initialize the system partition string string */
829     RtlInitUnicodeString(&SystemPartition, (PWCHAR)PartialInfo->Data);
830 
831     /* Enumerate the directory looking for the symbolic link string */
832     RtlInitUnicodeString(&SearchString, L"SymbolicLink");
833     RtlInitEmptyUnicodeString(&LinkTarget, LinkBuffer, sizeof(LinkBuffer));
834     Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory,
835                                     DirInfo,
836                                     sizeof(DirInfoBuffer),
837                                     TRUE,
838                                     TRUE,
839                                     &Context,
840                                     NULL);
841     if (!NT_SUCCESS(Status))
842     {
843         DPRINT1("SMSS: can't find drive letter for system partition\n");
844         return;
845     }
846 
847     /* Keep searching until we find it */
848     do
849     {
850         /* Is this it? */
851         if ((RtlEqualUnicodeString(&DirInfo->TypeName, &SearchString, TRUE)) &&
852             (DirInfo->Name.Length == 2 * sizeof(WCHAR)) &&
853             (DirInfo->Name.Buffer[1] == L':'))
854         {
855             /* Looks like we found it, open the link to get its target */
856             InitializeObjectAttributes(&ObjectAttributes,
857                                        &DirInfo->Name,
858                                        OBJ_CASE_INSENSITIVE,
859                                        SmpDosDevicesObjectDirectory,
860                                        NULL);
861             Status = NtOpenSymbolicLinkObject(&LinkHandle,
862                                               SYMBOLIC_LINK_ALL_ACCESS,
863                                               &ObjectAttributes);
864             if (NT_SUCCESS(Status))
865             {
866                 /* Open worked, query the target now */
867                 Status = NtQuerySymbolicLinkObject(LinkHandle,
868                                                    &LinkTarget,
869                                                    NULL);
870                 NtClose(LinkHandle);
871 
872                 /* Check if it matches the string we had found earlier */
873                 if ((NT_SUCCESS(Status)) &&
874                     ((RtlEqualUnicodeString(&SystemPartition,
875                                            &LinkTarget,
876                                            TRUE)) ||
877                     ((RtlPrefixUnicodeString(&SystemPartition,
878                                              &LinkTarget,
879                                              TRUE)) &&
880                      (LinkTarget.Buffer[SystemPartition.Length / sizeof(WCHAR)] == L'\\'))))
881                 {
882                     /* All done */
883                     break;
884                 }
885             }
886         }
887 
888         /* Couldn't find it, try again */
889         Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory,
890                                         DirInfo,
891                                         sizeof(DirInfoBuffer),
892                                         TRUE,
893                                         FALSE,
894                                         &Context,
895                                         NULL);
896     } while (NT_SUCCESS(Status));
897     if (!NT_SUCCESS(Status))
898     {
899         DPRINT1("SMSS: can't find drive letter for system partition\n");
900         return;
901     }
902 
903     /* Open the setup key again, for full access this time */
904     RtlInitUnicodeString(&UnicodeString,
905                          L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\Setup");
906     InitializeObjectAttributes(&ObjectAttributes,
907                                &UnicodeString,
908                                OBJ_CASE_INSENSITIVE,
909                                NULL,
910                                NULL);
911     Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
912     if (!NT_SUCCESS(Status))
913     {
914         DPRINT1("SMSS: can't open software setup key for writing: 0x%x\n",
915                 Status);
916         return;
917     }
918 
919     /* Wrap up the end of the link buffer */
920     wcsncpy(LinkBuffer, DirInfo->Name.Buffer, 2);
921     LinkBuffer[2] = L'\\';
922     LinkBuffer[3] = L'\0';
923 
924     /* Now set this as the "BootDir" */
925     RtlInitUnicodeString(&UnicodeString, L"BootDir");
926     Status = NtSetValueKey(KeyHandle,
927                            &UnicodeString,
928                            0,
929                            REG_SZ,
930                            LinkBuffer,
931                            4 * sizeof(WCHAR));
932     if (!NT_SUCCESS(Status))
933     {
934         DPRINT1("SMSS: couldn't write BootDir value: 0x%x\n", Status);
935     }
936     NtClose(KeyHandle);
937 }
938 
939 NTSTATUS
940 NTAPI
941 SmpCreateSecurityDescriptors(IN BOOLEAN InitialCall)
942 {
943     NTSTATUS Status;
944     PSID WorldSid = NULL, AdminSid = NULL, SystemSid = NULL;
945     PSID RestrictedSid = NULL, OwnerSid = NULL;
946     SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
947     SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
948     SID_IDENTIFIER_AUTHORITY CreatorAuthority = {SECURITY_CREATOR_SID_AUTHORITY};
949     ULONG AclLength, SidLength;
950     PACL Acl;
951     PACE_HEADER Ace;
952     BOOLEAN ProtectionRequired = FALSE;
953 
954     /* Check if this is the first call */
955     if (InitialCall)
956     {
957         /* Create and set the primary descriptor */
958         SmpPrimarySecurityDescriptor = &SmpPrimarySDBody;
959         Status = RtlCreateSecurityDescriptor(SmpPrimarySecurityDescriptor,
960                                              SECURITY_DESCRIPTOR_REVISION);
961         ASSERT(NT_SUCCESS(Status));
962         Status = RtlSetDaclSecurityDescriptor(SmpPrimarySecurityDescriptor,
963                                               TRUE,
964                                               NULL,
965                                               FALSE);
966         ASSERT(NT_SUCCESS(Status));
967 
968         /* Create and set the liberal descriptor */
969         SmpLiberalSecurityDescriptor = &SmpLiberalSDBody;
970         Status = RtlCreateSecurityDescriptor(SmpLiberalSecurityDescriptor,
971                                              SECURITY_DESCRIPTOR_REVISION);
972         ASSERT(NT_SUCCESS(Status));
973         Status = RtlSetDaclSecurityDescriptor(SmpLiberalSecurityDescriptor,
974                                               TRUE,
975                                               NULL,
976                                               FALSE);
977         ASSERT(NT_SUCCESS(Status));
978 
979         /* Create and set the \KnownDlls descriptor */
980         SmpKnownDllsSecurityDescriptor = &SmpKnownDllsSDBody;
981         Status = RtlCreateSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
982                                              SECURITY_DESCRIPTOR_REVISION);
983         ASSERT(NT_SUCCESS(Status));
984         Status = RtlSetDaclSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
985                                               TRUE,
986                                               NULL,
987                                               FALSE);
988         ASSERT(NT_SUCCESS(Status));
989 
990         /* Create and Set the \ApiPort descriptor */
991         SmpApiPortSecurityDescriptor = &SmpApiPortSDBody;
992         Status = RtlCreateSecurityDescriptor(SmpApiPortSecurityDescriptor,
993                                              SECURITY_DESCRIPTOR_REVISION);
994         ASSERT(NT_SUCCESS(Status));
995         Status = RtlSetDaclSecurityDescriptor(SmpApiPortSecurityDescriptor,
996                                               TRUE,
997                                               NULL,
998                                               FALSE);
999         ASSERT(NT_SUCCESS(Status));
1000     }
1001 
1002     /* Check if protection was requested in the registry (on by default) */
1003     if (SmpProtectionMode & 1) ProtectionRequired = TRUE;
1004 
1005     /* Exit if there's nothing to do */
1006     if (!(InitialCall || ProtectionRequired)) return STATUS_SUCCESS;
1007 
1008     /* Build the world SID */
1009     Status = RtlAllocateAndInitializeSid(&WorldAuthority, 1,
1010                                          SECURITY_WORLD_RID,
1011                                          0, 0, 0, 0, 0, 0, 0,
1012                                          &WorldSid);
1013     if (!NT_SUCCESS(Status))
1014     {
1015         WorldSid = NULL;
1016         goto Quickie;
1017     }
1018 
1019     /* Build the admin SID */
1020     Status = RtlAllocateAndInitializeSid(&NtAuthority, 2,
1021                                          SECURITY_BUILTIN_DOMAIN_RID,
1022                                          DOMAIN_ALIAS_RID_ADMINS,
1023                                          0, 0, 0, 0, 0, 0,
1024                                          &AdminSid);
1025     if (!NT_SUCCESS(Status))
1026     {
1027         AdminSid = NULL;
1028         goto Quickie;
1029     }
1030 
1031     /* Build the owner SID */
1032     Status = RtlAllocateAndInitializeSid(&CreatorAuthority, 1,
1033                                          SECURITY_CREATOR_OWNER_RID,
1034                                          0, 0, 0, 0, 0, 0, 0,
1035                                          &OwnerSid);
1036     if (!NT_SUCCESS(Status))
1037     {
1038         OwnerSid = NULL;
1039         goto Quickie;
1040     }
1041 
1042     /* Build the restricted SID */
1043     Status = RtlAllocateAndInitializeSid(&NtAuthority, 1,
1044                                          SECURITY_RESTRICTED_CODE_RID,
1045                                          0, 0, 0, 0, 0, 0, 0,
1046                                          &RestrictedSid);
1047     if (!NT_SUCCESS(Status))
1048     {
1049         RestrictedSid = NULL;
1050         goto Quickie;
1051     }
1052 
1053     /* Build the system SID */
1054     Status = RtlAllocateAndInitializeSid(&NtAuthority, 1,
1055                                          SECURITY_LOCAL_SYSTEM_RID,
1056                                          0, 0, 0, 0, 0, 0, 0,
1057                                          &SystemSid);
1058     if (!NT_SUCCESS(Status))
1059     {
1060         SystemSid = NULL;
1061         goto Quickie;
1062     }
1063 
1064     /* Now check if we're creating the core descriptors */
1065     if (!InitialCall)
1066     {
1067         /* We're skipping NextAcl so we have to do this here */
1068         SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(RestrictedSid) + RtlLengthSid(AdminSid);
1069         SidLength *= 2;
1070         goto NotInitial;
1071     }
1072 
1073     /* Allocate an ACL with two ACEs with two SIDs each */
1074     SidLength = RtlLengthSid(SystemSid) + RtlLengthSid(AdminSid);
1075     AclLength = sizeof(ACL) + 2 * sizeof(ACCESS_ALLOWED_ACE) + SidLength;
1076     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1077     if (!Acl) Status = STATUS_NO_MEMORY;
1078     if (!NT_SUCCESS(Status)) goto NextAcl;
1079 
1080     /* Now build the ACL and add the two ACEs */
1081     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1082     ASSERT(NT_SUCCESS(Status));
1083     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1084     ASSERT(NT_SUCCESS(Status));
1085     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, SystemSid);
1086     ASSERT(NT_SUCCESS(Status));
1087 
1088     /* Set this as the DACL */
1089     Status = RtlSetDaclSecurityDescriptor(SmpApiPortSecurityDescriptor,
1090                                           TRUE,
1091                                           Acl,
1092                                           FALSE);
1093     ASSERT(NT_SUCCESS(Status));
1094 
1095 NextAcl:
1096     /* Allocate an ACL with 6 ACEs, two ACEs per SID */
1097     SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(RestrictedSid) + RtlLengthSid(AdminSid);
1098     SidLength *= 2;
1099     AclLength = sizeof(ACL) + 6 * sizeof(ACCESS_ALLOWED_ACE) + SidLength;
1100     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1101     if (!Acl) Status = STATUS_NO_MEMORY;
1102     if (!NT_SUCCESS(Status)) goto NotInitial;
1103 
1104     /* Now build the ACL and add the six ACEs */
1105     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1106     ASSERT(NT_SUCCESS(Status));
1107     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE, WorldSid);
1108     ASSERT(NT_SUCCESS(Status));
1109     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE, RestrictedSid);
1110     ASSERT(NT_SUCCESS(Status));
1111     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1112     ASSERT(NT_SUCCESS(Status));
1113     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
1114     ASSERT(NT_SUCCESS(Status));
1115     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
1116     ASSERT(NT_SUCCESS(Status));
1117     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1118     ASSERT(NT_SUCCESS(Status));
1119 
1120     /* Now edit the last three ACEs and make them inheritable */
1121     Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
1122     ASSERT(NT_SUCCESS(Status));
1123     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1124     Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
1125     ASSERT(NT_SUCCESS(Status));
1126     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1127     Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
1128     ASSERT(NT_SUCCESS(Status));
1129     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1130 
1131     /* Set this as the DACL */
1132     Status = RtlSetDaclSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
1133                                           TRUE,
1134                                           Acl,
1135                                           FALSE);
1136     ASSERT(NT_SUCCESS(Status));
1137 
1138 NotInitial:
1139     /* The initial ACLs have been created, are we also protecting objects? */
1140     if (!ProtectionRequired) goto Quickie;
1141 
1142     /* Allocate an ACL with 7 ACEs, two ACEs per SID, and one final owner ACE */
1143     SidLength += RtlLengthSid(OwnerSid);
1144     AclLength = sizeof(ACL) + 7 * sizeof (ACCESS_ALLOWED_ACE) + 2 * SidLength;
1145     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1146     if (!Acl) Status = STATUS_NO_MEMORY;
1147     if (!NT_SUCCESS(Status)) goto Quickie;
1148 
1149     /* Build the ACL and add the seven ACEs */
1150     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1151     ASSERT(NT_SUCCESS(Status));
1152     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, WorldSid);
1153     ASSERT(NT_SUCCESS(Status));
1154     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, RestrictedSid);
1155     ASSERT(NT_SUCCESS(Status));
1156     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1157     ASSERT(NT_SUCCESS(Status));
1158     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, WorldSid);
1159     ASSERT(NT_SUCCESS(Status));
1160     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, RestrictedSid);
1161     ASSERT(NT_SUCCESS(Status));
1162     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1163     ASSERT(NT_SUCCESS(Status));
1164     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, OwnerSid);
1165     ASSERT(NT_SUCCESS(Status));
1166 
1167     /* Edit the last 4 ACEs to make then inheritable */
1168     Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
1169     ASSERT(NT_SUCCESS(Status));
1170     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1171     Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
1172     ASSERT(NT_SUCCESS(Status));
1173     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1174     Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
1175     ASSERT(NT_SUCCESS(Status));
1176     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1177     Status = RtlGetAce(Acl, 6, (PVOID)&Ace);
1178     ASSERT(NT_SUCCESS(Status));
1179     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1180 
1181     /* Set this as the DACL for the primary SD */
1182     Status = RtlSetDaclSecurityDescriptor(SmpPrimarySecurityDescriptor,
1183                                           TRUE,
1184                                           Acl,
1185                                           FALSE);
1186     ASSERT(NT_SUCCESS(Status));
1187 
1188     /* Allocate an ACL with 7 ACEs, two ACEs per SID, and one final owner ACE */
1189     AclLength = sizeof(ACL) + 7 * sizeof (ACCESS_ALLOWED_ACE) + 2 * SidLength;
1190     Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
1191     if (!Acl) Status = STATUS_NO_MEMORY;
1192     if (!NT_SUCCESS(Status)) goto Quickie;
1193 
1194     /* Build the ACL and add the seven ACEs */
1195     Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
1196     ASSERT(NT_SUCCESS(Status));
1197     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
1198     ASSERT(NT_SUCCESS(Status));
1199     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
1200     ASSERT(NT_SUCCESS(Status));
1201     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1202     ASSERT(NT_SUCCESS(Status));
1203     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
1204     ASSERT(NT_SUCCESS(Status));
1205     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
1206     ASSERT(NT_SUCCESS(Status));
1207     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
1208     ASSERT(NT_SUCCESS(Status));
1209     Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, OwnerSid);
1210     ASSERT(NT_SUCCESS(Status));
1211 
1212     /* Edit the last 4 ACEs to make then inheritable */
1213     Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
1214     ASSERT(NT_SUCCESS(Status));
1215     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1216     Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
1217     ASSERT(NT_SUCCESS(Status));
1218     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1219     Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
1220     ASSERT(NT_SUCCESS(Status));
1221     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1222     Status = RtlGetAce(Acl, 6, (PVOID)&Ace);
1223     ASSERT(NT_SUCCESS(Status));
1224     Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
1225 
1226     /* Now set this as the DACL for the liberal SD */
1227     Status = RtlSetDaclSecurityDescriptor(SmpLiberalSecurityDescriptor,
1228                                           TRUE,
1229                                           Acl,
1230                                           FALSE);
1231     ASSERT(NT_SUCCESS(Status));
1232 
1233 Quickie:
1234     /* Cleanup the SIDs */
1235     if (OwnerSid) RtlFreeHeap(RtlGetProcessHeap(), 0, OwnerSid);
1236     if (AdminSid) RtlFreeHeap(RtlGetProcessHeap(), 0, AdminSid);
1237     if (WorldSid) RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
1238     if (SystemSid) RtlFreeHeap(RtlGetProcessHeap(), 0, SystemSid);
1239     if (RestrictedSid) RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedSid);
1240     return Status;
1241 }
1242 
1243 NTSTATUS
1244 NTAPI
1245 SmpInitializeDosDevices(VOID)
1246 {
1247     NTSTATUS Status;
1248     PSMP_REGISTRY_VALUE RegEntry;
1249     SECURITY_DESCRIPTOR_CONTROL OldFlag = 0;
1250     OBJECT_ATTRIBUTES ObjectAttributes;
1251     UNICODE_STRING DestinationString;
1252     HANDLE DirHandle;
1253     PLIST_ENTRY NextEntry, Head;
1254 
1255     /* Open the GLOBAL?? directory */
1256     RtlInitUnicodeString(&DestinationString, L"\\??");
1257     InitializeObjectAttributes(&ObjectAttributes,
1258                                &DestinationString,
1259                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1260                                NULL,
1261                                NULL);
1262     Status = NtOpenDirectoryObject(&SmpDosDevicesObjectDirectory,
1263                                    DIRECTORY_ALL_ACCESS,
1264                                    &ObjectAttributes);
1265     if (!NT_SUCCESS(Status))
1266     {
1267         DPRINT1("SMSS: Unable to open %wZ directory - Status == %lx\n",
1268                 &DestinationString, Status);
1269         return Status;
1270     }
1271 
1272     /* Loop the DOS devices */
1273     Head = &SmpDosDevicesList;
1274     while (!IsListEmpty(Head))
1275     {
1276         /* Get the entry and remove it */
1277         NextEntry = RemoveHeadList(Head);
1278         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
1279 
1280         /* Initialize the attributes, and see which descriptor is being used */
1281         InitializeObjectAttributes(&ObjectAttributes,
1282                                    &RegEntry->Name,
1283                                    OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1284                                    SmpDosDevicesObjectDirectory,
1285                                    SmpPrimarySecurityDescriptor);
1286         if (SmpPrimarySecurityDescriptor)
1287         {
1288             /* Save the old flag and set it while we create this link */
1289             OldFlag = SmpPrimarySecurityDescriptor->Control;
1290             SmpPrimarySecurityDescriptor->Control |= SE_DACL_DEFAULTED;
1291         }
1292 
1293         /* Create the symbolic link */
1294         DPRINT("Creating symlink for %wZ to %wZ\n", &RegEntry->Name, &RegEntry->Value);
1295         Status = NtCreateSymbolicLinkObject(&DirHandle,
1296                                             SYMBOLIC_LINK_ALL_ACCESS,
1297                                             &ObjectAttributes,
1298                                             &RegEntry->Value);
1299         if (Status == STATUS_OBJECT_NAME_EXISTS)
1300         {
1301             /* Make it temporary and get rid of the handle */
1302             NtMakeTemporaryObject(DirHandle);
1303             NtClose(DirHandle);
1304 
1305             /* Treat this as success, and see if we got a name back */
1306             Status = STATUS_SUCCESS;
1307             if (RegEntry->Value.Length)
1308             {
1309                 /* Create it now with this name */
1310                 ObjectAttributes.Attributes &= ~OBJ_OPENIF;
1311                 Status = NtCreateSymbolicLinkObject(&DirHandle,
1312                                                     SYMBOLIC_LINK_ALL_ACCESS,
1313                                                     &ObjectAttributes,
1314                                                     &RegEntry->Value);
1315             }
1316         }
1317 
1318         /* If we were using a security descriptor, restore the non-defaulted flag */
1319         if (ObjectAttributes.SecurityDescriptor)
1320         {
1321             SmpPrimarySecurityDescriptor->Control = OldFlag;
1322         }
1323 
1324         /* Print a failure if we failed to create the symbolic link */
1325         if (!NT_SUCCESS(Status))
1326         {
1327             DPRINT1("SMSS: Unable to create %wZ => %wZ symbolic link object - Status == 0x%lx\n",
1328                     &RegEntry->Name,
1329                     &RegEntry->Value,
1330                     Status);
1331             break;
1332         }
1333 
1334         /* Close the handle */
1335         NtClose(DirHandle);
1336 
1337         /* Free this entry */
1338         if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
1339         if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
1340         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
1341     }
1342 
1343     /* Return the status */
1344     return Status;
1345 }
1346 
1347 VOID
1348 NTAPI
1349 SmpProcessModuleImports(IN PVOID Unused,
1350                         IN PCHAR ImportName)
1351 {
1352     ULONG Length = 0, Chars;
1353     WCHAR Buffer[MAX_PATH];
1354     PWCHAR DllName, DllValue;
1355     ANSI_STRING ImportString;
1356     UNICODE_STRING ImportUnicodeString;
1357     NTSTATUS Status;
1358 
1359     /* Skip NTDLL since it's already always mapped */
1360     if (!_stricmp(ImportName, "ntdll.dll")) return;
1361 
1362     /* Initialize our strings */
1363     RtlInitAnsiString(&ImportString, ImportName);
1364     RtlInitEmptyUnicodeString(&ImportUnicodeString, Buffer, sizeof(Buffer));
1365     Status = RtlAnsiStringToUnicodeString(&ImportUnicodeString, &ImportString, FALSE);
1366     if (!NT_SUCCESS(Status)) return;
1367 
1368     /* Loop in case we find a forwarder */
1369     ImportUnicodeString.MaximumLength = ImportUnicodeString.Length + sizeof(UNICODE_NULL);
1370     while (Length < ImportUnicodeString.Length)
1371     {
1372         if (ImportUnicodeString.Buffer[Length / sizeof(WCHAR)] == L'.') break;
1373         Length += sizeof(WCHAR);
1374     }
1375 
1376     /* Break up the values as needed */
1377     DllValue = ImportUnicodeString.Buffer;
1378     DllName = &ImportUnicodeString.Buffer[ImportUnicodeString.MaximumLength / sizeof(WCHAR)];
1379     Chars = Length >> 1;
1380     wcsncpy(DllName, ImportUnicodeString.Buffer, Chars);
1381     DllName[Chars] = 0;
1382 
1383     /* Add the DLL to the list */
1384     SmpSaveRegistryValue(&SmpKnownDllsList, DllName, DllValue, TRUE);
1385 }
1386 
1387 NTSTATUS
1388 NTAPI
1389 SmpInitializeKnownDllsInternal(IN PUNICODE_STRING Directory,
1390                                IN PUNICODE_STRING Path)
1391 {
1392     HANDLE DirFileHandle, DirHandle, SectionHandle, FileHandle, LinkHandle;
1393     UNICODE_STRING NtPath, DestinationString;
1394     OBJECT_ATTRIBUTES ObjectAttributes;
1395     NTSTATUS Status, Status1;
1396     PLIST_ENTRY NextEntry;
1397     PSMP_REGISTRY_VALUE RegEntry;
1398     ULONG_PTR ErrorParameters[3];
1399     UNICODE_STRING ErrorResponse;
1400     IO_STATUS_BLOCK IoStatusBlock;
1401     SECURITY_DESCRIPTOR_CONTROL OldFlag = 0;
1402     USHORT ImageCharacteristics;
1403 
1404     /* Initialize to NULL */
1405     DirFileHandle = NULL;
1406     DirHandle = NULL;
1407     NtPath.Buffer = NULL;
1408 
1409     /* Create the \KnownDLLs directory */
1410     InitializeObjectAttributes(&ObjectAttributes,
1411                                Directory,
1412                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1413                                NULL,
1414                                SmpKnownDllsSecurityDescriptor);
1415     Status = NtCreateDirectoryObject(&DirHandle,
1416                                      DIRECTORY_ALL_ACCESS,
1417                                      &ObjectAttributes);
1418     if (!NT_SUCCESS(Status))
1419     {
1420         /* Handle failure */
1421         DPRINT1("SMSS: Unable to create %wZ directory - Status == %lx\n",
1422                 Directory, Status);
1423         return Status;
1424     }
1425 
1426     /* Convert the path to native format */
1427     if (!RtlDosPathNameToNtPathName_U(Path->Buffer, &NtPath, NULL, NULL))
1428     {
1429         /* Fail if this didn't work */
1430         DPRINT1("SMSS: Unable to to convert %wZ to an Nt path\n", Path);
1431         Status = STATUS_OBJECT_NAME_INVALID;
1432         goto Quickie;
1433     }
1434 
1435     /* Open the path that was specified, which should be a directory */
1436     InitializeObjectAttributes(&ObjectAttributes,
1437                                &NtPath,
1438                                OBJ_CASE_INSENSITIVE,
1439                                NULL,
1440                                NULL);
1441     Status = NtOpenFile(&DirFileHandle,
1442                         FILE_LIST_DIRECTORY | SYNCHRONIZE,
1443                         &ObjectAttributes,
1444                         &IoStatusBlock,
1445                         FILE_SHARE_READ | FILE_SHARE_WRITE,
1446                         FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
1447     if (!NT_SUCCESS(Status))
1448     {
1449         /* Fail if we couldn't open it */
1450         DPRINT1("SMSS: Unable to open a handle to the KnownDll directory (%wZ)"
1451                 "- Status == %lx\n",
1452                 Path,
1453                 Status);
1454         FileHandle = NULL;
1455         goto Quickie;
1456     }
1457 
1458     /* Temporarily hack the SD to use a default DACL for this symbolic link */
1459     if (SmpPrimarySecurityDescriptor)
1460     {
1461         OldFlag = SmpPrimarySecurityDescriptor->Control;
1462         SmpPrimarySecurityDescriptor->Control |= SE_DACL_DEFAULTED;
1463     }
1464 
1465     /* Create a symbolic link to the directory in the object manager */
1466     RtlInitUnicodeString(&DestinationString, L"KnownDllPath");
1467     InitializeObjectAttributes(&ObjectAttributes,
1468                                &DestinationString,
1469                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
1470                                DirHandle,
1471                                SmpPrimarySecurityDescriptor);
1472     Status = NtCreateSymbolicLinkObject(&LinkHandle,
1473                                         SYMBOLIC_LINK_ALL_ACCESS,
1474                                         &ObjectAttributes,
1475                                         Path);
1476 
1477     /* Undo the hack */
1478     if (SmpPrimarySecurityDescriptor) SmpPrimarySecurityDescriptor->Control = OldFlag;
1479 
1480     /* Check if the symlink was created */
1481     if (!NT_SUCCESS(Status))
1482     {
1483         /* It wasn't, so bail out since the OS needs it to exist */
1484         DPRINT1("SMSS: Unable to create %wZ symbolic link - Status == %lx\n",
1485                 &DestinationString, Status);
1486         LinkHandle = NULL;
1487         goto Quickie;
1488     }
1489 
1490     /* We created it permanent, we can go ahead and close the handle now */
1491     Status1 = NtClose(LinkHandle);
1492     ASSERT(NT_SUCCESS(Status1));
1493 
1494     /* Now loop the known DLLs */
1495     NextEntry = SmpKnownDllsList.Flink;
1496     while (NextEntry != &SmpKnownDllsList)
1497     {
1498         /* Get the entry and move on */
1499         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
1500         NextEntry = NextEntry->Flink;
1501 
1502         DPRINT("Processing known DLL: %wZ-%wZ\n", &RegEntry->Name, &RegEntry->Value);
1503 
1504         /* Skip the entry if it's in the excluded list */
1505         if ((SmpFindRegistryValue(&SmpExcludeKnownDllsList,
1506                                   RegEntry->Name.Buffer)) ||
1507             (SmpFindRegistryValue(&SmpExcludeKnownDllsList,
1508                                   RegEntry->Value.Buffer)))
1509         {
1510             continue;
1511         }
1512 
1513         /* Open the actual file */
1514         InitializeObjectAttributes(&ObjectAttributes,
1515                                    &RegEntry->Value,
1516                                    OBJ_CASE_INSENSITIVE,
1517                                    DirFileHandle,
1518                                    NULL);
1519         Status1 = NtOpenFile(&FileHandle,
1520                              SYNCHRONIZE | FILE_EXECUTE,
1521                              &ObjectAttributes,
1522                              &IoStatusBlock,
1523                              FILE_SHARE_READ | FILE_SHARE_DELETE,
1524                              FILE_NON_DIRECTORY_FILE |
1525                              FILE_SYNCHRONOUS_IO_NONALERT);
1526         /* If we failed, skip it */
1527         if (!NT_SUCCESS(Status1)) continue;
1528 
1529         /* Checksum it */
1530         Status = LdrVerifyImageMatchesChecksum((HANDLE)((ULONG_PTR)FileHandle | 1),
1531                                                SmpProcessModuleImports,
1532                                                RegEntry,
1533                                                &ImageCharacteristics);
1534         if (!NT_SUCCESS(Status))
1535         {
1536             /* Checksum failed, so don't even try going further -- kill SMSS */
1537             RtlInitUnicodeString(&ErrorResponse,
1538                                  L"Verification of a KnownDLL failed.");
1539             ErrorParameters[0] = (ULONG_PTR)&ErrorResponse;
1540             ErrorParameters[1] = Status;
1541             ErrorParameters[2] = (ULONG_PTR)&RegEntry->Value;
1542             SmpTerminate(ErrorParameters, 5, RTL_NUMBER_OF(ErrorParameters));
1543         }
1544         else if (!(ImageCharacteristics & IMAGE_FILE_DLL))
1545         {
1546             /* An invalid known DLL entry will also kill SMSS */
1547             RtlInitUnicodeString(&ErrorResponse,
1548                                  L"Non-DLL file included in KnownDLL list.");
1549             ErrorParameters[0] = (ULONG_PTR)&ErrorResponse;
1550             ErrorParameters[1] = STATUS_INVALID_IMPORT_OF_NON_DLL;
1551             ErrorParameters[2] = (ULONG_PTR)&RegEntry->Value;
1552             SmpTerminate(ErrorParameters, 5, RTL_NUMBER_OF(ErrorParameters));
1553         }
1554 
1555         /* Temporarily hack the SD to use a default DACL for this section */
1556         if (SmpLiberalSecurityDescriptor)
1557         {
1558             OldFlag = SmpLiberalSecurityDescriptor->Control;
1559             SmpLiberalSecurityDescriptor->Control |= SE_DACL_DEFAULTED;
1560         }
1561 
1562         /* Create the section for this known DLL */
1563         InitializeObjectAttributes(&ObjectAttributes,
1564                                    &RegEntry->Value,
1565                                    OBJ_PERMANENT,
1566                                    DirHandle,
1567                                    SmpLiberalSecurityDescriptor)
1568         Status = NtCreateSection(&SectionHandle,
1569                                  SECTION_ALL_ACCESS,
1570                                  &ObjectAttributes,
1571                                  0,
1572                                  PAGE_EXECUTE,
1573                                  SEC_IMAGE,
1574                                  FileHandle);
1575 
1576         /* Undo the hack */
1577         if (SmpLiberalSecurityDescriptor) SmpLiberalSecurityDescriptor->Control = OldFlag;
1578 
1579         /* Check if we created the section okay */
1580         if (NT_SUCCESS(Status))
1581         {
1582             /* We can close it now, since it's marked permanent */
1583             Status1 = NtClose(SectionHandle);
1584             ASSERT(NT_SUCCESS(Status1));
1585         }
1586         else
1587         {
1588             /* If we couldn't make it "known", that's fine and keep going */
1589             DPRINT1("SMSS: CreateSection for KnownDll %wZ failed - Status == %lx\n",
1590                     &RegEntry->Value, Status);
1591         }
1592 
1593         /* Close the file since we can move on to the next one */
1594         Status1 = NtClose(FileHandle);
1595         ASSERT(NT_SUCCESS(Status1));
1596     }
1597 
1598 Quickie:
1599     /* Close both handles and free the NT path buffer */
1600     if (DirHandle)
1601     {
1602         Status1 = NtClose(DirHandle);
1603         ASSERT(NT_SUCCESS(Status1));
1604     }
1605     if (DirFileHandle)
1606     {
1607         Status1 = NtClose(DirFileHandle);
1608         ASSERT(NT_SUCCESS(Status1));
1609     }
1610     if (NtPath.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, NtPath.Buffer);
1611     return Status;
1612 }
1613 
1614 NTSTATUS
1615 NTAPI
1616 SmpInitializeKnownDlls(VOID)
1617 {
1618     NTSTATUS Status;
1619     PSMP_REGISTRY_VALUE RegEntry;
1620     UNICODE_STRING DestinationString;
1621     PLIST_ENTRY Head, NextEntry;
1622 
1623     /* Call the internal function */
1624     RtlInitUnicodeString(&DestinationString, L"\\KnownDlls");
1625     Status = SmpInitializeKnownDllsInternal(&DestinationString, &SmpKnownDllPath);
1626 
1627     /* Wipe out the list regardless of success */
1628     Head = &SmpKnownDllsList;
1629     while (!IsListEmpty(Head))
1630     {
1631         /* Remove this entry */
1632         NextEntry = RemoveHeadList(Head);
1633 
1634         /* Free it */
1635         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
1636         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
1637         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
1638         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
1639     }
1640 
1641     /* All done */
1642     return Status;
1643 }
1644 
1645 NTSTATUS
1646 NTAPI
1647 SmpCreateDynamicEnvironmentVariables(VOID)
1648 {
1649     NTSTATUS Status;
1650     SYSTEM_BASIC_INFORMATION BasicInfo;
1651     SYSTEM_PROCESSOR_INFORMATION ProcessorInfo;
1652     OBJECT_ATTRIBUTES ObjectAttributes;
1653     UNICODE_STRING ValueName, DestinationString;
1654     HANDLE KeyHandle, KeyHandle2;
1655     ULONG ResultLength;
1656     PWCHAR ValueData;
1657     WCHAR ValueBuffer[512], ValueBuffer2[512];
1658     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
1659     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo2 = (PVOID)ValueBuffer2;
1660 
1661     /* Get system basic information -- we'll need the CPU count */
1662     Status = NtQuerySystemInformation(SystemBasicInformation,
1663                                       &BasicInfo,
1664                                       sizeof(BasicInfo),
1665                                       NULL);
1666     if (!NT_SUCCESS(Status))
1667     {
1668         /* Bail out on failure */
1669         DPRINT1("SMSS: Unable to query system basic information - %x\n", Status);
1670         return Status;
1671     }
1672 
1673     /* Get the processor information, we'll query a bunch of revision info */
1674     Status = NtQuerySystemInformation(SystemProcessorInformation,
1675                                       &ProcessorInfo,
1676                                       sizeof(ProcessorInfo),
1677                                       NULL);
1678     if (!NT_SUCCESS(Status))
1679     {
1680         /* Bail out on failure */
1681         DPRINT1("SMSS: Unable to query system processor information - %x\n", Status);
1682         return Status;
1683     }
1684 
1685     /* We'll be writing all these environment variables over here */
1686     RtlInitUnicodeString(&DestinationString,
1687                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
1688                          L"Control\\Session Manager\\Environment");
1689     InitializeObjectAttributes(&ObjectAttributes,
1690                                &DestinationString,
1691                                OBJ_CASE_INSENSITIVE,
1692                                NULL,
1693                                NULL);
1694     Status = NtOpenKey(&KeyHandle, GENERIC_WRITE, &ObjectAttributes);
1695     if (!NT_SUCCESS(Status))
1696     {
1697         /* Bail out on failure */
1698         DPRINT1("SMSS: Unable to open %wZ - %x\n", &DestinationString, Status);
1699         return Status;
1700     }
1701 
1702     /* First let's write the OS variable */
1703     RtlInitUnicodeString(&ValueName, L"OS");
1704     ValueData = L"Windows_NT";
1705     DPRINT("Setting %wZ to %S\n", &ValueName, ValueData);
1706     Status = NtSetValueKey(KeyHandle,
1707                            &ValueName,
1708                            0,
1709                            REG_SZ,
1710                            ValueData,
1711                            (wcslen(ValueData) + 1) * sizeof(WCHAR));
1712     if (!NT_SUCCESS(Status))
1713     {
1714         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1715                 &ValueName, Status);
1716         NtClose(KeyHandle);
1717         return Status;
1718     }
1719 
1720     /* Next, let's write the CPU architecture variable */
1721     RtlInitUnicodeString(&ValueName, L"PROCESSOR_ARCHITECTURE");
1722     switch (ProcessorInfo.ProcessorArchitecture)
1723     {
1724         /* Pick the correct string that matches the architecture */
1725         case PROCESSOR_ARCHITECTURE_INTEL:
1726             ValueData = L"x86";
1727             break;
1728 
1729         case PROCESSOR_ARCHITECTURE_AMD64:
1730             ValueData = L"AMD64";
1731             break;
1732 
1733         case PROCESSOR_ARCHITECTURE_IA64:
1734             ValueData = L"IA64";
1735             break;
1736 
1737         default:
1738             ValueData = L"Unknown";
1739             break;
1740     }
1741 
1742     /* Set it */
1743     DPRINT("Setting %wZ to %S\n", &ValueName, ValueData);
1744     Status = NtSetValueKey(KeyHandle,
1745                            &ValueName,
1746                            0,
1747                            REG_SZ,
1748                            ValueData,
1749                            (wcslen(ValueData) + 1) * sizeof(WCHAR));
1750     if (!NT_SUCCESS(Status))
1751     {
1752         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1753                 &ValueName, Status);
1754         NtClose(KeyHandle);
1755         return Status;
1756     }
1757 
1758     /* And now let's write the processor level */
1759     RtlInitUnicodeString(&ValueName, L"PROCESSOR_LEVEL");
1760     swprintf(ValueBuffer, L"%u", ProcessorInfo.ProcessorLevel);
1761     DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1762     Status = NtSetValueKey(KeyHandle,
1763                            &ValueName,
1764                            0,
1765                            REG_SZ,
1766                            ValueBuffer,
1767                            (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1768     if (!NT_SUCCESS(Status))
1769     {
1770         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1771                 &ValueName, Status);
1772         NtClose(KeyHandle);
1773         return Status;
1774     }
1775 
1776     /* Now open the hardware CPU key */
1777     RtlInitUnicodeString(&DestinationString,
1778                          L"\\Registry\\Machine\\Hardware\\Description\\System\\"
1779                          L"CentralProcessor\\0");
1780     InitializeObjectAttributes(&ObjectAttributes,
1781                                &DestinationString,
1782                                OBJ_CASE_INSENSITIVE,
1783                                NULL,
1784                                NULL);
1785     Status = NtOpenKey(&KeyHandle2, KEY_READ, &ObjectAttributes);
1786     if (!NT_SUCCESS(Status))
1787     {
1788         DPRINT1("SMSS: Unable to open %wZ - %x\n", &DestinationString, Status);
1789         NtClose(KeyHandle);
1790         return Status;
1791     }
1792 
1793     /* So that we can read the identifier out of it... */
1794     RtlInitUnicodeString(&ValueName, L"Identifier");
1795     Status = NtQueryValueKey(KeyHandle2,
1796                              &ValueName,
1797                              KeyValuePartialInformation,
1798                              PartialInfo,
1799                              sizeof(ValueBuffer),
1800                              &ResultLength);
1801     if (!NT_SUCCESS(Status))
1802     {
1803         NtClose(KeyHandle2);
1804         NtClose(KeyHandle);
1805         DPRINT1("SMSS: Unable to read %wZ\\%wZ - %x\n",
1806                 &DestinationString, &ValueName, Status);
1807         return Status;
1808     }
1809 
1810     /* As well as the vendor... */
1811     RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
1812     Status = NtQueryValueKey(KeyHandle2,
1813                              &ValueName,
1814                              KeyValuePartialInformation,
1815                              PartialInfo2,
1816                              sizeof(ValueBuffer2),
1817                              &ResultLength);
1818     NtClose(KeyHandle2);
1819     if (NT_SUCCESS(Status))
1820     {
1821         /* To combine it into a single string */
1822         swprintf((PWCHAR)PartialInfo->Data + wcslen((PWCHAR)PartialInfo->Data),
1823                  L", %S",
1824                  PartialInfo2->Data);
1825     }
1826 
1827     /* So that we can set this as the PROCESSOR_IDENTIFIER variable */
1828     RtlInitUnicodeString(&ValueName, L"PROCESSOR_IDENTIFIER");
1829     DPRINT("Setting %wZ to %s\n", &ValueName, PartialInfo->Data);
1830     Status = NtSetValueKey(KeyHandle,
1831                            &ValueName,
1832                            0,
1833                            REG_SZ,
1834                            PartialInfo->Data,
1835                            (wcslen((PWCHAR)PartialInfo->Data) + 1) * sizeof(WCHAR));
1836     if (!NT_SUCCESS(Status))
1837     {
1838         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1839                 &ValueName, Status);
1840         NtClose(KeyHandle);
1841         return Status;
1842     }
1843 
1844     /* Now let's get the processor architecture */
1845     RtlInitUnicodeString(&ValueName, L"PROCESSOR_REVISION");
1846     switch (ProcessorInfo.ProcessorArchitecture)
1847     {
1848         /* Check if this is an older Intel CPU */
1849         case PROCESSOR_ARCHITECTURE_INTEL:
1850             if ((ProcessorInfo.ProcessorRevision >> 8) == 0xFF)
1851             {
1852                 /* These guys used a revision + stepping, so get the rev only */
1853                 swprintf(ValueBuffer, L"%02x", ProcessorInfo.ProcessorRevision & 0xFF);
1854                 _wcsupr(ValueBuffer);
1855                 break;
1856             }
1857 
1858         /* Modern Intel, as well as 64-bit CPUs use a revision without stepping */
1859         case PROCESSOR_ARCHITECTURE_IA64:
1860         case PROCESSOR_ARCHITECTURE_AMD64:
1861             swprintf(ValueBuffer, L"%04x", ProcessorInfo.ProcessorRevision);
1862             break;
1863 
1864         /* And anything else we'll just read the whole revision identifier */
1865         default:
1866             swprintf(ValueBuffer, L"%u", ProcessorInfo.ProcessorRevision);
1867             break;
1868     }
1869 
1870     /* Write the revision to the registry */
1871     DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1872     Status = NtSetValueKey(KeyHandle,
1873                            &ValueName,
1874                            0,
1875                            REG_SZ,
1876                            ValueBuffer,
1877                            (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1878     if (!NT_SUCCESS(Status))
1879     {
1880         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1881                 &ValueName, Status);
1882         NtClose(KeyHandle);
1883         return Status;
1884     }
1885 
1886     /* And finally, write the number of CPUs */
1887     RtlInitUnicodeString(&ValueName, L"NUMBER_OF_PROCESSORS");
1888     swprintf(ValueBuffer, L"%d", BasicInfo.NumberOfProcessors);
1889     DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1890     Status = NtSetValueKey(KeyHandle,
1891                            &ValueName,
1892                            0,
1893                            REG_SZ,
1894                            ValueBuffer,
1895                            (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1896     if (!NT_SUCCESS(Status))
1897     {
1898         DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1899                 &ValueName, Status);
1900         NtClose(KeyHandle);
1901         return Status;
1902     }
1903 
1904     /* Now we need to write the safeboot option key in a different format */
1905     RtlInitUnicodeString(&DestinationString,
1906                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
1907                          L"Control\\Safeboot\\Option");
1908     InitializeObjectAttributes(&ObjectAttributes,
1909                                &DestinationString,
1910                                OBJ_CASE_INSENSITIVE,
1911                                NULL,
1912                                NULL);
1913     Status = NtOpenKey(&KeyHandle2, KEY_ALL_ACCESS, &ObjectAttributes);
1914     if (NT_SUCCESS(Status))
1915     {
1916         /* This was indeed a safeboot, so check what kind of safeboot it was */
1917         RtlInitUnicodeString(&ValueName, L"OptionValue");
1918         Status = NtQueryValueKey(KeyHandle2,
1919                                  &ValueName,
1920                                  KeyValuePartialInformation,
1921                                  PartialInfo,
1922                                  sizeof(ValueBuffer),
1923                                  &ResultLength);
1924         NtClose(KeyHandle2);
1925         if (NT_SUCCESS(Status))
1926         {
1927             /* Convert from the integer value to the correct specifier */
1928             RtlInitUnicodeString(&ValueName, L"SAFEBOOT_OPTION");
1929             switch (*(PULONG)PartialInfo->Data)
1930             {
1931                 case 1:
1932                     wcscpy(ValueBuffer, L"MINIMAL");
1933                     break;
1934                 case 2:
1935                     wcscpy(ValueBuffer, L"NETWORK");
1936                     break;
1937                 case 3:
1938                     wcscpy(ValueBuffer, L"DSREPAIR");
1939                     break;
1940             }
1941 
1942             /* And write it in the environment! */
1943             DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
1944             Status = NtSetValueKey(KeyHandle,
1945                                    &ValueName,
1946                                    0,
1947                                    REG_SZ,
1948                                    ValueBuffer,
1949                                    (wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
1950             if (!NT_SUCCESS(Status))
1951             {
1952                 DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
1953                         &ValueName, Status);
1954                 NtClose(KeyHandle);
1955                 return Status;
1956             }
1957         }
1958         else
1959         {
1960             DPRINT1("SMSS: Failed querying safeboot option = %x\n", Status);
1961         }
1962     }
1963 
1964     /* We are all done now */
1965     NtClose(KeyHandle);
1966     return STATUS_SUCCESS;
1967 }
1968 
1969 NTSTATUS
1970 NTAPI
1971 SmpProcessFileRenames(VOID)
1972 {
1973     BOOLEAN OldState, HavePrivilege = FALSE;
1974     NTSTATUS Status;
1975     HANDLE FileHandle, OtherFileHandle;
1976     FILE_INFORMATION_CLASS InformationClass;
1977     OBJECT_ATTRIBUTES ObjectAttributes;
1978     IO_STATUS_BLOCK IoStatusBlock;
1979     UNICODE_STRING FileString;
1980     FILE_BASIC_INFORMATION BasicInfo;
1981     FILE_DISPOSITION_INFORMATION DeleteInformation;
1982     PFILE_RENAME_INFORMATION Buffer;
1983     PLIST_ENTRY Head, NextEntry;
1984     PSMP_REGISTRY_VALUE RegEntry;
1985     PWCHAR FileName;
1986     ULONG ValueLength, Length;
1987 
1988     /* Give us access to restore any files we want */
1989     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OldState);
1990     if (NT_SUCCESS(Status)) HavePrivilege = TRUE;
1991 
1992     // FIXME: Handle SFC-protected file renames!
1993     if (SmpAllowProtectedRenames)
1994         DPRINT1("SMSS: FIXME: Handle SFC-protected file renames!\n");
1995 
1996     /* Process pending files to rename */
1997     Head = &SmpFileRenameList;
1998     while (!IsListEmpty(Head))
1999     {
2000         /* Get this entry */
2001         NextEntry = RemoveHeadList(Head);
2002         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
2003         DPRINT("Processing PFRO: '%wZ' / '%wZ'\n", &RegEntry->Value, &RegEntry->Name);
2004 
2005         /* Skip past the '@' marker */
2006         if (!(RegEntry->Value.Length) && (*RegEntry->Name.Buffer == L'@'))
2007         {
2008             RegEntry->Name.Length -= sizeof(UNICODE_NULL);
2009             RegEntry->Name.Buffer++;
2010         }
2011 
2012         /* Open the file for delete access */
2013         InitializeObjectAttributes(&ObjectAttributes,
2014                                    &RegEntry->Name,
2015                                    OBJ_CASE_INSENSITIVE,
2016                                    NULL,
2017                                    NULL);
2018         Status = NtOpenFile(&OtherFileHandle,
2019                             DELETE | SYNCHRONIZE,
2020                             &ObjectAttributes,
2021                             &IoStatusBlock,
2022                             FILE_SHARE_READ | FILE_SHARE_WRITE,
2023                             FILE_SYNCHRONOUS_IO_NONALERT);
2024         if (!NT_SUCCESS(Status)) goto Quickie;
2025 
2026         /* Check if it's a rename or just a delete */
2027         ValueLength = RegEntry->Value.Length;
2028         if (!ValueLength)
2029         {
2030             /* Just a delete, set up the class, length and buffer */
2031             InformationClass = FileDispositionInformation;
2032             Length = sizeof(DeleteInformation);
2033             Buffer = (PFILE_RENAME_INFORMATION)&DeleteInformation;
2034 
2035             /* Set the delete disposition */
2036             DeleteInformation.DeleteFile = TRUE;
2037         }
2038         else
2039         {
2040             /* This is a rename, setup the class and length */
2041             InformationClass = FileRenameInformation;
2042             Length = ValueLength + sizeof(FILE_RENAME_INFORMATION);
2043 
2044             /* Skip past the special markers */
2045             FileName = RegEntry->Value.Buffer;
2046             if ((*FileName == L'!') || (*FileName == L'@'))
2047             {
2048                 FileName++;
2049                 Length -= sizeof(UNICODE_NULL);
2050             }
2051 
2052             /* Now allocate the buffer for the rename information */
2053             Buffer = RtlAllocateHeap(RtlGetProcessHeap(), SmBaseTag, Length);
2054             if (Buffer)
2055             {
2056                 /* Setup the buffer to point to the filename, and copy it */
2057                 Buffer->RootDirectory = NULL;
2058                 Buffer->FileNameLength = Length - sizeof(FILE_RENAME_INFORMATION);
2059                 Buffer->ReplaceIfExists = FileName != RegEntry->Value.Buffer;
2060                 RtlCopyMemory(Buffer->FileName, FileName, Buffer->FileNameLength);
2061             }
2062             else
2063             {
2064                 /* Fail */
2065                 Status = STATUS_NO_MEMORY;
2066             }
2067         }
2068 
2069         /* Check if everything is okay till here */
2070         if (NT_SUCCESS(Status))
2071         {
2072             /* Now either rename or delete the file as requested */
2073             Status = NtSetInformationFile(OtherFileHandle,
2074                                           &IoStatusBlock,
2075                                           Buffer,
2076                                           Length,
2077                                           InformationClass);
2078 
2079             /* Check if we seem to have failed because the file was readonly */
2080             if (!NT_SUCCESS(Status) &&
2081                 (InformationClass == FileRenameInformation) &&
2082                 (Status == STATUS_OBJECT_NAME_COLLISION) &&
2083                 Buffer->ReplaceIfExists)
2084             {
2085                 /* Open the file for write attribute access this time... */
2086                 DPRINT("\nSMSS: '%wZ' => '%wZ' failed - Status == %x, Possible readonly target\n",
2087                         &RegEntry->Name,
2088                         &RegEntry->Value,
2089                         STATUS_OBJECT_NAME_COLLISION);
2090                 FileString.Length = RegEntry->Value.Length - sizeof(WCHAR);
2091                 FileString.MaximumLength = RegEntry->Value.MaximumLength - sizeof(WCHAR);
2092                 FileString.Buffer = FileName;
2093                 InitializeObjectAttributes(&ObjectAttributes,
2094                                            &FileString,
2095                                            OBJ_CASE_INSENSITIVE,
2096                                            NULL,
2097                                            NULL);
2098                 Status = NtOpenFile(&FileHandle,
2099                                     FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
2100                                     &ObjectAttributes,
2101                                     &IoStatusBlock,
2102                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
2103                                     FILE_SYNCHRONOUS_IO_NONALERT);
2104                 if (!NT_SUCCESS(Status))
2105                 {
2106                     /* That didn't work, so bail out */
2107                     DPRINT1("     SMSS: Open Existing file Failed - Status == %x\n",
2108                             Status);
2109                 }
2110                 else
2111                 {
2112                     /* Now remove the read-only attribute from the file */
2113                     DPRINT("     SMSS: Open Existing Success\n");
2114                     RtlZeroMemory(&BasicInfo, sizeof(BasicInfo));
2115                     BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2116                     Status = NtSetInformationFile(FileHandle,
2117                                                   &IoStatusBlock,
2118                                                   &BasicInfo,
2119                                                   sizeof(BasicInfo),
2120                                                   FileBasicInformation);
2121                     NtClose(FileHandle);
2122                     if (!NT_SUCCESS(Status))
2123                     {
2124                         /* That didn't work, bail out */
2125                         DPRINT1("     SMSS: Set To NORMAL Failed - Status == %x\n",
2126                                 Status);
2127                     }
2128                     else
2129                     {
2130                         /* Now that the file is no longer read-only, delete! */
2131                         DPRINT("     SMSS: Set To NORMAL OK\n");
2132                         Status = NtSetInformationFile(OtherFileHandle,
2133                                                       &IoStatusBlock,
2134                                                       Buffer,
2135                                                       Length,
2136                                                       FileRenameInformation);
2137                         if (!NT_SUCCESS(Status))
2138                         {
2139                             /* That failed too! */
2140                             DPRINT1("     SMSS: Re-Rename Failed - Status == %x\n",
2141                                     Status);
2142                         }
2143                         else
2144                         {
2145                             /* Everything ok */
2146                             DPRINT("     SMSS: Re-Rename Worked OK\n");
2147                         }
2148                     }
2149                 }
2150             }
2151         }
2152 
2153         /* Close the file handle and check the operation result */
2154         NtClose(OtherFileHandle);
2155 Quickie:
2156         if (!NT_SUCCESS(Status))
2157         {
2158             /* We totally failed */
2159             DPRINT1("SMSS: '%wZ' => '%wZ' failed - Status == %x\n",
2160                     &RegEntry->Name, &RegEntry->Value, Status);
2161         }
2162         else if (RegEntry->Value.Length)
2163         {
2164             /* We succeed with a rename */
2165             DPRINT("SMSS: '%wZ' (renamed to) '%wZ'\n", &RegEntry->Name, &RegEntry->Value);
2166         }
2167         else
2168         {
2169             /* We succeeded with a delete */
2170             DPRINT("SMSS: '%wZ' (deleted)\n", &RegEntry->Name);
2171         }
2172 
2173         /* Now free this entry and keep going */
2174         if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
2175         if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
2176         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
2177     }
2178 
2179     /* Put back the restore privilege if we had requested it, and return */
2180     if (HavePrivilege) RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, FALSE, FALSE, &OldState);
2181     return Status;
2182 }
2183 
2184 NTSTATUS
2185 NTAPI
2186 SmpLoadDataFromRegistry(OUT PUNICODE_STRING InitialCommand)
2187 {
2188     NTSTATUS Status;
2189     PLIST_ENTRY Head, NextEntry;
2190     PSMP_REGISTRY_VALUE RegEntry;
2191     PVOID OriginalEnvironment;
2192     ULONG MuSessionId = 0;
2193     OBJECT_ATTRIBUTES ObjectAttributes;
2194     HANDLE KeyHandle;
2195     UNICODE_STRING DestinationString;
2196 
2197     /* Initialize the keywords we'll be looking for */
2198     RtlInitUnicodeString(&SmpDebugKeyword, L"debug");
2199     RtlInitUnicodeString(&SmpASyncKeyword, L"async");
2200     RtlInitUnicodeString(&SmpAutoChkKeyword, L"autocheck");
2201 
2202     /* Initialize all the registry-associated list heads */
2203     InitializeListHead(&SmpBootExecuteList);
2204     InitializeListHead(&SmpSetupExecuteList);
2205     InitializeListHead(&SmpPagingFileList);
2206     InitializeListHead(&SmpDosDevicesList);
2207     InitializeListHead(&SmpFileRenameList);
2208     InitializeListHead(&SmpKnownDllsList);
2209     InitializeListHead(&SmpExcludeKnownDllsList);
2210     InitializeListHead(&SmpSubSystemList);
2211     InitializeListHead(&SmpSubSystemsToLoad);
2212     InitializeListHead(&SmpSubSystemsToDefer);
2213     InitializeListHead(&SmpExecuteList);
2214     SmpPagingFileInitialize();
2215 
2216     /* Initialize the SMSS environment */
2217     Status = RtlCreateEnvironment(TRUE, &SmpDefaultEnvironment);
2218     if (!NT_SUCCESS(Status))
2219     {
2220         /* Fail if there was a problem */
2221         DPRINT1("SMSS: Unable to allocate default environment - Status == %X\n",
2222                 Status);
2223         SMSS_CHECKPOINT(RtlCreateEnvironment, Status);
2224         return Status;
2225     }
2226 
2227     /* Check if we were booted in PE mode (LiveCD should have this) */
2228     RtlInitUnicodeString(&DestinationString,
2229                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
2230                          L"Control\\MiniNT");
2231     InitializeObjectAttributes(&ObjectAttributes,
2232                                &DestinationString,
2233                                OBJ_CASE_INSENSITIVE,
2234                                NULL,
2235                                NULL);
2236     Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
2237     if (NT_SUCCESS(Status))
2238     {
2239         /* If the key exists, we were */
2240         NtClose(KeyHandle);
2241         MiniNTBoot = TRUE;
2242     }
2243 
2244     /* Print out if this is the case */
2245     if (MiniNTBoot) DPRINT("SMSS: !!! MiniNT Boot !!!\n");
2246 
2247     /* Open the environment key to see if we are booted in safe mode */
2248     RtlInitUnicodeString(&DestinationString,
2249                          L"\\Registry\\Machine\\System\\CurrentControlSet\\"
2250                          L"Control\\Session Manager\\Environment");
2251     InitializeObjectAttributes(&ObjectAttributes,
2252                                &DestinationString,
2253                                OBJ_CASE_INSENSITIVE,
2254                                NULL,
2255                                NULL);
2256     Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
2257     if (NT_SUCCESS(Status))
2258     {
2259         /* Delete the value if we found it */
2260         RtlInitUnicodeString(&DestinationString, L"SAFEBOOT_OPTION");
2261         NtDeleteValueKey(KeyHandle, &DestinationString);
2262         NtClose(KeyHandle);
2263     }
2264 
2265     /* Switch environments, then query the registry for all needed settings */
2266     OriginalEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
2267     NtCurrentPeb()->ProcessParameters->Environment = SmpDefaultEnvironment;
2268     Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
2269                                     L"Session Manager",
2270                                     SmpRegistryConfigurationTable,
2271                                     NULL,
2272                                     NULL);
2273     SmpDefaultEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
2274     NtCurrentPeb()->ProcessParameters->Environment = OriginalEnvironment;
2275     if (!NT_SUCCESS(Status))
2276     {
2277         /* We failed somewhere in registry initialization, which is bad... */
2278         DPRINT1("SMSS: RtlQueryRegistryValues failed - Status == %lx\n", Status);
2279         SMSS_CHECKPOINT(RtlQueryRegistryValues, Status);
2280         return Status;
2281     }
2282 
2283     /* Now we can start acting on the registry settings. First to DOS devices */
2284     Status = SmpInitializeDosDevices();
2285     if (!NT_SUCCESS(Status))
2286     {
2287         /* Failed */
2288         DPRINT1("SMSS: Unable to initialize DosDevices configuration - Status == %lx\n",
2289                 Status);
2290         SMSS_CHECKPOINT(SmpInitializeDosDevices, Status);
2291         return Status;
2292     }
2293 
2294     /* Next create the session directory... */
2295     RtlInitUnicodeString(&DestinationString, L"\\Sessions");
2296     InitializeObjectAttributes(&ObjectAttributes,
2297                                &DestinationString,
2298                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
2299                                NULL,
2300                                SmpPrimarySecurityDescriptor);
2301     Status = NtCreateDirectoryObject(&SmpSessionsObjectDirectory,
2302                                      DIRECTORY_ALL_ACCESS,
2303                                      &ObjectAttributes);
2304     if (!NT_SUCCESS(Status))
2305     {
2306         /* Fail */
2307         DPRINT1("SMSS: Unable to create %wZ object directory - Status == %lx\n",
2308                 &DestinationString, Status);
2309         SMSS_CHECKPOINT(NtCreateDirectoryObject, Status);
2310         return Status;
2311     }
2312 
2313     /* Next loop all the boot execute binaries */
2314     Head = &SmpBootExecuteList;
2315     while (!IsListEmpty(Head))
2316     {
2317         /* Remove each one from the list */
2318         NextEntry = RemoveHeadList(Head);
2319 
2320         /* Execute it */
2321         RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
2322         SmpExecuteCommand(&RegEntry->Name, 0, NULL, 0);
2323 
2324         /* And free it */
2325         if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
2326         if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
2327         RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
2328     }
2329 
2330     /* Now do any pending file rename operations... */
2331     if (!MiniNTBoot) SmpProcessFileRenames();
2332 
2333     /* And initialize known DLLs... */
2334     Status = SmpInitializeKnownDlls();
2335     if (!NT_SUCCESS(Status))
2336     {
2337         /* Fail if that didn't work */
2338         DPRINT1("SMSS: Unable to initialize KnownDll configuration - Status == %lx\n",
2339                 Status);
2340         SMSS_CHECKPOINT(SmpInitializeKnownDlls, Status);
2341         return Status;
2342     }
2343 
2344     /* Create the needed page files */
2345     if (!MiniNTBoot)
2346     {
2347         /* Loop every page file */
2348         Head = &SmpPagingFileList;
2349         while (!IsListEmpty(Head))
2350         {
2351             /* Remove each one from the list */
2352             NextEntry = RemoveHeadList(Head);
2353 
2354             /* Create the descriptor for it */
2355             RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
2356             SmpCreatePagingFileDescriptor(&RegEntry->Name);
2357 
2358             /* And free it */
2359             if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
2360             if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
2361             RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
2362         }
2363 
2364         /* Now create all the paging files for the descriptors that we have */
2365         SmpCreatePagingFiles();
2366     }
2367 
2368     /* Tell Cm it's now safe to fully enable write access to the registry */
2369     NtInitializeRegistry(CM_BOOT_FLAG_SMSS);
2370 
2371     /* Create all the system-based environment variables for later inheriting */
2372     Status = SmpCreateDynamicEnvironmentVariables();
2373     if (!NT_SUCCESS(Status))
2374     {
2375         /* Handle failure */
2376         SMSS_CHECKPOINT(SmpCreateDynamicEnvironmentVariables, Status);
2377         return Status;
2378     }
2379 
2380     /* And finally load all the subsystems for our first session! */
2381     Status = SmpLoadSubSystemsForMuSession(&MuSessionId,
2382                                            &SmpWindowsSubSysProcessId,
2383                                            InitialCommand);
2384     ASSERT(MuSessionId == 0);
2385     if (!NT_SUCCESS(Status)) SMSS_CHECKPOINT(SmpLoadSubSystemsForMuSession, Status);
2386     return Status;
2387 }
2388 
2389 NTSTATUS
2390 NTAPI
2391 SmpInit(IN PUNICODE_STRING InitialCommand,
2392         OUT PHANDLE ProcessHandle)
2393 {
2394     NTSTATUS Status, Status2;
2395     OBJECT_ATTRIBUTES ObjectAttributes;
2396     UNICODE_STRING PortName, EventName;
2397     HANDLE EventHandle, PortHandle;
2398     ULONG HardErrorMode;
2399 
2400     /* Create the SMSS Heap */
2401     SmBaseTag = RtlCreateTagHeap(RtlGetProcessHeap(),
2402                                  0,
2403                                  L"SMSS!",
2404                                  L"INIT");
2405     SmpHeap = RtlGetProcessHeap();
2406 
2407     /* Enable hard errors */
2408     HardErrorMode = TRUE;
2409     NtSetInformationProcess(NtCurrentProcess(),
2410                             ProcessDefaultHardErrorMode,
2411                             &HardErrorMode,
2412                             sizeof(HardErrorMode));
2413 
2414     /* Initialize the subsystem list and the session list, plus their locks */
2415     RtlInitializeCriticalSection(&SmpKnownSubSysLock);
2416     InitializeListHead(&SmpKnownSubSysHead);
2417     RtlInitializeCriticalSection(&SmpSessionListLock);
2418     InitializeListHead(&SmpSessionListHead);
2419 
2420     /* Initialize the process list */
2421     InitializeListHead(&NativeProcessList);
2422 
2423     /* Initialize session parameters */
2424     SmpNextSessionId = 1;
2425     SmpNextSessionIdScanMode = 0;
2426     SmpDbgSsLoaded = FALSE;
2427 
2428     /* Create the initial security descriptors */
2429     Status = SmpCreateSecurityDescriptors(TRUE);
2430     if (!NT_SUCCESS(Status))
2431     {
2432         /* Fail */
2433         SMSS_CHECKPOINT(SmpCreateSecurityDescriptors, Status);
2434         return Status;
2435     }
2436 
2437     /* Initialize subsystem names */
2438     RtlInitUnicodeString(&SmpSubsystemName, L"NT-Session Manager");
2439     RtlInitUnicodeString(&PosixName, L"POSIX");
2440     RtlInitUnicodeString(&Os2Name, L"OS2");
2441 
2442     /* Create the SM API Port */
2443     RtlInitUnicodeString(&PortName, L"\\SmApiPort");
2444     InitializeObjectAttributes(&ObjectAttributes, &PortName, 0, NULL, NULL);
2445     Status = NtCreatePort(&PortHandle,
2446                           &ObjectAttributes,
2447                           sizeof(SB_CONNECTION_INFO),
2448                           sizeof(SM_API_MSG),
2449                           sizeof(SB_API_MSG) * 32);
2450     ASSERT(NT_SUCCESS(Status));
2451     SmpDebugPort = PortHandle;
2452 
2453     /* Create two SM API threads */
2454     Status = RtlCreateUserThread(NtCurrentProcess(),
2455                                  NULL,
2456                                  FALSE,
2457                                  0,
2458                                  0,
2459                                  0,
2460                                  SmpApiLoop,
2461                                  PortHandle,
2462                                  NULL,
2463                                  NULL);
2464     ASSERT(NT_SUCCESS(Status));
2465     Status = RtlCreateUserThread(NtCurrentProcess(),
2466                                  NULL,
2467                                  FALSE,
2468                                  0,
2469                                  0,
2470                                  0,
2471                                  SmpApiLoop,
2472                                  PortHandle,
2473                                  NULL,
2474                                  NULL);
2475     ASSERT(NT_SUCCESS(Status));
2476 
2477     /* Create the write event that autochk can set after running */
2478     RtlInitUnicodeString(&EventName, L"\\Device\\VolumesSafeForWriteAccess");
2479     InitializeObjectAttributes(&ObjectAttributes,
2480                                &EventName,
2481                                OBJ_PERMANENT,
2482                                NULL,
2483                                NULL);
2484     Status2 = NtCreateEvent(&EventHandle,
2485                             EVENT_ALL_ACCESS,
2486                             &ObjectAttributes,
2487                             0,
2488                             0);
2489     if (!NT_SUCCESS(Status2))
2490     {
2491         /* Should never really fail */
2492         DPRINT1("SMSS: Unable to create %wZ event - Status == %lx\n",
2493                 &EventName, Status2);
2494         ASSERT(NT_SUCCESS(Status2));
2495     }
2496 
2497     /* Now initialize everything else based on the registry parameters */
2498     Status = SmpLoadDataFromRegistry(InitialCommand);
2499     if (NT_SUCCESS(Status))
2500     {
2501         /* Autochk should've run now. Set the event and save the CSRSS handle */
2502         *ProcessHandle = SmpWindowsSubSysProcess;
2503         NtSetEvent(EventHandle, 0);
2504         NtClose(EventHandle);
2505     }
2506 
2507     /* All done */
2508     return Status;
2509 }
2510