#include "precomp.h" #include #include "wmiauthz.h" /************************************************************************** Win32 Authz prototypes ***************************************************************************/ typedef BOOL (WINAPI*PAuthzAccessCheck)( IN DWORD Flags, IN AUTHZ_CLIENT_CONTEXT_HANDLE AuthzClientContext, IN PAUTHZ_ACCESS_REQUEST pRequest, IN AUTHZ_AUDIT_EVENT_HANDLE AuditInfo OPTIONAL, IN PSECURITY_DESCRIPTOR pSecurityDescriptor, IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL, IN DWORD OptionalSecurityDescriptorCount, IN OUT PAUTHZ_ACCESS_REPLY pReply, OUT PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE pAuthzHandle OPTIONAL ); typedef BOOL (WINAPI*PAuthzInitializeResourceManager)( IN DWORD AuthzFlags, IN PFN_AUTHZ_DYNAMIC_ACCESS_CHECK pfnAccessCheck OPTIONAL, IN PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS pfnComputeDynamicGroups OPTIONAL, IN PFN_AUTHZ_FREE_DYNAMIC_GROUPS pfnFreeDynamicGroups OPTIONAL, IN PCWSTR szResourceManagerName, OUT PAUTHZ_RESOURCE_MANAGER_HANDLE pAuthzResourceManager ); typedef BOOL (WINAPI*PAuthzInitializeContextFromSid)( IN DWORD Flags, IN PSID UserSid, IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzResourceManager, IN PLARGE_INTEGER pExpirationTime OPTIONAL, IN LUID Identifier, IN PVOID DynamicGroupArgs OPTIONAL, OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext ); typedef BOOL (WINAPI*PAuthzInitializeContextFromToken)( IN HANDLE TokenHandle, IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzResourceManager, IN PLARGE_INTEGER pExpirationTime OPTIONAL, IN LUID Identifier, IN DWORD Flags, IN PVOID DynamicGroupArgs OPTIONAL, OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext ); typedef BOOL (WINAPI*PAuthzFreeContext)( AUTHZ_CLIENT_CONTEXT_HANDLE ); typedef BOOL (WINAPI*PAuthzFreeResourceManager)( AUTHZ_RESOURCE_MANAGER_HANDLE ); BOOL WINAPI ComputeDynamicGroups( IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext, IN PVOID Args, OUT PSID_AND_ATTRIBUTES *pSidAttrArray, OUT PDWORD pSidCount, OUT PSID_AND_ATTRIBUTES *pRestrictedSidAttrArray, OUT PDWORD pRestrictedSidCount ) { BOOL bRet; *pRestrictedSidAttrArray = NULL; *pRestrictedSidCount = 0; // // if sid is not local system, then don't need to do anything. // *pSidAttrArray = NULL; *pSidCount = 0; if ( !*(BOOL*)(Args) ) { bRet = TRUE; } else { // // need to add authenticated users and everyone groups. // PSID_AND_ATTRIBUTES psa = new SID_AND_ATTRIBUTES[2]; if ( psa != NULL ) { ZeroMemory( psa, sizeof(SID_AND_ATTRIBUTES)*2 ); SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY; if ( bRet = AllocateAndInitializeSid( &wid, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &psa[0].Sid ) ) { if ( bRet = AllocateAndInitializeSid( &ntid, 1, SECURITY_AUTHENTICATED_USER_RID, 0,0,0,0,0,0,0, &psa[1].Sid ) ) { *pSidCount = 2; *pSidAttrArray = psa; } else { FreeSid( &psa[0].Sid ); delete psa; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } else { delete psa; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } else { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); bRet = FALSE; } } return bRet; } void WINAPI FreeDynamicGroups( PSID_AND_ATTRIBUTES psa ) { if ( psa != NULL ) { FreeSid( psa[0].Sid ); FreeSid( psa[1].Sid ); delete [] psa; } } /************************************************************************** CWmiAuthzApi ***************************************************************************/ #define FUNCMEMBER(FUNC) P ## FUNC m_fp ## FUNC; class CWmiAuthzApi { HMODULE m_hMod; public: FUNCMEMBER(AuthzInitializeContextFromToken) FUNCMEMBER(AuthzInitializeContextFromSid) FUNCMEMBER(AuthzInitializeResourceManager) FUNCMEMBER(AuthzAccessCheck) FUNCMEMBER(AuthzFreeContext) FUNCMEMBER(AuthzFreeResourceManager) CWmiAuthzApi() { ZeroMemory( this, sizeof(CWmiAuthzApi) ); } ~CWmiAuthzApi() { if ( m_hMod != NULL ) FreeLibrary( m_hMod ); } HRESULT Initialize(); }; #define SETFUNC(FUNC) \ m_fp ## FUNC = (P ## FUNC) GetProcAddress( m_hMod, #FUNC ); \ if ( m_fp ## FUNC == NULL ) return WBEM_E_NOT_SUPPORTED; HRESULT CWmiAuthzApi::Initialize() { m_hMod = LoadLibrary( _T("authz") ); if ( m_hMod == NULL ) { return WBEM_E_NOT_SUPPORTED; } SETFUNC(AuthzInitializeContextFromToken) SETFUNC(AuthzInitializeResourceManager) SETFUNC(AuthzInitializeContextFromSid) SETFUNC(AuthzInitializeContextFromToken) SETFUNC(AuthzAccessCheck) SETFUNC(AuthzFreeContext) SETFUNC(AuthzFreeResourceManager) return WBEM_S_NO_ERROR; }; /************************************************************************** CWmiAuthz ***************************************************************************/ #define CALLFUNC(API,FUNC) (*API->m_fp ## FUNC) CWmiAuthz::CWmiAuthz( CLifeControl* pControl ) : CUnkBase( pControl ), m_hResMgr(NULL), m_pApi(NULL), m_pAdministratorsSid(NULL), m_pLocalSystemSid(NULL) { } HRESULT CWmiAuthz::EnsureInitialized() { HRESULT hr; CInCritSec ics( &m_cs ); if ( m_hResMgr != NULL ) { return WBEM_S_NO_ERROR; } // // try to create the API object. // if ( m_pApi == NULL ) { m_pApi = new CWmiAuthzApi; if ( m_pApi == NULL ) { return WBEM_E_OUT_OF_MEMORY; } hr = m_pApi->Initialize(); if ( FAILED(hr) ) { delete m_pApi; m_pApi = NULL; return hr; } } // // initialize the authz res mgr. // if ( !CALLFUNC(m_pApi,AuthzInitializeResourceManager) ( AUTHZ_RM_FLAG_NO_AUDIT, NULL, ComputeDynamicGroups, FreeDynamicGroups, NULL, &m_hResMgr ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } // // allocate and initialize well known sids for authz special casing. // SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY; if ( !AllocateAndInitializeSid( &id, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &m_pAdministratorsSid) ) { return HRESULT_FROM_WIN32( GetLastError() ); } if ( !AllocateAndInitializeSid( &id, 1, SECURITY_LOCAL_SYSTEM_RID, 0,0,0,0,0,0,0, &m_pLocalSystemSid) ) { return HRESULT_FROM_WIN32( GetLastError() ); } return WBEM_S_NO_ERROR; } STDMETHODIMP CWmiAuthz::Shutdown() { return WBEM_S_NO_ERROR; } CWmiAuthz::~CWmiAuthz() { if ( m_hResMgr != NULL ) { CALLFUNC(m_pApi,AuthzFreeResourceManager)( m_hResMgr ); } if ( m_pApi != NULL ) { delete m_pApi; } if ( m_pAdministratorsSid != NULL ) { FreeSid( m_pAdministratorsSid ); } if ( m_pLocalSystemSid != NULL ) { FreeSid( m_pLocalSystemSid ); } } STDMETHODIMP CWmiAuthz::GetToken( const BYTE* pSid, IWbemToken** ppToken ) { HRESULT hr; *ppToken = NULL; hr = EnsureInitialized(); if ( SUCCEEDED( hr ) ) { AUTHZ_CLIENT_CONTEXT_HANDLE hCtx = NULL; LUID luid; ZeroMemory( &luid, sizeof(LUID) ); DWORD dwFlags = 0; BOOL bLocalSystem = FALSE; if ( EqualSid( PSID(pSid), m_pAdministratorsSid ) ) { // // this is a group sid, so specify this in the flags so // authz can handle it properly. // dwFlags = AUTHZ_SKIP_TOKEN_GROUPS; } else if ( EqualSid( PSID(pSid), m_pLocalSystemSid ) ) { // // authz doesn't handle local system so have to workaround // by disabling authz's group computation and do it ourselves. // bLocalSystem = TRUE; dwFlags = AUTHZ_SKIP_TOKEN_GROUPS; } if ( !CALLFUNC(m_pApi,AuthzInitializeContextFromSid)( dwFlags, PSID(pSid), m_hResMgr, NULL, luid, &bLocalSystem, &hCtx ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } *ppToken = new CWmiAuthzToken( this, hCtx ); if ( *ppToken == NULL ) { CALLFUNC(m_pApi,AuthzFreeContext)(hCtx); return WBEM_E_OUT_OF_MEMORY; } (*ppToken)->AddRef(); return WBEM_S_NO_ERROR; } return hr; } /*************************************************************************** CWmiAuthzToken ****************************************************************************/ CWmiAuthzToken::CWmiAuthzToken( CWmiAuthz* pOwner, AUTHZ_CLIENT_CONTEXT_HANDLE hCtx ) : CUnkBase(NULL), m_hCtx(hCtx), m_pOwner(pOwner) { // // we want to keep the owner alive, in case the caller has released theirs // m_pOwner->AddRef(); } CWmiAuthzToken::~CWmiAuthzToken() { CWmiAuthzApi* pApi = m_pOwner->GetApi(); CALLFUNC(pApi,AuthzFreeContext)(m_hCtx); m_pOwner->Release(); } STDMETHODIMP CWmiAuthzToken::AccessCheck( DWORD dwDesiredAccess, const BYTE* pSD, DWORD* pdwGrantedAccess ) { HRESULT hr; AUTHZ_ACCESS_REQUEST AccessReq; ZeroMemory( &AccessReq, sizeof(AUTHZ_ACCESS_REQUEST) ); AccessReq.DesiredAccess = dwDesiredAccess; AUTHZ_ACCESS_REPLY AccessRep; DWORD dwError; ZeroMemory( &AccessRep, sizeof(AUTHZ_ACCESS_REPLY) ); AccessRep.GrantedAccessMask = pdwGrantedAccess; AccessRep.ResultListLength = 1; AccessRep.Error = &dwError; AccessRep.SaclEvaluationResults = NULL; CWmiAuthzApi* pApi = m_pOwner->GetApi(); if ( !CALLFUNC(pApi,AuthzAccessCheck)( 0, m_hCtx, &AccessReq, NULL, PSECURITY_DESCRIPTOR(pSD), NULL, NULL, &AccessRep, NULL ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } return WBEM_S_NO_ERROR; } /**************************************************************************** WILL NOT BE COMPILED IN PRESENCE OF AUTHZ LIBRARY *****************************************************************************/ #ifndef __AUTHZ_H__ #include #include #include #include #include #include /**************************************************************************** CWmiNoAuthzToken *****************************************************************************/ STDMETHODIMP CWmiNoAuthzToken::AccessCheck( DWORD dwDesiredAccess, const BYTE* pSD, DWORD* pdwGrantedAccess ) { PACL pDacl; BOOL bDaclPresent, bDaclDefault; *pdwGrantedAccess = 0; if ( !GetSecurityDescriptorDacl( PSECURITY_DESCRIPTOR(pSD), &bDaclPresent, &pDacl, &bDaclDefault) ) { return HRESULT_FROM_WIN32( GetLastError() ); } if ( !bDaclPresent ) { *pdwGrantedAccess = 0xffffffff; } NTSTATUS stat = WmiAuthzGetAccessMask( m_Sid.GetPtr(), pDacl, pdwGrantedAccess ); if ( stat != 0 ) { return HRESULT_FROM_WIN32( GetLastError() ); } return WBEM_S_NO_ERROR; } /*************************************************************************** Functions moved from GroupForUser ****************************************************************************/ // critical section to keep us from tripping over our threads // during initialization CCritSec CSSamStartup; CCritSec CSNetAPIStartup; CCritSec CSAdvAPIStartup; // copied here to make it easy for this file to be included elsewhere bool IsPlatformNT(void) { OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(!GetVersionEx(&os)) return false; // should never happen return os.dwPlatformId == VER_PLATFORM_WIN32_NT; } class CUnicodeString : public UNICODE_STRING { public: CUnicodeString() { MaximumLength = Length = 0; Buffer = NULL; } CUnicodeString(LPCWSTR wsz) { Buffer = NULL; MaximumLength = Length = 0; *this = wsz; } ~CUnicodeString() { delete [] Buffer; } void operator=(LPCWSTR wsz) { delete [] Buffer; MaximumLength = sizeof(WCHAR) * (wcslen(wsz)+1); Buffer = new WCHAR[MaximumLength]; Length = MaximumLength - sizeof(WCHAR); wcscpy(Buffer, wsz); } operator LPCWSTR() {return Buffer;} }; typedef NTSTATUS (NTAPI *PSamConnect)(IN PUNICODE_STRING ServerName, OUT PSAM_HANDLE ServerHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes); typedef NTSTATUS (NTAPI *PSamCloseHandle)( OUT SAM_HANDLE SamHandle ); typedef NTSTATUS (NTAPI *PSamFreeMemory)( PVOID p ); typedef NTSTATUS (NTAPI *PSamLookupDomainInSamServer)( IN SAM_HANDLE ServerHandle, IN PUNICODE_STRING Name, OUT PSID * DomainId ); typedef NTSTATUS (NTAPI *PSamLookupNamesInDomain)( IN SAM_HANDLE DomainHandle, IN ULONG Count, IN PUNICODE_STRING Names, OUT PULONG *RelativeIds, OUT PSID_NAME_USE *Use); typedef NTSTATUS (NTAPI *PSamOpenDomain)( IN SAM_HANDLE ServerHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT PSAM_HANDLE DomainHandle ); typedef NTSTATUS (NTAPI *PSamOpenUser)( IN SAM_HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN ULONG UserId, OUT PSAM_HANDLE UserHandle ); typedef NTSTATUS (NTAPI *PSamGetGroupsForUser)( IN SAM_HANDLE UserHandle, OUT PGROUP_MEMBERSHIP * Groups, OUT PULONG MembershipCount); typedef NTSTATUS (NTAPI *PSamGetAliasMembership)( IN SAM_HANDLE DomainHandle, IN ULONG PassedCount, IN PSID *Sids, OUT PULONG MembershipCount, OUT PULONG *Aliases); // class to encapsulate the SAM APIs // make sure that we always free the lib, etc. class CSamRun { public: CSamRun() { } ~CSamRun() { // oh, all right - DON'T free the lib. // FreeLibrary(m_hSamDll); } bool RunSamRun(void); //protected: // okay, so I should make these all protected // and write accessor functions. Maybe I will someday. static HINSTANCE m_hSamDll; static PSamConnect m_pfnSamConnect; static PSamCloseHandle m_pfnSamCloseHandle; static PSamFreeMemory m_pfnSamFreeMemory; static PSamLookupDomainInSamServer m_pfnSamLookupDomainInSamServer; static PSamLookupNamesInDomain m_pfnSamLookupNamesInDomain; static PSamOpenDomain m_pfnSamOpenDomain; static PSamOpenUser m_pfnSamOpenUser; static PSamGetGroupsForUser m_pfnSamGetGroupsForUser; static PSamGetAliasMembership m_pfnSamGetAliasMembership; }; HINSTANCE CSamRun::m_hSamDll = NULL; PSamConnect CSamRun::m_pfnSamConnect = NULL; PSamCloseHandle CSamRun::m_pfnSamCloseHandle = NULL; PSamFreeMemory CSamRun::m_pfnSamFreeMemory = NULL; PSamLookupDomainInSamServer CSamRun::m_pfnSamLookupDomainInSamServer = NULL; PSamLookupNamesInDomain CSamRun::m_pfnSamLookupNamesInDomain = NULL; PSamOpenDomain CSamRun::m_pfnSamOpenDomain = NULL; PSamOpenUser CSamRun::m_pfnSamOpenUser = NULL; PSamGetGroupsForUser CSamRun::m_pfnSamGetGroupsForUser = NULL; PSamGetAliasMembership CSamRun::m_pfnSamGetAliasMembership = NULL; bool CSamRun::RunSamRun() { CInCritSec runInACriticalSectionSam(&CSSamStartup); if(m_hSamDll) return true; m_hSamDll = LoadLibrary(_T("samlib.dll")); if(m_hSamDll == NULL) return false; m_pfnSamConnect = (PSamConnect)GetProcAddress(m_hSamDll, "SamConnect"); if(m_pfnSamConnect == NULL) return false; m_pfnSamCloseHandle = (PSamCloseHandle)GetProcAddress(m_hSamDll, "SamCloseHandle"); if(m_pfnSamCloseHandle == NULL) return false; m_pfnSamFreeMemory = (PSamFreeMemory)GetProcAddress(m_hSamDll, "SamFreeMemory"); if(m_pfnSamFreeMemory == NULL) return false; m_pfnSamLookupDomainInSamServer = (PSamLookupDomainInSamServer)GetProcAddress(m_hSamDll, "SamLookupDomainInSamServer"); if(m_pfnSamLookupDomainInSamServer == NULL) return false; m_pfnSamLookupNamesInDomain = (PSamLookupNamesInDomain)GetProcAddress(m_hSamDll, "SamLookupNamesInDomain"); if(m_pfnSamLookupNamesInDomain == NULL) return false; m_pfnSamOpenDomain = (PSamOpenDomain)GetProcAddress(m_hSamDll, "SamOpenDomain"); if(m_pfnSamOpenDomain == NULL) return false; m_pfnSamOpenUser = (PSamOpenUser)GetProcAddress(m_hSamDll, "SamOpenUser"); if(m_pfnSamOpenUser == NULL) return false; m_pfnSamGetGroupsForUser = (PSamGetGroupsForUser)GetProcAddress(m_hSamDll, "SamGetGroupsForUser"); if(m_pfnSamGetGroupsForUser == NULL) return false; m_pfnSamGetAliasMembership = (PSamGetAliasMembership)GetProcAddress(m_hSamDll, "SamGetAliasMembership"); if(m_pfnSamGetAliasMembership == NULL) return false; return true; } typedef BOOL (NTAPI* PLookupAccountSidW) ( LPCWSTR lpSystemName, // name of local or remote computer PSID Sid, // security identifier LPWSTR Name, // account name buffer LPDWORD cbName, // size of account name buffer LPWSTR DomainName, // domain name LPDWORD cbDomainName, // size of domain name buffer PSID_NAME_USE peUse // SID type ); typedef NET_API_STATUS (NTAPI* PNetGetAnyDCName)( LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr ); typedef NET_API_STATUS (NTAPI* PNetGetDCName)( LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr ); typedef NET_API_STATUS (NTAPI* PNetApiBufferFree)( LPVOID Buffer ); typedef NET_API_STATUS (NTAPI* PNetWkstaGetInfo)( LPWSTR servername, DWORD level, LPBYTE *bufptr ); // dynamically load NetAPI32.DLL // so we can run without it on 9X class NetApiDLL { public: bool Init(void); static PNetGetAnyDCName m_pfnNetGetAnyDCName; static PNetGetAnyDCName m_pfnNetGetDCName; static PNetApiBufferFree m_pfnNetApiBufferFree; static PNetWkstaGetInfo m_pfnNetWkstaGetInfo; static HINSTANCE m_hDll; }; PNetGetAnyDCName NetApiDLL::m_pfnNetGetAnyDCName = NULL; PNetGetDCName NetApiDLL::m_pfnNetGetDCName = NULL; PNetApiBufferFree NetApiDLL::m_pfnNetApiBufferFree = NULL; PNetWkstaGetInfo NetApiDLL::m_pfnNetWkstaGetInfo = NULL; HINSTANCE NetApiDLL::m_hDll = NULL; bool NetApiDLL::Init(void) { CInCritSec inCS(&CSNetAPIStartup); bool bRet = false; if (m_hDll) bRet = true; else if (m_hDll = LoadLibrary(_T("NetAPI32.dll"))) { m_pfnNetGetAnyDCName = (PNetGetAnyDCName) GetProcAddress(m_hDll, "NetGetAnyDCName"); m_pfnNetGetDCName = (PNetGetDCName) GetProcAddress(m_hDll, "NetGetDCName"); m_pfnNetApiBufferFree = (PNetApiBufferFree) GetProcAddress(m_hDll, "NetApiBufferFree"); m_pfnNetWkstaGetInfo = (PNetWkstaGetInfo) GetProcAddress(m_hDll, "NetWkstaGetInfo"); bRet = ((m_pfnNetGetAnyDCName != NULL) && (m_pfnNetGetDCName != NULL) && (m_pfnNetApiBufferFree != NULL) && (m_pfnNetWkstaGetInfo != NULL)); if (!bRet) { FreeLibrary(m_hDll); m_hDll = NULL; } } else ERRORTRACE((LOG_ESS, "Failed to load NetAPI32.dll, 0x%X", GetLastError())); return bRet; } // dynamically load AdvAPI32.DLL // so we can run without it on 9X class AdvApiDLL { public: bool Init(void); static PLookupAccountSidW m_pfnLookupAccountSidW; static HINSTANCE m_hDll; }; PLookupAccountSidW AdvApiDLL::m_pfnLookupAccountSidW = NULL; HINSTANCE AdvApiDLL::m_hDll = NULL; bool AdvApiDLL::Init(void) { CInCritSec inCS(&CSAdvAPIStartup); bool bRet = false; if (m_hDll) bRet = true; else if (m_hDll = LoadLibrary(_T("AdvAPI32.dll"))) { m_pfnLookupAccountSidW = (PLookupAccountSidW) GetProcAddress(m_hDll, "LookupAccountSidW"); bRet = (m_pfnLookupAccountSidW != NULL); } else ERRORTRACE((LOG_ESS, "Failed to load AdvAPI32.dll, 0x%X", GetLastError())); return bRet; } class CSamHandle { private: SAM_HANDLE m_hHandle; CSamRun& m_sam; protected: void Close(); public: CSamHandle(CSamRun& sam) : m_sam(sam), m_hHandle(NULL) { } ~CSamHandle(); // same as operator SAM_HANDLE*() // gosh, I sure do love C! operator void**() {return &((void*)m_hHandle); } operator SAM_HANDLE() {return m_hHandle; } }; CSamHandle::~CSamHandle() { Close(); } void CSamHandle::Close() { if (m_hHandle) m_sam.m_pfnSamCloseHandle(m_hHandle); m_hHandle = NULL; } class CSamFreeMe { protected: void* m_p; CSamRun m_sam; public: CSamFreeMe(CSamRun& sam, void* p) : m_p(p), m_sam(sam) {} ~CSamFreeMe() {m_sam.m_pfnSamFreeMemory(m_p);} }; class CHeapFreeMe { protected: void* m_p; public: CHeapFreeMe(void* p) : m_p(p){} ~CHeapFreeMe() { if (m_p) HeapFree(GetProcessHeap(), 0, m_p); } }; PSID CreateUserSid(PSID pDomainSid, DWORD dwUserRid) { DWORD dwOldLen = GetLengthSid(pDomainSid); // PISID pSid = (PISID)new BYTE[dwOldLen + sizeof(DWORD)]; PISID pSid = (PISID)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwOldLen + sizeof(DWORD)); if (pSid) { memcpy(pSid, pDomainSid, dwOldLen); pSid->SubAuthority[pSid->SubAuthorityCount] = dwUserRid; pSid->SubAuthorityCount++; } return pSid; } class CHeapBigPointerArrayCleanerUpper { public: CHeapBigPointerArrayCleanerUpper(void** pArray = NULL, DWORD count = 0) : m_pArray(pArray), m_count(count) {} ~CHeapBigPointerArrayCleanerUpper() { if (m_pArray) for (DWORD i = 0; i < m_count; i++) if (m_pArray[i]) HeapFree(GetProcessHeap(), 0, m_pArray[i]); } protected: void** m_pArray; DWORD m_count; }; // returns STATUS_SUCCESS if user is in group // STATUS_ACCESS_DENIED if not // some error code or other on error NTSTATUS IsUserInGroup(PSID pSidUser, PSID pSidGroup) { if (!IsPlatformNT()) return STATUS_NOT_SUPPORTED; PSID* pSids = NULL; DWORD dwCount; NTSTATUS stat = EnumGroupsForUser(pSidUser, NULL, &pSids, &dwCount); // if we can't get to the domain controller, try just local groups if (stat) { WCHAR machineName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD size = MAX_COMPUTERNAME_LENGTH +1; GetComputerNameW(machineName, &size); stat = EnumGroupsForUser(pSidUser, machineName, &pSids, &dwCount); } // arrange for clean up no matter how we exit CHeapFreeMe freeArray(pSids); CHeapBigPointerArrayCleanerUpper cleanSids(pSids, dwCount); if (stat == STATUS_SUCCESS) { stat = STATUS_ACCESS_DENIED; for(DWORD i = 0; i < dwCount; i++) if (EqualSid(pSidGroup, pSids[i])) stat = STATUS_SUCCESS; } return stat; } // returns STATUS_SUCCESS if user is in admin group // STATUS_ACCESS_DENIED if not // some error code or other on error NTSTATUS IsUserAdministrator(PSID pSidUser) { if (!IsPlatformNT()) return STATUS_NOT_SUPPORTED; NTSTATUS stat = STATUS_ACCESS_DENIED; PSID pSidAdmins; SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&id, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0,&pSidAdmins)) { stat = WmiAuthzIsUserInGroup(pSidUser, pSidAdmins); // We're done with this FreeSid(pSidAdmins); } else { stat = GetLastError(); ERRORTRACE((LOG_ESS, "AllocateAndInitializeSid failed, error 0x%X\n", stat)); } return stat; } // retireves access mask corresponding to permissions granted // by dacl to account denoted in pSid // only deals with the ACCESS_ALLOWED/DENIED type aces // including the ACCESS_ALLOWED/DENIED_OBJECT_ACEs // - will error out if it finds a SYSTEM_AUDIT or unrecognized type. NTSTATUS GetAccessMask(PSID pSid, PACL pDacl, DWORD* pAccessMask) { if (!IsPlatformNT()) return STATUS_NOT_SUPPORTED; NTSTATUS stat = STATUS_SUCCESS; // let's zero this puppy out // lest someone not check the return code & compare against garbage *pAccessMask = 0; // will compute each & knock them against each other DWORD accessAllowed = 0; DWORD accessDenied = 0; PSID* pSids = NULL; DWORD dwCount; LPVOID pAce; stat = EnumGroupsForUser(pSid, NULL, &pSids, &dwCount); // arrange for clean up no matter how we exit CHeapFreeMe freeArray(pSids); CHeapBigPointerArrayCleanerUpper cleanSids(pSids, dwCount); // de buggy test harness // char name[300]; // char domain[300]; // DWORD x = 300, y = 300; // SID_NAME_USE eUse; //for (int q = 0; q < dwCount; q++) //{ // x = y = 300; // LookupAccountSid(NULL, pSids[q], name, &x, domain, &y, &eUse); //} if (stat == STATUS_SUCCESS) // iterate through all of the ACE's in the ACL // for each, iterate through all of the groups for the user // if one matches, OR in its allowed/disallowed mask for (DWORD nAce = 0; nAce < pDacl->AceCount; nAce++) if (GetAce(pDacl, nAce, &pAce)) { // de buggy test harness // x = y = 300; // LookupAccountSid(NULL, &(((ACCESS_ALLOWED_ACE*)pAce)->SidStart), name, &x, domain, &y, &eUse); for (DWORD nSid = 0; nSid < dwCount; nSid++) { // de buggy test harness // x = y = 300; // if (!LookupAccountSid(NULL, pSids[nSid], name, &x, domain, &y, &eUse)) // DWORD gubl = GetLastError(); switch (((ACE_HEADER*)pAce)->AceType) { case ACCESS_ALLOWED_ACE_TYPE: if (EqualSid(&(((ACCESS_ALLOWED_ACE*)pAce)->SidStart), pSids[nSid])) accessAllowed |= ((ACCESS_ALLOWED_ACE*)pAce)->Mask; break; case ACCESS_DENIED_ACE_TYPE: if (EqualSid(&(((ACCESS_DENIED_ACE*)pAce)->SidStart), pSids[nSid])) accessDenied |= ((ACCESS_DENIED_ACE*)pAce)->Mask; break; case ACCESS_ALLOWED_OBJECT_ACE_TYPE: if (EqualSid(&(((ACCESS_ALLOWED_OBJECT_ACE*)pAce)->SidStart), pSids[nSid])) accessAllowed |= ((ACCESS_ALLOWED_OBJECT_ACE*)pAce)->Mask; break; case ACCESS_DENIED_OBJECT_ACE_TYPE: if (EqualSid(&(((ACCESS_DENIED_OBJECT_ACE*)pAce)->SidStart), pSids[nSid])) accessDenied |= ((ACCESS_DENIED_OBJECT_ACE*)pAce)->Mask; break; default: // in too deep - bail! return STATUS_INVALID_PARAMETER; } } } else { // GetAce failed DWORD dwErr = GetLastError(); ERRORTRACE((LOG_ESS, "GetAce failed, error 0x%X", dwErr)); return dwErr; } if (stat == STATUS_SUCCESS) *pAccessMask = accessAllowed & ~accessDenied; return stat; } // given a SID & server name // will return all groups from the local domain of which user is a member // callers responsibility to HeapFree apSids & the memory to which they point. // pdwCount points to dword to receive count of group sids returned. // serverName may be NULL, in which case this function will look up // the sid on the local computer NTSTATUS EnumGroupsForUser(PSID pSid, LPCWSTR serverName, PSID** apGroupSids, DWORD* pdwCount) { if (!IsPlatformNT()) return STATUS_NOT_SUPPORTED; NTSTATUS status = STATUS_SUCCESS; NetApiDLL netDll; AdvApiDLL advDll; if (!(netDll.Init() && advDll.Init())) status = STATUS_DLL_INIT_FAILED; else { // retrieve user name & domain name DWORD domainSize = 0, nameSize = 0; SID_NAME_USE sidUse; // call once to find out how big a buffer we need advDll.m_pfnLookupAccountSidW(serverName, pSid, NULL, &nameSize, NULL, &domainSize, &sidUse); // buy bunches o' bigger buffers LPWSTR pAccountName = NULL, pDomainName = NULL; pAccountName = new WCHAR[nameSize]; pDomainName = new WCHAR[domainSize]; CDeleteMe delAcct(pAccountName); CDeleteMe delDomain(pDomainName); if (pAccountName && pDomainName) { if (advDll.m_pfnLookupAccountSidW(serverName, pSid, pAccountName, &nameSize, pDomainName, &domainSize, &sidUse)) { WKSTA_INFO_100 *pstInfo = NULL ; LPWSTR samServerName; // may not get filled in, careful... WCHAR serverNameBuffer[MAX_COMPUTERNAME_LENGTH +1] = L"\0"; if (serverName == NULL) { DWORD dNameSize = MAX_COMPUTERNAME_LENGTH +1; WCHAR computerName[MAX_COMPUTERNAME_LENGTH +1] = L"\0"; GetComputerNameW(computerName, &dNameSize); if (_wcsicmp(computerName, pDomainName) == 0) // local domain is the machine samServerName = pDomainName; else // go grab the Domain Controller { // use the local domain! status = netDll.m_pfnNetWkstaGetInfo( NULL , 100 , ( LPBYTE * ) &pstInfo ); if (status == 0) { LPBYTE dcName = NULL; status = netDll.m_pfnNetGetDCName(NULL, pstInfo->wki100_langroup, &dcName); // if we can't find a/the PDC, try for a backup... // if ((status == 0x54B) || (status == 0x995)) if (status) status = netDll.m_pfnNetGetAnyDCName(NULL, pstInfo->wki100_langroup, &dcName); netDll.m_pfnNetApiBufferFree(pstInfo); if (status == 0) { LPWSTR dcNameWithoutWhacks = (LPWSTR)dcName; // name is prefaced with "\\" dcNameWithoutWhacks += 2; wcscpy(serverNameBuffer, dcNameWithoutWhacks); samServerName = serverNameBuffer; netDll.m_pfnNetApiBufferFree(dcName); } } } } else // tweren't NULL - we'll use it samServerName = (LPWSTR)serverName; if (status == 0) status = EnumGroupsForUser(pAccountName, pDomainName, samServerName, apGroupSids, pdwCount); } else { // lookup account sid failed - dunno why. status = GetLastError(); ERRORTRACE((LOG_ESS, "LookupAccountSid failed: 0x%X\n", status)); } } else { ERRORTRACE((LOG_ESS, "Allocation Failure\n")); // couldn't allocate name buffers status = STATUS_NO_MEMORY; } } // if netDll.Init return status; } // given user name, domain name & server name // will return all groups of which user is a member // callers responsibility to HeapFree apSids & the memory to which they point. // pdwCount points to dword to receive count of group sids returned. NTSTATUS EnumGroupsForUser(LPCWSTR userName, LPCWSTR domainName, LPCWSTR serverName, PSID** apGroupSids, DWORD* pdwCount) { if (!IsPlatformNT()) return STATUS_NOT_SUPPORTED; CSamRun sam; if (!sam.RunSamRun()) return STATUS_DLL_INIT_FAILED; else { // will reuse this puppy without remorse. CUnicodeString buffer; NTSTATUS ntst = STATUS_SUCCESS; // get local handles CSamHandle hLocalSam(sam); ntst = sam.m_pfnSamConnect(NULL, (void**)hLocalSam, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, NULL); if(ntst) { ERRORTRACE((LOG_ESS, "SamConnect Failed, error 0x%X\n", ntst)); return ntst; } PSID pBuiltinDomainId = NULL; buffer = L"BUILTIN"; ntst = sam.m_pfnSamLookupDomainInSamServer(hLocalSam, &buffer, &pBuiltinDomainId); if(ntst) { ERRORTRACE((LOG_ESS, "SamLookupDomainInSamServer Failed on BUILTIN domain, error 0x%X\n", ntst)); return ntst; } CSamFreeMe freeBuiltinDomain(sam, pBuiltinDomainId); CSamHandle hBuiltinDomain(sam); ntst = sam.m_pfnSamOpenDomain(hLocalSam, DOMAIN_GET_ALIAS_MEMBERSHIP | DOMAIN_LOOKUP, pBuiltinDomainId, (void**)hBuiltinDomain); if(ntst) { ERRORTRACE((LOG_ESS, "SamOpenDomain Failed on BUILTIN domain, error 0x%X\n", ntst)); return ntst; } // make an 'everyone' sid PSID pSidEveryoneHeapAlloc = NULL; PSID pSidEveryone = NULL; SID_IDENTIFIER_AUTHORITY sa = SECURITY_WORLD_SID_AUTHORITY; if (AllocateAndInitializeSid(&sa, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &pSidEveryone)) { DWORD len = GetLengthSid(pSidEveryone); pSidEveryoneHeapAlloc = (PISID)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); if (!pSidEveryoneHeapAlloc) { FreeSid(pSidEveryone); return WBEM_E_OUT_OF_MEMORY; } memcpy(pSidEveryoneHeapAlloc, pSidEveryone, len); FreeSid(pSidEveryone); pSidEveryone = NULL; } else { ntst = GetLastError(); ERRORTRACE((LOG_ESS, "AllocateAndInitializeSid failed, error 0x%X\n", ntst)); return ntst; } CSamHandle hSam(sam); // connect & determine global groups buffer = serverName; ntst = sam.m_pfnSamConnect(&buffer, (void **)hSam, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, NULL); if (ntst) { ERRORTRACE((LOG_ESS, "SamConnect Failed on %S, error 0x%X\n", serverName, ntst)); return ntst; } DWORD dwMembershipCount; DWORD dwUserId; PSID* apSids; PSID pDomainId = NULL; buffer = domainName; ntst = sam.m_pfnSamLookupDomainInSamServer((SAM_HANDLE)hSam, &buffer, &pDomainId); if ( ntst == 0 ) { CSamFreeMe freeDomain(sam, pDomainId); CSamHandle hDomain(sam); ntst = sam.m_pfnSamOpenDomain((SAM_HANDLE)hSam, DOMAIN_GET_ALIAS_MEMBERSHIP | DOMAIN_LOOKUP, pDomainId, (void**)hDomain); if(ntst) return ntst; CSamHandle hUser(sam); buffer = userName; ULONG* pdwUserId = NULL; SID_NAME_USE* psnu = NULL; ntst = sam.m_pfnSamLookupNamesInDomain(hDomain, 1, &buffer, &pdwUserId, &psnu); if(ntst) { ERRORTRACE((LOG_ESS, "SamLookupNamesInDomain Failed on %S, error 0x%X\n", userName, ntst)); return ntst; } CSamFreeMe freeSnu(sam, psnu); dwUserId = *pdwUserId; sam.m_pfnSamFreeMemory(pdwUserId); ntst = sam.m_pfnSamOpenUser(hDomain, USER_LIST_GROUPS, dwUserId, (void**)hUser); if(ntst) return ntst; GROUP_MEMBERSHIP* aMemberships = NULL; ntst = sam.m_pfnSamGetGroupsForUser(hUser, &aMemberships, &dwMembershipCount); if(ntst) return ntst; CSamFreeMe freeMembers(sam, aMemberships); // got everything we need for the first bunch... apSids = (PSID*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PSID) * (dwMembershipCount +2)); if (apSids) { PSID pSid = CreateUserSid(pDomainId, dwUserId); if (!pSid) return STATUS_NO_MEMORY; apSids[0] = pSid; apSids[1] = pSidEveryoneHeapAlloc; for(DWORD i = 0; i < dwMembershipCount; i++) { pSid = CreateUserSid(pDomainId, aMemberships[i].RelativeId); if (pSid) apSids[i+2] = pSid; else { CHeapBigPointerArrayCleanerUpper cleanSids(apSids, dwMembershipCount +1); return STATUS_NO_MEMORY; } } } else return STATUS_NO_MEMORY; } else { apSids = (PSID*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PSID) * 2 ); if (!apSids) { return STATUS_NO_MEMORY; } WCHAR wszDomain[256]; DWORD cDomain = 256; SID_NAME_USE psnu; DWORD cSid = 256; apSids[0] = (PSID)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, cSid ); if ( apSids[0] == NULL ) { return STATUS_NO_MEMORY; } // // have to join the domain name and user name strings. This is // to qualify the name passed to LookupAccountName, but it also // provides a significant performance improvement in the call. // WCHAR wszFullName[512]; int cDomainName = wcslen(domainName); wcscpy(wszFullName,domainName); wszFullName[cDomainName] = '\\'; wcscpy(wszFullName+cDomainName+1, userName); if ( !LookupAccountNameW( NULL, wszFullName, apSids[0], &cSid, wszDomain, &cDomain, &psnu ) ) { return GetLastError(); } dwMembershipCount = 0; apSids[1] = pSidEveryoneHeapAlloc; } CHeapFreeMe freeArray(apSids); // do it again for the local case DWORD dwLocalGroupCount; DWORD* pdwLocalGroups; ntst = sam.m_pfnSamGetAliasMembership(hBuiltinDomain, dwMembershipCount+2, apSids, &dwLocalGroupCount, &pdwLocalGroups); if (ntst) { ERRORTRACE((LOG_ESS, "SamGetAliasMembership Failed, error 0x%X\n", ntst)); return ntst; } CSamFreeMe freeGroups(sam, pdwLocalGroups); // got both the global & the local - build us an array to hold them all: PSID* apAllSids = (PSID*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PSID) * (dwMembershipCount + dwLocalGroupCount +2)); if (apSids) { for(DWORD i = 0; i < dwMembershipCount +2; i++) apAllSids[i] = apSids[i]; for (i = 0; i < dwLocalGroupCount; i++) { PSID pSid = CreateUserSid(pBuiltinDomainId, pdwLocalGroups[i]); if (pSid) apAllSids[i +dwMembershipCount +2] = pSid; else // lazy man's cleanup - let the dtors do all the work; { ERRORTRACE((LOG_ESS, "Allocation Failure\n")); CHeapFreeMe freeArray(apAllSids); CHeapBigPointerArrayCleanerUpper cleanAllSids(apAllSids, dwMembershipCount + dwLocalGroupCount +1); return STATUS_NO_MEMORY; } } *apGroupSids = apAllSids; *pdwCount = dwMembershipCount + dwLocalGroupCount +2; } else { ERRORTRACE((LOG_ESS, "Allocation Failure\n")); return STATUS_NO_MEMORY; } } return STATUS_SUCCESS; } #endif