/*++ Copyright (c) 1998 Microsoft Corporation Module Name: registry.c Abstract: Implementation of registry code. Author: Wesley Witt (wesw) 18-Dec-1998 Revision History: Andrew Ritz (andrewr) 7-Jul-1999 : added comments --*/ #include "sfcp.h" #pragma hdrstop // // this is a list of all of the files that we protect on the system. note that // there is no such thing as a tier 1 file anymore, only tier 2 files. // PPROTECT_FILE_ENTRY Tier2Files; // // this is the total number of files we're protecting // ULONG CountTier2Files; // // used to signal the watcher that the next change // type is expected to the this type and if so the // change should be ignored // ULONG* IgnoreNextChange = NULL; ULARGE_INTEGER LastExemptionTime; NTSTATUS InitializeUnicodeString( IN PWSTR StrVal, IN ULONG StrLen, OPTIONAL OUT PUNICODE_STRING String ) /*++ Routine Description: Initialize a unicode_string given a unicode string pointer. this function handles NULL strings and initializes the unicode string buffer to NULL in this case Arguments: StrVal - pointer to null terminated unicode string StrLen - length in characters of unicode string. if not specified, we use the length of the string. String - pointer to a UNICODE_STRING structure that is filled in by this function. Return Value: NTSTATUS code indicating outcome. --*/ { ASSERT(String != NULL); if (StrVal == NULL) { String->Length = 0; String->MaximumLength = 0; String->Buffer = NULL; return STATUS_SUCCESS; } // // if the length was specified by the user, use that, otherwise use the // string length // String->Length = StrLen ? (USHORT)StrLen : (USHORT)UnicodeLen(StrVal); // // just say that the length is twice what we calculated as the current // length. // String->MaximumLength = String->Length + (sizeof(WCHAR)*2); String->Buffer = (PWSTR) MemAlloc( String->MaximumLength ); if (String->Buffer == NULL) { return STATUS_NO_MEMORY; } RtlMoveMemory( String->Buffer, StrVal, String->Length ); return STATUS_SUCCESS; } ULONG SfcQueryRegDword( PCWSTR KeyNameStr, PCWSTR ValueNameStr, ULONG DefaultValue ) /*++ Routine Description: retrieve a DWORD from the registry. if the value is not present or cannot be retrieved, we use a default value. calls registry api's using NT apis instead of win32 apis. Arguments: KeyNameStr - contains registry keyname to look for value under. ValueNameStr - contains registry value to retreive. DefaultValue - if we have problems retreiving the registry key or it is not set, use this default value. Return Value: registry DWORD value or default value if registry cannot be retreived. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; // // Open the registry key. // ASSERT((KeyNameStr != NULL) && (ValueNameStr != NULL)); KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString( &KeyName, KeyNameStr ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't open %ws key: 0x%x", KeyNameStr, Status ); return DefaultValue; } // // Query the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtQueryValueKey( Key, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength ); // // cleanup // NtClose(Key); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't query value key (%ws): 0x%x", ValueNameStr, Status ); return DefaultValue; } ASSERT(KeyValueInfo->Type == REG_DWORD); // // return value // return *((PULONG)&KeyValueInfo->Data); } ULONG SfcQueryRegDwordWithAlternate( IN PCWSTR FirstKey, IN PCWSTR SecondKey, IN PCWSTR ValueNameStr, IN ULONG DefaultValue ) /*++ Routine Description: retrieve a DWORD from the registry. if the value is not present in the first key location, we look in the second key location. If the key cannot be retrieved, we use a default value. calls registry api's using NT apis instead of win32 apis. Arguments: FirstKey - contains first registry keyname to look for value under. SecondKey - contains registry keyname to look for value under. ValueNameStr - contains registry value to retreive. DefaultValue - if we have problems retreiving the registry key or it is not set, use this default value. Return Value: registry DWORD value or default value if registry cannot be retreived. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; BOOL FirstTime; PCWSTR p; // // Open the registry key. // FirstTime = TRUE; ASSERT((FirstKey != NULL) && (ValueNameStr != NULL) && (SecondKey != NULL)); TryAgain: p = FirstTime ? FirstKey : SecondKey; KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString( &KeyName, p ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status) && !FirstTime) { DebugPrint2( LVL_VERBOSE, L"can't open %ws key: 0x%x", p, Status ); return DefaultValue; } if (!NT_SUCCESS(Status)) { ASSERT( FirstTime == TRUE ); FirstTime = FALSE; goto TryAgain; } // // Query the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtQueryValueKey( Key, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength ); // // cleanup // NtClose(Key); if (!NT_SUCCESS(Status) && !FirstTime) { DebugPrint2( LVL_VERBOSE, L"can't query value key (%ws): 0x%x", ValueNameStr, Status ); return DefaultValue; } if (!NT_SUCCESS(Status)) { ASSERT( FirstTime == TRUE ); FirstTime = FALSE; goto TryAgain; } ASSERT(KeyValueInfo->Type == REG_DWORD); // // return value // return *((PULONG)&KeyValueInfo->Data); } PWSTR SfcQueryRegString( PCWSTR KeyNameStr, PCWSTR ValueNameStr ) /*++ Routine Description: retrieve a string from the registry. if the value is not present or cannot be retrieved, we return NULL. calls registry api's using NT apis instead of win32 apis. Arguments: KeyNameStr - contains registry keyname to look for value under. ValueNameStr - contains registry value to retreive. Return Value: unicode string pointer or NULL if registry cannot be retreived. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; PWSTR s; ASSERT((KeyNameStr != NULL) && (ValueNameStr != NULL)); // // Open the registry key. // RtlZeroMemory( (PVOID)ValueBuffer, VALUE_BUFFER_SIZE ); KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString( &KeyName, KeyNameStr ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't open %ws key: 0x%x", KeyNameStr, Status ); return NULL; } // // Query the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtQueryValueKey( Key, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength ); // // cleanup // NtClose(Key); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't query value key (%ws): 0x%x", ValueNameStr, Status ); return 0; } if (KeyValueInfo->Type == REG_MULTI_SZ) { DebugPrint1( LVL_VERBOSE, L"Warning: value key %ws is REG_MULTI_SZ, we will only return first string in list", ValueNameStr ); } else { ASSERT(KeyValueInfo->Type == REG_SZ || KeyValueInfo->Type == REG_EXPAND_SZ); } // // string length + 16 for slop // s = (PWSTR) MemAlloc( KeyValueInfo->DataLength + 16 ); if (s == NULL) { return NULL; } CopyMemory( s, KeyValueInfo->Data, KeyValueInfo->DataLength ); return s; } ULONG SfcQueryRegPath( IN PCWSTR KeyNameStr, IN PCWSTR ValueNameStr, IN PCWSTR DefaultValue OPTIONAL, OUT PWSTR Buffer OPTIONAL, IN ULONG BufferSize OPTIONAL ) /*++ Routine Description: retrieves a path from the registry. if the value is not present or cannot be retrieved, it returns the passed-in default string. The function writes up to BufferSize - 1 characters and appends a null. calls registry api's using NT apis instead of win32 apis. Arguments: KeyNameStr - contains registry keyname to look for value under. ValueNameStr - contains registry value to retreive. DefaultValue - the value returned in case of an error Buffer - the buffer that receives the string BufferSize - the size of Buffer in chars Return Value: the length of the value data in chars, including the null --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) ValueBuffer; ULONG ValueLength; ULONG RequiredSize = 1; // for null PCWSTR retval = NULL; ASSERT(KeyNameStr != NULL && ValueNameStr != NULL); ASSERT(0 == BufferSize || Buffer != NULL); if(BufferSize != 0) Buffer[0] = 0; // // Open the registry key. // RtlInitUnicodeString( &KeyName, KeyNameStr ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if(NT_SUCCESS(Status)) { // // Query the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtQueryValueKey( Key, &ValueName, KeyValuePartialInformation, (PVOID) KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength ); NtClose(Key); if(NT_SUCCESS(Status)) { ASSERT(KeyValueInfo->Type == REG_SZ || KeyValueInfo->Type == REG_EXPAND_SZ); retval = (PCWSTR) KeyValueInfo->Data; } } if(NULL == retval || 0 == retval[0]) retval = DefaultValue; if(retval != NULL) { RequiredSize = ExpandEnvironmentStrings(retval, Buffer, BufferSize); if(BufferSize != 0 && BufferSize < RequiredSize) Buffer[BufferSize - 1] = 0; } return RequiredSize; } PWSTR SfcQueryRegStringWithAlternate( IN PCWSTR FirstKey, IN PCWSTR SecondKey, IN PCWSTR ValueNameStr ) /*++ Routine Description: retrieve a string from the registry. if the value is not present or cannot be retrieved, we try the second key, then we return NULL. This calls registry api's using NT apis instead of win32 apis. Arguments: FirstKey - contains registry keyname to look for value under. SecondKey - 2nd key to look for value under ValueNameStr - contains registry value to retreive. Return Value: unicode string pointer or NULL if registry cannot be retreived. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; PWSTR s; BOOL FirstTime; PCWSTR p; ASSERT((FirstKey != NULL) && (ValueNameStr != NULL) && (SecondKey != NULL)); FirstTime = TRUE; TryAgain: p = FirstTime ? FirstKey : SecondKey; // // Open the registry key. // RtlZeroMemory( (PVOID)ValueBuffer, VALUE_BUFFER_SIZE ); KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString( &KeyName, p ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status) && !FirstTime) { DebugPrint2( LVL_VERBOSE, L"can't open %ws key: 0x%x", p, Status ); return NULL; } if (!NT_SUCCESS(Status)) { ASSERT( FirstTime == TRUE ); FirstTime = FALSE; goto TryAgain; } // // Query the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtQueryValueKey( Key, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength ); // // cleanup // NtClose(Key); if (!NT_SUCCESS(Status) && !FirstTime) { DebugPrint2( LVL_VERBOSE, L"can't query value key (%ws): 0x%x", ValueNameStr, Status ); return 0; } if (!NT_SUCCESS(Status)) { ASSERT( FirstTime == TRUE ); FirstTime = FALSE; goto TryAgain; } if (KeyValueInfo->Type == REG_MULTI_SZ) { DebugPrint1( LVL_VERBOSE, L"Warning: value key %ws is REG_MULTI_SZ, we will only return first string in list", ValueNameStr ); } else { ASSERT(KeyValueInfo->Type == REG_SZ || KeyValueInfo->Type == REG_EXPAND_SZ); } // // string length + 16 for slop // s = (PWSTR) MemAlloc( KeyValueInfo->DataLength + 16 ); if (s == NULL) { return NULL; } CopyMemory( s, KeyValueInfo->Data, KeyValueInfo->DataLength ); return s; } ULONG SfcWriteRegDword( PCWSTR KeyNameStr, PCWSTR ValueNameStr, ULONG Value ) /*++ Routine Description: set a REG_DWORD value in the registry. Calls registry api's using NT apis instead of win32 apis. Arguments: KeyNameStr - contains registry keyname to look for value under. ValueNameStr - contains registry value to set. Value - actual value to be set Return Value: win32 error code indicating outcome. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; ASSERT((KeyNameStr != NULL) && (ValueNameStr != NULL)); // // Open the registry key. // RtlInitUnicodeString( &KeyName, KeyNameStr ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_SET_VALUE, &ObjectAttributes); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { // // key doesn't exist, let's try to create one // Status = NtCreateKey( &Key, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, 0, NULL ); } if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't open %ws key: 0x%x", KeyNameStr, Status ); return(RtlNtStatusToDosError(Status)); } // // set the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtSetValueKey( Key, &ValueName, 0, REG_DWORD, &Value, sizeof(ULONG) ); // // cleanup and leave // NtClose(Key); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't set value key (%ws): 0x%x", ValueNameStr, Status ); } return(RtlNtStatusToDosError(Status)); } DWORD SfcWriteRegString( PCWSTR KeyNameStr, PCWSTR ValueNameStr, PCWSTR Value ) /*++ Routine Description: set a REG_SZ value in the registry. Calls registry api's using NT apis instead of win32 apis. Arguments: KeyNameStr - contains registry keyname to look for value under. ValueNameStr - contains registry value to set. Value - actual value to be set Return Value: Win32 error code indicating outcome. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; ASSERT((KeyNameStr != NULL) && (ValueNameStr != NULL)); // // Open the registry key. // RtlInitUnicodeString( &KeyName, KeyNameStr ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_SET_VALUE, &ObjectAttributes); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { // // key doesn't exist, let's try to create one // Status = NtCreateKey( &Key, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, 0, NULL ); } if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't open %ws key: 0x%x", KeyNameStr, Status ); return(RtlNtStatusToDosError(Status)); } // // set the key value. // RtlInitUnicodeString( &ValueName, ValueNameStr ); Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, (PWSTR)Value, UnicodeLen(Value) ); // // cleanup // NtClose(Key); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_VERBOSE, L"can't set value key (%ws): 0x%x", ValueNameStr, Status ); return(RtlNtStatusToDosError(Status)) ; } return ERROR_SUCCESS; } #if 0 DWORD WsInAWorkgroup( VOID ) /*++ Routine Description: This function determines whether we are a member of a domain, or of a workgroup. First it checks to make sure we're running on a Windows NT system (otherwise we're obviously in a domain) and if so, queries LSA to get the Primary domain SID, if this is NULL, we're in a workgroup. If we fail for some random unexpected reason, we'll pretend we're in a domain (it's more restrictive). Arguments: None Return Value: TRUE - We're in a workgroup FALSE - We're in a domain --*/ { NT_PRODUCT_TYPE ProductType; OBJECT_ATTRIBUTES ObjectAttributes; LSA_HANDLE Handle; NTSTATUS Status; PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL; DWORD Result = FALSE; Status = RtlGetNtProductType( &ProductType ); if (!NT_SUCCESS( Status )) { DebugPrint( LVL_MINIMAL, L"Could not get Product type" ); return FALSE; } if (ProductType == NtProductLanManNt) { return FALSE; } InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); Status = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &Handle ); if (!NT_SUCCESS(Status)) { DebugPrint( LVL_MINIMAL, L"Could not open LSA Policy Database" ); return FALSE; } Status = LsaQueryInformationPolicy( Handle, PolicyPrimaryDomainInformation, (LPVOID)&PolicyPrimaryDomainInfo ); if (NT_SUCCESS(Status)) { if (PolicyPrimaryDomainInfo->Sid == NULL) { Result = TRUE; } else { Result = FALSE; } } if (PolicyPrimaryDomainInfo) { LsaFreeMemory( (PVOID)PolicyPrimaryDomainInfo ); } LsaClose( Handle ); return Result; } BOOL WaitForMUP( DWORD dwMaxWait ) /*++ Routine Description: Waits for MUP to initialize by looking for the event that is signalled when MUP is ready Arguments: dwMaxWait - amount of time we'll wait for MUP to initialize Return Value: TRUE - MUP is initialized FALSE - could not confirm MUP is initialized --*/ { HANDLE hEvent; BOOL bResult; INT i = 0; if (WsInAWorkgroup()) { return TRUE; } DebugPrint(LVL_MINIMAL, L"waiting for mup..."); // // Try to open the event // do { hEvent = OpenEvent( SYNCHRONIZE, FALSE, L"wkssvc: MUP finished initializing event" ); if (hEvent) { DebugPrint(LVL_MINIMAL, L"opened the mup event"); break; } if (GetLastError() != ERROR_FILE_NOT_FOUND) { break; } DebugPrint(LVL_MINIMAL, L"mup event does not yet exist, waiting..."); Sleep(2000); i++; } while (i < 30); if (!hEvent) { DebugPrint1(LVL_MINIMAL, L"Failed to open MUP event, ec=%d\n", GetLastError()); return FALSE; } // // Wait for the event to be signalled // bResult = (WaitForSingleObject (hEvent, dwMaxWait) == WAIT_OBJECT_0); // // Clean up // CloseHandle (hEvent); return bResult; } #endif NTSTATUS ExpandPathString( IN PWSTR PathString, IN ULONG PathStringLength, OUT PUNICODE_STRING FileName, OPTIONAL OUT PUNICODE_STRING PathName, OUT PUNICODE_STRING FullPathName OPTIONAL ) /*++ Routine Description: Routine takes a source string containing environment variables, expand this into the full path. Then it either copies this into a path, file, and full path, as requested. Arguments: PathString - source path string PathStringLength - source path string length FileName - receives filename part of the path if specified. If not specified, we only want the path part PathName - receives the path part of the expanded source. If FileName is not specified, we fill in pathname with the entire expanded path FullPathName - if FileName is specified, then this is filled in with the complete path. Return Value: NTSTATUS code indicating outcome. --*/ { NTSTATUS Status; UNICODE_STRING NewPath; UNICODE_STRING SrcPath; PWSTR FilePart; ASSERT((PathString != NULL)); ASSERT((FileName == NULL) ? (PathName != NULL) : ((FullPathName != NULL) && (PathName != NULL))); // // turn the pathstring and length into a UNICODE_STRING // SrcPath.Length = (USHORT)PathStringLength; SrcPath.MaximumLength = SrcPath.Length; SrcPath.Buffer = PathString; // // create a new scratch UNICODE_STRING // NewPath.Length = 0; NewPath.MaximumLength = (MAX_PATH*2) * sizeof(WCHAR); NewPath.Buffer = (PWSTR) MemAlloc( NewPath.MaximumLength ); if (NewPath.Buffer == NULL) { return STATUS_NO_MEMORY; } // // expand source environment string into scratch string // Status = RtlExpandEnvironmentStrings_U( NULL, &SrcPath, &NewPath, NULL ); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_MINIMAL, L"ExpandEnvironmentStrings failed for [%ws], ec=%08x", PathString, Status ); goto exit; } // // convert scratch string to lowercase // MyLowerString( NewPath.Buffer, NewPath.Length/sizeof(WCHAR) ); // // if filename isn't specified, then just copy the string into the pathname // and exit // if (FileName == NULL) { PathName->Length = NewPath.Length; PathName->MaximumLength = NewPath.MaximumLength; PathName->Buffer = NewPath.Buffer; return(STATUS_SUCCESS); } else { // // copy the full string into the fullpathname // Status = InitializeUnicodeString( NewPath.Buffer, NewPath.Length, FullPathName ); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_MINIMAL, L"InitializeUnicodeString failed for [%ws], ec=%08x", NewPath.Buffer, Status ); goto exit; } // // separate the path part from the file part // FilePart = wcsrchr( NewPath.Buffer, L'\\' ); if (FilePart == NULL) { Status = STATUS_NO_MEMORY; goto exit; } *FilePart = 0; FilePart += 1; Status = InitializeUnicodeString( NewPath.Buffer, 0, PathName ); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_MINIMAL, L"InitializeUnicodeString failed for [%ws], ec=%08x", NewPath.Buffer, Status ); goto exit; } Status = InitializeUnicodeString( FilePart, 0, FileName ); if (!NT_SUCCESS(Status)) { DebugPrint2( LVL_MINIMAL, L"InitializeUnicodeString failed for [%ws], ec=%08x", FilePart, Status ); } } FilePart -= 1; *FilePart = L'\\'; exit: MemFree( NewPath.Buffer ); return Status; } BOOL SfcDisableDllCache( BOOL LogMessage ) /*++ Routine Description: Routine disables the dllcache functionality. Specifically, we set the dll cache directory to the default and sets the cache size to zero. So we will never add files in the cache. We also log an error message if requested. Arguments: LogMessage - if TRUE, we log a message indicating the cache is disabled Return Value: NTSTATUS code indicating outcome. --*/ { PWSTR CacheDefault = DLLCACHE_DIR_DEFAULT; NTSTATUS Status; Status = ExpandPathString( CacheDefault, UnicodeLen(CacheDefault), NULL, &SfcProtectedDllPath, NULL ); if (NT_SUCCESS(Status)) { DebugPrint1(LVL_MINIMAL, L"default cache dir name=[%ws]", SfcProtectedDllPath.Buffer); SfcProtectedDllFileDirectory = SfcOpenDir( TRUE, TRUE, SfcProtectedDllPath.Buffer ); if (SfcProtectedDllFileDirectory == NULL) { DebugPrint(LVL_MINIMAL, L"could not open the cache dir, need to create"); SfcProtectedDllFileDirectory = SfcCreateDir( SfcProtectedDllPath.Buffer, TRUE ); if (SfcProtectedDllFileDirectory == NULL) { DebugPrint( LVL_MINIMAL, L"Cannot create ProtectedDllPath" ); } } } else { // // not enough memory...we're toast // DebugPrint( LVL_MINIMAL, L"Cannot open ProtectedDllPath" ); return(FALSE); } // // set the quota to zero // SFCQuota = 0; if (LogMessage) { SfcReportEvent( MSG_DLLCACHE_INVALID, NULL, NULL, 0 ); } return(TRUE); } NTSTATUS SfcInitializeDllList( IN PPROTECT_FILE_ENTRY Files, IN ULONG NumFiles, OUT PULONG Count ) /*++ Routine Description: Routine takes an empty array of SFC_REGISTRY_VALUE structures stored in the global SfcProtectedDllsList global and assigns the data from an array of PROTECT_FILE_ENTRY structures into these structures. Arguments: Files - pointer to first element in array of PROTECT_FILE_ENTRY structures NumFiles - number of elements in array of structures Count - receives number of files we correctly setup Return Value: NTSTATUS code indicating outcome. --*/ { NTSTATUS Status,FinalStatus, LoopStatus; ULONG Index; PSFC_REGISTRY_VALUE RegVal; ASSERT( (Files != NULL) && (SfcProtectedDllsList != NULL) && (Count != NULL) ); LoopStatus = FinalStatus = STATUS_SUCCESS; for (Index=0; IndexFileName, &RegVal->DirName, &RegVal->FullPathName ); if (!NT_SUCCESS(Status)) { // // if we have a problem initializing one of the array elements // keep going // DebugPrint1( LVL_MINIMAL, L"ExpandPathString failed, ec=%08x", Status ); FinalStatus = Status; continue; } // // set the layout inf name and the source file names if they are present // Status = InitializeUnicodeString( Files[Index].InfName, 0, &RegVal->InfName ); if (!NT_SUCCESS(Status)) { DebugPrint1( LVL_MINIMAL, L"InitializeUnicodeString failed, ec=%08x", Status ); LoopStatus = FinalStatus = Status; } Status = InitializeUnicodeString( Files[Index].SourceFileName, 0, &RegVal->SourceFileName ); if (!NT_SUCCESS(Status)) { DebugPrint1( LVL_MINIMAL, L"InitializeUnicodeString failed, ec=%08x", Status ); LoopStatus = FinalStatus = Status; } if (NT_SUCCESS(Status)) { *Count += 1; } // // WinSxs work (jonwis) This is NULL in all cases, unless this entry is // added by WinSxs (see dirwatch.c) // RegVal->pvWinSxsCookie = NULL; LoopStatus = STATUS_SUCCESS; } Status = FinalStatus; if (NT_SUCCESS(Status)) { ASSERT(*Count == NumFiles); } return(Status); } NTSTATUS SfcInitializeDllLists( PSFC_GET_FILES pfGetFiles ) /*++ Routine Description: Initialize the list of files we're going to protect. Arguments: None. Return Value: NTSTATUS code indicating outcome. --*/ { NTSTATUS Status; PWSTR s; BOOL FreeMem = TRUE; // // make sure we only call this guy once // if (SfcProtectedDllCount) { return STATUS_SUCCESS; } DebugPrint(LVL_MINIMAL, L"entering SfcInitializeDllLists()"); // // get the dllcache directory and store it into to SfcProtectedDllPath // global // s = SfcQueryRegStringWithAlternate( REGKEY_POLICY, REGKEY_WINLOGON, REGVAL_SFCDLLCACHEDIR ); if (s == NULL) { s = DLLCACHE_DIR_DEFAULT; FreeMem = FALSE; } Status = ExpandPathString( s, UnicodeLen(s), NULL, &SfcProtectedDllPath, NULL ); if (NT_SUCCESS(Status)) { WCHAR DontCare[MAX_PATH]; DWORD DriveType; DebugPrint1(LVL_MINIMAL, L"cache dir name=[%ws]", SfcProtectedDllPath.Buffer); DriveType = SfcGetPathType( SfcProtectedDllPath.Buffer, DontCare, UnicodeChars(DontCare)); if (DriveType != PATH_LOCAL) { DebugPrint2(LVL_MINIMAL, L"cache dir %ws does not appear to be a local path (type %d), we are disabling cache functionality", SfcProtectedDllPath.Buffer, DriveType); SfcDisableDllCache( SFCDisable != SFC_DISABLE_SETUP ); goto init; } // // get a handle to the dll cache directory // SfcProtectedDllFileDirectory = SfcOpenDir( TRUE, TRUE, SfcProtectedDllPath.Buffer ); if (SfcProtectedDllFileDirectory == NULL) { DebugPrint(LVL_MINIMAL, L"could not open the cache dir, need to create"); SfcProtectedDllFileDirectory = SfcCreateDir( SfcProtectedDllPath.Buffer, TRUE ); if (SfcProtectedDllFileDirectory == NULL) { DebugPrint( LVL_MINIMAL, L"Cannot open ProtectedDllPath" ); SfcDisableDllCache( SFCDisable != SFC_DISABLE_SETUP ); } else { // // force a scan if we just created the dll cache // SFCScan = SFC_SCAN_ALWAYS; } } } else { // // dll cache path in registry must be bogus...use default path and // set the quota to zero so the cache is effectively disabled. // SfcDisableDllCache( SFCDisable != SFC_DISABLE_SETUP ); } init: if (FreeMem) { MemFree( s ); } DebugPrint1(LVL_MINIMAL, L"cache dir name=[%ws]", SfcProtectedDllPath.Buffer); ASSERT( SfcProtectedDllFileDirectory != NULL ); // // now that we have the dll cache initialized, now retrieve the list of // files that we will protect. The list of files currently resides in // sfcfiles.dll. // ASSERT(pfGetFiles != NULL); Status = pfGetFiles( &Tier2Files, &CountTier2Files ); if (!NT_SUCCESS(Status)) { return Status; } // // Take the file list (we only have the tier2 list) and build an array of // SFC_REGISTRY_VALUE structures and store into the SfcProtectedDllsList // global // SfcProtectedDllsList = (PSFC_REGISTRY_VALUE) MemAlloc( sizeof(SFC_REGISTRY_VALUE)*CountTier2Files ); if (SfcProtectedDllsList == NULL) { return(STATUS_NO_MEMORY); } ASSERT(SfcProtectedDllCount == 0); // // now associate the data in our tier2 list with each of these structures // in the array // Status = SfcInitializeDllList( Tier2Files, CountTier2Files, &SfcProtectedDllCount ); if (CountTier2Files != SfcProtectedDllCount) { DebugPrint2( LVL_MINIMAL, L"incorrect number of files in list: required count: %d actual count %d", CountTier2Files, SfcProtectedDllCount ); ASSERT(!NT_SUCCESS(Status)); } else { IgnoreNextChange = (ULONG*) MemAlloc(SfcProtectedDllCount * sizeof(ULONG)); if(NULL == IgnoreNextChange) { Status = STATUS_NO_MEMORY; } } DebugPrint(LVL_MINIMAL, L"leaving SfcInitializeDllLists()"); return(Status); }