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