xref: /reactos/sdk/lib/rtl/sysvol.c (revision cc439606)
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 static NTSTATUS
416 RtlpSysVolTakeOwnership(IN PUNICODE_STRING DirectoryPath,
417                         IN PSECURITY_DESCRIPTOR SecurityDescriptor)
418 {
419     TOKEN_PRIVILEGES TokenPrivileges;
420     OBJECT_ATTRIBUTES ObjectAttributes;
421     SECURITY_DESCRIPTOR AbsSD;
422     PSID AdminSid = NULL;
423     IO_STATUS_BLOCK IoStatusBlock;
424     BOOLEAN TokenEnabled = FALSE;
425     HANDLE hToken = NULL;
426     HANDLE hDirectory = NULL;
427     NTSTATUS Status;
428     ULONG ReturnLength;
429 
430     Status = ZwOpenProcessToken(NtCurrentProcess(),
431                                 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
432                                 &hToken);
433     if (!NT_SUCCESS(Status))
434     {
435         goto Cleanup;
436     }
437 
438     /* attempt to enable the SE_TAKE_OWNERSHIP_PRIVILEGE privilege */
439     TokenPrivileges.PrivilegeCount = 1;
440     TokenPrivileges.Privileges[0].Luid.LowPart = SE_TAKE_OWNERSHIP_PRIVILEGE;
441     TokenPrivileges.Privileges[0].Luid.HighPart = 0;
442     TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
443     Status = ZwAdjustPrivilegesToken(hToken,
444                                      FALSE,
445                                      &TokenPrivileges,
446                                      sizeof(TokenPrivileges),
447                                      &TokenPrivileges,
448                                      &ReturnLength);
449     if (!NT_SUCCESS(Status))
450     {
451         goto Cleanup;
452     }
453     TokenEnabled = (TokenPrivileges.PrivilegeCount != 0);
454 
455     /* open the directory */
456     InitializeObjectAttributes(&ObjectAttributes,
457                                DirectoryPath,
458                                0,
459                                NULL,
460                                SecurityDescriptor);
461 
462     Status = ZwOpenFile(&hDirectory,
463                         SYNCHRONIZE | WRITE_OWNER,
464                         &ObjectAttributes,
465                         &IoStatusBlock,
466                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
467                         FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
468     if (!NT_SUCCESS(Status))
469     {
470         goto Cleanup;
471     }
472 
473     /* create the Administrators SID */
474     Status = RtlAllocateAndInitializeSid(&LocalSystemAuthority,
475                                          2,
476                                          SECURITY_BUILTIN_DOMAIN_RID,
477                                          DOMAIN_ALIAS_RID_ADMINS,
478                                          0,
479                                          0,
480                                          0,
481                                          0,
482                                          0,
483                                          0,
484                                          &AdminSid);
485     if (!NT_SUCCESS(Status))
486     {
487         goto Cleanup;
488     }
489 
490     /* create the security descriptor */
491     Status = RtlCreateSecurityDescriptor(&AbsSD,
492                                          SECURITY_DESCRIPTOR_REVISION);
493     if (!NT_SUCCESS(Status))
494     {
495         goto Cleanup;
496     }
497 
498     Status = RtlSetOwnerSecurityDescriptor(&AbsSD,
499                                            AdminSid,
500                                            FALSE);
501     if (!NT_SUCCESS(Status))
502     {
503         goto Cleanup;
504     }
505 
506     /* attempt to take ownership */
507     Status = ZwSetSecurityObject(hDirectory,
508                                  OWNER_SECURITY_INFORMATION,
509                                  &AbsSD);
510 
511 Cleanup:
512     if (TokenEnabled)
513     {
514         ZwAdjustPrivilegesToken(hToken,
515                                 FALSE,
516                                 &TokenPrivileges,
517                                 0,
518                                 NULL,
519                                 NULL);
520     }
521 
522     if (AdminSid != NULL)
523     {
524         RtlFreeSid(AdminSid);
525     }
526 
527     if (hDirectory != NULL)
528     {
529         ZwClose(hDirectory);
530     }
531 
532     if (hToken != NULL)
533     {
534         ZwClose(hToken);
535     }
536 
537     return Status;
538 }
539 
540 /*
541 * @implemented
542 */
543 NTSTATUS
544 NTAPI
545 RtlCreateSystemVolumeInformationFolder(IN PUNICODE_STRING VolumeRootPath)
546 {
547     OBJECT_ATTRIBUTES ObjectAttributes;
548     IO_STATUS_BLOCK IoStatusBlock;
549     HANDLE hDirectory;
550     UNICODE_STRING DirectoryName, NewPath;
551     ULONG PathLen;
552     PISECURITY_DESCRIPTOR SecurityDescriptor = NULL;
553     PSID SystemSid = NULL;
554     BOOLEAN AddSep = FALSE;
555     NTSTATUS Status;
556 
557     PAGED_CODE_RTL();
558 
559     RtlInitUnicodeString(&DirectoryName,
560                          L"System Volume Information");
561 
562     PathLen = VolumeRootPath->Length + DirectoryName.Length;
563 
564     /* make sure we don't overflow while appending the strings */
565     if (PathLen > 0xFFFC)
566     {
567         return STATUS_INVALID_PARAMETER;
568     }
569 
570     if (VolumeRootPath->Buffer[(VolumeRootPath->Length / sizeof(WCHAR)) - 1] != L'\\')
571     {
572         AddSep = TRUE;
573         PathLen += sizeof(WCHAR);
574     }
575 
576     /* allocate the new string */
577     NewPath.MaximumLength = (USHORT)PathLen + sizeof(WCHAR);
578     NewPath.Buffer = RtlpAllocateStringMemory(NewPath.MaximumLength,
579                                               TAG_USTR);
580     if (NewPath.Buffer == NULL)
581     {
582         return STATUS_INSUFFICIENT_RESOURCES;
583     }
584 
585     /* create the new path string */
586     NewPath.Length = VolumeRootPath->Length;
587     RtlCopyMemory(NewPath.Buffer,
588                   VolumeRootPath->Buffer,
589                   NewPath.Length);
590     if (AddSep)
591     {
592         NewPath.Buffer[NewPath.Length / sizeof(WCHAR)] = L'\\';
593         NewPath.Length += sizeof(WCHAR);
594     }
595     RtlCopyMemory(NewPath.Buffer + (NewPath.Length / sizeof(WCHAR)),
596                   DirectoryName.Buffer,
597                   DirectoryName.Length);
598     NewPath.Length += DirectoryName.Length;
599     NewPath.Buffer[NewPath.Length / sizeof(WCHAR)] = L'\0';
600 
601     ASSERT(NewPath.Length == PathLen);
602     ASSERT(NewPath.Length == NewPath.MaximumLength - sizeof(WCHAR));
603 
604     /* create the security descriptor for the new directory */
605     Status = RtlpSysVolCreateSecurityDescriptor(&SecurityDescriptor,
606                                                 &SystemSid);
607     if (NT_SUCCESS(Status))
608     {
609         /* create or open the directory */
610         InitializeObjectAttributes(&ObjectAttributes,
611                                    &NewPath,
612                                    0,
613                                    NULL,
614                                    SecurityDescriptor);
615 
616         Status = ZwCreateFile(&hDirectory,
617                               SYNCHRONIZE | WRITE_OWNER | WRITE_DAC | READ_CONTROL,
618                               &ObjectAttributes,
619                               &IoStatusBlock,
620                               NULL,
621                               FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
622                               FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
623                               FILE_OPEN_IF,
624                               FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
625                               NULL,
626                               0);
627         if (!NT_SUCCESS(Status))
628         {
629             Status = RtlpSysVolTakeOwnership(&NewPath,
630                                              SecurityDescriptor);
631 
632             if (NT_SUCCESS(Status))
633             {
634                 /* successfully took ownership, attempt to open it */
635                 Status = ZwCreateFile(&hDirectory,
636                                       SYNCHRONIZE | WRITE_OWNER | WRITE_DAC | READ_CONTROL,
637                                       &ObjectAttributes,
638                                       &IoStatusBlock,
639                                       NULL,
640                                       FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
641                                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
642                                       FILE_OPEN_IF,
643                                       FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
644                                       NULL,
645                                       0);
646             }
647         }
648 
649         if (NT_SUCCESS(Status))
650         {
651             /* check security now and adjust it if neccessary */
652             Status = RtlpSysVolCheckOwnerAndSecurity(hDirectory,
653                                                      SecurityDescriptor);
654             ZwClose(hDirectory);
655         }
656 
657         /* free allocated memory */
658         ASSERT(SecurityDescriptor != NULL);
659         ASSERT(SecurityDescriptor->Dacl != NULL);
660 
661         RtlpFreeMemory(SecurityDescriptor->Dacl,
662                        'cAeS');
663         RtlpFreeMemory(SecurityDescriptor,
664                        'dSeS');
665 
666         RtlFreeSid(SystemSid);
667     }
668 
669     RtlpFreeStringMemory(NewPath.Buffer,
670                          TAG_USTR);
671     return Status;
672 }
673 
674 /* EOF */
675