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