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