524 lines
11 KiB
C
524 lines
11 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Acledit.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This Module implements the Acl rtl editing functions that are defined in
|
|||
|
ntseapi.h
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Gary Kimura (GaryKi) 9-Nov-1989
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Pure Runtime Library Routine
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <nt.h>
|
|||
|
#include <ntrtl.h>
|
|||
|
#include "seopaque.h"
|
|||
|
|
|||
|
//
|
|||
|
// Define the local macros and procedure for this module
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Return a pointer to the first Ace in an Acl (even if the Acl is empty).
|
|||
|
//
|
|||
|
// PACE_HEADER
|
|||
|
// FirstAce (
|
|||
|
// IN PACL Acl
|
|||
|
// );
|
|||
|
//
|
|||
|
|
|||
|
#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
|
|||
|
|
|||
|
VOID
|
|||
|
AddData (
|
|||
|
IN PVOID From,
|
|||
|
IN ULONG FromSize,
|
|||
|
IN PVOID To,
|
|||
|
IN ULONG ToSize
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
DeleteData (
|
|||
|
IN PVOID Data,
|
|||
|
IN ULONG RemoveSize,
|
|||
|
IN ULONG TotalSize
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RtlMakePosixAcl(
|
|||
|
IN ULONG AclRevision,
|
|||
|
IN PSID UserSid,
|
|||
|
IN PSID GroupSid,
|
|||
|
IN ACCESS_MASK UserAccess,
|
|||
|
IN ACCESS_MASK GroupAccess,
|
|||
|
IN ACCESS_MASK OtherAccess,
|
|||
|
IN ULONG AclLength,
|
|||
|
OUT PACL Acl,
|
|||
|
OUT PULONG ReturnLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
|
|||
|
|
|||
|
Make an ACL representing Posix protection from AccessMask and
|
|||
|
security account ID (SID) information.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AclRevision - Indicates the ACL revision level of the access masks
|
|||
|
provided. The ACL generated will be revision compatible with this
|
|||
|
value and will not be a higher revision than this value.
|
|||
|
|
|||
|
UserSid - Provides the SID of the user (owner).
|
|||
|
|
|||
|
GroupSid - Provides the SID of the primary group.
|
|||
|
|
|||
|
UserAccess - Specifies the accesses to be given to the user (owner).
|
|||
|
|
|||
|
GroupAccess - Specifies the accesses to be given to the primary group.
|
|||
|
|
|||
|
OtherAccess - Specifies the accesses to be given to others (WORLD).
|
|||
|
|
|||
|
AclLength - Provides the length (in bytes) of the Acl buffer.
|
|||
|
|
|||
|
Acl - Points to a buffer to receive the generated ACL.
|
|||
|
|
|||
|
ReturnLength - Returns the actual length needed to store the resultant
|
|||
|
ACL. If this length is greater than that specified in AclLength,
|
|||
|
then STATUS_BUFFER_TOO_SMALL is returned and no ACL is generated.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The service completed successfully.
|
|||
|
|
|||
|
STATUS_UNKNOWN_REVISION - The revision level specified is not supported
|
|||
|
by this service.
|
|||
|
|
|||
|
STATUS_BUFFER_TOO_SMALL - Indicates the length of the output buffer
|
|||
|
wasn't large enough to hold the generated ACL. The length needed
|
|||
|
is returned via the ReturnLength parameter.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|||
|
|
|||
|
ULONG UserSidLength;
|
|||
|
ULONG GroupSidLength;
|
|||
|
ULONG WorldSidLength;
|
|||
|
ULONG RequiredAclSize;
|
|||
|
ULONG AceSize;
|
|||
|
ULONG CurrentAce;
|
|||
|
PACCESS_ALLOWED_ACE Ace;
|
|||
|
NTSTATUS Status;
|
|||
|
PSID WorldSid;
|
|||
|
|
|||
|
if (!RtlValidSid( UserSid ) || !RtlValidSid( GroupSid )) {
|
|||
|
return( STATUS_INVALID_SID );
|
|||
|
}
|
|||
|
|
|||
|
UserSidLength = RtlLengthSid( UserSid );
|
|||
|
GroupSidLength = RtlLengthSid( GroupSid );
|
|||
|
WorldSidLength = RtlLengthRequiredSid( 1 );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Figure out how much room we need for an ACL and three
|
|||
|
// ACCESS_ALLOWED Ace's
|
|||
|
//
|
|||
|
|
|||
|
RequiredAclSize = sizeof( ACL );
|
|||
|
|
|||
|
AceSize = sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG );
|
|||
|
|
|||
|
RequiredAclSize += (AceSize * 3) +
|
|||
|
UserSidLength +
|
|||
|
GroupSidLength +
|
|||
|
WorldSidLength ;
|
|||
|
|
|||
|
if (RequiredAclSize > AclLength) {
|
|||
|
*ReturnLength = RequiredAclSize;
|
|||
|
return( STATUS_BUFFER_TOO_SMALL );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The passed buffer is big enough, build the ACL in it.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlCreateAcl(
|
|||
|
Acl,
|
|||
|
RequiredAclSize,
|
|||
|
AclRevision
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce(
|
|||
|
Acl,
|
|||
|
ACL_REVISION2,
|
|||
|
UserAccess,
|
|||
|
UserSid
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce(
|
|||
|
Acl,
|
|||
|
ACL_REVISION2,
|
|||
|
GroupAccess,
|
|||
|
GroupSid
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlAllocateAndInitializeSid(&WorldSidAuthority, 1,
|
|||
|
SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSid);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce(
|
|||
|
Acl,
|
|||
|
ACL_REVISION2,
|
|||
|
OtherAccess,
|
|||
|
WorldSid
|
|||
|
);
|
|||
|
|
|||
|
RtlFreeSid(WorldSid);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
return( STATUS_SUCCESS );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RtlInterpretPosixAcl(
|
|||
|
IN ULONG AclRevision,
|
|||
|
IN PSID UserSid,
|
|||
|
IN PSID GroupSid,
|
|||
|
IN PACL Acl,
|
|||
|
OUT PACCESS_MASK UserAccess,
|
|||
|
OUT PACCESS_MASK GroupAccess,
|
|||
|
OUT PACCESS_MASK OtherAccess
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
|
|||
|
|
|||
|
Interpret an ACL representing Posix protection, returning AccessMasks.
|
|||
|
Use security account IDs (SIDs) for object owner and primary group
|
|||
|
identification.
|
|||
|
|
|||
|
This algorithm will pick up the first match of a given SID and ignore
|
|||
|
all further matches of that SID. The first unrecognized SID becomes
|
|||
|
the "other" SID.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AclRevision - Indicates the ACL revision level of the access masks to
|
|||
|
be returned.
|
|||
|
|
|||
|
UserSid - Provides the SID of the user (owner).
|
|||
|
|
|||
|
GroupSid - Provides the SID of the primary group.
|
|||
|
|
|||
|
Acl - Points to a buffer containing the ACL to interpret.
|
|||
|
|
|||
|
UserAccess - Receives the accesses allowed for the user (owner).
|
|||
|
|
|||
|
GroupAccess - Receives the accesses allowed for the primary group.
|
|||
|
|
|||
|
OtherAccess - Receives the accesses allowed for others (WORLD).
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The service completed successfully.
|
|||
|
|
|||
|
STATUS_UNKNOWN_REVISION - The revision level specified is not supported
|
|||
|
by this service.
|
|||
|
|
|||
|
STATUS_EXTRENEOUS_INFORMATION - This warning status value indicates the
|
|||
|
ACL contained protection or other information unrelated to Posix
|
|||
|
style protection. This is a warning only. The interpretation was
|
|||
|
otherwise successful and all access masks were returned.
|
|||
|
|
|||
|
STATUS_COULD_NOT_INTERPRET - Indicates the ACL does not contain
|
|||
|
sufficient Posix style (user/group) protection information. The
|
|||
|
ACL could not be interpreted.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
BOOLEAN UserFound = FALSE;
|
|||
|
BOOLEAN GroupFound = FALSE;
|
|||
|
BOOLEAN OtherFound = FALSE;
|
|||
|
ULONG i;
|
|||
|
PKNOWN_ACE Ace;
|
|||
|
|
|||
|
*UserAccess = *GroupAccess = *OtherAccess = 0;
|
|||
|
|
|||
|
if (AclRevision != ACL_REVISION2) {
|
|||
|
return( STATUS_UNKNOWN_REVISION );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Special case for ACLs that are just "Everyone: Full Control".
|
|||
|
//
|
|||
|
|
|||
|
if (Acl->AceCount < 3) {
|
|||
|
SID_IDENTIFIER_AUTHORITY WorldSidAuth = SECURITY_WORLD_SID_AUTHORITY;
|
|||
|
PSID WorldSid;
|
|||
|
|
|||
|
Status = RtlAllocateAndInitializeSid(&WorldSidAuth, 1,
|
|||
|
SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSid);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlGetAce(Acl, 0, (PVOID *)&Ace);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
RtlFreeSid(WorldSid);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
if (RtlEqualSid((PSID)&Ace->SidStart, WorldSid)) {
|
|||
|
*UserAccess = *GroupAccess = *OtherAccess = Ace->Mask;
|
|||
|
RtlFreeSid(WorldSid);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
RtlFreeSid(WorldSid);
|
|||
|
}
|
|||
|
|
|||
|
for (i = 0; i < Acl->AceCount && (!UserFound || !GroupFound || !OtherFound);
|
|||
|
++i) {
|
|||
|
Status = RtlGetAce(Acl, i, (PVOID *)&Ace);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
if (Ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
|
|||
|
Status = STATUS_EXTRANEOUS_INFORMATION;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (RtlEqualSid(
|
|||
|
(PSID)(&Ace->SidStart),
|
|||
|
UserSid
|
|||
|
) && !UserFound) {
|
|||
|
|
|||
|
*UserAccess = Ace->Mask;
|
|||
|
UserFound = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (RtlEqualSid(
|
|||
|
(PSID)(&Ace->SidStart),
|
|||
|
GroupSid
|
|||
|
) && !GroupFound) {
|
|||
|
*GroupAccess = Ace->Mask;
|
|||
|
GroupFound = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// It isn't the user, and it isn't the group, pick it up
|
|||
|
// as "other"
|
|||
|
//
|
|||
|
|
|||
|
if (!OtherFound) {
|
|||
|
*OtherAccess = Ace->Mask;
|
|||
|
OtherFound = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Internal support routine
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
AddData (
|
|||
|
IN PVOID From,
|
|||
|
IN ULONG FromSize,
|
|||
|
IN PVOID To,
|
|||
|
IN ULONG ToSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine copies data to a string of bytes. It does this by moving
|
|||
|
over data in the to string so that the from string will fit. It also
|
|||
|
assumes that the checks that the data will fit in memory have already
|
|||
|
been done. Pictorally the results are as follows.
|
|||
|
|
|||
|
Before:
|
|||
|
|
|||
|
From -> ffffffffff
|
|||
|
|
|||
|
To -> tttttttttttttttt
|
|||
|
|
|||
|
After:
|
|||
|
|
|||
|
From -> ffffffffff
|
|||
|
|
|||
|
To -> fffffffffftttttttttttttttt
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
From - Supplies a pointer to the source buffer
|
|||
|
|
|||
|
FromSize - Supplies the size of the from buffer in bytes
|
|||
|
|
|||
|
To - Supplies a pointer to the destination buffer
|
|||
|
|
|||
|
ToSize - Supplies the size of the to buffer in bytes
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LONG i;
|
|||
|
|
|||
|
//
|
|||
|
// Shift over the To buffer enough to fit in the From buffer
|
|||
|
//
|
|||
|
|
|||
|
for (i = ToSize - 1; i >= 0; i--) {
|
|||
|
|
|||
|
((PUCHAR)To)[i+FromSize] = ((PUCHAR)To)[i];
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now copy over the From buffer
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; (ULONG)i < FromSize; i += 1) {
|
|||
|
|
|||
|
((PUCHAR)To)[i] = ((PUCHAR)From)[i];
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// and return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Internal support routine
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
DeleteData (
|
|||
|
IN PVOID Data,
|
|||
|
IN ULONG RemoveSize,
|
|||
|
IN ULONG TotalSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine deletes a string of bytes from the front of a data buffer
|
|||
|
and compresses the data. It also zeros out the part of the string
|
|||
|
that is no longer in use. Pictorially the results are as follows
|
|||
|
|
|||
|
Before:
|
|||
|
|
|||
|
Data = DDDDDddddd
|
|||
|
RemoveSize = 5
|
|||
|
TotalSize = 10
|
|||
|
|
|||
|
After:
|
|||
|
|
|||
|
Data = ddddd00000
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Data - Supplies a pointer to the data being altered
|
|||
|
|
|||
|
RemoveSize - Supplies the number of bytes to delete from the front
|
|||
|
of the data buffer
|
|||
|
|
|||
|
TotalSize - Supplies the total number of bytes in the data buffer
|
|||
|
before the delete operation
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// Shift over the buffer to remove the amount
|
|||
|
//
|
|||
|
|
|||
|
for (i = RemoveSize; i < TotalSize; i++) {
|
|||
|
|
|||
|
((PUCHAR)Data)[i-RemoveSize] = ((PUCHAR)Data)[i];
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now as a safety precaution we'll zero out the rest of the string
|
|||
|
//
|
|||
|
|
|||
|
for (i = TotalSize - RemoveSize; i < TotalSize; i++) {
|
|||
|
|
|||
|
((PUCHAR)Data)[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|