xref: /reactos/ntoskrnl/se/acl.c (revision bbabe248)
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
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
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  * are the input arguments probed.
333  *
334  * @param[in] PoolType
335  * Pool type for new captured ACL for creation. The pool type determines
336  * how the ACL data should reside in the pool memory.
337  *
338  * @param[in] CaptureIfKernel
339  * If set to TRUE and the processor access mode being KernelMode, we're
340  * capturing an ACL directly in the kernel. Otherwise we're 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
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 = 0;
361     NTSTATUS Status = STATUS_SUCCESS;
362 
363     PAGED_CODE();
364 
365     if (AccessMode != KernelMode)
366     {
367         _SEH2_TRY
368         {
369             ProbeForRead(InputAcl,
370                          sizeof(ACL),
371                          sizeof(ULONG));
372             AclSize = InputAcl->AclSize;
373             ProbeForRead(InputAcl,
374                          AclSize,
375                          sizeof(ULONG));
376         }
377         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
378         {
379             /* Return the exception code */
380             _SEH2_YIELD(return _SEH2_GetExceptionCode());
381         }
382         _SEH2_END;
383 
384         NewAcl = ExAllocatePoolWithTag(PoolType,
385                                        AclSize,
386                                        TAG_ACL);
387         if (NewAcl != NULL)
388         {
389             _SEH2_TRY
390             {
391                 RtlCopyMemory(NewAcl,
392                               InputAcl,
393                               AclSize);
394 
395                 *CapturedAcl = NewAcl;
396             }
397             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
398             {
399                 /* Free the ACL and return the exception code */
400                 ExFreePoolWithTag(NewAcl, TAG_ACL);
401                 _SEH2_YIELD(return _SEH2_GetExceptionCode());
402             }
403             _SEH2_END;
404         }
405         else
406         {
407             Status = STATUS_INSUFFICIENT_RESOURCES;
408         }
409     }
410     else if (!CaptureIfKernel)
411     {
412         *CapturedAcl = InputAcl;
413     }
414     else
415     {
416         AclSize = InputAcl->AclSize;
417 
418         NewAcl = ExAllocatePoolWithTag(PoolType,
419                                        AclSize,
420                                        TAG_ACL);
421 
422         if (NewAcl != NULL)
423         {
424             RtlCopyMemory(NewAcl,
425                           InputAcl,
426                           AclSize);
427 
428             *CapturedAcl = NewAcl;
429         }
430         else
431         {
432             Status = STATUS_INSUFFICIENT_RESOURCES;
433         }
434     }
435 
436     return Status;
437 }
438 
439 /**
440  * @brief
441  * Releases (frees) a captured ACL from the memory pool.
442  *
443  * @param[in] CapturedAcl
444  * A valid captured ACL to free.
445  *
446  * @param[in] AccessMode
447  * Processor level access mode.
448  *
449  * @param[in] CaptureIfKernel
450  * If set to TRUE and the processor access mode being KernelMode, we're
451  * releasing an ACL directly in the kernel. Otherwise we're releasing
452  * within a kernel mode driver.
453  *
454  * @return
455  * Nothing.
456  */
457 VOID
458 NTAPI
459 SepReleaseAcl(
460     _In_ PACL CapturedAcl,
461     _In_ KPROCESSOR_MODE AccessMode,
462     _In_ BOOLEAN CaptureIfKernel)
463 {
464     PAGED_CODE();
465 
466     if (CapturedAcl != NULL &&
467         (AccessMode != KernelMode ||
468          (AccessMode == KernelMode && CaptureIfKernel)))
469     {
470         ExFreePoolWithTag(CapturedAcl, TAG_ACL);
471     }
472 }
473 
474 /**
475  * @brief
476  * Determines if a certain ACE can or cannot be propagated based on
477  * ACE inheritation flags and whatnot.
478  *
479  * @param[in] AceFlags
480  * Bit flags of an ACE to perform propagation checks.
481  *
482  * @param[out] NewAceFlags
483  * New ACE bit blags based on the specific ACE flags of the first
484  * argument parameter.
485  *
486  * @param[in] IsInherited
487  * If set to TRUE, an ACE is deemed as directly inherited from another
488  * instance. In that case we're allowed to propagate.
489  *
490  * @param[in] IsDirectoryObject
491  * If set to TRUE, an object directly inherits this ACE so we can propagate
492  * it.
493  *
494  * @return
495  * Returns TRUE if an ACE can be propagated, FALSE otherwise.
496  */
497 BOOLEAN
498 SepShouldPropagateAce(
499     _In_ UCHAR AceFlags,
500     _Out_ PUCHAR NewAceFlags,
501     _In_ BOOLEAN IsInherited,
502     _In_ BOOLEAN IsDirectoryObject)
503 {
504     if (!IsInherited)
505     {
506         *NewAceFlags = AceFlags;
507         return TRUE;
508     }
509 
510     if (!IsDirectoryObject)
511     {
512         if (AceFlags & OBJECT_INHERIT_ACE)
513         {
514             *NewAceFlags = AceFlags & ~VALID_INHERIT_FLAGS;
515             return TRUE;
516         }
517         return FALSE;
518     }
519 
520     if (AceFlags & NO_PROPAGATE_INHERIT_ACE)
521     {
522         if (AceFlags & CONTAINER_INHERIT_ACE)
523         {
524             *NewAceFlags = AceFlags & ~VALID_INHERIT_FLAGS;
525             return TRUE;
526         }
527         return FALSE;
528     }
529 
530     if (AceFlags & CONTAINER_INHERIT_ACE)
531     {
532         *NewAceFlags = CONTAINER_INHERIT_ACE | (AceFlags & OBJECT_INHERIT_ACE) | (AceFlags & ~VALID_INHERIT_FLAGS);
533         return TRUE;
534     }
535 
536     if (AceFlags & OBJECT_INHERIT_ACE)
537     {
538         *NewAceFlags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE | (AceFlags & ~VALID_INHERIT_FLAGS);
539         return TRUE;
540     }
541 
542     return FALSE;
543 }
544 
545 /**
546  * @brief
547  * Propagates (copies) an access control list.
548  *
549  * @param[out] AclDest
550  * The destination parameter with propagated ACL.
551  *
552  * @param[in,out] AclLength
553  * The length of the ACL that we propagate.
554  *
555  * @param[in] AclSource
556  * The source instance of a valid ACL.
557  *
558  * @param[in] Owner
559  * A SID that represents the main user that identifies the ACL.
560  *
561  * @param[in] Group
562  * A SID that represents a group that identifies the ACL.
563  *
564  * @param[in] IsInherited
565  * If set to TRUE, that means the ACL is directly inherited.
566  *
567  * @param[in] IsDirectoryObject
568  * If set to TRUE, that means the ACL is directly inherited because
569  * of the object that inherits it.
570  *
571  * @param[in] GenericMapping
572  * Generic mapping of access rights to map only certain effective
573  * ACEs.
574  *
575  * @return
576  * Returns STATUS_SUCCESS if ACL has been propagated successfully.
577  * STATUS_BUFFER_TOO_SMALL is returned if the ACL length is not greater
578  * than the maximum written size of the buffer for ACL propagation
579  * otherwise.
580  */
581 NTSTATUS
582 SepPropagateAcl(
583     _Out_writes_bytes_opt_(AclLength) PACL AclDest,
584     _Inout_ PULONG AclLength,
585     _In_reads_bytes_(AclSource->AclSize) PACL AclSource,
586     _In_ PSID Owner,
587     _In_ PSID Group,
588     _In_ BOOLEAN IsInherited,
589     _In_ BOOLEAN IsDirectoryObject,
590     _In_ PGENERIC_MAPPING GenericMapping)
591 {
592     ACCESS_MASK Mask;
593     PACCESS_ALLOWED_ACE AceSource;
594     PACCESS_ALLOWED_ACE AceDest;
595     PUCHAR CurrentDest;
596     PUCHAR CurrentSource;
597     ULONG i;
598     ULONG Written;
599     UCHAR AceFlags;
600     USHORT AceSize;
601     USHORT AceCount = 0;
602     PSID Sid;
603     BOOLEAN WriteTwoAces;
604 
605     ASSERT(RtlValidAcl(AclSource));
606     ASSERT(AclSource->AclSize % sizeof(ULONG) == 0);
607     ASSERT(AclSource->Sbz1 == 0);
608     ASSERT(AclSource->Sbz2 == 0);
609 
610     Written = 0;
611     if (*AclLength >= Written + sizeof(ACL))
612     {
613         RtlCopyMemory(AclDest,
614                       AclSource,
615                       sizeof(ACL));
616     }
617     Written += sizeof(ACL);
618 
619     CurrentDest = (PUCHAR)(AclDest + 1);
620     CurrentSource = (PUCHAR)(AclSource + 1);
621     for (i = 0; i < AclSource->AceCount; i++)
622     {
623         ASSERT((ULONG_PTR)CurrentDest % sizeof(ULONG) == 0);
624         ASSERT((ULONG_PTR)CurrentSource % sizeof(ULONG) == 0);
625         AceDest = (PACCESS_ALLOWED_ACE)CurrentDest;
626         AceSource = (PACCESS_ALLOWED_ACE)CurrentSource;
627 
628         if (AceSource->Header.AceType > ACCESS_MAX_MS_V2_ACE_TYPE)
629         {
630             /* FIXME: handle object & compound ACEs */
631             AceSize = AceSource->Header.AceSize;
632 
633             if (*AclLength >= Written + AceSize)
634             {
635                 RtlCopyMemory(AceDest, AceSource, AceSize);
636             }
637             CurrentDest += AceSize;
638             CurrentSource += AceSize;
639             Written += AceSize;
640             AceCount++;
641             continue;
642         }
643 
644         /* These all have the same structure */
645         ASSERT(AceSource->Header.AceType == ACCESS_ALLOWED_ACE_TYPE ||
646                AceSource->Header.AceType == ACCESS_DENIED_ACE_TYPE ||
647                AceSource->Header.AceType == SYSTEM_AUDIT_ACE_TYPE ||
648                AceSource->Header.AceType == SYSTEM_ALARM_ACE_TYPE);
649 
650         ASSERT(AceSource->Header.AceSize % sizeof(ULONG) == 0);
651         ASSERT(AceSource->Header.AceSize >= sizeof(*AceSource));
652         if (!SepShouldPropagateAce(AceSource->Header.AceFlags,
653                                    &AceFlags,
654                                    IsInherited,
655                                    IsDirectoryObject))
656         {
657             CurrentSource += AceSource->Header.AceSize;
658             continue;
659         }
660 
661         /* FIXME: filter out duplicate ACEs */
662         AceSize = AceSource->Header.AceSize;
663         Mask = AceSource->Mask;
664         Sid = (PSID)&AceSource->SidStart;
665         ASSERT(AceSize >= FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid(Sid));
666 
667         WriteTwoAces = FALSE;
668         /* Map effective ACE to specific rights */
669         if (!(AceFlags & INHERIT_ONLY_ACE))
670         {
671             RtlMapGenericMask(&Mask, GenericMapping);
672             Mask &= GenericMapping->GenericAll;
673 
674             if (IsInherited)
675             {
676                 if (RtlEqualSid(Sid, SeCreatorOwnerSid))
677                     Sid = Owner;
678                 else if (RtlEqualSid(Sid, SeCreatorGroupSid))
679                     Sid = Group;
680                 AceSize = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid(Sid);
681 
682                 /*
683                  * A generic container ACE becomes two ACEs:
684                  * - a specific effective ACE with no inheritance flags
685                  * - an inherit-only ACE that keeps the generic rights
686                  */
687                 if (IsDirectoryObject &&
688                     (AceFlags & CONTAINER_INHERIT_ACE) &&
689                     (Mask != AceSource->Mask || Sid != (PSID)&AceSource->SidStart))
690                 {
691                     WriteTwoAces = TRUE;
692                 }
693             }
694         }
695 
696         while (1)
697         {
698             if (*AclLength >= Written + AceSize)
699             {
700                 AceDest->Header.AceType = AceSource->Header.AceType;
701                 AceDest->Header.AceFlags = WriteTwoAces ? AceFlags & ~VALID_INHERIT_FLAGS
702                                                         : AceFlags;
703                 AceDest->Header.AceSize = AceSize;
704                 AceDest->Mask = Mask;
705                 RtlCopySid(AceSize - FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart),
706                            (PSID)&AceDest->SidStart,
707                            Sid);
708             }
709             Written += AceSize;
710 
711             AceCount++;
712             CurrentDest += AceSize;
713 
714             if (!WriteTwoAces)
715                 break;
716 
717             /* Second ACE keeps all the generics from the source ACE */
718             WriteTwoAces = FALSE;
719             AceDest = (PACCESS_ALLOWED_ACE)CurrentDest;
720             AceSize = AceSource->Header.AceSize;
721             Mask = AceSource->Mask;
722             Sid = (PSID)&AceSource->SidStart;
723             AceFlags |= INHERIT_ONLY_ACE;
724         }
725 
726         CurrentSource += AceSource->Header.AceSize;
727     }
728 
729     if (*AclLength >= sizeof(ACL))
730     {
731         AclDest->AceCount = AceCount;
732         AclDest->AclSize = Written;
733     }
734 
735     if (Written > *AclLength)
736     {
737         *AclLength = Written;
738         return STATUS_BUFFER_TOO_SMALL;
739     }
740     *AclLength = Written;
741     return STATUS_SUCCESS;
742 }
743 
744 /**
745  * @brief
746  * Selects an ACL and returns it to the caller.
747  *
748  * @param[in] ExplicitAcl
749  * If specified, the specified ACL to the call will be
750  * the selected ACL for the caller.
751  *
752  * @param[in] ExplicitPresent
753  * If set to TRUE and with specific ACL filled to the call, the
754  * function will immediately return the specific ACL as the selected
755  * ACL for the caller.
756  *
757  * @param[in] ExplicitDefaulted
758  * If set to FALSE and with specific ACL filled to the call, the ACL
759  * is not a default ACL. Otherwise it's a default ACL that we cannot
760  * select it as is.
761  *
762  * @param[in] ParentAcl
763  * If specified, the parent ACL will be used to determine the exact ACL
764  * length to check  if the ACL in question is not empty. If the list
765  * is not empty then the function will select such ACL to the caller.
766  *
767  * @param[in] DefaultAcl
768  * If specified, the default ACL will be the selected one for the caller.
769  *
770  * @param[out] AclLength
771  * The size length of an ACL.
772  *
773  * @param[in] Owner
774  * A SID that represents the main user that identifies the ACL.
775  *
776  * @param[in] Group
777  * A SID that represents a group that identifies the ACL.
778  *
779  * @param[out] AclPresent
780  * The returned boolean value, indicating if the ACL that we want to select
781  * does actually exist.
782  *
783  * @param[out] IsInherited
784  * The returned boolean value, indicating if the ACL we want to select it
785  * is actually inherited or not.
786  *
787  * @param[in] IsDirectoryObject
788  * If set to TRUE, the object inherits this ACL.
789  *
790  * @param[in] GenericMapping
791  * Generic mapping of access rights to map only certain effective
792  * ACEs of an ACL that we want to select it.
793  *
794  * @return
795  * Returns the selected access control list (ACL) to the caller,
796  * NULL otherwise.
797  */
798 PACL
799 SepSelectAcl(
800     _In_opt_ PACL ExplicitAcl,
801     _In_ BOOLEAN ExplicitPresent,
802     _In_ BOOLEAN ExplicitDefaulted,
803     _In_opt_ PACL ParentAcl,
804     _In_opt_ PACL DefaultAcl,
805     _Out_ PULONG AclLength,
806     _In_ PSID Owner,
807     _In_ PSID Group,
808     _Out_ PBOOLEAN AclPresent,
809     _Out_ PBOOLEAN IsInherited,
810     _In_ BOOLEAN IsDirectoryObject,
811     _In_ PGENERIC_MAPPING GenericMapping)
812 {
813     PACL Acl;
814     NTSTATUS Status;
815 
816     *AclPresent = TRUE;
817     if (ExplicitPresent && !ExplicitDefaulted)
818     {
819         Acl = ExplicitAcl;
820     }
821     else
822     {
823         if (ParentAcl)
824         {
825             *IsInherited = TRUE;
826             *AclLength = 0;
827             Status = SepPropagateAcl(NULL,
828                                      AclLength,
829                                      ParentAcl,
830                                      Owner,
831                                      Group,
832                                      *IsInherited,
833                                      IsDirectoryObject,
834                                      GenericMapping);
835             ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
836 
837             /* Use the parent ACL only if it's not empty */
838             if (*AclLength != sizeof(ACL))
839                 return ParentAcl;
840         }
841 
842         if (ExplicitPresent)
843         {
844             Acl = ExplicitAcl;
845         }
846         else if (DefaultAcl)
847         {
848             Acl = DefaultAcl;
849         }
850         else
851         {
852             *AclPresent = FALSE;
853             Acl = NULL;
854         }
855     }
856 
857     *IsInherited = FALSE;
858     *AclLength = 0;
859     if (Acl)
860     {
861         /* Get the length */
862         Status = SepPropagateAcl(NULL,
863                                  AclLength,
864                                  Acl,
865                                  Owner,
866                                  Group,
867                                  *IsInherited,
868                                  IsDirectoryObject,
869                                  GenericMapping);
870         ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
871     }
872     return Acl;
873 }
874 
875 /* EOF */
876