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