windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/xfiles/win9xsecurity.cpp
2020-09-26 16:20:57 +08:00

1362 lines
38 KiB
C++

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
Win9xSecurity.cpp
Abstract:
This class handles the importing of Win9x security data that was extracted from an old MMF format repository.
History:
03/17/2001 shbrown - created
--*/
#include "precomp.h"
#include <wbemcomn.h>
#include "Win9xSecurity.h"
#include <oahelp.inl>
bool CWin9xSecurity::Win9xBlobFileExists()
{
wchar_t wszFilePath[MAX_PATH+1];
if (!GetRepositoryDirectory(wszFilePath))
return false;
wcscat(wszFilePath, BLOB9X_FILENAME);
DWORD dwAttributes = GetFileAttributesW(wszFilePath);
if (dwAttributes != -1)
{
DWORD dwMask = FILE_ATTRIBUTE_DEVICE |
FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_OFFLINE |
FILE_ATTRIBUTE_REPARSE_POINT |
FILE_ATTRIBUTE_SPARSE_FILE;
if (!(dwAttributes & dwMask))
return true;
}
return false;
}
HRESULT CWin9xSecurity::ImportWin9xSecurity()
{
HRESULT hRes = WBEM_S_NO_ERROR;
wchar_t wszFilePath[MAX_PATH+1];
if (GetRepositoryDirectory(wszFilePath))
{
wcscat(wszFilePath, BLOB9X_FILENAME);
m_h9xBlobFile = CreateFileW(wszFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
// get a session and begin a transaction
CSession* pSession = new CSession(m_pControl);
if (pSession == NULL)
return WBEM_E_OUT_OF_MEMORY;
pSession->AddRef();
CReleaseMe relMe(pSession);
hRes = pSession->BeginWriteTransaction(0);
if (FAILED(hRes))
{
return hRes;
}
// process the file
if (m_h9xBlobFile != INVALID_HANDLE_VALUE)
{
try
{
hRes = DecodeWin9xBlobFile();
}
catch (...)
{
ERRORTRACE((LOG_WBEMCORE, "Traversal of Win9x security data import file failed\n"));
hRes = WBEM_E_FAILED;
}
CloseHandle(m_h9xBlobFile);
}
else
{
ERRORTRACE((LOG_WBEMCORE, "Could not open the Win9x security data import file for reading\n"));
hRes = WBEM_E_FAILED;
}
if (SUCCEEDED(hRes))
{
if (DeleteWin9xBlobFile())
hRes = pSession->CommitTransaction(0);
else
{
ERRORTRACE((LOG_WBEMCORE, "Win9x security data import completed but failed to delete import file\n"));
pSession->AbortTransaction(0);
hRes = WBEM_E_FAILED;
}
}
else
{
ERRORTRACE((LOG_WBEMCORE, "Win9x security data import failed to complete\n"));
pSession->AbortTransaction(0);
}
return hRes;
}
HRESULT CWin9xSecurity::DecodeWin9xBlobFile()
{
HRESULT hRes = WBEM_S_NO_ERROR;
// read the file header
if (!ReadWin9xHeader())
return WBEM_E_FAILED;
// import the file
BLOB9X_SPACER header;
DWORD dwSize;
while (hRes == WBEM_S_NO_ERROR)
{
// this loop will be exited when we either...
// - successfully process the whole import file, or
// - encounter an error
dwSize = 0;
if ((ReadFile(m_h9xBlobFile, &header, sizeof(header), &dwSize, NULL) == 0) || (dwSize != sizeof(header)))
{
hRes = WBEM_E_FAILED;
}
else if ((header.dwSpacerType == BLOB9X_TYPE_SECURITY_INSTANCE) ||
(header.dwSpacerType == BLOB9X_TYPE_SECURITY_BLOB))
{
hRes = ProcessWin9xBlob(&header);
}
else if (header.dwSpacerType == BLOB9X_TYPE_END_OF_FILE)
{
break;
}
else
{
hRes = WBEM_E_FAILED;
}
}
if (SUCCEEDED(hRes))
hRes = RecursiveInheritSecurity(NULL, L"root"); // force namespaces to inherit their inheritable security settings
return hRes;
}
bool CWin9xSecurity::ReadWin9xHeader()
{
BLOB9X_HEADER header;
DWORD dwSize = 0;
if ((ReadFile(m_h9xBlobFile, &header, sizeof(header), &dwSize, NULL) == 0) || (dwSize != sizeof(header)))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to retrieve the Win9x import file header information\n"));
return false;
}
if (strncmp(header.szSignature, BLOB9X_SIGNATURE, 9) != 0)
{
ERRORTRACE((LOG_WBEMCORE, "The import file is not a Win9x import file\n"));
return false;
}
return true;
}
HRESULT CWin9xSecurity::ProcessWin9xBlob(BLOB9X_SPACER* pHeader)
{
if (pHeader->dwNamespaceNameSize == 0)
{
ERRORTRACE((LOG_WBEMCORE, "Namespace name size is zero in Win9x import blob\n"));
return WBEM_E_FAILED;
}
if (pHeader->dwBlobSize == 0)
{
ERRORTRACE((LOG_WBEMCORE, "Blob size is zero in Win9x import blob\n"));
return WBEM_E_FAILED;
}
// read the namespace name
wchar_t* wszNamespaceName = new wchar_t[pHeader->dwNamespaceNameSize];
if (!wszNamespaceName)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<wchar_t> delMe1(wszNamespaceName);
DWORD dwSize = 0;
if ((ReadFile(m_h9xBlobFile, wszNamespaceName, pHeader->dwNamespaceNameSize, &dwSize, NULL) == 0) || (dwSize != pHeader->dwNamespaceNameSize))
return WBEM_E_FAILED;
// read the parent namespace name if it exists
wchar_t* wszParentClass = NULL;
if (pHeader->dwParentClassNameSize)
{
wszParentClass = new wchar_t[pHeader->dwParentClassNameSize];
if (!wszParentClass)
return WBEM_E_OUT_OF_MEMORY;
dwSize = 0;
if ((ReadFile(m_h9xBlobFile, wszParentClass, pHeader->dwParentClassNameSize, &dwSize, NULL) == 0) || (dwSize != pHeader->dwParentClassNameSize))
{
delete [] wszParentClass;
return WBEM_E_FAILED;
}
}
CVectorDeleteMe<wchar_t> delMe2(wszParentClass);
// read in the blob
char* pObjectBlob = new char[pHeader->dwBlobSize];
if (!pObjectBlob)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<char> delMe3(pObjectBlob);
dwSize = 0;
if ((ReadFile(m_h9xBlobFile, pObjectBlob, pHeader->dwBlobSize, &dwSize, NULL) == 0) || (dwSize != pHeader->dwBlobSize))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to read Win9x security blob for namespace %S\n", wszNamespaceName));
return WBEM_E_FAILED;
}
// get handle to the namespace so it can be used below
CNamespaceHandle* pNamespaceHandle = new CNamespaceHandle(m_pControl, m_pRepository);
if (!pNamespaceHandle)
return WBEM_E_OUT_OF_MEMORY;
pNamespaceHandle->AddRef();
CReleaseMe relme(pNamespaceHandle);
HRESULT hRes = pNamespaceHandle->Initialize(wszNamespaceName);
if (SUCCEEDED(hRes))
{
// process the blob according to its type
if (pHeader->dwSpacerType == BLOB9X_TYPE_SECURITY_INSTANCE)
hRes = ProcessWin9xSecurityInstance(pNamespaceHandle, wszParentClass, pObjectBlob, pHeader->dwBlobSize);
else // (pHeader->dwSpacerType == BLOB9X_TYPE_SECURITY_BLOB)
hRes = ProcessWin9xSecurityBlob(pNamespaceHandle, wszNamespaceName, pObjectBlob);
}
return hRes;
}
HRESULT CWin9xSecurity::ProcessWin9xSecurityInstance(CNamespaceHandle* pNamespaceHandle, wchar_t* wszParentClass, char* pObjectBlob, DWORD dwBlobSize)
{
// get parent class from the repository
_IWmiObject* pParentClass = 0;
HRESULT hRes = pNamespaceHandle->GetObjectByPath(wszParentClass, 0, IID_IWbemClassObject, (LPVOID*)&pParentClass);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to retrieve class %S from the repository; HRESULT = %#lx\n", wszParentClass, hRes));
return hRes;
}
CReleaseMe relMe1(pParentClass);
// merge object blob with parent class to produce instance
_IWmiObject* pInstance = 0;
hRes = pParentClass->Merge(WMIOBJECT_MERGE_FLAG_INSTANCE, dwBlobSize, pObjectBlob, &pInstance);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Unable to merge instance; HRESULT = %#lx\n", hRes));
return hRes;
}
CReleaseMe relMe2(pInstance);
// convert security class instance to ACE
bool bGroup = false;
if(wbem_wcsicmp(L"__ntlmgroup", wszParentClass) == 0)
bGroup = true;
CNtAce* pAce = ConvertOldObjectToAce(pInstance, bGroup);
if(!pAce)
{
ERRORTRACE((LOG_WBEMCORE, "Unable to convert old security instance to ACE"));
return WBEM_E_FAILED;
}
CDeleteMe<CNtAce> delMe(pAce);
// store the ACE
hRes = StoreAce(pAce);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Unable to store ACE; HRESULT = %#lx\n", hRes));
return hRes;
}
return hRes;
}
// this function was stolen from coredll\secure.cpp with minor modifications to remove calls to IsNT()
CNtAce* CWin9xSecurity::ConvertOldObjectToAce(_IWmiObject* 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(_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);
return pace;
}
HRESULT CWin9xSecurity::StoreAce(CNtAce* pAce)
{
// get handle to the root namespace
CNamespaceHandle* pRootNamespaceHandle = new CNamespaceHandle(m_pControl, m_pRepository);
if (!pRootNamespaceHandle)
return WBEM_E_OUT_OF_MEMORY;
pRootNamespaceHandle->AddRef();
CReleaseMe relme1(pRootNamespaceHandle);
HRESULT hRes = pRootNamespaceHandle->Initialize(L"root");
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to connect to namespace; HRESULT = %#lx\n", hRes));
return hRes;
}
// get root namespace SD
CNtSecurityDescriptor sdRoot;
hRes = GetSDFromNamespace(pRootNamespaceHandle, sdRoot);
if (FAILED(hRes))
return hRes;
// Delete all entries in the SD with the same name
wchar_t* wszAccountName;
hRes = pAce->GetFullUserName2(&wszAccountName);
if(FAILED(hRes))
return hRes;
CVectorDeleteMe<wchar_t> delMe(wszAccountName);
if (!StripMatchingEntries(sdRoot, wszAccountName))
return WBEM_E_FAILED;
// add in the new security
if (!AddAceToSD(sdRoot, pAce))
return WBEM_E_FAILED;
// set the security
hRes = SetNamespaceSecurity(pRootNamespaceHandle, sdRoot);
return hRes;
}
bool CWin9xSecurity::StripMatchingEntries(CNtSecurityDescriptor& sd, const wchar_t* wszAccountName)
{
// Get the DACL
CNtAcl* pAcl;
pAcl = sd.GetDacl();
if(!pAcl)
return false;
CDeleteMe<CNtAcl> dm(pAcl);
// enumerate through the aces
DWORD dwNumAces = pAcl->GetNumAces();
BOOL bChanged = FALSE;
HRESULT hRes = WBEM_S_NO_ERROR;
for(long nIndex = (long)dwNumAces-1; nIndex >= 0; nIndex--)
{
CNtAce* pAce = pAcl->GetAce(nIndex);
if(pAce)
{
wchar_t* wszAceListUserName;
hRes = pAce->GetFullUserName2(&wszAceListUserName);
if(FAILED(hRes))
return false;
CVectorDeleteMe<wchar_t> delMe(wszAceListUserName);
if(wbem_wcsicmp(wszAceListUserName, wszAccountName) == 0)
{
if (!pAcl->DeleteAce(nIndex))
return false;
bChanged = TRUE;
}
}
}
if(bChanged)
{
if (!sd.SetDacl(pAcl))
return false;
}
return true;
}
bool CWin9xSecurity::AddAceToSD(CNtSecurityDescriptor& sd, CNtAce* pAce)
{
CNtAcl* pacl = sd.GetDacl();
if(!pacl)
return false;
CDeleteMe<CNtAcl> delMe(pacl);
if (!pacl->AddAce(pAce))
return false;
if (!sd.SetDacl(pacl))
return false;
return true;
}
HRESULT CWin9xSecurity::ProcessWin9xSecurityBlob(CNamespaceHandle* pNamespaceHandle, const wchar_t* wszNamespaceName, const char* pObjectBlob)
{
HRESULT hRes = WBEM_S_NO_ERROR;
// convert the Win9x security blob into a more proper NT security blob
char* pNsSecurity = NULL;
if (!ConvertSecurityBlob(pObjectBlob, &pNsSecurity))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to convert Win9x security blob for namespace %S\n", wszNamespaceName));
return WBEM_E_FAILED;
}
CVectorDeleteMe<char> delMe1(pNsSecurity);
// get the parent namespace name, and if a parent exists, get a pointer to it so it can be used below
CNamespaceHandle* pParentNamespaceHandle = new CNamespaceHandle(m_pControl, m_pRepository);
if (!pParentNamespaceHandle)
return WBEM_E_OUT_OF_MEMORY;
pParentNamespaceHandle->AddRef();
CReleaseMe relme(pParentNamespaceHandle);
wchar_t* wszParentNamespaceName = new wchar_t[wcslen(wszNamespaceName)+1];
if (!wszParentNamespaceName)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<wchar_t> delMe2(wszParentNamespaceName);
wcscpy(wszParentNamespaceName, wszNamespaceName);
wchar_t* pSlash = wcsrchr(wszParentNamespaceName, '\\');
bool bRoot = true;
if (pSlash)
{
bRoot = false;
*pSlash = L'\0';
hRes = pParentNamespaceHandle->Initialize(wszParentNamespaceName);
if (FAILED(hRes))
return hRes;
}
// now transform the old security blob that consisted of a header and array of ACE's
// into a proper Security Descriptor that can be stored in the property
CNtSecurityDescriptor mmfNsSD;
hRes = TransformBlobToSD(bRoot, pParentNamespaceHandle, pNsSecurity, 0, mmfNsSD);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to convert security blob to SD for namespace %S\n", wszNamespaceName));
return hRes;
}
// now set the security
hRes = SetNamespaceSecurity(pNamespaceHandle, mmfNsSD);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to set namespace security for namespace %S\n", wszNamespaceName));
return hRes;
}
return hRes;
}
bool CWin9xSecurity::ConvertSecurityBlob(const char* pOrgNsSecurity, char** ppNewNsSecurity)
{
// convert an old Win9x pseudo-blob into a blob with NT-style ACE's
if (!pOrgNsSecurity || !ppNewNsSecurity)
return false;
DWORD* pdwData = (DWORD*)pOrgNsSecurity;
DWORD dwSize = *pdwData;
pdwData++;
DWORD dwVersion = *pdwData;
if(dwVersion != 1 || dwSize == 0 || dwSize > 64000)
{
ERRORTRACE((LOG_WBEMCORE, "Invalid security blob header\n"));
return false;
}
pdwData++;
DWORD dwStoredAsNT = *pdwData;
if (dwStoredAsNT)
{
ERRORTRACE((LOG_WBEMCORE, "NT security blob detected; should be Win9x\n"));
return false;
}
CFlexAceArray AceList;
if (!AceList.DeserializeWin9xSecurityBlob(pOrgNsSecurity))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to deserialize a Win9x security blob\n"));
return false;
}
// serialize the new WinNT blob
if (!AceList.SerializeWinNTSecurityBlob(ppNewNsSecurity))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to serialize a WinNT security blob\n"));
return false;
}
return true;
}
HRESULT CWin9xSecurity::TransformBlobToSD(bool bRoot, CNamespaceHandle* pParentNamespaceHandle, const char* pNsSecurity, DWORD dwStoredAsNT, CNtSecurityDescriptor& mmfNsSD)
{
// now transform the old security blob that consisted of a header and array of ACE's
// into a proper Security Descriptor that can be stored in the property
// build up an ACL from our blob, if we have one
CNtAcl acl;
if (pNsSecurity)
{
DWORD* pdwData = (DWORD*) pNsSecurity;
pdwData += 3;
int iAceCount = (int)*pdwData;
pdwData += 2;
BYTE* pAceData = (BYTE*)pdwData;
PGENERIC_ACE pAce = NULL;
for (int iCnt = 0; iCnt < iAceCount; iCnt++)
{
pAce = (PGENERIC_ACE)pAceData;
if (!pAce)
{
ERRORTRACE((LOG_WBEMCORE, "Failed to access GENERIC_ACE within security blob\n"));
return WBEM_E_FAILED;
}
CNtAce ace(pAce);
if(ace.GetStatus() != 0)
{
ERRORTRACE((LOG_WBEMCORE, "Failed to construct CNtAce from GENERIC_ACE\n"));
return WBEM_E_FAILED;
}
acl.AddAce(&ace);
if (acl.GetStatus() != 0)
{
ERRORTRACE((LOG_WBEMCORE, "Failed to add ACE to ACL\n"));
return WBEM_E_FAILED;
}
pAceData += ace.GetSize();
}
}
// for Win9x, the security blob for ROOT would not have had any default
// root aces for administrators and everyone, so create them
if (bRoot)
{
if (!AddDefaultRootAces(&acl))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to create default root ACE's\n"));
return WBEM_E_FAILED;
}
}
// a real SD was constructed and passed in by reference, now set it up properly
SetOwnerAndGroup(mmfNsSD);
mmfNsSD.SetDacl(&acl);
if (mmfNsSD.GetStatus() != 0)
{
ERRORTRACE((LOG_WBEMCORE, "Failed to convert namespace security blob to SD\n"));
return WBEM_E_FAILED;
}
// add in the parent's inheritable aces, if this is not ROOT
if (!bRoot)
{
HRESULT hRes = GetParentsInheritableAces(pParentNamespaceHandle, mmfNsSD);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to inherit parent's inheritable ACE's; HRESULT = %#lx\n", hRes));
return hRes;
}
}
return WBEM_S_NO_ERROR;
}
HRESULT CWin9xSecurity::SetNamespaceSecurity(CNamespaceHandle* pNamespaceHandle, CNtSecurityDescriptor& mmfNsSD)
{
if (!pNamespaceHandle)
return WBEM_E_FAILED;
// get the singleton object
IWbemClassObject* pThisNamespace = NULL;
wchar_t* wszThisNamespace = new wchar_t[wcslen(L"__thisnamespace=@")+1];
wcscpy(wszThisNamespace, L"__thisnamespace=@");
HRESULT hRes = pNamespaceHandle->GetObjectByPath(wszThisNamespace, 0, IID_IWbemClassObject, (LPVOID*)&pThisNamespace);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to get singleton namespace object; HRESULT = %#lx\n", hRes));
return hRes;
}
CReleaseMe relMe(pThisNamespace);
// copy SD data into a safearray
SAFEARRAY FAR* psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = mmfNsSD.GetSize();
psa = SafeArrayCreate( VT_UI1, 1 , rgsabound );
if (!psa)
return WBEM_E_OUT_OF_MEMORY;
char* pData = NULL;
hRes = SafeArrayAccessData(psa, (void HUGEP* FAR*)&pData);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed SafeArrayAccessData; HRESULT = %#lx\n", hRes));
return hRes;
}
memcpy(pData, mmfNsSD.GetPtr(), mmfNsSD.GetSize());
hRes = SafeArrayUnaccessData(psa);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed SafeArrayUnaccessData; HRESULT = %#lx\n", hRes));
return hRes;
}
pData = NULL;
// put the safearray into a variant and set the property on the instance
VARIANT var;
var.vt = VT_UI1|VT_ARRAY;
var.parray = psa;
hRes = pThisNamespace->Put(L"SECURITY_DESCRIPTOR" , 0, &var, 0);
VariantClear(&var);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to put SECURITY_DESCRIPTOR property; HRESULT = %#lx\n", hRes));
return hRes;
}
// put back the instance
CEventCollector eventCollector;
hRes = pNamespaceHandle->PutObject(IID_IWbemClassObject, pThisNamespace, WBEM_FLAG_CREATE_OR_UPDATE, NULL, NULL, eventCollector);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to put back singleton instance; HRESULT = %#lx\n", hRes));
return hRes;
}
return hRes;
}
bool CWin9xSecurity::AddDefaultRootAces(CNtAcl * pacl )
{
if (!pacl)
return false;
PSID pRawSid;
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
//
// 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,
SECURITY_NETWORK_SERVICE_RID,0,0,0,0,0,0,0,&pRawSid))
{
CNtSid SidUsers(pRawSid);
FreeSid(pRawSid);
CNtAce * pace = new CNtAce(dwAccessMaskNetworkLocalService, ACCESS_ALLOWED_ACE_TYPE,
CONTAINER_INHERIT_ACE, SidUsers);
if ( NULL == pace )
{
return false;
}
CDeleteMe<CNtAce> dm(pace);
pacl->AddAce(pace);
}
else
{
ERRORTRACE((LOG_WBEMCORE, "ERROR: Unable to add default root aces (SECURITY_NETWORK_SERVICE_RID)\n"));
}
//
// 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,
SECURITY_LOCAL_SERVICE_RID,0,0,0,0,0,0,0,&pRawSid))
{
CNtSid SidUsers(pRawSid);
FreeSid(pRawSid);
CNtAce * pace = new CNtAce(dwAccessMaskNetworkLocalService, ACCESS_ALLOWED_ACE_TYPE,
CONTAINER_INHERIT_ACE, SidUsers);
if ( NULL == pace )
{
return false;
}
CDeleteMe<CNtAce> dm(pace);
pacl->AddAce(pace);
}
else
{
ERRORTRACE((LOG_WBEMCORE, "ERROR: Unable to add default root aces (SECURITY_LOCAL_SERVICE_RID)\n"));
}
// add Administrator
if(AllocateAndInitializeSid( &id, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0,&pRawSid))
{
CNtSid SidAdmin(pRawSid);
FreeSid(pRawSid);
DWORD dwMask = FULL_RIGHTS;
CNtAce * pace = new CNtAce(dwMask, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, SidAdmin);
if ( NULL == pace )
return false;
CDeleteMe<CNtAce> dm(pace);
pacl->AddAce(pace);
if (pacl->GetStatus() != 0)
return false;
}
// add Everyone
SID_IDENTIFIER_AUTHORITY id2 = SECURITY_WORLD_SID_AUTHORITY;
if(AllocateAndInitializeSid( &id2, 1,
0,0,0,0,0,0,0,0,&pRawSid))
{
CNtSid SidUsers(pRawSid);
FreeSid(pRawSid);
DWORD dwMask = WBEM_ENABLE | WBEM_METHOD_EXECUTE | WBEM_WRITE_PROVIDER;
CNtAce * pace = new CNtAce(dwMask, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE, SidUsers);
if ( NULL == pace )
return false;
CDeleteMe<CNtAce> dm(pace);
pacl->AddAce(pace);
if (pacl->GetStatus() != 0)
return false;
}
return true;
}
HRESULT CWin9xSecurity::GetParentsInheritableAces(CNamespaceHandle* pParentNamespaceHandle, CNtSecurityDescriptor &sd)
{
if (!pParentNamespaceHandle)
return WBEM_E_FAILED;
// Get the parent namespace's SD
CNtSecurityDescriptor sdParent;
HRESULT hRes = GetSDFromNamespace(pParentNamespaceHandle, sdParent);
if (FAILED(hRes))
return hRes;
// strip out the inherited aces so we have a consistent SD
if (!StripOutInheritedAces(sd))
return WBEM_E_FAILED;
// Go through the parents dacl and add any inheritable aces to ours.
if (!CopyInheritAces(sd, sdParent))
return WBEM_E_FAILED;
return hRes;
}
HRESULT CWin9xSecurity::GetSDFromNamespace(CNamespaceHandle* pNamespaceHandle, CNtSecurityDescriptor& sd)
{
if (!pNamespaceHandle)
return WBEM_E_FAILED;
// get the singleton object
IWbemClassObject* pThisNamespace = NULL;
wchar_t* wszThisNamespace = new wchar_t[wcslen(L"__thisnamespace=@")+1];
wcscpy(wszThisNamespace, L"__thisnamespace=@");
HRESULT hRes = pNamespaceHandle->GetObjectByPath(wszThisNamespace, 0, IID_IWbemClassObject, (LPVOID*)&pThisNamespace);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to get singleton namespace object; HRESULT = %#lx\n", hRes));
return hRes;
}
CReleaseMe relMe(pThisNamespace);
// Get the security descriptor argument
VARIANT var;
VariantInit(&var);
hRes = pThisNamespace->Get(L"SECURITY_DESCRIPTOR", 0, &var, NULL, NULL);
if (FAILED(hRes))
{
VariantClear(&var);
ERRORTRACE((LOG_WBEMCORE, "Failed to get SECURITY_DESCRIPTOR property; HRESULT = %#lx\n", hRes));
return hRes;
}
if(var.vt != (VT_ARRAY | VT_UI1))
{
VariantClear(&var);
ERRORTRACE((LOG_WBEMCORE, "Failed to get SECURITY_DESCRIPTOR property due to incorrect variant type\n"));
return WBEM_E_FAILED;
}
SAFEARRAY* psa = var.parray;
PSECURITY_DESCRIPTOR pSD;
hRes = SafeArrayAccessData(psa, (void HUGEP* FAR*)&pSD);
if (FAILED(hRes))
{
VariantClear(&var);
ERRORTRACE((LOG_WBEMCORE, "GetSDFromNamespace failed SafeArrayAccessData; HRESULT = %#lx\n", hRes));
return hRes;
}
BOOL bValid = IsValidSecurityDescriptor(pSD);
if (!bValid)
{
VariantClear(&var);
ERRORTRACE((LOG_WBEMCORE, "GetSDFromNamespace retrieved an invalid security descriptor\n"));
return WBEM_E_FAILED;
}
CNtSecurityDescriptor sdNew(pSD);
// Check to make sure the owner and group is not NULL!!!!
CNtSid *pTmpSid = sdNew.GetOwner();
if (pTmpSid == NULL)
{
ERRORTRACE((LOG_WBEMCORE, "Security descriptor was retrieved and it had no owner\n"));
}
delete pTmpSid;
pTmpSid = sdNew.GetGroup();
if (pTmpSid == NULL)
{
ERRORTRACE((LOG_WBEMCORE, "Security descriptor was retrieved and it had no group\n"));
}
delete pTmpSid;
sd = sdNew;
SafeArrayUnaccessData(psa);
VariantClear(&var);
return hRes;
}
bool CWin9xSecurity::StripOutInheritedAces(CNtSecurityDescriptor& sd)
{
// Get the DACL
CNtAcl* pAcl;
pAcl = sd.GetDacl();
if(!pAcl)
return false;
CDeleteMe<CNtAcl> dm(pAcl);
// enumerate through the aces
DWORD dwNumAces = pAcl->GetNumAces();
BOOL bChanged = FALSE;
for(long nIndex = (long)dwNumAces-1; nIndex >= 0; nIndex--)
{
CNtAce *pAce = pAcl->GetAce(nIndex);
CDeleteMe<CNtAce> dm2(pAce);
if(pAce)
{
long lFlags = pAce->GetFlags();
if(lFlags & INHERITED_ACE)
{
pAcl->DeleteAce(nIndex);
bChanged = TRUE;
}
}
}
if(bChanged)
sd.SetDacl(pAcl);
return true;
}
bool CWin9xSecurity::CopyInheritAces(CNtSecurityDescriptor& sd, CNtSecurityDescriptor& sdParent)
{
// Get the acl list for both SDs
CNtAcl * pacl = sd.GetDacl();
if(pacl == NULL)
return false;
CDeleteMe<CNtAcl> dm0(pacl);
CNtAcl * paclParent = sdParent.GetDacl();
if(paclParent == NULL)
return false;
CDeleteMe<CNtAcl> dm1(paclParent);
//
// If parent SD is protected from ACE inheritance only add Local/Network service.
// This can happen during following conditions:
// If an upgrade is done from Win9x and we've stored away the namespace security
// into a temporary file another WMI client can create a namespace and set NS security
// with SE_DACL_PROTECTED AFTER we've stored the file (can you say RSOP??). Now, at the
// point of reboot, we scan the file and populate security for each namespace. Since we
// dont have any namespace security stored for this 'new' namespace, we let it simply inherit
// from parent which is bad since they explicitly set the SE_DACL_PROTECTED control bit. To
// remedy this, we check to see if the SD is protected and if so, do not inherit anything.
//
if ( IsProtected ( sd ) == true )
{
return true ;
}
int iNumParent = paclParent->GetNumAces();
for(int iCnt = 0; iCnt < iNumParent; iCnt++)
{
CNtAce *pParentAce = paclParent->GetAce(iCnt);
CDeleteMe<CNtAce> dm2(pParentAce);
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 true;
}
BOOL CWin9xSecurity::SetOwnerAndGroup(CNtSecurityDescriptor &sd)
{
PSID pRawSid;
BOOL bRet = FALSE;
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
if(AllocateAndInitializeSid( &id, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0,&pRawSid))
{
CNtSid SidAdmins(pRawSid);
bRet = sd.SetGroup(&SidAdmins); // Access check doesn't really care what you put,
// so long as you put something for the owner
if(bRet)
bRet = sd.SetOwner(&SidAdmins);
FreeSid(pRawSid);
return bRet;
}
return bRet;
}
//
// CNamespaceListSink is used by the query in RecursiveInheritSecurity below
//
class CNamespaceListSink : public CUnkBase<IWbemObjectSink, &IID_IWbemObjectSink>
{
CWStringArray &m_aNamespaceList;
public:
CNamespaceListSink(CWStringArray &aNamespaceList)
: m_aNamespaceList(aNamespaceList)
{
}
~CNamespaceListSink()
{
}
STDMETHOD(Indicate)(long lNumObjects, IWbemClassObject** apObjects)
{
HRESULT hRes;
for (int i = 0; i != lNumObjects; i++)
{
if (apObjects[i] != NULL)
{
_IWmiObject *pInst = NULL;
hRes = apObjects[i]->QueryInterface(IID__IWmiObject, (void**)&pInst);
if (FAILED(hRes))
return hRes;
CReleaseMe rm(pInst);
BSTR strKey = NULL;
hRes = pInst->GetKeyString(0, &strKey);
if(FAILED(hRes))
return hRes;
CSysFreeMe sfm(strKey);
if (m_aNamespaceList.Add(strKey) != CWStringArray::no_error)
return WBEM_E_OUT_OF_MEMORY;
}
}
return WBEM_S_NO_ERROR;
}
STDMETHOD(SetStatus)(long lFlags, HRESULT hresResult, BSTR, IWbemClassObject*)
{
return WBEM_S_NO_ERROR;
}
};
HRESULT CWin9xSecurity::RecursiveInheritSecurity(CNamespaceHandle* pParentNamespaceHandle, const wchar_t *wszNamespace)
{
// force namespaces to inherit their inheritable security settings
HRESULT hRes = WBEM_S_NO_ERROR;
// get handle to the namespace
CNamespaceHandle* pNamespaceHandle = new CNamespaceHandle(m_pControl, m_pRepository);
if (!pNamespaceHandle)
return WBEM_E_OUT_OF_MEMORY;
pNamespaceHandle->AddRef();
CReleaseMe relme1(pNamespaceHandle);
hRes = pNamespaceHandle->Initialize(wszNamespace);
if (FAILED(hRes))
{
ERRORTRACE((LOG_WBEMCORE, "Failed to connect to namespace; HRESULT = %#lx\n", hRes));
return hRes;
}
// inherit parent's inheritable security if there is a parent
if (pParentNamespaceHandle)
{
CNtSecurityDescriptor sdNamespace;
hRes = GetSDFromNamespace(pNamespaceHandle, sdNamespace);
if (FAILED(hRes))
return hRes;
hRes = GetParentsInheritableAces(pParentNamespaceHandle, sdNamespace);
if (FAILED(hRes))
return hRes;
hRes = SetNamespaceSecurity(pNamespaceHandle, sdNamespace);
if (FAILED(hRes))
return hRes;
}
//Enumerate child namespaces
CWStringArray aListNamespaces;
CNamespaceListSink* pSink = new CNamespaceListSink(aListNamespaces);
if (!pSink)
return WBEM_E_OUT_OF_MEMORY;
pSink->AddRef();
CReleaseMe relme2(pSink);
if (SUCCEEDED(hRes))
{
IWbemQuery *pQuery = NULL;
hRes = CoCreateInstance(CLSID_WbemQuery, NULL, CLSCTX_INPROC_SERVER, IID_IWbemQuery, (void **)&pQuery);
if (FAILED(hRes))
return hRes;
CReleaseMe relme3(pQuery);
hRes = pQuery->Parse(L"SQL", L"select * from __namespace", 0);
if (FAILED(hRes))
return hRes;
hRes = pNamespaceHandle->ExecQuerySink(pQuery, 0, 0, pSink, NULL);
}
//Work through list and call ourselves with that namespace name
if (SUCCEEDED(hRes))
{
for (int i = 0; i != aListNamespaces.Size(); i++)
{
//Build the full name of this namespace
wchar_t *wszChildNamespace = new wchar_t[wcslen(wszNamespace) + wcslen(aListNamespaces[i]) + wcslen(L"\\") + 1];
if (wszChildNamespace == NULL)
{
hRes = WBEM_E_OUT_OF_MEMORY;
break;
}
CVectorDeleteMe<wchar_t> delMe(wszChildNamespace);
wcscpy(wszChildNamespace, wszNamespace);
wcscat(wszChildNamespace, L"\\");
wcscat(wszChildNamespace, aListNamespaces[i]);
// Do the inherit
hRes = RecursiveInheritSecurity(pNamespaceHandle, wszChildNamespace);
if (FAILED(hRes))
break;
}
}
return hRes;
}
BOOL CWin9xSecurity::DeleteWin9xBlobFile()
{
// delete the file
wchar_t wszFilePath[MAX_PATH+1];
if (!GetRepositoryDirectory(wszFilePath))
return FALSE;
wcscat(wszFilePath, BLOB9X_FILENAME);
return DeleteFileW(wszFilePath);
}
bool CWin9xSecurity::GetRepositoryDirectory(wchar_t wszRepositoryDirectory[MAX_PATH+1])
{
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\WBEM\\CIMOM", 0, KEY_READ, &hKey))
return false;
wchar_t wszTmp[MAX_PATH + 1];
DWORD dwLen = MAX_PATH + 1;
long lRes = RegQueryValueExW(hKey, L"Repository Directory", NULL, NULL, (LPBYTE)wszTmp, &dwLen);
RegCloseKey(hKey);
if(lRes)
return false;
if (ExpandEnvironmentStringsW(wszTmp,wszRepositoryDirectory, MAX_PATH + 1) == 0)
return false;
return true;
}
//***************************************************************************
//
// 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();
}
//***************************************************************************
//
// bool CFlexAceArray::DeserializeWin9xSecurityBlob()
//
// Description. Deserializes the Win9x pseudo-aces out of a blob.
// The blob starts off with 5 dwords preceding the aces themselves:
// <TOTAL SIZE><VERSION><ISNT><ACE_COUNT><RESERVED><ACE> ... <ACE>
//
//***************************************************************************
bool CFlexAceArray::DeserializeWin9xSecurityBlob(const char* pData)
{
if (!pData)
return false;
DWORD* pdwData = (DWORD*)pData;
pdwData += 3;
int iAceCount = (int)*pdwData;
pdwData += 2;
// Set the ace data
BYTE* pAceData = (BYTE*)pdwData;
DWORD dwAceSize = 0;
CBaseAce* pAce = NULL;
for (int iCnt = 0; iCnt < iAceCount; iCnt++)
{
// if the user is preceeded by a ".\" advance the pointer past it
if (_wcsnicmp((WCHAR*)pAceData, L".\\", 2) == 0)
pAceData += 4;
dwAceSize = 2*(wcslen((WCHAR*)pAceData) + 1) + 12;
pAce = new CNtAce();
if (!pAce)
return false;
// Deserialize Win9x pseudo ace into NT ace
pAce->Deserialize(pAceData);
// only add ACE's that we were successful in creating
if (pAce->GetStatus() == 0)
Add(pAce);
pAceData += dwAceSize;
}
return true;
}
//***************************************************************************
//
// bool CFlexAceArray::SerializeWinNTSecurityBlob()
//
// Description. Serializes the WinNT aces into a blob.
// The blob starts off with 5 dwords preceding the aces themselves:
// <TOTAL SIZE><VERSION><ISNT><ACE_COUNT><RESERVED><ACE> ... <ACE>
//
// "version" should be 1.
// "ISNT" should be 1
//
//***************************************************************************
bool CFlexAceArray::SerializeWinNTSecurityBlob(char** ppData)
{
// Start by determining the total size needed
DWORD dwSize = 5 * sizeof(DWORD); // for the header stuff
int iAceCount = Size(); // get count of aces stored in array
CBaseAce* pAce = NULL;
for (int iCnt = 0; iCnt < iAceCount; iCnt++) // add each of the ace sizes
{
pAce = (CBaseAce*)GetAt(iCnt);
if (!pAce)
return false;
dwSize += pAce->GetSerializedSize();
}
// Allocate the blob, set the pointer from the caller;
BYTE* pData = new BYTE[dwSize];
if (!pData)
return false;
*ppData = (char*)pData;
// Set the header info
DWORD* pdwData = (DWORD *)pData;
*pdwData = dwSize;
pdwData++;
*pdwData = 1; // version
pdwData++;
*pdwData = 1; // ISNT
pdwData++;
*pdwData = iAceCount;
pdwData++;
*pdwData = 0; // reserved
pdwData++;
// Set the ace data
BYTE* pAceData = (BYTE*)pdwData;
for(iCnt = 0; iCnt < iAceCount; iCnt++)
{
pAce = (CBaseAce*)GetAt(iCnt);
pAce->Serialize(pAceData);
pAceData += pAce->GetSerializedSize();;
}
return true;
}
/*
--------------------------------------------------------------------------
|
| Checks to see if the Acl contains an ACE with the specified SID.
| The characteristics of the ACE is irrelevant. Only SID comparison applies.
|
--------------------------------------------------------------------------
*/
bool CWin9xSecurity::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;
}