/* -*- 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 (c) 2012 Spectra Logic Corporation.
 * 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
 */

/*
 * Module Name:
 *
 *        acl_freebsd_sd.c
 *
 * Abstract:
 *
 *        Likewise Posix File System Driver (PVFS)
 *
 *        FreeBSD Security Descriptor NTFS ACL implementation
 *
 * Authors: Justin Gibbs <justing@spectralogic.com>
 */

#include "pvfs.h"

#ifdef HAVE_SD_SUPPORT

/****************************************************************
 ***************************************************************/

static
NTSTATUS
PvfsSidAclToAclSize(
    IN  struct sid_acl *pSidAcl, 
    IN  PSID pOwnerSid,
    IN  PSID pGroupSid,
    OUT PULONG pAclSize
    )
{
    NTSTATUS ntStatus = 0;
    struct sid_acl_iter aclIter;
    struct sid_acl_entry *pSidAce;

    *pAclSize = ACL_HEADER_SIZE;
    sid_acl_iter_init(&aclIter, pSidAcl);
    while ((pSidAce = sid_acl_iter_next(&aclIter)) != NULL)
    {
        switch (pSidAce->sae_tag)
        {
            case ACL_USER_OBJ:
                *pAclSize += RtlLengthSid(pOwnerSid) +
                   sizeof(ACCESS_ALLOWED_ACE);
                break;

            case ACL_GROUP_OBJ:
                *pAclSize += RtlLengthSid(pGroupSid) +
                   sizeof(ACCESS_ALLOWED_ACE);
                break;

            case ACL_USER:
            case ACL_GROUP:
                *pAclSize += RtlLengthSid((PSID)pSidAce->sae_sid) +
                   sizeof(ACCESS_ALLOWED_ACE);
                break;
            case ACL_EVERYONE:
                *pAclSize += RtlLengthSid(gpPvfsEveryoneSid) +
                   sizeof(ACCESS_ALLOWED_ACE);
                break;
            default:
                break;
        }
    }

    return ntStatus;
}

/****************************************************************
 ***************************************************************/

typedef struct _BSD_TO_LW_FLAGS {
    ULONG BsdFlags;
    ULONG LwFlags;
} BSD_TO_LW_FLAGS;

static const BSD_TO_LW_FLAGS AcePermMap[] =
{
    { ACL_EXECUTE,                          FILE_EXECUTE|FILE_TRAVERSE        },
    { ACL_READ_DATA|ACL_LIST_DIRECTORY,     FILE_READ_DATA
                                           |FILE_LIST_DIRECTORY               },
    { ACL_WRITE_DATA|ACL_ADD_FILE,          FILE_WRITE_DATA|FILE_ADD_FILE     },
    { ACL_APPEND_DATA|ACL_ADD_SUBDIRECTORY, FILE_APPEND_DATA
                                           |FILE_ADD_SUBDIRECTORY
                                           |FILE_CREATE_PIPE_INSTANCE         },
    { ACL_READ_NAMED_ATTRS,                 FILE_READ_EA                      },
    { ACL_WRITE_NAMED_ATTRS,                FILE_WRITE_EA                     },
    { ACL_DELETE_CHILD,                     FILE_DELETE_CHILD                 },
    { ACL_READ_ATTRIBUTES,                  READ_CONTROL|FILE_READ_ATTRIBUTES },
    { ACL_WRITE_ATTRIBUTES,                 FILE_WRITE_ATTRIBUTES             },
    { ACL_DELETE,                           DELETE                            },
    { ACL_READ_ACL,                         READ_CONTROL                      },
    { ACL_WRITE_ACL,                        WRITE_DAC                         },
    { ACL_WRITE_OWNER,                      WRITE_OWNER                       },
    { ACL_SYNCHRONIZE,                      SYNCHRONIZE                       },
    { 0,                                    0                                 }
};

static const BSD_TO_LW_FLAGS AceFlagMap[] =
{
    { ACL_ENTRY_FILE_INHERIT,               OBJECT_INHERIT_ACE                },
    { ACL_ENTRY_DIRECTORY_INHERIT,          CONTAINER_INHERIT_ACE             },
    { ACL_ENTRY_NO_PROPAGATE_INHERIT,       NO_PROPAGATE_INHERIT_ACE          },
    { ACL_ENTRY_INHERIT_ONLY,               INHERIT_ONLY_ACE                  },
    { ACL_ENTRY_INHERITED,                  INHERITED_ACE                     },
    { 0,                                    0                                 }
};

/****************************************************************
 ***************************************************************/

static
ULONG
PvfsConvertAceToLw(const BSD_TO_LW_FLAGS *pMapEntry, ULONG bsdMask)
{
    ULONG lwMask = 0;

    while (pMapEntry->BsdFlags != 0) {
        if (bsdMask & pMapEntry->BsdFlags)
            lwMask |= pMapEntry->LwFlags;
        pMapEntry++;
    }
    return lwMask;
}

/****************************************************************
 ***************************************************************/

static
ULONG
PvfsConvertAceToBsd(const BSD_TO_LW_FLAGS *pMapEntry, ULONG lwMask)
{
    ULONG bsdMask = 0;

    while (pMapEntry->LwFlags != 0) {
        if (lwMask & pMapEntry->LwFlags)
            bsdMask |= pMapEntry->BsdFlags;
        pMapEntry++;
    }
    return bsdMask;
}

/****************************************************************
 ***************************************************************/

static
ULONG
PvfsConvertAcePermToLw(ULONG bsdMask)
{
    return PvfsConvertAceToLw(AcePermMap, bsdMask);
}

/****************************************************************
 ***************************************************************/

static
ULONG
PvfsConvertAcePermToBsd(ULONG lwMask)
{
    return PvfsConvertAceToBsd(AcePermMap, lwMask);
}

/****************************************************************
 ***************************************************************/

static
ULONG
PvfsConvertAceFlagsToLw(ULONG bsdFlags)
{
    return PvfsConvertAceToLw(AceFlagMap, bsdFlags);
}

/****************************************************************
 ***************************************************************/

static
ULONG
PvfsConvertAceFlagsToBsd(ULONG lwFlags)
{
    return PvfsConvertAceToBsd(AceFlagMap, lwFlags);
}

/****************************************************************
 ***************************************************************/

static
NTSTATUS
PvfsAddAce(
    IN PACL Acl,
    IN PSID pAceSid,
    IN struct sid_acl_entry *pSidAce,
    IN SECURITY_INFORMATION aclType
    )
{
    NTSTATUS status = STATUS_INVALID_PARAMETER;
    ULONG lwAceFlags = PvfsConvertAceFlagsToLw(pSidAce->sae_flags);
    ULONG lwAceMask = PvfsConvertAcePermToLw(pSidAce->sae_perm);

    switch (pSidAce->sae_entry_type)
    {
        case ACL_ENTRY_TYPE_ALLOW:
            if (aclType == DACL_SECURITY_INFORMATION)
            {
                status = RtlAppendAccessAllowedAceEx(
                             Acl,
                             ACL_REVISION,
                             lwAceFlags,
                             lwAceMask,
                             pAceSid
                         );
            }
            break;
        case ACL_ENTRY_TYPE_DENY:
            if (aclType == DACL_SECURITY_INFORMATION)
            {
                status = RtlAppendAccessDeniedAceEx(
                             Acl,
                             ACL_REVISION,
                             lwAceFlags,
                             lwAceMask,
                             pAceSid
                             );
            }
            break;
        case ACL_ENTRY_TYPE_AUDIT:
            if (aclType == SACL_SECURITY_INFORMATION)
            {
                status = RtlAppendSystemAuditAceEx(
                             Acl,
                             ACL_REVISION,
                             lwAceFlags,
                             lwAceMask,
                             pAceSid
                             );
            }
            break;
        case ACL_ENTRY_TYPE_ALARM:
            // Deprecated in Windows.  Just ignore if part of a SACL.
            if (aclType == SACL_SECURITY_INFORMATION)
            {
                status = STATUS_SUCCESS;
            }
            break;
        default:
            status = STATUS_NOT_SUPPORTED;
            break;
    }
    return status;
}

/****************************************************************
 ***************************************************************/

static
NTSTATUS
PvfsSidAclToAcl(
    IN struct sid_acl *pSidAcl,
    IN PSID pOwnerSid,
    IN PSID pGroupSid,
    IN SECURITY_INFORMATION aclType,
    OUT PACL *ppAcl)
{
    struct sid_acl_iter aclIter;
    PACL pAcl = NULL;
    struct sid_acl_entry *pSidAce = NULL;
    DWORD dwSizeAcl;
    NTSTATUS status = STATUS_SUCCESS;

    *ppAcl = NULL;
    status = PvfsSidAclToAclSize(
                 pSidAcl,
                 pOwnerSid,
                 pGroupSid,
                 &dwSizeAcl);
    BAIL_ON_NT_STATUS(status);

    status = RTL_ALLOCATE(&pAcl, VOID, dwSizeAcl);
    BAIL_ON_NT_STATUS(status);

    status = RtlCreateAcl(pAcl, dwSizeAcl, ACL_REVISION);
    BAIL_ON_NT_STATUS(status);

    sid_acl_iter_init(&aclIter, pSidAcl);
    while ((pSidAce = sid_acl_iter_next(&aclIter)) != NULL)
    {
        PSID pAcePrincipal = NULL;

        switch (pSidAce->sae_tag)
        {
            case ACL_USER_OBJ:
                pAcePrincipal = pOwnerSid;
                break;
            case ACL_GROUP_OBJ:
                pAcePrincipal = pGroupSid;
                break;
            case ACL_EVERYONE:
                pAcePrincipal = gpPvfsEveryoneSid;
                break;
            case ACL_USER:
            case ACL_GROUP:
                pAcePrincipal = (PSID)pSidAce->sae_sid;
                break;
            default:
                continue;
        }
        status = PvfsAddAce(pAcl, pAcePrincipal, pSidAce, aclType);
        BAIL_ON_NT_STATUS(status);
    }

    *ppAcl = pAcl;
    pAcl = NULL;

error:
    LW_RTL_FREE(&pAcl);
    return status;
}

/****************************************************************
 ***************************************************************/

static
NTSTATUS
PvfsSdSerializedToSecurityDescriptor(
    IN struct security_descriptor_serialized *pFSecDescSerialized,
    IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDescRelative,
    IN OUT PULONG pSecDescRelativeLen
    )
{
    struct security_descriptor FSecDesc;
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL;
    PACL pDacl = NULL;
    PACL pSacl = NULL;
    NTSTATUS status = STATUS_SUCCESS;

    status = PvfsAllocateMemory(
                 OUT_PPVOID(&pSecDesc),
                 SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE,
                 FALSE);
    BAIL_ON_NT_STATUS(status);

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

    security_descriptor_deserialize(&FSecDesc, pFSecDescSerialized);

    // Owner
    status = RtlSetOwnerSecurityDescriptor(
                 pSecDesc,
                 (PSID)FSecDesc.sd_owner,
                 FALSE);
    BAIL_ON_NT_STATUS(status);

    // Group
    status = RtlSetGroupSecurityDescriptor(
                 pSecDesc,
                 (PSID)FSecDesc.sd_group,
                 FALSE);
    BAIL_ON_NT_STATUS(status);

    // DACL
    if (FSecDesc.sd_dacl != NULL)
    {
        status = PvfsSidAclToAcl(
                     FSecDesc.sd_dacl,
                     (PSID)FSecDesc.sd_owner,
                     (PSID)FSecDesc.sd_group,
                     DACL_SECURITY_INFORMATION,
                     &pDacl);
        BAIL_ON_NT_STATUS(status);

        status = RtlSetDaclSecurityDescriptor(
                     pSecDesc,
                     /*IsDacPresent*/TRUE,
                     pDacl,
                     /*IsDacDefaulted*/FALSE);
        BAIL_ON_NT_STATUS(status);
    }

    // SACL
    if (FSecDesc.sd_sacl != NULL)
    {
        status = PvfsSidAclToAcl(
                     FSecDesc.sd_sacl,
                     (PSID)FSecDesc.sd_owner,
                     (PSID)FSecDesc.sd_group,
                     SACL_SECURITY_INFORMATION,
                     &pSacl);
        BAIL_ON_NT_STATUS(status);

        status = RtlSetSaclSecurityDescriptor(
                     pSecDesc,
                     /*IsDacPresent*/TRUE,
                     pSacl,
                     /*IsDacDefaulted*/FALSE);
        BAIL_ON_NT_STATUS(status);
    }

    status = RtlAbsoluteToSelfRelativeSD(
                 pSecDesc,
                 pSecDescRelative,
                 pSecDescRelativeLen);
    BAIL_ON_NT_STATUS(status);

error:
    LW_RTL_FREE(&pDacl);
    LW_RTL_FREE(&pSacl);
    PvfsFreeMemory(OUT_PPVOID(&pSecDesc));

    return status;
}

/****************************************************************
 ***************************************************************/

NTSTATUS
PvfsGetSecurityDescriptorFileSd(
    IN PPVFS_CCB pCcb,
    IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDescRelative,
    IN OUT PULONG pSecDescRelativeLen
    )
{
    struct security_descriptor_serialized *pFSecDescSerialized = NULL;
    PSTR pszStringSecurityDescriptor = NULL;
    NTSTATUS status = STATUS_SUCCESS;
    size_t secDescBufLen = *pSecDescRelativeLen;
    size_t secDescLen = 0;
    int secInfoAll = (SD_INFO_OWNER |
                      SD_INFO_GROUP |
                      SD_INFO_DACL |
                      SD_INFO_SACL);
    int unixerr = 0;

    status = PvfsAllocateMemory(
                 OUT_PPVOID(&pFSecDescSerialized),
                 secDescBufLen,
                 FALSE);
    BAIL_ON_NT_STATUS(status);

    unixerr = security_descriptor_get_fd(
                  pCcb->fd,
                  secInfoAll,
                  pFSecDescSerialized,
                  secDescBufLen,
                  &secDescLen);
    if (unixerr == -1)
    {
        PVFS_BAIL_ON_UNIX_ERROR(unixerr, status);
    }

    if (secDescLen > secDescBufLen)
    {
        LWIO_LOG_TRACE("secDescLen %d > secDescBufLen %d\n", 
            secDescLen, secDescBufLen);

        status = STATUS_BUFFER_TOO_SMALL;
        BAIL_ON_NT_STATUS(status);
    }

    status = PvfsSdSerializedToSecurityDescriptor(
                 pFSecDescSerialized,
                 pSecDescRelative,
                 pSecDescRelativeLen);
    BAIL_ON_NT_STATUS(status);

    if (LwRtlLogGetLevel() >= LW_RTL_LOG_LEVEL_TRACE) {

        status = RtlAllocateSddlCStringFromSecurityDescriptor(
                     &pszStringSecurityDescriptor,
                     pSecDescRelative,
                     SDDL_REVISION_1,
                     secInfoAll);
        BAIL_ON_NT_STATUS(status);

        LWIO_LOG_TRACE("GetSd: %s\n", pszStringSecurityDescriptor);
    }

error:
    PvfsFreeMemory(OUT_PPVOID(&pFSecDescSerialized));
    RTL_FREE(&pszStringSecurityDescriptor);

    return status;
}

/****************************************************************
 ***************************************************************/

NTSTATUS
PvfsGetSecurityDescriptorFilenameSd(
    IN PCSTR pszFilename,
    IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDescRelative,
    IN OUT PULONG pSecDescRelativeLen
    )
{
    struct security_descriptor_serialized *pFSecDescSerialized = NULL;
    PSTR pszStringSecurityDescriptor = NULL;
    NTSTATUS status = STATUS_SUCCESS;
    size_t secDescBufLen = *pSecDescRelativeLen;
    size_t secDescLen = 0;
    int secInfoAll = (SD_INFO_OWNER |
                      SD_INFO_GROUP |
                      SD_INFO_DACL |
                      SD_INFO_SACL);
    int unixerr = 0;

    status = PvfsAllocateMemory(
                 OUT_PPVOID(&pFSecDescSerialized),
                 secDescBufLen,
                 FALSE);
    BAIL_ON_NT_STATUS(status);

    unixerr = security_descriptor_get_file(
                  pszFilename,
                  secInfoAll,
                  pFSecDescSerialized,
                  secDescBufLen,
                  &secDescLen);
    if (unixerr == -1)
    {
        PVFS_BAIL_ON_UNIX_ERROR(unixerr, status);
    }

    if (secDescLen > secDescBufLen)
    {
        LWIO_LOG_TRACE("secDescLen %d > secDescBufLen %d",
            secDescLen, secDescBufLen);

        status = STATUS_BUFFER_TOO_SMALL;
        BAIL_ON_NT_STATUS(status);
    }

    status = PvfsSdSerializedToSecurityDescriptor(
                 pFSecDescSerialized,
                 pSecDescRelative,
                 pSecDescRelativeLen);
    BAIL_ON_NT_STATUS(status);

    if (LwRtlLogGetLevel() >= LW_RTL_LOG_LEVEL_TRACE) {
        status = RtlAllocateSddlCStringFromSecurityDescriptor(
                     &pszStringSecurityDescriptor,
                     pSecDescRelative,
                     SDDL_REVISION_1,
                     secInfoAll);
        BAIL_ON_NT_STATUS(status);

        LWIO_LOG_TRACE(
            "GetSd on %s: %s",
            pszFilename,
            pszStringSecurityDescriptor);
    }

error:
    PvfsFreeMemory(OUT_PPVOID(&pFSecDescSerialized));
    RTL_FREE(&pszStringSecurityDescriptor);

    return status;
}

/****************************************************************
 ***************************************************************/

static
NTSTATUS
PvfsAclToSidAcl(
    IN PACL Acl,
    IN PSID pOwnerSid,
    IN PSID pGroupSid,
    IN SECURITY_INFORMATION aclType,
    OUT struct sid_acl **ppSidAcl)
{
    struct sid_acl *pSidAcl = NULL;
    NTSTATUS status = STATUS_SUCCESS;
    USHORT sizeUsed = 0;
    USHORT usLoop = 0;
    USHORT aceOffset = 0;
    USHORT aceCount = 0;

    *ppSidAcl = NULL;

    pSidAcl = sid_acl_alloc();
    if (pSidAcl == NULL) {
        status = STATUS_NO_MEMORY;
        BAIL_ON_NT_STATUS(status);
    }
    sizeUsed = RtlGetAclSizeUsed(Acl);
    aceCount = RtlGetAclAceCount(Acl);
    for (usLoop=0; usLoop < aceCount; usLoop++)
    {
        PACE_HEADER pAceHeader = NULL;
        PACCESS_ALLOWED_ACE pLwAce = NULL;
        PSID pSid = NULL;
        struct sid_acl_entry *pSidAce = NULL;
        acl_entry_type_t entryType = ACL_ENTRY_TYPE_ALARM;
        acl_tag_t entryTag = ACL_UNDEFINED_TAG;
        SECURITY_INFORMATION requiredAclType = 0;
        int unixError = EINVAL;

        status = RtlIterateAce(Acl, sizeUsed, &aceOffset, &pAceHeader);
        BAIL_ON_NT_STATUS(status);

        switch (pAceHeader->AceType)
        {
            case ACCESS_ALLOWED_ACE_TYPE:
                entryType = ACL_ENTRY_TYPE_ALLOW;
                requiredAclType = DACL_SECURITY_INFORMATION;
                break;
            case ACCESS_DENIED_ACE_TYPE:
                entryType = ACL_ENTRY_TYPE_DENY;
                requiredAclType = DACL_SECURITY_INFORMATION;
                break;
            case SYSTEM_AUDIT_ACE_TYPE:
                entryType = ACL_ENTRY_TYPE_AUDIT;
                requiredAclType = SACL_SECURITY_INFORMATION;
                break;
            case SYSTEM_ALARM_ACE_TYPE:
                entryType = ACL_ENTRY_TYPE_ALARM;
                requiredAclType = SACL_SECURITY_INFORMATION;
                break;
            default:
                continue;
        }
        if (requiredAclType != aclType) {
            status = STATUS_INVALID_PARAMETER;
            BAIL_ON_NT_STATUS(status);
        }

        pLwAce = (PACCESS_ALLOWED_ACE)pAceHeader;
        pSid = (PSID)&pLwAce->SidStart;

        if (RtlEqualSid(pSid, pOwnerSid))
        {
            entryTag = ACL_USER_OBJ;
            pSid = NULL;
        }
        else if (RtlEqualSid(pSid, pGroupSid))
        {
            entryTag = ACL_GROUP_OBJ;
            pSid = NULL;
        }
        else if (RtlEqualSid(pSid, gpPvfsEveryoneSid))
        {
            entryTag = ACL_EVERYONE;
            pSid = NULL;
        }
        else
        {
            ULONG idUnused;
            BOOLEAN bIsUser;

            status = PvfsSecuritySidMapToId(&idUnused, &bIsUser, pSid);
            BAIL_ON_NT_STATUS(status);

            if (bIsUser)
                entryTag = ACL_USER;
            else
                entryTag = ACL_GROUP;
        }
        // Update and Add Ace
        pSidAce = sid_ace_alloc((struct sid *)pSid);
        if (pSidAce == NULL) {
            status = STATUS_NO_MEMORY;
            BAIL_ON_NT_STATUS(status);
        }
        pSidAce->sae_tag = entryTag;
        pSidAce->sae_perm = PvfsConvertAcePermToBsd(pLwAce->Mask);
        pSidAce->sae_entry_type = entryType;
        pSidAce->sae_flags = PvfsConvertAceFlagsToBsd(pAceHeader->AceFlags);
        unixError = sid_acl_append_ace(&pSidAcl, pSidAce);
        sid_ace_free(pSidAce);
        if (unixError != 0)
        {
            // unixError, not errno, has the status so we must
            // do this manually.
            status = LwErrnoToNtStatus(unixError);
            BAIL_ON_NT_STATUS(status);
        }
    }

    *ppSidAcl = pSidAcl;
    pSidAcl = NULL;
error:
    if (pSidAcl != NULL)
    {
        sid_acl_free(pSidAcl);
        pSidAcl = NULL;
    }

    return status;
}

/****************************************************************
 ***************************************************************/

NTSTATUS
PvfsSetSecurityDescriptorFileSd(
    IN PPVFS_CCB pCcb,
    IN PSECURITY_DESCRIPTOR_RELATIVE pSecDescRelative,
    IN ULONG SecDescLen
    )
{
    PSECURITY_DESCRIPTOR_ABSOLUTE pSecDescAbs = NULL;
    PSTR pszStringSecurityDescriptor = NULL;
    struct security_descriptor sd;
    struct security_descriptor_serialized *pSds = NULL;
    struct sid_acl *pSidDacl = NULL;
    struct sid_acl *pSidSacl = NULL;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PACL Acl = NULL;
    BOOLEAN bDefaulted = FALSE;
    BOOLEAN bAclPresent = FALSE;
    NTSTATUS status = STATUS_SUCCESS;
    int unixError = EINVAL;
    int secInfoAll = (SD_INFO_OWNER |
                      SD_INFO_GROUP |
                      SD_INFO_DACL |
                      SD_INFO_SACL);

    if (LwRtlLogGetLevel() >= LW_RTL_LOG_LEVEL_TRACE) {
        status = RtlAllocateSddlCStringFromSecurityDescriptor(
                     &pszStringSecurityDescriptor,
                     pSecDescRelative,
                     SDDL_REVISION_1,
                     secInfoAll);
        BAIL_ON_NT_STATUS(status);

        LWIO_LOG_TRACE("SetSd: %s\n", pszStringSecurityDescriptor);
    }

    status = PvfsSecurityAclSelfRelativeToAbsoluteSD(
                 &pSecDescAbs,
                 pSecDescRelative);
    BAIL_ON_NT_STATUS(status);

    // Construct BSD SD
    security_descriptor_init(&sd);

    status = RtlGetOwnerSecurityDescriptor(
                 pSecDescAbs,
                 &pOwnerSid,
                 &bDefaulted);
    BAIL_ON_NT_STATUS(status);
    security_descriptor_set_owner(&sd, (struct sid *)pOwnerSid, bDefaulted);

    status = RtlGetGroupSecurityDescriptor(
                 pSecDescAbs,
                 &pGroupSid,
                 &bDefaulted);
    BAIL_ON_NT_STATUS(status);
    security_descriptor_set_group(&sd, (struct sid *)pGroupSid, bDefaulted);

    // Build DACL
    status = RtlGetDaclSecurityDescriptor(
                 pSecDescAbs,
                 &bAclPresent,
                 &Acl,
                 &bDefaulted);
    BAIL_ON_NT_STATUS(status);
    if (bAclPresent)
    {
        status = PvfsAclToSidAcl(
                     Acl,
                     pOwnerSid,
                     pGroupSid,
                     DACL_SECURITY_INFORMATION,
                     &pSidDacl);
        BAIL_ON_NT_STATUS(status);

        security_descriptor_set_dacl(&sd, pSidDacl, bDefaulted);
    }

    // Build SACL
    status = RtlGetSaclSecurityDescriptor(
                 pSecDescAbs,
                 &bAclPresent,
                 &Acl,
                 &bDefaulted);
    BAIL_ON_NT_STATUS(status);
    if (bAclPresent)
    {
        status = PvfsAclToSidAcl(
                     Acl,
                     pOwnerSid,
                     pGroupSid,
                     SACL_SECURITY_INFORMATION,
                     &pSidSacl);
        BAIL_ON_NT_STATUS(status);

        security_descriptor_set_sacl(&sd, pSidSacl, bDefaulted);
    }

    // Then apply.
    unixError = security_descriptor_serialize(&sd, &pSds);
    if (unixError != 0)
    {
        // unixError, not errno, has the status so we must
        // do this manually.
        status = LwErrnoToNtStatus(unixError);
        BAIL_ON_NT_STATUS(status);
    }

    if (PvfsIsDefaultStream(pCcb->pScb))
    {
        // Default stream is written to the actual fd
        unixError = security_descriptor_set_fd(
                        pCcb->fd,
                        secInfoAll,
                        pSds,
                        pSds->sds_size);
    }
    else
    {
        // Set the attributes on the base file object
        unixError = security_descriptor_set_file(
                        pCcb->pScb->pOwnerFcb->pszFilename,
                        secInfoAll,
                        pSds,
                        pSds->sds_size);
    }

    if (unixError == -1)
    {
        PVFS_BAIL_ON_UNIX_ERROR(unixError, status);
    }

error:
    RTL_FREE(&pszStringSecurityDescriptor);

    if (pSecDescAbs)
    {
        PvfsFreeAbsoluteSecurityDescriptor(&pSecDescAbs);
    }

    if (pSds != NULL)
    {
        serialized_security_descriptor_free(pSds);
        pSds = NULL;
    }

    if (pSidDacl != NULL)
    {
        sid_acl_free(pSidDacl);
        pSidDacl = NULL;
    }

    if (pSidSacl != NULL)
    {
        sid_acl_free(pSidSacl);
        pSidSacl = NULL;
    }

    return status;
}


#endif   /* HAVE_SD_SUPPORT  */

