Reactos
1/*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Security manager
5 * FILE: lib/rtl/acl.c
6 * PROGRAMER: David Welch <welch@cwcom.net>
7 */
8
9/* INCLUDES *****************************************************************/
10
11#include <rtl.h>
12#include <../../ntoskrnl/include/internal/se.h>
13#define NDEBUG
14#include <debug.h>
15
16/* PRIVATE FUNCTIONS **********************************************************/
17
18BOOLEAN
19NTAPI
20RtlFirstFreeAce(IN PACL Acl,
21 OUT PACE* FirstFreeAce)
22{
23 PACE Current;
24 ULONG_PTR AclEnd;
25 ULONG i;
26 PAGED_CODE_RTL();
27
28 /* Assume failure */
29 *FirstFreeAce = NULL;
30
31 /* Get the start and end pointers */
32 Current = (PACE)(Acl + 1);
33 AclEnd = (ULONG_PTR)Acl + Acl->AclSize;
34
35 /* Loop all the ACEs */
36 for (i = 0; i < Acl->AceCount; i++)
37 {
38 /* If any is beyond the DACL, bail out, otherwise keep going */
39 if ((ULONG_PTR)Current >= AclEnd) return FALSE;
40 Current = (PACE)((ULONG_PTR)Current + Current->Header.AceSize);
41 }
42
43 /* If the last spot is empty and still valid, return it */
44 if ((ULONG_PTR)Current <= AclEnd) *FirstFreeAce = Current;
45 return TRUE;
46}
47
48VOID
49NTAPI
50RtlpAddData(IN PVOID AceList,
51 IN ULONG AceListLength,
52 IN PVOID Ace,
53 IN ULONG Offset)
54{
55 /* Shift the buffer down */
56 if (Offset > 0)
57 {
58 RtlCopyMemory((PVOID)((ULONG_PTR)Ace + AceListLength),
59 Ace,
60 Offset);
61 }
62
63 /* Copy the new data in */
64 if (AceListLength) RtlCopyMemory(Ace, AceList, AceListLength);
65}
66
67VOID
68NTAPI
69RtlpDeleteData(IN PVOID Ace,
70 IN ULONG AceSize,
71 IN ULONG Offset)
72{
73 /* Move the data up */
74 if (AceSize < Offset)
75 {
76 RtlMoveMemory(Ace,
77 (PVOID)((ULONG_PTR)Ace + AceSize),
78 Offset - AceSize);
79 }
80
81 /* Zero the rest */
82 if ((Offset - AceSize) < Offset)
83 {
84 RtlZeroMemory((PVOID)((ULONG_PTR)Ace + Offset - AceSize), AceSize);
85 }
86}
87
88NTSTATUS
89NTAPI
90RtlpAddKnownAce(IN PACL Acl,
91 IN ULONG Revision,
92 IN ULONG Flags,
93 IN ACCESS_MASK AccessMask,
94 IN PSID Sid,
95 IN UCHAR Type)
96{
97 PKNOWN_ACE Ace;
98 ULONG AceSize, InvalidFlags;
99 PAGED_CODE_RTL();
100
101 /* Check the validity of the SID */
102 if (!RtlValidSid(Sid)) return STATUS_INVALID_SID;
103
104 /* Check the validity of the revision */
105 if ((Acl->AclRevision > ACL_REVISION4) || (Revision > ACL_REVISION4))
106 {
107 return STATUS_REVISION_MISMATCH;
108 }
109
110 /* Pick the smallest of the revisions */
111 if (Revision < Acl->AclRevision) Revision = Acl->AclRevision;
112
113 /* Validate the flags */
114 if (Type == SYSTEM_AUDIT_ACE_TYPE)
115 {
116 InvalidFlags = Flags & ~(VALID_INHERIT_FLAGS |
117 SUCCESSFUL_ACCESS_ACE_FLAG |
118 FAILED_ACCESS_ACE_FLAG);
119 }
120 else
121 {
122 InvalidFlags = Flags & ~VALID_INHERIT_FLAGS;
123 }
124
125 /* If flags are invalid, bail out */
126 if (InvalidFlags != 0) return STATUS_INVALID_PARAMETER;
127
128 /* If ACL is invalid, bail out */
129 if (!RtlValidAcl(Acl)) return STATUS_INVALID_ACL;
130
131 /* If there's no free ACE, bail out */
132 if (!RtlFirstFreeAce(Acl, (PACE*)&Ace)) return STATUS_INVALID_ACL;
133
134 /* Calculate the size of the ACE and bail out if it's too small */
135 AceSize = RtlLengthSid(Sid) + sizeof(ACE);
136 if (!(Ace) || ((ULONG_PTR)Ace + AceSize > (ULONG_PTR)Acl + Acl->AclSize))
137 {
138 return STATUS_ALLOTTED_SPACE_EXCEEDED;
139 }
140
141 /* Initialize the header and common fields */
142 Ace->Header.AceFlags = (BYTE)Flags;
143 Ace->Header.AceType = Type;
144 Ace->Header.AceSize = (WORD)AceSize;
145 Ace->Mask = AccessMask;
146
147 /* Copy the SID */
148 RtlCopySid(RtlLengthSid(Sid), &Ace->SidStart, Sid);
149
150 /* Fill out the ACL header and return */
151 Acl->AceCount++;
152 Acl->AclRevision = (BYTE)Revision;
153 return STATUS_SUCCESS;
154}
155
156NTSTATUS
157NTAPI
158RtlpAddKnownObjectAce(IN PACL Acl,
159 IN ULONG Revision,
160 IN ULONG Flags,
161 IN ACCESS_MASK AccessMask,
162 IN GUID *ObjectTypeGuid OPTIONAL,
163 IN GUID *InheritedObjectTypeGuid OPTIONAL,
164 IN PSID Sid,
165 IN UCHAR Type)
166{
167 PKNOWN_OBJECT_ACE Ace;
168 ULONG_PTR SidStart;
169 ULONG AceSize, InvalidFlags, AceObjectFlags = 0;
170 PAGED_CODE_RTL();
171
172 /* Check the validity of the SID */
173 if (!RtlValidSid(Sid)) return STATUS_INVALID_SID;
174
175 /* Check the validity of the revision */
176 if ((Acl->AclRevision > ACL_REVISION4) || (Revision != ACL_REVISION4))
177 {
178 return STATUS_REVISION_MISMATCH;
179 }
180
181 /* Pick the smallest of the revisions */
182 if (Revision < Acl->AclRevision) Revision = Acl->AclRevision;
183
184 /* Validate the flags */
185 if ((Type == SYSTEM_AUDIT_OBJECT_ACE_TYPE) ||
186 (Type == SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE))
187 {
188 InvalidFlags = Flags & ~(VALID_INHERIT_FLAGS |
189 SUCCESSFUL_ACCESS_ACE_FLAG | FAILED_ACCESS_ACE_FLAG);
190 }
191 else
192 {
193 InvalidFlags = Flags & ~VALID_INHERIT_FLAGS;
194 }
195
196 /* If flags are invalid, bail out */
197 if (InvalidFlags != 0) return STATUS_INVALID_PARAMETER;
198
199 /* If ACL is invalid, bail out */
200 if (!RtlValidAcl(Acl)) return STATUS_INVALID_ACL;
201
202 /* If there's no free ACE, bail out */
203 if (!RtlFirstFreeAce(Acl, (PACE*)&Ace)) return STATUS_INVALID_ACL;
204
205 /* Calculate the size of the ACE */
206 AceSize = RtlLengthSid(Sid) + sizeof(ACE) + sizeof(ULONG);
207
208 /* Add-in the size of the GUIDs if any and update flags as needed */
209 if (ObjectTypeGuid)
210 {
211 AceObjectFlags |= ACE_OBJECT_TYPE_PRESENT;
212 AceSize += sizeof(GUID);
213 }
214 if (InheritedObjectTypeGuid)
215 {
216 AceObjectFlags |= ACE_INHERITED_OBJECT_TYPE_PRESENT;
217 AceSize += sizeof(GUID);
218 }
219
220 /* Bail out if there's not enough space in the ACL */
221 if (!(Ace) || ((ULONG_PTR)Ace + AceSize > (ULONG_PTR)Acl + Acl->AclSize))
222 {
223 return STATUS_ALLOTTED_SPACE_EXCEEDED;
224 }
225
226 /* Initialize the header and common fields */
227 Ace->Header.AceFlags = (BYTE)Flags;
228 Ace->Header.AceType = Type;
229 Ace->Header.AceSize = (WORD)AceSize;
230 Ace->Mask = AccessMask;
231 Ace->Flags = AceObjectFlags;
232
233 /* Copy the GUIDs */
234 SidStart = (ULONG_PTR)&Ace->SidStart;
235 if (ObjectTypeGuid )
236 {
237 RtlCopyMemory((PVOID)SidStart, ObjectTypeGuid, sizeof(GUID));
238 SidStart += sizeof(GUID);
239 }
240 if (InheritedObjectTypeGuid)
241 {
242 RtlCopyMemory((PVOID)SidStart, InheritedObjectTypeGuid, sizeof(GUID));
243 SidStart += sizeof(GUID);
244 }
245
246 /* Copy the SID */
247 RtlCopySid(RtlLengthSid(Sid), (PSID)SidStart, Sid);
248
249 /* Fill out the ACL header and return */
250 Acl->AceCount++;
251 Acl->AclRevision = (BYTE)Revision;
252 return STATUS_SUCCESS;
253}
254
255/* PUBLIC FUNCTIONS ***********************************************************/
256
257/*
258 * @implemented
259 */
260NTSTATUS
261NTAPI
262RtlAddAccessAllowedAce(IN OUT PACL Acl,
263 IN ULONG Revision,
264 IN ACCESS_MASK AccessMask,
265 IN PSID Sid)
266{
267 PAGED_CODE_RTL();
268
269 /* Call the worker function */
270 return RtlpAddKnownAce(Acl,
271 Revision,
272 0,
273 AccessMask,
274 Sid,
275 ACCESS_ALLOWED_ACE_TYPE);
276}
277
278/*
279 * @implemented
280 */
281NTSTATUS
282NTAPI
283RtlAddAccessAllowedAceEx(IN OUT PACL Acl,
284 IN ULONG Revision,
285 IN ULONG Flags,
286 IN ACCESS_MASK AccessMask,
287 IN PSID Sid)
288{
289 PAGED_CODE_RTL();
290
291 /* Call the worker function */
292 return RtlpAddKnownAce(Acl,
293 Revision,
294 Flags,
295 AccessMask,
296 Sid,
297 ACCESS_ALLOWED_ACE_TYPE);
298}
299
300/*
301 * @implemented
302 */
303NTSTATUS
304NTAPI
305RtlAddAccessAllowedObjectAce(IN OUT PACL Acl,
306 IN ULONG Revision,
307 IN ULONG Flags,
308 IN ACCESS_MASK AccessMask,
309 IN GUID *ObjectTypeGuid OPTIONAL,
310 IN GUID *InheritedObjectTypeGuid OPTIONAL,
311 IN PSID Sid)
312{
313 PAGED_CODE_RTL();
314
315 /* Is there no object data? */
316 if (!(ObjectTypeGuid) && !(InheritedObjectTypeGuid))
317 {
318 /* Use the usual routine */
319 return RtlpAddKnownAce(Acl,
320 Revision,
321 Flags,
322 AccessMask,
323 Sid,
324 ACCESS_ALLOWED_ACE_TYPE);
325 }
326
327 /* Use the object routine */
328 return RtlpAddKnownObjectAce(Acl,
329 Revision,
330 Flags,
331 AccessMask,
332 ObjectTypeGuid,
333 InheritedObjectTypeGuid,
334 Sid,
335 ACCESS_ALLOWED_OBJECT_ACE_TYPE);
336}
337
338/*
339 * @implemented
340 */
341NTSTATUS
342NTAPI
343RtlAddAccessDeniedAce(IN PACL Acl,
344 IN ULONG Revision,
345 IN ACCESS_MASK AccessMask,
346 IN PSID Sid)
347{
348 PAGED_CODE_RTL();
349
350 /* Call the worker function */
351 return RtlpAddKnownAce(Acl,
352 Revision,
353 0,
354 AccessMask,
355 Sid,
356 ACCESS_DENIED_ACE_TYPE);
357}
358
359/*
360 * @implemented
361 */
362NTSTATUS
363NTAPI
364RtlAddAccessDeniedAceEx(IN OUT PACL Acl,
365 IN ULONG Revision,
366 IN ULONG Flags,
367 IN ACCESS_MASK AccessMask,
368 IN PSID Sid)
369{
370 PAGED_CODE_RTL();
371
372 /* Call the worker function */
373 return RtlpAddKnownAce(Acl,
374 Revision,
375 Flags,
376 AccessMask,
377 Sid,
378 ACCESS_DENIED_ACE_TYPE);
379}
380
381/*
382 * @implemented
383 */
384NTSTATUS
385NTAPI
386RtlAddAccessDeniedObjectAce(IN OUT PACL Acl,
387 IN ULONG Revision,
388 IN ULONG Flags,
389 IN ACCESS_MASK AccessMask,
390 IN GUID *ObjectTypeGuid OPTIONAL,
391 IN GUID *InheritedObjectTypeGuid OPTIONAL,
392 IN PSID Sid)
393{
394 PAGED_CODE_RTL();
395
396 /* Is there no object data? */
397 if (!(ObjectTypeGuid) && !(InheritedObjectTypeGuid))
398 {
399 /* Use the usual routine */
400 return RtlpAddKnownAce(Acl,
401 Revision,
402 Flags,
403 AccessMask,
404 Sid,
405 ACCESS_DENIED_ACE_TYPE);
406 }
407
408 /* There's object data, use the object routine */
409 return RtlpAddKnownObjectAce(Acl,
410 Revision,
411 Flags,
412 AccessMask,
413 ObjectTypeGuid,
414 InheritedObjectTypeGuid,
415 Sid,
416 ACCESS_DENIED_OBJECT_ACE_TYPE);
417}
418
419/*
420 * @implemented
421 */
422NTSTATUS
423NTAPI
424RtlAddAuditAccessAce(IN PACL Acl,
425 IN ULONG Revision,
426 IN ACCESS_MASK AccessMask,
427 IN PSID Sid,
428 IN BOOLEAN Success,
429 IN BOOLEAN Failure)
430{
431 ULONG Flags = 0;
432 PAGED_CODE_RTL();
433
434 /* Add flags */
435 if (Success) Flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
436 if (Failure) Flags |= FAILED_ACCESS_ACE_FLAG;
437
438 /* Call the worker routine */
439 return RtlpAddKnownAce(Acl,
440 Revision,
441 Flags,
442 AccessMask,
443 Sid,
444 SYSTEM_AUDIT_ACE_TYPE);
445}
446
447/*
448 * @implemented
449 */
450NTSTATUS
451NTAPI
452RtlAddAuditAccessAceEx(IN PACL Acl,
453 IN ULONG Revision,
454 IN ULONG Flags,
455 IN ACCESS_MASK AccessMask,
456 IN PSID Sid,
457 IN BOOLEAN Success,
458 IN BOOLEAN Failure)
459{
460 PAGED_CODE_RTL();
461
462 /* Add flags */
463 if (Success) Flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
464 if (Failure) Flags |= FAILED_ACCESS_ACE_FLAG;
465
466 /* Call the worker routine */
467 return RtlpAddKnownAce(Acl,
468 Revision,
469 Flags,
470 AccessMask,
471 Sid,
472 SYSTEM_AUDIT_ACE_TYPE);
473}
474
475/*
476 * @implemented
477 */
478NTSTATUS
479NTAPI
480RtlAddAuditAccessObjectAce(IN PACL Acl,
481 IN ULONG Revision,
482 IN ULONG Flags,
483 IN ACCESS_MASK AccessMask,
484 IN GUID *ObjectTypeGuid OPTIONAL,
485 IN GUID *InheritedObjectTypeGuid OPTIONAL,
486 IN PSID Sid,
487 IN BOOLEAN Success,
488 IN BOOLEAN Failure)
489{
490 /* Add flags */
491 if (Success) Flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
492 if (Failure) Flags |= FAILED_ACCESS_ACE_FLAG;
493
494 /* Is there no object data? */
495 if (!(ObjectTypeGuid) && !(InheritedObjectTypeGuid))
496 {
497 /* Call the normal routine */
498 return RtlpAddKnownAce(Acl,
499 Revision,
500 Flags,
501 AccessMask,
502 Sid,
503 SYSTEM_AUDIT_ACE_TYPE);
504 }
505
506 /* There's object data, use the object routine */
507 return RtlpAddKnownObjectAce(Acl,
508 Revision,
509 Flags,
510 AccessMask,
511 ObjectTypeGuid,
512 InheritedObjectTypeGuid,
513 Sid,
514 SYSTEM_AUDIT_OBJECT_ACE_TYPE);
515}
516
517/*
518 * @implemented
519 */
520NTSTATUS
521NTAPI
522RtlGetAce(IN PACL Acl,
523 IN ULONG AceIndex,
524 OUT PVOID *Ace)
525{
526 ULONG i;
527 PAGED_CODE_RTL();
528
529 /* Bail out if the revision or the index are invalid */
530 if ((Acl->AclRevision < MIN_ACL_REVISION) ||
531 (Acl->AclRevision > MAX_ACL_REVISION) ||
532 (AceIndex >= Acl->AceCount))
533 {
534 return STATUS_INVALID_PARAMETER;
535 }
536
537 /* Loop through the ACEs */
538 *Ace = (PVOID)((PACE)(Acl + 1));
539 for (i = 0; i < AceIndex; i++)
540 {
541 /* Bail out if an invalid ACE is ever found */
542 if ((ULONG_PTR)*Ace >= (ULONG_PTR)Acl + Acl->AclSize)
543 {
544 return STATUS_INVALID_PARAMETER;
545 }
546
547 /* Keep going */
548 *Ace = (PVOID)((PACE)((ULONG_PTR)(*Ace) + ((PACE)(*Ace))->Header.AceSize));
549 }
550
551 /* Check if the last ACE is still valid */
552 if ((ULONG_PTR)*Ace >= (ULONG_PTR)Acl + Acl->AclSize)
553 {
554 return STATUS_INVALID_PARAMETER;
555 }
556
557 /* All good, return */
558 return STATUS_SUCCESS;
559}
560
561/*
562 * @implemented
563 */
564NTSTATUS
565NTAPI
566RtlAddAce(IN PACL Acl,
567 IN ULONG AclRevision,
568 IN ULONG StartingIndex,
569 IN PVOID AceList,
570 IN ULONG AceListLength)
571{
572 PACE Ace, FreeAce;
573 USHORT NewAceCount;
574 ULONG Index;
575 PAGED_CODE_RTL();
576
577 /* Bail out if the ACL is invalid */
578 if (!RtlValidAcl(Acl)) return STATUS_INVALID_PARAMETER;
579
580 /* Bail out if there's no space */
581 if (!RtlFirstFreeAce(Acl, &FreeAce)) return STATUS_INVALID_PARAMETER;
582
583 /* Loop over all the ACEs, keeping track of new ACEs as we go along */
584 for (Ace = AceList, NewAceCount = 0;
585 Ace < (PACE)((ULONG_PTR)AceList + AceListLength);
586 NewAceCount++)
587 {
588 /* Make sure that the revision of this ACE is valid in this list.
589 The initial check looks strange, but it is what Windows does. */
590 if (Ace->Header.AceType <= ACCESS_MAX_MS_ACE_TYPE)
591 {
592 if (Ace->Header.AceType > ACCESS_MAX_MS_V3_ACE_TYPE)
593 {
594 if (AclRevision < ACL_REVISION4) return STATUS_INVALID_PARAMETER;
595 }
596 else if (Ace->Header.AceType > ACCESS_MAX_MS_V2_ACE_TYPE)
597 {
598 if (AclRevision < ACL_REVISION3) return STATUS_INVALID_PARAMETER;
599 }
600 }
601
602 /* Move to the next ACE */
603 Ace = (PACE)((ULONG_PTR)Ace + Ace->Header.AceSize);
604 }
605
606 /* Bail out if there's no more space for us */
607 if ((ULONG_PTR)Ace > ((ULONG_PTR)AceList + AceListLength))
608 {
609 return STATUS_INVALID_PARAMETER;
610 }
611
612 /* Bail out if there's no free ACE spot, or if we would overflow it */
613 if (!(FreeAce) ||
614 ((ULONG_PTR)FreeAce + AceListLength > (ULONG_PTR)Acl + Acl->AclSize))
615 {
616 return STATUS_BUFFER_TOO_SMALL;
617 }
618
619 /* Go down the list until we find our index */
620 Ace = (PACE)(Acl + 1);
621 for (Index = 0; (Index < StartingIndex) && (Index < Acl->AceCount); Index++)
622 {
623 Ace = (PACE)((ULONG_PTR)Ace + Ace->Header.AceSize);
624 }
625
626 /* Found where we want to do, add us to the list */
627 RtlpAddData(AceList,
628 AceListLength,
629 Ace,
630 (ULONG_PTR)FreeAce - (ULONG_PTR)Ace);
631
632 /* Update the header and return */
633 Acl->AceCount += NewAceCount;
634 Acl->AclRevision = (UCHAR)min(Acl->AclRevision, AclRevision);
635 return STATUS_SUCCESS;
636}
637
638/*
639 * @implemented
640 */
641NTSTATUS
642NTAPI
643RtlDeleteAce(IN PACL Acl,
644 IN ULONG AceIndex)
645{
646 PACE FreeAce, Ace;
647 PAGED_CODE_RTL();
648
649 /* Bail out if the ACL is invalid */
650 if (!RtlValidAcl(Acl)) return STATUS_INVALID_PARAMETER;
651
652 /* Bail out if there's no space or if we're full */
653 if ((Acl->AceCount <= AceIndex) || !(RtlFirstFreeAce(Acl, &FreeAce)))
654 {
655 return STATUS_INVALID_PARAMETER;
656 }
657
658 /* Enumerate until the indexed ACE is reached */
659 Ace = (PACE)(Acl + 1);
660 while (AceIndex--) Ace = (PACE)((ULONG_PTR)Ace + Ace->Header.AceSize);
661
662 /* Delete this ACE */
663 RtlpDeleteData(Ace,
664 Ace->Header.AceSize,
665 (ULONG)((ULONG_PTR)FreeAce - (ULONG_PTR)Ace));
666
667 /* Decrease an ACE and return success */
668 Acl->AceCount--;
669 return STATUS_SUCCESS;
670}
671
672/*
673 * @implemented
674 */
675NTSTATUS
676NTAPI
677RtlCreateAcl(IN PACL Acl,
678 IN ULONG AclSize,
679 IN ULONG AclRevision)
680{
681 PAGED_CODE_RTL();
682
683 /* Bail out if too small */
684 if (AclSize < sizeof(ACL)) return STATUS_BUFFER_TOO_SMALL;
685
686 /* Bail out if too large or invalid revision */
687 if ((AclRevision < MIN_ACL_REVISION) ||
688 (AclRevision > MAX_ACL_REVISION) ||
689 (AclSize > MAXUSHORT))
690 {
691 return STATUS_INVALID_PARAMETER;
692 }
693
694 /* Setup the header */
695 Acl->AclSize = (USHORT)ROUND_UP(AclSize, 4);
696 Acl->AclRevision = (UCHAR)AclRevision;
697 Acl->AceCount = 0;
698 Acl->Sbz1 = 0;
699 Acl->Sbz2 = 0;
700 return STATUS_SUCCESS;
701}
702
703/*
704 * @implemented
705 */
706NTSTATUS
707NTAPI
708RtlQueryInformationAcl(IN PACL Acl,
709 IN PVOID Information,
710 IN ULONG InformationLength,
711 IN ACL_INFORMATION_CLASS InformationClass)
712{
713 PACE Ace;
714 PACL_REVISION_INFORMATION RevisionInfo;
715 PACL_SIZE_INFORMATION SizeInfo;
716 PAGED_CODE_RTL();
717
718 /* Validate the ACL revision */
719 if ((Acl->AclRevision < MIN_ACL_REVISION) ||
720 (Acl->AclRevision > MAX_ACL_REVISION))
721 {
722 return STATUS_INVALID_PARAMETER;
723 }
724
725 /* Check what the caller is querying */
726 switch (InformationClass)
727 {
728 /* Revision data */
729 case AclRevisionInformation:
730
731 /* Bail out if the buffer is too small */
732 if (InformationLength < sizeof(ACL_REVISION_INFORMATION))
733 {
734 return STATUS_BUFFER_TOO_SMALL;
735 }
736
737 /* Return the current revision */
738 RevisionInfo = (PACL_REVISION_INFORMATION)Information;
739 RevisionInfo->AclRevision = Acl->AclRevision;
740 break;
741
742 /* Size data */
743 case AclSizeInformation:
744
745 /* Bail out if the buffer is too small */
746 if (InformationLength < sizeof(ACL_SIZE_INFORMATION))
747 {
748 return STATUS_BUFFER_TOO_SMALL;
749 }
750
751 /* Bail out if there's no space in the ACL */
752 if (!RtlFirstFreeAce(Acl, &Ace)) return STATUS_INVALID_PARAMETER;
753
754 /* Read the number of ACEs and check if there was a free ACE */
755 SizeInfo = (PACL_SIZE_INFORMATION)Information;
756 SizeInfo->AceCount = Acl->AceCount;
757 if (Ace)
758 {
759 /* Return how much space there is in the ACL */
760 SizeInfo->AclBytesInUse = (ULONG_PTR)Ace - (ULONG_PTR)Acl;
761 SizeInfo->AclBytesFree = Acl->AclSize - SizeInfo->AclBytesInUse;
762 }
763 else
764 {
765 /* No free ACE, means the whole ACL is full */
766 SizeInfo->AclBytesInUse = Acl->AclSize;
767 SizeInfo->AclBytesFree = 0;
768 }
769 break;
770
771 default:
772 /* Anything else is illegal */
773 return STATUS_INVALID_INFO_CLASS;
774 }
775
776 /* All done */
777 return STATUS_SUCCESS;
778}
779
780/*
781 * @implemented
782 */
783NTSTATUS
784NTAPI
785RtlSetInformationAcl(IN PACL Acl,
786 IN PVOID Information,
787 IN ULONG InformationLength,
788 IN ACL_INFORMATION_CLASS InformationClass)
789{
790 PACL_REVISION_INFORMATION Info ;
791 PAGED_CODE_RTL();
792
793 /* Validate the ACL revision */
794 if ((Acl->AclRevision < MIN_ACL_REVISION) ||
795 (Acl->AclRevision > MAX_ACL_REVISION))
796 {
797 return STATUS_INVALID_PARAMETER;
798 }
799
800 /* What is the caller trying to set? */
801 switch (InformationClass)
802 {
803 /* This is the only info class */
804 case AclRevisionInformation:
805
806 /* Make sure the buffer is large enough */
807 if (InformationLength < sizeof(ACL_REVISION_INFORMATION))
808 {
809 return STATUS_BUFFER_TOO_SMALL;
810 }
811
812 /* Make sure the new revision is within the acceptable bounds*/
813 Info = (PACL_REVISION_INFORMATION)Information;
814 if (Acl->AclRevision >= Info->AclRevision)
815 {
816 return STATUS_INVALID_PARAMETER;
817 }
818
819 /* Set the new revision */
820 Acl->AclRevision = (BYTE)Info->AclRevision;
821 break;
822
823 default:
824 /* Anything else is invalid */
825 return STATUS_INVALID_INFO_CLASS;
826 }
827
828 /* All good */
829 return STATUS_SUCCESS;
830}
831
832/*
833 * @implemented
834 */
835BOOLEAN
836NTAPI
837RtlValidAcl(IN PACL Acl)
838{
839 PACE_HEADER Ace;
840 PISID Sid;
841 ULONG i;
842 USHORT RequiredObjectAceSize;
843 PULONG Flags;
844 ULONG GuidSize;
845 PAGED_CODE_RTL();
846
847 _SEH2_TRY
848 {
849 /* First, validate the revision */
850 if ((Acl->AclRevision < MIN_ACL_REVISION) ||
851 (Acl->AclRevision > MAX_ACL_REVISION))
852 {
853 DPRINT1("Invalid ACL revision: %u\n", Acl->AclRevision);
854 _SEH2_YIELD(return FALSE);
855 }
856
857 /* Next, validate that the ACL is USHORT-aligned */
858 if (ROUND_DOWN(Acl->AclSize, sizeof(USHORT)) != Acl->AclSize)
859 {
860 DPRINT1("Misaligned ACL size: %u\n", Acl->AclSize);
861 _SEH2_YIELD(return FALSE);
862 }
863
864 /* And that it's big enough */
865 if (Acl->AclSize < sizeof(ACL))
866 {
867 DPRINT1("Too small ACL size: %u\n", Acl->AclSize);
868 _SEH2_YIELD(return FALSE);
869 }
870
871 /* Loop each ACE */
872 Ace = (PACE_HEADER)((ULONG_PTR)Acl + sizeof(ACL));
873 for (i = 0; i < Acl->AceCount; i++)
874 {
875 /* Validate we have space for this ACE header */
876 if (((ULONG_PTR)Ace + sizeof(ACE_HEADER)) >= ((ULONG_PTR)Acl + Acl->AclSize))
877 {
878 DPRINT1("Invalid ACE size\n");
879 _SEH2_YIELD(return FALSE);
880 }
881
882 /* Validate the length of this ACE */
883 if (ROUND_DOWN(Ace->AceSize, sizeof(USHORT)) != Ace->AceSize)
884 {
885 DPRINT1("Invalid ACE size: %lx\n", Ace->AceSize);
886 _SEH2_YIELD(return FALSE);
887 }
888
889 /* Validate we have space for the entire ACE */
890 if (((ULONG_PTR)Ace + Ace->AceSize) > ((ULONG_PTR)Acl + Acl->AclSize))
891 {
892 DPRINT1("Invalid ACE size %lx %lx\n", Ace->AceSize, Acl->AclSize);
893 _SEH2_YIELD(return FALSE);
894 }
895
896 /* Check what kind of ACE this is */
897 if (Ace->AceType <= ACCESS_MAX_MS_V2_ACE_TYPE)
898 {
899 /* Validate the length of this ACE */
900 if (ROUND_DOWN(Ace->AceSize, sizeof(ULONG)) != Ace->AceSize)
901 {
902 DPRINT1("Invalid ACE size\n");
903 _SEH2_YIELD(return FALSE);
904 }
905
906 /* The ACE size should at least have enough for the header */
907 if (Ace->AceSize < sizeof(ACE_HEADER))
908 {
909 DPRINT1("Invalid ACE size: %lx %lx\n", Ace->AceSize, sizeof(ACE_HEADER));
910 _SEH2_YIELD(return FALSE);
911 }
912
913 /* Check if the SID revision is valid */
914 Sid = (PISID)&((PKNOWN_ACE)Ace)->SidStart;
915 if (Sid->Revision != SID_REVISION)
916 {
917 DPRINT1("Invalid SID\n");
918 _SEH2_YIELD(return FALSE);
919 }
920
921 /* Check if the SID is out of bounds */
922 if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES)
923 {
924 DPRINT1("Invalid SID\n");
925 _SEH2_YIELD(return FALSE);
926 }
927
928 /* The ACE size should at least have enough for the header and SID */
929 if (Ace->AceSize < (sizeof(ACE_HEADER) + RtlLengthSid(Sid)))
930 {
931 DPRINT1("Invalid ACE size\n");
932 _SEH2_YIELD(return FALSE);
933 }
934 }
935 else if (Ace->AceType == ACCESS_ALLOWED_OBJECT_ACE_TYPE ||
936 Ace->AceType == ACCESS_DENIED_OBJECT_ACE_TYPE)
937 {
938 /* Object ACEs are supported starting with Revision 4 */
939 if (Acl->AclRevision < ACL_REVISION4)
940 {
941 DPRINT1("Invalid ACL revision for Object ACE: %u\n", Acl->AclRevision);
942 _SEH2_YIELD(return FALSE);
943 }
944
945 /* Validate the length of this ACE */
946 if (ROUND_DOWN(Ace->AceSize, sizeof(ULONG)) != Ace->AceSize)
947 {
948 DPRINT1("Misaligned Object ACE size: %lx\n", Ace->AceSize);
949 _SEH2_YIELD(return FALSE);
950 }
951
952 /* The ACE size should at least have enough space for the known object ACE header */
953 if (Ace->AceSize < sizeof(KNOWN_OBJECT_ACE))
954 {
955 DPRINT1("Too small Object ACE size to hold KNOWN_OBJECT_ACE header: %lx\n", Ace->AceSize);
956 _SEH2_YIELD(return FALSE);
957 }
958
959 /* This ACL may have multiple Object ACEs so reset the size counter */
960 GuidSize = 0;
961
962 /* If we have GUIDs include them */
963 Flags = (PULONG)&((PKNOWN_OBJECT_ACE)Ace)->Flags;
964 if (*Flags & ACE_OBJECT_TYPE_PRESENT)
965 {
966 GuidSize += sizeof(GUID);
967 }
968
969 if (*Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT)
970 {
971 GuidSize += sizeof(GUID);
972 }
973
974 /* Check if the SID revision is valid */
975 Sid = (PISID)((ULONG_PTR)&((PKNOWN_OBJECT_ACE)Ace)->SidStart + GuidSize);
976 if (Sid->Revision != SID_REVISION)
977 {
978 DPRINT1("Object ACE SID has invalid revision: %u\n", Sid->Revision);
979 _SEH2_YIELD(return FALSE);
980 }
981
982 /* Check if the SID is out of bounds */
983 if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES)
984 {
985 DPRINT1("Object ACE SID's sub-authority count is out of bounds: %u\n", Sid->SubAuthorityCount);
986 _SEH2_YIELD(return FALSE);
987 }
988
989 /* The ACE size should at least have enough space for the known object ACE header, GUIDs and the SID */
990 RequiredObjectAceSize = (sizeof(KNOWN_OBJECT_ACE) - sizeof(ULONG)) + GuidSize + RtlLengthSid(Sid);
991 if (Ace->AceSize < RequiredObjectAceSize)
992 {
993 DPRINT1("Too small Object ACE size: AceSize %u RequiredSize %u\n", Ace->AceSize, RequiredObjectAceSize);
994 _SEH2_YIELD(return FALSE);
995 }
996 }
997 else if (Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE)
998 {
999 DPRINT1("Unsupported ACE in ReactOS, assuming valid\n");
1000 }
1001 else if ((Ace->AceType >= ACCESS_MIN_MS_OBJECT_ACE_TYPE) &&
1002 (Ace->AceType <= ACCESS_MAX_MS_OBJECT_ACE_TYPE))
1003 {
1004 DPRINT1("Unsupported ACE in ReactOS, assuming valid\n");
1005 }
1006 else
1007 {
1008 /* Unknown ACE, see if it's as big as a header at least */
1009 if (Ace->AceSize < sizeof(ACE_HEADER))
1010 {
1011 DPRINT1("Unknown ACE\n");
1012 _SEH2_YIELD(return FALSE);
1013 }
1014 }
1015
1016 /* Move to the next ace */
1017 Ace = (PACE_HEADER)((ULONG_PTR)Ace + Ace->AceSize);
1018 }
1019 }
1020 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1021 {
1022 /* Something was invalid, fail */
1023 _SEH2_YIELD(return FALSE);
1024 }
1025 _SEH2_END;
1026
1027 /* The ACL looks ok */
1028 return TRUE;
1029}
1030
1031/* EOF */