Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1513 lines
46 KiB

/*++
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;
}