/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.  You should have received a copy of the GNU General
 * Public License along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        samdbsecurity.c
 *
 * Abstract:
 *
 *
 *      Likewise SAM Database Provider
 *
 *      Security Descriptor handling routines
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 *
 */

#include "includes.h"


typedef struct _ACCESS_LIST
{
    PSID        *ppSid;
    ACCESS_MASK AccessMask;
    ULONG       ulAccessType;

} ACCESS_LIST, *PACCESS_LIST;


static
DWORD
SamDbCreateBuiltinDomainDacl(
    PSID pSid,
    PACL *ppDacl
    );

static
DWORD
SamDbCreateDacl(
    PACL *ppDacl,
    PACCESS_LIST pList
    );

static
DWORD
SamDbCreateBuiltinGroupDacl(
    PSID pSid,
    PACL *ppDacl
    );

static
DWORD
SamDbCreateLocalDomainDacl(
    PSID pSid,
    PACL *ppDacl
    );

static
DWORD
SamDbCreateLocalWellKnownGroupDacl(
    PSID pSid,
    PACL *ppDacl
    );

static
DWORD
SamDbCreateLocalWellKnownUserDacl(
    PSID pAccountSid,
    PACL *ppDacl
    );

static
DWORD
SamDbCreateNewLocalUserDacl(
    PSID pSid,
    PACL *ppDacl
    );

static
DWORD
SamDbCreateNewLocalGroupDacl(
    PSID pSid,
    PACL *ppDacl
    );

static
DWORD
SamDbBuildQuerySecurityDescriptorStatement(
    PSAMDB_CONTEXT DbContext,
    PSID ObjectSid,
    sqlite3_stmt** pSqlStatement,
    PSAMDB_ATTRIBUTE* pInputAttributes,
    PDWORD pInputAttributesCount,
    PSAMDB_ATTRIBUTE* pOutputAttributes,
    PDWORD pOutputAttributesCount
    );

static
DWORD
SamDbMarshalSecurityDescriptor(
    PSAMDB_ATTRIBUTE OutputAttributes,
    DWORD OutputAttributesCount,
    PSECURITY_DESCRIPTOR_RELATIVE* pObjectSecurityDescriptor,
    PDWORD pObjectSecurityDescriptorLength
    );

static
DWORD
SamDbBuildSetSecurityDescriptorStatement(
    PSAMDB_CONTEXT DbContext,
    PSID ObjectSid,
    PSECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor,
    DWORD SecurityDescriptorLength,
    sqlite3_stmt** pSqlStatement,
    PSAMDB_ATTRIBUTE* pAttributes,
    PDWORD pAttributesCount
    );


DWORD
SamDbCreateBuiltinDomainSecDesc(
    PSID pSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner to LOCAL_SYSTEM
     */
    dwError = LwAllocateWellKnownSid(WinLocalSystemSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(
                                    pSecDesc,
                                    pOwnerSid,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group to BUILTIN\Administrators
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(
                                    pSecDesc,
                                    pGroupSid,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* Create default DACL */
    dwError = SamDbCreateBuiltinDomainDacl(pSid,
                                           &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateBuiltinDomainDacl(
    PSID pSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminAccessMask = STANDARD_RIGHTS_READ |
                                  DOMAIN_ACCESS_LOOKUP_INFO_1 |
                                  DOMAIN_ACCESS_SET_INFO_1 |
                                  DOMAIN_ACCESS_LOOKUP_INFO_2 |
                                  DOMAIN_ACCESS_SET_INFO_2 |
                                  DOMAIN_ACCESS_CREATE_USER |
                                  DOMAIN_ACCESS_CREATE_GROUP |
                                  DOMAIN_ACCESS_CREATE_ALIAS |
                                  DOMAIN_ACCESS_LOOKUP_ALIAS |
                                  DOMAIN_ACCESS_ENUM_ACCOUNTS |
                                  DOMAIN_ACCESS_OPEN_ACCOUNT |
                                  DOMAIN_ACCESS_SET_INFO_3;

    ACCESS_MASK AllAccessMask = STANDARD_RIGHTS_READ |
                                DOMAIN_ACCESS_LOOKUP_INFO_1 |
                                DOMAIN_ACCESS_LOOKUP_INFO_2 |
                                DOMAIN_ACCESS_LOOKUP_ALIAS |
                                DOMAIN_ACCESS_ENUM_ACCOUNTS |
                                DOMAIN_ACCESS_OPEN_ACCOUNT;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, AccessList);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


static
DWORD
SamDbCreateDacl(
    PACL *ppDacl,
    PACCESS_LIST pList
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwDaclSize = 0;
    PACL pDacl = NULL;
    DWORD i = 0;
    ULONG ulSidSize = 0;

    dwDaclSize += ACL_HEADER_SIZE;

    for (i = 0; pList[i].ppSid && (*pList[i].ppSid); i++)
    {
        ulSidSize = RtlLengthSid(*(pList[i].ppSid));

        if (pList[i].ulAccessType == ACCESS_ALLOWED_ACE_TYPE)
        {
            dwDaclSize += ulSidSize + sizeof(ACCESS_ALLOWED_ACE);
        }
        else if (pList[i].ulAccessType == ACCESS_DENIED_ACE_TYPE)
        {
            dwDaclSize += ulSidSize + sizeof(ACCESS_DENIED_ACE);
        }
    }

    dwError = LwAllocateMemory(dwDaclSize,
                               OUT_PPVOID(&pDacl));
    BAIL_ON_LSA_ERROR(dwError);

    ntStatus = RtlCreateAcl(pDacl, dwDaclSize, ACL_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    for (i = 0; pList[i].ppSid && (*pList[i].ppSid); i++)
    {
        if (pList[i].ulAccessType == ACCESS_ALLOWED_ACE_TYPE)
        {
            ntStatus = RtlAddAccessAllowedAceEx(pDacl,
                                                ACL_REVISION,
                                                0,
                                                pList[i].AccessMask,
                                                *(pList[i].ppSid));
        }
        else if (pList[i].ulAccessType == ACCESS_DENIED_ACE_TYPE)
        {
            ntStatus = RtlAddAccessDeniedAceEx(pDacl,
                                               ACL_REVISION,
                                               0,
                                               pList[i].AccessMask,
                                               *(pList[i].ppSid));
        }

        BAIL_ON_NT_STATUS(ntStatus);
    }

    *ppDacl = pDacl;

cleanup:
    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbCreateBuiltinGroupSecDesc(
    PSID pSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(pSecDesc,
                                             pOwnerSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(pSecDesc,
                                             pGroupSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* create default DACL */
    dwError = SamDbCreateBuiltinGroupDacl(pSid,
                                          &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateBuiltinGroupDacl(
    PSID pSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminAccessMask = STANDARD_RIGHTS_REQUIRED |
                                  ALIAS_ACCESS_ADD_MEMBER |
                                  ALIAS_ACCESS_REMOVE_MEMBER |
                                  ALIAS_ACCESS_GET_MEMBERS |
                                  ALIAS_ACCESS_LOOKUP_INFO |
                                  ALIAS_ACCESS_SET_INFO;

    ACCESS_MASK AllAccessMask = STANDARD_RIGHTS_READ |
                                ALIAS_ACCESS_GET_MEMBERS |
                                ALIAS_ACCESS_LOOKUP_INFO;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, AccessList);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbCreateLocalDomainSecDesc(
    PSID pSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner to SYSTEM
     */
    dwError = LwAllocateWellKnownSid(WinLocalSystemSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(
                                    pSecDesc,
                                    pOwnerSid,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group to BUILTIN\Administrators
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(
                                    pSecDesc,
                                    pGroupSid,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* Create default DACL */
    dwError = SamDbCreateLocalDomainDacl(pSid,
                                         &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateLocalDomainDacl(
    PSID pSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminAccessMask = STANDARD_RIGHTS_REQUIRED |
                                  DOMAIN_ACCESS_LOOKUP_INFO_1 |
                                  DOMAIN_ACCESS_SET_INFO_1 |
                                  DOMAIN_ACCESS_LOOKUP_INFO_2 |
                                  DOMAIN_ACCESS_SET_INFO_2 |
                                  DOMAIN_ACCESS_CREATE_USER |
                                  DOMAIN_ACCESS_CREATE_GROUP |
                                  DOMAIN_ACCESS_CREATE_ALIAS |
                                  DOMAIN_ACCESS_LOOKUP_ALIAS |
                                  DOMAIN_ACCESS_ENUM_ACCOUNTS |
                                  DOMAIN_ACCESS_OPEN_ACCOUNT |
                                  DOMAIN_ACCESS_SET_INFO_3;

    ACCESS_MASK AllAccessMask = STANDARD_RIGHTS_READ |
                                DOMAIN_ACCESS_LOOKUP_INFO_1 |
                                DOMAIN_ACCESS_LOOKUP_INFO_2 |
                                DOMAIN_ACCESS_LOOKUP_ALIAS |
                                DOMAIN_ACCESS_ENUM_ACCOUNTS |
                                DOMAIN_ACCESS_OPEN_ACCOUNT;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    ULONG ulLocalAdminSidLen = 0;
    PSID pLocalAdminSid = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pLocalAdminSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };

    ulLocalAdminSidLen = RtlLengthRequiredSid(pSid->SubAuthorityCount + 1);
    dwError = LwAllocateMemory(ulLocalAdminSidLen,
                               OUT_PPVOID(&pLocalAdminSid));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCopySid(ulLocalAdminSidLen,
                          pLocalAdminSid,
                          pSid);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlAppendRidSid(ulLocalAdminSidLen,
                               pLocalAdminSid,
                               DOMAIN_USER_RID_ADMIN);

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, AccessList);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);
    LW_SAFE_FREE_MEMORY(pLocalAdminSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbCreateLocalWellKnownGroupSecDesc(
    PSID pSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(pSecDesc,
                                             pOwnerSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(pSecDesc,
                                             pGroupSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* create default DACL */
    dwError = SamDbCreateLocalWellKnownGroupDacl(
                                pSid,
                                &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateLocalWellKnownGroupDacl(
    PSID pSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminAccessMask = STANDARD_RIGHTS_REQUIRED |
                                  ALIAS_ACCESS_ADD_MEMBER |
                                  ALIAS_ACCESS_REMOVE_MEMBER |
                                  ALIAS_ACCESS_GET_MEMBERS |
                                  ALIAS_ACCESS_LOOKUP_INFO |
                                  ALIAS_ACCESS_SET_INFO;

    ACCESS_MASK AllAccessMask = STANDARD_RIGHTS_READ |
                                ALIAS_ACCESS_GET_MEMBERS |
                                ALIAS_ACCESS_LOOKUP_INFO;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    ULONG ulLocalAdminSidLen = 0;
    PSID pLocalAdminSid = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pLocalAdminSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };


    ulLocalAdminSidLen = RtlLengthRequiredSid(pSid->SubAuthorityCount + 1);
    dwError = LwAllocateMemory(ulLocalAdminSidLen,
                               OUT_PPVOID(&pLocalAdminSid));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCopySid(ulLocalAdminSidLen,
                          pLocalAdminSid,
                          pSid);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlAppendRidSid(ulLocalAdminSidLen,
                               pLocalAdminSid,
                               DOMAIN_USER_RID_ADMIN);

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, AccessList);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);
    LW_SAFE_FREE_MEMORY(pLocalAdminSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbCreateLocalWellKnownUserSecDesc(
    PSID pUserSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner (BUITLIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(pSecDesc,
                                             pOwnerSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(pSecDesc,
                                             pGroupSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* create default DACL */
    dwError = SamDbCreateLocalWellKnownUserDacl(
                         pUserSid,
                         &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateLocalWellKnownUserDacl(
    PSID pAccountSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminsAccessMask = STANDARD_RIGHTS_READ |
                                   WRITE_DAC |
                                   WRITE_OWNER |
                                   DELETE |
                                   USER_ACCESS_GET_NAME_ETC |
                                   USER_ACCESS_GET_LOCALE |
                                   USER_ACCESS_SET_LOC_COM |
                                   USER_ACCESS_GET_LOGONINFO |
                                   USER_ACCESS_GET_ATTRIBUTES |
                                   USER_ACCESS_SET_ATTRIBUTES |
                                   USER_ACCESS_CHANGE_PASSWORD |
                                   USER_ACCESS_SET_PASSWORD |
                                   USER_ACCESS_GET_GROUPS |
                                   USER_ACCESS_GET_GROUP_MEMBERSHIP |
                                   USER_ACCESS_CHANGE_GROUP_MEMBERSHIP;

    ACCESS_MASK AccountAccessMask = STANDARD_RIGHTS_READ |
                                    USER_ACCESS_SET_LOC_COM |
                                    USER_ACCESS_CHANGE_PASSWORD;

    ACCESS_MASK AllAccessMask = STANDARD_RIGHTS_READ |
                                USER_ACCESS_GET_NAME_ETC |
                                USER_ACCESS_GET_LOCALE |
                                USER_ACCESS_GET_LOGONINFO |
                                USER_ACCESS_GET_ATTRIBUTES |
                                USER_ACCESS_CHANGE_PASSWORD |
                                USER_ACCESS_GET_GROUPS |
                                USER_ACCESS_GET_GROUP_MEMBERSHIP;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminsAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pAccountSid,
            .AccessMask   = AccountAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, &AccessList[0]);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbCreateNewLocalUserSecDesc(
    PSID pSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(pSecDesc,
                                             pOwnerSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(pSecDesc,
                                             pGroupSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* create default DACL */
    dwError = SamDbCreateNewLocalUserDacl(pSid,
                                          &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    LW_SAFE_FREE_MEMORY(pSecDesc);
    LW_SAFE_FREE_MEMORY(pDacl);

    LW_SAFE_FREE_MEMORY(pOwnerSid);
    LW_SAFE_FREE_MEMORY(pGroupSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateNewLocalUserDacl(
    PSID pAccountSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminAccessMask =
        STANDARD_RIGHTS_ALL |
        USER_ACCESS_GET_NAME_ETC |
        USER_ACCESS_GET_LOCALE |
        USER_ACCESS_SET_LOC_COM |
        USER_ACCESS_GET_LOGONINFO |
        USER_ACCESS_GET_ATTRIBUTES |
        USER_ACCESS_SET_ATTRIBUTES |
        USER_ACCESS_CHANGE_PASSWORD |
        USER_ACCESS_SET_PASSWORD |
        USER_ACCESS_GET_GROUPS |
        USER_ACCESS_GET_GROUP_MEMBERSHIP |
        USER_ACCESS_CHANGE_GROUP_MEMBERSHIP;

    ACCESS_MASK AccountAccessMask =
        STANDARD_RIGHTS_READ |
        USER_ACCESS_GET_NAME_ETC |
        USER_ACCESS_GET_LOCALE |
        USER_ACCESS_GET_LOGONINFO |
        USER_ACCESS_GET_ATTRIBUTES |
        USER_ACCESS_CHANGE_PASSWORD |
        USER_ACCESS_GET_GROUPS |
        USER_ACCESS_GET_GROUP_MEMBERSHIP;

    ACCESS_MASK AllAccessMask =
        STANDARD_RIGHTS_READ |
        USER_ACCESS_GET_NAME_ETC |
        USER_ACCESS_GET_LOCALE |
        USER_ACCESS_GET_LOGONINFO |
        USER_ACCESS_GET_ATTRIBUTES |
        USER_ACCESS_CHANGE_PASSWORD |
        USER_ACCESS_GET_GROUPS |
        USER_ACCESS_GET_GROUP_MEMBERSHIP;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pAccountSid,
            .AccessMask   = AccountAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, AccessList);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbCreateNewLocalGroupSecDesc(
    PSID pSid,
    PSECURITY_DESCRIPTOR_RELATIVE *ppSecDescRel,
    PULONG pulSecDescLen
    )
{
    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pSecDescRel = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE pNewSecDescRel = NULL;
    ULONG ulSecDescLen = 1024;
    PACL pDacl = NULL;

    dwError = LwAllocateMemory(SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                               OUT_PPVOID(&pSecDesc));
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                                    pSecDesc,
                                    SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set owner (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pOwnerSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetOwnerSecurityDescriptor(pSecDesc,
                                             pOwnerSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /*
     * Set group (BUILTIN\Administrators)
     */
    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pGroupSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetGroupSecurityDescriptor(pSecDesc,
                                             pGroupSid,
                                             FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* create default DACL */
    dwError = SamDbCreateNewLocalGroupDacl(pSid,
                                           &pDacl);
    BAIL_ON_SAMDB_ERROR(dwError);

    ntStatus = RtlSetDaclSecurityDescriptor(
                                    pSecDesc,
                                    TRUE,
                                    pDacl,
                                    FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    do
    {
        dwError = LwReallocMemory(pSecDescRel,
                                  OUT_PPVOID(&pNewSecDescRel),
                                  ulSecDescLen);
        BAIL_ON_SAMDB_ERROR(dwError);

        pSecDescRel    = pNewSecDescRel;
        pNewSecDescRel = NULL;

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                                    pSecDesc,
                                    pSecDescRel,
                                    &ulSecDescLen);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ulSecDescLen *= 2;
        }

    } while (ntStatus != STATUS_SUCCESS &&
             ulSecDescLen <= SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE);

    *ppSecDescRel  = pSecDescRel;
    *pulSecDescLen = ulSecDescLen;

cleanup:
    LW_SAFE_FREE_MEMORY(pSecDesc);
    LW_SAFE_FREE_MEMORY(pDacl);

    LW_SAFE_FREE_MEMORY(pOwnerSid);
    LW_SAFE_FREE_MEMORY(pGroupSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pSecDescRel);

    *ppSecDescRel  = NULL;
    *pulSecDescLen = 0;

    goto cleanup;
}


static
DWORD
SamDbCreateNewLocalGroupDacl(
    PSID pAccountSid,
    PACL *ppDacl
    )
{
    ACCESS_MASK AdminAccessMask =
        STANDARD_RIGHTS_ALL |
        ALIAS_ACCESS_ADD_MEMBER |
        ALIAS_ACCESS_REMOVE_MEMBER |
        ALIAS_ACCESS_GET_MEMBERS |
        ALIAS_ACCESS_LOOKUP_INFO |
        ALIAS_ACCESS_SET_INFO;

    ACCESS_MASK AllAccessMask =
        STANDARD_RIGHTS_READ |
        ALIAS_ACCESS_GET_MEMBERS |
        ALIAS_ACCESS_LOOKUP_INFO;

    DWORD dwError = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PACL pDacl = NULL;
    PSID pBuiltinAdminsSid = NULL;
    PSID pWorldSid = NULL;

    ACCESS_LIST AccessList[] = {
        {
            .ppSid        = &pBuiltinAdminsSid,
            .AccessMask   = AdminAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = &pWorldSid,
            .AccessMask   = AllAccessMask,
            .ulAccessType = ACCESS_ALLOWED_ACE_TYPE
        },
        {
            .ppSid        = NULL,
            .AccessMask   = 0,
            .ulAccessType = 0
        }
    };

    dwError = LwAllocateWellKnownSid(WinBuiltinAdministratorsSid,
                                   NULL,
                                   &pBuiltinAdminsSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = LwAllocateWellKnownSid(WinWorldSid,
                                   NULL,
                                   &pWorldSid,
                                   NULL);
    BAIL_ON_SAMDB_ERROR(dwError);

    dwError = SamDbCreateDacl(&pDacl, AccessList);
    BAIL_ON_LSA_ERROR(dwError);

    *ppDacl = pDacl;

cleanup:
    LW_SAFE_FREE_MEMORY(pBuiltinAdminsSid);
    LW_SAFE_FREE_MEMORY(pWorldSid);

    if (dwError == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        dwError = LwNtStatusToWin32Error(ntStatus);
    }

    return dwError;

error:
    LW_SAFE_FREE_MEMORY(pDacl);
    *ppDacl = NULL;

    goto cleanup;
}


DWORD
SamDbQuerySecurityDescriptor(
    IN PSID Sid,
    IN SECURITY_INFORMATION SecurityInformation,
    OUT PSECURITY_DESCRIPTOR_RELATIVE* pSecurityDescriptor
    )
{
    DWORD error = ERROR_SUCCESS;
    PSAMDB_CONTEXT dbContext = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE securityDescriptor = NULL;

    error = SamDbAcquireContext(
                  &dbContext);
    BAIL_ON_SAMDB_ERROR(error);

    error = SamDbQuerySecurityDescriptorInternal(
                  dbContext,
                  Sid,
                  SecurityInformation,
                  &securityDescriptor);
    BAIL_ON_SAMDB_ERROR(error);

cleanup:
    if (dbContext)
    {
        SamDbReleaseContext(dbContext);
    }

    *pSecurityDescriptor = securityDescriptor;

    return error;
}


DWORD
SamDbQuerySecurityDescriptorInternal(
    PSAMDB_CONTEXT DbContext,
    PSID Sid,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR_RELATIVE* pSecurityDescriptor
    )
{
    DWORD error = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    sqlite3_stmt* sqlStatement = NULL;
    PSAMDB_ATTRIBUTE inputAttributes = NULL;
    DWORD inputAttributesCount = 0;
    PSAMDB_ATTRIBUTE outputAttributes = NULL;
    DWORD outputAttributesCount = 0;
    PSECURITY_DESCRIPTOR_RELATIVE objectSecurityDescriptor = NULL;
    DWORD objectSecurityDescriptorLength = 0;
    PSECURITY_DESCRIPTOR_RELATIVE returnSecurityDescriptor = NULL;
    DWORD returnSecurityDescriptorLength = 0;

    //
    // Query security object by object SID
    //
    error = SamDbBuildQuerySecurityDescriptorStatement(
                  DbContext,
                  Sid,
                  &sqlStatement,
                  &inputAttributes,
                  &inputAttributesCount,
                  &outputAttributes,
                  &outputAttributesCount);
    BAIL_ON_SAMDB_ERROR(error);

    error = SamDbBindAttributeValues(
                  DbContext,
                  sqlStatement,
                  inputAttributes,
                  inputAttributesCount);
    BAIL_ON_SAMDB_ERROR(error);

    error = sqlite3_step(sqlStatement);
    if (error == SQLITE_ROW)
    {
        error = ERROR_SUCCESS;
    }
    else
    {
        SAMDB_LOG_ERROR("Error code %u returned from sqlite3_step() "
                        "while %u was expected",
                        error,
                        SQLITE_ROW);
        BAIL_ON_SAMDB_SQLITE_ERROR_STMT(error, sqlStatement);
    }

    //
    // Copy query results to output attributes
    //
    error = SamDbGetOutputAttributeValues(
                  DbContext,
                  sqlStatement,
                  outputAttributes,
                  outputAttributesCount);
    BAIL_ON_SAMDB_ERROR(error);


    //
    // Marshal returned blob to SECURITY_DESCRIPTOR_RELATIVE
    //
    error = SamDbMarshalSecurityDescriptor(
                  outputAttributes,
                  outputAttributesCount,
                  &objectSecurityDescriptor,
                  &objectSecurityDescriptorLength);
    BAIL_ON_SAMDB_ERROR(error);

    //
    // Query requested security information
    //
    ntStatus = RtlQuerySecurityDescriptorInfo(
                  SecurityInformation,
                  returnSecurityDescriptor,
                  &returnSecurityDescriptorLength,
                  objectSecurityDescriptor);
    if (ntStatus == STATUS_BUFFER_TOO_SMALL)
    {
        ntStatus = STATUS_SUCCESS;
    }
    else if (ntStatus != STATUS_SUCCESS)
    {
        BAIL_ON_NT_STATUS(ntStatus);
    }

    error = LwAllocateMemory(
                  returnSecurityDescriptorLength,
                  OUT_PPVOID(&returnSecurityDescriptor));
    BAIL_ON_SAMDB_ERROR(error);

    ntStatus = RtlQuerySecurityDescriptorInfo(
                  SecurityInformation,
                  returnSecurityDescriptor,
                  &returnSecurityDescriptorLength,
                  objectSecurityDescriptor);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:
error:
    if (sqlStatement)
    {
        sqlite3_finalize(sqlStatement);
    }

    if (inputAttributes)
    {
        SamDbFreeAttributes(
                   inputAttributes,
                   inputAttributesCount);
    }

    if (outputAttributes)
    {
        SamDbFreeAttributes(
                   outputAttributes,
                   outputAttributesCount);
    }

    if (objectSecurityDescriptor)
    {
        SamDbFree(objectSecurityDescriptor);
    }

    if (error == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        error = LwNtStatusToWin32Error(ntStatus);
    }

    *pSecurityDescriptor = returnSecurityDescriptor;

    return error;
}


#define SAMDB_SQL_QUERY_SECURITY_DESCRIPTOR_PREFIX \
    "SELECT "

#define SAMDB_SQL_QUERY_SECURITY_DESCRIPTOR_WHERE \
    " FROM " SAM_DB_OBJECTS_TABLE " WHERE "


static
DWORD
SamDbBuildQuerySecurityDescriptorStatement(
    PSAMDB_CONTEXT DbContext,
    PSID ObjectSid,
    sqlite3_stmt** pSqlStatement,
    PSAMDB_ATTRIBUTE* pInputAttributes,
    PDWORD pInputAttributesCount,
    PSAMDB_ATTRIBUTE* pOutputAttributes,
    PDWORD pOutputAttributesCount
    )
{
    DWORD error = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD inputAttributesCount = 0;
    PSAMDB_ATTRIBUTE inputAttributes = NULL;
    DWORD inputAttributeIndex = 0;
    DWORD outputAttributesCount = 0;
    PSAMDB_ATTRIBUTE outputAttributes = NULL;
    DWORD outputAttributeIndex = 0;
    PWSTR* ppwszSid = NULL;
    size_t sqlStatementSize = 256;
    size_t sqlStatementLength = 0;
    size_t maxSqlStatementSize = 4096;
    PSTR sqlStatementString = NULL;
    sqlite3_stmt* sqlStatement = NULL;

    SAMDB_ATTRIBUTE inputAttrDef[] = {
        {
            SAMDB_ATTR_OBJECT_SID,
            SAMDB_ATTR_TYPE_TEXT,
            NULL
        }
    };

    SAMDB_ATTRIBUTE outputAttrDef[] = {
        {
            SAMDB_ATTR_SECURITY_DESCRIPTOR,
            SAMDB_ATTR_TYPE_BLOB,
            NULL
        }
    };

    error = LwAllocateMemory(
                    sizeof(inputAttrDef),
                    OUT_PPVOID(&inputAttributes));
    BAIL_ON_SAMDB_ERROR(error);

    error = LwAllocateMemory(
                    sizeof(*ppwszSid),
                    OUT_PPVOID(&ppwszSid));
    BAIL_ON_SAMDB_ERROR(error);

    ntStatus = RtlAllocateWC16StringFromSid(
                    ppwszSid,
                    ObjectSid);
    BAIL_ON_NT_STATUS(ntStatus);

    inputAttributes[inputAttributeIndex] = inputAttrDef[inputAttributeIndex];
    inputAttributes->Value = ppwszSid;
    inputAttributes->FreeFn = SamDbAttributeFreeSidString;

    inputAttributeIndex++;
    inputAttributesCount = inputAttributeIndex;

    error = LwAllocateMemory(
                    sizeof(outputAttrDef),
                    OUT_PPVOID(&outputAttributes));
    BAIL_ON_SAMDB_ERROR(error);

    outputAttributes[outputAttributeIndex] = outputAttrDef[outputAttributeIndex];
    outputAttributeIndex++;
    outputAttributesCount = outputAttributeIndex;

    //
    // Create SQL statement string (SELECT SecurityDescriptor,...
    //
    error = LwAllocateMemory(
                    sizeof(sqlStatementString[0]) * sqlStatementSize,
                    OUT_PPVOID(&sqlStatementString));
    BAIL_ON_SAMDB_ERROR(error);

    error = SamDbPrintfString(
                    &sqlStatementString,
                    &sqlStatementSize,
                    &sqlStatementLength,
                    maxSqlStatementSize,
                    "%s",
                    SAMDB_SQL_QUERY_SECURITY_DESCRIPTOR_PREFIX);
    BAIL_ON_SAMDB_ERROR(error);

    for (outputAttributeIndex = 0;
         outputAttributeIndex < outputAttributesCount;
         outputAttributeIndex++)
    {
        //
        // Append SQL column name(s) separated with ","
        //
        error = SamDbPrintfString(
                        &sqlStatementString,
                        &sqlStatementSize,
                        &sqlStatementLength,
                        maxSqlStatementSize,
                        (outputAttributeIndex < outputAttributesCount - 1) ? "%s," : "%s ",
                        outputAttributes[outputAttributeIndex].DbColumnName);
        BAIL_ON_SAMDB_ERROR(error);
    }

    //
    // Append (FROM samdbobjects WHERE ...)
    //
    error = SamDbPrintfString(
                    &sqlStatementString,
                    &sqlStatementSize,
                    &sqlStatementLength,
                    maxSqlStatementSize,
                    "%s",
                    SAMDB_SQL_QUERY_SECURITY_DESCRIPTOR_WHERE);
    BAIL_ON_SAMDB_ERROR(error);

    for (inputAttributeIndex = 0;
         inputAttributeIndex < inputAttributesCount;
         inputAttributeIndex++)
    {
        //
        // Append sqlite value placeholders for conditions "?{1,2,...}"
        //
        error = SamDbPrintfString(
                  &sqlStatementString,
                  &sqlStatementSize,
                  &sqlStatementLength,
                  maxSqlStatementSize,
                  (inputAttributeIndex < inputAttributesCount - 1) ? "%s = ?%u AND" : "%s = ?%u;",
                  inputAttributes[inputAttributeIndex].DbColumnName,
                  inputAttributeIndex + 1);
        BAIL_ON_SAMDB_ERROR(error);
    }

    error = sqlite3_prepare_v2(
                      DbContext->DbHandle,
                      sqlStatementString,
                      -1,
                      &sqlStatement,
                      NULL);
    if (error)
    {
        SAMDB_LOG_DEBUG("Error when preparing SQL statement:\n%s",
                        sqlStatementString);
    }
    BAIL_ON_SAMDB_SQLITE_ERROR_DB(error, DbContext->DbHandle);

cleanup:
error:
    if (error)
    {
        if (sqlStatement)
        {
            sqlite3_finalize(sqlStatement);
        }

        if (inputAttributes)
        {
            SamDbFreeAttributes(
                        inputAttributes,
                        inputAttributesCount);
            inputAttributes = NULL;
            inputAttributesCount = 0;
        }

        if (outputAttributes)
        {
            SamDbFreeAttributes(
                        outputAttributes,
                        outputAttributesCount);
            outputAttributes = NULL;
            outputAttributesCount = 0;
        }

        sqlStatement = NULL;
    }

    LW_SAFE_FREE_MEMORY(sqlStatementString);

    *pSqlStatement = sqlStatement;
    *pInputAttributes = inputAttributes;
    *pInputAttributesCount = inputAttributesCount;
    *pOutputAttributes = outputAttributes;
    *pOutputAttributesCount = outputAttributesCount;

    return error;
}


static
DWORD
SamDbMarshalSecurityDescriptor(
    PSAMDB_ATTRIBUTE OutputAttributes,
    DWORD OutputAttributesCount,
    PSECURITY_DESCRIPTOR_RELATIVE* pObjectSecurityDescriptor,
    PDWORD pObjectSecurityDescriptorLength
    )
{
    DWORD error = ERROR_SUCCESS;
    DWORD outputAttributeIndex = 0;
    int securityDescriptorLength = 0;
    PSECURITY_DESCRIPTOR_RELATIVE securityDescriptor = NULL;
    CHAR securityDescriptorAttrName[] = SAMDB_ATTR_SECURITY_DESCRIPTOR;

    for (outputAttributeIndex = 0;
         outputAttributeIndex < OutputAttributesCount;
         outputAttributeIndex++)
    {
        if (RtlCStringIsEqual(
                  OutputAttributes[outputAttributeIndex].DbColumnName,
                  securityDescriptorAttrName,
                  TRUE))
        {
            PSAMDB_ATTRIBUTE_VALUE_BLOB blob =
                OutputAttributes[outputAttributeIndex].Value;

            if (blob && blob->Length > 0)
            {
                securityDescriptorLength = blob->Length;
                securityDescriptor       = blob->Value;

                // NULL out value and destructor pointers to avoid
                // double-free when the actual security descriptor
                // gets freed
                OutputAttributes[outputAttributeIndex].Value = NULL;
                OutputAttributes[outputAttributeIndex].FreeFn = NULL;

                // Free the blob pointer because only Value (now security
                // descriptor) is needed.
                LW_SAFE_FREE_MEMORY(blob);
            }

            break;
        }
    }

    *pObjectSecurityDescriptor = securityDescriptor;
    *pObjectSecurityDescriptorLength = securityDescriptorLength;

    return error;
}


DWORD
SamDbSetSecurityDescriptor(
    IN PSID Sid,
    IN SECURITY_INFORMATION SecurityInformation,
    IN PSECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor
    )
{
    DWORD error = ERROR_SUCCESS;
    PSAMDB_CONTEXT dbContext = NULL;
    DWORD securityDescriptorLength = 0;

    error = SamDbAcquireContext(
                  &dbContext);
    BAIL_ON_SAMDB_ERROR(error);

    securityDescriptorLength =
        RtlLengthSecurityDescriptorRelative(SecurityDescriptor);

    error = SamDbSetSecurityDescriptorInternal(
                  dbContext,
                  Sid,
                  SecurityInformation,
                  SecurityDescriptor,
                  securityDescriptorLength);
    BAIL_ON_SAMDB_ERROR(error);

cleanup:
    if (dbContext)
    {
        SamDbReleaseContext(dbContext);
    }

    return error;
}


DWORD
SamDbSetSecurityDescriptorInternal(
    PSAMDB_CONTEXT DbContext,
    PSID Sid,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor,
    DWORD SecurityDescriptorLength
    )
{
    DWORD error = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    SECURITY_INFORMATION allSecurityInfo = OWNER_SECURITY_INFORMATION |
                                           GROUP_SECURITY_INFORMATION |
                                           DACL_SECURITY_INFORMATION;
    PSECURITY_DESCRIPTOR_RELATIVE currentSecurityDescriptor = NULL;
    PSECURITY_DESCRIPTOR_RELATIVE newSecurityDescriptor = NULL;
    GENERIC_MAPPING genericMapping = {0};
    DWORD newSecurityDescriptorLength = 0;
    PSAMDB_ATTRIBUTE inputAttributes = NULL;
    DWORD inputAttributesCount = 0;
    sqlite3_stmt* sqlStatement = NULL;

    error = SamDbQuerySecurityDescriptorInternal(
                      DbContext,
                      Sid,
                      allSecurityInfo,
                      &currentSecurityDescriptor);
    BAIL_ON_SAMDB_ERROR(error);

    if (currentSecurityDescriptor)
    {
        ntStatus = RtlSetSecurityDescriptorInfo(
                          SecurityInformation,
                          SecurityDescriptor,
                          currentSecurityDescriptor,
                          newSecurityDescriptor,
                          &newSecurityDescriptorLength,
                          &genericMapping);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            ntStatus = STATUS_SUCCESS;
        }
        else if (ntStatus != STATUS_SUCCESS)
        {
            BAIL_ON_NT_STATUS(ntStatus);
        }

        error = LwAllocateMemory(
                          newSecurityDescriptorLength,
                          OUT_PPVOID(&newSecurityDescriptor));
        BAIL_ON_SAMDB_ERROR(error);

        ntStatus = RtlSetSecurityDescriptorInfo(
                          SecurityInformation,
                          SecurityDescriptor,
                          currentSecurityDescriptor,
                          newSecurityDescriptor,
                          &newSecurityDescriptorLength,
                          &genericMapping);
        BAIL_ON_NT_STATUS(ntStatus);
    }
    else
    {
        //
        // This is the case only when initialising objects in new database
        //
        newSecurityDescriptorLength = SecurityDescriptorLength;
        error = LwAllocateMemory(
                       newSecurityDescriptorLength,
                       OUT_PPVOID(&newSecurityDescriptor));
        BAIL_ON_SAMDB_ERROR(error);

        memcpy(newSecurityDescriptor,
               SecurityDescriptor,
               newSecurityDescriptorLength);
    }

    error = SamDbBuildSetSecurityDescriptorStatement(
                      DbContext,
                      Sid,
                      newSecurityDescriptor,
                      newSecurityDescriptorLength,
                      &sqlStatement,
                      &inputAttributes,
                      &inputAttributesCount);
    BAIL_ON_SAMDB_ERROR(error);

    error = SamDbBindAttributeValues(
                      DbContext,
                      sqlStatement,
                      inputAttributes,
                      inputAttributesCount);
    BAIL_ON_SAMDB_ERROR(error);

    error = sqlite3_step(sqlStatement);
    if (error == SQLITE_DONE)
    {
        error = ERROR_SUCCESS;
    }
    else
    {
        SAMDB_LOG_ERROR("Error code %u returned from sqlite3_step() "
                        "while %u was expected",
                        error,
                        SQLITE_DONE);
        BAIL_ON_SAMDB_SQLITE_ERROR_STMT(error, sqlStatement);
    }

cleanup:
error:
    if (sqlStatement)
    {
        sqlite3_finalize(sqlStatement);
    }

    if (inputAttributes)
    {
        SamDbFreeAttributes(
                   inputAttributes,
                   inputAttributesCount);
    }

    LW_SAFE_FREE_MEMORY(currentSecurityDescriptor);
    LW_SAFE_FREE_MEMORY(newSecurityDescriptor);

    if (error == ERROR_SUCCESS &&
        ntStatus != STATUS_SUCCESS)
    {
        error = LwNtStatusToWin32Error(ntStatus);
    }

    return error;
}


#define SAMDB_SQL_SET_SECURITY_DESCRIPTOR \
    "UPDATE " SAM_DB_OBJECTS_TABLE " SET " SAM_DB_COL_SECURITY_DESCRIPTOR " = ?1 "  \
    "WHERE " SAM_DB_COL_OBJECT_SID " = ?2;"


static
DWORD
SamDbBuildSetSecurityDescriptorStatement(
    PSAMDB_CONTEXT DbContext,
    PSID ObjectSid,
    PSECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor,
    DWORD SecurityDescriptorLength,
    sqlite3_stmt** pSqlStatement,
    PSAMDB_ATTRIBUTE* pInputAttributes,
    PDWORD pInputAttributesCount
    )
{
    DWORD error = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD inputAttributesCount = 0;
    DWORD inputAttributeIndex = 0;
    PSAMDB_ATTRIBUTE inputAttributes = NULL;
    PWSTR* ppwszSid = NULL;
    PSAMDB_ATTRIBUTE_VALUE_BLOB blob = NULL;
    size_t sqlStatementSize = 256;
    size_t sqlStatementLength = 0;
    size_t maxSqlStatementSize = 4096;
    PSTR sqlStatementString = NULL;
    sqlite3_stmt* sqlStatement = NULL;

    SAMDB_ATTRIBUTE inputAttrDef[] = {
        {
            SAMDB_ATTR_SECURITY_DESCRIPTOR,
            SAMDB_ATTR_TYPE_BLOB,
            NULL
        },
        {
            SAMDB_ATTR_OBJECT_SID,
            SAMDB_ATTR_TYPE_TEXT,
            NULL
        },
    };

    error = LwAllocateMemory(
                    sizeof(inputAttrDef),
                    OUT_PPVOID(&inputAttributes));
    BAIL_ON_SAMDB_ERROR(error);

    error = LwAllocateMemory(
                    sizeof(*ppwszSid),
                    OUT_PPVOID(&ppwszSid));
    BAIL_ON_SAMDB_ERROR(error);

    ntStatus = RtlAllocateWC16StringFromSid(
                    ppwszSid,
                    ObjectSid);
    BAIL_ON_NT_STATUS(ntStatus);

    error = LwAllocateMemory(
                    sizeof(*blob),
                    OUT_PPVOID(&blob));
    BAIL_ON_SAMDB_ERROR(error);

    blob->Length = SecurityDescriptorLength;
    error = LwAllocateMemory(
                    blob->Length,
                    OUT_PPVOID(&blob->Value));

    memcpy(blob->Value, SecurityDescriptor, blob->Length);

    inputAttributes[inputAttributeIndex] = inputAttrDef[0];
    inputAttributes[inputAttributeIndex].Value = blob;
    inputAttributes[inputAttributeIndex++].FreeFn = SamDbAttributeFreeBlob;

    inputAttributes[inputAttributeIndex] = inputAttrDef[1];
    inputAttributes[inputAttributeIndex].Value = ppwszSid;
    inputAttributes[inputAttributeIndex++].FreeFn = SamDbAttributeFreeSidString;

    inputAttributesCount = inputAttributeIndex;

    //
    // Create SQL statement string
    // (UPDATE samdbobjects SET SecurityDescriptor ... WHERE ObjectSid ...)
    //
    error = LwAllocateMemory(
                    sizeof(sqlStatementString[0]) * sqlStatementSize,
                    OUT_PPVOID(&sqlStatementString));
    BAIL_ON_SAMDB_ERROR(error);

    error = SamDbPrintfString(
                    &sqlStatementString,
                    &sqlStatementSize,
                    &sqlStatementLength,
                    maxSqlStatementSize,
                    "%s",
                    SAMDB_SQL_SET_SECURITY_DESCRIPTOR);
    BAIL_ON_SAMDB_ERROR(error);

    error = sqlite3_prepare_v2(
                    DbContext->DbHandle,
                    sqlStatementString,
                    -1,
                    &sqlStatement,
                    NULL);
    if (error)
    {
        SAMDB_LOG_DEBUG("Error when preparing SQL statement:\n%s",
                        sqlStatementString);
    }
    BAIL_ON_SAMDB_SQLITE_ERROR_DB(error, DbContext->DbHandle);

cleanup:
error:
    if (error)
    {
        if (inputAttributes)
        {
            SamDbFreeAttributes(
                    inputAttributes,
                    inputAttributesCount);
            inputAttributes = NULL;
            inputAttributesCount = 0;
        }

        if (sqlStatement)
        {
            sqlite3_finalize(sqlStatement);
            sqlStatement = NULL;
        }
    }

    LW_SAFE_FREE_MEMORY(sqlStatementString);

    *pSqlStatement = sqlStatement;
    *pInputAttributes = inputAttributes;
    *pInputAttributesCount = inputAttributesCount;

    return error;
}
