|
|
//***************************************************************************
//
// (c) 2000-2001 by Microsoft Corp. All Rights Reserved.
//
// repsecurity.cpp
//
// a-davcoo 25-Jan-00 Created to house repository security
// functionality.
//
//***************************************************************************
#define _REPSECURITY_CPP_
#pragma warning( disable : 4786 ) // identifier was truncated to 'number' characters in the
#pragma warning( disable : 4251 ) // needs to have dll-interface to be used by clients of class
#define DBINITCONSTANTS // Initialize OLE constants...
#define INITGUID // ...once in each app.
#define _WIN32_DCOM
#include "precomp.h"
#include <std.h>
#include <sqlexec.h>
#include <repdrvr.h>
#include <wbemint.h>
#include <math.h>
#include <resource.h>
#include <reputils.h>
#include <crc64.h>
#include <smrtptr.h>
#include <winntsec.h>
//***************************************************************************
//
// CWmiDbSession::GetObjectSecurity
//
//***************************************************************************
HRESULT CWmiDbSession::GetObjectSecurity (CSQLConnection *pConn, SQL_ID dObjectId, PNTSECURITY_DESCRIPTOR *ppSD, DWORD dwSDLength, DWORD dwFlags, BOOL &bHasDacl) { HRESULT hr = WBEM_S_NO_ERROR;
// Retrieve this object and populate the SD.
bHasDacl = FALSE;
// See if this ID even has an SD.
if (!((CWmiDbController *)m_pController)->HasSecurityDescriptor(dObjectId)) return WBEM_S_NO_ERROR; if(IsInAdminGroup()) return WBEM_S_NO_ERROR;
BOOL bNeedToRel = FALSE; if (!pConn) { hr = GetSQLCache()->GetConnection(&pConn, FALSE, FALSE); bNeedToRel = TRUE; }
if (SUCCEEDED(hr)) { PNTSECURITY_DESCRIPTOR pSD = NULL;
// For now, we will never cache the __SECURITY_DESCRIPTOR property. Always
// retrieve it from a proc call.
hr = CSQLExecProcedure::GetSecurityDescriptor(pConn, dObjectId, &pSD, dwSDLength, dwFlags); if (hr == WBEM_E_NOT_FOUND) { bHasDacl = FALSE; hr = WBEM_S_NO_ERROR; } else { PACL pDACL=NULL; BOOL bDefaulted, bTemp; BOOL bSuccess=GetSecurityDescriptorDacl (pSD, &bTemp, &pDACL, &bDefaulted); if (!bSuccess) { hr = WBEM_E_FAILED; } else { bHasDacl = (bTemp && pDACL!=NULL); } }
if (bHasDacl) *ppSD = pSD; else { *ppSD = NULL; delete pSD; }
if (bNeedToRel) GetSQLCache()->ReleaseConnection(pConn, hr, FALSE); }
return hr; }
//***************************************************************************
//
// CWmiDbSession::VerifyObjectSecurity()
//
// This method verifies that the caller has the requested access to the
// target object. If the reqested access can be granted, a successfull
// result is returned. If not, WBEM_E_ACCESS_DENIED is returned. This
// method will check the entire security inheritance tree, as required.
//
//***************************************************************************
HRESULT CWmiDbSession::VerifyObjectSecurity(CSQLConnection *pConn, IWmiDbHandle __RPC_FAR *pTarget, DWORD AccessType) { CWmiDbHandle *pObject=(CWmiDbHandle *)pTarget; HRESULT hr=VerifyObjectSecurity (pConn, pObject->m_dObjectId, pObject->m_dClassId, pObject->m_dScopeId, 0, AccessType);
return hr; }
//***************************************************************************
//
// CWmiDbSession::VerifyObjectSecurity()
//
// This method verifies that the caller has the requested access to the
// target object. If the reqested access can be granted, a successfull
// result is returned. If not, WBEM_E_ACCESS_DENIED is returned. This
// method will check the entire security inheritance tree, as required.
// The ScopeId and ScopeClassId are allowed to be 0 when calling this
// method.
//
//***************************************************************************
HRESULT CWmiDbSession::VerifyObjectSecurity(CSQLConnection *pConn, SQL_ID ObjectId, SQL_ID ClassId, SQL_ID ScopeId, SQL_ID ScopeClassId, DWORD AccessType) { // Validate the state of the session and initialize the schema cache if required.
if (!m_pController || ((CWmiDbController *)m_pController)->m_dwCurrentStatus == WBEM_E_SHUTTING_DOWN) { return WBEM_E_SHUTTING_DOWN; }
if (!((CWmiDbController *)m_pController)->m_bCacheInit) { HRESULT hr=LoadSchemaCache(); if (SUCCEEDED(hr)) ((CWmiDbController *)m_pController)->m_bCacheInit=TRUE; else return hr; }
// Obtain the object's effective security descriptor and see if access to the object
// is granted. Access to administrators is always granted.
HRESULT hr=WBEM_S_NO_ERROR; if (!((CWmiDbController *)m_pController)->m_bIsAdmin) { PNTSECURITY_DESCRIPTOR pSD=NULL; DWORD dwSDLength;
hr=GetEffectiveObjectSecurity (pConn, ObjectId, ClassId, ScopeId, ScopeClassId, &pSD, dwSDLength); if (SUCCEEDED(hr) && pSD) { BOOL bHasDACL; hr=AccessCheck (pSD, AccessType, bHasDACL);
delete pSD; } }
return hr; }
//***************************************************************************
//
// CWmiDbSession::VerifyObjectSecurity()
//
// Used internally to the repository driver, currently only by PutObject().
//
//***************************************************************************
HRESULT CWmiDbSession::VerifyObjectSecurity(CSQLConnection *pConn, SQL_ID dScopeID, SQL_ID dScopeClassId, LPWSTR lpObjectPath, CWbemClassObjectProps *pProps, DWORD dwHandleType, DWORD dwReqAccess, SQL_ID &dObjectId, SQL_ID &dClassId) { // Validate the state of the session and initialize the schema cache if required.
if (!m_pController || ((CWmiDbController *)m_pController)->m_dwCurrentStatus == WBEM_E_SHUTTING_DOWN) { return WBEM_E_SHUTTING_DOWN; }
if (!((CWmiDbController *)m_pController)->m_bCacheInit) { HRESULT hr=LoadSchemaCache(); if (SUCCEEDED(hr)) ((CWmiDbController *)m_pController)->m_bCacheInit=TRUE; else return hr; }
// If we have an objectid, then the security has already been validated. We can just
// return the classid.
HRESULT hr=WBEM_S_NO_ERROR; if (dObjectId && !dClassId) { hr=GetSchemaCache()->GetClassID(pProps->lpClassName, dScopeID, dClassId); } else { // Otherwise, we don't have an objectid and we'll need to get it from the database
// or from the schema cache.
hr=GetSchemaCache()->GetClassID(pProps->lpClassName, dScopeID, dClassId); if (pProps->dwGenus==WBEM_GENUS_CLASS) { dObjectId=dClassId; }
// Make sure the parent class isn't locked.
if (!dClassId) { hr=GetSchemaCache()->GetClassID(pProps->lpSuperClass, dScopeID, dClassId); }
// If not found, make sure we can access the class.
BOOL bNewObj=(!dObjectId); if (bNewObj) { hr=VerifyClassSecurity(pConn, dClassId, dwReqAccess); } else { hr=VerifyObjectSecurity(pConn, dObjectId, dClassId, dScopeID, dScopeClassId, dwReqAccess); if (SUCCEEDED(hr)) { // Make sure we aren't blocked by any other handle type.
bool bImmediate=!((dwHandleType & 0xF0000000)==WMIDB_HANDLE_TYPE_SUBSCOPED); hr=((CWmiDbController *)m_pController)->LockCache.AddLock(bImmediate, dObjectId, WMIDB_HANDLE_TYPE_EXCLUSIVE, NULL, dScopeID, dClassId, &((CWmiDbController *)m_pController)->SchemaCache, false, 0, 0);
if (SUCCEEDED(hr)) { hr=((CWmiDbController *)m_pController)->LockCache.DeleteLock(dObjectId, false, WMIDB_HANDLE_TYPE_EXCLUSIVE); } } } }
// Done.
return hr; }
//***************************************************************************
//
// CWmiDbSession::VerifyClassSecurity()
//
// This method verifies that the caller has the requested access to the
// target class. If the reqested access can be granted, a successfull
// result is returned. If not, WBEM_E_ACCESS_DENIED is returned. This
// method will check the entire security inheritance tree, as required.
//
//***************************************************************************
HRESULT CWmiDbSession::VerifyClassSecurity(CSQLConnection *pConn, SQL_ID ClassId, DWORD AccessType) { return VerifyObjectSecurity (pConn, ClassId, 1, 0, 0, AccessType); }
//***************************************************************************
//
// CWmiDbSession::GetEffectiveObjectSecurity()
//
// This method searches for the SD which should be applied to verification
// of access against the specified object. This SD could be the one directly
// associated with the object, or if the object does not have a SD with a
// DACL, it will be the inherited SD. Note, ScopeId and ScopeClassId can
// be 0 when calling this function.
//
//***************************************************************************
HRESULT CWmiDbSession::GetEffectiveObjectSecurity(CSQLConnection *pConn, SQL_ID ObjectId, SQL_ID ClassId, SQL_ID ScopeId, SQL_ID ScopeClassId, PNTSECURITY_DESCRIPTOR *ppSD, DWORD &dwSDLength ) { // Attempt to get the security descriptor directly associated with the object.
HRESULT hr = 0; BOOL bHasDACL; if (ClassId == 1) hr = GetObjectSecurity (pConn, ObjectId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_CLASS, bHasDACL); else hr = GetObjectSecurity (pConn, ObjectId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_INSTANCE, bHasDACL);
// If the object did not have a security descriptor, get it's inherited security descriptor.
if (SUCCEEDED(hr) && !bHasDACL) { hr=GetInheritedObjectSecurity (pConn, ObjectId, ClassId, ScopeId, ScopeClassId, ppSD, dwSDLength); }
// Done.
return hr; }
//***************************************************************************
//
// CWmiDbSession::GetInheritedObjectSecurity()
//
// This method determines what security descriptor (if any) would be
// inherited by a target object, if the target object had no security
// descriptor. Note that ScopeId and ScopeClassId are allowed to be 0
// when calling this method.
//
//***************************************************************************
HRESULT CWmiDbSession::GetInheritedObjectSecurity(CSQLConnection *pConn, SQL_ID ObjectId, SQL_ID ClassId, SQL_ID ScopeId, SQL_ID ScopeClassId, PNTSECURITY_DESCRIPTOR *ppSD, DWORD &dwSDLength) { // Determine whether the target object is a class or an instance. Handling
// of instances includes handling of __Instances containers. Note, a target
// with a ClassId of 1 is a class object and the object ID is the class's
// class ID.
HRESULT hr=WBEM_S_NO_ERROR; if (ClassId==1) { hr=GetInheritedClassSecurity (pConn, ObjectId, ScopeId, ScopeClassId, ppSD, dwSDLength); } else { hr=GetInheritedInstanceSecurity (pConn, ObjectId, ClassId, ScopeId, ScopeClassId, ppSD, dwSDLength); }
// Done.
return hr; }
//***************************************************************************
//
// CWmiDbSession::GetInheritedClassSecurity()
//
// This method determines what security descriptor (if any) would be inherited
// by a target class, if the target class had no security descriptor. This
// function will work properly for classes scoped to an object, although this
// isn't currently being supported in the rest of WMI. The ScopeId and the
// ScopeClassId are allowed to be 0 when using this method.
//
//***************************************************************************
HRESULT CWmiDbSession::GetInheritedClassSecurity(CSQLConnection *pConn, SQL_ID ClassId, SQL_ID ScopeId, SQL_ID ScopeClassId, PNTSECURITY_DESCRIPTOR *ppSD, DWORD &dwSDLength) { // Create some convience variables.
CSchemaCache *pSchema=&((CWmiDbController *)m_pController)->SchemaCache; // Iterate through the chain of superclasses, until a security descriptor
// is found, or the top-level base class is reached.
HRESULT hr=WBEM_S_NO_ERROR; bool bTopReached=false; BOOL bHasDACL = FALSE; SQL_ID dParentId=ClassId; do { // Attempt to locate the superclass.
hr=pSchema->GetParentId (dParentId, dParentId); if (SUCCEEDED(hr) && dParentId != 1) { hr = GetObjectSecurity(pConn, dParentId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_CLASS, bHasDACL); } else if (hr==WBEM_E_NOT_FOUND || dParentId == 1) { // Top of the class hierarchy has been reached.
hr=WBEM_S_NO_ERROR; bTopReached=true; } } while (SUCCEEDED(hr) && !bTopReached && !bHasDACL);
// If we are at the top of the hierarchy, check security on __Classes for this
// namespace.
if (bTopReached && !bHasDACL) { IWmiDbHandle *pClassesObj = NULL;
_bstr_t sName; if (ScopeId && SUCCEEDED(pSchema->GetNamespaceName(ScopeId, &sName))) { sName += L":__Classes=@"; SQL_ID dClassSec = CRC64::GenerateHashValue(sName); hr = GetObjectSecurity(pConn, dClassSec, ppSD, dwSDLength, 0, bHasDACL); } }
// Done.
return hr; }
//***************************************************************************
//
// CWmiDbSession::GetInheritedInstanceSecurity()
//
// This method determines what security descriptor (if any) would be inher-
// ited by a target instance, if the target instance had no SD. This method
// works for both real instances and instances of __Instances containers.
// The ScopeId and the ScopeClassId are allowed to be 0 when using this
// method.
//
//***************************************************************************
HRESULT CWmiDbSession::GetInheritedInstanceSecurity(CSQLConnection *pConn, SQL_ID ObjectId, SQL_ID ClassId, SQL_ID ScopeId, SQL_ID ScopeClassId, PNTSECURITY_DESCRIPTOR *ppSD, DWORD &dwSDLength) { // Setup some convience variables.
CSchemaCache *pSchema=&((CWmiDbController *)m_pController)->SchemaCache;
// See if the target is an __Instances container. If so, it's security is
// inherited from the __Instances containers of parent classes, and finaly
// from it's scoping object (owning namespace).
HRESULT hr=WBEM_S_NO_ERROR; if (ClassId==INSTANCESCLASSID) { hr=GetInheritedContainerSecurity (pConn, ObjectId, ScopeId, ScopeClassId, ppSD, dwSDLength); } else { // See if the target object is a namespace. If it is, then do not attempt
// to inherit security from parent namespaces. The security inheritance tree
// effictively stops at the first namespace.
/// A-DAVCOO: Of course we should inherit security from the parent namespaces.
/// A-DAVCOO: But, those namespaces might not even be in the same repository.
/// A-DAVCOO: Hence, that case needs to be handled outside of this driver.
if (ClassId!=NAMESPACECLASSID && !pSchema->IsDerivedClass (NAMESPACECLASSID, ClassId)) { // Determine the parent scope/namespace as well as it's class, if they
// were not supplied by the caller.
if (!ScopeId) { // We do not have the scope, so get the scope and it's class.
hr=pSchema->GetParentNamespace (ObjectId, ScopeId, &ScopeClassId); } else if (!ScopeClassId) { // We have the scope, but not the scope's class.
hr=pSchema->GetNamespaceClass (ScopeId, ScopeClassId); }
// If the instance is scoped directly to a namespace, then we first walk
// the instance's __Instances container chain. Note, that an __Instances
// container's object ID is the class ID it represents.
if (SUCCEEDED(hr)) { if (ScopeClassId==NAMESPACECLASSID || pSchema->IsDerivedClass (NAMESPACECLASSID, ScopeClassId)) { // Check the class's immediate __Instances container.
BOOL bHasDACL; hr = GetObjectSecurity (pConn, ClassId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_INSTANCE, bHasDACL); if (SUCCEEDED(hr)) { // The class's __Instances container did not supply a DACL, so get
// the __Instances container's inherited security.
hr=GetInheritedContainerSecurity (pConn, ClassId, 0, 0, ppSD, dwSDLength); } } else { // The instance is not scoped immediately to a namespace derived class,
// so, check the security of it's scoping object.
BOOL bHasDACL; hr = GetObjectSecurity (pConn, ScopeId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_INSTANCE, bHasDACL); if (SUCCEEDED(hr)) { // Scoping object had no DACL, so use it's inherited security.
hr=GetInheritedInstanceSecurity (pConn, ScopeId, ScopeClassId, 0, 0, ppSD, dwSDLength); } } } } else { BOOL bHasDACL = FALSE;
// Check security on this namespace object.
hr = GetObjectSecurity (pConn, ScopeId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_INSTANCE, bHasDACL); } }
// Done.
return hr; }
//***************************************************************************
//
// CWmiDbSession::GetInheritedContainerSecurity()
//
// This method determines what security descriptor (if any) would be inherited
// by a target __Instances container, if the target container had no security
// descriptor. Note, that as a side effect of the alogrithm used, this method
// will work for __Instances containers scoped to an object as well as to a
// namespace, if such beasts ever exist for the purposes of scoping a class to
// an instance. The ScopeId and the ScopeClassId are allowed to be 0 when
// using this method.
//
//***************************************************************************
HRESULT CWmiDbSession::GetInheritedContainerSecurity(CSQLConnection *pConn, SQL_ID ClassId, SQL_ID ScopeId, SQL_ID ScopeClassId, PNTSECURITY_DESCRIPTOR *ppSD, DWORD &dwSDLength) { // Setup some convience variables.
CSchemaCache *pSchema=&((CWmiDbController *)m_pController)->SchemaCache;
// Iterate through the chain of superclasses, checking each __Instances
// container for an appropriate security descriptor and DACL.
HRESULT hr=WBEM_S_NO_ERROR; BOOL bHasDACL=false; bool bTopReached=false; SQL_ID dParentId=ClassId; do { // Attempt to locate the superclass.
hr=pSchema->GetParentId (dParentId, dParentId); if (SUCCEEDED(hr)) { // A class's class ID is used for it's __Instances conatiner's object ID.
hr = GetObjectSecurity(pConn, dParentId, ppSD, dwSDLength, WMIDB_SECURITY_FLAG_INSTANCE, bHasDACL); } else if (hr==WBEM_E_NOT_FOUND) { // Top of the class hierarchy has been reached.
hr = WBEM_S_NO_ERROR; bTopReached = true; } } while (SUCCEEDED(hr) && !bHasDACL && !bTopReached);
return hr; }
//***************************************************************************
//
// CSecurityCache::AccessCheck()
//
// This function returns a successful result if the requested access to the
// object is granted, an error result if an error occurs while validating
// access, or WMI_E_ACCESS_DENIED if access was sucessfully verified but
// denied.
//
// NOTE: If an object does not have and SD or it has a NULL DACL, access
// is granted, but, bHasDACL is set to FALSE.
//
//***************************************************************************
HRESULT CWmiDbSession::AccessCheck( PNTSECURITY_DESCRIPTOR pSD, DWORD dwAccessType, BOOL &bHasDACL ) { bHasDACL=TRUE;
// An object without an SD grants all access. The caller should check bHasDACL and
// inherit security as appropriate.
HRESULT hr=WBEM_S_NO_ERROR; if (pSD==NULL) { bHasDACL=FALSE; } else { // If the object has an SD, but no DACL, then all access is granted. The caller
// should check bHasDACL and propagate the check upwards as necessary.
PACL pDACL=NULL; BOOL bDefaulted; BOOL bSuccess=GetSecurityDescriptorDacl (pSD, &bHasDACL, &pDACL, &bDefaulted); if (!bSuccess) { hr=WBEM_E_FAILED; } else if (!bHasDACL || pDACL==NULL) { bHasDACL=FALSE; } else { bHasDACL=TRUE;
HRESULT hr2 = CoImpersonateClient(); if (SUCCEEDED(hr)) { // Because we have called CoImpersonateClient(), the thread will have an access
// token (inferred from Okuntseff, p151). Threads don't have one by default.
HANDLE hClientToken=NULL; HANDLE hThread = GetCurrentThread(); bSuccess=OpenThreadToken (hThread, TOKEN_READ | TOKEN_QUERY , TRUE, &hClientToken); if (!bSuccess) { DWORD dwRet = GetLastError(); if (dwRet == ERROR_NO_TOKEN) { // No thread token. Look at process tokens.
HANDLE hProc = GetCurrentProcess(); HANDLE hProcToken; bSuccess = OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE , &hProcToken); if (bSuccess) { bSuccess = DuplicateToken (
hProcToken, SecurityDelegation , & hClientToken ) ;
CloseHandle ( hProcToken ) ; }
CloseHandle(hProc); } if (!bSuccess) { DEBUGTRACE((LOG_WBEMCORE, "OpenThreadToken failed with %ld", dwRet)); hr=WBEM_E_FAILED; } } if (bSuccess) { GENERIC_MAPPING accessmap; DWORD dw1 = 0, dw2 = 1;
accessmap.GenericWrite=0; accessmap.GenericRead=0; accessmap.GenericExecute=0; accessmap.GenericAll=0;
MapGenericMask(&dwAccessType, &accessmap); PRIVILEGE_SET ps[10]; dw1 = 10 * sizeof(PRIVILEGE_SET);
DWORD PsSize, GrantedAccess; BOOL AccessStatus;
bSuccess = ::AccessCheck( pSD, // security descriptor
hClientToken, // handle to client access token
dwAccessType, // requested access rights
&accessmap, // map generic to specific rights
ps, // receives privileges used
&dw1, // size of privilege-set buffer
&dw2, // retrieves mask of granted rights
&AccessStatus // retrieves results of access check
);
if (!bSuccess) { DWORD dwRet = GetLastError(); DEBUGTRACE((LOG_WBEMCORE, "AccessCheck failed with %ld", dwRet)); hr=WBEM_E_FAILED; } else if (!AccessStatus) { hr=WBEM_E_ACCESS_DENIED; }
CloseHandle (hClientToken); } CloseHandle(hThread);
CoRevertToSelf(); } } }
return hr;
}
|