xref: /reactos/sdk/lib/rtl/acl.c (revision cad780e1)
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
RtlFirstFreeAce(IN PACL Acl,OUT PACE * FirstFreeAce)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
RtlpAddData(IN PVOID AceList,IN ULONG AceListLength,IN PVOID Ace,IN ULONG Offset)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
RtlpDeleteData(IN PVOID Ace,IN ULONG AceSize,IN ULONG Offset)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
RtlpAddKnownAce(IN PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN PSID Sid,IN UCHAR Type)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
RtlpAddKnownObjectAce(IN PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN GUID * ObjectTypeGuid OPTIONAL,IN GUID * InheritedObjectTypeGuid OPTIONAL,IN PSID Sid,IN UCHAR Type)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
RtlAddAccessAllowedAce(IN OUT PACL Acl,IN ULONG Revision,IN ACCESS_MASK AccessMask,IN PSID Sid)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
RtlAddAccessAllowedAceEx(IN OUT PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN PSID Sid)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
RtlAddAccessAllowedObjectAce(IN OUT PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN GUID * ObjectTypeGuid OPTIONAL,IN GUID * InheritedObjectTypeGuid OPTIONAL,IN PSID Sid)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
RtlAddAccessDeniedAce(IN PACL Acl,IN ULONG Revision,IN ACCESS_MASK AccessMask,IN PSID Sid)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
RtlAddAccessDeniedAceEx(IN OUT PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN PSID Sid)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
RtlAddAccessDeniedObjectAce(IN OUT PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN GUID * ObjectTypeGuid OPTIONAL,IN GUID * InheritedObjectTypeGuid OPTIONAL,IN PSID Sid)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
RtlAddAuditAccessAce(IN PACL Acl,IN ULONG Revision,IN ACCESS_MASK AccessMask,IN PSID Sid,IN BOOLEAN Success,IN BOOLEAN Failure)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
RtlAddAuditAccessAceEx(IN PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN PSID Sid,IN BOOLEAN Success,IN BOOLEAN Failure)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
RtlAddAuditAccessObjectAce(IN PACL Acl,IN ULONG Revision,IN ULONG Flags,IN ACCESS_MASK AccessMask,IN GUID * ObjectTypeGuid OPTIONAL,IN GUID * InheritedObjectTypeGuid OPTIONAL,IN PSID Sid,IN BOOLEAN Success,IN BOOLEAN Failure)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
RtlGetAce(IN PACL Acl,IN ULONG AceIndex,OUT PVOID * Ace)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
RtlAddAce(IN PACL Acl,IN ULONG AclRevision,IN ULONG StartingIndex,IN PVOID AceList,IN ULONG AceListLength)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
RtlDeleteAce(IN PACL Acl,IN ULONG AceIndex)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
RtlCreateAcl(IN PACL Acl,IN ULONG AclSize,IN ULONG AclRevision)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
RtlQueryInformationAcl(IN PACL Acl,IN PVOID Information,IN ULONG InformationLength,IN ACL_INFORMATION_CLASS InformationClass)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
RtlSetInformationAcl(IN PACL Acl,IN PVOID Information,IN ULONG InformationLength,IN ACL_INFORMATION_CLASS InformationClass)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
RtlValidAcl(IN PACL Acl)837 RtlValidAcl(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 */
1032