xref: /reactos/sdk/lib/rtl/acl.c (revision 84ccccab)
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 
18 BOOLEAN
19 NTAPI
20 RtlFirstFreeAce(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 
48 VOID
49 NTAPI
50 RtlpAddData(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 
67 VOID
68 NTAPI
69 RtlpDeleteData(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 
88 NTSTATUS
89 NTAPI
90 RtlpAddKnownAce(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 
156 NTSTATUS
157 NTAPI
158 RtlpAddKnownObjectAce(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  */
260 NTSTATUS
261 NTAPI
262 RtlAddAccessAllowedAce(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  */
281 NTSTATUS
282 NTAPI
283 RtlAddAccessAllowedAceEx(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  */
303 NTSTATUS
304 NTAPI
305 RtlAddAccessAllowedObjectAce(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  */
341 NTSTATUS
342 NTAPI
343 RtlAddAccessDeniedAce(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  */
362 NTSTATUS
363 NTAPI
364 RtlAddAccessDeniedAceEx(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  */
384 NTSTATUS
385 NTAPI
386 RtlAddAccessDeniedObjectAce(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  */
422 NTSTATUS
423 NTAPI
424 RtlAddAuditAccessAce(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  */
450 NTSTATUS
451 NTAPI
452 RtlAddAuditAccessAceEx(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  */
478 NTSTATUS
479 NTAPI
480 RtlAddAuditAccessObjectAce(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  */
520 NTSTATUS
521 NTAPI
522 RtlGetAce(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  */
564 NTSTATUS
565 NTAPI
566 RtlAddAce(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  */
641 NTSTATUS
642 NTAPI
643 RtlDeleteAce(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  */
675 NTSTATUS
676 NTAPI
677 RtlCreateAcl(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  */
706 NTSTATUS
707 NTAPI
708 RtlQueryInformationAcl(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  */
783 NTSTATUS
784 NTAPI
785 RtlSetInformationAcl(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  */
835 BOOLEAN
836 NTAPI
837 RtlValidAcl(IN PACL Acl)
838 {
839     PACE_HEADER Ace;
840     PISID Sid;
841     ULONG i;
842     PAGED_CODE_RTL();
843 
844     _SEH2_TRY
845     {
846         /* First, validate the revision */
847         if ((Acl->AclRevision < MIN_ACL_REVISION) ||
848             (Acl->AclRevision > MAX_ACL_REVISION))
849         {
850             DPRINT1("Invalid ACL revision\n");
851             _SEH2_YIELD(return FALSE);
852         }
853 
854         /* Next, validate that the ACL is USHORT-aligned */
855         if (ROUND_DOWN(Acl->AclSize, sizeof(USHORT)) != Acl->AclSize)
856         {
857             DPRINT1("Invalid ACL size\n");
858             _SEH2_YIELD(return FALSE);
859         }
860 
861         /* And that it's big enough */
862         if (Acl->AclSize < sizeof(ACL))
863         {
864             DPRINT1("Invalid ACL size\n");
865             _SEH2_YIELD(return FALSE);
866         }
867 
868         /* Loop each ACE */
869         Ace = (PACE_HEADER)((ULONG_PTR)Acl + sizeof(ACL));
870         for (i = 0; i < Acl->AceCount; i++)
871         {
872             /* Validate we have space for this ACE header */
873             if (((ULONG_PTR)Ace + sizeof(ACE_HEADER)) >= ((ULONG_PTR)Acl + Acl->AclSize))
874             {
875                 DPRINT1("Invalid ACE size\n");
876                 _SEH2_YIELD(return FALSE);
877             }
878 
879             /* Validate the length of this ACE */
880             if (ROUND_DOWN(Ace->AceSize, sizeof(USHORT)) != Ace->AceSize)
881             {
882                 DPRINT1("Invalid ACE size: %lx\n", Ace->AceSize);
883                 _SEH2_YIELD(return FALSE);
884             }
885 
886             /* Validate we have space for the entire ACE */
887             if (((ULONG_PTR)Ace + Ace->AceSize) > ((ULONG_PTR)Acl + Acl->AclSize))
888             {
889                 DPRINT1("Invalid ACE size %lx %lx\n", Ace->AceSize, Acl->AclSize);
890                 _SEH2_YIELD(return FALSE);
891             }
892 
893             /* Check what kind of ACE this is */
894             if (Ace->AceType <= ACCESS_MAX_MS_V2_ACE_TYPE)
895             {
896                 /* Validate the length of this ACE */
897                 if (ROUND_DOWN(Ace->AceSize, sizeof(ULONG)) != Ace->AceSize)
898                 {
899                     DPRINT1("Invalid ACE size\n");
900                     _SEH2_YIELD(return FALSE);
901                 }
902 
903                 /* The ACE size should at least have enough for the header */
904                 if (Ace->AceSize < sizeof(ACE_HEADER))
905                 {
906                     DPRINT1("Invalid ACE size: %lx %lx\n", Ace->AceSize, sizeof(ACE_HEADER));
907                     _SEH2_YIELD(return FALSE);
908                 }
909 
910                 /* Check if the SID revision is valid */
911                 Sid = (PISID)&((PKNOWN_ACE)Ace)->SidStart;
912                 if (Sid->Revision != SID_REVISION)
913                 {
914                     DPRINT1("Invalid SID\n");
915                     _SEH2_YIELD(return FALSE);
916                 }
917 
918                 /* Check if the SID is out of bounds */
919                 if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES)
920                 {
921                     DPRINT1("Invalid SID\n");
922                     _SEH2_YIELD(return FALSE);
923                 }
924 
925                 /* The ACE size should at least have enough for the header and SID */
926                 if (Ace->AceSize < (sizeof(ACE_HEADER) + RtlLengthSid(Sid)))
927                 {
928                     DPRINT1("Invalid ACE size\n");
929                     _SEH2_YIELD(return FALSE);
930                 }
931             }
932             else if (Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE)
933             {
934                 DPRINT1("Unsupported ACE in ReactOS, assuming valid\n");
935             }
936             else if ((Ace->AceType >= ACCESS_MIN_MS_OBJECT_ACE_TYPE) &&
937                      (Ace->AceType <= ACCESS_MAX_MS_OBJECT_ACE_TYPE))
938             {
939                 DPRINT1("Unsupported ACE in ReactOS, assuming valid\n");
940             }
941             else
942             {
943                 /* Unknown ACE, see if it's as big as a header at least */
944                 if (Ace->AceSize < sizeof(ACE_HEADER))
945                 {
946                     DPRINT1("Unknown ACE\n");
947                     _SEH2_YIELD(return FALSE);
948                 }
949             }
950 
951             /* Move to the next ace */
952             Ace = (PACE_HEADER)((ULONG_PTR)Ace + Ace->AceSize);
953         }
954     }
955     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
956     {
957         /* Something was invalid, fail */
958         _SEH2_YIELD(return FALSE);
959     }
960     _SEH2_END;
961 
962     /* The ACL looks ok */
963     return TRUE;
964 }
965 
966 /* EOF */
967