|
|
/*++
Copyright (C) 1998-2001 Microsoft Corporation
Module Name:
SECURE.CPP
Abstract:
Contains various routines used for ACL based security. It is defined in secure.h
History:
a-davj 05-NOV-98 Created.
--*/
#include "precomp.h"
#include <wbemcore.h>
#include <oleauto.h>
#include <genutils.h>
#include <safearry.h>
#include <oahelp.inl>
#include <fcntl.h>
#define WBEM_WMISETUP __TEXT("WMISetup")
// /////////////////////////////////////////////////
AutoRevertSecTlsFlag::AutoRevertSecTlsFlag ( LPVOID dir ) { m_bDir = dir ; TlsSetValue ( CCoreQueue::GetSecFlagTlsIndex(), (LPVOID)dir ); }
AutoRevertSecTlsFlag::AutoRevertSecTlsFlag() { m_bDir = TlsGetValue ( CCoreQueue::GetSecFlagTlsIndex() ) ; }
AutoRevertSecTlsFlag::~AutoRevertSecTlsFlag() { TlsSetValue ( CCoreQueue::GetSecFlagTlsIndex(), (LPVOID)1 ) ; }
VOID AutoRevertSecTlsFlag::SetSecTlsFlag ( LPVOID dir ) { TlsSetValue ( CCoreQueue::GetSecFlagTlsIndex(), (LPVOID)dir ); }
//***************************************************************************
//
// SetOwnerAndGroup
//
// Sets the owner and group of the SD to the Admininstrators group
//
//***************************************************************************
BOOL SetOwnerAndGroup(CNtSecurityDescriptor &sd) { PSID pRawSid; BOOL bRet = FALSE;
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY; if(AllocateAndInitializeSid( &id, 2, // SEC:REVIEWED 2002-03-22 : OK
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0,&pRawSid)) { CNtSid SidAdmins(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError != SidAdmins.GetStatus()) return FALSE; bRet = sd.SetGroup(&SidAdmins); // Access check doesnt really care what you put, so long as you
// put something for the owner
if(bRet) bRet = sd.SetOwner(&SidAdmins);
return bRet; } return bRet; }
//***************************************************************************
//
// CFlexAceArray::~CFlexAceArray()
//
// Cleans up safe array entries.
//
//***************************************************************************
CFlexAceArray::~CFlexAceArray() { for(int iCnt = 0; iCnt < Size(); iCnt++) { CBaseAce * pace = (CBaseAce *)GetAt(iCnt); if(pace) delete pace; } Empty(); }
//***************************************************************************
//
// SetStatusAndReturnOK
//
// If there is an error, it dumps an error message. It also sets the status
//
//***************************************************************************
HRESULT SetStatusAndReturnOK(SCODE sc, IWbemObjectSink* pSink, char * pMsg) { if(sc != S_OK && pMsg) ERRORTRACE((LOG_WBEMCORE, "SecurityMethod failed doing %s, sc = 0x%x", pMsg, sc)); pSink->SetStatus(0,sc, NULL, NULL); return S_OK; }
//***************************************************************************
//
// DumpErrorMsgAndReturn
//
// Dumps out an error message
//
//***************************************************************************
HRESULT DumpErrorMsgAndReturn(SCODE sc, char * pMsg) { if(pMsg) ERRORTRACE((LOG_WBEMCORE, "%s, sc = 0x%x", pMsg, sc)); return sc; }
//***************************************************************************
//
// CWbemNamespace::GetSDMethod
//
// Implements the GetSD method. This method returns the security descriptor
//
//***************************************************************************
HRESULT CWbemNamespace::GetSDMethod(IWbemClassObject* pOutParams) { // Load up the return object with the security descriptor
SCODE sc = EnsureSecurity(); if(sc != S_OK) return DumpErrorMsgAndReturn(sc, "GetSDMethod failed creating a SD");
CNtSecurityDescriptor &sd = GetSDRef();
sc = CopySDIntoProperty(L"SD", sd, pOutParams); return sc;
}
//***************************************************************************
//
// SetSDMethod
//
// Implements the SetSD method. This method sets the security descriptor
//
//***************************************************************************
HRESULT CWbemNamespace::RecursiveSDMerge() { // Enumerate the child namespaces
CSynchronousSink* pSyncSink = CSynchronousSink::Create(); if(pSyncSink == NULL) return WBEM_E_OUT_OF_MEMORY; pSyncSink->AddRef(); CReleaseMe rm4(pSyncSink);
HRESULT hres = CRepository::ExecQuery ( m_pSession, m_pNsHandle, L"select * from __Namespace", pSyncSink, WBEM_FLAG_DEEP ); if(FAILED(hres)) return hres;
pSyncSink->Block();
// For each child
for(int i = 0; i < pSyncSink->GetObjects().GetSize(); i++) {
// Get the child namespace
CWbemNamespace* pNewNs = CWbemNamespace::CreateInstance();
if (pNewNs == NULL) { return WBEM_E_OUT_OF_MEMORY; } CReleaseMe rm2((IWbemServices *)pNewNs);
VARIANT var; VariantInit(&var); CWbemObject* pObj = (CWbemObject*)pSyncSink->GetObjects().GetAt(i); hres = pObj->Get(L"name", 0, &var, NULL, NULL); if(SUCCEEDED(hres) && var.vt == VT_BSTR && var.bstrVal && m_pThisNamespace) { CClearMe cm(&var); DWORD dwLen = wcslen(m_pThisNamespace) + wcslen(var.bstrVal) + 2; // SEC:REVIEWED 2002-03-22 : OK, Nulls are provably there
WCHAR * pNewName = new WCHAR[dwLen]; if(pNewName == NULL) return WBEM_E_OUT_OF_MEMORY; CDeleteMe<WCHAR> dm(pNewName); StringCchCopyW(pNewName, dwLen, m_pThisNamespace); StringCchCatW(pNewName, dwLen, L"\\"); StringCchCatW(pNewName, dwLen, var.bstrVal);
hres = pNewNs->Initialize(pNewName, NULL, m_dwSecurityFlags, m_dwPermission, m_bForClient, FALSE, NULL, 0xFFFFFFFF, FALSE, NULL); if(FAILED(hres)) return hres; // Merge parents SD into the child
if(pNewNs->IsNamespaceSDProtected()) continue;
hres = SetSecurityForNS(pNewNs->m_pSession, pNewNs->m_pNsHandle, m_pSession, m_pNsHandle, TRUE); if(FAILED(hres)) return hres; // Recursively call the child
hres = pNewNs->RecursiveSDMerge(); if (FAILED(hres)) return hres; } } return S_OK; }
BOOL IsProtected(CNtSecurityDescriptor & sd) { PSECURITY_DESCRIPTOR pActual = sd.GetPtr(); if(pActual == NULL) return FALSE;
SECURITY_DESCRIPTOR_CONTROL Control; DWORD dwRevision; BOOL bRet = GetSecurityDescriptorControl(pActual, &Control, &dwRevision); if(bRet == FALSE) return FALSE;
if(Control & SE_DACL_PROTECTED) return TRUE; else return FALSE;
}
BOOL CWbemNamespace::IsNamespaceSDProtected() { // Get the SD for this namespace
HRESULT hRes = EnsureSecurity(); if(FAILED(hRes)) return FALSE;
// check the control flag
return IsProtected(m_sd);
}
HRESULT StripOutInheritedAces(CNtSecurityDescriptor &sd) {
// Get the DACL
CNtAcl * DestAcl; DestAcl = sd.GetDacl(); if(DestAcl == FALSE) return WBEM_E_INVALID_PARAMETER; CDeleteMe<CNtAcl> dm(DestAcl);
// enumerate through the aces
DWORD dwNumAces = DestAcl->GetNumAces(); BOOL bChanged = FALSE; for(long nIndex = (long)dwNumAces-1; nIndex >= 0; nIndex--) { CNtAce *pAce = DestAcl->GetAce(nIndex); if(pAce && CNtAce::NoError == pAce->GetStatus()) { long lFlags = pAce->GetFlags(); if(lFlags & INHERITED_ACE) { DestAcl->DeleteAce(nIndex); bChanged = TRUE; delete pAce; } } } if(bChanged) sd.SetDacl(DestAcl); return S_OK; }
HRESULT CWbemNamespace::GetParentsInheritableAces(CNtSecurityDescriptor &sd) { // Get the parent namespace's SD
if(m_pThisNamespace == NULL) return WBEM_E_CRITICAL_ERROR;
// Start by figuring out what the parents name is. Do this by copying the namespace name,
// then nulling out the last back slash.
int iLen = wcslen(m_pThisNamespace); // SEC:REVIEWED 2002-03-22 : OK, Null is provably there
WCHAR * pParentName = new WCHAR[iLen + 1]; if(pParentName == NULL) return WBEM_E_OUT_OF_MEMORY;
CDeleteMe<WCHAR> dm(pParentName); StringCchCopyW(pParentName, iLen + 1, m_pThisNamespace);
BOOL bFoundBackSlash = FALSE; WCHAR * pTest = pParentName+iLen-1;
for (; pTest >= pParentName; pTest--) { if ( *pTest == '\\' || *pTest == '/' ) { bFoundBackSlash = TRUE; *pTest = 0; break; } } if(!bFoundBackSlash) return S_OK; // probably already in root
// Open the parent namespace
CWbemNamespace* pNewNs = CWbemNamespace::CreateInstance();
if (pNewNs == NULL) { return WBEM_E_OUT_OF_MEMORY; } IUnknown * pUnk = NULL; HRESULT hres = pNewNs->QueryInterface(IID_IUnknown, (void **)&pUnk); if(FAILED(hres)) return hres; pNewNs->Release(); // ref count held by pUnk
CReleaseMe rm2(pUnk);
hres = pNewNs->Initialize(pParentName, NULL, m_dwSecurityFlags, m_dwPermission, m_bForClient, FALSE, NULL, 0xFFFFFFFF, FALSE, NULL); if(FAILED(hres)) return hres;
hres = pNewNs->EnsureSecurity(); if(FAILED(hres)) return FALSE;
// Go through the parents dacl and add and inheritiable aces to ours.
hres = CopyInheritAces(sd, pNewNs->m_sd); return hres; }
HRESULT CWbemNamespace::SetSDMethod(IWbemClassObject* pInParams) {
// Make sure that there is an input argument
if(pInParams == NULL) return DumpErrorMsgAndReturn(WBEM_E_INVALID_PARAMETER, "SetSD failed due to null pInParams");
// Get the security descriptor argument
CNtSecurityDescriptor sd; HRESULT hr = GetSDFromProperty(L"SD", sd, pInParams); if(FAILED(hr)) return hr;
// Check to make sure the SD is valid before attempting to store it
// CNtSecurityDescriptor does this via IsValidSecurityDescriptor so
// all we need to do is check the status of sd before continuing.
// NT RAID#: 152990 [marioh]
if ( sd.GetStatus() != CNtSecurityDescriptor::NoError ) return WBEM_E_INVALID_OBJECT;
//
// Reject SecurityDescriptors with NULL Owner or NULL group
//
// This is temporarily removed since _SOMEONE_ decided we need
// to RI yesterday and test wasnt quite done with smoking this.
//
CNtSid *pTmpSid = sd.GetOwner ( ) ; CNtSid *pTmpSid2 = sd.GetGroup ( ) ; CDeleteMe<CNtSid> owner ( pTmpSid ) ; CDeleteMe<CNtSid> group ( pTmpSid2 ) ;
if ( pTmpSid == NULL || pTmpSid2 == NULL ) { return WBEM_E_FAILED ; } if (CNtSid::NoError != pTmpSid->GetStatus() || CNtSid::NoError != pTmpSid2->GetStatus()) { return WBEM_E_FAILED; } // Some editors return inherited aces, and others dont. Strip the inherited ones so
// that we have a consistent SD.
StripOutInheritedAces(sd);
//
// Make sure to order the ACEs that are in the pInparams.
// NT Bug: 515545 [marioh]
//
CNtAcl* pAcl = sd.GetDacl ( ) ; CDeleteMe <CNtAcl> dacl ( pAcl ) ; CNtAcl* pOrderedAcl = NULL ; CDeleteMe <CNtAcl> ordDacl ( pOrderedAcl ) ;
if ( pAcl != NULL ) { //
// Order the DACL.
//
pOrderedAcl = pAcl->OrderAces ( ) ; if ( pOrderedAcl == NULL ) { return WBEM_E_FAILED ; }
//
// Now, set the DACL to the newly ordered one.
//
if ( sd.SetDacl ( pOrderedAcl ) == FALSE ) { return WBEM_E_FAILED ; } }
// Get the inherited aces from the parent
if(!IsProtected(sd)) GetParentsInheritableAces(sd);
// Store the sd.
hr = StoreSDIntoNamespace(m_pSession, m_pNsHandle, sd); if(FAILED(hr)) return hr;
hr = RecursiveSDMerge(); return hr; }
//***************************************************************************
//
// IsAceValid()
//
// Does a sanity check on aces
//
//***************************************************************************
bool IsAceValid(DWORD dwMask, DWORD dwType, DWORD dwFlag) { bool bOK = true; if(dwMask & WBEM_FULL_WRITE_REP && ((dwMask & WBEM_PARTIAL_WRITE_REP) == 0 || (dwMask & WBEM_WRITE_PROVIDER) == 0)) { bOK = false; return false; }
// DONT allow INHERIT_ONLY_ACE with out CONTAINER_INHERIT
DWORD dwTemp = dwFlag & (INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE); if(dwTemp == INHERIT_ONLY_ACE) bOK = false;
DWORD dwBadAccess = dwMask & ~(FULL_RIGHTS); DWORD dwBadFlag = dwFlag & ~(CONTAINER_INHERIT_ACE | NO_PROPAGATE_INHERIT_ACE | INHERIT_ONLY_ACE | INHERITED_ACE); if(dwBadFlag || dwBadAccess) bOK = false;
if((dwType != 0) && (dwType != 1)) bOK = false;
if(!bOK) ERRORTRACE((LOG_WBEMCORE, "Got passed a bad ace, dwMask=0x%x, dwType=0x%x, dwFlag=0x%x", dwMask, dwType, dwFlag));
return bOK; }
//***************************************************************************
//
// GetCallerAccessRightsMethod
//
// Implements the GetCallerAccessRights methods. It returns the access rignts
// of the current caller.
//
//***************************************************************************
HRESULT CWbemNamespace::GetCallerAccessRightsMethod(IWbemClassObject* pOutParams) { VARIANT var; var.vt = VT_I4;
var.lVal = m_dwPermission; //
// Instead of using the 'saved' permission set from original namespace connect
// we get the caller access rights every time. This is to avoid the scenario
// whereby user A connects and then sets the proxyblanket to user B and makes
// a call to GetCallerAccessRights. Without getting the access rights each time
// we would end of with A's access rights.
//
var.lVal = GetUserAccess ( ) ; //var.lVal = m_dwPermission;
SCODE sc = pOutParams->Put(L"rights" , 0, &var, 0); if(sc != S_OK) return DumpErrorMsgAndReturn(sc, "GetCallerAccessRights failed putting the dwAccesMask property"); return S_OK; }
//***************************************************************************
//
// SecurityMethod
//
// Implements the security methods.
//
//***************************************************************************
HRESULT CWbemNamespace::SecurityMethod(LPWSTR wszMethodName, long lFlags, IWbemClassObject *pInParams, IWbemContext *pCtx, IWbemObjectSink* pSink) { SCODE sc;
// Do some parameter checking
if(pSink == NULL || wszMethodName == NULL) return WBEM_E_INVALID_PARAMETER;
IWbemClassObject * pClass = NULL; IWbemClassObject * pOutClass = NULL; IWbemClassObject* pOutParams = NULL;
// Get the class object
sc = GetObject(L"__SystemSecurity", 0, pCtx, &pClass, NULL); if(sc != S_OK || pClass == NULL) return SetStatusAndReturnOK(sc, pSink, "getting the class object");
// All the methods return data, so create an instance of the
// output argument class.
sc = pClass->GetMethod(wszMethodName, 0, NULL, &pOutClass); pClass->Release(); if(sc != S_OK) return SetStatusAndReturnOK(sc, pSink, "getting the method");
sc = pOutClass->SpawnInstance(0, &pOutParams); pOutClass->Release(); if(sc != S_OK || pOutParams == NULL) return SetStatusAndReturnOK(sc, pSink, "spawning an instance of the output class");
CReleaseMe rm(pOutParams);
// Depending on the actual method, call the appropritate routine
if(!wbem_wcsicmp(wszMethodName, L"GetSD")) { if (!Allowed(READ_CONTROL)) sc = WBEM_E_ACCESS_DENIED; else sc = GetSDMethod(pOutParams); } else if(!wbem_wcsicmp(wszMethodName, L"Get9XUserList")) { sc = WBEM_E_METHOD_DISABLED; } else if(!wbem_wcsicmp(wszMethodName, L"SetSD")) { if (!Allowed(WRITE_DAC)) sc = WBEM_E_ACCESS_DENIED; else sc = SetSDMethod(pInParams); } else if(!wbem_wcsicmp(wszMethodName, L"Set9XUserList")) { sc = WBEM_E_METHOD_DISABLED; } else if(!wbem_wcsicmp(wszMethodName, L"GetCallerAccessRights")) { sc = GetCallerAccessRightsMethod(pOutParams); } else { return SetStatusAndReturnOK(WBEM_E_INVALID_PARAMETER, pSink, "Invalid method name"); } if(sc != S_OK) return SetStatusAndReturnOK(sc, pSink, "calling method");
// Set the return code
VARIANT var; var.vt = VT_I4; var.lVal = 0; // special name for return value.
sc = pOutParams->Put(L"ReturnValue" , 0, &var, 0); if(sc != S_OK) return SetStatusAndReturnOK(sc, pSink, "setting the ReturnCode property");
// Send the output object back to the client via the sink. Then
// release the pointers and free the strings.
sc = pSink->Indicate(1, &pOutParams);
// all done now, set the status
sc = pSink->SetStatus(0,WBEM_S_NO_ERROR,NULL,NULL);
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// GetUserAccess
//
// Determines the allowed access for a user.
//
//***************************************************************************
DWORD CWbemNamespace::GetUserAccess() { DWORD dwRet = 0; if(IsInAdminGroup()) return FULL_RIGHTS;
if(S_OK !=EnsureSecurity()) return dwRet; // nothing!
dwRet = GetNTUserAccess();
if((dwRet & WBEM_REMOTE_ACCESS) == 0) { HANDLE hAccessToken; if(SUCCEEDED(GetAccessToken(hAccessToken))) { BOOL bRemote = IsRemote(hAccessToken); CloseHandle(hAccessToken); if(bRemote) dwRet = 0; } } if(m_pThisNamespace && (wbem_wcsicmp(L"root\\security", m_pThisNamespace) == 0 || wbem_wcsicmp(L"root/security", m_pThisNamespace) == 0)) if((dwRet & READ_CONTROL) == 0) dwRet = 0; return dwRet;
}
//***************************************************************************
//
// GetNTUserAccess
//
// Determines the allowed access for a user.
//
//***************************************************************************
DWORD CWbemNamespace::GetNTUserAccess() {
HANDLE hAccessToken = INVALID_HANDLE_VALUE; if(S_OK != GetAccessToken(hAccessToken)) return FULL_RIGHTS; // Not having a token indicates an internal thread
CCloseHandle cm(hAccessToken);
DWORD dwMask = 0;
if(IsAdmin(hAccessToken)) return FULL_RIGHTS;
// use the SD
GENERIC_MAPPING map; map.GenericRead = 1; map.GenericWrite = 0x1C; map.GenericExecute = 2; map.GenericAll = 0x6001f; PRIVILEGE_SET ps[3]; DWORD dwSize = 3 * sizeof(PRIVILEGE_SET); BOOL bResult; long testbit = 1; for(int iCnt = 0; iCnt < 26; iCnt++, testbit <<= 1) { // dont bother testing bits that we dont use
DWORD dwGranted = 0; if(testbit & (FULL_RIGHTS)) { BOOL bOK = AccessCheck(m_sd.GetPtr(), hAccessToken, testbit, &map, ps, &dwSize, &dwGranted, &bResult); if(bOK && bResult && dwGranted) { // if the right is full repository, make sure the user also gets the lesser write
// access or else the logic for putting/deleting classes will have problems.
if(testbit == WBEM_FULL_WRITE_REP) dwMask |= (WBEM_PARTIAL_WRITE_REP|WBEM_WRITE_PROVIDER); dwMask |= testbit; } } }
return dwMask; }
//***************************************************************************
//
// bool CWbemNamespace::Allowed(DWORD dwRequired)
//
// Description. Tests if the user has the requested permission. This is
// called before something like a WRITE is done. Since nt supports
// supports impersonation, this is always called. For 9X, a simple check
// of the permissions strored at the time of connection is OK.
//
//***************************************************************************
bool CWbemNamespace::Allowed(DWORD dwRequired) { //
// Check for admin first
//
GENERIC_MAPPING map; map.GenericRead = 1; map.GenericWrite = 0x1C; map.GenericExecute = 2; map.GenericAll = 0x6001f; PRIVILEGE_SET ps[3]; DWORD dwSize = 3 * sizeof(PRIVILEGE_SET); BOOL bResult; DWORD dwGranted = 0; BOOL bOK;
HANDLE hAccessToken = INVALID_HANDLE_VALUE;
if(S_OK != GetAccessToken(hAccessToken)) return true; CCloseHandle cm(hAccessToken);
bOK = AccessCheck(m_sdCheckAdmin.GetPtr(), hAccessToken, 1, &map, ps, &dwSize, &dwGranted, &bResult); if(bOK && bResult && dwGranted) return true;
//
// Not an admin. Continue
//
if(EnsureSecurity() != S_OK) return false;
//
// Always include the check for account enabled right.
// NOTE: This is safe. We dont really care about the explicit
// checks for PARTIAL of FULL write below if the account is disabled.
//
// NOTE: Why, oh, why did we go the anti-NT security path???????????????
//
DWORD dwRequiredCheck = dwRequired ; dwRequired |= WBEM_ENABLE ;
// For nt, the current users priviledges are checked on the fly via access check
CInCritSec ics(&m_cs); // grab the cs since we are using the security desc. // SEC:REVIEWED 2002-03-22 : Assumes entry
if(IsRemote(hAccessToken)) { //
// Check to see if the user is remote enabled before continuing. If they are not
// remote enabled, we fail (except in admin cases).
//
dwGranted = 0 ; bResult = FALSE ; bOK = AccessCheck(m_sd.GetPtr(), hAccessToken, WBEM_REMOTE_ACCESS, &map, ps, &dwSize, &dwGranted, &bResult); if ( !bOK || !bResult || !dwGranted ) { return IsAdmin(hAccessToken) ? true : false ; } }
bOK = AccessCheck(m_sd.GetPtr(), hAccessToken, dwRequired, &map, ps, &dwSize, &dwGranted, &bResult); bool bRet = (bOK && bResult && dwGranted);
// Having full repository write gives access to the "lower" write capabilities. So if the lower
// right is rejected, double check for the full access right.
if(bRet == false && (dwRequiredCheck == WBEM_PARTIAL_WRITE_REP || dwRequiredCheck == WBEM_WRITE_PROVIDER)) { bOK = AccessCheck(m_sd.GetPtr(), hAccessToken, WBEM_FULL_WRITE_REP|WBEM_ENABLE, &map, ps, &dwSize, &dwGranted, &bResult); bRet = (bOK && bResult && dwGranted); } if(bRet == FALSE) bRet = TRUE == IsAdmin(hAccessToken);
return bRet; }
//***************************************************************************
//
// HRESULT CWbemNamespace::InitializeSD()
//
// Description. Creates the SD
//
//***************************************************************************
HRESULT CWbemNamespace::InitializeSD(IWmiDbSession *pSession) { HRESULT hr; if (pSession == NULL) { hr = CRepository::GetDefaultSession(&pSession); if (FAILED(hr)) return hr; } else pSession->AddRef(); CReleaseMe relMe2(pSession); IWbemClassObject * pThisNSObject = NULL;
//AutoRevert av; // switches to system and back to client
BOOL bWasImpersonating = WbemIsImpersonating(); if( bWasImpersonating ) { if (FAILED(hr = CoRevertToSelf())) return hr; } //
// Lets disable security checks here. This is a special case. If we didnt do this
// a connection to a namespace would fail if the user didnt have the right to see
// the security descriptor.
//
AutoRevertSecTlsFlag secFlag ( (LPVOID)0 ) ; hr = CRepository::GetObject(pSession, m_pNsHandle, L"__thisnamespace=@", WBEM_FLAG_USE_SECURITY_DESCRIPTOR | WMIDB_FLAG_ADMIN_VERIFIED, &pThisNSObject); if(FAILED(hr)) { if(bWasImpersonating) { if (FAILED(CoImpersonateClient())) { hr = WBEM_E_FAILED ; } } return hr; } CReleaseMe rm1(pThisNSObject); hr = GetSDFromProperty(L"SECURITY_DESCRIPTOR", m_sd, pThisNSObject);
if(bWasImpersonating) { if ( FAILED (CoImpersonateClient())) { hr = WBEM_E_FAILED ; } } return hr ; }
//***************************************************************************
//
// HRESULT CWbemNamespace::EnsureSecurity()
//
// Description. Generally doesnt do anything except for the first time
//
//***************************************************************************
HRESULT CWbemNamespace::EnsureSecurity() { SCODE sc = S_OK; CInCritSec cs(&m_cs); // SEC:REVIEWED 2002-03-22 : Assumes entry
if(m_bSecurityInitialized) return S_OK;
sc = InitializeSD(NULL); if(sc == S_OK) m_bSecurityInitialized = true; return sc; }
CBaseAce * ConvertOldObjectToAce(IWbemClassObject * pObj, bool bGroup) { // Get the properties out of the old object
CVARIANT vName; pObj->Get(L"Name", 0, &vName, 0, 0); LPWSTR pName = NULL; if(vName.GetType() != VT_BSTR) return NULL; // ignore this one.
pName = LPWSTR(vName);
CVARIANT vDomain; LPWSTR pDomain = L"."; pObj->Get(L"Authority", 0, &vDomain, 0, 0); if(vDomain.GetType() == VT_BSTR) pDomain = LPWSTR(vDomain);
bool bEditSecurity = false; bool bEnabled = false; bool bExecMethods = false;
DWORD dwMask = 0; CVARIANT vEnabled; CVARIANT vEditSecurity; CVARIANT vExecMethods; CVARIANT vPermission;
pObj->Get(L"Enabled", 0, &vEnabled, 0, 0); pObj->Get(L"EditSecurity", 0, &vEditSecurity, 0, 0); pObj->Get(L"ExecuteMethods", 0, &vExecMethods, 0, 0); pObj->Get(L"Permissions", 0, &vPermission, 0, 0);
if (vEnabled.GetType() != VT_NULL && vEnabled.GetBool()) bEnabled = true;
if (vEditSecurity.GetType() != VT_NULL && vEditSecurity.GetBool()) bEditSecurity = true;
if (vExecMethods.GetType() != VT_NULL && vExecMethods.GetBool()) bExecMethods = true;
DWORD dwPermission = 0; if (vPermission.GetType() != VT_NULL && vPermission.GetLONG() > dwPermission) dwPermission = vPermission.GetLONG();
// Now translate the old settings into new ones
if(bEnabled) dwMask = WBEM_ENABLE | WBEM_REMOTE_ACCESS | WBEM_WRITE_PROVIDER;
if(bEditSecurity) dwMask |= READ_CONTROL;
if(bEditSecurity && dwPermission > 0) dwMask |= WRITE_DAC;
if(bExecMethods) dwMask |= WBEM_METHOD_EXECUTE;
if(dwPermission >= 1) dwMask |= WBEM_PARTIAL_WRITE_REP;
if(dwPermission >= 2) dwMask |= WBEM_FULL_WRITE_REP | WBEM_PARTIAL_WRITE_REP | WBEM_WRITE_PROVIDER;
// By default, CNtSid will look up the group name from either the local machine,
// the domain, or a trusted domain. So we need to be explicit
WString wc; if(pDomain) if(wbem_wcsicmp(pDomain, L".") ) { wc = pDomain; wc += L"\\"; } wc += pName;
// under m1, groups that were not enabled were just ignored. Therefore the bits
// cannot be transfer over since m3 has allows and denies, but no noops. Also,
// win9x doesnt have denies, do we want to noop those users also.
if(!bEnabled && bGroup) dwMask = 0;
// In general, m1 just supported allows. However, a user entry that was not enabled was
// treated as a deny. Note that win9x does not allow actual denies.
DWORD dwType = ACCESS_ALLOWED_ACE_TYPE;
if(!bGroup && !bEnabled) { dwMask |= (WBEM_ENABLE | WBEM_REMOTE_ACCESS | WBEM_WRITE_PROVIDER); dwType = ACCESS_DENIED_ACE_TYPE; } CNtSid Sid(wc, NULL); if(Sid.GetStatus() != CNtSid::NoError) { ERRORTRACE((LOG_WBEMCORE, "Error converting m1 security ace, name = %S, error = 0x%x", wc, Sid.GetStatus())); return NULL; } CNtAce * pace = new CNtAce(dwMask, dwType, CONTAINER_INHERIT_ACE, Sid); if (pace && CNtAce::NoError != pace->GetStatus()) { delete pace; pace = NULL; } return pace; }
//***************************************************************************
//
// BOOL IsRemote()
//
// Description. returns true if the box is NT and the caller remote
//
//***************************************************************************
BOOL IsRemote(HANDLE hToken) { PSID pRawSid; SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY; BOOL bRet = TRUE; if(AllocateAndInitializeSid( &id, 1, // SEC:REVIEWED 2002-03-22 : OK
SECURITY_INTERACTIVE_RID, 0, 0,0,0,0,0,0,&pRawSid)) // S-1-5-4
{ CNtSid Sid(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError == Sid.GetStatus()) { if(CNtSecurity::IsUserInGroup(hToken, Sid)) bRet = FALSE; } }
//
//Add proper check for remotness. In addition to the INTERACTIVE group,
//we also check NETWORK_RID membership
//
if ( bRet ) { if(AllocateAndInitializeSid( &id, 1, // SEC:REVIEWED 2002-03-22 : OK
SECURITY_NETWORK_RID, 0, 0,0,0,0,0,0,&pRawSid)) // S-1-5-4
{ CNtSid Sid(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError == Sid.GetStatus()) { if(!CNtSecurity::IsUserInGroup(hToken, Sid)) bRet = FALSE; } } } return bRet; }
HRESULT AddDefaultRootAces(CNtAcl * pacl) { PSID pRawSid; SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
if(AllocateAndInitializeSid( &id, 2, // SEC:REVIEWED 2002-03-22 : OK
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0,&pRawSid)) { CNtSid SidAdmin(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError != SidAdmin.GetStatus()) return WBEM_E_FAILED; DWORD dwMask = FULL_RIGHTS; wmilib::auto_ptr<CNtAce> pace( new CNtAce(dwMask, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, SidAdmin)); if ( NULL == pace.get() ) return WBEM_E_OUT_OF_MEMORY; if (CNtAce::NoError != pace->GetStatus()) return WBEM_E_OUT_OF_MEMORY;
pacl->AddAce(pace.get()); }
//
// Add ACE's for NETWORK_SERVICE ACCOUNT. These accounts have the following rights:
// 1. WBEM_ENABLE
// 2. WBEM_METHOD_EXECUTE
// 3. WBEM_WRITE_PROVIDER
//
DWORD dwAccessMaskNetworkLocalService = WBEM_ENABLE | WBEM_METHOD_EXECUTE | WBEM_WRITE_PROVIDER ;
if(AllocateAndInitializeSid( &id, 1, // SEC:REVIEWED 2002-03-22 : OK
SECURITY_NETWORK_SERVICE_RID,0,0,0,0,0,0,0,&pRawSid)) { CNtSid SidUsers(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError != SidUsers.GetStatus()) return WBEM_E_FAILED; wmilib::auto_ptr<CNtAce> pace( new CNtAce(dwAccessMaskNetworkLocalService, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, SidUsers)); if ( NULL == pace.get() ) return WBEM_E_OUT_OF_MEMORY; if (CNtAce::NoError != pace->GetStatus()) return WBEM_E_OUT_OF_MEMORY;
pacl->AddAce(pace.get()); }
//
// Add ACE's for NETWORK_SERVICE ACCOUNT. These accounts have the following rights:
// 1. WBEM_ENABLE
// 2. WBEM_METHOD_EXECUTE
// 3. WBEM_WRITE_PROVIDER
//
if(AllocateAndInitializeSid( &id, 1, // SEC:REVIEWED 2002-03-22 : OK
SECURITY_LOCAL_SERVICE_RID,0,0,0,0,0,0,0,&pRawSid)) { CNtSid SidUsers(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError != SidUsers.GetStatus()) return WBEM_E_FAILED; wmilib::auto_ptr<CNtAce> pace( new CNtAce(dwAccessMaskNetworkLocalService, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, SidUsers)); if (NULL == pace.get()) return WBEM_E_OUT_OF_MEMORY; if (CNtAce::NoError != pace->GetStatus()) return WBEM_E_OUT_OF_MEMORY;
pacl->AddAce(pace.get()); }
SID_IDENTIFIER_AUTHORITY id2 = SECURITY_WORLD_SID_AUTHORITY; // SEC:REVIEWED 2002-03-22 : OK
if(AllocateAndInitializeSid( &id2, 1, // SEC:REVIEWED 2002-03-22 : OK
0,0,0,0,0,0,0,0,&pRawSid)) { CNtSid SidUsers(pRawSid); FreeSid(pRawSid); if (CNtSid::NoError != SidUsers.GetStatus()) return WBEM_E_FAILED; DWORD dwMask = WBEM_ENABLE | WBEM_METHOD_EXECUTE | WBEM_WRITE_PROVIDER; wmilib::auto_ptr<CNtAce> pace( new CNtAce(dwMask, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, SidUsers)); if (NULL == pace.get()) return WBEM_E_OUT_OF_MEMORY; if (CNtAce::NoError != pace->GetStatus()) return WBEM_E_OUT_OF_MEMORY;
pacl->AddAce(pace.get()); } return S_OK; }
HRESULT CopySDIntoProperty(LPWSTR pPropName, CNtSecurityDescriptor &sd, IWbemClassObject *pThisNSObject) { if (sd.GetStatus() != CNtSecurityDescriptor::NoError) return WBEM_E_FAILED;
// move the SD into a variant.
SAFEARRAY FAR* psa; SAFEARRAYBOUND rgsabound[1]; rgsabound[0].lLbound = 0; long lSize = sd.GetSize(); rgsabound[0].cElements = lSize; psa = SafeArrayCreate( VT_UI1, 1 , rgsabound ); if(psa == NULL) return DumpErrorMsgAndReturn(WBEM_E_FAILED, "GetSDMethod failed creating a safe array");
char * pData = NULL; SCODE sc = SafeArrayAccessData(psa, (void HUGEP* FAR*)&pData); if(sc != S_OK) return DumpErrorMsgAndReturn(sc, "GetSDMethod failed accessing safe array data");
memcpy(pData, sd.GetPtr(), lSize); // SEC:REVIEWED 2002-03-22 : OK
SafeArrayUnaccessData(psa); VARIANT var; var.vt = VT_UI1|VT_ARRAY; var.parray = psa;
sc = pThisNSObject->Put(pPropName , 0, &var, 0);
VariantClear(&var); return sc; }
HRESULT GetSDFromProperty(LPWSTR pPropName, CNtSecurityDescriptor &sd, IWbemClassObject *pThisNSObject) { // Get the security descriptor argument
HRESULT hRes = S_OK ;
_variant_t var; SCODE sc = pThisNSObject->Get(pPropName , 0, &var, NULL, NULL); if (sc != S_OK) { CVARIANT vPath; pThisNSObject->Get(L"__PATH", 0, &vPath, 0, 0); DEBUGTRACE((LOG_WBEMCORE, "Getting SD from %S failed due to code 0x%X\n", V_BSTR(&vPath), sc)); return WBEM_E_CRITICAL_ERROR; }
if(var.vt != (VT_ARRAY | VT_UI1)) { CVARIANT vPath; pThisNSObject->Get(L"__PATH", 0, &vPath, 0, 0); DEBUGTRACE((LOG_WBEMCORE, "Getting SD from %S failed due to incorrect VARIANT type\n", V_BSTR(&vPath) )); return WBEM_E_INVALID_PARAMETER; }
SAFEARRAY * psa = V_ARRAY(&var); PSECURITY_DESCRIPTOR pSD = NULL; sc = SafeArrayAccessData(psa, (void HUGEP* FAR*)&pSD); if (FAILED(sc)) return DumpErrorMsgAndReturn(WBEM_E_INVALID_PARAMETER, "SetSD failed trying accessing SD property");
OnDelete<SAFEARRAY *,HRESULT (*)(SAFEARRAY *),SafeArrayUnaccessData> unacc(psa);
if (0 == psa->rgsabound[0].cElements) return WBEM_E_INVALID_PARAMETER; if (!IsValidSecurityDescriptor(pSD)) return WBEM_E_INVALID_PARAMETER;
CNtSecurityDescriptor sdNew(pSD);
CNtSid *pTmpSid = sdNew.GetOwner(); if ( pTmpSid == NULL ) { ERRORTRACE((LOG_WBEMCORE, "ERROR: Security descriptor was retrieved and it had no owner\n")); } delete pTmpSid;
pTmpSid = sdNew.GetGroup(); if (pTmpSid == NULL ) { ERRORTRACE((LOG_WBEMCORE, "ERROR: Security descriptor was retrieved and it had no group\n")); } delete pTmpSid; sd = sdNew; if ( sd.GetStatus ( ) != CNtSecurityDescriptor::NoError ) { hRes = WBEM_E_OUT_OF_MEMORY ; }
return hRes ; }
HRESULT CopyInheritAces(CNtSecurityDescriptor & sd, CNtSecurityDescriptor & sdParent) { // Get the acl list for both SDs
CNtAcl * pacl = sd.GetDacl(); if(pacl == NULL) return WBEM_E_OUT_OF_MEMORY; CDeleteMe<CNtAcl> dm0(pacl);
CNtAcl * paclParent = sdParent.GetDacl(); if(paclParent == NULL) return WBEM_E_OUT_OF_MEMORY; CDeleteMe<CNtAcl> dm1(paclParent);
int iNumParent = paclParent->GetNumAces(); for(int iCnt = 0; iCnt < iNumParent; iCnt++) { CNtAce *pParentAce = paclParent->GetAce(iCnt); if (pParentAce == NULL) return WBEM_E_OUT_OF_MEMORY; CDeleteMe<CNtAce> dm2(pParentAce); if (CNtAce::NoError != pParentAce->GetStatus()) continue;
long lFlags = pParentAce->GetFlags(); if(lFlags & CONTAINER_INHERIT_ACE) {
if(lFlags & NO_PROPAGATE_INHERIT_ACE) lFlags ^= CONTAINER_INHERIT_ACE; lFlags |= INHERITED_ACE;
// If this is an inherit only ace we need to clear this
// in the children.
// NT RAID: 161761 [marioh]
if ( lFlags & INHERIT_ONLY_ACE ) lFlags ^= INHERIT_ONLY_ACE;
pParentAce->SetFlags(lFlags); pacl->AddAce(pParentAce); } } sd.SetDacl(pacl); return S_OK; }
HRESULT StoreSDIntoNamespace(IWmiDbSession * pSession, IWmiDbHandle *pNSToSet, CNtSecurityDescriptor & sd) { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check to make sure the SD DACL is valid before attempting to put
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CNtAcl* pAcl = sd.GetDacl ( ); if ( NULL == pAcl ) return WBEM_E_INVALID_PARAMETER;
CDeleteMe<CNtAcl> dm (pAcl); if ( !IsValidAclForNSSecurity ( pAcl ) ) { return WBEM_E_INVALID_PARAMETER; } AutoRevertSecTlsFlag secFlag ( (LPVOID) 0 ) ; IWbemClassObject * pThisNSObject = NULL; HRESULT hr = CRepository::GetObject(pSession, pNSToSet, L"__thisnamespace=@", WBEM_FLAG_USE_SECURITY_DESCRIPTOR, &pThisNSObject); if(FAILED(hr)) return hr; CReleaseMe rm1(pThisNSObject);
hr = CopySDIntoProperty(L"SECURITY_DESCRIPTOR", sd, pThisNSObject); if(FAILED(hr)) return hr; return CRepository::PutObject(pSession, pNSToSet, IID_IWbemClassObject, pThisNSObject, WMIDB_DISABLE_EVENTS | WBEM_FLAG_USE_SECURITY_DESCRIPTOR); }
HRESULT SetSecurityForNS(IWmiDbSession * pSession, IWmiDbHandle *pNSToSet, IWmiDbSession * pParentSession, IWmiDbHandle * pNSParent, BOOL bExisting) { IWbemClassObject * pThisNSObject = NULL;
// Get the __thisnamespace object
AutoRevertSecTlsFlag secFlag ( (LPVOID) 0 ) ; HRESULT hr = CRepository::GetObject(pSession, pNSToSet, L"__thisnamespace=@", WBEM_FLAG_USE_SECURITY_DESCRIPTOR, &pThisNSObject); if(FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failed to get __thisnamespace=@ object for current namespace <0x%X>!\n", hr)); return hr; } CReleaseMe rm1(pThisNSObject);
// Create the new SD
CNtSecurityDescriptor sd; CNtAcl DestAcl;
if(bExisting) { // Fill in the security descriptor
hr = GetSDFromProperty(L"SECURITY_DESCRIPTOR", sd, pThisNSObject); if(FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure in GetSDFromProperty <0x%X>!\n", hr)); return hr; } hr = StripOutInheritedAces (sd); if ( FAILED (hr) ) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure in StripOutInheritedAces <0x%X>!\n", hr)); return hr; }
} else { // NT RAID: 198935 Prefix [marioh]
if ( !SetOwnerAndGroup(sd) ) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure in SetOwnerAndGroup <0x%X>!\n", hr)); return WBEM_E_FAILED; }
if ( !sd.SetDacl(&DestAcl) ) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure in SetDacl <0x%X>!\n", hr)); return WBEM_E_FAILED; } }
CNtAcl * pacl = sd.GetDacl(); if (pacl == NULL) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure in GetDacl <0x%X>!\n", hr)); return WBEM_E_FAILED; }
CDeleteMe<CNtAcl> del1(pacl);
if(pNSParent == NULL) { // If there is no parent, this must be root. Create a default one
hr = AddDefaultRootAces(pacl); if (FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure for AddDefaultRootAces <0x%X>!\n", hr)); return hr; } BOOL bRet = sd.SetDacl(pacl); if (bRet == FALSE) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure for SetDacl (2) <0x%X>!\n", hr)); return WBEM_E_FAILED ; } } else { // Get the parents __thisnamespace
IWbemClassObject * pParentThisNSObject = NULL; hr = CRepository::GetObject(pParentSession, pNSParent, L"__thisnamespace=@", WBEM_FLAG_USE_SECURITY_DESCRIPTOR, &pParentThisNSObject); if(FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failed to get __thisnamespace=@ object for parent namespace <0x%X>!\n", hr)); return hr; } CReleaseMe rm11(pParentThisNSObject); CNtSecurityDescriptor sdParent; hr = GetSDFromProperty(L"SECURITY_DESCRIPTOR", sdParent, pParentThisNSObject); if(FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure for GetSDFromProperty <0x%X>!\n", hr)); return hr; } hr = CopyInheritAces(sd, sdParent); if (FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure for CopyInheritAces <0x%X>!\n", hr)); return hr; } } if(FAILED(hr)) return hr;
hr = CopySDIntoProperty(L"SECURITY_DESCRIPTOR", sd, pThisNSObject); if(FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure for CopySDIntoProperty <0x%X>!\n", hr)); return hr; }
// Extract sd property once more.
// ==============================
CNtSecurityDescriptor VerifiedSd; hr = GetSDFromProperty(L"SECURITY_DESCRIPTOR", VerifiedSd, pThisNSObject);
if (FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failure for GetSDFromProperty (2) <0x%X>!\n", hr)); CVARIANT vPath; pThisNSObject->Get(L"__PATH", 0, &vPath, 0, 0); DEBUGTRACE((LOG_WBEMCORE, "Error creating security descriptor for new namespace %S", V_BSTR(&vPath) )); return WBEM_E_CRITICAL_ERROR; }
// Go ahead and store the object.
// ==============================
hr = CRepository::PutObject(pSession, pNSToSet, IID_IWbemClassObject, pThisNSObject, WMIDB_DISABLE_EVENTS | WBEM_FLAG_USE_SECURITY_DESCRIPTOR);
if (FAILED(hr)) { ERRORTRACE((LOG_WBEMCORE, "SetSecurityForNS: Failed to put secured object back <0x%X>!\n", hr)); } return hr; }
//***************************************************************************
//
// IsValidAclForNSSecurity
//
// Checks the ACEs for the following:
// 2. Standard NT ACE correctness [IsValidAce]
// 1. ACE inheritance/propogation flag correctness for WMI namespace
// security
//
// Parameters:
// <CNtAcl&> ACL to be checked
//
// Return:
// TRUE if ACL is valid
// FALSE if ACL is invalid
//
//***************************************************************************
BOOL IsValidAclForNSSecurity (CNtAcl* pAcl) { BOOL bRet = TRUE;
// Standard NT ACL check
if (!pAcl->IsValid()) return FALSE; // Loop through all the ACEs in the list
ULONG ulNum = pAcl->GetNumAces( ); for ( ULONG ulCnt = 0; ulCnt < ulNum; ulCnt++ ) { CNtAce* pAce = pAcl->GetAce ( ulCnt ); if (NULL == pAce) return FALSE; CDeleteMe<CNtAce> autoDel ( pAce ); if ( !IsAceValid ( pAce->GetAccessMask(), pAce->GetType(), pAce->GetFlags() ) ) { return FALSE; } }
return bRet; }
|