xref: /reactos/ntoskrnl/se/acl.c (revision 8b75dce4)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Access control lists (ACLs) implementation
5  * COPYRIGHT:   Copyright David Welch <welch@cwcom.net>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <debug.h>
13 
14 /* GLOBALS ********************************************************************/
15 
16 PACL SePublicDefaultDacl = NULL;
17 PACL SeSystemDefaultDacl = NULL;
18 PACL SePublicDefaultUnrestrictedDacl = NULL;
19 PACL SePublicOpenDacl = NULL;
20 PACL SePublicOpenUnrestrictedDacl = NULL;
21 PACL SeUnrestrictedDacl = NULL;
22 PACL SeSystemAnonymousLogonDacl = NULL;
23 
24 /* FUNCTIONS ******************************************************************/
25 
26 /**
27  * @brief
28  * Initializes known discretionary access control lists in the system upon
29  * kernel and Executive initialization procedure.
30  *
31  * @return
32  * Returns TRUE if all the DACLs have been successfully initialized,
33  * FALSE otherwise.
34  */
35 CODE_SEG("INIT")
36 BOOLEAN
37 NTAPI
SepInitDACLs(VOID)38 SepInitDACLs(VOID)
39 {
40     ULONG AclLength;
41 
42     /* create PublicDefaultDacl */
43     AclLength = sizeof(ACL) +
44                 (sizeof(ACE) + RtlLengthSid(SeWorldSid)) +
45                 (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
46                 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid));
47 
48     SePublicDefaultDacl = ExAllocatePoolWithTag(PagedPool,
49                                                 AclLength,
50                                                 TAG_ACL);
51     if (SePublicDefaultDacl == NULL)
52         return FALSE;
53 
54     RtlCreateAcl(SePublicDefaultDacl,
55                  AclLength,
56                  ACL_REVISION);
57 
58     RtlAddAccessAllowedAce(SePublicDefaultDacl,
59                            ACL_REVISION,
60                            GENERIC_EXECUTE,
61                            SeWorldSid);
62 
63     RtlAddAccessAllowedAce(SePublicDefaultDacl,
64                            ACL_REVISION,
65                            GENERIC_ALL,
66                            SeLocalSystemSid);
67 
68     RtlAddAccessAllowedAce(SePublicDefaultDacl,
69                            ACL_REVISION,
70                            GENERIC_ALL,
71                            SeAliasAdminsSid);
72 
73     /* create PublicDefaultUnrestrictedDacl */
74     AclLength = sizeof(ACL) +
75                 (sizeof(ACE) + RtlLengthSid(SeWorldSid)) +
76                 (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
77                 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid)) +
78                 (sizeof(ACE) + RtlLengthSid(SeRestrictedCodeSid));
79 
80     SePublicDefaultUnrestrictedDacl = ExAllocatePoolWithTag(PagedPool,
81                                                             AclLength,
82                                                             TAG_ACL);
83     if (SePublicDefaultUnrestrictedDacl == NULL)
84         return FALSE;
85 
86     RtlCreateAcl(SePublicDefaultUnrestrictedDacl,
87                  AclLength,
88                  ACL_REVISION);
89 
90     RtlAddAccessAllowedAce(SePublicDefaultUnrestrictedDacl,
91                            ACL_REVISION,
92                            GENERIC_EXECUTE,
93                            SeWorldSid);
94 
95     RtlAddAccessAllowedAce(SePublicDefaultUnrestrictedDacl,
96                            ACL_REVISION,
97                            GENERIC_ALL,
98                            SeLocalSystemSid);
99 
100     RtlAddAccessAllowedAce(SePublicDefaultUnrestrictedDacl,
101                            ACL_REVISION,
102                            GENERIC_ALL,
103                            SeAliasAdminsSid);
104 
105     RtlAddAccessAllowedAce(SePublicDefaultUnrestrictedDacl,
106                            ACL_REVISION,
107                            GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL,
108                            SeRestrictedCodeSid);
109 
110     /* create PublicOpenDacl */
111     AclLength = sizeof(ACL) +
112                 (sizeof(ACE) + RtlLengthSid(SeWorldSid)) +
113                 (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
114                 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid));
115 
116     SePublicOpenDacl = ExAllocatePoolWithTag(PagedPool,
117                                              AclLength,
118                                              TAG_ACL);
119     if (SePublicOpenDacl == NULL)
120         return FALSE;
121 
122     RtlCreateAcl(SePublicOpenDacl,
123                  AclLength,
124                  ACL_REVISION);
125 
126     RtlAddAccessAllowedAce(SePublicOpenDacl,
127                            ACL_REVISION,
128                            GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
129                            SeWorldSid);
130 
131     RtlAddAccessAllowedAce(SePublicOpenDacl,
132                            ACL_REVISION,
133                            GENERIC_ALL,
134                            SeLocalSystemSid);
135 
136     RtlAddAccessAllowedAce(SePublicOpenDacl,
137                            ACL_REVISION,
138                            GENERIC_ALL,
139                            SeAliasAdminsSid);
140 
141     /* create PublicOpenUnrestrictedDacl */
142     AclLength = sizeof(ACL) +
143                 (sizeof(ACE) + RtlLengthSid(SeWorldSid)) +
144                 (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
145                 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid)) +
146                 (sizeof(ACE) + RtlLengthSid(SeRestrictedCodeSid));
147 
148     SePublicOpenUnrestrictedDacl = ExAllocatePoolWithTag(PagedPool,
149                                                          AclLength,
150                                                          TAG_ACL);
151     if (SePublicOpenUnrestrictedDacl == NULL)
152         return FALSE;
153 
154     RtlCreateAcl(SePublicOpenUnrestrictedDacl,
155                  AclLength,
156                  ACL_REVISION);
157 
158     RtlAddAccessAllowedAce(SePublicOpenUnrestrictedDacl,
159                            ACL_REVISION,
160                            GENERIC_ALL,
161                            SeWorldSid);
162 
163     RtlAddAccessAllowedAce(SePublicOpenUnrestrictedDacl,
164                            ACL_REVISION,
165                            GENERIC_ALL,
166                            SeLocalSystemSid);
167 
168     RtlAddAccessAllowedAce(SePublicOpenUnrestrictedDacl,
169                            ACL_REVISION,
170                            GENERIC_ALL,
171                            SeAliasAdminsSid);
172 
173     RtlAddAccessAllowedAce(SePublicOpenUnrestrictedDacl,
174                            ACL_REVISION,
175                            GENERIC_READ | GENERIC_EXECUTE,
176                            SeRestrictedCodeSid);
177 
178     /* create SystemDefaultDacl */
179     AclLength = sizeof(ACL) +
180                 (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
181                 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid));
182 
183     SeSystemDefaultDacl = ExAllocatePoolWithTag(PagedPool,
184                                                 AclLength,
185                                                 TAG_ACL);
186     if (SeSystemDefaultDacl == NULL)
187         return FALSE;
188 
189     RtlCreateAcl(SeSystemDefaultDacl,
190                  AclLength,
191                  ACL_REVISION);
192 
193     RtlAddAccessAllowedAce(SeSystemDefaultDacl,
194                            ACL_REVISION,
195                            GENERIC_ALL,
196                            SeLocalSystemSid);
197 
198     RtlAddAccessAllowedAce(SeSystemDefaultDacl,
199                            ACL_REVISION,
200                            GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL,
201                            SeAliasAdminsSid);
202 
203     /* create UnrestrictedDacl */
204     AclLength = sizeof(ACL) +
205                 (sizeof(ACE) + RtlLengthSid(SeWorldSid)) +
206                 (sizeof(ACE) + RtlLengthSid(SeRestrictedCodeSid));
207 
208     SeUnrestrictedDacl = ExAllocatePoolWithTag(PagedPool,
209                                                AclLength,
210                                                TAG_ACL);
211     if (SeUnrestrictedDacl == NULL)
212         return FALSE;
213 
214     RtlCreateAcl(SeUnrestrictedDacl,
215                  AclLength,
216                  ACL_REVISION);
217 
218     RtlAddAccessAllowedAce(SeUnrestrictedDacl,
219                            ACL_REVISION,
220                            GENERIC_ALL,
221                            SeWorldSid);
222 
223     RtlAddAccessAllowedAce(SeUnrestrictedDacl,
224                            ACL_REVISION,
225                            GENERIC_READ | GENERIC_EXECUTE,
226                            SeRestrictedCodeSid);
227 
228     /* create SystemAnonymousLogonDacl */
229     AclLength = sizeof(ACL) +
230                 (sizeof(ACE) + RtlLengthSid(SeWorldSid)) +
231                 (sizeof(ACE) + RtlLengthSid(SeAnonymousLogonSid));
232 
233     SeSystemAnonymousLogonDacl = ExAllocatePoolWithTag(PagedPool,
234                                                        AclLength,
235                                                        TAG_ACL);
236     if (SeSystemAnonymousLogonDacl == NULL)
237         return FALSE;
238 
239     RtlCreateAcl(SeSystemAnonymousLogonDacl,
240                  AclLength,
241                  ACL_REVISION);
242 
243     RtlAddAccessAllowedAce(SeSystemAnonymousLogonDacl,
244                            ACL_REVISION,
245                            GENERIC_ALL,
246                            SeWorldSid);
247 
248     RtlAddAccessAllowedAce(SeSystemAnonymousLogonDacl,
249                            ACL_REVISION,
250                            GENERIC_ALL,
251                            SeAnonymousLogonSid);
252 
253     return TRUE;
254 }
255 
256 /**
257  * @brief
258  * Allocates a discretionary access control list based on certain properties
259  * of a regular and primary access tokens.
260  *
261  * @param[in] Token
262  * An access token.
263  *
264  * @param[in] PrimaryToken
265  * A primary access token.
266  *
267  * @param[out] Dacl
268  * The returned allocated DACL.
269  *
270  * @return
271  * Returns STATUS_SUCCESS if DACL creation from tokens has completed
272  * successfully. STATUS_INSUFFICIENT_RESOURCES is returned if DACL
273  * allocation from memory pool fails otherwise.
274  */
275 NTSTATUS
276 NTAPI
SepCreateImpersonationTokenDacl(_In_ PTOKEN Token,_In_ PTOKEN PrimaryToken,_Out_ PACL * Dacl)277 SepCreateImpersonationTokenDacl(
278     _In_ PTOKEN Token,
279     _In_ PTOKEN PrimaryToken,
280     _Out_ PACL* Dacl)
281 {
282     ULONG AclLength;
283     PACL TokenDacl;
284 
285     PAGED_CODE();
286 
287     *Dacl = NULL;
288 
289     AclLength = sizeof(ACL) +
290         (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid)) +
291         (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
292         (sizeof(ACE) + RtlLengthSid(SeRestrictedCodeSid)) +
293         (sizeof(ACE) + RtlLengthSid(Token->UserAndGroups->Sid)) +
294         (sizeof(ACE) + RtlLengthSid(PrimaryToken->UserAndGroups->Sid));
295 
296     TokenDacl = ExAllocatePoolWithTag(PagedPool, AclLength, TAG_ACL);
297     if (TokenDacl == NULL)
298     {
299         return STATUS_INSUFFICIENT_RESOURCES;
300     }
301 
302     RtlCreateAcl(TokenDacl, AclLength, ACL_REVISION);
303     RtlAddAccessAllowedAce(TokenDacl, ACL_REVISION, GENERIC_ALL,
304                            Token->UserAndGroups->Sid);
305     RtlAddAccessAllowedAce(TokenDacl, ACL_REVISION, GENERIC_ALL,
306                            PrimaryToken->UserAndGroups->Sid);
307     RtlAddAccessAllowedAce(TokenDacl, ACL_REVISION, GENERIC_ALL,
308                            SeAliasAdminsSid);
309     RtlAddAccessAllowedAce(TokenDacl, ACL_REVISION, GENERIC_ALL,
310                            SeLocalSystemSid);
311 
312     if (Token->RestrictedSids != NULL || PrimaryToken->RestrictedSids != NULL)
313     {
314         RtlAddAccessAllowedAce(TokenDacl, ACL_REVISION, GENERIC_ALL,
315                                SeRestrictedCodeSid);
316     }
317 
318     *Dacl = TokenDacl;
319 
320     return STATUS_SUCCESS;
321 }
322 
323 /**
324  * @brief
325  * Captures an access control list from an already valid input ACL.
326  *
327  * @param[in] InputAcl
328  * A valid ACL.
329  *
330  * @param[in] AccessMode
331  * Processor level access mode. The processor mode determines how
332  * the input arguments are probed.
333  *
334  * @param[in] PoolType
335  * Pool type for new captured ACL for creation. The pool type determines
336  * in which memory pool the ACL data should reside.
337  *
338  * @param[in] CaptureIfKernel
339  * If set to TRUE and the processor access mode being KernelMode, we are
340  * capturing an ACL directly in the kernel. Otherwise we are capturing
341  * within a kernel mode driver.
342  *
343  * @param[out] CapturedAcl
344  * The returned and allocated captured ACL.
345  *
346  * @return
347  * Returns STATUS_SUCCESS if the ACL has been successfully captured.
348  * STATUS_INSUFFICIENT_RESOURCES is returned otherwise.
349  */
350 NTSTATUS
351 NTAPI
SepCaptureAcl(_In_ PACL InputAcl,_In_ KPROCESSOR_MODE AccessMode,_In_ POOL_TYPE PoolType,_In_ BOOLEAN CaptureIfKernel,_Out_ PACL * CapturedAcl)352 SepCaptureAcl(
353     _In_ PACL InputAcl,
354     _In_ KPROCESSOR_MODE AccessMode,
355     _In_ POOL_TYPE PoolType,
356     _In_ BOOLEAN CaptureIfKernel,
357     _Out_ PACL *CapturedAcl)
358 {
359     PACL NewAcl;
360     ULONG AclSize;
361 
362     PAGED_CODE();
363 
364     /* If in kernel mode and we do not capture, just
365      * return the given ACL and don't validate it. */
366     if ((AccessMode == KernelMode) && !CaptureIfKernel)
367     {
368         *CapturedAcl = InputAcl;
369         return STATUS_SUCCESS;
370     }
371 
372     /* Otherwise, capture and validate the ACL, depending on the access mode */
373     if (AccessMode != KernelMode)
374     {
375         _SEH2_TRY
376         {
377             ProbeForRead(InputAcl,
378                          sizeof(ACL),
379                          sizeof(ULONG));
380             AclSize = InputAcl->AclSize;
381             ProbeForRead(InputAcl,
382                          AclSize,
383                          sizeof(ULONG));
384         }
385         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
386         {
387             /* Return the exception code */
388             _SEH2_YIELD(return _SEH2_GetExceptionCode());
389         }
390         _SEH2_END;
391 
392         /* Validate the minimal size an ACL can have */
393         if (AclSize < sizeof(ACL))
394             return STATUS_INVALID_ACL;
395 
396         NewAcl = ExAllocatePoolWithTag(PoolType,
397                                        AclSize,
398                                        TAG_ACL);
399         if (!NewAcl)
400             return STATUS_INSUFFICIENT_RESOURCES;
401 
402         _SEH2_TRY
403         {
404             RtlCopyMemory(NewAcl, InputAcl, AclSize);
405         }
406         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
407         {
408             /* Free the ACL and return the exception code */
409             ExFreePoolWithTag(NewAcl, TAG_ACL);
410             _SEH2_YIELD(return _SEH2_GetExceptionCode());
411         }
412         _SEH2_END;
413     }
414     else
415     {
416         AclSize = InputAcl->AclSize;
417 
418         /* Validate the minimal size an ACL can have */
419         if (AclSize < sizeof(ACL))
420             return STATUS_INVALID_ACL;
421 
422         NewAcl = ExAllocatePoolWithTag(PoolType,
423                                        AclSize,
424                                        TAG_ACL);
425         if (!NewAcl)
426             return STATUS_INSUFFICIENT_RESOURCES;
427 
428         RtlCopyMemory(NewAcl, InputAcl, AclSize);
429     }
430 
431     /* Validate the captured ACL */
432     if (!RtlValidAcl(NewAcl))
433     {
434         /* Free the ACL and fail */
435         ExFreePoolWithTag(NewAcl, TAG_ACL);
436         return STATUS_INVALID_ACL;
437     }
438 
439     /* It's valid, return it */
440     *CapturedAcl = NewAcl;
441     return STATUS_SUCCESS;
442 }
443 
444 /**
445  * @brief
446  * Releases (frees) a captured ACL from the memory pool.
447  *
448  * @param[in] CapturedAcl
449  * A valid captured ACL to free.
450  *
451  * @param[in] AccessMode
452  * Processor level access mode.
453  *
454  * @param[in] CaptureIfKernel
455  * If set to TRUE and the processor access mode being KernelMode, we're
456  * releasing an ACL directly in the kernel. Otherwise we're releasing
457  * within a kernel mode driver.
458  *
459  * @return
460  * Nothing.
461  */
462 VOID
463 NTAPI
SepReleaseAcl(_In_ PACL CapturedAcl,_In_ KPROCESSOR_MODE AccessMode,_In_ BOOLEAN CaptureIfKernel)464 SepReleaseAcl(
465     _In_ PACL CapturedAcl,
466     _In_ KPROCESSOR_MODE AccessMode,
467     _In_ BOOLEAN CaptureIfKernel)
468 {
469     PAGED_CODE();
470 
471     if (CapturedAcl != NULL &&
472         (AccessMode != KernelMode ||
473          (AccessMode == KernelMode && CaptureIfKernel)))
474     {
475         ExFreePoolWithTag(CapturedAcl, TAG_ACL);
476     }
477 }
478 
479 /**
480  * @brief
481  * Determines if a certain ACE can or cannot be propagated based on
482  * ACE inheritation flags and whatnot.
483  *
484  * @param[in] AceFlags
485  * Bit flags of an ACE to perform propagation checks.
486  *
487  * @param[out] NewAceFlags
488  * New ACE bit blags based on the specific ACE flags of the first
489  * argument parameter.
490  *
491  * @param[in] IsInherited
492  * If set to TRUE, an ACE is deemed as directly inherited from another
493  * instance. In that case we're allowed to propagate.
494  *
495  * @param[in] IsDirectoryObject
496  * If set to TRUE, an object directly inherits this ACE so we can propagate
497  * it.
498  *
499  * @return
500  * Returns TRUE if an ACE can be propagated, FALSE otherwise.
501  */
502 BOOLEAN
SepShouldPropagateAce(_In_ UCHAR AceFlags,_Out_ PUCHAR NewAceFlags,_In_ BOOLEAN IsInherited,_In_ BOOLEAN IsDirectoryObject)503 SepShouldPropagateAce(
504     _In_ UCHAR AceFlags,
505     _Out_ PUCHAR NewAceFlags,
506     _In_ BOOLEAN IsInherited,
507     _In_ BOOLEAN IsDirectoryObject)
508 {
509     if (!IsInherited)
510     {
511         *NewAceFlags = AceFlags;
512         return TRUE;
513     }
514 
515     if (!IsDirectoryObject)
516     {
517         if (AceFlags & OBJECT_INHERIT_ACE)
518         {
519             *NewAceFlags = AceFlags & ~VALID_INHERIT_FLAGS;
520             return TRUE;
521         }
522         return FALSE;
523     }
524 
525     if (AceFlags & NO_PROPAGATE_INHERIT_ACE)
526     {
527         if (AceFlags & CONTAINER_INHERIT_ACE)
528         {
529             *NewAceFlags = AceFlags & ~VALID_INHERIT_FLAGS;
530             return TRUE;
531         }
532         return FALSE;
533     }
534 
535     if (AceFlags & CONTAINER_INHERIT_ACE)
536     {
537         *NewAceFlags = CONTAINER_INHERIT_ACE | (AceFlags & OBJECT_INHERIT_ACE) | (AceFlags & ~VALID_INHERIT_FLAGS);
538         return TRUE;
539     }
540 
541     if (AceFlags & OBJECT_INHERIT_ACE)
542     {
543         *NewAceFlags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE | (AceFlags & ~VALID_INHERIT_FLAGS);
544         return TRUE;
545     }
546 
547     return FALSE;
548 }
549 
550 /**
551  * @brief
552  * Propagates (copies) an access control list.
553  *
554  * @param[out] AclDest
555  * The destination parameter with propagated ACL.
556  *
557  * @param[in,out] AclLength
558  * The length of the ACL that we propagate.
559  *
560  * @param[in] AclSource
561  * The source instance of a valid ACL.
562  *
563  * @param[in] Owner
564  * A SID that represents the main user that identifies the ACL.
565  *
566  * @param[in] Group
567  * A SID that represents a group that identifies the ACL.
568  *
569  * @param[in] IsInherited
570  * If set to TRUE, that means the ACL is directly inherited.
571  *
572  * @param[in] IsDirectoryObject
573  * If set to TRUE, that means the ACL is directly inherited because
574  * of the object that inherits it.
575  *
576  * @param[in] GenericMapping
577  * Generic mapping of access rights to map only certain effective
578  * ACEs.
579  *
580  * @return
581  * Returns STATUS_SUCCESS if ACL has been propagated successfully.
582  * STATUS_BUFFER_TOO_SMALL is returned if the ACL length is not greater
583  * than the maximum written size of the buffer for ACL propagation
584  * otherwise.
585  */
586 NTSTATUS
587 SepPropagateAcl(
_Out_writes_bytes_opt_(AclLength)588     _Out_writes_bytes_opt_(AclLength) PACL AclDest,
589     _Inout_ PULONG AclLength,
590     _In_reads_bytes_(AclSource->AclSize) PACL AclSource,
591     _In_ PSID Owner,
592     _In_ PSID Group,
593     _In_ BOOLEAN IsInherited,
594     _In_ BOOLEAN IsDirectoryObject,
595     _In_ PGENERIC_MAPPING GenericMapping)
596 {
597     ACCESS_MASK Mask;
598     PACCESS_ALLOWED_ACE AceSource;
599     PACCESS_ALLOWED_ACE AceDest;
600     PUCHAR CurrentDest;
601     PUCHAR CurrentSource;
602     ULONG i;
603     ULONG Written;
604     UCHAR AceFlags;
605     USHORT AceSize;
606     USHORT AceCount = 0;
607     PSID Sid;
608     BOOLEAN WriteTwoAces;
609 
610     ASSERT(RtlValidAcl(AclSource));
611     ASSERT(AclSource->AclSize % sizeof(ULONG) == 0);
612     ASSERT(AclSource->Sbz1 == 0);
613     ASSERT(AclSource->Sbz2 == 0);
614 
615     Written = 0;
616     if (*AclLength >= Written + sizeof(ACL))
617     {
618         RtlCopyMemory(AclDest,
619                       AclSource,
620                       sizeof(ACL));
621     }
622     Written += sizeof(ACL);
623 
624     CurrentDest = (PUCHAR)(AclDest + 1);
625     CurrentSource = (PUCHAR)(AclSource + 1);
626     for (i = 0; i < AclSource->AceCount; i++)
627     {
628         ASSERT((ULONG_PTR)CurrentDest % sizeof(ULONG) == 0);
629         ASSERT((ULONG_PTR)CurrentSource % sizeof(ULONG) == 0);
630         AceDest = (PACCESS_ALLOWED_ACE)CurrentDest;
631         AceSource = (PACCESS_ALLOWED_ACE)CurrentSource;
632 
633         if (AceSource->Header.AceType > ACCESS_MAX_MS_V2_ACE_TYPE)
634         {
635             /* FIXME: handle object & compound ACEs */
636             AceSize = AceSource->Header.AceSize;
637 
638             if (*AclLength >= Written + AceSize)
639             {
640                 RtlCopyMemory(AceDest, AceSource, AceSize);
641             }
642             CurrentDest += AceSize;
643             CurrentSource += AceSize;
644             Written += AceSize;
645             AceCount++;
646             continue;
647         }
648 
649         /* These all have the same structure */
650         ASSERT(AceSource->Header.AceType == ACCESS_ALLOWED_ACE_TYPE ||
651                AceSource->Header.AceType == ACCESS_DENIED_ACE_TYPE ||
652                AceSource->Header.AceType == SYSTEM_AUDIT_ACE_TYPE ||
653                AceSource->Header.AceType == SYSTEM_ALARM_ACE_TYPE);
654 
655         ASSERT(AceSource->Header.AceSize % sizeof(ULONG) == 0);
656         ASSERT(AceSource->Header.AceSize >= sizeof(*AceSource));
657         if (!SepShouldPropagateAce(AceSource->Header.AceFlags,
658                                    &AceFlags,
659                                    IsInherited,
660                                    IsDirectoryObject))
661         {
662             CurrentSource += AceSource->Header.AceSize;
663             continue;
664         }
665 
666         /* FIXME: filter out duplicate ACEs */
667         AceSize = AceSource->Header.AceSize;
668         Mask = AceSource->Mask;
669         Sid = (PSID)&AceSource->SidStart;
670         ASSERT(AceSize >= FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid(Sid));
671 
672         WriteTwoAces = FALSE;
673         /* Map effective ACE to specific rights */
674         if (!(AceFlags & INHERIT_ONLY_ACE))
675         {
676             RtlMapGenericMask(&Mask, GenericMapping);
677             Mask &= GenericMapping->GenericAll;
678 
679             if (IsInherited)
680             {
681                 if (RtlEqualSid(Sid, SeCreatorOwnerSid))
682                     Sid = Owner;
683                 else if (RtlEqualSid(Sid, SeCreatorGroupSid))
684                     Sid = Group;
685                 AceSize = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid(Sid);
686 
687                 /*
688                  * A generic container ACE becomes two ACEs:
689                  * - a specific effective ACE with no inheritance flags
690                  * - an inherit-only ACE that keeps the generic rights
691                  */
692                 if (IsDirectoryObject &&
693                     (AceFlags & CONTAINER_INHERIT_ACE) &&
694                     (Mask != AceSource->Mask || Sid != (PSID)&AceSource->SidStart))
695                 {
696                     WriteTwoAces = TRUE;
697                 }
698             }
699         }
700 
701         while (1)
702         {
703             if (*AclLength >= Written + AceSize)
704             {
705                 AceDest->Header.AceType = AceSource->Header.AceType;
706                 AceDest->Header.AceFlags = WriteTwoAces ? AceFlags & ~VALID_INHERIT_FLAGS
707                                                         : AceFlags;
708                 AceDest->Header.AceSize = AceSize;
709                 AceDest->Mask = Mask;
710                 RtlCopySid(AceSize - FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart),
711                            (PSID)&AceDest->SidStart,
712                            Sid);
713             }
714             Written += AceSize;
715 
716             AceCount++;
717             CurrentDest += AceSize;
718 
719             if (!WriteTwoAces)
720                 break;
721 
722             /* Second ACE keeps all the generics from the source ACE */
723             WriteTwoAces = FALSE;
724             AceDest = (PACCESS_ALLOWED_ACE)CurrentDest;
725             AceSize = AceSource->Header.AceSize;
726             Mask = AceSource->Mask;
727             Sid = (PSID)&AceSource->SidStart;
728             AceFlags |= INHERIT_ONLY_ACE;
729         }
730 
731         CurrentSource += AceSource->Header.AceSize;
732     }
733 
734     if (*AclLength >= sizeof(ACL))
735     {
736         AclDest->AceCount = AceCount;
737         AclDest->AclSize = Written;
738     }
739 
740     if (Written > *AclLength)
741     {
742         *AclLength = Written;
743         return STATUS_BUFFER_TOO_SMALL;
744     }
745     *AclLength = Written;
746     return STATUS_SUCCESS;
747 }
748 
749 /**
750  * @brief
751  * Selects an ACL and returns it to the caller.
752  *
753  * @param[in] ExplicitAcl
754  * If specified, the specified ACL to the call will be
755  * the selected ACL for the caller.
756  *
757  * @param[in] ExplicitPresent
758  * If set to TRUE and with specific ACL filled to the call, the
759  * function will immediately return the specific ACL as the selected
760  * ACL for the caller.
761  *
762  * @param[in] ExplicitDefaulted
763  * If set to FALSE and with specific ACL filled to the call, the ACL
764  * is not a default ACL. Otherwise it's a default ACL that we cannot
765  * select it as is.
766  *
767  * @param[in] ParentAcl
768  * If specified, the parent ACL will be used to determine the exact ACL
769  * length to check  if the ACL in question is not empty. If the list
770  * is not empty then the function will select such ACL to the caller.
771  *
772  * @param[in] DefaultAcl
773  * If specified, the default ACL will be the selected one for the caller.
774  *
775  * @param[out] AclLength
776  * The size length of an ACL.
777  *
778  * @param[in] Owner
779  * A SID that represents the main user that identifies the ACL.
780  *
781  * @param[in] Group
782  * A SID that represents a group that identifies the ACL.
783  *
784  * @param[out] AclPresent
785  * The returned boolean value, indicating if the ACL that we want to select
786  * does actually exist.
787  *
788  * @param[out] IsInherited
789  * The returned boolean value, indicating if the ACL we want to select it
790  * is actually inherited or not.
791  *
792  * @param[in] IsDirectoryObject
793  * If set to TRUE, the object inherits this ACL.
794  *
795  * @param[in] GenericMapping
796  * Generic mapping of access rights to map only certain effective
797  * ACEs of an ACL that we want to select it.
798  *
799  * @return
800  * Returns the selected access control list (ACL) to the caller,
801  * NULL otherwise.
802  */
803 PACL
SepSelectAcl(_In_opt_ PACL ExplicitAcl,_In_ BOOLEAN ExplicitPresent,_In_ BOOLEAN ExplicitDefaulted,_In_opt_ PACL ParentAcl,_In_opt_ PACL DefaultAcl,_Out_ PULONG AclLength,_In_ PSID Owner,_In_ PSID Group,_Out_ PBOOLEAN AclPresent,_Out_ PBOOLEAN IsInherited,_In_ BOOLEAN IsDirectoryObject,_In_ PGENERIC_MAPPING GenericMapping)804 SepSelectAcl(
805     _In_opt_ PACL ExplicitAcl,
806     _In_ BOOLEAN ExplicitPresent,
807     _In_ BOOLEAN ExplicitDefaulted,
808     _In_opt_ PACL ParentAcl,
809     _In_opt_ PACL DefaultAcl,
810     _Out_ PULONG AclLength,
811     _In_ PSID Owner,
812     _In_ PSID Group,
813     _Out_ PBOOLEAN AclPresent,
814     _Out_ PBOOLEAN IsInherited,
815     _In_ BOOLEAN IsDirectoryObject,
816     _In_ PGENERIC_MAPPING GenericMapping)
817 {
818     PACL Acl;
819     NTSTATUS Status;
820 
821     *AclPresent = TRUE;
822     if (ExplicitPresent && !ExplicitDefaulted)
823     {
824         Acl = ExplicitAcl;
825     }
826     else
827     {
828         if (ParentAcl)
829         {
830             *IsInherited = TRUE;
831             *AclLength = 0;
832             Status = SepPropagateAcl(NULL,
833                                      AclLength,
834                                      ParentAcl,
835                                      Owner,
836                                      Group,
837                                      *IsInherited,
838                                      IsDirectoryObject,
839                                      GenericMapping);
840             ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
841 
842             /* Use the parent ACL only if it's not empty */
843             if (*AclLength != sizeof(ACL))
844                 return ParentAcl;
845         }
846 
847         if (ExplicitPresent)
848         {
849             Acl = ExplicitAcl;
850         }
851         else if (DefaultAcl)
852         {
853             Acl = DefaultAcl;
854         }
855         else
856         {
857             *AclPresent = FALSE;
858             Acl = NULL;
859         }
860     }
861 
862     *IsInherited = FALSE;
863     *AclLength = 0;
864     if (Acl)
865     {
866         /* Get the length */
867         Status = SepPropagateAcl(NULL,
868                                  AclLength,
869                                  Acl,
870                                  Owner,
871                                  Group,
872                                  *IsInherited,
873                                  IsDirectoryObject,
874                                  GenericMapping);
875         ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
876     }
877     return Acl;
878 }
879 
880 /* EOF */
881