/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    applyacl.c

Abstract:

    Routines to apply default ACLs to system files and directories
    during setup.

Author:

    Ted Miller (tedm) 16-Feb-1996

Revision History:

--*/

#include "setupp.h"
#pragma hdrstop

#define MAXULONG    0xffffffff

//
// Universal well known SIDs
//
PSID NullSid;
PSID WorldSid;
PSID LocalSid;
PSID CreatorOwnerSid;
PSID CreatorGroupSid;

//
// SIDs defined by NT
//
PSID DialupSid;
PSID NetworkSid;
PSID BatchSid;
PSID InteractiveSid;
PSID ServiceSid;
PSID LocalSystemSid;
PSID AliasAdminsSid;
PSID AliasUsersSid;
PSID AliasGuestsSid;
PSID AliasPowerUsersSid;
PSID AliasAccountOpsSid;
PSID AliasSystemOpsSid;
PSID AliasPrintOpsSid;
PSID AliasBackupOpsSid;
PSID AliasReplicatorSid;


typedef struct _ACE_DATA {
    ACCESS_MASK AccessMask;
    PSID        *Sid;
    UCHAR       AceType;
    UCHAR       AceFlags;
} ACE_DATA, *PACE_DATA;

//
// This structure is valid for access allowed, access denied, audit,
// and alarm ACEs.
//
typedef struct _ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    //
    // The SID follows in the buffer
    //
} ACE, *PACE;


//
// Number of ACEs currently defined for files and directories.
//
#define DIRS_AND_FILES_ACE_COUNT 19

//
// Table describing the data to put into each ACE.
//
// This table will be read during initialization and used to construct a
// series of ACEs.  The index of each ACE in the Aces array defined below
// corresponds to the ordinals used in the ACL section of perms.inf
//
ACE_DATA AceDataTableForDirsAndFiles[DIRS_AND_FILES_ACE_COUNT] = {

    //
    // Index 0 is unused
    //
    { 0,NULL,0,0 },

    //
    // ACE 1
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
        &AliasAccountOpsSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE
    },

    //
    // ACE 2
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &AliasAdminsSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 3
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
        &AliasAdminsSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE
    },

    //
    // ACE 4
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &CreatorOwnerSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 5
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &NetworkSid,
        ACCESS_DENIED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 6
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &AliasPrintOpsSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 7
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
        &AliasReplicatorSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 8
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_EXECUTE,
        &AliasReplicatorSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 9
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &AliasSystemOpsSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 10
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
        &AliasSystemOpsSid,
        ACCESS_ALLOWED_ACE_TYPE,
        OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
    },

    //
    // ACE 11
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &WorldSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 12
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
        &WorldSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE
    },

    //
    // ACE 13
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
        &WorldSid,
        ACCESS_ALLOWED_ACE_TYPE,
        OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
    },

    //
    // ACE 14
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_EXECUTE,
        &WorldSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE
    },

    //
    // ACE 15
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_EXECUTE,
        &WorldSid,
        ACCESS_ALLOWED_ACE_TYPE,
        OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
    },

    //
    // ACE 16
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE,
        &WorldSid,
        ACCESS_ALLOWED_ACE_TYPE,
        OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
    },

    //
    // ACE 17
    // (for files and directories)
    //
    {
        GENERIC_ALL,
        &LocalSystemSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    },

    //
    // ACE 18
    // (for files and directories)
    //
    {
        GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
        &AliasPowerUsersSid,
        ACCESS_ALLOWED_ACE_TYPE,
        CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
    }
};


//
// Array of ACEs to be applied to the objects (files and directories).
// They will be initialized during program startup based on the data in the
// AceDataTable. The index of each element corresponds to the
// ordinals used in the [ACL] section of perms.inf.
//
PACE AcesForDirsAndFiles[DIRS_AND_FILES_ACE_COUNT];

//
// Array that contains the size of each ACE in the
// array AcesForDirsAndFiles. These sizes are needed
// in order to allocate a buffer of the right size
// when we build an ACL.
//
ULONG AceSizesForDirsAndFiles[DIRS_AND_FILES_ACE_COUNT];



VOID
TearDownAces(
    IN OUT PACE*        AcesArray,
    IN     ULONG        ArrayCount
    );


VOID
TearDownSids(
    VOID
    );


DWORD
InitializeSids(
    VOID
    )

/*++

Routine Description:

    This function initializes the global variables used by and exposed
    by security.

Arguments:

    None.

Return Value:

    Win32 error indicating outcome.

--*/

{
    SID_IDENTIFIER_AUTHORITY NullSidAuthority    = SECURITY_NULL_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY WorldSidAuthority   = SECURITY_WORLD_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY LocalSidAuthority   = SECURITY_LOCAL_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY NtAuthority         = SECURITY_NT_AUTHORITY;

    BOOL b = TRUE;

    //
    // Ensure the SIDs are in a well-known state
    //
    NullSid = NULL;
    WorldSid = NULL;
    LocalSid = NULL;
    CreatorOwnerSid = NULL;
    CreatorGroupSid = NULL;
    DialupSid = NULL;
    NetworkSid = NULL;
    BatchSid = NULL;
    InteractiveSid = NULL;
    ServiceSid = NULL;
    LocalSystemSid = NULL;
    AliasAdminsSid = NULL;
    AliasUsersSid = NULL;
    AliasGuestsSid = NULL;
    AliasPowerUsersSid = NULL;
    AliasAccountOpsSid = NULL;
    AliasSystemOpsSid = NULL;
    AliasPrintOpsSid = NULL;
    AliasBackupOpsSid = NULL;
    AliasReplicatorSid = NULL;

    //
    // Allocate and initialize the universal SIDs
    //
    b = b && AllocateAndInitializeSid(
                &NullSidAuthority,
                1,
                SECURITY_NULL_RID,
                0,0,0,0,0,0,0,
                &NullSid
                );

    b = b && AllocateAndInitializeSid(
                &WorldSidAuthority,
                1,
                SECURITY_WORLD_RID,
                0,0,0,0,0,0,0,
                &WorldSid
                );

    b = b && AllocateAndInitializeSid(
                &LocalSidAuthority,
                1,
                SECURITY_LOCAL_RID,
                0,0,0,0,0,0,0,
                &LocalSid
                );

    b = b && AllocateAndInitializeSid(
                &CreatorSidAuthority,
                1,
                SECURITY_CREATOR_OWNER_RID,
                0,0,0,0,0,0,0,
                &CreatorOwnerSid
                );

    b = b && AllocateAndInitializeSid(
                &CreatorSidAuthority,
                1,
                SECURITY_CREATOR_GROUP_RID,
                0,0,0,0,0,0,0,
                &CreatorGroupSid
                );

    //
    // Allocate and initialize the NT defined SIDs
    //
    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                1,
                SECURITY_DIALUP_RID,
                0,0,0,0,0,0,0,
                &DialupSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                1,
                SECURITY_NETWORK_RID,
                0,0,0,0,0,0,0,
                &NetworkSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                1,
                SECURITY_BATCH_RID,
                0,0,0,0,0,0,0,
                &BatchSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                1,
                SECURITY_INTERACTIVE_RID,
                0,0,0,0,0,0,0,
                &InteractiveSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                1,
                SECURITY_SERVICE_RID,
                0,0,0,0,0,0,0,
                &ServiceSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                1,
                SECURITY_LOCAL_SYSTEM_RID,
                0,0,0,0,0,0,0,
                &LocalSystemSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_ADMINS,
                0,0,0,0,0,0,
                &AliasAdminsSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_USERS,
                0,0,0,0,0,0,
                &AliasUsersSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_GUESTS,
                0,0,0,0,0,0,
                &AliasGuestsSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_POWER_USERS,
                0,0,0,0,0,0,
                &AliasPowerUsersSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_ACCOUNT_OPS,
                0,0,0,0,0,0,
                &AliasAccountOpsSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_SYSTEM_OPS,
                0,0,0,0,0,0,
                &AliasSystemOpsSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_PRINT_OPS,
                0,0,0,0,0,0,
                &AliasPrintOpsSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_BACKUP_OPS,
                0,0,0,0,0,0,
                &AliasBackupOpsSid
                );

    b = b && AllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_REPLICATOR,
                0,0,0,0,0,0,
                &AliasReplicatorSid
                );

    if(!b) {
        TearDownSids();
    }

    return(b ? NO_ERROR : GetLastError());
}


VOID
TearDownSids(
    VOID
    )
{
    if(NullSid) {
        FreeSid(NullSid);
    }
    if(WorldSid) {
        FreeSid(WorldSid);
    }
    if(LocalSid) {
        FreeSid(LocalSid);
    }
    if(CreatorOwnerSid) {
        FreeSid(CreatorOwnerSid);
    }
    if(CreatorGroupSid) {
        FreeSid(CreatorGroupSid);
    }
    if(DialupSid) {
        FreeSid(DialupSid);
    }
    if(NetworkSid) {
        FreeSid(NetworkSid);
    }
    if(BatchSid) {
        FreeSid(BatchSid);
    }
    if(InteractiveSid) {
        FreeSid(InteractiveSid);
    }
    if(ServiceSid) {
        FreeSid(ServiceSid);
    }
    if(LocalSystemSid) {
        FreeSid(LocalSystemSid);
    }
    if(AliasAdminsSid) {
        FreeSid(AliasAdminsSid);
    }
    if(AliasUsersSid) {
        FreeSid(AliasUsersSid);
    }
    if(AliasGuestsSid) {
        FreeSid(AliasGuestsSid);
    }
    if(AliasPowerUsersSid) {
        FreeSid(AliasPowerUsersSid);
    }
    if(AliasAccountOpsSid) {
        FreeSid(AliasAccountOpsSid);
    }
    if(AliasSystemOpsSid) {
        FreeSid(AliasSystemOpsSid);
    }
    if(AliasPrintOpsSid) {
        FreeSid(AliasPrintOpsSid);
    }
    if(AliasBackupOpsSid) {
        FreeSid(AliasBackupOpsSid);
    }
    if(AliasReplicatorSid) {
        FreeSid(AliasReplicatorSid);
    }
}



DWORD
InitializeAces(
    IN OUT PACE_DATA    DataTable,
    IN OUT PACE*        AcesArray,
    IN OUT PULONG       AceSizesArray,
    IN     ULONG        ArrayCount
    )

/*++

Routine Description:

    Initializes the array of ACEs as described in the DataTable

Arguments:

    DataTable - Pointer to the array that contains the data
                describing each ACE.
    AcesArray - Array that will contain the ACEs.

    AceSizesArray - Array that contains the sizes for each ACE.

    ArrayCount - Number of elements in each array.

Return Value:

    Win32 error code indicating outcome.

--*/

{
    unsigned u;
    DWORD Length;
    DWORD rc;
    BOOL b;
    DWORD SidLength;

    //
    // Initialize to a known state.
    //
    ZeroMemory(AcesArray,ArrayCount*sizeof(PACE));

    //
    // Create ACEs for each item in the data table.
    // This involves merging the ace data with the SID data, which
    // are initialized in an earlier step.
    //
    for(u=1; u<ArrayCount; u++) {

        SidLength = GetLengthSid(*(DataTable[u].Sid));
        Length = SidLength + sizeof(ACE) + sizeof(ACCESS_MASK)- sizeof(ULONG);
        AceSizesArray[u] = Length;

        AcesArray[u] = malloc(Length);
        if(!AcesArray[u]) {
            TearDownAces(AcesArray, ArrayCount);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        AcesArray[u]->Header.AceType  = DataTable[u].AceType;
        AcesArray[u]->Header.AceFlags = DataTable[u].AceFlags;
        AcesArray[u]->Header.AceSize  = (WORD)Length;

        AcesArray[u]->Mask = DataTable[u].AccessMask;

        b = CopySid(
                SidLength,                           // Length - sizeof(ACE) + sizeof(ULONG),
                (PUCHAR)AcesArray[u] + sizeof(ACE),
                *(DataTable[u].Sid)
                );

        if(!b) {
            rc = GetLastError();
            TearDownAces(AcesArray, ArrayCount);
            return(rc);
        }
    }

    return(NO_ERROR);
}



VOID
TearDownAces(
    IN OUT PACE*        AcesArray,
    IN     ULONG        ArrayCount
    )

/*++

Routine Description:

    Destroys the array of ACEs as described in the DataTable

Arguments:

    None

Return Value:

    None

--*/

{
    unsigned u;


    for(u=1; u<ArrayCount; u++) {

        if(AcesArray[u]) {
            free(AcesArray[u]);
        }
    }
}


ULONG
ApplyAclToDirOrFile(
    IN PCWSTR FullPath,
    IN PULONG AcesToApply,
    IN ULONG  ArraySize
    )

/*++

Routine Description:

    Applies an ACL to a specified file or directory.

Arguments:

    FullPath - supplies full win32 path to the file or directory
        to receive the ACL

    AcesIndexArray - Array that contains the index to the ACEs to be used in the ACL.

    ArraySize - Number of elements in the array.

Return Value:

--*/

{
    DWORD AceCount;
    DWORD Ace;
    INT AceIndex;
    DWORD rc;
    SECURITY_DESCRIPTOR SecurityDescriptor;
    PACL Acl;
    UCHAR AclBuffer[2048];
    BOOL b;
    PCWSTR AclSection;
    ACL_SIZE_INFORMATION AclSizeInfo;

    //
    // Initialize a security descriptor and an ACL.
    // We use a large static buffer to contain the ACL.
    //
    Acl = (PACL)AclBuffer;
    if(!InitializeAcl(Acl,sizeof(AclBuffer),ACL_REVISION2)
    || !InitializeSecurityDescriptor(&SecurityDescriptor,SECURITY_DESCRIPTOR_REVISION)) {
        return(GetLastError());
    }


    //
    // Build up the DACL from the indices on the list we just looked up
    // in the ACL section.
    //
    rc = NO_ERROR;
    AceCount = ArraySize;
    for(Ace=0; Ace < AceCount; Ace++) {
       AceIndex = AcesToApply[ Ace ];
       if((AceIndex == 0) || (AceIndex >= DIRS_AND_FILES_ACE_COUNT)) {
          return(ERROR_INVALID_DATA);
       }

        b = AddAce(
                Acl,
                ACL_REVISION2,
                MAXULONG,
                AcesForDirsAndFiles[AceIndex],
                AcesForDirsAndFiles[AceIndex]->Header.AceSize
                );

        //
        // Track first error we encounter.
        //
        if(!b) {
            rc = GetLastError();
        }
    }

    if(rc != NO_ERROR) {
        return(rc);
    }

    //
    // Truncate the ACL, since only a fraction of the size we originally
    // allocated for it is likely to be in use.
    //
    if(!GetAclInformation(Acl,&AclSizeInfo,sizeof(ACL_SIZE_INFORMATION),AclSizeInformation)) {
        return(GetLastError());
    }
    Acl->AclSize = (WORD)AclSizeInfo.AclBytesInUse;

    //
    // Add the ACL to the security descriptor as the DACL
    //
    if(!SetSecurityDescriptorDacl(&SecurityDescriptor,TRUE,Acl,FALSE)) {
        return(GetLastError());
    }

    //
    // Finally, apply the security descriptor.
    //
    rc = SetFileSecurity(FullPath,DACL_SECURITY_INFORMATION,&SecurityDescriptor)
       ? NO_ERROR
       : GetLastError();

    return(rc);
}



DWORD
ApplySecurityToRepairInfo(
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    DWORD d, TempError;
    WCHAR Directory[MAX_PATH];
    BOOL SetAclsNt;
    DWORD FsFlags;
    DWORD Result;
    BOOL b;
    ULONG Count;
    PWSTR  Files[] = {
        L"sam",
        L"security",
        L"software",
        L"system",
        L"default",
        L"ntuser.dat",
        L"sam._",
        L"security._",
        L"software._",
        L"system._",
        L"default._",
        L"ntuser.da_"
        };

    //
    // Get the file system of the system drive.
    // On x86 get the file system of the system partition.
    //
    d = NO_ERROR;
    SetAclsNt = FALSE;
    Result = GetWindowsDirectory(Directory,MAX_PATH);
    if(Result == 0) {
        MYASSERT(FALSE);
        return( GetLastError());
    }
    Directory[3] = 0;


    //
    //  ApplySecurity to directories and files, if needed
    //

    b = GetVolumeInformation(Directory,NULL,0,NULL,NULL,&FsFlags,NULL,0);
    if(b && (FsFlags & FS_PERSISTENT_ACLS)) {
        SetAclsNt = TRUE;
    }

    if(SetAclsNt) {
        //
        // Initialize SIDs
        //
        d = InitializeSids();
        if(d != NO_ERROR) {
            return(d);
        }
        //
        // Initialize ACEs
        //
        d = InitializeAces(AceDataTableForDirsAndFiles, AcesForDirsAndFiles, AceSizesForDirsAndFiles, DIRS_AND_FILES_ACE_COUNT);
        if(d != NO_ERROR) {
            TearDownSids();
            return(d);
        }
        //
        // Go do the real work.
        //
        for( Count = 0; Count < sizeof( Files ) / sizeof( PWSTR ); Count++ ) {
            ULONG   AcesToApply[] = {  2,
                                      17
                                    };

            GetWindowsDirectory(Directory,MAX_PATH);
            wcscat( Directory, L"\\repair\\" );
            wcscat( Directory, Files[ Count ] );
            TempError = ApplyAclToDirOrFile( Directory, AcesToApply, sizeof( AcesToApply) / sizeof( ULONG ) );
            if( TempError != NO_ERROR ) {
                if( d == NO_ERROR ) {
                    d = TempError;
                }
            }
        }

        //
        // Clean up.
        //
        TearDownAces(AcesForDirsAndFiles, DIRS_AND_FILES_ACE_COUNT);
        TearDownSids();
    }
    return(d);
}