|
|
// Copyright (c) 2000-2002 Microsoft Corporation, All Rights Reserved
// Helpers.cpp: Helper functions for the SecUtil component
//#include "FWCommon.h"
//#include <windows.h>
//#include <winnt.h>
/*
#ifndef USE_POLARITY
// For most users, this is the correct setting for POLARITY.
#define USE_POLARITY
#endif
*/
#include "precomp.h"
#include <cominit.h>
#include <vector>
#include "Helpers.h"
#include "AssertBreak.h"
#include "CVARIANT.H"
#include <crtdbg.h>
#define IDS_NTDLLDOTDLL L"NTDLL.DLL"
#define IDS_NTOPENDIRECTORYOBJECT "NtOpenDirectoryObject"
#define IDS_NTQUERYDIRECTORYOBJECT "NtQueryDirectoryObject"
#define IDS_RTLINITUNICODESTRING "RtlInitUnicodeString"
#define IDS_WHACKWHACKBASENAMEDOBJECTS L"\\BaseNamedObjects"
#define IDS_NTQUERYINFORMATIONPROCESS "NtQueryInformationProcess"
#define IDS_NTOPENPROCESS "NtOpenProcess"
#define IDS_WIN32_ERROR_CODE L"Win32ErrorCode"
#define IDS_ADDITIONAL_DESCRIPTION L"AdditionalDescription"
#define IDS_OPERATION L"Operation"
typedef NTSTATUS (NTAPI *PFN_NT_OPEN_DIRECTORY_OBJECT) ( OUT PHANDLE DirectoryHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes );
typedef NTSTATUS (NTAPI *PFN_NT_QUERY_DIRECTORY_OBJECT) ( IN HANDLE DirectoryHandle, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, IN BOOLEAN RestartScan, IN OUT PULONG Context, OUT PULONG ReturnLength OPTIONAL );
typedef VOID (WINAPI *PFN_NTDLL_RTL_INIT_UNICODE_STRING) ( OUT PUNICODE_STRING DestinationString, IN PCWSTR SourceString );
typedef NTSTATUS (NTAPI *PFN_NTDLL_NT_QUERY_INFORMATION_PROCESS) ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL );
typedef NTSTATUS (NTAPI *PFN_NT_OPEN_PROCESS) ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL );
//***************************************************************************
//
// CreateInst
//
// Purpose: Creates a new instance.
//
// Return: S_OK if all is well, otherwise an error code is returned
//
//***************************************************************************
HRESULT CreateInst( IWbemServices *pNamespace, IWbemClassObject **pNewInst, BSTR bstrClassName, IWbemContext *pCtx) { HRESULT hr = S_OK; IWbemClassObjectPtr pClass; hr = pNamespace->GetObject( bstrClassName, 0, pCtx, &pClass, NULL); if(SUCCEEDED(hr)) { hr = pClass->SpawnInstance( 0, pNewInst); } return hr; }
//***************************************************************************
//
// GetObjInstKeyVal
//
// Purpose: Obtains an object's instance key from an object path.
//
// Return: true if the key was obtained.
//
//***************************************************************************
HRESULT GetObjInstKeyVal( const BSTR ObjectPath, LPCWSTR wstrClassName, LPCWSTR wstrKeyPropName, LPWSTR wstrObjInstKeyVal, long lBufLen) { HRESULT hr = WBEM_S_NO_ERROR; WCHAR* pwcEqualSign = NULL; WCHAR* pwcTmp = NULL;
if(!ObjectPath) { return WBEM_E_INVALID_PARAMETER; }
if((pwcEqualSign = wcschr(ObjectPath, L'=')) != NULL) { pwcEqualSign++; long lLen = wcslen(pwcEqualSign) * sizeof(WCHAR); if(*pwcEqualSign && lLen > 0 && lLen < (long)(lBufLen - sizeof(WCHAR))) { wcscpy(wstrObjInstKeyVal, pwcEqualSign);
// Remove any quotation marks that might
// be there...
RemoveQuotes(wstrObjInstKeyVal);
// Also need to check that the class name
// matches the name specified...
WCHAR wstrClass[_MAX_PATH]; wcscpy(wstrClass, ObjectPath); pwcTmp = wcschr(wstrClass, L'='); if(pwcTmp) { *pwcTmp = '\0'; // Either the key property was specified or
// it wasn't...
pwcTmp = NULL; pwcTmp = wcschr(wstrClass, L'.'); if(pwcTmp) { // Key property specified, so check that
// both it and the class name are correct...
*pwcTmp = '\0'; if(_wcsicmp(wstrClassName, wstrClass) == 0) { if(_wcsicmp(wstrKeyPropName, ++pwcTmp) != 0) { hr = WBEM_E_INVALID_PARAMETER; } } else { hr = WBEM_E_INVALID_CLASS; } } else { // No key prop specified, so only need
// to check that the class name is correct...
if(_wcsicmp(wstrClassName, wstrClass) != 0) { hr = WBEM_E_INVALID_CLASS; } } } else { hr = WBEM_E_INVALID_PARAMETER; } } else { hr = WBEM_E_INVALID_PARAMETER; } } else { hr = WBEM_E_INVALID_PARAMETER; }
return hr; }
HRESULT GetJobObjectList( std::vector<_bstr_t>& rgbstrtJOs) { HRESULT hr = S_OK; HINSTANCE hinst = NULL; SmartCloseHANDLE hDir; PBYTE pbBuff = NULL;
try { if((hinst = ::LoadLibrary(IDS_NTDLLDOTDLL)) != NULL) { PFN_NT_OPEN_DIRECTORY_OBJECT pfnNtOpenDirectoryObject = NULL; PFN_NT_QUERY_DIRECTORY_OBJECT pfnNtQueryDirectoryObject = NULL; PFN_NTDLL_RTL_INIT_UNICODE_STRING pfnRtlInitUnicodeString = NULL;
pfnNtOpenDirectoryObject = (PFN_NT_OPEN_DIRECTORY_OBJECT) ::GetProcAddress(hinst, IDS_NTOPENDIRECTORYOBJECT);
pfnNtQueryDirectoryObject = (PFN_NT_QUERY_DIRECTORY_OBJECT) ::GetProcAddress(hinst, IDS_NTQUERYDIRECTORYOBJECT);
pfnRtlInitUnicodeString = (PFN_NTDLL_RTL_INIT_UNICODE_STRING) ::GetProcAddress(hinst, IDS_RTLINITUNICODESTRING);
if(pfnNtOpenDirectoryObject != NULL && pfnNtQueryDirectoryObject != NULL && pfnRtlInitUnicodeString != NULL) { OBJECT_ATTRIBUTES oaAttributes; UNICODE_STRING ustrNtFileName; NTSTATUS ntstat = -1L;
pfnRtlInitUnicodeString(&ustrNtFileName, IDS_WHACKWHACKBASENAMEDOBJECTS);
InitializeObjectAttributes(&oaAttributes, &ustrNtFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
ntstat = pfnNtOpenDirectoryObject(&hDir, FILE_READ_DATA, &oaAttributes); if(NT_SUCCESS(ntstat)) { ULONG ulContext = -1L; ntstat = STATUS_SUCCESS; ULONG ulBufLen = 0L; ULONG ulNewBufLen = 0L;
// First query to get buffer size to allocate...
ntstat = pfnNtQueryDirectoryObject(hDir, // IN HANDLE DirectoryHandle,
NULL, // OUT PVOID Buffer,
0L, // IN ULONG Length,
FALSE, // IN BOOLEAN ReturnSingleEntry,
TRUE, // IN BOOLEAN RestartScan,
&ulContext, // IN OUT PULONG Context,
&ulBufLen); // OUT PULONG ReturnLength OPTIONAL
pbBuff = new BYTE[ulBufLen]; if(pbBuff) { // then loop through all the entries...
for(; ntstat != STATUS_NO_MORE_ENTRIES && pbBuff != NULL;) { ntstat = pfnNtQueryDirectoryObject(hDir, // IN HANDLE DirectoryHandle,
pbBuff, // OUT PVOID Buffer,
ulBufLen, // IN ULONG Length,
TRUE, // IN BOOLEAN ReturnSingleEntry,
FALSE, // IN BOOLEAN RestartScan,
&ulContext, // IN OUT PULONG Context,
&ulNewBufLen); // OUT PULONG ReturnLength OPTIONAL
if(ntstat == STATUS_BUFFER_TOO_SMALL) { // Deallocate buffer and reallocate...
if(pbBuff != NULL) { delete pbBuff; pbBuff = NULL; } pbBuff = new BYTE[ulNewBufLen]; ulBufLen = ulNewBufLen; } else if(NT_SUCCESS(ntstat)) { // All went well, should have data...
if(pbBuff != NULL) { POBJECT_DIRECTORY_INFORMATION podi = (POBJECT_DIRECTORY_INFORMATION) pbBuff; LPWSTR wstrName = (LPWSTR)podi->Name.Buffer; LPWSTR wstrType = (LPWSTR)podi->TypeName.Buffer; // do something...
// offset to string name is in four bytes...
if(wstrName != NULL && wstrType != NULL && wcslen(wstrType) == 3) { WCHAR wstrTmp[4]; wstrTmp[3] = '\0'; wcsncpy(wstrTmp, wstrType, 3); if(_wcsicmp(wstrTmp, L"job") == 0) { rgbstrtJOs.push_back(_bstr_t(wstrName)); } } } } else if(ntstat == STATUS_NO_MORE_ENTRIES) { // we will break
} else { // Something we weren't expecting happened, so bail out...
hr = E_FAIL; } } // while we still have entries
delete pbBuff; pbBuff = NULL; } else { hr = E_OUTOFMEMORY; } } // NtOpenDirectoryObject succeeded
else { hr = E_FAIL; } } // Got the fn ptrs
else { hr = E_FAIL; } ::FreeLibrary(hinst); } else { hr = E_FAIL; } } catch(...) { if(pbBuff != NULL) { delete pbBuff; pbBuff = NULL; } if(hinst != NULL) { ::FreeLibrary(hinst); hinst = NULL; } throw; }
return hr; }
void StringFromSid(PSID psid, _bstr_t& strSID) { // Initialize m_strSid - human readable form of our SID
SID_IDENTIFIER_AUTHORITY *psia = NULL; psia = ::GetSidIdentifierAuthority(psid); WCHAR wstrTmp[_MAX_PATH];
// We assume that only last byte is used
// (authorities between 0 and 15).
// Correct this if needed.
ASSERT( psia->Value[0] == psia->Value[1] == psia->Value[2] == psia->Value[3] == psia->Value[4] == 0 );
DWORD dwTopAuthority = psia->Value[5];
wsprintf(wstrTmp, L"S-1-%u", dwTopAuthority);
WCHAR wstrSubAuthority[_MAX_PATH];
int iSubAuthorityCount = *(GetSidSubAuthorityCount(psid));
for ( int i = 0; i < iSubAuthorityCount; i++ ) {
DWORD dwSubAuthority = *(GetSidSubAuthority(psid, i)); wsprintf(wstrSubAuthority, L"%u", dwSubAuthority); wcscat(wstrTmp, L"-"); wcscat(wstrTmp, wstrSubAuthority); }
strSID = wstrTmp; }
void RemoveQuotes(LPWSTR wstrObjInstKeyVal) { WCHAR wstrTmp[MAX_PATH] = { L'\0' }; WCHAR* pwchr = NULL;
// Get rid of the first quote...
if((pwchr = wcschr(wstrObjInstKeyVal, L'"')) != NULL) { wcscpy(wstrTmp, pwchr+1); }
// now the last...
if((pwchr = wcsrchr(wstrTmp, L'"')) != NULL) { *pwchr = L'\0'; }
wcscpy(wstrObjInstKeyVal, wstrTmp); }
HRESULT CheckImpersonationLevel() { HRESULT hr = WBEM_E_ACCESS_DENIED; OSVERSIONINFOW OsVersionInfoW;
OsVersionInfoW.dwOSVersionInfoSize = sizeof (OSVERSIONINFOW); GetVersionExW(&OsVersionInfoW);
if (OsVersionInfoW.dwPlatformId == VER_PLATFORM_WIN32_NT) { HRESULT hRes = WbemCoImpersonateClient(); if (SUCCEEDED(hRes)) // From cominit.cpp - needed for nt3.51
{ // Now, let's check the impersonation level. First, get the thread token
SmartCloseHANDLE hThreadTok; DWORD dwImp, dwBytesReturned;
if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadTok )) { DWORD dwLastError = GetLastError();
if (dwLastError == ERROR_NO_TOKEN) { // If the CoImpersonate works, but the OpenThreadToken fails due to ERROR_NO_TOKEN, we
// are running under the process token (either local system, or if we are running
// with /exe, the rights of the logged in user). In either case, impersonation rights
// don't apply. We have the full rights of that user.
hr = WBEM_S_NO_ERROR; } else { // If we failed to get the thread token for any other reason, an error.
hr = WBEM_E_ACCESS_DENIED; } } else { // We really do have a thread token, so let's retrieve its level
if (GetTokenInformation( hThreadTok, TokenImpersonationLevel, &dwImp, sizeof(DWORD), &dwBytesReturned )) { // Is the impersonation level Impersonate?
if ((dwImp == SecurityImpersonation) || (dwImp == SecurityDelegation)) { hr = WBEM_S_NO_ERROR; } else { hr = WBEM_E_ACCESS_DENIED; } } else { hr = WBEM_E_FAILED; } }
if (FAILED(hr)) { WbemCoRevertToSelf(); } } else if (hRes == E_NOTIMPL) { // On 3.51 or vanilla 95, this call is not implemented, we should work anyway
hr = WBEM_S_NO_ERROR; } } else { // let win9X in...
hr = WBEM_S_NO_ERROR; }
return hr; }
HRESULT SetStatusObject( IWbemContext* pCtx, IWbemServices* pSvcs, DWORD dwError, LPCWSTR wstrErrorDescription, LPCWSTR wstrOperation, LPCWSTR wstrNamespace, IWbemClassObject** ppStatusObjOut) { HRESULT hr = WBEM_S_NO_ERROR;
IWbemClassObject* pStatusObj = NULL;
ASSERT_BREAK(pCtx != NULL); ASSERT_BREAK(pSvcs != NULL);
if(pSvcs && ppStatusObjOut && pCtx) { pStatusObj = GetStatusObject( pCtx, pSvcs); if(pStatusObj != NULL) { CVARIANT v;
// Set the error code:
v.SetLONG(dwError); pStatusObj->Put( IDS_WIN32_ERROR_CODE, 0, &v, NULL); v.Clear();
// Set the error description
if(wstrErrorDescription != NULL && *wstrErrorDescription != L'\0') { v.SetStr(wstrErrorDescription); pStatusObj->Put( IDS_ADDITIONAL_DESCRIPTION, 0, &v, NULL); v.Clear(); }
if(wstrOperation != NULL && *wstrOperation != L'\0') { v.SetStr(wstrOperation); pStatusObj->Put( IDS_OPERATION, 0, &v, NULL); v.Clear(); } }
if(pStatusObj) { if(*ppStatusObjOut != NULL) { (*ppStatusObjOut)->Release(); *ppStatusObjOut = NULL; } *ppStatusObjOut = pStatusObj; } else { hr = WBEM_E_FAILED; } } else { hr = WBEM_E_FAILED; }
return hr; }
IWbemClassObject* GetStatusObject( IWbemContext* pContext, IWbemServices* pSrvc) { ASSERT_BREAK(pContext != NULL); ASSERT_BREAK(pSrvc != NULL); IWbemClassObjectPtr pStatusObjClass; IWbemClassObject* pStatusObjectInstance = NULL;
if(pContext && pSrvc) { if(pSrvc) { // not checking return code, error object should be NULL on error
pSrvc->GetObject( _bstr_t(JOB_OBJECT_STATUS_OBJECT), 0, pContext, &pStatusObjClass, NULL);
if(pStatusObjClass) { pStatusObjClass->SpawnInstance( 0, &pStatusObjectInstance); } } }
ASSERT_BREAK(pStatusObjectInstance); return pStatusObjectInstance; }
void UndecorateNamesInNamedJONameList( std::vector<_bstr_t>& rgNamedJOs) { std::vector<_bstr_t> rgUndecoratedNames; CHString chstrTemp;
for(long m = 0L; m < rgNamedJOs.size(); m++) { UndecorateJOName( rgNamedJOs[m], chstrTemp); _bstr_t bstrtTemp((LPCWSTR)chstrTemp); rgUndecoratedNames.push_back( bstrtTemp); }
// Wipe out the original vector...
rgNamedJOs.clear();
// Push in new vector's contents...
for(m = 0L; m < rgUndecoratedNames.size(); m++) { rgNamedJOs.push_back( rgUndecoratedNames[m]); } }
// Takes a decorated job object name and
// undecorates it. Decorated job object names
// have a backslash preceeding any character
// that should be uppercase once undecorated.
//
// Due to the way CIMOM handles backslashes,
// we will get capital letters preceeded by
// two, not just one, backslashes. Hence, we
// must strip them both.
//
// According to the decoration scheme, the
// following are both lower case: 'A' and 'a',
// while the following are both upper case:
// '\a' and '\A'.
//
void UndecorateJOName( LPCWSTR wstrDecoratedName, CHString& chstrUndecoratedJOName) { if(wstrDecoratedName != NULL && *wstrDecoratedName != L'\0') { LPWSTR wstrDecoratedNameLower = NULL;
try { wstrDecoratedNameLower = new WCHAR[wcslen(wstrDecoratedName)+1];
if(wstrDecoratedNameLower) { wcscpy(wstrDecoratedNameLower, wstrDecoratedName); _wcslwr(wstrDecoratedNameLower);
WCHAR* p3 = chstrUndecoratedJOName.GetBuffer( wcslen(wstrDecoratedNameLower) + 1);
const WCHAR* p1 = wstrDecoratedNameLower; const WCHAR* p2 = p1 + 1;
while(*p1 != L'\0') { if(*p1 == L'\\') { if(*p2 != NULL) { // Might have any number of
// backslashes back to back,
// which we will treat as
// being the same as one
// backslash - i.e., we will
// skip over the backslash(s)
// and copy over the following
// letter.
while(*p2 == L'\\') { p2++; }; *p3 = towupper(*p2); p3++;
p1 = p2 + 1; if(*p1 != L'\0') { p2 = p1 + 1; } } else { p1++; } } else { *p3 = *p1; p3++;
p1 = p2; if(*p1 != L'\0') { p2 = p1 + 1; } } } *p3 = NULL;
chstrUndecoratedJOName.ReleaseBuffer();
delete wstrDecoratedNameLower; wstrDecoratedNameLower = NULL; } } catch(...) { if(wstrDecoratedNameLower) { delete wstrDecoratedNameLower; wstrDecoratedNameLower = NULL; } throw; } } }
// Does the inverse of the above function.
// However, here, we only need to put in one
// backslash before each uppercase letter.
// CIMOM will add the second backslash.
void DecorateJOName( LPCWSTR wstrUndecoratedName, CHString& chstrDecoratedJOName) { if(wstrUndecoratedName != NULL && *wstrUndecoratedName != L'\0') { // Worst case is that we will have
// a decorated string twice as long
// as the undecorated string (happens
// is every character in the undecorated
// string is a capital letter).
WCHAR* p3 = chstrDecoratedJOName.GetBuffer( 2 * (wcslen(wstrUndecoratedName) + 1));
const WCHAR* p1 = wstrUndecoratedName;
while(*p1 != L'\0') { if(iswupper(*p1)) { // Add in a backslash...
*p3 = L'\\'; p3++;
// Add in the character...
*p3 = *p1; p3++; p1++; } else { // Add in the character...
*p3 = *p1; p3++; p1++; } }
*p3 = NULL; chstrDecoratedJOName.ReleaseBuffer();
// What if we had a job called Job,
// and someone specified it in the
// object path as "Job" instead of
// "\Job"? We DON'T want to find it
// in such a case, because this would
// appear to not be adhering to our
// own convention. Hence, we
// lowercase the incoming string.
chstrDecoratedJOName.MakeLower(); } }
// map standard API return values (defined WinError.h)
// to WBEMish hresults (defined in WbemCli.h)
HRESULT WinErrorToWBEMhResult(LONG error) { HRESULT hr = WBEM_E_FAILED;
switch (error) { case ERROR_SUCCESS: hr = WBEM_S_NO_ERROR; break; case ERROR_ACCESS_DENIED: hr = WBEM_E_ACCESS_DENIED; break; case ERROR_NOT_ENOUGH_MEMORY: case ERROR_OUTOFMEMORY: hr = WBEM_E_OUT_OF_MEMORY; break; case ERROR_ALREADY_EXISTS: hr = WBEM_E_ALREADY_EXISTS; break; case ERROR_BAD_NETPATH: case ERROR_INVALID_DATA: case ERROR_BAD_PATHNAME: case REGDB_E_INVALIDVALUE: case ERROR_PATH_NOT_FOUND: case ERROR_FILE_NOT_FOUND: case ERROR_INVALID_PRINTER_NAME: case ERROR_BAD_USERNAME: case ERROR_NOT_READY: case ERROR_INVALID_NAME: hr = WBEM_E_NOT_FOUND; break; default: hr = WBEM_E_FAILED; }
return hr; }
// Copied from sid.h
bool GetNameAndDomainFromPSID( PSID pSid, CHString& chstrName, CHString& chstrDomain) { bool fRet = false; // pSid should be valid...
_ASSERT( (pSid != NULL) && ::IsValidSid( pSid ) );
if ( (pSid != NULL) && ::IsValidSid( pSid ) ) { // Initialize account name and domain name
LPTSTR pszAccountName = NULL; LPTSTR pszDomainName = NULL; DWORD dwAccountNameSize = 0; DWORD dwDomainNameSize = 0; DWORD dwLastError = ERROR_SUCCESS; BOOL bResult = TRUE;
try { // This call should fail
SID_NAME_USE snuAccountType; bResult = ::LookupAccountSid( NULL, pSid, pszAccountName, &dwAccountNameSize, pszDomainName, &dwDomainNameSize, &snuAccountType ); dwLastError = ::GetLastError();
if ( ERROR_INSUFFICIENT_BUFFER == dwLastError ) {
// Allocate buffers
if ( dwAccountNameSize != 0 ) { pszAccountName = (LPTSTR) new TCHAR[ dwAccountNameSize * sizeof(TCHAR) ]; }
if ( dwDomainNameSize != 0 ) { pszDomainName = (LPTSTR) new TCHAR[ dwDomainNameSize * sizeof(TCHAR) ]; }
// Make second call
bResult = ::LookupAccountSid( NULL, pSid, pszAccountName, &dwAccountNameSize, pszDomainName, &dwDomainNameSize, &snuAccountType );
if ( bResult == TRUE ) { chstrName = pszAccountName; chstrDomain = pszDomainName; } else {
// There are some accounts that do not have names, such as Logon Ids,
// for example S-1-5-X-Y. So this is still legal
chstrName = _T("Unknown Account"); chstrDomain = _T("Unknown Domain"); }
if ( NULL != pszAccountName ) { delete pszAccountName; pszAccountName = NULL; }
if ( NULL != pszDomainName ) { delete pszDomainName; pszDomainName = NULL; }
fRet = true;
} // If ERROR_INSUFFICIENT_BUFFER
} // try
catch(...) { if ( NULL != pszAccountName ) { delete pszAccountName; pszAccountName = NULL; }
if ( NULL != pszDomainName ) { delete pszDomainName; pszDomainName = NULL; } throw; } } // IF IsValidSid
return fRet; }
|