// Copyright (c) 2000-2002 Microsoft Corporation, All Rights Reserved // // CMDH.cpp - Helper class for working with // logical disks mapped by logon // session. // // Created: 4/23/2000 Kevin Hughes (khughes) // // USEAGE NOTE: This class presents a view of // information pertaining to mapped drives in // the context of the process id specified in // the class constructor. #include #include #include #include #define _WINNT_ // have what is needed from above #pragma warning (disable: 4786) #pragma warning (disable: 4284) #include #include #include #include //sprintf #include #include #include #include #include #include #include "DllWrapperBase.h" #include "AdvApi32Api.h" #include "NtDllApi.h" #include "Kernel32Api.h" #include #include "cmdh.h" #include #include #include <..\..\framework\provexpt\include\provexpt.h> #include "Sid.h" #include "AccessEntry.h" // CAccessEntry class #include "AccessEntryList.h" #include "DACL.h" // CDACL class #include "SACL.h" #include "securitydescriptor.h" #include "CToken.h" #include "SecureKernelObj.h" #include #include #include /////////////////////////////////////////////////////////////////////////////// // CMDH Public interface functions /////////////////////////////////////////////////////////////////////////////// HRESULT CMDH::GetMDData( DWORD dwReqProps, VARIANT* pvarData) { HRESULT hr = S_OK; if(!pvarData) return E_POINTER; if(SUCCEEDED(hr)) { hr = GetMappedDisksAndData( dwReqProps, pvarData); } return hr; } HRESULT CMDH::GetOneMDData( BSTR bstrDrive, DWORD dwReqProps, VARIANT* pvarData) { HRESULT hr = S_OK; if(!pvarData) return E_POINTER; if(SUCCEEDED(hr)) { hr = GetSingleMappedDiskAndData( bstrDrive, dwReqProps, pvarData); } return hr; } /////////////////////////////////////////////////////////////////////////////// // CMDH Private internal functions /////////////////////////////////////////////////////////////////////////////// // This function does pretty much all of the // work this object was constructed to do - // it obtains, for the process space that this // server is running in, the set of mapped // drives, and for each of these, the following // information as well: // HRESULT CMDH::GetMappedDisksAndData( DWORD dwReqProps, VARIANT* pvarData) { HRESULT hr = WBEM_S_NO_ERROR; ::VariantInit(pvarData); V_VT(pvarData) = VT_EMPTY; // Get the mapped drives into a vector... std::vector<_bstr_t> vecMappedDrives; { // Impersonate member process... SmartRevertTokenHANDLE hCurImpTok; hCurImpTok = Impersonate(); if(hCurImpTok != INVALID_HANDLE_VALUE) { GetMappedDriveList( vecMappedDrives); } } // Now allocate the two dimensional // safearray that will hold the properties // for each mapped drive... SAFEARRAY* saDriveProps = NULL; SAFEARRAYBOUND rgsabound[2]; rgsabound[0].cElements = PROP_COUNT; rgsabound[0].lLbound = 0; rgsabound[1].cElements = vecMappedDrives.size(); rgsabound[1].lLbound = 0; saDriveProps = ::SafeArrayCreate( VT_BSTR, 2, rgsabound); if(saDriveProps) { // For each mapped drive, obtain its // properties and store in the safearray... for(long m = 0; m < vecMappedDrives.size() && SUCCEEDED(hr); m++) { hr = GetMappedDriveInfo( vecMappedDrives[m], m, saDriveProps, dwReqProps); } // And finally package the safearray // into the outgoing variant. if(SUCCEEDED(hr)) { ::VariantInit(pvarData); V_VT(pvarData) = VT_BSTR | VT_ARRAY; V_ARRAY(pvarData) = saDriveProps; } } else { hr = WBEM_E_OUT_OF_MEMORY; } return hr; } // Similar to GetMappedDisksAndData, but only // retrieves info for a single disk. // HRESULT CMDH::GetSingleMappedDiskAndData( BSTR bstrDrive, DWORD dwReqProps, VARIANT* pvarData) { HRESULT hr = WBEM_S_NO_ERROR; ::VariantInit(pvarData); V_VT(pvarData) = VT_EMPTY; // Impersonate member process... SmartRevertTokenHANDLE hCurImpTok; hCurImpTok = Impersonate(); if(hCurImpTok != INVALID_HANDLE_VALUE) { // Get the mapped drives into a vector... std::vector<_bstr_t> vecMappedDrives; GetMappedDriveList( vecMappedDrives); // Now allocate the two dimensional // safearray that will hold the properties // for each mapped drive... // Note: in this routine, it is really // only a one dimensional array, but, // for code reuse, we'll treat it as a // two dimensional array with only one // element in one of the dimensions. SAFEARRAY* saDriveProps = NULL; SAFEARRAYBOUND rgsabound[2]; rgsabound[0].cElements = PROP_COUNT; rgsabound[0].lLbound = 0; rgsabound[1].cElements = 1; // for code reuse rgsabound[1].lLbound = 0; saDriveProps = ::SafeArrayCreate( VT_BSTR, 2, rgsabound); if(saDriveProps) { // See if the drive specified is a member // of the vector. _bstr_t bstrtTmp = bstrDrive; bstrtTmp += L"\\"; bool fFoundIt = false; for(long n = 0; n < vecMappedDrives.size() && !fFoundIt; n++) { if(_wcsicmp(bstrtTmp, vecMappedDrives[n]) == 0) { fFoundIt = true; n--; } } // For the mapped drive, obtain its // properties and store in the safearray... if(fFoundIt) { hr = GetMappedDriveInfo( vecMappedDrives[n], 0, // for code reuse saDriveProps, dwReqProps); // And finally package the safearray // into the outgoing variant. ::VariantInit(pvarData); V_VT(pvarData) = VT_BSTR | VT_ARRAY; V_ARRAY(pvarData) = saDriveProps; } } else { hr = WBEM_E_OUT_OF_MEMORY; } } return hr; } // Builds a list of mapped drives as // seen with respect to the process // identified by m_dwImpPID. Hence, this // routine will return a valid picture // of the drives seen by m_dwImpPID, regardless // of our current thread impersonation. // #ifdef NTONLY // uses ntdll.dll functions void CMDH::GetMappedDriveList( std::vector<_bstr_t>& vecMappedDrives) { // Need to call NtQueryInformationProcess, // asking for ProcessDeviceMap info, specifying // a handle to the process identified by // m_dwImpPID. // Need to get a process handle to the // process specified by PID. NTSTATUS Status; PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo; Status = ::NtQueryInformationProcess( ::GetCurrentProcess() /*hProcess*/, ProcessDeviceMap, &ProcessDeviceMapInfo.Query, sizeof(ProcessDeviceMapInfo.Query), NULL); if(NT_SUCCESS(Status)) { WCHAR wstrDrive[4]; for(short s = 0; s < 32; s++) { if(ProcessDeviceMapInfo.Query.DriveMap & (1< _MAX_PATH)) { wstrNewProvName = new WCHAR[dwProvName]; if(wstrNewProvName != NULL) { dwRetCode = ::WNetGetConnection( wstrTempDrive, wstrNewProvName, &dwProvName); if(dwRetCode == NO_ERROR || dwRetCode == ERROR_CONNECTION_UNAVAIL) { hr = SetProperty( lDrivePropArrayDriveIndex, PROP_PROVIDER_NAME, wstrNewProvName, saDriveProps); } else { hr = HRESULT_FROM_WIN32(dwRetCode); } delete wstrNewProvName; } else { hr = E_OUTOFMEMORY; } } else { hr = HRESULT_FROM_WIN32(dwRetCode); } } } catch(Structured_Exception se) { DelayLoadDllExceptionFilter(se.GetExtendedInfo()); if(wstrNewProvName) { delete wstrNewProvName; wstrNewProvName = NULL; } hr = WBEM_E_FAILED; } catch(...) { if(wstrNewProvName) { delete wstrNewProvName; wstrNewProvName = NULL; } // The filter will do the work. Just re-throw here. throw; } return hr; } // Presents a view based on the current // impersonation of the current thread. // HRESULT CMDH::GetDriveVolumeInformation( LPCWSTR wstrDriveName, long lDrivePropArrayDriveIndex, SAFEARRAY* saDriveProps) { HRESULT hr = S_OK; DWORD dwResult = ERROR_SUCCESS; WCHAR wstrVolumeName[_MAX_PATH]; WCHAR wstrFileSystem[_MAX_PATH]; WCHAR wstrTmp[_MAX_PATH]; DWORD dwSerialNumber; DWORD dwMaxComponentLength; DWORD dwFSFlags; BOOL fReturn = ::GetVolumeInformation( wstrDriveName, wstrVolumeName, sizeof(wstrVolumeName)/sizeof(WCHAR), &dwSerialNumber, &dwMaxComponentLength, &dwFSFlags, wstrFileSystem, sizeof(wstrFileSystem)/sizeof(WCHAR) ); if(fReturn) { // Win32 API will return volume information for all drive types. SetProperty( lDrivePropArrayDriveIndex, PROP_VOLUME_NAME, wstrVolumeName, saDriveProps); SetProperty( lDrivePropArrayDriveIndex, PROP_FILE_SYSTEM, wstrFileSystem, saDriveProps); if (dwSerialNumber != 0) { WCHAR wstrSerialNumber[_MAX_PATH]; wsprintf(wstrSerialNumber, L"%.8X", dwSerialNumber); SetProperty( lDrivePropArrayDriveIndex, PROP_VOLUME_SERIAL_NUMBER, wstrSerialNumber, saDriveProps); } SetProperty( lDrivePropArrayDriveIndex, PROP_COMPRESSED, STR_FROM_bool(dwFSFlags & FS_VOL_IS_COMPRESSED), saDriveProps); SetProperty( lDrivePropArrayDriveIndex, PROP_SUPPORTS_FILE_BASED_COMPRESSION, STR_FROM_bool(dwFSFlags & FS_FILE_COMPRESSION), saDriveProps); _ultow(dwMaxComponentLength, wstrTmp, 10); SetProperty( lDrivePropArrayDriveIndex, PROP_MAXIMUM_COMPONENT_LENGTH, wstrTmp, saDriveProps); SetProperty( lDrivePropArrayDriveIndex, PROP_SUPPORTS_DISK_QUOTAS, STR_FROM_bool(dwFSFlags & FILE_VOLUME_QUOTAS), saDriveProps); // To get the state of the volume, // we need to get the Interface pointer... IDiskQuotaControlPtr pIQuotaControl; ::SetLastError(ERROR_SUCCESS); if(SUCCEEDED(CoCreateInstance( CLSID_DiskQuotaControl, NULL, CLSCTX_INPROC_SERVER, IID_IDiskQuotaControl, (void **)&pIQuotaControl))) { WCHAR wstrVolumePathName[MAX_PATH + 1]; ::SetLastError(ERROR_SUCCESS); BOOL fRetVal = FALSE; CKernel32Api* pKernel32 = NULL; pKernel32 = (CKernel32Api*)CResourceManager::sm_TheResourceManager.GetResource( g_guidKernel32Api, NULL); try { if(pKernel32) { pKernel32->GetVolumePathName( wstrDriveName, wstrVolumePathName, MAX_PATH, &fRetVal); CResourceManager::sm_TheResourceManager.ReleaseResource( g_guidKernel32Api, pKernel32); pKernel32 = NULL; } } catch(...) { if(pKernel32) { CResourceManager::sm_TheResourceManager.ReleaseResource( g_guidKernel32Api, pKernel32); } throw; } if(fRetVal) { ::SetLastError(ERROR_SUCCESS); if(SUCCEEDED(pIQuotaControl->Initialize( wstrVolumePathName, TRUE))) { DWORD dwQuotaState; ::SetLastError(ERROR_SUCCESS); hr = pIQuotaControl->GetQuotaState(&dwQuotaState); if(SUCCEEDED(hr)) { SetProperty( lDrivePropArrayDriveIndex, PROP_QUOTAS_INCOMPLETE, STR_FROM_bool(DISKQUOTA_FILE_INCOMPLETE(dwQuotaState)), saDriveProps); SetProperty( lDrivePropArrayDriveIndex, PROP_QUOTAS_REBUILDING, STR_FROM_bool(DISKQUOTA_FILE_REBUILDING(dwQuotaState)), saDriveProps); SetProperty( lDrivePropArrayDriveIndex, PROP_QUOTAS_DISABLED, STR_FROM_bool(DISKQUOTA_STATE_DISABLED & dwQuotaState), saDriveProps); } else { dwResult = GetLastError(); } } else { dwResult = GetLastError(); } } } else { dwResult = GetLastError(); } } else { dwResult = GetLastError(); } // for chkdsk VolumeDirty Property BOOLEAN fVolumeDirty = FALSE; BOOL fSuccess = FALSE; _bstr_t bstrtDosDrive(wstrDriveName); UNICODE_STRING string = { 0 }; _bstr_t nt_drive_name; try { if ( RtlDosPathNameToNtPathName_U ( (LPCWSTR)bstrtDosDrive, &string, NULL, NULL ) ) { string.Buffer[string.Length/sizeof(WCHAR) - 1] = 0; nt_drive_name = string.Buffer; if(string.Buffer) { RtlFreeUnicodeString(&string); string.Buffer = NULL; } } else { dwResult = RtlNtStatusToDosError ( (NTSTATUS)NtCurrentTeb()->LastStatusValue ); } } catch(...) { if(string.Buffer) { RtlFreeUnicodeString(&string); string.Buffer = NULL; } throw; } if ( dwResult == ERROR_SUCCESS ) { ::SetLastError(ERROR_SUCCESS); fSuccess = IsVolumeDirty( nt_drive_name, &fVolumeDirty ); if(fSuccess) { SetProperty( lDrivePropArrayDriveIndex, PROP_PERFORM_AUTOCHECK, STR_FROM_bool(!fVolumeDirty), saDriveProps); } else { dwResult = GetLastError(); } } if(dwResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(dwResult); } return hr; } // Presents a view based on the current // impersonation of the current thread. // BOOLEAN CMDH::IsVolumeDirty( _bstr_t &bstrtNtDriveName, BOOLEAN *Result) { UNICODE_STRING u; OBJECT_ATTRIBUTES obj; NTSTATUS status = 0; IO_STATUS_BLOCK iosb; HANDLE h = 0; ULONG r = 0; BOOLEAN bRetVal = FALSE; WCHAR wstrNtDriveName[_MAX_PATH]; wcscpy(wstrNtDriveName, bstrtNtDriveName); u.Length = (USHORT) wcslen(wstrNtDriveName) * sizeof(WCHAR); u.MaximumLength = u.Length; u.Buffer = wstrNtDriveName; InitializeObjectAttributes(&obj, &u, OBJ_CASE_INSENSITIVE, 0, 0); status = NtOpenFile( &h, SYNCHRONIZE | FILE_READ_DATA, &obj, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); if(NT_SUCCESS(status)) { try { status = NtFsControlFile( h, NULL, NULL, NULL, &iosb, FSCTL_IS_VOLUME_DIRTY, NULL, 0, &r, sizeof(r)); if(NT_SUCCESS(status)) { #if(_WIN32_WINNT >= 0x0500) *Result = (BOOLEAN)(r & VOLUME_IS_DIRTY); #else *Result = (BOOLEAN)r; #endif bRetVal = TRUE; } } catch(...) { NtClose(h); h = 0; throw; } NtClose(h); h = 0; } return bRetVal; } // Presents a view based on the current // impersonation of the current thread. // HRESULT CMDH::GetDriveFreeSpace( LPCWSTR wstrDriveName, long lDrivePropArrayDriveIndex, SAFEARRAY* saDriveProps) { HRESULT hr = S_OK; ULARGE_INTEGER uliTotalBytes; ULARGE_INTEGER uliUserFreeBytes; ULARGE_INTEGER uliTotalFreeBytes; ::SetLastError(ERROR_SUCCESS); if(::GetDiskFreeSpaceEx( wstrDriveName, &uliUserFreeBytes, &uliTotalBytes, &uliTotalFreeBytes)) { WCHAR wstrTmp[128] = { L'\0' }; SetProperty( lDrivePropArrayDriveIndex, PROP_SIZE, _ui64tow( uliTotalBytes.QuadPart, wstrTmp, 10), saDriveProps); SetProperty( lDrivePropArrayDriveIndex, PROP_FREE_SPACE, _ui64tow( uliTotalFreeBytes.QuadPart, wstrTmp, 10), saDriveProps); } else { hr = HRESULT_FROM_WIN32(::GetLastError()); } return hr; } // Sets a property for a given drive // in the drive safearray. // HRESULT CMDH::SetProperty( long lDrivePropArrayDriveIndex, long lDrivePropArrayPropIndex, LPCWSTR wstrPropValue, SAFEARRAY* saDriveProps) { HRESULT hr = S_OK; bstr_t bstrTmp( wstrPropValue); // this will work because PutElement makes a copy long ix[2]; ix[0] = lDrivePropArrayPropIndex; ix[1] = lDrivePropArrayDriveIndex; hr = ::SafeArrayPutElement(saDriveProps, ix, (void *)((BSTR)bstrTmp)); return hr; } // Sets our current thread's impersonation // to the token belonging to the process // identified by our member, m_dwImpPID. // HANDLE CMDH::Impersonate() { HANDLE hCurToken = INVALID_HANDLE_VALUE; // Find the explorer process... if(m_dwImpPID != -1L) { // // get SID of current process // CSid csidCurrentProcess; { // // get process credentials // and try to get client's back when leaving scope // WbemCoRevertToSelf () ; ScopeGuard SmartWbemCoImpersonateClientFnc = MakeGuard ( WbemCoImpersonateClient ) ; CProcessToken cpt ( NULL, true, TOKEN_QUERY ) ; PBYTE buff = NULL ; DWORD dwBuff = 0L ; if ( FALSE == ::GetTokenInformation ( cpt.GetTokenHandle () , TokenUser , NULL , 0 , &dwBuff ) ) { if ( ERROR_INSUFFICIENT_BUFFER == ::GetLastError () ) { buff = new BYTE [ dwBuff ] ; wmilib::auto_buffer < BYTE > SmartBuff ( buff ) ; if ( TRUE == ::GetTokenInformation ( cpt.GetTokenHandle () , TokenUser , buff , dwBuff , &dwBuff ) ) { csidCurrentProcess = CSid ( ( ( PTOKEN_USER ) buff )->User.Sid ) ; } } } SmartWbemCoImpersonateClientFnc.Dismiss () ; HRESULT t_hResult = S_OK ; if ( FAILED ( t_hResult = WbemCoImpersonateClient () ) ) { throw CFramework_Exception( L"CoImpersonateClient failed", t_hResult ) ; } } if ( csidCurrentProcess.IsValid () ) { // // smart CloseHandle // ScopeGuard SmartCloseHandleFnc = MakeGuard ( CloseHandle, hCurToken ) ; bool fOK = false; if(::OpenThreadToken( ::GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, TRUE, &hCurToken)) { SmartCloseHandle hProcess; hProcess = ::OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, m_dwImpPID); if(hProcess != INVALID_HANDLE_VALUE) { // now open its token... SmartCloseHandle hProcToken; if(::OpenProcessToken( hProcess, TOKEN_READ | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hProcToken)) { CProcessToken cpt ( hProcToken ); if ( cpt.IsValidToken () ) { TOKEN_TYPE type; if ( cpt.GetTokenType ( type ) ) { if ( TokenPrimary == type ) { CToken ct; if ( ct.Duplicate ( cpt, FALSE ) ) { // Set the thread token... if(::SetThreadToken(NULL, ct.GetTokenHandle ())) { fOK = true; } } } else { #if DBG == 1 // for testing purpose I will let process break ::DebugBreak(); #endif // Set the thread token... if(::SetThreadToken(NULL, cpt.GetTokenHandle ())) { fOK = true; } } } } } } } SmartCloseHandleFnc.Dismiss () ; if(!fOK) { if(hCurToken != INVALID_HANDLE_VALUE) { ::CloseHandle(hCurToken); hCurToken = INVALID_HANDLE_VALUE; } } else { BOOL bSucceeded = FALSE ; // // need to adjust SD associated with thread // to contain current process' SID // CThreadToken ctt; if ( ctt.IsValidToken () ) { // Obtain access to its security descriptor... CSecureKernelObj sko(ctt.GetTokenHandle(), FALSE); // Modify the security descriptor... if( sko.AddDACLEntry ( csidCurrentProcess, ENUM_ACCESS_ALLOWED_ACE_TYPE, TOKEN_ALL_ACCESS, 0, NULL, NULL )) { if ( ERROR_SUCCESS == sko.ApplySecurity( DACL_SECURITY_INFORMATION ) ) { bSucceeded = TRUE ; } } } if ( FALSE == bSucceeded ) { // // we need to revert back as SD was // not successfully adjusted // if ( ! ::SetThreadToken ( NULL, hCurToken ) ) { ::CloseHandle ( hCurToken ) ; throw CFramework_Exception(L"SetThreadToken failed", GetLastError()); } ::CloseHandle ( hCurToken ) ; hCurToken = INVALID_HANDLE_VALUE ; } } } } return hCurToken; }