xref: /reactos/sdk/lib/rtl/sysvol.c (revision 84344399)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * PURPOSE:         Boot Data implementation
5  * FILE:            lib/rtl/bootdata.c
6  * PROGRAMMERS:
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <rtl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* FUNCTIONS *****************************************************************/
16 
17 static SID_IDENTIFIER_AUTHORITY LocalSystemAuthority = {SECURITY_NT_AUTHORITY};
18 
19 static NTSTATUS
20 RtlpSysVolCreateSecurityDescriptor(OUT PISECURITY_DESCRIPTOR *SecurityDescriptor,
21                                    OUT PSID *SystemSid)
22 {
23     PSECURITY_DESCRIPTOR AbsSD = NULL;
24     PSID LocalSystemSid = NULL;
25     PACL Dacl = NULL;
26     ULONG DaclSize;
27     NTSTATUS Status;
28 
29     /* create the local SYSTEM SID */
30     Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority,
31                                          1,
32                                          SECURITY_LOCAL_SYSTEM_RID,
33                                          0,
34                                          0,
35                                          0,
36                                          0,
37                                          0,
38                                          0,
39                                          0,
40                                          &LocalSystemSid);
41     if (!NT_SUCCESS(Status))
42     {
43         return Status;
44     }
45 
46     /* allocate and initialize the security descriptor */
47     AbsSD = RtlpAllocateMemory(sizeof(SECURITY_DESCRIPTOR),
48                                'dSeS');
49     if (AbsSD == NULL)
50     {
51         Status = STATUS_NO_MEMORY;
52         goto Cleanup;
53     }
54 
55     Status = RtlCreateSecurityDescriptor(AbsSD,
56                                          SECURITY_DESCRIPTOR_REVISION);
57     if (!NT_SUCCESS(Status))
58     {
59         goto Cleanup;
60     }
61 
62     /* allocate and create the DACL */
63     DaclSize = sizeof(ACL) + sizeof(ACE) +
64                RtlLengthSid(LocalSystemSid);
65     Dacl = RtlpAllocateMemory(DaclSize,
66                               'cAeS');
67     if (Dacl == NULL)
68     {
69         Status = STATUS_NO_MEMORY;
70         goto Cleanup;
71     }
72 
73     Status = RtlCreateAcl(Dacl,
74                           DaclSize,
75                           ACL_REVISION);
76     if (!NT_SUCCESS(Status))
77     {
78         goto Cleanup;
79     }
80 
81     Status = RtlAddAccessAllowedAceEx(Dacl,
82                                       ACL_REVISION,
83                                       OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
84                                       STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
85                                       LocalSystemSid);
86     if (!NT_SUCCESS(Status))
87     {
88         goto Cleanup;
89     }
90 
91     /* set the DACL in the security descriptor */
92     Status = RtlSetDaclSecurityDescriptor(AbsSD,
93                                           TRUE,
94                                           Dacl,
95                                           FALSE);
96 
97     /* all done */
98     if (NT_SUCCESS(Status))
99     {
100         *SecurityDescriptor = AbsSD;
101         *SystemSid = LocalSystemSid;
102     }
103     else
104     {
105 Cleanup:
106         if (LocalSystemSid != NULL)
107         {
108             RtlFreeSid(LocalSystemSid);
109         }
110 
111         if (Dacl != NULL)
112         {
113             RtlpFreeMemory(Dacl,
114                            'cAeS');
115         }
116 
117         if (AbsSD != NULL)
118         {
119             RtlpFreeMemory(AbsSD,
120                            'dSeS');
121         }
122     }
123 
124     return Status;
125 }
126 
127 static NTSTATUS
128 RtlpSysVolCheckOwnerAndSecurity(IN HANDLE DirectoryHandle,
129                                 IN PISECURITY_DESCRIPTOR SecurityDescriptor)
130 {
131     PSECURITY_DESCRIPTOR RelSD = NULL;
132     PSECURITY_DESCRIPTOR NewRelSD = NULL;
133     PSECURITY_DESCRIPTOR AbsSD = NULL;
134 #ifdef _WIN64
135     BOOLEAN AbsSDAllocated = FALSE;
136 #endif
137     PSID AdminSid = NULL;
138     PSID LocalSystemSid = NULL;
139     ULONG DescriptorSize;
140     ULONG AbsSDSize, RelSDSize = 0;
141     PACL Dacl;
142     BOOLEAN DaclPresent, DaclDefaulted;
143     PSID OwnerSid;
144     BOOLEAN OwnerDefaulted;
145     ULONG AceIndex;
146     PACE Ace = NULL;
147     NTSTATUS Status;
148 
149     /* find out how much memory we need to allocate for the self-relative
150        descriptor we're querying */
151     Status = ZwQuerySecurityObject(DirectoryHandle,
152                                    OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
153                                    NULL,
154                                    0,
155                                    &DescriptorSize);
156     if (Status != STATUS_BUFFER_TOO_SMALL)
157     {
158         /* looks like the FS doesn't support security... return success */
159         Status = STATUS_SUCCESS;
160         goto Cleanup;
161     }
162 
163     /* allocate enough memory for the security descriptor */
164     RelSD = RtlpAllocateMemory(DescriptorSize,
165                                'dSeS');
166     if (RelSD == NULL)
167     {
168         Status = STATUS_NO_MEMORY;
169         goto Cleanup;
170     }
171 
172     /* query the self-relative security descriptor */
173     Status = ZwQuerySecurityObject(DirectoryHandle,
174                                    OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
175                                    RelSD,
176                                    DescriptorSize,
177                                    &DescriptorSize);
178     if (!NT_SUCCESS(Status))
179     {
180         /* FIXME - handle the case where someone else modified the owner and/or
181                    DACL while we allocated memory. But that should be *very*
182                    unlikely.... */
183         goto Cleanup;
184     }
185 
186     /* query the owner and DACL from the descriptor */
187     Status = RtlGetOwnerSecurityDescriptor(RelSD,
188                                            &OwnerSid,
189                                            &OwnerDefaulted);
190     if (!NT_SUCCESS(Status))
191     {
192         goto Cleanup;
193     }
194 
195     Status = RtlGetDaclSecurityDescriptor(RelSD,
196                                           &DaclPresent,
197                                           &Dacl,
198                                           &DaclDefaulted);
199     if (!NT_SUCCESS(Status))
200     {
201         goto Cleanup;
202     }
203 
204     /* create the Administrators SID */
205     Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority,
206                                          2,
207                                          SECURITY_BUILTIN_DOMAIN_RID,
208                                          DOMAIN_ALIAS_RID_ADMINS,
209                                          0,
210                                          0,
211                                          0,
212                                          0,
213                                          0,
214                                          0,
215                                          &AdminSid);
216     if (!NT_SUCCESS(Status))
217     {
218         goto Cleanup;
219     }
220 
221     /* create the local SYSTEM SID */
222     Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority,
223                                          1,
224                                          SECURITY_LOCAL_SYSTEM_RID,
225                                          0,
226                                          0,
227                                          0,
228                                          0,
229                                          0,
230                                          0,
231                                          0,
232                                          &LocalSystemSid);
233     if (!NT_SUCCESS(Status))
234     {
235         goto Cleanup;
236     }
237 
238     /* check if the Administrators are the owner and at least a not-NULL DACL
239        is present */
240     if (OwnerSid != NULL &&
241         RtlEqualSid(OwnerSid,
242                     AdminSid) &&
243         DaclPresent && Dacl != NULL)
244     {
245         /* check the DACL for an Allowed ACE for the SYSTEM account */
246         AceIndex = 0;
247         do
248         {
249             Status = RtlGetAce(Dacl,
250                                AceIndex++,
251                                (PVOID*)&Ace);
252             if (!NT_SUCCESS(Status))
253             {
254                 Ace = NULL;
255             }
256             else if (Ace != NULL && Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
257             {
258                 /* check if the the ACE is a set of allowed permissions for the
259                    local SYSTEM account */
260                 if (RtlEqualSid((PSID)(Ace + 1),
261                                 LocalSystemSid))
262                 {
263                     /* check if the ACE is inherited by noncontainer and
264                        container objects, if not attempt to change that */
265                     if (!(Ace->Header.AceFlags & OBJECT_INHERIT_ACE) ||
266                         !(Ace->Header.AceFlags & CONTAINER_INHERIT_ACE))
267                     {
268                         Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
269                         Status = ZwSetSecurityObject(DirectoryHandle,
270                                                      DACL_SECURITY_INFORMATION,
271                                                      RelSD);
272                     }
273                     else
274                     {
275                         /* all done, we have access */
276                         Status = STATUS_SUCCESS;
277                     }
278 
279                     goto Cleanup;
280                 }
281             }
282         } while (Ace != NULL);
283     }
284 
285     AbsSDSize = DescriptorSize;
286 
287     /* because we need to change any existing data we need to convert it to
288        an absolute security descriptor first */
289     Status = RtlSelfRelativeToAbsoluteSD2(RelSD,
290                                           &AbsSDSize);
291 #ifdef _WIN64
292     if (Status == STATUS_BUFFER_TOO_SMALL)
293     {
294         /* this error code can only be returned on 64 bit builds because
295            the size of an absolute security descriptor is greater than the
296            size of a self-relative security descriptor */
297         ASSERT(AbsSDSize > DescriptorSize);
298 
299         AbsSD = RtlpAllocateMemory(DescriptorSize,
300                                    'dSeS');
301         if (AbsSD == NULL)
302         {
303             Status = STATUS_NO_MEMORY;
304             goto Cleanup;
305         }
306 
307         AbsSDAllocated = TRUE;
308 
309         /* make a raw copy of the self-relative descriptor */
310         RtlCopyMemory(AbsSD,
311                       RelSD,
312                       DescriptorSize);
313 
314         /* finally convert it */
315         Status = RtlSelfRelativeToAbsoluteSD2(AbsSD,
316                                               &AbsSDSize);
317     }
318     else
319 #endif
320     {
321         AbsSD = RelSD;
322     }
323 
324     if (!NT_SUCCESS(Status))
325     {
326         goto Cleanup;
327     }
328 
329     /* set the owner SID */
330     Status = RtlSetOwnerSecurityDescriptor(AbsSD,
331                                            AdminSid,
332                                            FALSE);
333     if (!NT_SUCCESS(Status))
334     {
335         goto Cleanup;
336     }
337 
338     /* set the DACL in the security descriptor */
339     Status = RtlSetDaclSecurityDescriptor(AbsSD,
340                                           TRUE,
341                                           SecurityDescriptor->Dacl,
342                                           FALSE);
343     if (!NT_SUCCESS(Status))
344     {
345         goto Cleanup;
346     }
347 
348     /* convert it back to a self-relative descriptor, find out how much
349        memory we need */
350     Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
351                                          NULL,
352                                          &RelSDSize);
353     if (Status != STATUS_BUFFER_TOO_SMALL)
354     {
355         goto Cleanup;
356     }
357 
358     /* allocate enough memory for the new self-relative descriptor */
359     NewRelSD = RtlpAllocateMemory(RelSDSize,
360                                   'dSeS');
361     if (NewRelSD == NULL)
362     {
363         Status = STATUS_NO_MEMORY;
364         goto Cleanup;
365     }
366 
367     /* convert the security descriptor to self-relative format */
368     Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
369                                          NewRelSD,
370                                          &RelSDSize);
371     if (Status == STATUS_BUFFER_TOO_SMALL)
372     {
373         goto Cleanup;
374     }
375 
376     /* finally attempt to change the security information */
377     Status = ZwSetSecurityObject(DirectoryHandle,
378                                  OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
379                                  NewRelSD);
380 
381 Cleanup:
382     if (AdminSid != NULL)
383     {
384         RtlFreeSid(AdminSid);
385     }
386 
387     if (LocalSystemSid != NULL)
388     {
389         RtlFreeSid(LocalSystemSid);
390     }
391 
392     if (RelSD != NULL)
393     {
394         RtlpFreeMemory(RelSD,
395                        'dSeS');
396     }
397 
398     if (NewRelSD != NULL)
399     {
400         RtlpFreeMemory(NewRelSD,
401                        'dSeS');
402     }
403 
404 #ifdef _WIN64
405     if (AbsSDAllocated)
406     {
407         RtlpFreeMemory(AbsSD,
408                        'dSeS');
409     }
410 #endif
411 
412     return Status;
413 }
414 
415 _Must_inspect_result_
416 static
417 NTSTATUS
418 RtlpSysVolTakeOwnership(IN PUNICODE_STRING DirectoryPath,
419                         IN PSECURITY_DESCRIPTOR SecurityDescriptor)
420 {
421     TOKEN_PRIVILEGES TokenPrivileges;
422     OBJECT_ATTRIBUTES ObjectAttributes;
423     SECURITY_DESCRIPTOR AbsSD;
424     PSID AdminSid = NULL;
425     IO_STATUS_BLOCK IoStatusBlock;
426     BOOLEAN TokenEnabled = FALSE;
427     HANDLE hToken = NULL;
428     HANDLE hDirectory = NULL;
429     NTSTATUS Status;
430     ULONG ReturnLength;
431 
432     Status = ZwOpenProcessToken(NtCurrentProcess(),
433                                 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
434                                 &hToken);
435     if (!NT_SUCCESS(Status))
436     {
437         goto Cleanup;
438     }
439 
440     /* attempt to enable the SE_TAKE_OWNERSHIP_PRIVILEGE privilege */
441     TokenPrivileges.PrivilegeCount = 1;
442     TokenPrivileges.Privileges[0].Luid.LowPart = SE_TAKE_OWNERSHIP_PRIVILEGE;
443     TokenPrivileges.Privileges[0].Luid.HighPart = 0;
444     TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
445     Status = ZwAdjustPrivilegesToken(hToken,
446                                      FALSE,
447                                      &TokenPrivileges,
448                                      sizeof(TokenPrivileges),
449                                      &TokenPrivileges,
450                                      &ReturnLength);
451     if (!NT_SUCCESS(Status))
452     {
453         goto Cleanup;
454     }
455     TokenEnabled = (TokenPrivileges.PrivilegeCount != 0);
456 
457     /* open the directory */
458     InitializeObjectAttributes(&ObjectAttributes,
459                                DirectoryPath,
460                                0,
461                                NULL,
462                                SecurityDescriptor);
463 
464     Status = ZwOpenFile(&hDirectory,
465                         SYNCHRONIZE | WRITE_OWNER,
466                         &ObjectAttributes,
467                         &IoStatusBlock,
468                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
469                         FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
470     if (!NT_SUCCESS(Status))
471     {
472         goto Cleanup;
473     }
474 
475     /* create the Administrators SID */
476     Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority,
477                                          2,
478                                          SECURITY_BUILTIN_DOMAIN_RID,
479                                          DOMAIN_ALIAS_RID_ADMINS,
480                                          0,
481                                          0,
482                                          0,
483                                          0,
484                                          0,
485                                          0,
486                                          &AdminSid);
487     if (!NT_SUCCESS(Status))
488     {
489         goto Cleanup;
490     }
491 
492     /* create the security descriptor */
493     Status = RtlCreateSecurityDescriptor(&AbsSD,
494                                          SECURITY_DESCRIPTOR_REVISION);
495     if (!NT_SUCCESS(Status))
496     {
497         goto Cleanup;
498     }
499 
500     Status = RtlSetOwnerSecurityDescriptor(&AbsSD,
501                                            AdminSid,
502                                            FALSE);
503     if (!NT_SUCCESS(Status))
504     {
505         goto Cleanup;
506     }
507 
508     /* attempt to take ownership */
509     Status = ZwSetSecurityObject(hDirectory,
510                                  OWNER_SECURITY_INFORMATION,
511                                  &AbsSD);
512 
513 Cleanup:
514     if (TokenEnabled)
515     {
516         /* Disable privileges that we had to enable, whetever the result was. */
517         NTSTATUS Status2 = ZwAdjustPrivilegesToken(hToken,
518                                                    FALSE,
519                                                    &TokenPrivileges,
520                                                    0,
521                                                    NULL,
522                                                    NULL);
523         /* This must succeed */
524         ASSERT(NT_SUCCESS(Status2));
525         (void)Status2;
526     }
527 
528     if (AdminSid != NULL)
529     {
530         RtlFreeSid(AdminSid);
531     }
532 
533     if (hDirectory != NULL)
534     {
535         ZwClose(hDirectory);
536     }
537 
538     if (hToken != NULL)
539     {
540         ZwClose(hToken);
541     }
542 
543     return Status;
544 }
545 
546 /*
547 * @implemented
548 */
549 NTSTATUS
550 NTAPI
551 RtlCreateSystemVolumeInformationFolder(IN PUNICODE_STRING VolumeRootPath)
552 {
553     OBJECT_ATTRIBUTES ObjectAttributes;
554     IO_STATUS_BLOCK IoStatusBlock;
555     HANDLE hDirectory;
556     UNICODE_STRING DirectoryName, NewPath;
557     ULONG PathLen;
558     PISECURITY_DESCRIPTOR SecurityDescriptor = NULL;
559     PSID SystemSid = NULL;
560     BOOLEAN AddSep = FALSE;
561     NTSTATUS Status;
562 
563     PAGED_CODE_RTL();
564 
565     RtlInitUnicodeString(&DirectoryName,
566                          L"System Volume Information");
567 
568     PathLen = VolumeRootPath->Length + DirectoryName.Length;
569 
570     /* make sure we don't overflow while appending the strings */
571     if (PathLen > 0xFFFC)
572     {
573         return STATUS_INVALID_PARAMETER;
574     }
575 
576     if (VolumeRootPath->Buffer[(VolumeRootPath->Length / sizeof(WCHAR)) - 1] != L'\\')
577     {
578         AddSep = TRUE;
579         PathLen += sizeof(WCHAR);
580     }
581 
582     /* allocate the new string */
583     NewPath.MaximumLength = (USHORT)PathLen + sizeof(WCHAR);
584     NewPath.Buffer = RtlpAllocateStringMemory(NewPath.MaximumLength,
585                                               TAG_USTR);
586     if (NewPath.Buffer == NULL)
587     {
588         return STATUS_INSUFFICIENT_RESOURCES;
589     }
590 
591     /* create the new path string */
592     NewPath.Length = VolumeRootPath->Length;
593     RtlCopyMemory(NewPath.Buffer,
594                   VolumeRootPath->Buffer,
595                   NewPath.Length);
596     if (AddSep)
597     {
598         NewPath.Buffer[NewPath.Length / sizeof(WCHAR)] = L'\\';
599         NewPath.Length += sizeof(WCHAR);
600     }
601     RtlCopyMemory(NewPath.Buffer + (NewPath.Length / sizeof(WCHAR)),
602                   DirectoryName.Buffer,
603                   DirectoryName.Length);
604     NewPath.Length += DirectoryName.Length;
605     NewPath.Buffer[NewPath.Length / sizeof(WCHAR)] = L'\0';
606 
607     ASSERT(NewPath.Length == PathLen);
608     ASSERT(NewPath.Length == NewPath.MaximumLength - sizeof(WCHAR));
609 
610     /* create the security descriptor for the new directory */
611     Status = RtlpSysVolCreateSecurityDescriptor(&SecurityDescriptor,
612                                                 &SystemSid);
613     if (NT_SUCCESS(Status))
614     {
615         /* create or open the directory */
616         InitializeObjectAttributes(&ObjectAttributes,
617                                    &NewPath,
618                                    0,
619                                    NULL,
620                                    SecurityDescriptor);
621 
622         Status = ZwCreateFile(&hDirectory,
623                               SYNCHRONIZE | WRITE_OWNER | WRITE_DAC | READ_CONTROL,
624                               &ObjectAttributes,
625                               &IoStatusBlock,
626                               NULL,
627                               FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
628                               FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
629                               FILE_OPEN_IF,
630                               FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
631                               NULL,
632                               0);
633         if (!NT_SUCCESS(Status))
634         {
635             Status = RtlpSysVolTakeOwnership(&NewPath,
636                                              SecurityDescriptor);
637 
638             if (NT_SUCCESS(Status))
639             {
640                 /* successfully took ownership, attempt to open it */
641                 Status = ZwCreateFile(&hDirectory,
642                                       SYNCHRONIZE | WRITE_OWNER | WRITE_DAC | READ_CONTROL,
643                                       &ObjectAttributes,
644                                       &IoStatusBlock,
645                                       NULL,
646                                       FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
647                                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
648                                       FILE_OPEN_IF,
649                                       FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
650                                       NULL,
651                                       0);
652             }
653         }
654 
655         if (NT_SUCCESS(Status))
656         {
657             /* check security now and adjust it if neccessary */
658             Status = RtlpSysVolCheckOwnerAndSecurity(hDirectory,
659                                                      SecurityDescriptor);
660             ZwClose(hDirectory);
661         }
662 
663         /* free allocated memory */
664         ASSERT(SecurityDescriptor != NULL);
665         ASSERT(SecurityDescriptor->Dacl != NULL);
666 
667         RtlpFreeMemory(SecurityDescriptor->Dacl,
668                        'cAeS');
669         RtlpFreeMemory(SecurityDescriptor,
670                        'dSeS');
671 
672         RtlFreeSid(SystemSid);
673     }
674 
675     RtlpFreeStringMemory(NewPath.Buffer,
676                          TAG_USTR);
677     return Status;
678 }
679 
680 /* EOF */
681