//****************************************************************************** // // Microsoft Confidential. Copyright (c) Microsoft Corporation 1999. All rights reserved // // File: ChkAcc.cpp // // Description: RSOP Security functions // // History: 31-Jul-99 leonardm Created // //****************************************************************************** #include "uenv.h" #include #include #include #include #include #include #include "ChkAcc.h" #include "smartptr.h" #include "RsopUtil.h" #include "RsopDbg.h" CDebug dbgAccessCheck( L"Software\\Microsoft\\Windows NT\\CurrentVersion\\winlogon", L"ChkAccDebugLevel", L"ChkAcc.log", L"ChkAcc.bak", TRUE ); #undef dbg #define dbg dbgAccessCheck //****************************************************************************** // // Class: CSid // // Description: Objects of this class encapsulate and simplify manipulation of // SIDs for the purposes of access control checks. // // History: 7/30/99 leonardm Created. // //****************************************************************************** class CSid { private: bool m_bState; // Flag to indicate whether the object is currently // associated with a valid SID. PSID m_pSid; // Pointer to the SID encapsulated by thi class. CWString m_sUser; // User name CWString m_sDomain; // Domain where the user account resides. CWString m_sComputer; // Computer in the domain where the user account exists CWString m_sSidString; // String representation of the encapsulated SID. SID_NAME_USE m_eUse; // SID type. public: // // Overloaded constructors // CSid() : m_bState(false), m_pSid(NULL), m_eUse(SidTypeUnknown){} CSid(const PSID pSid, const WCHAR* szComputer = NULL ) : m_bState(false), m_pSid(NULL), m_eUse(SidTypeUnknown) { Initialize(pSid, szComputer); } CSid(const WCHAR* szUser, const WCHAR* szComputer = NULL ) : m_bState(false), m_pSid(NULL), m_eUse(SidTypeUnknown) { Initialize(szUser, szComputer); } CSid(const CSid& otherSid) : m_bState(false), m_pSid(NULL), m_eUse(SidTypeUnknown) { Initialize(otherSid.User(), otherSid.Computer()); } ~CSid() { Reset(); } // // Attempts to initialize an object of this class by associating it with // an existing user represented in the SID pointed to by pSid. // bool Initialize(PSID pSid, const WCHAR* szComputer = NULL); // // Attempts to initialize an object of this class by associating it with // an existing user represented by szUser. // bool Initialize(const WCHAR* szUser, const WCHAR* szComputer = NULL); // // Indicates whether this object is currently associated with a valid user. // bool IsValid() const{ return m_bState; } // // Overload of assignment operator. Copies one CSid to another. // After a this call two objects encapsulate the same SID. // However, each owns its own memory. So the destructor of // one object can be called without invalidating resources used by the other. // CSid& operator = (CSid otherSid); // // Overload of assignment operator. Initializes // the current object with an existing SID. // CSid& operator = (PSID pSid); // // Returns a pointer to the SID encapsulated by this object. // PSID GetSidPtr() const{ return m_pSid; } // // Returns the name of the user associated with this object. // const WCHAR* User() const{ return m_sUser; } // // Returns the name of the computer used to Initialize this object with a user. // const WCHAR* Computer() const{ return m_sComputer; } // // Returns the name of the domain of which the user is a member. // const WCHAR* Domain() const{ return m_sDomain; } // // Returns a the SID associated with object in // a string format suitable for display // const WCHAR* SidString() const{ return m_sSidString; } // // Returns the type as it was found in the SID associated // with this object during processing of Initialize. // SID_NAME_USE SidType() const{ return m_eUse; } // // Breaks the association of this object with an exisitng SID. Releases // memory allocate during Initialize and set the internal state to Invalid. // This call is safe even if the object is not initialized. // void Reset(); }; //****************************************************************************** // // Function: CSid::Initialize // // Description: Attempts to initialize an object of this class by associating it with // an existing user represented in the SID pointed to by pSid. // // Parameters: - pSid: Pointer to an exisitng SID. The memory pointed to // by this parameter may be released after a call to // this function since objects of this class allocate // and release their own memory for the associated SID. // - szComputer: Pointer to a string naming the computer where the // lookup of the account is to take place. If NULL, the // curent computer is used. // // Return: true on success. false otherwise. // // History: 7/30/99 leonardm Created. // //****************************************************************************** bool CSid::Initialize(const PSID pSid, const WCHAR* szComputer/* = NULL */) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CSid::Initialize - Entering..."); Reset(); if(!IsValidSid(pSid)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - IsValidSid returned false!"); return m_bState; } m_pSid = new BYTE[GetLengthSid(pSid)]; if(!m_pSid) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - call to new failed!"); return m_bState; } BOOL bRes = CopySid(GetLengthSid(pSid), m_pSid, pSid); if(!bRes) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - Call to CopySid returned FALSE!"); Reset(); return m_bState; } DWORD cbUser = 0; DWORD cbDomain = 0; LookupAccountSid(szComputer, m_pSid, NULL, &cbUser, NULL, &cbDomain, &m_eUse); if(cbUser && cbDomain) { XPtrSTxpszUser = new WCHAR[cbUser]; if(!xpszUser) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - call to new failed!"); Reset(); return m_bState; } XPtrSTxpszDomain = new WCHAR[cbDomain]; if(!xpszDomain) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - call to new failed!"); Reset(); return m_bState; } bRes = LookupAccountSid(szComputer, m_pSid, xpszUser, &cbUser, xpszDomain, &cbDomain, &m_eUse); if(!bRes) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - call to LookupAccountSid returned FALSE!"); Reset(); return m_bState; } m_sUser = xpszUser; if(!m_sUser.ValidString()) { Reset(); return m_bState; } m_sDomain = xpszDomain; if(!m_sDomain.ValidString()) { Reset(); return m_bState; } XPtrLFszSidString = NULL; bRes = ConvertSidToStringSid(m_pSid, &szSidString); if(!bRes) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CSid::Initialize - call to ConvertSidToStringSid returned false!"); Reset(); return m_bState; } m_sSidString = szSidString; if(!m_sSidString.ValidString()) { Reset(); return m_bState; } m_bState = true; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CSid::Initialize - Leaving successfully."); return m_bState; } //****************************************************************************** // // Function: CSid::Initialize // // Description: Attempts to initialize an object of this class by associating it with // an existing user represented by szUser. // // Parameters: - szUser: Name of an existing user. // - szComputer: Pointer to a string naming the computer where the // lookup of the account is to take place. If NULL, the // curent computer is used. // // Return: true on success. false otherwise. // // History: 7/30/99 leonardm Created. // //****************************************************************************** bool CSid::Initialize(const WCHAR* szUser, const WCHAR* szComputer/* = NULL */) { Reset(); if(!szUser) { return m_bState; } m_sUser = szUser; if(!m_sUser.ValidString()) { Reset(); return m_bState; } m_sComputer = szComputer ? szComputer : L""; if(!m_sComputer.ValidString()) { Reset(); return m_bState; } DWORD cSid = 0; DWORD cDomain = 0; LookupAccountName(szComputer, szUser, NULL, &cSid, NULL, &cDomain, &m_eUse); if(cSid && cDomain) { m_pSid = new BYTE[cSid]; if(!m_pSid) { Reset(); return m_bState; } XPtrSTxpszDomain = new WCHAR[cDomain]; if(!xpszDomain) { Reset(); return m_bState; } BOOL bRes = LookupAccountName(szComputer, szUser, m_pSid, &cSid, xpszDomain, &cDomain, &m_eUse); if(!bRes) { Reset(); return m_bState; } m_sDomain = xpszDomain; if(!m_sDomain.ValidString()) { Reset(); return m_bState; } XPtrLFszSidString = NULL; bRes = ConvertSidToStringSid(m_pSid, &szSidString); if(!bRes) { Reset(); return m_bState; } m_sSidString = szSidString; if(!m_sSidString.ValidString()) { Reset(); return m_bState; } m_bState = true; } return m_bState; } //****************************************************************************** // // Function: Reset // // Description: Breaks the association of this object with an exisitng SID. Releases // memory allocate during Initialize and set the internal state to Invalid. // This call is safe even if the object is not initialized. // // Parameters: None. // // Return: // // History: 7/30/99 leonardm Created. // //****************************************************************************** void CSid::Reset() { delete[] (BYTE*)m_pSid; m_pSid = NULL; m_sUser = m_sDomain = m_sComputer = m_sSidString = L""; m_eUse = SidTypeUnknown; m_bState = false; } //****************************************************************************** // // Function: operator = // // Description: Overload of assignment operator. Copies one CSid to another. // After a this call two objects encapsulate the same SID. // However, each owns its own memory. So the destructor of // one object can be called without invalidating resources used by the other. // // Parameters: - otherSid: The CSid whose value is to be copied. // // Return: A reference to the object on which the call is invoked. // // History: 7/30/99 leonardm Created. // //****************************************************************************** CSid& CSid::operator = (CSid otherSid) { if(this == &otherSid) { return *this; } Reset(); if(!IsValidSid(otherSid.GetSidPtr())) { return *this; } Initialize(otherSid.User(), otherSid.Computer()); return *this; } //****************************************************************************** // // Function: operator = // // Description: Overload of assignment operator. Initializes the current object with // an existing SID. // // Parameters: - otherSid: The CSid whose value is to be copied. // // Return: A reference to the object on which the call is invoked. // // History: 7/30/99 leonardm Created. // //****************************************************************************** CSid& CSid::operator = (PSID pSid) { Reset(); if(!IsValidSid(pSid)) { return *this; } Initialize(pSid); return *this; } //****************************************************************************** // // Structure: CTLink // // Description: // // History: 8/02/99 leonardm Created. // //****************************************************************************** template struct CTLink { T* m_pData; CTLink* m_pNext; CTLink* m_pPrev; CTLink(T* pData) : m_pData(pData), m_pNext(NULL), m_pPrev(NULL){} ~CTLink() { delete m_pData; } }; //****************************************************************************** // // Structure: CRsopToken // // Description: This reprents a pseudo-token containing an arbitrary // combination of SIDs which // can be used to check access to objects protected with security descriptors. // // History: 7/30/99 leonardm Created. // //****************************************************************************** struct CRsopToken { CTLink* m_pSidsHead; CTLink* m_pSidsTail; // // Default constructor. Constructs an object with no sids. Access checks // against an CRsopToken with no SIDs will always fail; even on objects // with no DACL. // CRsopToken() : m_pSidsHead(NULL), m_pSidsTail(NULL) {} // // Destructor. Releases the memory pointed // to by each of the elements of m_pSidsHead. // ~CRsopToken(); // // Adds a CSid to this object. The client of this class allocates the memory // for the CSid and this class releases the memory in the destructor. // HRESULT AddSid(CSid* pSid); }; //****************************************************************************** // // Function: CRsopToken::~CRsopToken // // Description: Destructor. Releases the memory pointed to by each of the elements of // m_pSidsHead. // // Parameters: None. // // Return: N/A // // History: 7/30/99 leonardm Created. // //****************************************************************************** CRsopToken::~CRsopToken() { CTLink* pLinkIterator = m_pSidsHead; while(pLinkIterator) { CTLink* pLinkToDelete = pLinkIterator; pLinkIterator = pLinkIterator->m_pNext; delete pLinkToDelete; } } //****************************************************************************** // // Function: CRsopToken::AddSid // // Description: Adds a CSid to this object. The client of this class allocates the // memory for the CSid and this class releases the memory in the destructor. // // Parameters: - pSid: A pointer to a CSid. The memory pointed to by pSid will be released by // in the destructor. // // Return: On success it returns S_OK. // On failure it returns E_OUTOFMEMORY. // // History: 7/30/99 leonardm Created. // //****************************************************************************** HRESULT CRsopToken::AddSid(CSid* pSid) { // // first check if the Sid is already in there // for(CTLink* pTraverseLink = m_pSidsHead; pTraverseLink; pTraverseLink = pTraverseLink->m_pNext) { // // If one of the SIDs in the RsopToken matches // this Sid, return // if (EqualSid(pSid->GetSidPtr(), pTraverseLink->m_pData->GetSidPtr())) return S_OK; } // // Allocate a new link using the pSid passed in. // CTLink* pLink = new CTLink(pSid); if(!pLink) { return E_OUTOFMEMORY; } if(!m_pSidsHead) { m_pSidsHead = pLink; } else { m_pSidsTail->m_pNext = pLink; } m_pSidsTail = pLink; return S_OK; } //****************************************************************************** // // Function: GetUserInfo // // Description: // // Parameters: // // Return: S_OK if successful. An HRESULT error code on failure. // // History: 8/7/99 leonardm Created. // //****************************************************************************** HRESULT GetUserInfo(const CWString& sUser, CWString& sUserName, CWString& sUserDomain, CWString& sUserDC) { if(!sUser.ValidString()) { return E_FAIL; } NET_API_STATUS status; XPtrSTxpUserName = NULL; XPtrSTxpUserDomain = NULL; size_t len = sUser.length(); WCHAR* backslashPos = wcschr(sUser, L'\\'); if(backslashPos) { size_t index = backslashPos - sUser; xpUserDomain = new WCHAR[index + 1]; if(!xpUserDomain ) { return E_OUTOFMEMORY; } wcsncpy(xpUserDomain, sUser, index); xpUserDomain[index] = L'\0'; xpUserName = new WCHAR[len - index]; if(!xpUserName) { return E_OUTOFMEMORY; } wcsncpy(xpUserName, backslashPos + 1, len - index - 1); xpUserName[len - index - 1] = L'\0'; } sUserName = xpUserName ? CWString(xpUserName) : sUser; if(!sUserName.ValidString()) { return E_FAIL; } if(xpUserDomain) { // Use supplied domain. sUserDomain = xpUserDomain; } else { // Use current domain WKSTA_INFO_100* pWkstaInfo = NULL; status = NetWkstaGetInfo(NULL,100,(LPBYTE*)&pWkstaInfo); if(status != NERR_Success) { return E_FAIL; } sUserDomain = pWkstaInfo->wki100_langroup; NetApiBufferFree(pWkstaInfo); } if(!sUserDomain.ValidString()) { return E_FAIL; } PDOMAIN_CONTROLLER_INFO pDCInfo = 0; DWORD dwError = DsGetDcName(0, sUserDomain, 0, 0, 0, &pDCInfo ); if ( dwError != NO_ERROR ) { return HRESULT_FROM_WIN32( dwError ); } sUserDC = pDCInfo->DomainControllerName; NetApiBufferFree(pDCInfo); if ( !sUserDC.ValidString() ) { return E_FAIL; } return S_OK; } //****************************************************************************** // // Function: AddSpecialGroup // // Description: // // Parameters: // // Return: S_OK if successful. An HRESULT error code on failure. // // History: 8/7/99 leonardm Created. // //****************************************************************************** HRESULT AddSpecialGroup(PRSOPTOKEN pRsopToken, PSID pSid) { CRsopToken* pToken = static_cast(pRsopToken); XPtrST xpCSid = new CSid(pSid); if(!xpCSid) { return E_OUTOFMEMORY; } if(!xpCSid->IsValid()) { return E_FAIL; } HRESULT hr = pToken->AddSid(xpCSid); if(FAILED(hr)) { return hr; } xpCSid.Acquire(); return S_OK; } //****************************************************************************** // // Function: AddSpecialGroups // // Description: // // Parameters: // // Return: S_OK if successful. An HRESULT error code on failure. // // History: 8/7/99 leonardm Created. // //****************************************************************************** HRESULT AddSpecialGroups(PRSOPTOKEN pRsopToken ) { BOOL bRes; PSID pSid; HRESULT hr; // // Everyone // SID_IDENTIFIER_AUTHORITY IdentifierAuthority_World = SECURITY_WORLD_SID_AUTHORITY; bRes = AllocateAndInitializeSid(&IdentifierAuthority_World, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSid); if(!bRes) { DWORD dwLastError = GetLastError(); return E_FAIL; } hr = AddSpecialGroup(pRsopToken, pSid); FreeSid(pSid); if(FAILED(hr)) { return hr; } // // Authenticated users // SID_IDENTIFIER_AUTHORITY IdentifierAuthority_NT = SECURITY_NT_AUTHORITY; bRes = AllocateAndInitializeSid(&IdentifierAuthority_NT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &pSid); if(!bRes) { DWORD dwLastError = GetLastError(); return E_FAIL; } hr = AddSpecialGroup(pRsopToken, pSid); FreeSid(pSid); if(FAILED(hr)) { return hr; } return S_OK; } //****************************************************************************** // // Function: AddGlobalGroups // // Description: // // Parameters: // // Return: S_OK if successful. An HRESULT error code on failure. // // History: 8/7/99 leonardm Created. // //****************************************************************************** HRESULT AddGlobalGroups(const CWString& sUserName, const CWString& sUserDC, PRSOPTOKEN pRsopToken) { if(!sUserName.ValidString()) { return E_FAIL; } CRsopToken* pToken = static_cast(pRsopToken); BYTE* pBuffer = NULL; DWORD dwEntriesread; DWORD dwTotalentries; NET_API_STATUS result = NetUserGetGroups( sUserDC, sUserName, 0, &pBuffer, MAX_PREFERRED_LENGTH, &dwEntriesread, &dwTotalentries); if(result != NERR_Success) { return HRESULT_FROM_WIN32(result); } HRESULT hr = S_OK; GROUP_USERS_INFO_0* pGui = (GROUP_USERS_INFO_0*)pBuffer; XPtrST xpCSid; for(DWORD dwi = 0; dwi < dwEntriesread; dwi++) { xpCSid = new CSid((pGui[dwi]).grui0_name); if(!xpCSid) { hr = E_OUTOFMEMORY; break; } if(!xpCSid->IsValid()) { hr = E_FAIL; break; } hr = pToken->AddSid(xpCSid); if(FAILED(hr)) { break; } xpCSid.Acquire(); } NetApiBufferFree(pBuffer); return hr; } //****************************************************************************** // // Function: AddLocalGroups // // Description: // // Parameters: // // Return: S_OK if successful. An HRESULT error code on failure. // // //****************************************************************************** HRESULT AddLocalGroups(const CWString& sUserName, const CWString& sUserDC, PRSOPTOKEN pRsopToken) { if(!sUserName.ValidString()) { return E_FAIL; } CRsopToken* pToken = static_cast(pRsopToken); BYTE* pBuffer = NULL; DWORD dwEntriesread; DWORD dwTotalentries; NET_API_STATUS result = NetUserGetLocalGroups( sUserDC, sUserName, 0, LG_INCLUDE_INDIRECT, &pBuffer, MAX_PREFERRED_LENGTH, &dwEntriesread, &dwTotalentries); if(result != NERR_Success) { HRESULT_FROM_WIN32(result); } HRESULT hr = S_OK; LPLOCALGROUP_USERS_INFO_0 pLui = (LPLOCALGROUP_USERS_INFO_0)pBuffer; XPtrST xpCSid; for(DWORD dwi = 0; dwi < dwEntriesread; dwi++) { xpCSid = new CSid((pLui[dwi]).lgrui0_name); if(!xpCSid) { hr = E_OUTOFMEMORY; break; } if(!xpCSid->IsValid()) { hr = E_FAIL; break; } hr = pToken->AddSid(xpCSid); if(FAILED(hr)) { break; } xpCSid.Acquire(); } NetApiBufferFree(pBuffer); return hr; } //****************************************************************************** // // Function: ExpandGroup // // Description: Expands a given group by expanding to include all the member subgroups etc.. // // Parameters: - pRsopToken: Rsop token // hAuthz: A pointer to the Authz resource manager // (that we are using to expand the grps) // pCSid: Sid of the group // // // Return: S_OK if successful. An HRESULT error code on failure. // //****************************************************************************** HRESULT ExpandGroup(CRsopToken *pRsopToken, AUTHZ_RESOURCE_MANAGER_HANDLE hAuthz, CSid *pCSid ) { AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzContext=0; LUID luid = {0}; HRESULT hrRet = S_OK; DWORD dwSize=0; XPtrLF xGrps; XPtrST xpCSid; if (!AuthziInitializeContextFromSid(0, pCSid->GetSidPtr(), hAuthz, NULL, luid, // we are not using it NULL, &hAuthzContext)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"ExpandGroup - AuthziInitializeContextFromSid failed. Error - %d", GetLastError()); hrRet = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } // // Now get the list of expanded sids // find the size first // if (!AuthzGetInformationFromContext(hAuthzContext, AuthzContextInfoGroupsSids, NULL, &dwSize, 0)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"ExpandGroup - AuthzGetInformationFromContext failed. Error - %d", GetLastError()); hrRet = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } } xGrps = (PTOKEN_GROUPS)LocalAlloc(LPTR, dwSize); if (!xGrps) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"ExpandGroup - Couldn't allocate memory for the token grps. Error - %d", GetLastError()); hrRet = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } if (!AuthzGetInformationFromContext(hAuthzContext, AuthzContextInfoGroupsSids, dwSize, &dwSize, xGrps)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - AuthzGetInformationFromContext(2) failed. Error - %d", GetLastError()); hrRet = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } for (DWORD i = 0; i < xGrps->GroupCount; i++) { xpCSid = new CSid(xGrps->Groups[i].Sid, NULL); if (!xpCSid) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - couldn't allocate memory(2). Error - %d", GetLastError()); hrRet = HRESULT_FROM_WIN32(GetLastError()); goto Exit; } hrRet = pRsopToken->AddSid(xpCSid); if(FAILED(hrRet)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - AddSid failed. Error - 0x%x", hrRet); goto Exit; } xpCSid.Acquire(); } hrRet = S_OK; Exit: if (hAuthzContext) AuthzFreeContext(hAuthzContext); return hrRet; } //****************************************************************************** // // Function: RsopCreateToken // // Description: Creates a pseudo-token using an exisitng user or machine account plus // the accounts of which that user is currently a member of. // The returned pseudo-token can be used subsequently in call // to other RSOP security functions to check access to // objects protected by security descriptors. // // Parameters: - szaccountName: Pointer to a user or machine account name. // - psaUserSecurityGroups: Pointer ta SAFEARRAY of BSTRs representing // security groups. // If NULL, then all the current security groups for the // szaccountName are added to the RsopToken. // If not NULL but pointing to an empty array, // only the szaccountName is added to the RsopToken. // - ppRsopToken: Address of a PRSOPTOKEN that receives the newly // created pseudo-token // // // Return: S_OK if successful. An HRESULT error code on failure. // // History: 8/7/99 leonardm Created. // //****************************************************************************** HRESULT RsopCreateToken(WCHAR* szAccountName, SAFEARRAY* psaUserSecurityGroups, PRSOPTOKEN* ppRsopToken ) { dbg.Initialize( L"Software\\Microsoft\\Windows NT\\CurrentVersion\\winlogon", L"ChkAccDebugLevel", L"ChkAcc.log", L"ChkAcc.bak"); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopCreateToken - Entering..."); HRESULT hr = E_FAIL; // // Instantiate a new CRsopToken // *ppRsopToken = NULL; XPtrSTxpRsopToken = new CRsopToken(); if(!xpRsopToken) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - Operator new returned NULL. Creation of a CRsopToken failed."); return E_OUTOFMEMORY; } // // Add to the new CRsopToken a CSid corresponding to the // principal represented by parameter szAccountName. For // dummy target the szAccountName will be null. // XPtrSTxpCSid; if ( szAccountName ) { xpCSid = new CSid(szAccountName); if(!xpCSid) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - Operator new returned NULL. Creation of a CSid failed."); return E_OUTOFMEMORY; } if(!xpCSid->IsValid()) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - Call to CSid::IsValid failed."); return E_FAIL; } hr = xpRsopToken->AddSid(xpCSid); if(FAILED(hr)) { return hr; } xpCSid.Acquire(); } // // If the parameter is NULL, that means we are to add the security // groups to which the user currently belongs. // if(!psaUserSecurityGroups) { if (szAccountName) { CWString sUser = szAccountName; CWString sUserName; CWString sUserDomain; CWString sUserDC; hr = GetUserInfo(sUser, sUserName, sUserDomain, sUserDC); if(FAILED(hr)) { return hr; } /* for cross domain cases this fails // // Get Global group membership // hr = AddGlobalGroups(sUserName, sUserDC, xpRsopToken); if(FAILED(hr)) { return hr; } */ hr = AddLocalGroups(sUserName, sUserDC, xpRsopToken); if(FAILED(hr)) { return hr; } // // Universal groups across domains cannot be retrieved by // NetUserGetGroups but can be fetched by authz functions. // xpCSid = new CSid(szAccountName); AUTHZ_RESOURCE_MANAGER_HANDLE hAuthz; if (!AuthzInitializeResourceManager(NULL, NULL, NULL, NULL, 0, &hAuthz)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - AuthzInitializeResourceManager failed. Error - %d", GetLastError()); return HRESULT_FROM_WIN32(GetLastError()); } hr = ExpandGroup(xpRsopToken, hAuthz, xpCSid); if (FAILED(hr)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - ExpandGrp failed for user. Error - 0x%x", hr); AuthzFreeResourceManager(hAuthz); return hr; } AuthzFreeResourceManager(hAuthz); // xpCSid will be freed automatically. } } // // Otherwise, we add only those groups named in the SAFERARRAY. // else { BSTR* pbstr; AUTHZ_RESOURCE_MANAGER_HANDLE hAuthz; if (!AuthzInitializeResourceManager(NULL, NULL, NULL, NULL, 0, &hAuthz)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - AuthzInitializeResourceManager failed. Error - %d", GetLastError()); return HRESULT_FROM_WIN32(GetLastError()); } // Get a pointer to the elements of the array. hr = SafeArrayAccessData(psaUserSecurityGroups, (void**)&pbstr); if(FAILED(hr)) { AuthzFreeResourceManager(hAuthz); return hr; } int count = psaUserSecurityGroups->rgsabound[0].cElements; for (int i = 0; i < count; i++) { xpCSid = new CSid(pbstr[i]); if(!xpCSid) { AuthzFreeResourceManager(hAuthz); SafeArrayUnaccessData(psaUserSecurityGroups); return E_OUTOFMEMORY; } if(!xpCSid->IsValid()) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - %s is invalid", pbstr[i]); AuthzFreeResourceManager(hAuthz); SafeArrayUnaccessData(psaUserSecurityGroups); return HRESULT_FROM_WIN32(ERROR_INVALID_ACCOUNT_NAME); } // WellKnownGroups cannot be expanded ... if ( xpCSid->SidType() != SidTypeWellKnownGroup ) { hr = ExpandGroup(xpRsopToken, hAuthz, xpCSid); if (FAILED(hr)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopCreateToken - ExpandGrp failed. Error - 0x%x", hr); AuthzFreeResourceManager(hAuthz); SafeArrayUnaccessData(psaUserSecurityGroups); return hr; } } hr = xpRsopToken->AddSid(xpCSid); if(FAILED(hr)) { AuthzFreeResourceManager(hAuthz); SafeArrayUnaccessData(psaUserSecurityGroups); return hr; } xpCSid.Acquire(); } AuthzFreeResourceManager(hAuthz); SafeArrayUnaccessData(psaUserSecurityGroups); } hr = AddSpecialGroups(xpRsopToken); if(FAILED(hr)) { return hr; } *ppRsopToken = xpRsopToken; xpRsopToken.Acquire(); return S_OK; } //****************************************************************************** // // Function: RsopDeleteToken // // Description: Destroys a pseudo-token previously created by any of the overloaded // forms of RSOPCreateRsopToken // // Parameters: - pRsopToken: Pointer to a valid PRSOPTOKEN // // Return: S_OK on success. An HRESULT error code on failure. // // History: 7/30/99 leonardm Created. // //****************************************************************************** HRESULT RsopDeleteToken(PRSOPTOKEN pRsopToken) { CRsopToken* pToken = static_cast(pRsopToken); delete pToken; return S_OK; } //****************************************************************************** // // Function: GetAceSid // // Description: // // Parameters: // // Return: // // History: 10/19/99 leonardm Created. // //****************************************************************************** PISID GetAceSid(PACE_HEADER pAceHeader) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - Entering."); // // Check for invalid argument // if(!pAceHeader) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"GetAceSid - Invalid parameter: pAceHeader is NULL"); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - Leaving."); return NULL; } PISID pSid = NULL; PACCESS_ALLOWED_ACE pACCESS_ALLOWED_ACE; PACCESS_ALLOWED_OBJECT_ACE pACCESS_ALLOWED_OBJECT_ACE; PACCESS_DENIED_ACE pACCESS_DENIED_ACE; PACCESS_DENIED_OBJECT_ACE pACCESS_DENIED_OBJECT_ACE; // // Cast the ACE header to the appropriate ACE type based on the 'Acetype' member of the // ACE header. // switch(pAceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - ACE type: ACCESS_ALLOWED_ACE_TYPE"); pACCESS_ALLOWED_ACE = reinterpret_cast(pAceHeader); pSid = reinterpret_cast(&(pACCESS_ALLOWED_ACE->SidStart)); break; case ACCESS_ALLOWED_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - ACE type: ACCESS_ALLOWED_OBJECT_ACE_TYPE"); pACCESS_ALLOWED_OBJECT_ACE = reinterpret_cast(pAceHeader); if( (pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) && (pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT)) { // // If both ACE_OBJECT_TYPE_PRESENT and ACE_INHERITED_OBJECT_TYPE_PRESENT are set in // the ACE flags, the SID starts at the offset specified by 'SidStart'. // pSid = reinterpret_cast(&(pACCESS_ALLOWED_OBJECT_ACE->SidStart)); } else if((pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) || (pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT)) { // // If either ACE_OBJECT_TYPE_PRESENT or ACE_INHERITED_OBJECT_TYPE_PRESENT is set in // the ACE flags, the SID starts at the offset specified by 'InheritedObjectType'. // pSid = reinterpret_cast(&(pACCESS_ALLOWED_OBJECT_ACE->InheritedObjectType)); } else { // // If neither ACE_OBJECT_TYPE_PRESENT nor ACE_INHERITED_OBJECT_TYPE_PRESENT is set in // the ACE flags, the SID starts at the offset specified by 'ObjectType'. // pSid = reinterpret_cast(&(pACCESS_ALLOWED_OBJECT_ACE->ObjectType)); } break; case ACCESS_DENIED_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - ACE type: ACCESS_DENIED_ACE_TYPE"); pACCESS_DENIED_ACE = reinterpret_cast(pAceHeader); pSid = reinterpret_cast(&(pACCESS_DENIED_ACE->SidStart)); break; case ACCESS_DENIED_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - ACE type: ACCESS_DENIED_OBJECT_ACE_TYPE"); pACCESS_DENIED_OBJECT_ACE = reinterpret_cast(pAceHeader); if( (pACCESS_DENIED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) && (pACCESS_DENIED_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT)) { // // If both ACE_OBJECT_TYPE_PRESENT and ACE_INHERITED_OBJECT_TYPE_PRESENT are set in // the ACE flags, the SID starts at the offset specified by 'SidStart'. // pSid = reinterpret_cast(&(pACCESS_DENIED_OBJECT_ACE->SidStart)); } else if((pACCESS_DENIED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) || (pACCESS_DENIED_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT)) { // // If either ACE_OBJECT_TYPE_PRESENT or ACE_INHERITED_OBJECT_TYPE_PRESENT is set in // the ACE flags, the SID starts at the offset specified by 'InheritedObjectType'. // pSid = reinterpret_cast(&(pACCESS_DENIED_OBJECT_ACE->InheritedObjectType)); } else { // // If neither ACE_OBJECT_TYPE_PRESENT nor ACE_INHERITED_OBJECT_TYPE_PRESENT is set in // the ACE flags, the SID starts at the offset specified by 'ObjectType'. // pSid = reinterpret_cast(&(pACCESS_DENIED_OBJECT_ACE->ObjectType)); } break; default: dbg.Msg(DEBUG_MESSAGE_WARNING, L"GetAceSid - Unexpected ACE type found. Type: 0x%08X", pAceHeader->AceType); break; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"GetAceSid - Leaving."); return pSid; } //****************************************************************************** // // Function: CheckAceApplies // // Description: // // Parameters: // // Return: S_OK on success. An HRESULT error code on failure. // // History: 8/8/99 leonardm Created. // //****************************************************************************** HRESULT CheckAceApplies(PACE_HEADER pAceHeader, PRSOPTOKEN pRsopToken, bool* pbAceApplies) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CheckAceApplies - Entering."); // // Get the SID from the ACE associated with this Ace Header. // PISID pSid = GetAceSid(pAceHeader); if(!pSid) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"CheckAceApplies - GetAceSid failed."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CheckAceApplies - Leaving."); return E_FAIL; } *pbAceApplies = false; // // Compare the SID from the ACE with all the SIDs in the RsopToken. // CRsopToken* pToken = static_cast(pRsopToken); for(CTLink* pLink = pToken->m_pSidsHead; pLink; pLink = pLink->m_pNext) { // // If one of the SIDs in the RsopToken matches // the SID in the ACE, this ACE applies. // if(EqualSid(pSid, pLink->m_pData->GetSidPtr())) { *pbAceApplies = true; dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CheckAceApplies - One of the SIDs in the RsopToken matches the SID in the ACE. The ACE applies."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CheckAceApplies - Leaving."); return S_OK; } } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CheckAceApplies - None of the SIDs in the RsopToken matches the SID in the ACE. The ACE does not apply."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"CheckAceApplies - Leaving."); return S_OK; } //****************************************************************************** const DWORD MAX_PERM_BITS=25; //****************************************************************************** enum EPermission{ PERMISSION_DENIED, PERMISSION_ALLOWED, PERMISSION_NOT_SET}; //****************************************************************************** // // Class: CSubObjectPerm // // Description: // // History: 11/09/99 leonardm Created. // //****************************************************************************** class CSubObjectPerm { private: CTLink* m_pGuidsHead; CTLink* m_pGuidsTail; EPermission permissionBits[MAX_PERM_BITS]; public: CSubObjectPerm(); ~CSubObjectPerm(); HRESULT AddGuid(GUID* pGuid); void ProcessAceMask(DWORD dwMask, EPermission permission, GUID* pGuid); DWORD GetAccumulatedPermissions(); bool AnyDenied(); }; //****************************************************************************** // // Function: CSubObjectPerm::CSubObjectPerm // // Description: // // Parameters: // // Return: // // History: 11/09/99 leonardm Created. // //****************************************************************************** CSubObjectPerm::CSubObjectPerm() : m_pGuidsHead(NULL), m_pGuidsTail(NULL) { for (int i = 0; i < MAX_PERM_BITS; i++) { permissionBits[i] = PERMISSION_NOT_SET; } } //****************************************************************************** // // Function: CSubObjectPerm::~CSubObjectPerm // // Description: // // Parameters: // // Return: // // History: 11/09/99 leonardm Created. // //****************************************************************************** CSubObjectPerm::~CSubObjectPerm() { CTLink* pLinkIterator = m_pGuidsHead; while(pLinkIterator) { CTLink* pLinkToDelete = pLinkIterator; pLinkIterator = pLinkIterator->m_pNext; delete pLinkToDelete; } } //****************************************************************************** // // Function: CSubObjectPerm::AddGuid // // Description: // // Parameters: // // Return: // // History: 11/09/99 leonardm Created. // //****************************************************************************** HRESULT CSubObjectPerm::AddGuid(GUID* pGuid) { CTLink* pLink = new CTLink(pGuid); if(!pLink) { return E_OUTOFMEMORY; } if(!m_pGuidsHead) { m_pGuidsHead = pLink; } else { m_pGuidsTail->m_pNext = pLink; } m_pGuidsTail = pLink; return S_OK; } //****************************************************************************** // // Function: CSubObjectPerm::ProcessAceMask // // Description: // // Parameters: // // Return: // // History: 11/09/99 leonardm Created. // //****************************************************************************** void CSubObjectPerm::ProcessAceMask(DWORD dwMask, EPermission permission, GUID* pGuid) { bool bAceApplies = false; if(!pGuid) { bAceApplies = true; } else if(pGuid && m_pGuidsHead) { CTLink* pLinkIterator = m_pGuidsHead; while(pLinkIterator) { if(*(pLinkIterator->m_pData) == *pGuid) { bAceApplies = true; break; } pLinkIterator = pLinkIterator->m_pNext; } } if(bAceApplies) { DWORD dwTemp = 0x00000001; for(int i = 0; i < MAX_PERM_BITS; i++) { if((dwMask & dwTemp) && (permissionBits[i] == PERMISSION_NOT_SET)) { permissionBits[i] = permission; } dwTemp <<= 1; } } } //****************************************************************************** // // Function: CSubObjectPerm::AccumulatedPermissions // // Description: // // Parameters: // // Return: // // History: 11/09/99 leonardm Created. // //****************************************************************************** DWORD CSubObjectPerm::GetAccumulatedPermissions() { DWORD dwAccumulatedPermissions = 0; for(int i = MAX_PERM_BITS - 1; i >= 0; i--) { dwAccumulatedPermissions <<= 1; if(permissionBits[i] == PERMISSION_ALLOWED) { dwAccumulatedPermissions |= 0x00000001; } } return dwAccumulatedPermissions; } //****************************************************************************** // // Function: CSubObjectPerm::AnyDenied // // Description: // // Parameters: // // Return: // // History: 11/09/99 leonardm Created. // //****************************************************************************** bool CSubObjectPerm::AnyDenied() { for(int i = 0; i < MAX_PERM_BITS; i++) { if(permissionBits[i] == PERMISSION_DENIED) { return true; } } return false; } //****************************************************************************** // // Struct: CDSObject // // Description: // // // History: 10/25/99 leonardm Created. // //****************************************************************************** struct CDSObject { DWORD m_dwLevel; GUID* m_pGuid; CDSObject() : m_pGuid(NULL){} ~CDSObject() { delete m_pGuid; } }; //****************************************************************************** // // Class: CAccumulatedPermissions // // Description: // // // History: 10/25/99 leonardm Created. // //****************************************************************************** class CAccumulatedPermissions { CTLink* m_pSubObjectsHead; CTLink* m_pSubObjectsTail; CTLink* m_pDSObjectsHead; CTLink* m_pDSObjectsTail; bool m_bInitialized; public: CAccumulatedPermissions(POBJECT_TYPE_LIST pObjectTypeList, DWORD ObjectTypeListLength); ~CAccumulatedPermissions(); void ProcessAceMask(DWORD dwMask, EPermission permission, GUID* pGuid); DWORD GetAccumulatedPermissions(); bool AnyDenied(); bool Initialized(){return m_bInitialized;} }; //****************************************************************************** // // Function: CAccumulatedPermissions::CAccumulatedPermissions // // Description: // // Parameters: // // Return: // // History: 10/25/99 leonardm Created. // //****************************************************************************** CAccumulatedPermissions::CAccumulatedPermissions( POBJECT_TYPE_LIST pObjectTypeList, DWORD ObjectTypeListLength) : m_pSubObjectsHead(NULL), m_pSubObjectsTail(NULL), m_bInitialized(false), m_pDSObjectsHead(NULL), m_pDSObjectsTail(NULL) { if(!pObjectTypeList || ObjectTypeListLength == 0) { XPtrSTxpSubObjectPerm = new CSubObjectPerm; if(!xpSubObjectPerm) { return; } m_pSubObjectsHead = new CTLink(xpSubObjectPerm); if(!m_pSubObjectsHead) { return; } xpSubObjectPerm.Acquire(); m_pSubObjectsTail = m_pSubObjectsHead; m_bInitialized = true; return; } DWORD dwCurrentLevel; for(DWORD i = 0; i < ObjectTypeListLength; i++) { if( i==0 ) { // // This assumes that the first element in the list of // OBJECT_TYPEs pointed to by pObjectTypeList is at // level ACCESS_OBJECT_GUID. // dwCurrentLevel = pObjectTypeList[i].Level; XPtrSTxpDSObject = new CDSObject; if(!xpDSObject) { return; } XPtrSTxpGuid = new GUID(*(pObjectTypeList[i].ObjectType)); if(!xpGuid) { return; } m_pDSObjectsHead = new CTLink(xpDSObject); if(!m_pDSObjectsHead) { return; } xpDSObject.Acquire(); m_pDSObjectsHead->m_pData->m_pGuid = xpGuid.Acquire(); m_pDSObjectsHead->m_pData->m_dwLevel = pObjectTypeList[i].Level; m_pDSObjectsTail = m_pDSObjectsHead; continue; } else if(pObjectTypeList[i].Level > dwCurrentLevel) { dwCurrentLevel = pObjectTypeList[i].Level; XPtrST xpDSObject = new CDSObject; if(!xpDSObject) { return; } XPtrSTxpGuid = new GUID(*(pObjectTypeList[i].ObjectType)); if(!xpGuid) { return; } CTLink* pDSObjectLink = new CTLink(xpDSObject); if(!pDSObjectLink) { return; } xpDSObject.Acquire(); pDSObjectLink->m_pData->m_pGuid = xpGuid.Acquire(); pDSObjectLink->m_pData->m_dwLevel = pObjectTypeList[i].Level; pDSObjectLink->m_pPrev = m_pDSObjectsTail; m_pDSObjectsTail->m_pNext = pDSObjectLink; m_pDSObjectsTail = pDSObjectLink; } else { XPtrSTxpSubObjectPerm = new CSubObjectPerm; if(!xpSubObjectPerm) { return; } CTLink* pSubObjectLink = new CTLink(xpSubObjectPerm); if(!pSubObjectLink) { return; } xpSubObjectPerm.Acquire(); CTLink* pLinkIterator = m_pDSObjectsHead; while(pLinkIterator) { XPtrSTxpGuid = new GUID(*(pLinkIterator->m_pData->m_pGuid)); if(!xpGuid) { delete pSubObjectLink; return; } if(FAILED(pSubObjectLink->m_pData->AddGuid(xpGuid))) { delete pSubObjectLink; return; } xpGuid.Acquire(); pLinkIterator = pLinkIterator->m_pNext; } if(!m_pSubObjectsHead) { m_pSubObjectsHead = pSubObjectLink; } else { m_pSubObjectsTail->m_pNext = pSubObjectLink; } m_pSubObjectsTail = pSubObjectLink; pLinkIterator = m_pDSObjectsTail; if(pLinkIterator) { while(pLinkIterator->m_pData->m_dwLevel >= pObjectTypeList[i].Level) { CTLink* pLinkToDelete = pLinkIterator; pLinkIterator = pLinkIterator->m_pPrev; delete pLinkToDelete; m_pDSObjectsTail = pLinkIterator; if(m_pDSObjectsTail) { m_pDSObjectsTail->m_pNext = NULL; } } } XPtrSTxpDSObject = new CDSObject; if(!xpDSObject) { return; } XPtrSTxpGuid = new GUID(*(pObjectTypeList[i].ObjectType)); if(!xpGuid) { return; } CTLink* pLink = new CTLink(xpDSObject); if(!pLink) { return; } xpDSObject.Acquire(); pLink->m_pData->m_pGuid = xpGuid.Acquire(); pLink->m_pData->m_dwLevel = pObjectTypeList[i].Level; pLink->m_pPrev = m_pDSObjectsTail; m_pDSObjectsTail->m_pNext = pLink; m_pDSObjectsTail = pLink; } } CTLink* pLinkIterator = m_pDSObjectsHead; if(pLinkIterator) { XPtrSTxpSubObject = new CSubObjectPerm; if(!xpSubObject) { return; } CTLink* pSubObjectLink = new CTLink(xpSubObject); if(!pSubObjectLink) { return; } xpSubObject.Acquire(); while(pLinkIterator) { XPtrSTxpGuid = new GUID(*(pLinkIterator->m_pData->m_pGuid)); if(!xpGuid) { delete pSubObjectLink; return; } if(FAILED(pSubObjectLink->m_pData->AddGuid(xpGuid))) { delete pSubObjectLink; return; } xpGuid.Acquire(); pLinkIterator = pLinkIterator->m_pNext; } if(!m_pSubObjectsHead) { m_pSubObjectsHead = pSubObjectLink; } else { m_pSubObjectsTail->m_pNext = pSubObjectLink; } m_pSubObjectsTail = pSubObjectLink; } pLinkIterator = m_pDSObjectsHead; while(pLinkIterator) { CTLink* pLinkToDelete = pLinkIterator; pLinkIterator = pLinkIterator->m_pNext; delete pLinkToDelete; } m_pDSObjectsHead = m_pDSObjectsTail = NULL; m_bInitialized = true; } //****************************************************************************** // // Function: CAccumulatedPermissions::~CAccumulatedPermissions // // Description: // // Parameters: // // Return: // // History: 10/25/99 leonardm Created. // //****************************************************************************** CAccumulatedPermissions::~CAccumulatedPermissions() { CTLink* pSubObjectLinkIterator = m_pSubObjectsHead; while(pSubObjectLinkIterator) { CTLink* pSubObjectLinkToDelete = pSubObjectLinkIterator; pSubObjectLinkIterator = pSubObjectLinkIterator->m_pNext; delete pSubObjectLinkToDelete; } CTLink* pDSObjectLinkIterator = m_pDSObjectsHead; while(pDSObjectLinkIterator) { CTLink* pDSObjectLinkToDelete = pDSObjectLinkIterator; pDSObjectLinkIterator = pDSObjectLinkIterator->m_pNext; delete pDSObjectLinkToDelete; } } //****************************************************************************** // // Function: CAccumulatedPermissions::ProcessAceMask // // Description: // // Parameters: // // Return: // // History: 8/8/99 leonardm Created. // //****************************************************************************** void CAccumulatedPermissions::ProcessAceMask(DWORD dwMask, EPermission permission, GUID* pGuid) { CTLink* pLinkIterator = m_pSubObjectsHead; while(pLinkIterator) { pLinkIterator->m_pData->ProcessAceMask(dwMask, permission, pGuid); pLinkIterator = pLinkIterator->m_pNext; } } //****************************************************************************** // // Function: CAccumulatedPermissions::AccumulatedPermissions // // Description: // // Parameters: // // Return: // // History: 8/8/99 leonardm Created. // //****************************************************************************** DWORD CAccumulatedPermissions::GetAccumulatedPermissions() { DWORD dwAccumulatedPermissions = 0x01FFFFFF; CTLink* pLinkIterator = m_pSubObjectsHead; while(pLinkIterator) { dwAccumulatedPermissions &= pLinkIterator->m_pData->GetAccumulatedPermissions(); pLinkIterator = pLinkIterator->m_pNext; } return dwAccumulatedPermissions; } //****************************************************************************** // // Function: CAccumulatedPermissions::AnyDenied // // Description: // // Parameters: // // Return: // // History: 8/8/99 leonardm Created. // //****************************************************************************** bool CAccumulatedPermissions::AnyDenied() { CTLink* pLinkIterator = m_pSubObjectsHead; while(pLinkIterator) { if(pLinkIterator->m_pData->AnyDenied()) { return true; } pLinkIterator = pLinkIterator->m_pNext; } return false; } //****************************************************************************** // // Function: LogGuid // // Description: // // Parameters: // // Return: void // // History: 10/26/99 leonardm Created. // //****************************************************************************** void LogGuid(GUID& guid) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogGuid - Entering."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogGuid - {0x%08x 0x%04x 0x%04x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogGuid - (%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x)", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogGuid - Leaving."); } //****************************************************************************** // // Function: LogSid // // Description: // // Parameters: // // Return: void // // History: 10/26/99 leonardm Created. // //****************************************************************************** void LogSid(PSID pSid) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Entering."); XPtrSTxpCSid = new CSid(pSid); if(!xpCSid) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"LogSid - Call to operator new failed."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Leaving."); return; } if(!(xpCSid->IsValid())) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"LogSid - call to CSid::IsValid returned false."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Leaving."); return; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid string: %s.", xpCSid->SidString()); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid User: %s.", xpCSid->User()); if(xpCSid->SidType() == SidTypeUser) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeUser."); } else if(xpCSid->SidType() == SidTypeGroup) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeGroup."); } else if(xpCSid->SidType() == SidTypeDomain) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeDomain."); } else if(xpCSid->SidType() == SidTypeAlias) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeAlias."); } else if(xpCSid->SidType() == SidTypeWellKnownGroup) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeWellKnownGroup."); } else if(xpCSid->SidType() == SidTypeDeletedAccount) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeDeletedAccount."); } else if(xpCSid->SidType() == SidTypeInvalid) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeInvalid."); } else if(xpCSid->SidType() == SidTypeUnknown) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeUnknown."); } else if(xpCSid->SidType() == SidTypeComputer) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid type: SidTypeComputer."); } else { dbg.Msg(DEBUG_MESSAGE_WARNING, L"LogSid - Sid type: UNKNOWN SID type."); } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid Domain: %s.", xpCSid->Domain()); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Sid Computer: %s.", xpCSid->Computer()); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogSid - Leaving."); } //****************************************************************************** // // Function: LogAce // // Description: // // Parameters: // // Return: void // // History: 10/26/99 leonardm Created. // //****************************************************************************** void LogAce(PACE_HEADER pAceHeader) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Entering."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - AceType = 0x%08X.", pAceHeader->AceType); PACCESS_ALLOWED_ACE pACCESS_ALLOWED_ACE = NULL; PACCESS_ALLOWED_OBJECT_ACE pACCESS_ALLOWED_OBJECT_ACE = NULL; PACCESS_DENIED_ACE pACCESS_DENIED_ACE = NULL; PACCESS_DENIED_OBJECT_ACE pACCESS_DENIED_OBJECT_ACE = NULL; PSYSTEM_AUDIT_ACE pSYSTEM_AUDIT_ACE = NULL; PSYSTEM_AUDIT_OBJECT_ACE pSYSTEM_AUDIT_OBJECT_ACE = NULL; PSYSTEM_ALARM_ACE pSYSTEM_ALARM_ACE = NULL; PSYSTEM_ALARM_OBJECT_ACE pSYSTEM_ALARM_OBJECT_ACE = NULL; switch(pAceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: ACCESS_ALLOWED_ACE_TYPE"); pACCESS_ALLOWED_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pACCESS_ALLOWED_ACE->Mask); break; case ACCESS_ALLOWED_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: ACCESS_ALLOWED_OBJECT_ACE_TYPE"); pACCESS_ALLOWED_OBJECT_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pACCESS_ALLOWED_OBJECT_ACE->Mask); if(pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_OBJECT_TYPE_PRESENT."); LogGuid(pACCESS_ALLOWED_OBJECT_ACE->ObjectType); } if(pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_INHERITED_OBJECT_TYPE_PRESENT."); LogGuid(pACCESS_ALLOWED_OBJECT_ACE->InheritedObjectType); } break; case ACCESS_DENIED_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: ACCESS_DENIED_ACE_TYPE"); pACCESS_DENIED_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pACCESS_DENIED_ACE->Mask); break; case ACCESS_DENIED_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: ACCESS_DENIED_OBJECT_ACE_TYPE"); pACCESS_DENIED_OBJECT_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pACCESS_DENIED_OBJECT_ACE->Mask); if(pACCESS_DENIED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_OBJECT_TYPE_PRESENT."); LogGuid(pACCESS_DENIED_OBJECT_ACE->ObjectType); } if(pACCESS_DENIED_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_INHERITED_OBJECT_TYPE_PRESENT."); LogGuid(pACCESS_DENIED_OBJECT_ACE->InheritedObjectType); } break; case SYSTEM_AUDIT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: SYSTEM_AUDIT_ACE_TYPE"); pSYSTEM_AUDIT_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pSYSTEM_AUDIT_ACE->Mask); break; case SYSTEM_AUDIT_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: SYSTEM_AUDIT_OBJECT_ACE_TYPE"); pSYSTEM_AUDIT_OBJECT_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pSYSTEM_AUDIT_OBJECT_ACE->Mask); if(pSYSTEM_AUDIT_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_OBJECT_TYPE_PRESENT."); LogGuid(pSYSTEM_AUDIT_OBJECT_ACE->ObjectType); } if(pSYSTEM_AUDIT_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_INHERITED_OBJECT_TYPE_PRESENT."); LogGuid(pSYSTEM_AUDIT_OBJECT_ACE->InheritedObjectType); } break; case SYSTEM_ALARM_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: SYSTEM_ALARM_ACE_TYPE"); pSYSTEM_ALARM_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pSYSTEM_ALARM_ACE->Mask); break; case SYSTEM_ALARM_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE type: SYSTEM_ALARM_OBJECT_ACE_TYPE"); pSYSTEM_ALARM_OBJECT_ACE = reinterpret_cast(pAceHeader); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - ACE Mask: 0x%08X", pSYSTEM_ALARM_OBJECT_ACE->Mask); if(pSYSTEM_ALARM_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_OBJECT_TYPE_PRESENT."); LogGuid(pSYSTEM_ALARM_OBJECT_ACE->ObjectType); } if(pSYSTEM_ALARM_OBJECT_ACE->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Flag is on: ACE_INHERITED_OBJECT_TYPE_PRESENT."); LogGuid(pSYSTEM_ALARM_OBJECT_ACE->InheritedObjectType); } break; default: dbg.Msg(DEBUG_MESSAGE_WARNING, L"LogAce - ACE type: UNKNOWN ACE type."); break; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - AceFlags = 0x%08X.", pAceHeader->AceFlags); if(pAceHeader->AceFlags & OBJECT_INHERIT_ACE) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: OBJECT_INHERIT_ACE."); } if(pAceHeader->AceFlags & CONTAINER_INHERIT_ACE) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: CONTAINER_INHERIT_ACE."); } if(pAceHeader->AceFlags & NO_PROPAGATE_INHERIT_ACE) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: NO_PROPAGATE_INHERIT_ACE."); } if(pAceHeader->AceFlags & INHERIT_ONLY_ACE) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: INHERIT_ONLY_ACE."); } if(pAceHeader->AceFlags & INHERITED_ACE) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: INHERITED_ACE."); } if(pAceHeader->AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: SUCCESSFUL_ACCESS_ACE_FLAG."); } if(pAceHeader->AceFlags & FAILED_ACCESS_ACE_FLAG) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - The Following ACE header flag is on: FAILED_ACCESS_ACE_FLAG."); } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - AceSize = 0x%08X.", pAceHeader->AceSize); PISID pSid = GetAceSid(pAceHeader); if(!pSid) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"LogAce - Call to GetAceSid failed."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Leaving."); return; } LogSid(pSid); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"LogAce - Leaving."); } //****************************************************************************** // // Function: ProcessAce // // Description: // // Parameters: // // Return: S_OK on success. An HRESULT error code on failure. // // History: 8/8/99 leonardm Created. // //****************************************************************************** HRESULT ProcessAce( PACE_HEADER pAceHeader, PRSOPTOKEN pRsopToken, POBJECT_TYPE_LIST pObjectTypeList, DWORD ObjectTypeListLength, DWORD dwDesiredAccessMask, CAccumulatedPermissions& accumulatedPermissions, bool* pbAccessExplicitlyDenied) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Entering."); // // Check parameters. // if( !pAceHeader || !pRsopToken || (ObjectTypeListLength && !pObjectTypeList) || !pbAccessExplicitlyDenied) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"ProcessAce - Invalid argument(s)."); return E_FAIL; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Desired Access Mask: 0x%08X.", dwDesiredAccessMask); if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Desired Access Mask == MAXIMUM_ALLOWED."); } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Accumulated Permissions BEFORE Processing Ace: 0x%08X.", accumulatedPermissions.GetAccumulatedPermissions()); // // Log ACE information // LogAce(pAceHeader); *pbAccessExplicitlyDenied = false; // // ACEs with INHERIT_ONLY_ACE flag do no control access to the current object. // Therefore, we ignore them. // if(pAceHeader->AceFlags & INHERIT_ONLY_ACE) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Found ACE with INHERIT_ONLY_ACE flag. Ace does not apply."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Leaving."); return S_OK; } // // If this ACE does not reference any of the SIDs contained in the RsopToken, // we ignore it. // bool bAceApplies; HRESULT hr = CheckAceApplies(pAceHeader, pRsopToken, &bAceApplies); if(FAILED(hr)) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"ProcessAce - CheckAceApplies failed. Return code: 0x%08X", hr); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Leaving."); return hr; } if(!bAceApplies) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - ACE does not apply."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Leaving."); return S_OK; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - ACE aplies."); PACCESS_ALLOWED_ACE pACCESS_ALLOWED_ACE; PACCESS_ALLOWED_OBJECT_ACE pACCESS_ALLOWED_OBJECT_ACE; PACCESS_DENIED_ACE pACCESS_DENIED_ACE; PACCESS_DENIED_OBJECT_ACE pACCESS_DENIED_OBJECT_ACE; DWORD i; DWORD dwMask; switch(pAceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - ACE type: ACCESS_ALLOWED_ACE_TYPE"); pACCESS_ALLOWED_ACE = reinterpret_cast(pAceHeader); // // If the requested access is MAXIMUM_ALLOWED, consider all bits in the Mask // controlled by this ACE. // Otherwise, consider only those bits that are also specified in the // desired access mask. // if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { dwMask = pACCESS_ALLOWED_ACE->Mask; } else { dwMask = dwDesiredAccessMask & pACCESS_ALLOWED_ACE->Mask; } accumulatedPermissions.ProcessAceMask( dwMask, PERMISSION_ALLOWED, NULL); break; case ACCESS_ALLOWED_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - ACE type: ACCESS_ALLOWED_OBJECT_ACE_TYPE"); pACCESS_ALLOWED_OBJECT_ACE = reinterpret_cast(pAceHeader); // // We have chosen to process only those object ACEs that have // the flag ACE_OBJECT_TYPE_PRESENT set. // if(pACCESS_ALLOWED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) { // // Notice that if this function is invoked with no // ObjectTypeList, object ACEs are disregarded. // for(i = 0; i < ObjectTypeListLength; i++) { POBJECT_TYPE_LIST pObjectType = &(pObjectTypeList[i]); if(*(pObjectType->ObjectType) == pACCESS_ALLOWED_OBJECT_ACE->ObjectType) { // // If the requested access is MAXIMUM_ALLOWED, consider all bits in the Mask // controlled by this ACE. // Otherwise, consider only those bits that are also specified in the // desired access mask. // if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { dwMask = pACCESS_ALLOWED_OBJECT_ACE->Mask; } else { dwMask = dwDesiredAccessMask & pACCESS_ALLOWED_OBJECT_ACE->Mask; } accumulatedPermissions.ProcessAceMask( dwMask, PERMISSION_ALLOWED, &(pACCESS_ALLOWED_OBJECT_ACE->ObjectType)); break; } } } break; case ACCESS_DENIED_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - ACE type: ACCESS_DENIED_ACE_TYPE"); pACCESS_DENIED_ACE = reinterpret_cast(pAceHeader); // // If the requested access is MAXIMUM_ALLOWED, consider all bits in the Mask // controlled by this ACE. // Otherwise, consider only those bits that are also specified in the // desired access mask. // if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { dwMask = pACCESS_DENIED_ACE->Mask; } else { dwMask = dwDesiredAccessMask & pACCESS_DENIED_ACE->Mask; } accumulatedPermissions.ProcessAceMask( dwMask, PERMISSION_DENIED, NULL); if(dwDesiredAccessMask != MAXIMUM_ALLOWED) { if(accumulatedPermissions.AnyDenied()) { *pbAccessExplicitlyDenied = true; } } break; case ACCESS_DENIED_OBJECT_ACE_TYPE: dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - ACE type: ACCESS_DENIED_OBJECT_ACE_TYPE"); pACCESS_DENIED_OBJECT_ACE = reinterpret_cast(pAceHeader); // // We have chosen to process only those object ACEs that have // the flag ACE_OBJECT_TYPE_PRESENT set. // if(pACCESS_DENIED_OBJECT_ACE->Flags & ACE_OBJECT_TYPE_PRESENT) { // // Notice that if this function is invoked with no // ObjectTypeList, object ACEs are disregarded. // for(i = 0; i < ObjectTypeListLength; i++) { POBJECT_TYPE_LIST pObjectType = &(pObjectTypeList[i]); if(*(pObjectType->ObjectType) == pACCESS_DENIED_OBJECT_ACE->ObjectType) { // // If the requested access is MAXIMUM_ALLOWED, consider all bits in the Mask // controlled by this ACE. // Otherwise, consider only those bits that are also specified in the // desired access mask. // if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { dwMask = pACCESS_DENIED_OBJECT_ACE->Mask; } else { dwMask = dwDesiredAccessMask & pACCESS_DENIED_OBJECT_ACE->Mask; } accumulatedPermissions.ProcessAceMask( dwMask, PERMISSION_DENIED, &(pACCESS_DENIED_OBJECT_ACE->ObjectType)); if(dwDesiredAccessMask != MAXIMUM_ALLOWED) { if(accumulatedPermissions.AnyDenied()) { *pbAccessExplicitlyDenied = true; } } break; } } } break; default: dbg.Msg(DEBUG_MESSAGE_WARNING, L"ProcessAce - Unexpected ACE type found in ACE header. Type: 0x%08x", pAceHeader->AceType); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Leaving."); return E_FAIL; break; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Accumulated Permissions AFTER Processing Ace: 0x%08X.", accumulatedPermissions.GetAccumulatedPermissions()); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"ProcessAce - Leaving."); return S_OK; } //****************************************************************************** // // Function: RsopAccessCheckByType // // Description: // // Parameters: - pSecurityDescriptor, // - pPrincipalSelfSid: // - pRsopToken: // - dwDesiredAccessMask: // - pObjectTypeList: // - ObjectTypeListLength: // - pGenericMapping: // - pPrivilegeSet: // - pdwPrivilegeSetLength: // - pdwGrantedAccessMask: // - pbAccessStatus: // // Return: S_OK on success. An HRESULT error code on failure. // // History: 7/30/99 leonardm Created. // //****************************************************************************** HRESULT RsopAccessCheckByType( PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pPrincipalSelfSid, PRSOPTOKEN pRsopToken, DWORD dwDesiredAccessMask, POBJECT_TYPE_LIST pObjectTypeList, DWORD ObjectTypeListLength, PGENERIC_MAPPING pGenericMapping, PPRIVILEGE_SET pPrivilegeSet, LPDWORD pdwPrivilegeSetLength, LPDWORD pdwGrantedAccessMask, LPBOOL pbAccessStatus) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Entering."); // // Check arguments. // if( !pSecurityDescriptor | !IsValidSecurityDescriptor(pSecurityDescriptor) | !pRsopToken | !pGenericMapping | !pdwGrantedAccessMask | !pbAccessStatus) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopAccessCheckByType - Function invoked with invalid arguments."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Leaving."); return E_INVALIDARG; } // // Get the DACL from the Security Descriptor // BOOL bDaclPresent; PACL pDacl; BOOL bDaclDefaulted; if(!GetSecurityDescriptorDacl(pSecurityDescriptor, &bDaclPresent, &pDacl, &bDaclDefaulted)) { DWORD dwLastError = GetLastError(); dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopAccessCheckByType - GetSecurityDescriptorDacl failed. GetLastError=0x%08X", dwLastError); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Leaving."); return E_FAIL; } // // Map generic rights specified in dwDesiredAccessMask to standard // and specific rights. // This is necessary because the ACEs in the DACL specify standard // and specific rights only. // if(dwDesiredAccessMask != MAXIMUM_ALLOWED) { MapGenericMask(&dwDesiredAccessMask, pGenericMapping); } // // If no DACL is present (as indicated by bDaclPresent) in the security descriptor, // or if it present (as indicated by bDaclPresent) but it is a NULL DACL // the object implicitly grants all access. // if(!bDaclPresent || pDacl == NULL) { if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { *pdwGrantedAccessMask = pGenericMapping->GenericAll | pGenericMapping->GenericExecute | pGenericMapping->GenericRead | pGenericMapping->GenericWrite; } else { *pdwGrantedAccessMask = dwDesiredAccessMask; } *pbAccessStatus = TRUE; dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - No DACL present. All access is granted."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Leaving."); return S_OK; } DWORD dwAceCount = pDacl->AceCount; // // If the DACL is present but it is empty, // the object implicitly denies access to everyone. // if(!dwAceCount) { *pdwGrantedAccessMask = 0; *pbAccessStatus = FALSE; dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - The DACL is present but it is empty. All access is denied."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Leaving."); return S_OK; } // // At this point we have an array of ACES structures // // If the desired access is different from MAXIMUM_ALLOWED, // inspect them until one of the following happens: // // 1. An ACE is found which explicitly denies one of the requested // access rights. In this case checking stops immediately and // access is (explicitly) denied. // // 2. All the requested accesses are explicitly granted by one or // more ACEs. In this case checking stops immediately and // access is (explicitly) allowed. // // // 3. All the ACEs have been inspected inspected and there is at least // one requested access right that has not been explicitly allowed. // In this case, access is (implicitly) denied. // // If the desired access is MAXIMUM_ALLOWED, inspect all the ACEs. // PISID pSid; BYTE* pByte; CAccumulatedPermissions accumulatedPermissions( pObjectTypeList, ObjectTypeListLength); if(!accumulatedPermissions.Initialized()) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopAccessCheckByType - CAccumulatedPermissions failed to initialize."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Leaving."); return E_FAIL; } *pdwGrantedAccessMask = 0; *pbAccessStatus = FALSE; // // Log SID information contained in the RsopToken. // CRsopToken* pToken = static_cast(pRsopToken); for(CTLink* pLink = pToken->m_pSidsHead; pLink; pLink = pLink->m_pNext) { LogSid(pLink->m_pData->GetSidPtr()); } // // The first ACE immediately follows the DACL structure. We don't know up front // the type of ACE so we get the ACE header which has a format common to all // ACE types. // PACE_HEADER pAceHeader = reinterpret_cast(pDacl+1); for(DWORD i=0; i(reinterpret_cast(pAceHeader) + pAceHeader->AceSize); } if(dwDesiredAccessMask == MAXIMUM_ALLOWED) { *pdwGrantedAccessMask = accumulatedPermissions.GetAccumulatedPermissions(); *pbAccessStatus = *pdwGrantedAccessMask ? TRUE : FALSE; } dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopAccessCheckByType - Leaving."); return S_OK; } //****************************************************************************** // // Function: RsopFileAccessCheck // // Description: Determines whether the security descriptor pointed to by pSecurityDescriptor // grants the set of file access rights specified in dwDesiredAccessMask // to the client identified by the RSOPTOKEN pointed to by pRsopToken. // // Parameters: - pszFileName: Pointer to an existing filename. // - pRsopToken: Pointer to a valid RSOPTOKEN against which access // is to be checked. // - dwDesiredAccessMask: Mask of requested generic and/or standard and/or specific access rights, // - pdwGrantedAccessMask: On success, if pbAccessStatus is true, it contains // the mask of standard and specific rights granted. // If pbAccessStatus is false, it is set to 0. // On failure, it is not modified. // - pbAccessStatus: On success, indicates wether the requested set // of access rights was granted. // On failure, it is not modified // // Return: S_OK on success. An HRESULT error code on failure. // // History: 7/30/99 leonardm Created. // //****************************************************************************** HRESULT RsopFileAccessCheck(LPTSTR pszFileName, PRSOPTOKEN pRsopToken, DWORD dwDesiredAccessMask, LPDWORD pdwGrantedAccessMask, LPBOOL pbAccessStatus) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopFileAccessCheck - Entering."); // // Check for invalid arguments. // if( !pszFileName |!pRsopToken | !pdwGrantedAccessMask | !pbAccessStatus) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopFileAccessCheck - Function called with invalid parameters."); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopFileAccessCheck - Leaving."); return E_INVALIDARG; } // // Attempt to get a handle with READ_CONTROL access right that can be used to // read the security descriptor. // XHandle hFile = CreateFile( pszFileName, READ_CONTROL, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(hFile == INVALID_HANDLE_VALUE) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopFileAccessCheck - Call to CreateFile failed. Filename: %s. Last error: 0x%08X", pszFileName, GetLastError()); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopFileAccessCheck - Leaving."); return E_FAIL; } // // Use the handle to get the security descriptor with only the DACL in it. // PACL pDacl; XPtrLFxpSecurityDescriptor = NULL; DWORD status = GetSecurityInfo( hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, reinterpret_cast(&xpSecurityDescriptor)); if(status != ERROR_SUCCESS) { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopFileAccessCheck - Call to GetSecurityInfo failed. Return: 0x%08X", status); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopFileAccessCheck - Leaving."); return E_FAIL; } // // This will be used by RSOPAccessCheckByType to map generic rights specified in // dwDesiredAccessMask to standard and specific rights. // GENERIC_MAPPING FileGenericMapping; FileGenericMapping.GenericRead = FILE_GENERIC_READ; FileGenericMapping.GenericWrite = FILE_GENERIC_WRITE; FileGenericMapping.GenericExecute = FILE_GENERIC_EXECUTE; FileGenericMapping.GenericAll = FILE_ALL_ACCESS; // // Call RsopAccessCheckByType to do the actual checking. // HRESULT hr = RsopAccessCheckByType( xpSecurityDescriptor, NULL, pRsopToken, dwDesiredAccessMask, NULL, 0, &FileGenericMapping, NULL, 0, pdwGrantedAccessMask, pbAccessStatus); if(SUCCEEDED(hr)) { dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopFileAccessCheck - Leaving successfully."); } else { dbg.Msg(DEBUG_MESSAGE_WARNING, L"RsopFileAccessCheck - Call to RsopAccessCheckByType failed. Return: 0x%08X", hr); dbg.Msg(DEBUG_MESSAGE_VERBOSE, L"RsopFileAccessCheck - Leaving."); } return hr; } //****************************************************************************** // // Function: RsopSidsFromToken // // Description: Returns all the sids in the token // // Parameters: pRsopToken -- an rsop token from which to obtain sids // ppGroups -- a pointer to the address of a TOKEN_GROUPS structure // that will be allocated by this function and will contain // references to the sids. The caller should free this // pointer with LocalFree -- this will also free all memory // referenced by the structure. // // Return: S_OK on success. An HRESULT error code on failure. // // //****************************************************************************** HRESULT RsopSidsFromToken(PRSOPTOKEN pRsopToken, PTOKEN_GROUPS* ppGroups) { HRESULT hr; CRsopToken* pToken; // // Initializations // hr = S_OK; *ppGroups = NULL; pToken = (CRsopToken*) pRsopToken; // // First, determine the number of groups and the size // needed for each sid // CTLink* pCurrent; DWORD cbSize; DWORD cGroups; cbSize = 0; cGroups = 0; // // Iterate through each sid, adding its size to the total // needed to store the sids // for ( pCurrent = pToken->m_pSidsHead; pCurrent; pCurrent = pCurrent->m_pNext) { cbSize += RtlLengthSid(pCurrent->m_pData->GetSidPtr()); cGroups++; } // // Add in the size of the fixed portion of the return structure. // Note that the fixed portion of the structure already has // space for one group, so we exclude that group from the amount // neeeded to allocate if we are allocating at least one group // cbSize += sizeof(TOKEN_GROUPS) + (sizeof(SID_AND_ATTRIBUTES) * (cGroups - (cGroups ? 1 : 0))); // // Now allocate space for the groups // *ppGroups = (PTOKEN_GROUPS) LocalAlloc( LPTR, cbSize ); if ( !*ppGroups ) { return E_OUTOFMEMORY; } // // Set the count member of the structure // (*ppGroups)->GroupCount = cGroups; // // If there are groups, copy the sids // if ( 0 != cGroups ) { PSID pCurrentSid; PSID_AND_ATTRIBUTES pCurrentGroup; // // Set the current sid to an offset past the // array of SID_AND_ATTRIBUTE structures that // represents each group // pCurrentSid = &((*ppGroups)->Groups[cGroups]); // // Set the current group to the first SID_AND_ATTRIBUTE structure // pCurrentGroup = (PSID_AND_ATTRIBUTES) &((*ppGroups)->Groups); // // We have no information in the rsop token regarding // the attributes, so we clear this member // pCurrentGroup->Attributes = 0; // // Iterate through each group and copy it // for (pCurrent = pToken->m_pSidsHead; pCurrent; pCurrent = pCurrent->m_pNext) { DWORD cbSid; NTSTATUS Status; // // Determine the length of the source sid // cbSid = RtlLengthSid(pCurrent->m_pData->GetSidPtr()); // // Copy the source sid to the current sid // Status = RtlCopySid( cbSid, pCurrentSid, pCurrent->m_pData->GetSidPtr()); // // Check for errors // if (STATUS_SUCCESS != Status) { hr = HRESULT_FROM_WIN32(Status); break; } // // Set the current group's sid reference to the // current sid // pCurrentGroup->Sid = pCurrentSid; // // Move our current sid offset forward by the length of // the current sid. Move our group reference forward as well. // pCurrentSid = (PSID) (((BYTE*) pCurrentSid) + cbSid); pCurrentGroup++; } } // // Free any memory on failure and remove // any reference to it // if (FAILED(hr)) { LocalFree(*ppGroups); *ppGroups = NULL; } return hr; }