xref: /reactos/base/system/smss/sminit.c (revision f43ce465)
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
SmpSaveRegistryValue(IN PLIST_ENTRY ListAddress,IN PWSTR Name,IN PWCHAR Value,IN BOOLEAN Flags)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
SmpFindRegistryValue(IN PLIST_ENTRY List,IN PWSTR ValueName)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
SmpConfigureProtectionMode(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureAllowProtectedRenames(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureObjectDirectories(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureMemoryMgmt(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureFileRenames(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureExcludeKnownDlls(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureDosDevices(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpInitializeKnownDllPath(IN PUNICODE_STRING DllPath,IN PWCHAR Buffer,IN ULONG Length)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
SmpConfigureKnownDlls(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureEnvironment(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpConfigureSubSystems(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
SmpTranslateSystemPartitionInformation(VOID)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
SmpCreateSecurityDescriptors(IN BOOLEAN InitialCall)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
SmpInitializeDosDevices(VOID)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
SmpProcessModuleImports(IN PVOID Unused,IN PCHAR ImportName)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
SmpInitializeKnownDllsInternal(IN PUNICODE_STRING Directory,IN PUNICODE_STRING Path)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
SmpInitializeKnownDlls(VOID)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
SmpCreateDynamicEnvironmentVariables(VOID)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
SmpProcessFileRenames(VOID)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
SmpLoadDataFromRegistry(OUT PUNICODE_STRING InitialCommand)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
SmpInit(IN PUNICODE_STRING InitialCommand,OUT PHANDLE ProcessHandle)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