/*****************************************************************************/ /* Copyright (c) 1999-2001 Microsoft Corporation, All Rights Reserved / /*****************************************************************************/ //================================================================= // // AccessRights.CPP -- Base class for obtaining effective access // rights. // // Copyright (c) 1999-2001 Microsoft Corporation, All Rights Reserved // // Revisions: 6/11/99 a-kevhu Created // //================================================================= #include "precomp.h" #ifdef NTONLY #include #include "AdvApi32Api.h" #include "accctrl.h" #include "sid.h" #include "AccessEntryList.h" #include "AccessRights.h" //============================================================================== // CONSTRUCTORS AND DESTRUCTORS //============================================================================== // Default initialization... CAccessRights::CAccessRights(bool fUseCurThrTok /* = false */) : m_dwError(ERROR_SUCCESS) { if(fUseCurThrTok) { // Initialize using the current thread token... InitTrustee(true); } } // Initialization specifying user only. Sid domain/account // not resolved. ACL uninitialized. CAccessRights::CAccessRights(const USER user, USER_SPECIFIER usp) : m_dwError(ERROR_SUCCESS) { if(usp == USER_IS_PSID) { m_csid = CSid((PSID)user, NULL, false); InitTrustee(false); } else if(usp == USER_IS_HANDLE) { ASSERT_BREAK(user != NULL); InitTrustee(false, (HANDLE)user); } } // Initialization of user and acl. Sid domain/account // not resolved. ACL initialized. CAccessRights::CAccessRights(const USER user, const PACL pacl, USER_SPECIFIER usp) : m_ael(pacl, false), m_dwError(ERROR_SUCCESS) { if(usp == USER_IS_PSID) { m_csid = CSid((PSID)user, NULL, false); InitTrustee(false); } else if(usp == USER_IS_HANDLE) { ASSERT_BREAK(user != NULL); InitTrustee(false, (HANDLE)user); } } // Initialization of acl only. ACL Sids not resolved. CAccessRights::CAccessRights(const PACL pacl, bool fUseCurThrTok /* = false */) : m_ael(pacl, false), m_dwError(ERROR_SUCCESS) { if(fUseCurThrTok) { // Initialize using the current thread token... InitTrustee(true); } } // Copy constructor /* Not complete yet CAccessRights::CAccessRights(const CAccessRights &RAccessRights) { // Copy members. We may or may not have either. if(RAccessRights.m_csid.IsValid() && RAccessRights.m_csid.IsOK()) { m_csid = RAccessRights.m_csid; } m_ael.Clear(); if(!RAccessRights.m_ael.IsEmpty()) { // The best way to do this, to guarentee that the sids are not // resolved into domain/name, is to gat a PACL, then reinitialize // ourselves from it. PACL paclNew = NULL; try { if(RAccessRights.FillEmptyPACL(paclNew)) { if(paclNew != NULL) { if(!m_ael.InitFromWin32ACL(paclNew, ALL_ACE_TYPES, false)) { // If something went wrong, clean // up after ourselves. m_ael.Clear(); } delete paclNew; paclNew = NULL; } } } catch(...) { if(paclNew != NULL) { delete paclNew; paclNew = NULL; } throw; } } } */ // Destructor - members destruct themselves. CAccessRights::~CAccessRights() { } //============================================================================== // UTILITY FUNCTIONS //============================================================================== AR_RET_CODE CAccessRights::GetEffectiveAccessRights(PACCESS_MASK pAccessMask) { DWORD dwRet = AR_GENERIC_FAILURE; CAdvApi32Api *pAdvApi32 = NULL; PACL pacl = NULL; try { pAdvApi32 = (CAdvApi32Api*) CResourceManager::sm_TheResourceManager.GetResource(g_guidAdvApi32Api, NULL); if(pAdvApi32 != NULL) { if((dwRet = FillEmptyPACL(&pacl)) == ERROR_SUCCESS) { ASSERT_BREAK(pacl != NULL); if(pacl != NULL) { if(m_csid.IsValid() && m_csid.IsOK()) { pAdvApi32->GetEffectiveRightsFromAclW(pacl, &m_trustee, pAccessMask, &dwRet); } else { dwRet = AR_BAD_SID; } delete pacl; pacl = NULL; } } CResourceManager::sm_TheResourceManager.ReleaseResource(g_guidAdvApi32Api, pAdvApi32); pAdvApi32 = NULL; } } catch(...) { if(pacl != NULL) { delete pacl; pacl = NULL; } if(pAdvApi32 != NULL) { CResourceManager::sm_TheResourceManager.ReleaseResource(g_guidAdvApi32Api, pAdvApi32); pAdvApi32 = NULL; } throw; } return dwRet; } bool CAccessRights::InitTrustee(bool fInitFromCurrentThread, const HANDLE hToken) { bool fRet = false; // The main thing done here is a sid is obtained and the TRUSTEE struct // filled in. if(fInitFromCurrentThread) { // Get the sid of the user/group of the current thread... SmartCloseHandle hThreadToken; if(::OpenThreadToken(::GetCurrentThread(), TOKEN_READ, FALSE, &hThreadToken)) { InitSidFromToken(hThreadToken); } } else { // If we were given a hToken, use it instead... if(hToken != NULL) { InitSidFromToken(hToken); } } // We should now have a valid sid in our member CSid (either from the // InitSidFromToken calls or from construction). // Now we need to initialize the TRUSTEE object. Check again that our sid // is in good standing... if(m_csid.IsValid() && m_csid.IsOK()) { m_trustee.pMultipleTrustee = NULL; m_trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; m_trustee.TrusteeForm = TRUSTEE_IS_SID; m_trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; // we could be operating // on behalf of a user, // group, well-known-group, // who knows. m_trustee.ptstrName = (LPWSTR)m_csid.GetPSid(); fRet = true; } else { m_dwError = AR_BAD_SID; } return fRet; } bool CAccessRights::InitSidFromToken(const HANDLE hThreadToken) { bool fRet = false; if(hThreadToken != NULL) { DWORD dwLength = 0L; DWORD dwReqLength = 0L; PSID psid = NULL; LPVOID pBuff = NULL; if(!::GetTokenInformation(hThreadToken, TokenUser, NULL, 0, &dwReqLength)) { if(::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // Allocate a buffer to hold the token info... try { pBuff = new BYTE[dwReqLength]; if(pBuff != NULL) { dwLength = dwReqLength; // Now that we have the right size buffer, call again... if(::GetTokenInformation(hThreadToken, TokenUser, pBuff, dwLength, &dwReqLength)) { if(pBuff != NULL) { TOKEN_USER *pTokUsr = (TOKEN_USER*)pBuff; psid = pTokUsr->User.Sid; ASSERT_BREAK((psid != NULL) && ::IsValidSid(psid)); if((psid != NULL) && ::IsValidSid(psid)) { m_csid = CSid(psid, NULL, false); fRet = true; } else { m_dwError = AR_BAD_SID; } } } delete pBuff; pBuff = NULL; } } catch(...) { if(pBuff != NULL) { delete pBuff; pBuff = NULL; } throw; } } } } return fRet; } // Resets the user to the user to whom the current thread token belongs. bool CAccessRights::SetUserToThisThread() { return InitTrustee(true, NULL); } // Resets the user to the user specified by psid or handle bool CAccessRights::SetUser(const USER user, USER_SPECIFIER usp) { bool fRet = false; if(usp == USER_IS_PSID) { CSid csidTemp((PSID)user); if(csidTemp.IsValid() && csidTemp.IsOK()) { m_csid = csidTemp; fRet = true; } } else if(usp == USER_IS_HANDLE) { fRet = InitSidFromToken((HANDLE)user); } return fRet; } // Resets the acl to the passed in PACL bool CAccessRights::SetAcl(const PACL pacl) { bool fRet = false; if(pacl != NULL) { m_ael.Clear(); if(m_ael.InitFromWin32ACL(pacl, ALL_ACE_TYPES, false) == ERROR_SUCCESS) { fRet = true; } } return fRet; } // Gets us a filled out PACL, which must be freed by the caller, using delete. AR_RET_CODE CAccessRights::FillEmptyPACL(PACL *paclOut) { DWORD dwRet = AR_GENERIC_FAILURE; if(paclOut != NULL) { // The best way to do this, to guarentee that the sids are not // resolved into domain/name, is to get a PACL, then reinitialize // ourselves from it. DWORD dwAclSize = 0L; if(m_ael.NumEntries() > 0) { if(m_ael.CalculateWin32ACLSize(&dwAclSize)) { if(dwAclSize > sizeof(ACL)) { PACL paclTemp = NULL; try { paclTemp = (PACL) new BYTE[dwAclSize]; if(paclTemp != NULL) { ::InitializeAcl(paclTemp, dwAclSize, ACL_REVISION); if((dwRet = m_ael.FillWin32ACL(paclTemp)) == ERROR_SUCCESS) { *paclOut = paclTemp; dwRet = ERROR_SUCCESS; } } } catch(...) { if(paclTemp != NULL) { delete paclTemp; paclTemp = NULL; } throw; } } else { dwRet = AR_ACL_EMPTY; } } else { dwRet = AR_BAD_ACL; } } else { dwRet = AR_ACL_EMPTY; } } return dwRet; } bool CAccessRights::GetCSid(CSid &csid, bool fResolve) { bool fRet = false; if(m_dwError == ERROR_SUCCESS) { if(m_csid.IsValid() && m_csid.IsOK()) { if(fResolve) { // Need to create a new one since ours doesn't // have account or domain name resolved. CSid csidTemp(m_csid.GetPSid()); if(csidTemp.IsValid() && csidTemp.IsOK()) { csid = csidTemp; fRet = true; } } else { csid = m_csid; fRet = true; } } } return fRet; } #endif