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