/*++

Copyright (C) Microsoft Corporation, 1996 - 1999

Module Name:

    NTacls

Abstract:

    This module implements the CSecurityAttribute class.  It's job is to
    encapsulate the NT security descriptors as needed by Calais.

Author:

    Doug Barlow (dbarlow) 1/24/1997

Environment:

    Windows NT, Win32, C++ w/ Exceptions

Notes:

    ?Notes?

--*/

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <CalaisLb.h>


const CSecurityDescriptor::SecurityId
    CSecurityDescriptor::SID_Null =        { SECURITY_NULL_SID_AUTHORITY,    1, SECURITY_NULL_RID,           0 },
    CSecurityDescriptor::SID_World =       { SECURITY_WORLD_SID_AUTHORITY,   1, SECURITY_WORLD_RID,          0 },
    CSecurityDescriptor::SID_Local =       { SECURITY_LOCAL_SID_AUTHORITY,   1, SECURITY_LOCAL_RID,          0 },
    CSecurityDescriptor::SID_Owner =       { SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_OWNER_RID,  0 },
    CSecurityDescriptor::SID_Group =       { SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_GROUP_RID,  0 },
    CSecurityDescriptor::SID_Admins =      { SECURITY_NT_AUTHORITY,          2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS },
    CSecurityDescriptor::SID_SrvOps =      { SECURITY_NT_AUTHORITY,          2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_SYSTEM_OPS },
    CSecurityDescriptor::SID_DialUp =      { SECURITY_NT_AUTHORITY,          1, SECURITY_DIALUP_RID,         0 },
    CSecurityDescriptor::SID_Network =     { SECURITY_NT_AUTHORITY,          1, SECURITY_NETWORK_RID,        0 },
    CSecurityDescriptor::SID_Batch =       { SECURITY_NT_AUTHORITY,          1, SECURITY_BATCH_RID,          0 },
    CSecurityDescriptor::SID_Interactive = { SECURITY_NT_AUTHORITY,          1, SECURITY_INTERACTIVE_RID,    0 },
    CSecurityDescriptor::SID_Service =     { SECURITY_NT_AUTHORITY,          1, SECURITY_SERVICE_RID,        0 },
    CSecurityDescriptor::SID_System =      { SECURITY_NT_AUTHORITY,          1, SECURITY_LOCAL_SYSTEM_RID,   0 },
    CSecurityDescriptor::SID_LocalService ={ SECURITY_NT_AUTHORITY,          1, SECURITY_LOCAL_SERVICE_RID,  0 },
    CSecurityDescriptor::SID_SysDomain =   { SECURITY_NT_AUTHORITY,          1, SECURITY_BUILTIN_DOMAIN_RID, 0 };

CSecurityDescriptor::CSecurityDescriptor()
{
    m_pSD = NULL;
    m_pOwner = NULL;
    m_pGroup = NULL;
    m_pDACL = NULL;
    m_pSACL= NULL;
    m_fInheritance = FALSE;
}

CSecurityDescriptor::~CSecurityDescriptor()
{
    if (m_pSD)
        delete m_pSD;
    if (m_pOwner)
        delete[] (LPBYTE)m_pOwner;
    if (m_pGroup)
        delete[] (LPBYTE)m_pGroup;
    if (m_pDACL)
        delete[] (LPBYTE)m_pDACL;
    if (m_pSACL)
        delete[] (LPBYTE)m_pSACL;
}

HRESULT CSecurityDescriptor::Initialize()
{
    if (m_pSD)
    {
        delete m_pSD;
        m_pSD = NULL;
    }
    if (m_pOwner)
    {
        delete[] (LPBYTE)(m_pOwner);
        m_pOwner = NULL;
    }
    if (m_pGroup)
    {
        delete[] (LPBYTE)(m_pGroup);
        m_pGroup = NULL;
    }
    if (m_pDACL)
    {
        delete[] (LPBYTE)(m_pDACL);
        m_pDACL = NULL;
    }
    if (m_pSACL)
    {
        delete[] (LPBYTE)(m_pSACL);
        m_pSACL = NULL;
    }

    m_pSD = new SECURITY_DESCRIPTOR;
    if (!m_pSD)
        return E_OUTOFMEMORY;
    if (!InitializeSecurityDescriptor(m_pSD, SECURITY_DESCRIPTOR_REVISION))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        delete m_pSD;
        m_pSD = NULL;
        _ASSERTE(FALSE);
        return hr;
    }
    // Set the DACL to allow EVERYONE
    SetSecurityDescriptorDacl(m_pSD, TRUE, NULL, FALSE);
    return S_OK;
}

HRESULT CSecurityDescriptor::InitializeFromProcessToken(BOOL bDefaulted)
{
    PSID pUserSid;
    PSID pGroupSid;
    HRESULT hr;

    Initialize();
    hr = GetProcessSids(&pUserSid, &pGroupSid);
    if (!FAILED(hr))
        hr = SetOwner(pUserSid, bDefaulted);
    if (!FAILED(hr))
        hr = SetGroup(pGroupSid, bDefaulted);

    if (pUserSid)
        delete[] (LPBYTE)(pUserSid);
    if (pGroupSid)
        delete[] (LPBYTE)(pGroupSid);

    if (FAILED(hr))
        return hr;
    return S_OK;
}

HRESULT CSecurityDescriptor::InitializeFromThreadToken(BOOL bDefaulted, BOOL bRevertToProcessToken)
{
    PSID pUserSid;
    PSID pGroupSid;
    HRESULT hr;

    Initialize();
    hr = GetThreadSids(&pUserSid, &pGroupSid);
    if (HRESULT_CODE(hr) == ERROR_NO_TOKEN && bRevertToProcessToken)
        hr = GetProcessSids(&pUserSid, &pGroupSid);
    if (!FAILED(hr))
        hr = SetOwner(pUserSid, bDefaulted);
    if (!FAILED(hr))
        hr = SetGroup(pGroupSid, bDefaulted);

    if (pUserSid)
        delete[] (LPBYTE)(pUserSid);
    if (pGroupSid)
        delete[] (LPBYTE)(pGroupSid);

    if (FAILED(hr))
        return hr;
    return S_OK;
}

HRESULT CSecurityDescriptor::SetOwner(PSID pOwnerSid, BOOL bDefaulted)
{
    _ASSERTE(m_pSD);

    // Mark the SD as having no owner
    if (!SetSecurityDescriptorOwner(m_pSD, NULL, bDefaulted))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        return hr;
    }

    if (m_pOwner)
    {
        delete[] (LPBYTE)(m_pOwner);
        m_pOwner = NULL;
    }

    // If they asked for no owner don't do the copy
    if (pOwnerSid == NULL)
        return S_OK;

    // Make a copy of the Sid for the return value
    DWORD dwSize = GetLengthSid(pOwnerSid);

    m_pOwner = (PSID) new BYTE[dwSize];
    if (!m_pOwner)
    {
        // Insufficient memory to allocate Sid
        _ASSERTE(FALSE);
        return E_OUTOFMEMORY;
    }
    if (!CopySid(dwSize, m_pOwner, pOwnerSid))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        delete[] (LPBYTE)(m_pOwner);
        m_pOwner = NULL;
        return hr;
    }

    _ASSERTE(IsValidSid(m_pOwner));

    if (!SetSecurityDescriptorOwner(m_pSD, m_pOwner, bDefaulted))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        delete[] (LPBYTE)(m_pOwner);
        m_pOwner = NULL;
        return hr;
    }

    return S_OK;
}

HRESULT CSecurityDescriptor::SetGroup(PSID pGroupSid, BOOL bDefaulted)
{
    _ASSERTE(m_pSD);

    // Mark the SD as having no Group
    if (!SetSecurityDescriptorGroup(m_pSD, NULL, bDefaulted))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        return hr;
    }

    if (m_pGroup)
    {
        delete[] (LPBYTE)(m_pGroup);
        m_pGroup = NULL;
    }

    // If they asked for no Group don't do the copy
    if (pGroupSid == NULL)
        return S_OK;

    // Make a copy of the Sid for the return value
    DWORD dwSize = GetLengthSid(pGroupSid);

    m_pGroup = (PSID) new BYTE[dwSize];
    if (!m_pGroup)
    {
        // Insufficient memory to allocate Sid
        _ASSERTE(FALSE);
        return E_OUTOFMEMORY;
    }
    if (!CopySid(dwSize, m_pGroup, pGroupSid))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        delete[] (LPBYTE)(m_pGroup);
        m_pGroup = NULL;
        return hr;
    }

    _ASSERTE(IsValidSid(m_pGroup));

    if (!SetSecurityDescriptorGroup(m_pSD, m_pGroup, bDefaulted))
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        delete[] (LPBYTE)(m_pGroup);
        m_pGroup = NULL;
        return hr;
    }

    return S_OK;
}

HRESULT CSecurityDescriptor::Allow(const SecurityId *psidPrincipal, DWORD dwAccessMask)
{
    HRESULT hr = AddAccessAllowedACEToACL(&m_pDACL, psidPrincipal, dwAccessMask);
    if (SUCCEEDED(hr))
    {
        if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE))
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
    return hr;
}

HRESULT CSecurityDescriptor::Allow(LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
    HRESULT hr = AddAccessAllowedACEToACL(&m_pDACL, pszPrincipal, dwAccessMask);
    if (SUCCEEDED(hr))
    {
        if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE))
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
    return hr;
}

HRESULT CSecurityDescriptor::AllowOwner(DWORD dwAccessMask)
{
    HRESULT hr = AddAccessAllowedACEToACL(&m_pDACL, dwAccessMask);
    if (SUCCEEDED(hr))
    {
        if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE))
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
    return hr;
}

HRESULT CSecurityDescriptor::Deny(const SecurityId *psidPrincipal, DWORD dwAccessMask)
{
    HRESULT hr = AddAccessDeniedACEToACL(&m_pDACL, psidPrincipal, dwAccessMask);
    if (SUCCEEDED(hr))
    {
        if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE))
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
    return hr;
}

HRESULT CSecurityDescriptor::Deny(LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
    HRESULT hr = AddAccessDeniedACEToACL(&m_pDACL, pszPrincipal, dwAccessMask);
    if (SUCCEEDED(hr))
    {
        if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE))
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
    return hr;
}

HRESULT CSecurityDescriptor::Revoke(LPCTSTR pszPrincipal)
{
    HRESULT hr = RemovePrincipalFromACL(m_pDACL, pszPrincipal);
    if (SUCCEEDED(hr))
    {
        if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE))
            hr = HRESULT_FROM_WIN32(GetLastError());
    }
    return hr;
}

HRESULT CSecurityDescriptor::GetProcessSids(PSID* ppUserSid, PSID* ppGroupSid)
{
    BOOL bRes;
    HRESULT hr;
    HANDLE hToken = NULL;
    if (ppUserSid)
        *ppUserSid = NULL;
    if (ppGroupSid)
        *ppGroupSid = NULL;
    bRes = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
    if (!bRes)
    {
        // Couldn't open process token
        hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        return hr;
    }
    hr = GetTokenSids(hToken, ppUserSid, ppGroupSid);
    CloseHandle(hToken);
    return hr;
}

HRESULT CSecurityDescriptor::GetThreadSids(PSID* ppUserSid, PSID* ppGroupSid, BOOL bOpenAsSelf)
{
    BOOL bRes;
    HRESULT hr;
    HANDLE hToken = NULL;
    if (ppUserSid)
        *ppUserSid = NULL;
    if (ppGroupSid)
        *ppGroupSid = NULL;
    bRes = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, bOpenAsSelf, &hToken);
    if (!bRes)
    {
        // Couldn't open thread token
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }
    hr = GetTokenSids(hToken, ppUserSid, ppGroupSid);
    CloseHandle(hToken);
    return hr;
}


HRESULT CSecurityDescriptor::GetTokenSids(HANDLE hToken, PSID* ppUserSid, PSID* ppGroupSid)
{
    DWORD dwSize;
    HRESULT hr;
    PTOKEN_USER ptkUser = NULL;
    PTOKEN_PRIMARY_GROUP ptkGroup = NULL;

    if (ppUserSid)
        *ppUserSid = NULL;
    if (ppGroupSid)
        *ppGroupSid = NULL;

    if (ppUserSid)
    {
        // Get length required for TokenUser by specifying buffer length of 0
        GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
        hr = GetLastError();
        if (hr != ERROR_INSUFFICIENT_BUFFER)
        {
            // Expected ERROR_INSUFFICIENT_BUFFER
            _ASSERTE(FALSE);
            hr = HRESULT_FROM_WIN32(hr);
            goto failed;
        }

        ptkUser = (TOKEN_USER*) new BYTE[dwSize];
        if (!ptkUser)
        {
            // Insufficient memory to allocate TOKEN_USER
            _ASSERTE(FALSE);
            hr = E_OUTOFMEMORY;
            goto failed;
        }
        // Get Sid of process token.
        if (!GetTokenInformation(hToken, TokenUser, ptkUser, dwSize, &dwSize))
        {
            // Couldn't get user info
            hr = HRESULT_FROM_WIN32(GetLastError());
            _ASSERTE(FALSE);
            goto failed;
        }

        // Make a copy of the Sid for the return value
        dwSize = GetLengthSid(ptkUser->User.Sid);

        PSID pSid = (PSID) new BYTE[dwSize];
        if (!pSid)
        {
            // Insufficient memory to allocate Sid
            _ASSERTE(FALSE);
            hr = E_OUTOFMEMORY;
            goto failed;
        }
        if (!CopySid(dwSize, pSid, ptkUser->User.Sid))
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            _ASSERTE(FALSE);
            goto failed;
        }

        _ASSERTE(IsValidSid(pSid));
        *ppUserSid = pSid;
        delete[] (LPBYTE)(ptkUser);
        ptkUser = NULL;
    }
    if (ppGroupSid)
    {
        // Get length required for TokenPrimaryGroup by specifying buffer length of 0
        GetTokenInformation(hToken, TokenPrimaryGroup, NULL, 0, &dwSize);
        hr = GetLastError();
        if (hr != ERROR_INSUFFICIENT_BUFFER)
        {
            // Expected ERROR_INSUFFICIENT_BUFFER
            _ASSERTE(FALSE);
            hr = HRESULT_FROM_WIN32(hr);
            goto failed;
        }

        ptkGroup = (TOKEN_PRIMARY_GROUP*) new BYTE[dwSize];
        if (!ptkGroup)
        {
            // Insufficient memory to allocate TOKEN_USER
            _ASSERTE(FALSE);
            hr = E_OUTOFMEMORY;
            goto failed;
        }
        // Get Sid of process token.
        if (!GetTokenInformation(hToken, TokenPrimaryGroup, ptkGroup, dwSize, &dwSize))
        {
            // Couldn't get user info
            hr = HRESULT_FROM_WIN32(GetLastError());
            _ASSERTE(FALSE);
            goto failed;
        }

        // Make a copy of the Sid for the return value
        dwSize = GetLengthSid(ptkGroup->PrimaryGroup);

        PSID pSid = (PSID) new BYTE[dwSize];
        if (!pSid)
        {
            // Insufficient memory to allocate Sid
            _ASSERTE(FALSE);
            hr = E_OUTOFMEMORY;
            goto failed;
        }
        if (!CopySid(dwSize, pSid, ptkGroup->PrimaryGroup))
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            _ASSERTE(FALSE);
            goto failed;
        }

        _ASSERTE(IsValidSid(pSid));

        *ppGroupSid = pSid;
        delete[] (LPBYTE)(ptkGroup);
        ptkGroup = NULL;
    }

    return S_OK;

failed:
    if (ptkUser)
        delete[] (LPBYTE)(ptkUser);
    if (ptkGroup)
        delete[] (LPBYTE)(ptkGroup);
    return hr;
}


HRESULT CSecurityDescriptor::GetCurrentUserSID(PSID *ppSid)
{
    HANDLE tkHandle = NULL;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tkHandle))
    {
        TOKEN_USER *tkUser;
        DWORD tkSize;
        DWORD sidLength;

        // Call to get size information for alloc
        GetTokenInformation(tkHandle, TokenUser, NULL, 0, &tkSize);
        tkUser = (TOKEN_USER *) new BYTE[tkSize];
        if (NULL == tkUser)
        {
            CloseHandle(tkHandle);
            return E_OUTOFMEMORY;
        }

        // Now make the real call
        if (GetTokenInformation(tkHandle, TokenUser, tkUser, tkSize, &tkSize))
        {
            sidLength = GetLengthSid(tkUser->User.Sid);
            *ppSid = (PSID) new BYTE[sidLength];
            if (NULL != *ppSid)
            {
                memcpy(*ppSid, tkUser->User.Sid, sidLength);
                CloseHandle(tkHandle);

                delete[] (LPBYTE)(tkUser);
                return S_OK;
            }
            else
            {
                CloseHandle(tkHandle);
                delete[] (LPBYTE)(tkUser);
                return E_OUTOFMEMORY;
            }
        }
        else
        {
            CloseHandle(tkHandle);
            delete[] (LPBYTE)(tkUser);
            return HRESULT_FROM_WIN32(GetLastError());
        }
    }
    return HRESULT_FROM_WIN32(GetLastError());
}


HRESULT CSecurityDescriptor::GetPrincipalSID(LPCTSTR pszPrincipal, PSID *ppSid)
{
    HRESULT hr;
    LPTSTR pszRefDomain = NULL;
    DWORD dwDomainSize = 0;
    DWORD dwSidSize = 0;
    SID_NAME_USE snu;

    // Call to get size info for alloc
    LookupAccountName(NULL, pszPrincipal, *ppSid, &dwSidSize, pszRefDomain, &dwDomainSize, &snu);

    hr = GetLastError();
    if (hr != ERROR_INSUFFICIENT_BUFFER)
        return HRESULT_FROM_WIN32(hr);

    pszRefDomain = new TCHAR[dwDomainSize];
    if (pszRefDomain == NULL)
        return E_OUTOFMEMORY;

    *ppSid = (PSID) new BYTE[dwSidSize];
    if (*ppSid != NULL)
    {
        if (!LookupAccountName(NULL, pszPrincipal, *ppSid, &dwSidSize, pszRefDomain, &dwDomainSize, &snu))
        {
            delete[] (LPBYTE)(*ppSid);
            *ppSid = NULL;
            delete[] pszRefDomain;
            return HRESULT_FROM_WIN32(GetLastError());
        }
        delete[] pszRefDomain;
        return S_OK;
    }
    delete[] pszRefDomain;
    return E_OUTOFMEMORY;
}


HRESULT CSecurityDescriptor::Attach(PSECURITY_DESCRIPTOR pSelfRelativeSD)
{
    PACL    pDACL = NULL;
    PACL    pSACL = NULL;
    BOOL    bDACLPresent, bSACLPresent;
    BOOL    bDefaulted;
    PACL    m_pDACL = NULL;
    ACCESS_ALLOWED_ACE* pACE;
    HRESULT hr;
    PSID    pUserSid;
    PSID    pGroupSid;

    hr = Initialize();
    if(FAILED(hr))
        return hr;

    // get the existing DACL.
    if (!GetSecurityDescriptorDacl(pSelfRelativeSD, &bDACLPresent, &pDACL, &bDefaulted))
        goto failed;

    if (bDACLPresent)
    {
        if (pDACL)
        {
            // allocate new DACL.
            if (!(m_pDACL = (PACL) new BYTE[pDACL->AclSize]))
                goto failed;

            // initialize the DACL
            if (!InitializeAcl(m_pDACL, pDACL->AclSize, ACL_REVISION))
                goto failed;

            // copy the ACES
            for (int i = 0; i < pDACL->AceCount; i++)
            {
                if (!GetAce(pDACL, i, (void **)&pACE))
                    goto failed;

                if (!AddAccessAllowedAce(m_pDACL, ACL_REVISION, pACE->Mask, (PSID)&(pACE->SidStart)))
                    goto failed;
            }

            if (!IsValidAcl(m_pDACL))
                goto failed;
        }

        // set the DACL
        if (!SetSecurityDescriptorDacl(m_pSD, m_pDACL ? TRUE : FALSE, m_pDACL, bDefaulted))
            goto failed;
    }

    // get the existing SACL.
    if (!GetSecurityDescriptorSacl(pSelfRelativeSD, &bSACLPresent, &pSACL, &bDefaulted))
        goto failed;

    if (bSACLPresent)
    {
        if (pSACL)
        {
            // allocate new SACL.
            if (!(m_pSACL = (PACL) new BYTE[pSACL->AclSize]))
                goto failed;

            // initialize the SACL
            if (!InitializeAcl(m_pSACL, pSACL->AclSize, ACL_REVISION))
                goto failed;

            // copy the ACES
            for (int i = 0; i < pSACL->AceCount; i++)
            {
                if (!GetAce(pSACL, i, (void **)&pACE))
                    goto failed;

                if (!AddAccessAllowedAce(m_pSACL, ACL_REVISION, pACE->Mask, (PSID)&(pACE->SidStart)))
                    goto failed;
            }

            if (!IsValidAcl(m_pSACL))
                goto failed;
        }

        // set the SACL
        if (!SetSecurityDescriptorSacl(m_pSD, m_pSACL ? TRUE : FALSE, m_pSACL, bDefaulted))
            goto failed;
    }

    if (!GetSecurityDescriptorOwner(m_pSD, &pUserSid, &bDefaulted))
        goto failed;

    if (FAILED(SetOwner(pUserSid, bDefaulted)))
        goto failed;

    if (!GetSecurityDescriptorGroup(m_pSD, &pGroupSid, &bDefaulted))
        goto failed;

    if (FAILED(SetGroup(pGroupSid, bDefaulted)))
        goto failed;

    if (!IsValidSecurityDescriptor(m_pSD))
        goto failed;

    return hr;

failed:
    if (m_pDACL)
        delete[] (LPBYTE)(m_pDACL);
    if (m_pSD)
        delete[] (LPBYTE)(m_pSD);
    return E_UNEXPECTED;
}

HRESULT CSecurityDescriptor::AttachObject(HANDLE hObject)
{
    HRESULT hr;
    DWORD dwSize = 0;
    PSECURITY_DESCRIPTOR pSD = NULL;

    GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
        DACL_SECURITY_INFORMATION, pSD, 0, &dwSize);

    hr = GetLastError();
    if (hr != ERROR_INSUFFICIENT_BUFFER)
        return HRESULT_FROM_WIN32(hr);

    pSD = (PSECURITY_DESCRIPTOR) new BYTE[dwSize];
    if (NULL==pSD)
        return E_OUTOFMEMORY;

    if (!GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
        DACL_SECURITY_INFORMATION, pSD, dwSize, &dwSize))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        delete[] (LPBYTE)(pSD);
        return hr;
    }

    hr = Attach(pSD);
    delete[] (LPBYTE)(pSD);
    return hr;
}


HRESULT CSecurityDescriptor::CopyACL(PACL pDest, PACL pSrc)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    LPVOID pAce;
    ACE_HEADER *aceHeader;

    if (pSrc == NULL)
        return S_OK;

    if (!GetAclInformation(pSrc, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
        return HRESULT_FROM_WIN32(GetLastError());

    // Copy all of the ACEs to the new ACL
    for (UINT i = 0; i < aclSizeInfo.AceCount; i++)
    {
        if (!GetAce(pSrc, i, &pAce))
            return HRESULT_FROM_WIN32(GetLastError());

        aceHeader = (ACE_HEADER *) pAce;

        if (!AddAce(pDest, ACL_REVISION, 0xffffffff, pAce, aceHeader->AceSize))
            return HRESULT_FROM_WIN32(GetLastError());
    }

    return S_OK;
}

HRESULT CSecurityDescriptor::AddAccessDeniedACEToACL(PACL *ppAcl, LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    int aclSize;
    DWORD returnValue;
    PSID principalSID;
    PACL oldACL, newACL;

    oldACL = *ppAcl;

    returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
    if (FAILED(returnValue))
        return returnValue;

    aclSizeInfo.AclBytesInUse = 0;
    if (*ppAcl != NULL)
        GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

    newACL = (PACL) new BYTE[aclSize];
    if (NULL==newACL)
    {
        delete[] (LPBYTE)principalSID;
        return E_OUTOFMEMORY;
    }

    if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
    {
        delete[] (LPBYTE)(newACL);
        delete[] (LPBYTE)(principalSID);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    if (!AddAccessDeniedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
    {
        delete[] (LPBYTE)(newACL);
        delete[] (LPBYTE)(principalSID);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    returnValue = CopyACL(newACL, oldACL);
    if (FAILED(returnValue))
    {
        delete[] (LPBYTE)(newACL);
        delete[] (LPBYTE)(principalSID);
        return returnValue;
    }

    *ppAcl = newACL;

    if (oldACL != NULL)
        delete[] (LPBYTE)(oldACL);
    delete[] (LPBYTE)(principalSID);
    return S_OK;
}


HRESULT CSecurityDescriptor::AddAccessDeniedACEToACL(PACL *ppAcl, const SecurityId *psidPrincipal, DWORD dwAccessMask)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    int aclSize;
    DWORD returnValue;
    PSID principalSID;
    PACL oldACL, newACL;
    DWORD dwLen, dwIx;

    oldACL = *ppAcl;

    ASSERT(255 >= psidPrincipal->dwRidCount);
    dwLen = GetSidLengthRequired((UCHAR)psidPrincipal->dwRidCount);
    principalSID = (PSID)(new BYTE[dwLen]);
    if (NULL==principalSID)
        return E_OUTOFMEMORY;
    if (!InitializeSid(
                principalSID,
                (PSID_IDENTIFIER_AUTHORITY)&psidPrincipal->sid,
                (UCHAR)psidPrincipal->dwRidCount))
    {
        delete[] (LPBYTE)principalSID;
        return HRESULT_FROM_WIN32(GetLastError());
    }
    for (dwIx = 0; dwIx < psidPrincipal->dwRidCount; dwIx += 1)
        *GetSidSubAuthority(principalSID, dwIx) = psidPrincipal->rgRids[dwIx];
    if (!IsValidSid(principalSID))
    {
        delete[] (LPBYTE)principalSID;
        return HRESULT_FROM_WIN32(GetLastError());
    }

    aclSizeInfo.AclBytesInUse = 0;
    if (*ppAcl != NULL)
        GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

    newACL = (PACL) new BYTE[aclSize];
    if (NULL==newACL)
    {
        delete[] (LPBYTE)principalSID;
        return E_OUTOFMEMORY;
    }

    if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
    {
        delete[] (LPBYTE)newACL;
        delete[] (LPBYTE)(principalSID);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    if (!AddAccessDeniedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
    {
        delete[] (LPBYTE)newACL;
        delete[] (LPBYTE)(principalSID);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    returnValue = CopyACL(newACL, oldACL);
    if (FAILED(returnValue))
    {
        delete[] (LPBYTE)newACL;
        delete[] (LPBYTE)(principalSID);
        return returnValue;
    }

    *ppAcl = newACL;

    if (oldACL != NULL)
        delete[] (LPBYTE)(oldACL);
    delete[] (LPBYTE)(principalSID);
    return S_OK;
}


HRESULT CSecurityDescriptor::AddAccessAllowedACEToACL(PACL *ppAcl, const SecurityId *psidPrincipal, DWORD dwAccessMask)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    int aclSize;
    DWORD returnValue;
    PSID principalSID = NULL;
    PACL oldACL, newACL;
    DWORD dwLen, dwIx;

    oldACL = *ppAcl;

    ASSERT(255 >= psidPrincipal->dwRidCount);
    dwLen = GetSidLengthRequired((UCHAR)psidPrincipal->dwRidCount);
    principalSID = (PSID)(new BYTE[dwLen]);
    if (NULL==principalSID)
        return E_OUTOFMEMORY;
    if (!InitializeSid(
                principalSID,
                (PSID_IDENTIFIER_AUTHORITY)&psidPrincipal->sid,
                (UCHAR)psidPrincipal->dwRidCount))
    {
        delete[] (LPBYTE)principalSID;
        return HRESULT_FROM_WIN32(GetLastError());
    }
    for (dwIx = 0; dwIx < psidPrincipal->dwRidCount; dwIx += 1)
        *GetSidSubAuthority(principalSID, dwIx) = psidPrincipal->rgRids[dwIx];
    if (!IsValidSid(principalSID))
    {
        delete[] (LPBYTE)principalSID;
        return HRESULT_FROM_WIN32(GetLastError());
    }

    aclSizeInfo.AclBytesInUse = 0;
    if (*ppAcl != NULL)
        GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

    newACL = (PACL) new BYTE[aclSize];
    if (NULL==newACL) {
        delete[] (LPBYTE)principalSID;
        return E_OUTOFMEMORY;
    }

    if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
    {
        delete[] (LPBYTE)newACL;
        delete[] (LPBYTE)principalSID;
        return HRESULT_FROM_WIN32(GetLastError());
    }

    returnValue = CopyACL(newACL, oldACL);
    if (FAILED(returnValue))
    {
        delete[] (LPBYTE)newACL;
        delete[] (LPBYTE)principalSID;
        return returnValue;
    }

    if (!AddAccessAllowedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
    {
        delete[] (LPBYTE)newACL;
        delete[] (LPBYTE)principalSID;
        return HRESULT_FROM_WIN32(GetLastError());
    }

    *ppAcl = newACL;

    if (oldACL != NULL)
        delete[] (LPBYTE)(oldACL);
    delete[] (LPBYTE)principalSID;
    return S_OK;
}


HRESULT CSecurityDescriptor::AddAccessAllowedACEToACL(PACL *ppAcl, LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    int aclSize;
    DWORD returnValue;
    PSID principalSID;
    PACL oldACL, newACL;

    oldACL = *ppAcl;

    returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
    if (FAILED(returnValue))
        return returnValue;

    aclSizeInfo.AclBytesInUse = 0;
    if (*ppAcl != NULL)
        GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

    newACL = (PACL) new BYTE[aclSize];
    if (NULL==newACL)
    {
        delete[] (LPBYTE)principalSID;
        return E_OUTOFMEMORY;
    }

    if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
    {
        delete[] (LPBYTE)(newACL);
        delete[] (LPBYTE)(principalSID);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    returnValue = CopyACL(newACL, oldACL);
    if (FAILED(returnValue))
    {
        delete[] (LPBYTE)(newACL);
        delete[] (LPBYTE)(principalSID);
        return returnValue;
    }

    if (!AddAccessAllowedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
    {
        delete[] (LPBYTE)(newACL);
        delete[] (LPBYTE)(principalSID);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    *ppAcl = newACL;

    if (oldACL != NULL)
        delete[] (LPBYTE)(oldACL);
    delete[] (LPBYTE)(principalSID);
    return S_OK;
}

HRESULT CSecurityDescriptor::AddAccessAllowedACEToACL(PACL *ppAcl, DWORD dwAccessMask)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    int aclSize;
    DWORD returnValue;
    PACL oldACL, newACL;

    oldACL = *ppAcl;

    if (!IsValidSid(m_pOwner))
    {
        _ASSERTE(FALSE);
        return E_INVALIDARG;
    }

    aclSizeInfo.AclBytesInUse = 0;
    if (*ppAcl != NULL)
        GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(m_pOwner) - sizeof(DWORD);

    newACL = (PACL) new BYTE[aclSize];
    if (NULL==newACL)
    {
        return E_OUTOFMEMORY;
    }

    if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
    {
        delete[] (LPBYTE)(newACL);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    returnValue = CopyACL(newACL, oldACL);
    if (FAILED(returnValue))
    {
        delete[] (LPBYTE)(newACL);
        return returnValue;
    }

    if (!AddAccessAllowedAce(newACL, ACL_REVISION2, dwAccessMask, m_pOwner))
    {
        delete[] (LPBYTE)(newACL);
        return HRESULT_FROM_WIN32(GetLastError());
    }

    *ppAcl = newACL;

    if (oldACL != NULL)
        delete[] (LPBYTE)(oldACL);
    return S_OK;
}

HRESULT CSecurityDescriptor::RemovePrincipalFromACL(PACL pAcl, LPCTSTR pszPrincipal)
{
    ACL_SIZE_INFORMATION aclSizeInfo;
    ULONG i;
    LPVOID ace;
    ACCESS_ALLOWED_ACE *accessAllowedAce;
    ACCESS_DENIED_ACE *accessDeniedAce;
    SYSTEM_AUDIT_ACE *systemAuditAce;
    PSID principalSID;
    DWORD returnValue;
    ACE_HEADER *aceHeader;

    returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
    if (FAILED(returnValue))
        return returnValue;

    GetAclInformation(pAcl, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

    for (i = 0; i < aclSizeInfo.AceCount; i++)
    {
        if (!GetAce(pAcl, i, &ace))
        {
            delete[] (LPBYTE)(principalSID);
            return HRESULT_FROM_WIN32(GetLastError());
        }

        aceHeader = (ACE_HEADER *) ace;

        if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
        {
            accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;

            if (EqualSid(principalSID, (PSID) &accessAllowedAce->SidStart))
            {
                DeleteAce(pAcl, i);
                delete[] (LPBYTE)(principalSID);
                return S_OK;
            }
        } else

        if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
        {
            accessDeniedAce = (ACCESS_DENIED_ACE *) ace;

            if (EqualSid(principalSID, (PSID) &accessDeniedAce->SidStart))
            {
                DeleteAce(pAcl, i);
                delete[] (LPBYTE)(principalSID);
                return S_OK;
            }
        } else

        if (aceHeader->AceType == SYSTEM_AUDIT_ACE_TYPE)
        {
            systemAuditAce = (SYSTEM_AUDIT_ACE *) ace;

            if (EqualSid(principalSID, (PSID) &systemAuditAce->SidStart))
            {
                DeleteAce(pAcl, i);
                delete[] (LPBYTE)(principalSID);
                return S_OK;
            }
        }
    }
    delete[] (LPBYTE)(principalSID);
    return S_OK;
}


HRESULT CSecurityDescriptor::SetPrivilege(LPCTSTR privilege, BOOL bEnable, HANDLE hToken)
{
    HRESULT hr;
    TOKEN_PRIVILEGES tpPrevious;
    TOKEN_PRIVILEGES tp;
    DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
    LUID luid;
    HANDLE hMyToken = NULL;

    // if no token specified open process token
    if (hToken == 0)
    {
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hMyToken))
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            _ASSERTE(FALSE);
            return hr;
        }
        hToken = hMyToken;
    }

    if (!LookupPrivilegeValue(NULL, privilege, &luid ))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        if (NULL != hMyToken)
            CloseHandle(hMyToken);
        return hr;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = 0;

    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        if (NULL != hMyToken)
            CloseHandle(hMyToken);
        return hr;
    }

    tpPrevious.PrivilegeCount = 1;
    tpPrevious.Privileges[0].Luid = luid;

    if (bEnable)
        tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
    else
        tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes);

    if (!AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        _ASSERTE(FALSE);
        if (NULL != hMyToken)
            CloseHandle(hMyToken);
        return hr;
    }
    if (NULL != hMyToken)
        CloseHandle(hMyToken);
    return S_OK;
}

CSecurityDescriptor::operator LPSECURITY_ATTRIBUTES()
{
    m_saAttrs.nLength = sizeof (m_saAttrs);
    m_saAttrs.lpSecurityDescriptor = m_pSD;
    m_saAttrs.bInheritHandle = m_fInheritance;
    return (&m_saAttrs);
}