/*++

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;

}