|
|
/************************************************************************
Copyright (c) 2000 - 2000 Microsoft Corporation
Module Name :
helpers.cpp
Abstract :
General helper functions.
Author :
Revision History :
***********************************************************************/
#include "qmgrlibp.h"
#include <bitsmsg.h>
#include <sddl.h>
#include <shlwapi.h>
#include "helpers.tmh"
FILETIME GetTimeAfterDelta( UINT64 uDelta ) { FILETIME ftCurrentTime; GetSystemTimeAsFileTime( &ftCurrentTime ); UINT64 uCurrentTime = FILETIMEToUINT64( ftCurrentTime ); uCurrentTime += uDelta;
return UINT64ToFILETIME( uCurrentTime ); }
//---------------------------------------------------------------------
// QmgrFileExists
// Checks if a file exists.
//
// Returns: TRUE if false exists, FALSE otherwise
//---------------------------------------------------------------------
BOOL QMgrFileExists(LPCTSTR szFile) { DWORD dwAttr = GetFileAttributes(szFile);
if (dwAttr == 0xFFFFFFFF) //failed
return FALSE;
return (BOOL)(!(dwAttr & FILE_ATTRIBUTE_DIRECTORY)); }
//
// Class for managing global static data that is different per installation
//
class GlobalInfo *g_GlobalInfo = NULL;
GlobalInfo::GlobalInfo( TCHAR * QmgrDirectory, LARGE_INTEGER PerformanceCounterFrequency, HKEY QmgrRegistryRoot, UINT64 JobInactivityTimeout, UINT64 TimeQuantaLength, UINT32 DefaultNoProgressTimeout, UINT32 DefaultMinimumRetryDelay, SECURITY_DESCRIPTOR *MetadataSecurityDescriptor, DWORD MetadataSecurityDescriptorLength, SidHandle AdministratorsSid, SidHandle LocalSystemSid, SidHandle NetworkUsersSid, SidHandle AnonymousSid ) : m_QmgrDirectory( QmgrDirectory ), m_PerformanceCounterFrequency( PerformanceCounterFrequency ), m_QmgrRegistryRoot( QmgrRegistryRoot ), m_JobInactivityTimeout( JobInactivityTimeout ), m_TimeQuantaLength( TimeQuantaLength ), m_DefaultNoProgressTimeout( DefaultNoProgressTimeout ), m_DefaultMinimumRetryDelay( DefaultMinimumRetryDelay ), m_MetadataSecurityDescriptor( MetadataSecurityDescriptor ), m_MetadataSecurityDescriptorLength( MetadataSecurityDescriptorLength ), m_AdministratorsSid( AdministratorsSid ), m_LocalSystemSid( LocalSystemSid ), m_NetworkUsersSid( NetworkUsersSid ), m_AnonymousSid( AnonymousSid ) { }
GlobalInfo::~GlobalInfo() { delete[] (TCHAR*)m_QmgrDirectory; delete (SECURITY_DESCRIPTOR*)m_MetadataSecurityDescriptor;
if ( m_QmgrRegistryRoot ) CloseHandle( m_QmgrRegistryRoot ); }
DWORD GlobalInfo::RegGetDWORD( HKEY hKey, const TCHAR * pValue, DWORD dwDefault ) { DWORD dwValue; DWORD dwType = REG_DWORD; DWORD dwSize = sizeof(dwValue);
LONG lResult = RegQueryValueEx( //SEC: REVIEWED 2002-03-28
hKey, pValue, NULL, &dwType, (LPBYTE)&dwValue, &dwSize );
if ( ERROR_SUCCESS != lResult || dwType != REG_DWORD || dwSize != sizeof(dwValue) ) { LogWarning( "Unable to read the registry value %!ts!, using default value of %u", pValue, dwDefault ); return dwDefault; }
LogInfo( "Retrieved registry value %u from key value %!ts!", dwValue, pValue ); return dwValue; }
SidHandle BITSAllocateAndInitializeSid( BYTE nSubAuthorityCount, // count of subauthorities
DWORD dwSubAuthority0, // subauthority 0
DWORD dwSubAuthority1 ) // subauthority 1
{
ASSERT( nSubAuthorityCount <= 2 );
SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY; PSID pSid = NULL;
if(! AllocateAndInitializeSid( //SEC: REVIEWED 2002-03-28
&Authority, nSubAuthorityCount, dwSubAuthority0, dwSubAuthority1, 0, 0, 0, 0, 0, 0, &pSid) ) { HRESULT hResult = HRESULT_FROM_WIN32(GetLastError()); LogError( "AllocateAndInitializeSid failed, error %!winerr!\n" , hResult ); throw ComError( hResult ); }
SidHandle NewSid( DuplicateSid( pSid ) ); FreeSid( pSid ); pSid = NULL;
if ( !NewSid.get()) { LogError( "Unable to duplicate sid, error %!winerr!\n" , E_OUTOFMEMORY ); throw ComError( E_OUTOFMEMORY ); }
return NewSid; }
StringHandle BITSSHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags ) {
auto_ptr<WCHAR> Folder( new WCHAR[ MAX_PATH ] );
HRESULT hResult = SHGetFolderPath( hwndOwner, nFolder, hToken, dwFlags, Folder.get() );
if (FAILED(hResult)) { LogError( "SHGetFolderPathFailed, error %!winerr!", hResult ); throw ComError( hResult ); }
return StringHandle( Folder.get() ); }
HRESULT GlobalInfo::Init() /*
Initialize the global info for BITS. */
{ GlobalInfo *pGlobalInfo = NULL; HKEY hQmgrKey = NULL; HKEY hQmgrPolicyKey = NULL; PACL pDacl = NULL;
LogInfo( "Starting init of global info\n" );
try { DWORD dwResult; HRESULT hResult = E_FAIL; DWORD dwReturnLength;
LARGE_INTEGER PerformanceCounterFrequency; BOOL bResult = QueryPerformanceFrequency( &PerformanceCounterFrequency ); if ( !bResult ) throw ComError( E_FAIL );
SidHandle AdministratorsSid = BITSAllocateAndInitializeSid( 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS );
SidHandle LocalSystemSid = BITSAllocateAndInitializeSid( 1, SECURITY_LOCAL_SYSTEM_RID, 0 );
SidHandle NetworkUsersSid = BITSAllocateAndInitializeSid( 1, SECURITY_NETWORK_RID, 0);
SidHandle AnonymousSid = BITSAllocateAndInitializeSid( 1, SECURITY_ANONYMOUS_LOGON_RID, 0);
// initialize the metadata's security descriptor.
auto_ptr<char> TempSDDataPtr( new char[SECURITY_DESCRIPTOR_MIN_LENGTH] ); PSECURITY_DESCRIPTOR pTempSD = (PSECURITY_DESCRIPTOR)TempSDDataPtr.get(); InitializeSecurityDescriptor(pTempSD, SECURITY_DESCRIPTOR_REVISION); //SEC: REVIEWED 2002-03-28
auto_ptr<EXPLICIT_ACCESS> ExplicitAccessPtr( new EXPLICIT_ACCESS[2] ); EXPLICIT_ACCESS *ExplicitAccess = ExplicitAccessPtr.get(); memset( ExplicitAccess, 0, sizeof(EXPLICIT_ACCESS) * 2); //SEC: REVIEWED 2002-03-28
ExplicitAccess[0].grfAccessPermissions = GENERIC_ALL; ExplicitAccess[0].grfAccessMode = SET_ACCESS; ExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP; ExplicitAccess[0].Trustee.ptstrName = (LPTSTR) AdministratorsSid.get();
ExplicitAccess[1].grfAccessPermissions = GENERIC_ALL; ExplicitAccess[1].grfAccessMode = SET_ACCESS; ExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_USER; ExplicitAccess[1].Trustee.ptstrName = (LPTSTR) LocalSystemSid.get();
dwResult = SetEntriesInAcl( //SEC: REVIEWED 2002-03-28
2, ExplicitAccess, NULL, &pDacl );
if ( ERROR_SUCCESS != dwResult ) { hResult = HRESULT_FROM_WIN32( dwResult ); LogError( "SetEntiesInAcl, error %!winerr!\n", hResult ); throw ComError( hResult ); }
if (!SetSecurityDescriptorDacl( //SEC: REVIEWED 2002-03-28
pTempSD, TRUE, // fDaclPresent flag
pDacl, FALSE)) // not a default DACL
{ hResult = HRESULT_FROM_WIN32( GetLastError() ); LogError( "SetSecurityDescriptorDacl, error %!winerr!", hResult ); throw ComError( hResult ); }
DWORD dwRequiredSecurityDescriptorLength = 0; MakeSelfRelativeSD( pTempSD, NULL, &dwRequiredSecurityDescriptorLength );
auto_ptr<SECURITY_DESCRIPTOR> pMetadataSecurityDescriptor( (SECURITY_DESCRIPTOR*)new char[dwRequiredSecurityDescriptorLength] );
if (!pMetadataSecurityDescriptor.get()) { throw ComError( E_OUTOFMEMORY ); }
if (!MakeSelfRelativeSD( pTempSD, pMetadataSecurityDescriptor.get(), &dwRequiredSecurityDescriptorLength ) ) { hResult = HRESULT_FROM_WIN32(GetLastError()); LogError( "MakeSelfRelativeSD, error %!winerr!", hResult ); throw ComError( hResult ); }
LocalFree( pDacl ); pDacl = NULL;
SECURITY_ATTRIBUTES MetadataSecurityAttributes; MetadataSecurityAttributes.nLength = sizeof(MetadataSecurityAttributes); MetadataSecurityAttributes.lpSecurityDescriptor = pMetadataSecurityDescriptor.get(); MetadataSecurityAttributes.bInheritHandle = FALSE;
// Build path where the metadata will be stored.
StringHandle AllUsersDirectory = BITSSHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT );
size_t Length = lstrlen( AllUsersDirectory ) + lstrlen(C_QMGR_DIRECTORY) + 1; //SEC: REVIEWED 2002-03-28
auto_ptr<TCHAR> QmgrDirectory( new TCHAR[ Length ] );
// Create the Application Data\Microsoft\Network directory if needed.
// Its access permissions should be inherited from the parent.
//
THROW_HRESULT( StringCchCopy( QmgrDirectory.get(), Length, AllUsersDirectory )); THROW_HRESULT( StringCchCat( QmgrDirectory.get(), Length, C_QMGR_PARENT_DIRECTORY ));
dwResult = GetFileAttributes( QmgrDirectory.get() ); if ( (-1 == dwResult) || !(dwResult & FILE_ATTRIBUTE_DIRECTORY)) { LogError( "parent directory doesn't exist, attempt to create %!ts!.\n", QmgrDirectory.get() );
bResult = CreateDirectory(QmgrDirectory.get(), NULL ); //SEC: REVIEWED 2002-08-16
if ( !bResult ) { hResult = HRESULT_FROM_WIN32( GetLastError() ); LogError( "Unable to create parent directory, error %!winerr!\n", hResult ); throw ComError( hResult ); } }
// Create the BITS directory if needed.
// Its permissions should be restrictive because we don't want non-admins to be able to read our files.
//
THROW_HRESULT( StringCchCopy( QmgrDirectory.get(), Length, AllUsersDirectory )); THROW_HRESULT( StringCchCat( QmgrDirectory.get(), Length, C_QMGR_DIRECTORY ));
dwResult = GetFileAttributes( QmgrDirectory.get() ); if ( (-1 == dwResult) || !(dwResult & FILE_ATTRIBUTE_DIRECTORY)) { LogError( "BITS directory doesn't exist, attempt to create %!ts!.\n", QmgrDirectory.get() );
bResult = CreateDirectory(QmgrDirectory.get(), &MetadataSecurityAttributes); //SEC: REVIEWED 2002-03-28
if ( !bResult ) { hResult = HRESULT_FROM_WIN32( GetLastError() ); LogError( "Unable to create BITS directory, error %!winerr!\n", hResult ); throw ComError( hResult ); } }
// Open the main policy registry key
dwResult = (DWORD)RegOpenKey( HKEY_LOCAL_MACHINE, C_QMGR_POLICY_REG_KEY, &hQmgrPolicyKey);
if ( ERROR_SUCCESS != dwResult ) { LogWarning("Unable to open the main policy registry key\n"); }
// Open the main qmgr registry key
dwResult = (DWORD)RegCreateKeyEx( HKEY_LOCAL_MACHINE, // root key
C_QMGR_REG_KEY, // subkey
0, // reserved
NULL, // class name
REG_OPTION_NON_VOLATILE, // option
KEY_ALL_ACCESS, // security // SEC: REVIEWED 2002-03-28
&MetadataSecurityAttributes,// security attribute
&hQmgrKey, NULL);
if ( ERROR_SUCCESS != dwResult ) { hResult = HRESULT_FROM_WIN32( dwResult ); LogError( "Unable to open main BITS key, error %!winerr!\n", hResult ); throw ComError( hResult ); }
UINT64 JobInactivityTimeout; // Get the inactivity timeout value for job;
{ DWORD dwValue; DWORD dwType = REG_DWORD; DWORD dwSize = sizeof(dwValue);
LONG lResult;
if ( hQmgrPolicyKey ) { lResult = RegQueryValueEx( //SEC: REVIEWED 2002-03-28
hQmgrPolicyKey, C_QMGR_JOB_INACTIVITY_TIMEOUT, NULL, &dwType, (LPBYTE)&dwValue, &dwSize ); }
if ( !hQmgrPolicyKey || ERROR_SUCCESS != lResult || dwType != REG_DWORD || dwSize != sizeof(dwValue) ) { JobInactivityTimeout = RegGetDWORD( hQmgrKey, C_QMGR_JOB_INACTIVITY_TIMEOUT, C_QMGR_JOB_INACTIVITY_TIMEOUT_DEFAULT); JobInactivityTimeout *= NanoSec100PerSec;
} else { LogInfo("Retrieved job inactivity timeout of %u days from policy", dwValue ); JobInactivityTimeout = dwValue * NanoSec100PerSec * 60/*secs per min*/ * 60/*mins per hour*/ * 24 /* hours per day*/; } }
UINT64 TimeQuantaLength = RegGetDWORD( hQmgrKey, C_QMGR_TIME_QUANTA_LENGTH, C_QMGR_TIME_QUANTA_LENGTH_DEFAULT ); TimeQuantaLength *= NanoSec100PerSec;
UINT32 DefaultNoProgressTimeout = // global data is in seconds.
RegGetDWORD( hQmgrKey, C_QMGR_NO_PROGRESS_TIMEOUT, C_QMGR_NO_PROGRESS_TIMEOUT_DEFAULT );
UINT32 DefaultMinimumRetryDelay = // global data is in seconds
RegGetDWORD( hQmgrKey, C_QMGR_MINIMUM_RETRY_DELAY, C_QMGR_MINIMUM_RETRY_DELAY_DEFAULT );
pGlobalInfo = new GlobalInfo( QmgrDirectory.get(), PerformanceCounterFrequency, hQmgrKey, JobInactivityTimeout, TimeQuantaLength, DefaultNoProgressTimeout, DefaultMinimumRetryDelay, pMetadataSecurityDescriptor.get(), dwRequiredSecurityDescriptorLength, AdministratorsSid, LocalSystemSid, NetworkUsersSid, AnonymousSid );
if ( !pGlobalInfo ) throw ComError( E_OUTOFMEMORY );
QmgrDirectory.release(); pMetadataSecurityDescriptor.release(); if ( hQmgrPolicyKey ) CloseHandle( hQmgrPolicyKey ); }
catch( ComError Error ) { LogError( "An exception occured creating global info, error %!winerr!", Error.Error() );
if ( hQmgrKey ) CloseHandle( hQmgrKey ); hQmgrKey = NULL;
if ( hQmgrPolicyKey ) CloseHandle( hQmgrPolicyKey ); hQmgrPolicyKey = NULL;
// LocalFree has if guard
LocalFree( pDacl );
return Error.Error(); }
LogInfo( "Finished init of global info" ); g_GlobalInfo = pGlobalInfo; return S_OK; }
HRESULT GlobalInfo::Uninit() { delete g_GlobalInfo; g_GlobalInfo = NULL; return S_OK; }
LONG ExternalFuncExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) { // This function is called by the exception filter that wraps external functions.
// The purpose is to treat unhandled exceptions as unhandled instead of propagating
// across the network
// If this exception is a MSVCRT exception, bash the exception code
// so that MSVCRT won't call ExitProcess.
if ( ExceptionInfo && ExceptionInfo->ExceptionRecord && ('msc' | 0xE0000000) == ExceptionInfo->ExceptionRecord->ExceptionCode ) { ExceptionInfo->ExceptionRecord->ExceptionCode = ('BIT' | 0xE0000000); }
LONG Result = UnhandledExceptionFilter( ExceptionInfo );
if ( EXCEPTION_CONTINUE_SEARCH == Result ) { // Need to act like the dispatcher. Call kernel again specifying second change semantics
NtRaiseException( ExceptionInfo->ExceptionRecord, ExceptionInfo->ContextRecord, FALSE ); } // exception handler returns RPC_E_SERVERFAULT
return EXCEPTION_EXECUTE_HANDLER; }
SidHandle & SidHandle::operator=( const SidHandle & r ) { if (m_pValue == r.m_pValue ) { return *this; }
if (InterlockedDecrement(m_pRefs) == 0) { delete m_pRefs; delete m_pValue; }
m_pValue = r.m_pValue; m_pRefs = r.m_pRefs;
InterlockedIncrement(m_pRefs);
return *this; }
StringHandle::StringData StringHandle::s_EmptyString = { 0, 1, { L'\0' } // Initialize with 1 ref so it is never deleted
};
bool CSidSorter::operator()( const SidHandle & handle1, const SidHandle & handle2 ) const { const PSID psid1 = handle1.get(); const PSID psid2 = handle2.get();
if ( !psid1 || !psid2 ) return (INT_PTR)psid1 < (INT_PTR)psid2;
if (*GetSidSubAuthorityCount( psid1 ) < *GetSidSubAuthorityCount( psid2 )) { return true; }
// at this point, we known psd1 is >= psd2. // Stop if psid1 is
// longer so that the preceding for loop doesn't overstep the sid
// array on psd2.
if ( *GetSidSubAuthorityCount( psid1 ) > *GetSidSubAuthorityCount( psid2 ) ) return false;
// arrays have equal length
for (UCHAR i=0; i < *GetSidSubAuthorityCount( psid1 ); ++i) { if (*GetSidSubAuthority( psid1, i ) < *GetSidSubAuthority( psid2, i )) return true; // sid1 is less then sid2
else if ( *GetSidSubAuthority( psid1, i ) > *GetSidSubAuthority( psid2, i ) ) return false; // sid1 is greater then sid2
// subauthorities are the same, move on to the next subauthority
}
// arrays are identical
return false; }
//------------------------------------------------------------------------
PSID DuplicateSid( PSID _Sid ) /*++
Routine Description:
Clones a SID. The new SID is allocated using the global operator new.
At entry:
_Sid is the SID to clone.
At exit:
the return is NULL if an error occurs, otherwise a pointer to the new SID.
--*/ { DWORD Length = GetLengthSid( _Sid ); SID * psid;
try { psid = (SID *) new char[Length]; } catch( ComError Error ) { return NULL; }
if (!CopySid( Length, psid, _Sid )) // SEC: REVIEWED 2002-03-28
{
delete[] psid; return NULL; }
return psid; }
LPCWSTR TruncateString( LPCWSTR String, SIZE_T MaxLength, auto_ptr<WCHAR> & AutoPointer ) { if ( wcslen( String ) <= MaxLength ) // SEC: REVIEWED 2002-03-28
return String;
AutoPointer = auto_ptr<WCHAR>( new WCHAR[ MaxLength + 1 ] ); wcsncpy( AutoPointer.get(), String, MaxLength ); // SEC: REVIEWED 2002-03-28
AutoPointer.get()[ MaxLength ] = L'\0'; return AutoPointer.get();
}
PLATFORM_PRODUCT_VERSION g_PlatformVersion; DWORD g_PlatformMajorVersion; DWORD g_PlatformMinorVersion;
bool bIsWin9x;
BOOL DetectProductVersion() {
OSVERSIONINFO VersionInfo; VersionInfo.dwOSVersionInfoSize = sizeof( VersionInfo );
if ( !GetVersionEx( &VersionInfo ) ) return FALSE;
g_PlatformMajorVersion = VersionInfo.dwMajorVersion; g_PlatformMinorVersion = VersionInfo.dwMinorVersion;
switch( VersionInfo.dwPlatformId ) {
case VER_PLATFORM_WIN32_WINDOWS: g_PlatformVersion = ( VersionInfo.dwMajorVersion > 0 ) ? WIN98_PLATFORM : WIN95_PLATFORM; bIsWin9x = true; return TRUE;
case VER_PLATFORM_WIN32_NT: bIsWin9x = false;
if ( VersionInfo.dwMajorVersion < 5 ) return FALSE;
if ( VersionInfo.dwMajorVersion > 5 ) { g_PlatformVersion = WINDOWSXP_PLATFORM; return TRUE; }
g_PlatformVersion = ( VersionInfo.dwMinorVersion > 0 ) ? WINDOWSXP_PLATFORM : WINDOWS2000_PLATFORM;
return TRUE;
default: return FALSE;
} }
StringHandle CombineUrl( LPCWSTR BaseUrl, LPCWSTR RelativeUrl, DWORD Flags ) { DWORD Length = 0; HRESULT hr;
hr = UrlCombine( BaseUrl, RelativeUrl, 0, &Length, Flags );
if (hr != E_POINTER) { ASSERT( FAILED(hr) );
throw ComError( hr ); }
auto_ptr<WCHAR> AbsoluteUrl ( new WCHAR[ Length ] );
THROW_HRESULT( UrlCombine( BaseUrl, RelativeUrl, AbsoluteUrl.get(), &Length, Flags ));
//
// The string handle constructor clones the auto_ptr.
//
return AbsoluteUrl.get(); }
bool IsAnyDebuggerPresent() { if (IsDebuggerPresent()) { return true; }
SYSTEM_KERNEL_DEBUGGER_INFORMATION KdInfo = { 0 };
NtQuerySystemInformation( SystemKernelDebuggerInformation, &KdInfo, sizeof(KdInfo), NULL);
if (KdInfo.KernelDebuggerEnabled) { return true; }
return false; }
LPWSTR MidlCopyString( LPCWSTR source, size_t Length ) { if (Length == -1) { Length = 1+wcslen( source ); // SEC: REVIEWED 2002-03-28
}
LPWSTR copy = reinterpret_cast<LPWSTR>( CoTaskMemAlloc( Length * sizeof( wchar_t ))); if (!copy) { return NULL; }
if (FAILED(StringCchCopy( copy, Length, source ))) { CoTaskMemFree( copy ); return NULL; }
return copy; }
LPWSTR CopyString( LPCWSTR source, size_t Length ) { if (Length == -1) { Length = 1+wcslen( source ); // SEC: REVIEWED 2002-03-28
}
CAutoString copy( new wchar_t[ Length ]);
THROW_HRESULT( StringCchCopy( copy.get(), Length, source ));
return copy.release(); }
|