windows-nt/Source/XPSP1/NT/base/subsys/sm/sfc/dll/registry.c

1473 lines
35 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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; Index<NumFiles; Index++) {
RegVal = &SfcProtectedDllsList[*Count];
//
// set the directory name, filename and full path members
//
Status = ExpandPathString(
Files[Index].FileName,
UnicodeLen(Files[Index].FileName),
&RegVal->FileName,
&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);
}