//*************************************************************************** // // Copyright (c) 1998-1999 Microsoft Corporation // // PXYCACHE.CPP // // alanbos 22-Sep-98 Created. // // Defines the CSWbemProxyCache class // //*************************************************************************** #include "precomp.h" // Need to try and figure out the domain static BSTR BuildDomainUser (BSTR bsSimpleUser) { BSTR bsDomainUser = NULL; HANDLE hToken = NULL; if (OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, TRUE, &hToken) || OpenProcessToken (GetCurrentProcess (), TOKEN_READ, &hToken)) { // Get the user sid TOKEN_USER tu; DWORD dwLen = 0; GetTokenInformation (hToken, TokenUser, &tu, sizeof(tu), &dwLen); if (0 < dwLen) { BYTE* pTemp = new BYTE[dwLen]; if (pTemp) { DWORD dwRealLen = dwLen; if (GetTokenInformation (hToken, TokenUser, pTemp, dwRealLen, &dwLen)) { PSID pSid = ((TOKEN_USER*)pTemp)->User.Sid; // Do the first lookup to get the buffer sizes required. DWORD dwNameLen = 0; DWORD dwDomainLen = 0; LPWSTR pUser = 0; LPWSTR pDomain = 0; SID_NAME_USE Use; LookupAccountSidW (NULL, pSid, pUser, &dwNameLen, pDomain, &dwDomainLen, &Use); DWORD dwLastErr = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwLastErr) { // Allocate the required buffers and look them up again. pUser = new WCHAR [dwNameLen + 1]; if (pUser) { pDomain = new WCHAR [dwDomainLen + wcslen (bsSimpleUser) + 2]; if (pDomain) { if (LookupAccountSidW (NULL, pSid, pUser, &dwNameLen, pDomain, &dwDomainLen, &Use)) { // Now get the domain out if (pDomain) { wcscat (pDomain, L"\\"); wcscat (pDomain, bsSimpleUser); bsDomainUser = SysAllocString (pDomain); } } delete [] pDomain; } delete [] pUser; } } } delete [] pTemp; } } CloseHandle(hToken); } return bsDomainUser; } //*************************************************************************** // // CSWbemProxyCache::CSWbemProxyCache // // CONSTRUCTOR // Create a new proxy cache based on the supplied proxy and // authentication parameters. // //*************************************************************************** CSWbemProxyCache::CSWbemProxyCache ( IUnknown *pUnk, BSTR bsAuthority, BSTR bsUser, BSTR bsPassword, CWbemLocatorSecurity *pLocatorSecurity) { InitializeCriticalSection (&m_cs); EnterCriticalSection (&m_cs); InitializeMembers (pUnk); /* // Don't need this any more - the scenario this was fixing (see SMS Bug DB #53347) works in Whistler // without this workaround. Also, this is causing Scripting to fail with UPN names since DetermineLoginTypeEx // below doesn't recognize UPN user names. if (CSWbemSecurity::IsNT() && bsUser && (0 < wcslen (bsUser))) { // On NT make sure we have a valid domain name if one is not specified BSTR bsDomain = NULL; BSTR bsSimpleUser = NULL; BSTR bsPrincipalDummy = NULL; if (SUCCEEDED (DetermineLoginTypeEx (bsDomain, bsSimpleUser, bsPrincipalDummy, bsAuthority, bsUser))) { if (!bsDomain || (0 == wcslen (bsDomain))) m_bsUser = BuildDomainUser (bsSimpleUser); } SysFreeString (bsPrincipalDummy); SysFreeString (bsSimpleUser); SysFreeString (bsDomain); } */ // Unless we've already set this, do it now if (!m_bsUser) m_bsUser = SysAllocString (bsUser); m_bsAuthority = SysAllocString (bsAuthority); m_bsPassword = SysAllocString (bsPassword); m_bUsingExplicitUserName = m_bsUser && (0 < wcslen (m_bsUser)); InitializeCache (pUnk, pLocatorSecurity, (pLocatorSecurity) && pLocatorSecurity->IsAuthenticationSet (), (pLocatorSecurity) && pLocatorSecurity->IsImpersonationSet ()); // No longer need the credentials at this point - zap 'em ClearCredentials (); LeaveCriticalSection (&m_cs); } //*************************************************************************** // // CSWbemProxyCache::CSWbemProxyCache // // CONSTRUCTOR // Create a new proxy cache based on the supplied proxy and // authentication parameters. // //*************************************************************************** CSWbemProxyCache::CSWbemProxyCache ( IUnknown *pUnk, COAUTHIDENTITY *pCoAuthIdentity, BSTR bsPrincipal, BSTR bsAuthority) { InitializeCriticalSection (&m_cs); EnterCriticalSection (&m_cs); InitializeMembers (pUnk); if (bsAuthority) m_bsAuthority = SysAllocString (bsAuthority); if (bsPrincipal) m_bsPrincipal = SysAllocString (bsPrincipal); if (pCoAuthIdentity) WbemAllocAuthIdentity (pCoAuthIdentity->User, pCoAuthIdentity->Password, pCoAuthIdentity->Domain, &m_pCoAuthIdentity); m_bUsingExplicitUserName = m_pCoAuthIdentity && m_pCoAuthIdentity->User && (0 < wcslen (m_pCoAuthIdentity->User)); InitializeCache (pUnk); LeaveCriticalSection (&m_cs); } CSWbemProxyCache::CSWbemProxyCache ( IUnknown *pUnk, CSWbemSecurity *pSecurity) { InitializeCriticalSection (&m_cs); EnterCriticalSection (&m_cs); InitializeMembers (pUnk); if (pSecurity) { m_pCoAuthIdentity = pSecurity->GetCoAuthIdentity (); m_bsPrincipal = SysAllocString (pSecurity->GetPrincipal ()); m_bsAuthority = SysAllocString (pSecurity->GetAuthority ()); m_bUsingExplicitUserName = pSecurity->IsUsingExplicitUserName (); } InitializeCache (pUnk, pSecurity); LeaveCriticalSection (&m_cs); } void CSWbemProxyCache::InitializeMembers (IUnknown *pUnk) { m_cRef = 1; m_pCoAuthIdentity = NULL; m_bsPrincipal = NULL; m_bsAuthority = NULL; m_bsUser = NULL; m_bsPassword = NULL; m_bUsingExplicitUserName = false; m_bUseDefaultInfo = DetermineBlanketOptions (pUnk); } //*************************************************************************** // // CSWbemProxyCache::~CSWbemProxyCache // // DESTRUCTOR // //*************************************************************************** CSWbemProxyCache::~CSWbemProxyCache () { EnterCriticalSection (&m_cs); ClearCredentials (); if (m_bsAuthority) SysFreeString (m_bsAuthority); if (m_bsPrincipal) SysFreeString (m_bsPrincipal); if (m_pCoAuthIdentity) { WbemFreeAuthIdentity (m_pCoAuthIdentity); m_pCoAuthIdentity = NULL; } for (int i = 0; i < WBEMS_MAX_AUTHN_LEVEL + 1 - WBEMS_MIN_AUTHN_LEVEL; i++) for (int j = 0; j < WBEMS_MAX_IMP_LEVEL + 1 - WBEMS_MIN_IMP_LEVEL; j++) if (pUnkArray [i][j]) { pUnkArray [i][j] -> Release (); pUnkArray [i] [j] = NULL; } LeaveCriticalSection (&m_cs); DeleteCriticalSection (&m_cs); } //*************************************************************************** // HRESULT CSWbemProxyCache::QueryInterface // long CSWbemProxyCache::AddRef // long CSWbemProxyCache::Release // // DESCRIPTION: // // Standard Com IUNKNOWN functions. // //*************************************************************************** STDMETHODIMP CSWbemProxyCache::QueryInterface ( IN REFIID riid, OUT LPVOID *ppv ) { *ppv=NULL; if (IID_IUnknown==riid) *ppv=this; if (NULL!=*ppv) { ((LPUNKNOWN)*ppv)->AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CSWbemProxyCache::AddRef(void) { long l = InterlockedIncrement(&m_cRef); return l; } STDMETHODIMP_(ULONG) CSWbemProxyCache::Release(void) { long l = InterlockedDecrement(&m_cRef); if (0L!=l) return l; delete this; return 0; } //*************************************************************************** // // CSWbemProxyCache::SetBlanketOptions // // DESCRIPTION: // // Called from constructor only to set up interaction mode with // SetBlanket calls. // //*************************************************************************** bool CSWbemProxyCache::DetermineBlanketOptions (IUnknown *pUnk) { bool result = false; if (CSWbemSecurity::IsNT() && (4 < CSWbemSecurity::GetNTMajorVersion ())) { HANDLE hToken = NULL; if (OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, true, &hToken)) { // Certainly a candidate to use default settings for // authorization and authentication service on the blanket. // Check if we are delegating. DWORD dwBytesReturned = 0; SECURITY_IMPERSONATION_LEVEL impLevel; if (GetTokenInformation(hToken, TokenImpersonationLevel, &impLevel, sizeof(SECURITY_IMPERSONATION_LEVEL), &dwBytesReturned) && (SecurityDelegation == impLevel)) { // Looks promising - now check for whether we are using kerberos IClientSecurity *pSec; DWORD dwAuthnSvc, dwAuthzSvc, dwImp, dwAuth, dwCapabilities; if (pUnk && SUCCEEDED(pUnk->QueryInterface(IID_IClientSecurity, (void **) &pSec))) { if (SUCCEEDED (pSec->QueryBlanket(pUnk, &dwAuthnSvc, &dwAuthzSvc, NULL, &dwAuth, &dwImp, NULL, &dwCapabilities))) { if (RPC_C_AUTHN_WINNT != dwAuthnSvc) result = true; } pSec->Release (); } } CloseHandle (hToken); } } return result; } //*************************************************************************** // // CSWbemProxyCache::InitializeCache // // DESCRIPTION: // // Called from constructor only to set up cache and initial pUnk.. // // PARAMETERS: // // pUnk "seed" pUnk // pSecurity if specified, is an ISWbemSecurity used to // override the intial authn/imp/etc. settings // //*************************************************************************** void CSWbemProxyCache::InitializeCache ( IUnknown *pUnk, ISWbemSecurity *pSecurity, bool bPropagateAuthentication, bool bPropagateImpersonation ) { for (int i = 0; i < WBEMS_MAX_AUTHN_LEVEL + 1 - WBEMS_MIN_AUTHN_LEVEL; i++) for (int j = 0; j < WBEMS_MAX_IMP_LEVEL + 1 - WBEMS_MIN_IMP_LEVEL; j++) pUnkArray [i] [j] = NULL; if (pUnk) { DWORD dwAuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT; DWORD dwImpLevel = RPC_C_IMP_LEVEL_IMPERSONATE; HRESULT hr = GetAuthImp (pUnk, &dwAuthnLevel, &dwImpLevel); /* * If we have been passed a "seed" Security object, use the auth/imp * settings of that Seed as our initial setting. Otherwise * use the settings supplied in the current proxy. */ if (pSecurity) { if (!bPropagateImpersonation || FAILED(pSecurity->get_ImpersonationLevel (&m_dwInitialImpLevel))) m_dwInitialImpLevel = (WbemImpersonationLevelEnum) dwImpLevel; if (!bPropagateAuthentication || FAILED(pSecurity->get_AuthenticationLevel (&m_dwInitialAuthnLevel))) m_dwInitialAuthnLevel = (WbemAuthenticationLevelEnum) dwAuthnLevel; /* * If settings are identical, use the proxy we have but set the * blanket to ensure user/password/authority are set. */ if (((WbemImpersonationLevelEnum) dwImpLevel == m_dwInitialImpLevel) && ((WbemAuthenticationLevelEnum) dwAuthnLevel == m_dwInitialAuthnLevel)) { SecureProxy (pUnk, m_dwInitialAuthnLevel, m_dwInitialImpLevel); pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL] [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL] = pUnk; pUnk->AddRef (); } else { // Need to create a new proxy IClientSecurity *pCliSec = NULL; if (S_OK == pUnk->QueryInterface (IID_IClientSecurity, (PPVOID) &pCliSec)) { IUnknown *pNewUnk = NULL; // If successful this AddRef's pUnk HRESULT sc = pCliSec->CopyProxy(pUnk, &pNewUnk); if (S_OK == sc) { SecureProxy (pNewUnk, m_dwInitialAuthnLevel, m_dwInitialImpLevel); pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL] [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL] = pNewUnk; // NB: pNewUnk already AddRef'd above by CopyProxy } pCliSec->Release (); } } } else { m_dwInitialAuthnLevel = (WbemAuthenticationLevelEnum) dwAuthnLevel; m_dwInitialImpLevel = (WbemImpersonationLevelEnum) dwImpLevel; pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL] [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL] = pUnk; pUnk->AddRef (); } } } //*************************************************************************** // // CSWbemProxyCache::GetProxy // // DESCRIPTION: // // Return a proxy from the cache with the desired authentication and // impersonation level. // // PARAMETERS: // // authnLevel required authentication level // impLevel required impersonation level // forceResecure whether to force a resecure of an extant proxy // // RETURN VALUES: // Pointer to copied proxy, or NULL. If not NULL, caller must Release. // //*************************************************************************** IUnknown *CSWbemProxyCache::GetProxy ( WbemAuthenticationLevelEnum authnLevel, WbemImpersonationLevelEnum impLevel, bool forceResecure) { EnterCriticalSection (&m_cs); IUnknown *pUnk = pUnkArray [authnLevel - WBEMS_MIN_AUTHN_LEVEL] [impLevel - WBEMS_MIN_IMP_LEVEL]; if (pUnk) { // Already cached this proxy - reuse pUnk->AddRef (); // Force a resecure? This is useful if we have just changed the // privileges in the current token and need to stim RPC to pick // them up. if (forceResecure) SecureProxy (pUnk, authnLevel, impLevel); } else { // Need to create a copy of the proxy; use the first // created element as a base IUnknown *pUnkFirst = pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL] [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL]; if (pUnkFirst) { // Now copy the proxy IClientSecurity *pCliSec = NULL; if (S_OK == pUnkFirst->QueryInterface (IID_IClientSecurity, (PPVOID) &pCliSec)) { // If successful this AddRef's pUnk HRESULT sc = pCliSec->CopyProxy(pUnkFirst, &pUnk); if (S_OK == sc) { SecureProxy (pUnk, authnLevel, impLevel); pUnkArray [authnLevel - WBEMS_MIN_AUTHN_LEVEL] [impLevel - WBEMS_MIN_IMP_LEVEL] = pUnk; // AddRef because we are handing pUnk back pUnk->AddRef (); } pCliSec->Release (); } } } LeaveCriticalSection (&m_cs); return pUnk; } //*************************************************************************** // // CSWbemProxyCache::SecureProxy // // DESCRIPTION: // // Secure the given proxy using the provided settings // // PARAMETERS: // // authnLevel authentication level of proxy // impLevel impersonation level of proxy // // RETURN VALUES: // none // //*************************************************************************** void CSWbemProxyCache::SecureProxy ( IUnknown *pUnk, WbemAuthenticationLevelEnum authnLevel, WbemImpersonationLevelEnum impLevel) { /* * Due to the vagaries of the IClientSecurity::SetBlanket call, * any COAUTHIDENTITY pointer passed into that call must remain * valid either until SetBlanket is called again or all proxies * on the object are released. So we need to store any returned * COAUTHIDENTITY so that it remains valid for the lifetime * of this cache. */ EnterCriticalSection (&m_cs); if (pUnk) { /* * Note that our implicit assumption is that we only ever * need one COAUTHIDENTITY per cache. This is because * the ingredients of the structure (user, password and * authority) are set at cache initialization time and * never change thereafter. */ if (m_pCoAuthIdentity) { SetInterfaceSecurityDecrypt (pUnk, m_pCoAuthIdentity, m_bsPrincipal, authnLevel, impLevel, GetCapabilities (), m_bUseDefaultInfo); } else { // See if we get one BSTR bsPrincipal = NULL; SetInterfaceSecurityEncrypt (pUnk, m_bsAuthority, m_bsUser, m_bsPassword, authnLevel, impLevel, GetCapabilities (), &m_pCoAuthIdentity, &bsPrincipal, m_bUseDefaultInfo); if (bsPrincipal) { if (m_bsPrincipal) SysFreeString (m_bsPrincipal); m_bsPrincipal = bsPrincipal; } } } LeaveCriticalSection (&m_cs); return; } //*************************************************************************** // // CSWbemProxyCache::GetCapabilities // // DESCRIPTION: // // Return the EOAC capabilities value depending on the OS platform and // user credentials (or absence thereof). // // RETURN VALUES: // // The determined capabilities. // //*************************************************************************** DWORD CSWbemProxyCache::GetCapabilities () { /* * For NT5 (and presumably greater) we enable Static * Cloaking on the proxy. This allows RPC to use the * Privilege settings in the Impersonation token. * * Note that we use static cloaking so that thread identity * is only used during CoSetProxyBlanket calls; dynamic * cloaking forces it to be used on all calls to the proxy, * so is much less efficient. Since we don't allow different * users to access the same proxy, static cloaking is sufficient. * * It makes no sense to explicitly supply a User/Password and specify * cloaking, as DCOM authentication of proxy uses either * an explicit SEC_WINNT_AUTH_IDENTITY (for User & Password) * or the current proxy identity (in the process token or * impersonation token). Requesting cloaking implies that the * proxy identity in the impersonation token is to be used, and * therefore that any User/Password is irrelevant (and vice-versa). * * See MSDN documentation on CoSetProxyBlanket for more info. */ DWORD dwCapabilities = EOAC_NONE; if (CSWbemSecurity::IsNT () && (4 < CSWbemSecurity::GetNTMajorVersion ()) && !m_bUsingExplicitUserName) dwCapabilities |= EOAC_STATIC_CLOAKING; return dwCapabilities ; } COAUTHIDENTITY *CSWbemProxyCache::GetCoAuthIdentity () { HRESULT hr = E_FAIL; COAUTHIDENTITY *pAuthIdent = NULL; if (m_pCoAuthIdentity) hr = WbemAllocAuthIdentity (m_pCoAuthIdentity->User, m_pCoAuthIdentity->Password, m_pCoAuthIdentity->Domain, &pAuthIdent); return pAuthIdent; } void CSWbemProxyCache::ClearCredentials () { if (m_bsUser) { _wcsnset (m_bsUser, L'0', wcslen (m_bsUser)); SysFreeString (m_bsUser); m_bsUser = NULL; } if (m_bsPassword) { _wcsnset (m_bsPassword, L'0', wcslen (m_bsPassword)); SysFreeString (m_bsPassword); m_bsPassword = NULL; } }