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;
|
||
|
||
}
|