/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    dbpob.c

Abstract:

    LSA Database Object Manager - Private Routines

    These routines perform low-level functions private to the LSA Database
    Object Manager.

Author:

    Scott Birrell       (ScottBi)   	January 8, 1992

Environment:

Revision History:

--*/

#include "lsasrvp.h"
#include "dbp.h"


NTSTATUS
LsapDbLogicalToPhysicalSubKey(
    IN LSAPR_HANDLE ObjectHandle,
    OUT PUNICODE_STRING PhysicalSubKeyNameU,
    IN PUNICODE_STRING LogicalSubKeyNameU
    )

/*++

Routine Description:

    This routine converts a Logical Name of a subkey of an open object to
    the corresponding Physical Name.  The Physical Name of a subkey is the
    hierarchic Registry key name relative to the Registry Key corresponding
    to the LSA Database root object.  It is constructed by extracting the
    Physical Name of the object from its handle and appending "\" and the
    given Logical SubKey name.

Arguments:

    ObjectHandle - Handle to open object from an LsapDbOpenObject call.

    PhysicalSubKeyNameU - Pointer to Unicode string that will receive the
        Physical Name of the subkey.

    LogicalSubKeyNameU - Pointer to Unicode string that contains the
        Logical name of the subkey.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_INSUFFICIENT_RESOURCES - Not enough system resources to
            allocate intermediate and final string buffers needed.
--*/

{
    NTSTATUS Status;

    LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;

    Status = LsapDbJoinSubPaths(
                 &Handle->PhysicalNameU,
                 LogicalSubKeyNameU,
                 PhysicalSubKeyNameU
                 );

    return Status;
}


NTSTATUS
LsapDbJoinSubPaths(
    IN PUNICODE_STRING MajorSubPathU,
    IN PUNICODE_STRING MinorSubPathU,
    OUT PUNICODE_STRING JoinedPathU
    )

/*++

Routine Description:

    This function joins together two parts of a Regsitry SubPath, inserting
    a "\" as a separator.  The Minor SubPath must not begin with a "\".
    Either or both sub path components may be NULL.  Except where both
    sub path components are NULL, memory is always allocated for the output
    buffer.  This memory must be freed when no longer required by calling
    RtlFreeUnicodeString() on the output string.

Arguments:

    MajorSubPathU - Pointer to Unicode String containing an absolute or
        relative subpath.

    MinorSubPathU - Pointer to Unicode String containing a relative
        subpath.

    JoinedPathU - Pointer to Unicode String that will receive the joined
        path.  Memory will be allocated for the JoinedPath buffer.

Return Value:

    NTSTATUS - Standard Nt Result Code

        STATUS_INSUFFICIENT_RESOURCES - Not enough system resources to
            allocate intermediate and final string buffers needed.
--*/

{
    NTSTATUS Status;
    USHORT JoinedPathLength;

    //
    // Compute the size needed for the Joined Sub Path string.
    // The Joined Sub Path has the following form:
    //
    // <Major Sub Path> + L"\" + <Minor Sub Path>
    //
    // where the "+" operator denotes concatenation.
    //
    // If both major and minor sub path are null, then result string
    // is empty.
    //
    // If either major or minor sub path is null, then path separator is
    // omitted.
    //

    if (MajorSubPathU == NULL) {

        //
        // If MinorSubPathU is also NULL, just set the output
        // buffer size to 0.
        //

        if (MinorSubPathU == NULL) {

            JoinedPathU->Length = 0;
            JoinedPathU->Buffer = NULL;
            return STATUS_SUCCESS;
        }

        JoinedPathLength = MinorSubPathU->MaximumLength;

    } else if (MinorSubPathU == NULL) {

        JoinedPathLength = MajorSubPathU->MaximumLength;

    } else {

        JoinedPathLength = MajorSubPathU->Length +
                              (USHORT) sizeof( OBJ_NAME_PATH_SEPARATOR ) +
                              MinorSubPathU->Length;

    }

    //
    // Now allocate buffer for the Joined Sub Path string
    //

    JoinedPathU->Length = 0;
    JoinedPathU->MaximumLength = JoinedPathLength;
    JoinedPathU->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, JoinedPathLength );

    if (JoinedPathU->Buffer == NULL) {
        goto JoinSubPathError;
    }

    if (MajorSubPathU != NULL) {

        Status = RtlAppendUnicodeStringToString( JoinedPathU,
                                                 MajorSubPathU
                                               );

        if (!NT_SUCCESS(Status)) {
            goto JoinSubPathError;
        }

    }

    if (MinorSubPathU != NULL) {

        if (MajorSubPathU != NULL) {

            Status = RtlAppendUnicodeToString( JoinedPathU,
                                               L"\\"
                                             );

            if (!NT_SUCCESS(Status)) {
                goto JoinSubPathError;
            }
        }

        Status = RtlAppendUnicodeStringToString( JoinedPathU,
                                                 MinorSubPathU
                                               );
        if (!NT_SUCCESS(Status)) {
            goto JoinSubPathError;
        }

    }

    return Status;

JoinSubPathError:

    //
    // If necessary, free the Joined Sub Path string buffer.
    //

    if (JoinedPathU->Buffer != NULL) {

        RtlFreeHeap( RtlProcessHeap(), 0, JoinedPathU->Buffer );
    }

    return Status;
}


NTSTATUS
LsapDbCreateSDAttributeObject(
    IN LSAPR_HANDLE ObjectHandle,
    IN OUT PLSAP_DB_OBJECT_INFORMATION ObjectInformation
    )

/*++

Routine Description:

    This function creates the initial Security Descriptor Attribute for an LSA
    Database object.  The DACL in this SD is dependent on the object type.

Arguments:

    ObjectHandle - Handle to open object.

    ObjectInformation - Pointer to Object Information structure containing
        the object's Sid and type.  A pointer to the created SD is filled in.

Return Value:

    NTSTATUS - Standard Nt Result Code.

--*/

{
    NTSTATUS Status;
    ULONG DaclLength;
    PACL Dacl;
    HANDLE LsaProcessHandle = NULL;
    HANDLE LsaProcessTokenHandle = NULL;
    PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
    ULONG SecurityDescriptorLength;
    PSECURITY_DESCRIPTOR ContainerDescriptor = NULL;
    ULONG ContainerDescriptorLength;
    SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
    OBJECT_ATTRIBUTES LsaProcessObjectAttributes;
    SECURITY_DESCRIPTOR CreatorDescriptor;
    LSAP_DB_HANDLE ContainerHandle;
    LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
    PTEB CurrentTeb;

    //
    // We will be creating a Security Descriptor in Self-Relative format.
    // The information that goes into the SD comes from two sources - the Lsa
    // Process's token and the information we provide, such as DACL.  First,
    // we need to open the Lsa process to access its token.
    //

    InitializeObjectAttributes(
        &LsaProcessObjectAttributes,
        NULL,
        0,
        NULL,
        NULL
        );

    CurrentTeb = NtCurrentTeb();

    Status = NtOpenProcess(
                 &LsaProcessHandle,
                 PROCESS_QUERY_INFORMATION,
                 &LsaProcessObjectAttributes,
                 &CurrentTeb->ClientId
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Now open the Lsa process's token with appropriate access
    //

    Status = NtOpenProcessToken(
                 LsaProcessHandle,
                 TOKEN_QUERY,
                 &LsaProcessTokenHandle
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Next, we want to specify a DACL to define the access for the
    // object whose SD is being created.
    //
    // Give GENERIC_ALL and, if the object is deletable, DELETE access to
    // the group DOMAIN_ALIAS_ADMINS.
    // Give GENERIC_EXECUTE access to WORLD.
    //
    // Note that the group ALIAS_ADMINS does NOT require access.  This access is not
    // required because a logon to a member of DOMAIN_ADMIN results in a token
    // being constructed that has ALIAS_ADMINS added (by an Lsa authentication
    // filter routine).
    //
    // Construct a Security Descriptor that will contain only the DACL
    // we want and all other fields set to NULL.
    //

    Status = RtlCreateSecurityDescriptor(
                 &CreatorDescriptor,
                 SECURITY_DESCRIPTOR_REVISION
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Calculate length of DACL required.  It will hold two Access Allowed
    // ACE's, one for DOMAIN_ALIAS_ADMINS and one for WORLD.  The size of the DACL is
    // the size of the ACL header plus the sizes of the ACE's minus a
    // redundant ULONG built into the header.
    //

    DaclLength = sizeof (ACL) - sizeof (ULONG) +
                      sizeof (ACCESS_ALLOWED_ACE ) +
                      RtlLengthSid( LsapAliasAdminsSid ) +
                      sizeof (ACCESS_ALLOWED_ACE ) +
                      RtlLengthSid( LsapWorldSid );

    Status = STATUS_INSUFFICIENT_RESOURCES;

    Dacl = LsapAllocateLsaHeap(DaclLength);

    if (Dacl == NULL) {

        goto CreateSDError;
    }

    Status = RtlCreateAcl(
                 Dacl,
                 DaclLength,
                 ACL_REVISION
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Now add the Access Allowed ACE for the group DOMAIN_ALIAS_ADMINS to the
    // object's DACL.
    //

    Status = RtlAddAccessAllowedAce(
                 Dacl,
                 ACL_REVISION,
                 LsapDbState.DbObjectTypes[Handle->ObjectTypeId].AliasAdminsAccess,
                 LsapAliasAdminsSid
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Now add the Access Allowed ACE for the group WORLD to the
    // object's DACL.
    //

    Status = RtlAddAccessAllowedAce(
                 Dacl,
                 ACL_REVISION,
                 LsapDbState.DbObjectTypes[Handle->ObjectTypeId].WorldAccess,
                 LsapWorldSid
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Set the initial owner of the object
    //

    Status = RtlSetOwnerSecurityDescriptor(
                 &CreatorDescriptor,
                 LsapDbState.DbObjectTypes[Handle->ObjectTypeId].InitialOwnerSid,
                 FALSE
                 );

    if (!NT_SUCCESS(Status)) {

         goto CreateSDError;
    }

    //
    // Hook the newly constructed DACL for the LsaDb object into the
    // Modification Descriptor.
    //

    Status = RtlSetDaclSecurityDescriptor(
                 &CreatorDescriptor,
                 TRUE,
                 Dacl,
                 FALSE
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // If there is a container object, obtain its Security Descriptor so that
    // we can use it as the basis for our new descriptor.  The new
    // descriptor will be equal to the container descriptor with DACL replaced
    // by the Modification Descriptor just constructed.
    //
    // Reading the container SD takes several steps:
    //
    // o Get the length of the Container SD
    // o Allocate a buffer for the SD
    // o Read the SD
    //
    // Obtain the length of the container object's SD by issuing a read for
    // the SecDesc subkey of the container object's Registry key, with a
    // dummy buffer whose size is too small.
    //

    ContainerHandle = (LSAP_DB_HANDLE) ObjectInformation->ObjectAttributes.RootDirectory;

    if (ContainerHandle != NULL) {

        //
        // Obtain the length of the container object's SD by issuing a read for
        // the SecDesc subkey of the container object's Registry key, with a
        // dummy buffer whose size is too small.
        //

        ContainerDescriptorLength = 0;

        Status = LsapDbReadAttributeObject(
                     ContainerHandle,
                     &LsapDbNames[SecDesc],
                     NULL,
                     &ContainerDescriptorLength
                     );

        if (!NT_SUCCESS(Status)) {

            goto CreateSDError;
        }

        //
        // Allocate a buffer from the Lsa Heap for the container object's SD.
        //

        Status = STATUS_INSUFFICIENT_RESOURCES;

        ContainerDescriptor = LsapAllocateLsaHeap( ContainerDescriptorLength );

        if (ContainerDescriptor == NULL) {

            goto CreateSDError;
        }

        //
        // Read the container object's SD.  It is the value of the SecDesc
        // subkey.
        //

        Status = LsapDbReadAttributeObject(
                     ContainerHandle,
                     &LsapDbNames[SecDesc],
                     ContainerDescriptor,
                     &ContainerDescriptorLength
                     );

        if (!NT_SUCCESS(Status)) {

            goto CreateSDError;
        }
    }

    //
    // Now we are ready to construct the Self-Relative Security Descriptor.
    // Information in the SD will be based on that in the LSA Process
    // Token, except for the DACL we provide in a Security Descriptor.
    // Note that we pass in the LSA Process Token explicitly because we
    // are not impersonating a client.
    //

    Status = RtlNewSecurityObject(
                 ContainerDescriptor,
                 &CreatorDescriptor,
                 &SecurityDescriptor,
                 FALSE,
                 LsaProcessTokenHandle,
                 &(LsapDbState.
                     DbObjectTypes[Handle->ObjectTypeId].GenericMapping)
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

    //
    // Set up the Security Quality Of Service
    //

    SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
    SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    SecurityQualityOfService.EffectiveOnly = FALSE;

    //
    // Store the Security Descriptor
    //

    ObjectInformation->ObjectAttributes.SecurityDescriptor = SecurityDescriptor;

    //
    // The InitializeObjectAttributes macro stores NULL for the
    // SecurityQualityOfService field, so we must manually copy that
    // structure.
    //

    ObjectInformation->ObjectAttributes.SecurityQualityOfService =
        &SecurityQualityOfService;

    //
    // Obtain the length of the newly created SD.
    //

    SecurityDescriptorLength = RtlLengthSecurityDescriptor(
                                   SecurityDescriptor
                                   );

    //
    // Add a Registry transaction to write the Security Descriptor as the
    // value of the new object's SecDesc subkey.
    //

    Status = LsapDbWriteAttributeObject(
                 Handle,
                 &(LsapDbNames[SecDesc]),
                 SecurityDescriptor,
                 SecurityDescriptorLength
                 );

    if (!NT_SUCCESS(Status)) {

        goto CreateSDError;
    }

CreateSDFinish:

    //
    // If necessary, free the memory allocated for the Container Descriptor
    //

    if (ContainerDescriptor != NULL) {

        LsapFreeLsaHeap( ContainerDescriptor );
        ContainerDescriptor = NULL;
    }

    //
    // If necessary, free the memory allocated for the DACL
    //

    if (Dacl != NULL) {

        LsapFreeLsaHeap(Dacl);
        Dacl = NULL;
    }

    //
    // Close the Handles to our process and token.
    //

    if ( LsaProcessHandle != NULL ) {
        (VOID) NtClose( LsaProcessHandle );
    }

    if ( LsaProcessTokenHandle != NULL ) {
        (VOID) NtClose( LsaProcessTokenHandle );
    }

    return(Status);

CreateSDError:

    //
    // If necessary, free the memory allocated for SecurityDescriptor.
    //

    if (SecurityDescriptor != NULL) {

        RtlFreeHeap( RtlProcessHeap(), 0, SecurityDescriptor );
        SecurityDescriptor = NULL;
        ObjectInformation->ObjectAttributes.SecurityDescriptor = NULL;
    }

    goto CreateSDFinish;
}


NTSTATUS
LsapDbCheckCountObject(
    IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
    )

/*++

Routine Description:

    This function checks if the number of objects of a given type has
    reached the type-dependent maximum limit (if any).  If the limit
    is reached, a type-dependent error status is returned.  Currently,
    only Secret objects have a limit imposed.

Arguments:

    Handle - Handle to open object.

    ObjectTypeId - Specifies the type of the object.

Return Value:

    NTSTATUS - Standard Nt Result Code.

        STATUS_TOO_MANY_SECRETS - Too many Secrets

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_DB_OBJECT_TYPE ObjectType;

    ObjectType = &(LsapDbState.DbObjectTypes[ObjectTypeId]);

    //
    // If there is an Object Count Limit, check that it has not been
    // reached.
    //

    if ((ObjectType->ObjectCountLimited) &&
        (ObjectType->ObjectCount == ObjectType->MaximumObjectCount)) {

        Status = ObjectType->ObjectCountError;
    }

    return(Status);
}


#define LsapDbIncrementCountObject(ObjectTypeId)                     \
    {                                                                \
        LsapDbState.DbObjectTypes[ObjectTypeId].ObjectCount++;       \
    }


#define LsapDbDecrementCountObject(ObjectTypeId)                     \
    {                                                                \
        LsapDbState.DbObjectTypes[ObjectTypeId].ObjectCount--;       \
    }
