You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1254 lines
41 KiB
1254 lines
41 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sxsisol.c
|
|
|
|
Abstract:
|
|
|
|
Current directory support
|
|
|
|
Author:
|
|
|
|
sxsdev(mgrier, jaykrell, xiaoyuw, jonwis) 04-Jun-2002
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.cxx"
|
|
#include "nt.h"
|
|
#include "ntos.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "string.h"
|
|
#include "ctype.h"
|
|
#include "sxstypes.h"
|
|
#include "ntdllp.h"
|
|
|
|
//
|
|
// This seems bizarre that these constants are here but I don't see them elsewhere.
|
|
|
|
#define SXSISOL_DOT L"."
|
|
#define SXSISOL_CONSTANT_STRING_U(s) { sizeof(s) - sizeof((s)[0]), sizeof(s), (PWSTR)(s) }
|
|
const static UNICODE_STRING sxsisol_ExtensionDelimiters = SXSISOL_CONSTANT_STRING_U(SXSISOL_DOT);
|
|
|
|
#define IFNOT_NTSUCCESS_EXIT(x) \
|
|
do { \
|
|
Status = (x); \
|
|
if (!NT_SUCCESS(Status)) \
|
|
goto Exit; \
|
|
} while (0)
|
|
|
|
#define PARAMETER_CHECK(x) \
|
|
do { \
|
|
if (!(x)) { \
|
|
Status = STATUS_INVALID_PARAMETER; \
|
|
goto Exit; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define INTERNAL_ERROR_CHECK(x) \
|
|
do { \
|
|
if (!(x)) { \
|
|
RtlAssert("Internal error check failed", __FILE__, __LINE__, #x); \
|
|
Status = STATUS_INTERNAL_ERROR; \
|
|
goto Exit; \
|
|
} \
|
|
} while (0)
|
|
|
|
typedef struct _SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS {
|
|
RTL_UNICODE_STRING_BUFFER PublicUnicodeStringBuffer;
|
|
PUNICODE_STRING PrivatePreallocatedString;
|
|
PUNICODE_STRING PrivateDynamicallyAllocatedString;
|
|
PUNICODE_STRING *PrivateUsedString;
|
|
BOOLEAN Valid;
|
|
} SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS, *PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS;
|
|
|
|
typedef RTL_STRING_LENGTH_TYPE *PRTL_STRING_LENGTH_TYPE;
|
|
|
|
static
|
|
void
|
|
sxsisol_InitUnicodeStringBufferAroundUnicodeStrings(
|
|
PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This,
|
|
PUNICODE_STRING PreallocatedString,
|
|
PUNICODE_STRING DynamicallyAllocatedString,
|
|
PUNICODE_STRING *UsedString
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Success(
|
|
PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
|
|
);
|
|
|
|
static
|
|
void
|
|
sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Failure(
|
|
PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_PathHasExtension(
|
|
IN PCUNICODE_STRING Path,
|
|
OUT bool &rfHasExtension
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_PathAppendDefaultExtension(
|
|
IN PCUNICODE_STRING Path,
|
|
IN PCUNICODE_STRING DefaultExtension OPTIONAL,
|
|
OUT PRTL_UNICODE_STRING_BUFFER PathWithExtension,
|
|
OUT bool &rfAppended
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_CanonicalizeFullPathFileName(
|
|
IN OUT PUNICODE_STRING FileName, // could be a fullpath or relative path, and only fullpath are dealt with
|
|
IN PUNICODE_STRING FullPathPreallocatedString,
|
|
IN OUT PUNICODE_STRING FullPathDynamicString
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_RespectDotLocal(
|
|
IN PCUNICODE_STRING FileName,
|
|
OUT PRTL_UNICODE_STRING_BUFFER FullPathResult,
|
|
IN OUT ULONG *OutFlags OPTIONAL
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_SearchActCtxForDllName(
|
|
IN PCUNICODE_STRING FileName,
|
|
IN BOOLEAN fExistenceTest,
|
|
IN OUT SIZE_T &TotalLength,
|
|
IN OUT ULONG *OutFlags,
|
|
OUT PRTL_UNICODE_STRING_BUFFER FullPathResult
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
sxsisol_ExpandEnvironmentStrings_UEx(
|
|
IN PVOID Environment OPTIONAL,
|
|
IN PCUNICODE_STRING Source,
|
|
OUT PRTL_UNICODE_STRING_BUFFER Destination
|
|
);
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlDosApplyFileIsolationRedirection_Ustr(
|
|
ULONG Flags,
|
|
PCUNICODE_STRING FileNameIn,
|
|
PCUNICODE_STRING DefaultExtension,
|
|
PUNICODE_STRING PreAllocatedString,
|
|
PUNICODE_STRING DynamicallyAllocatedString,
|
|
PUNICODE_STRING *OutFullPath,
|
|
ULONG *OutFlags,
|
|
SIZE_T *FilePartPrefixCch,
|
|
SIZE_T *BytesRequired
|
|
)
|
|
|
|
// ++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function implements the basic capability of taking a path
|
|
// and applying any appropriate isolation/redirection to it.
|
|
//
|
|
// Notably it today includes redirection for Fusion manifest-based
|
|
// isolation and .local isolation.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Flags - Flags affecting the behavior of this function.
|
|
//
|
|
// RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL
|
|
// Controls whether this function applies ".local" semantics
|
|
// to the redirection.
|
|
//
|
|
// FileNameIn - Path that is being checked for isolation/redirection.
|
|
// This may be "any" sort of Win32/DOS path - absolute, relative,
|
|
// leaf name only.
|
|
//
|
|
// .local redirection applies equally to all kinds of paths.
|
|
//
|
|
// Fusion manifest-based redirection normally applies to relative
|
|
// paths, except when a component is listed in the "system default
|
|
// manifest", in which case attempts to load the DLL by full path
|
|
// (e.g. "c:\windows\system32\comctl32.dll") need to be redirected
|
|
// into the side-by-side store.
|
|
//
|
|
// DefaultExtension
|
|
//
|
|
// Default extension to apply to FileNameIn if it does not have
|
|
// an extension.
|
|
//
|
|
// PreAllocatedString (optional)
|
|
//
|
|
// UNICODE_STRING that this function can use to return results
|
|
// where the caller is responsible for managing the storage
|
|
// associated with the allocation. The function may use this
|
|
// string and will not attempt to dynamically resize it;
|
|
// if the space required exceeds the maximum length of
|
|
// the preallocated string, either dynamic storage is allocated
|
|
// or the function will fail with the buffer-too-small
|
|
// NTSTATUS code.
|
|
//
|
|
// If NULL is passed for this parameter, the space for the
|
|
// output and temporary strings will be dynamically allocated.
|
|
//
|
|
// DynamicallyAllocatedString (optional)
|
|
//
|
|
// UNICODE_STRING which if present should refer to an "empty"
|
|
// UNICODE_STRING (sizes an buffer pointer 0 and NULL
|
|
// respectively). If PreAllocatedString was not passed or
|
|
// is not big enough for the results, the information about
|
|
// the dynamically allocated string is returned in this
|
|
// parameter. If no dynamic allocation was performed, this
|
|
// string is left null.
|
|
//
|
|
// OutFullPath (optional)
|
|
//
|
|
// PUNICODE_STRING pointer that is filled in with the
|
|
// UNICODE_STRING pointer passed in as PreAllocatedString or
|
|
// DynamicallyAllocatedString as appropriate. If only one
|
|
// of the two (PreAllocatedString, DynamicallyAllocatedString)
|
|
// is passed in, this parameter may be omitted.
|
|
//
|
|
// FilePartPrefixCch
|
|
//
|
|
// Returned number of characters in the resultant path up to
|
|
// and including the last path separator.
|
|
//
|
|
// BytesRequired
|
|
//
|
|
// Returned byte count of the size of the string needed for
|
|
// storing the resultant full path.
|
|
//
|
|
// The expected calling sequence for RtlDosApplyFileIsolationRedirection_Ustr() is something
|
|
// like this:
|
|
//
|
|
// {
|
|
// WCHAR Buffer[MAX_PATH];
|
|
// UNICODE_STRING PreAllocatedString;
|
|
// UNICODE_STRING DynamicallyAllocatedString = { 0, 0, NULL };
|
|
// PUNICODE_STRING FullPath;
|
|
//
|
|
// PreAllocatedString.Length = 0;
|
|
// PreAllocatedString.MaximumLength = sizeof(Buffer);
|
|
// PreAllocatedString.Buffer = Buffer;
|
|
//
|
|
// Status = RtlDosApplyFileIsolationRedirection_Ustr(
|
|
// Flags,
|
|
// FileToCheck,
|
|
// DefaultExtensionToApply, // for example ".DLL"
|
|
// &PreAllocatedString,
|
|
// &DynamicallyAllocatedString,
|
|
// &FullPath);
|
|
// if (!NT_SUCCESS(Status)) return Status;
|
|
// // now code uses FullPath as the complete path name...
|
|
//
|
|
// // In exit paths, always free the dynamic string:
|
|
// RtlFreeUnicodeString(&DynamicallyAllocatedString);
|
|
// }
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the success/failure of the function.
|
|
//
|
|
// --
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
|
|
//
|
|
// perf and frame size problems here..
|
|
//
|
|
WCHAR FullPathPreallocatedBuffer[64]; // seldom used
|
|
UNICODE_STRING FullPathDynamicString = { 0, 0, NULL };
|
|
UNICODE_STRING FullPathPreallocatedString = { 0, sizeof(FullPathPreallocatedBuffer), FullPathPreallocatedBuffer };
|
|
|
|
//
|
|
// This is where we append .dll (or any other extension).
|
|
// This is considered an unusual case -- when people say LoadLibrary(kernel32) instead of LoadLibrary(kernel32.dll).
|
|
//
|
|
UCHAR FileNameWithExtensionStaticBuffer[16 * sizeof(WCHAR)];
|
|
RTL_UNICODE_STRING_BUFFER FileNameWithExtensionBuffer;
|
|
|
|
UNICODE_STRING FileName;
|
|
|
|
SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS StringWrapper;
|
|
const PRTL_UNICODE_STRING_BUFFER FullPathResult = &StringWrapper.PublicUnicodeStringBuffer;
|
|
SIZE_T TotalLength = 0;
|
|
USHORT FilePartPrefixCb = 0;
|
|
ULONG OutFlagsTemp = 0; // local copy; we'll copy out on success
|
|
|
|
// Initialize out parameters first
|
|
if (OutFlags != NULL)
|
|
*OutFlags = 0;
|
|
|
|
if (FilePartPrefixCch != NULL)
|
|
*FilePartPrefixCch = 0;
|
|
|
|
if (BytesRequired != NULL)
|
|
*BytesRequired = (DOS_MAX_PATH_LENGTH * sizeof(WCHAR)); // sleazy, but I doubt it was usually correct
|
|
|
|
if (DynamicallyAllocatedString != NULL) {
|
|
RtlInitEmptyUnicodeString(DynamicallyAllocatedString, NULL, 0);
|
|
}
|
|
|
|
//
|
|
// step1 : initialization
|
|
//
|
|
RtlInitUnicodeStringBuffer(
|
|
&FileNameWithExtensionBuffer,
|
|
FileNameWithExtensionStaticBuffer,
|
|
sizeof(FileNameWithExtensionStaticBuffer));
|
|
|
|
sxsisol_InitUnicodeStringBufferAroundUnicodeStrings(
|
|
&StringWrapper,
|
|
PreAllocatedString,
|
|
DynamicallyAllocatedString,
|
|
OutFullPath);
|
|
|
|
// Valid input conditions:
|
|
// 1. You have to have a filename
|
|
// 2. If you specify both the preallocated buffer and the dynamically
|
|
// allocated buffer, you need the OutFullPath parameter to detect
|
|
// which was actually populated.
|
|
// 3. If you ask for the file part prefix cch, you need an output
|
|
// buffer; otherwise you can't know what the cch is relative to.
|
|
|
|
PARAMETER_CHECK((Flags & ~(RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL)) == 0);
|
|
PARAMETER_CHECK(FileNameIn != NULL);
|
|
PARAMETER_CHECK(
|
|
!(((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL) && (FilePartPrefixCch != NULL)) ||
|
|
((PreAllocatedString != NULL) && (DynamicallyAllocatedString != NULL) && (OutFullPath == NULL))));
|
|
|
|
FileName = *FileNameIn;
|
|
|
|
//
|
|
// step2: append extension (".dll" usually) if needed
|
|
//
|
|
bool fAppended;
|
|
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
sxsisol_PathAppendDefaultExtension(
|
|
&FileName,
|
|
DefaultExtension,
|
|
&FileNameWithExtensionBuffer,
|
|
fAppended));
|
|
|
|
if (fAppended)
|
|
FileName = FileNameWithExtensionBuffer.String;
|
|
|
|
//
|
|
// step3: For fullpaths, canonicalize .. and . and such.
|
|
// Comments:
|
|
// We do this so fullpaths like c:\windows\.\system32\comctl32.dll can be correctly
|
|
// redirected to "system default".
|
|
//
|
|
// It'd be nice to use the buffers our caller gave us here, but it is a bit tricky.
|
|
//
|
|
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
sxsisol_CanonicalizeFullPathFileName(
|
|
&FileName, // FileName could be reset inside this func
|
|
&FullPathPreallocatedString,
|
|
&FullPathDynamicString));
|
|
|
|
//
|
|
// Step4: Deal with .local if existed
|
|
//
|
|
|
|
if ((Flags & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL) &&
|
|
(NtCurrentPeb()->ProcessParameters != NULL) &&
|
|
(NtCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROC_DLL_REDIRECTION_LOCAL)) {
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
sxsisol_RespectDotLocal(
|
|
&FileName,
|
|
FullPathResult,
|
|
&OutFlagsTemp));
|
|
}
|
|
|
|
// If it was not redirected by .local, try activation contexts/manifests
|
|
if (!(OutFlagsTemp & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT)) {
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
sxsisol_SearchActCtxForDllName(
|
|
&FileName,
|
|
((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL))? TRUE : FALSE,
|
|
TotalLength,
|
|
OutFlags,
|
|
FullPathResult));
|
|
|
|
}
|
|
|
|
// we got the path but the input buffer is not big-enough
|
|
if ((DynamicallyAllocatedString == NULL) && (PreAllocatedString != NULL) && (FullPathResult->String.Buffer != PreAllocatedString->Buffer))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL; // no dynamic buffer, only a small static buffer
|
|
goto Exit;
|
|
}
|
|
|
|
if (FilePartPrefixCch != NULL) {
|
|
//
|
|
// We should have a full path at this point. Compute the length of the
|
|
// string up through the last path separator.
|
|
//
|
|
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
RtlFindCharInUnicodeString(
|
|
RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
|
|
&FullPathResult->String,
|
|
&RtlDosPathSeperatorsString,
|
|
&FilePartPrefixCb));
|
|
// The prefix length from RtlFindCharInUnicodeString does not include
|
|
// the pattern character matched. Convert from byte count to character
|
|
// count and include space for the separator.
|
|
*FilePartPrefixCch = (FilePartPrefixCb / sizeof(WCHAR)) + 1;
|
|
}
|
|
|
|
IFNOT_NTSUCCESS_EXIT(sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Success(&StringWrapper));
|
|
|
|
if (OutFlags != NULL)
|
|
*OutFlags = OutFlagsTemp;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
if (!NT_SUCCESS(Status))
|
|
sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Failure(&StringWrapper);
|
|
|
|
RtlFreeUnicodeString(&FullPathDynamicString);
|
|
RtlFreeUnicodeStringBuffer(&FileNameWithExtensionBuffer);
|
|
|
|
// Map section not found back to key not found so that callers don't
|
|
// have to worry about sections being present vs. the lookup key
|
|
// being present.
|
|
ASSERT(Status != STATUS_SXS_SECTION_NOT_FOUND);
|
|
INTERNAL_ERROR_CHECK(Status != STATUS_SXS_SECTION_NOT_FOUND);
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
sxsisol_InitUnicodeStringBufferAroundUnicodeStrings(
|
|
PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This,
|
|
PUNICODE_STRING PreallocatedString,
|
|
PUNICODE_STRING DynamicallyAllocatedString,
|
|
PUNICODE_STRING *UsedString
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Initialize a wrapper around the UNICODE_STRING pair
|
|
// (preallocated vs. dynamically allocated) and the
|
|
// actual PUNICODE_STRING which holds the value.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// This
|
|
// Pointer to the wrapper struct to initialize.
|
|
//
|
|
// PreallocatedString (optional)
|
|
// Pointer to a UNICODE_STRING that holds the preallocated
|
|
// buffer.
|
|
//
|
|
// DynamicallyAllocatedString (optional)
|
|
// Pointer to a UNICODE_STRING which will store information
|
|
// about any dynamic allocations done to support the
|
|
// RTL_UNICODE_STRING_BUFFER manipulations.
|
|
//
|
|
// UsedString
|
|
// Pointer to the PUNICODE_STRING which will be updated
|
|
// to indicate which of PreallocatedString or
|
|
// DynamicallyAllocatedString were actually used.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success or failure of
|
|
// the function.
|
|
//
|
|
//--
|
|
|
|
{
|
|
ASSERT(This != NULL);
|
|
|
|
if (PreallocatedString != NULL) {
|
|
RtlInitUnicodeStringBuffer(
|
|
&This->PublicUnicodeStringBuffer,
|
|
(PUCHAR)PreallocatedString->Buffer,
|
|
PreallocatedString->MaximumLength);
|
|
} else
|
|
RtlInitUnicodeStringBuffer(&This->PublicUnicodeStringBuffer, NULL, 0);
|
|
|
|
This->PrivatePreallocatedString = PreallocatedString;
|
|
This->PrivateDynamicallyAllocatedString = DynamicallyAllocatedString;
|
|
This->PrivateUsedString = UsedString;
|
|
This->Valid = TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Success(
|
|
PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Cleans up use of a SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS
|
|
// structure, performing appropriate cleanup for success vs. failure
|
|
// cases.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// This
|
|
// Pointer to the wrapper struct to clean up
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success or failure of
|
|
// the function.
|
|
//
|
|
//--
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
|
|
INTERNAL_ERROR_CHECK(This != NULL);
|
|
|
|
if (This->Valid) {
|
|
RTL_UNICODE_STRING_BUFFER &rUSB = This->PublicUnicodeStringBuffer;
|
|
UNICODE_STRING &rUS = rUSB.String;
|
|
|
|
INTERNAL_ERROR_CHECK(
|
|
(This->PrivateDynamicallyAllocatedString == NULL) ||
|
|
(This->PrivateDynamicallyAllocatedString->Buffer == NULL));
|
|
|
|
if ((This->PrivatePreallocatedString != NULL) &&
|
|
(This->PrivatePreallocatedString->Buffer == rUS.Buffer)) {
|
|
|
|
INTERNAL_ERROR_CHECK(rUS.Length <= This->PrivatePreallocatedString->MaximumLength);
|
|
This->PrivatePreallocatedString->Length = rUS.Length;
|
|
|
|
INTERNAL_ERROR_CHECK(This->PrivateUsedString != NULL);
|
|
*This->PrivateUsedString = This->PrivatePreallocatedString;
|
|
} else if (This->PrivateDynamicallyAllocatedString != NULL) {
|
|
*This->PrivateDynamicallyAllocatedString = rUS;
|
|
|
|
INTERNAL_ERROR_CHECK(This->PrivateUsedString != NULL);
|
|
*This->PrivateUsedString = This->PrivateDynamicallyAllocatedString;
|
|
} else {
|
|
RtlFreeUnicodeStringBuffer(&rUSB);
|
|
//Status = STATUS_NAME_TOO_LONG;
|
|
//goto Exit;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Exit:
|
|
if (This != NULL) {
|
|
RtlZeroMemory(This, sizeof(*This));
|
|
ASSERT(!This->Valid);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Failure(
|
|
PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Cleans up use of a SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS
|
|
// structure, performing appropriate cleanup for failure cases.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// This
|
|
// Pointer to the wrapper struct to initialize.
|
|
//
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None
|
|
//
|
|
//--
|
|
|
|
{
|
|
ASSERT(This != NULL);
|
|
|
|
if (This->Valid) {
|
|
if (This->PrivateDynamicallyAllocatedString != NULL) {
|
|
ASSERT(This->PrivateDynamicallyAllocatedString->Buffer == NULL);
|
|
}
|
|
|
|
RtlFreeUnicodeStringBuffer(&This->PublicUnicodeStringBuffer);
|
|
}
|
|
|
|
RtlZeroMemory(This, sizeof(*This));
|
|
ASSERT(!This->Valid);
|
|
}
|
|
|
|
NTSTATUS
|
|
sxsisol_PathHasExtension(
|
|
IN PCUNICODE_STRING Path,
|
|
OUT bool &rfHasExtension
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Locate the extension of a file in a path.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Path
|
|
// Path in which to find extension.
|
|
//
|
|
// rfHasExtension
|
|
// On a successful outcome of the call, indicates that
|
|
// the path passed in Path does have an extension.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success/failure of the
|
|
// call.
|
|
//
|
|
//--
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
RTL_STRING_LENGTH_TYPE LocalPrefixLength;
|
|
|
|
rfHasExtension = false;
|
|
|
|
Status = RtlFindCharInUnicodeString(
|
|
RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
|
|
Path,
|
|
&sxsisol_ExtensionDelimiters,
|
|
&LocalPrefixLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status != STATUS_NOT_FOUND)
|
|
goto Exit;
|
|
} else {
|
|
rfHasExtension = true;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
sxsisol_PathAppendDefaultExtension(
|
|
IN PCUNICODE_STRING Path,
|
|
IN PCUNICODE_STRING DefaultExtension OPTIONAL,
|
|
IN OUT PRTL_UNICODE_STRING_BUFFER PathWithExtension,
|
|
OUT bool &rfAppended
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// If a path does not contain an extension, add one.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Path
|
|
// Path in which to find extension.
|
|
//
|
|
// DefaultExtension
|
|
// Extension to append to the path.
|
|
//
|
|
// PathWithExtension
|
|
// Initialized RTL_UNICODE_STRING_BUFFER into which
|
|
// the modified path (including extension) is written.
|
|
//
|
|
// If the path already has an extension, nothing
|
|
// is written to PathWithExtension.
|
|
//
|
|
// rfAppended
|
|
// Out bool set to true iff the path was modified with
|
|
// an extension.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success/failure of the
|
|
// call.
|
|
//
|
|
//--
|
|
{
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
UNICODE_STRING Strings[2];
|
|
ULONG NumberOfStrings = 1;
|
|
|
|
rfAppended = false;
|
|
|
|
PARAMETER_CHECK(Path != NULL);
|
|
PARAMETER_CHECK(PathWithExtension != NULL);
|
|
|
|
if ((DefaultExtension != NULL) && !RTL_STRING_IS_EMPTY(DefaultExtension)) {
|
|
bool fHasExtension = false;
|
|
IFNOT_NTSUCCESS_EXIT(sxsisol_PathHasExtension(Path, fHasExtension));
|
|
if (!fHasExtension) {
|
|
Strings[0] = *Path;
|
|
Strings[1] = *DefaultExtension;
|
|
NumberOfStrings = 2;
|
|
PathWithExtension->String.Length = 0;
|
|
IFNOT_NTSUCCESS_EXIT(RtlMultiAppendUnicodeStringBuffer(PathWithExtension, NumberOfStrings, Strings));
|
|
rfAppended = true;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
sxsisol_CanonicalizeFullPathFileName(
|
|
IN OUT PUNICODE_STRING FileName, // could be a fullpath or relative path, and only fullpath are dealt with
|
|
IN PUNICODE_STRING FullPathPreallocatedString,
|
|
IN OUT PUNICODE_STRING FullPathDynamicString
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Canonicalize a path name for comparison against globally
|
|
// redirected absolute file paths (e.g. the system default
|
|
// activation context).
|
|
//
|
|
// Given an absolute path, like c:\foo\bar.dll or c:\foo\..\bar.dll,
|
|
// return a canonicalized fullpaty like c:\foo\bar.dll or c:\bar.dll
|
|
//
|
|
// Given a relative path like bar.dll or ..\bar.dll, leave it unchanged.
|
|
//
|
|
// \\?\ and \\.\ followed by drive letter colon slash are changed,
|
|
// but followed by anything else is not.
|
|
// \\?\c:\foo => c:\foo
|
|
// \\.\c:\foo => c:\foo
|
|
// \\?\pipe => \\?\pipe
|
|
// \\.\pipe => \\.\pipe
|
|
// \\?\unc\machine\share\foo => \\?\unc\machine\share\foo
|
|
//
|
|
// Arguments:
|
|
//
|
|
// FileName
|
|
// Name of the file to be canonicalized.
|
|
// If the file name is modified, this UNICODE_STRING
|
|
// is overwritten with information about where the
|
|
// canonicalized path is written; this will be one of
|
|
// the two UNICODE_STRING parameters:
|
|
// FullPathPreallocatedString or FullPathDynamicString.
|
|
//
|
|
// Note that is may not be exactly either one of their
|
|
// values but may instead reference a substring contained
|
|
// in them.
|
|
//
|
|
// FullPathPreallocatedString
|
|
// Optional preallocated buffer used to hold the canonicalized
|
|
// full path of FileName.
|
|
//
|
|
// FullPathDynamicString
|
|
// Optional UNICODE_STRING which is used if the file name
|
|
// passed in FileName must be canonicalized and the
|
|
// canonicalization does not fit in FullPathPreallocatedString.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success/failure of the
|
|
// call.
|
|
//
|
|
//--
|
|
{
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
RTL_PATH_TYPE PathType;
|
|
PUNICODE_STRING FullPathUsed = NULL;
|
|
bool fDynamicAllocatedBufferUsed = false;
|
|
|
|
PARAMETER_CHECK(FileName != NULL);
|
|
// The dynamic string is optional, but if it is present, the buffer must be NULL.
|
|
PARAMETER_CHECK((FullPathDynamicString == NULL) || (FullPathDynamicString->Buffer == NULL));
|
|
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
RtlGetFullPathName_UstrEx(
|
|
FileName,
|
|
FullPathPreallocatedString,
|
|
FullPathDynamicString,
|
|
&FullPathUsed,
|
|
NULL,
|
|
NULL,
|
|
&PathType,
|
|
NULL));
|
|
|
|
if ((PathType == RtlPathTypeLocalDevice) || (PathType == RtlPathTypeDriveAbsolute) || (PathType == RtlPathTypeUncAbsolute)) {
|
|
UNICODE_STRING RealFullPath;
|
|
RealFullPath = *FullPathUsed;
|
|
|
|
if (PathType == RtlPathTypeLocalDevice) {
|
|
|
|
ASSERT(RTL_STRING_GET_LENGTH_CHARS(FullPathUsed) > 4);
|
|
ASSERT((FileName->Buffer[0] == L'\\') && (FileName->Buffer[1] == L'\\') &&
|
|
((FileName->Buffer[2] == L'.') || (FileName->Buffer[2] == L'?' )) && (FileName->Buffer[3] == L'\\'));
|
|
|
|
//
|
|
// only deal with \\?\C:\ or \\.\C:\, ignore other cases such as \\?\UNC\ or \\.\UNC\
|
|
//
|
|
if ((FileName->Buffer[5] == L':') && ( FileName->Buffer[6] == L'\\')) {
|
|
//
|
|
// Remove first four characters from string, "\\?\c:\" => "c:\"
|
|
//
|
|
FileName->Length -= (4 * sizeof(WCHAR));
|
|
FileName->Buffer += 4;
|
|
FileName->MaximumLength -= (4 * sizeof(WCHAR));
|
|
|
|
RealFullPath.Length -= (4 * sizeof(WCHAR));
|
|
RealFullPath.Buffer += 4;
|
|
RealFullPath.MaximumLength -= (4 * sizeof(WCHAR));
|
|
}
|
|
}
|
|
if (FileName->Length > RealFullPath.Length) { // FileName contains redundant path part, so FileName must be reset
|
|
if (FullPathUsed == FullPathDynamicString)
|
|
fDynamicAllocatedBufferUsed = true;
|
|
|
|
*FileName = RealFullPath;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
if (!fDynamicAllocatedBufferUsed)
|
|
RtlFreeUnicodeString(FullPathDynamicString); // free here or at the end of the caller
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
sxsisol_RespectDotLocal(
|
|
IN PCUNICODE_STRING FileName,
|
|
OUT PRTL_UNICODE_STRING_BUFFER FullPathResult,
|
|
OUT ULONG *OutFlags OPTIONAL
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Determine if the file pass in FileName has .local based
|
|
// redirection in the app folder.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// FileName
|
|
// Leaf name of the file to look for. (e.g. "foo.dll")
|
|
//
|
|
// FullPathResult
|
|
// Returned full path to the file found.
|
|
//
|
|
// OutFlags
|
|
// Option out parameter; the value
|
|
// RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT
|
|
// is ORed in if a .local based redirection is found.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success/failure of the
|
|
// call.
|
|
//
|
|
//--
|
|
{
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
UNICODE_STRING LocalDllNameInAppDir = { 0 };
|
|
UNICODE_STRING LocalDllNameInDotLocalDir = { 0 };
|
|
PUNICODE_STRING LocalDllNameFound = NULL;
|
|
|
|
PARAMETER_CHECK(FullPathResult != NULL);
|
|
|
|
IFNOT_NTSUCCESS_EXIT(
|
|
RtlComputePrivatizedDllName_U(
|
|
FileName,
|
|
&LocalDllNameInAppDir,
|
|
&LocalDllNameInDotLocalDir));
|
|
|
|
if (RtlDoesFileExists_UStr(&LocalDllNameInDotLocalDir)) {
|
|
LocalDllNameFound = &LocalDllNameInDotLocalDir;
|
|
} else if (RtlDoesFileExists_UStr(&LocalDllNameInAppDir)) {
|
|
LocalDllNameFound = &LocalDllNameInAppDir;
|
|
}
|
|
|
|
if (LocalDllNameFound != NULL) {
|
|
IFNOT_NTSUCCESS_EXIT(RtlAssignUnicodeStringBuffer(FullPathResult, LocalDllNameFound));
|
|
|
|
if (OutFlags != NULL)
|
|
*OutFlags |= RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Exit:
|
|
RtlFreeUnicodeString(&LocalDllNameInAppDir);
|
|
RtlFreeUnicodeString(&LocalDllNameInDotLocalDir);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
sxsisol_SearchActCtxForDllName(
|
|
IN PCUNICODE_STRING FileNameIn,
|
|
IN BOOLEAN fExistenceTest,
|
|
IN OUT SIZE_T &TotalLength,
|
|
IN OUT ULONG *OutFlags,
|
|
OUT PRTL_UNICODE_STRING_BUFFER FullPathResult
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Determine if the active activation context(s) contain a redirection
|
|
// for a given file name and if so, compute the full absolute path
|
|
// of the redirection.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// FileNameIn
|
|
// Name of the file to search for in the activation context.
|
|
//
|
|
// This file name is searched for exactly in the activation
|
|
// context dll redirection section (case insensitive).
|
|
//
|
|
// fExistenceTest
|
|
// Set to true to indicate that we're only interested in
|
|
// whether the path in FileNameIn is redirected.
|
|
//
|
|
// TotalLength
|
|
// Total length of the path out.
|
|
//
|
|
// OutFlags
|
|
// Flags set to indicate the disposition of the call.
|
|
//
|
|
// RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_ACTCTX_REDIRECT
|
|
// Activation context based redirection was found and applied.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success/failure of the
|
|
// call.
|
|
//
|
|
//--
|
|
{
|
|
UNICODE_STRING FileNameTemp;
|
|
PUNICODE_STRING FileName;
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
ACTIVATION_CONTEXT_SECTION_KEYED_DATA askd = {sizeof(askd)};
|
|
const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION *DllRedirData;
|
|
const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT *PathSegmentArray;
|
|
RTL_UNICODE_STRING_BUFFER EnvironmentExpandedDllPathBuffer; // seldom used
|
|
SIZE_T PathSegmentCount = 0;
|
|
PACTIVATION_CONTEXT ActivationContext = NULL;
|
|
PCUNICODE_STRING AssemblyStorageRoot = NULL;
|
|
ULONG i;
|
|
|
|
FileNameTemp = *FileNameIn;
|
|
FileName = &FileNameTemp;
|
|
|
|
RtlInitUnicodeStringBuffer(&EnvironmentExpandedDllPathBuffer, NULL, 0);
|
|
|
|
Status = RtlFindActivationContextSectionString(
|
|
FIND_ACTIVATION_CONTEXT_SECTION_KEY_RETURN_ACTIVATION_CONTEXT |
|
|
FIND_ACTIVATION_CONTEXT_SECTION_KEY_RETURN_FLAGS,
|
|
NULL,
|
|
ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
|
|
FileName,
|
|
&askd);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status == STATUS_SXS_SECTION_NOT_FOUND)
|
|
Status = STATUS_SXS_KEY_NOT_FOUND;
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
if (fExistenceTest == TRUE) {
|
|
Status = STATUS_SUCCESS;
|
|
// This was just an existence test. return the successful status.
|
|
goto Exit;
|
|
}
|
|
|
|
ActivationContext = askd.ActivationContext;
|
|
|
|
if ((askd.Length < sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION)) ||
|
|
(askd.DataFormatVersion != ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_FORMAT_WHISTLER)) {
|
|
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
|
|
goto Exit;
|
|
}
|
|
|
|
DllRedirData = (const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION *) askd.Data;
|
|
|
|
// validate DllRedirData
|
|
// If the path segment list extends beyond the section, we're outta here
|
|
if ((((ULONG) DllRedirData->PathSegmentOffset) > askd.SectionTotalLength) ||
|
|
(DllRedirData->PathSegmentCount > (MAXULONG / sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT))) ||
|
|
(DllRedirData->PathSegmentOffset > (MAXULONG - (DllRedirData->PathSegmentCount * sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT)))) ||
|
|
(((ULONG) (DllRedirData->PathSegmentOffset + (DllRedirData->PathSegmentCount * sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT)))) > askd.SectionTotalLength)) {
|
|
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s - Path segment array extends beyond section limits\n",
|
|
__FUNCTION__);
|
|
#endif // DBG
|
|
|
|
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
|
|
goto Exit;
|
|
}
|
|
|
|
// If the entry requires path root resolution, do so!
|
|
if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT) {
|
|
NTSTATUS InnerStatus = STATUS_SUCCESS;
|
|
ULONG GetAssemblyStorageRootFlags = 0;
|
|
|
|
// There's no need to support both a dynamic root and environment variable
|
|
// expansion.
|
|
if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND) {
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"[%x.%x] SXS: %s - Relative redirection plus env var expansion.\n",
|
|
HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess),
|
|
HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
|
|
__FUNCTION__);
|
|
|
|
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
|
|
goto Exit;
|
|
}
|
|
|
|
if (askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_PROCESS_DEFAULT) {
|
|
INTERNAL_ERROR_CHECK(!(askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_SYSTEM_DEFAULT));
|
|
GetAssemblyStorageRootFlags |= RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_PROCESS_DEFAULT;
|
|
}
|
|
if (askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_SYSTEM_DEFAULT){
|
|
GetAssemblyStorageRootFlags |= RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_SYSTEM_DEFAULT;
|
|
}
|
|
|
|
Status = RtlGetAssemblyStorageRoot(
|
|
GetAssemblyStorageRootFlags,
|
|
ActivationContext,
|
|
askd.AssemblyRosterIndex,
|
|
&AssemblyStorageRoot,
|
|
&RtlpAssemblyStorageMapResolutionDefaultCallback,
|
|
(PVOID) &InnerStatus);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ((Status == STATUS_CANCELLED) && (!NT_SUCCESS(InnerStatus)))
|
|
Status = InnerStatus;
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
PathSegmentArray = (PCACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT) (((ULONG_PTR) askd.SectionBase) + DllRedirData->PathSegmentOffset);
|
|
TotalLength = 0;
|
|
PathSegmentCount = DllRedirData->PathSegmentCount;
|
|
|
|
for (i=0; i != PathSegmentCount; i++) {
|
|
const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT *PathSegment = &PathSegmentArray[i];
|
|
|
|
// If the character array is outside the bounds of the section, something's hosed.
|
|
if ((((ULONG) PathSegmentArray[i].Offset) > askd.SectionTotalLength) ||
|
|
(PathSegmentArray[i].Offset > (MAXULONG - PathSegmentArray[i].Length)) ||
|
|
(((ULONG) (PathSegmentArray[i].Offset + PathSegmentArray[i].Length)) > askd.SectionTotalLength)) {
|
|
#if DBG
|
|
DbgPrintEx(
|
|
DPFLTR_SXS_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"SXS: %s - path segment %lu at %p (offset: %ld, length: %lu, bounds: %lu) buffer is outside section bounds\n",
|
|
__FUNCTION__,
|
|
i,
|
|
PathSegment->Offset,
|
|
PathSegment->Offset,
|
|
PathSegment->Length,
|
|
askd.SectionTotalLength);
|
|
#endif // DBG
|
|
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
|
|
goto Exit;
|
|
}
|
|
|
|
TotalLength += (RTL_STRING_LENGTH_TYPE)PathSegment->Length;
|
|
if (TotalLength >= MAXUSHORT) { // everytime TotalLength is changed, needs to check whether it is out of range.
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
}
|
|
}
|
|
if (AssemblyStorageRoot != NULL) {
|
|
TotalLength += AssemblyStorageRoot->Length;
|
|
if (TotalLength >= MAXUSHORT)
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
}
|
|
|
|
//
|
|
// TotalLength is an approximation, the better the approx, the better perf, but
|
|
// it does not need to be exact.
|
|
// Jaykrell May 2002
|
|
//
|
|
IFNOT_NTSUCCESS_EXIT(RtlEnsureUnicodeStringBufferSizeBytes(FullPathResult, (RTL_STRING_LENGTH_TYPE)TotalLength));
|
|
if (AssemblyStorageRoot != NULL)
|
|
IFNOT_NTSUCCESS_EXIT(RtlAssignUnicodeStringBuffer(FullPathResult, AssemblyStorageRoot));
|
|
|
|
for (i=0; i != PathSegmentCount; i++) {
|
|
UNICODE_STRING PathSegmentString;
|
|
const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT *PathSegment = &PathSegmentArray[i];
|
|
|
|
PathSegmentString.Length = (RTL_STRING_LENGTH_TYPE)PathSegment->Length;
|
|
PathSegmentString.Buffer = (PWSTR)(((ULONG_PTR) askd.SectionBase) + PathSegment->Offset);
|
|
IFNOT_NTSUCCESS_EXIT(RtlAppendUnicodeStringBuffer(FullPathResult, &PathSegmentString));
|
|
}
|
|
|
|
if (!(DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_INCLUDES_BASE_NAME)) {
|
|
if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SYSTEM_DEFAULT_REDIRECTED_SYSTEM32_DLL) {
|
|
// only in this case, FileName needs to be reseted
|
|
RTL_STRING_LENGTH_TYPE PrefixLength;
|
|
|
|
//
|
|
// get the leaf filename
|
|
//
|
|
Status = RtlFindCharInUnicodeString(
|
|
RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
|
|
FileName,
|
|
&RtlDosPathSeperatorsString,
|
|
&PrefixLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
INTERNAL_ERROR_CHECK(Status != STATUS_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
|
|
FileName->Length = FileName->Length - PrefixLength - sizeof(WCHAR);
|
|
FileName->Buffer = FileName->Buffer + (PrefixLength / sizeof(WCHAR)) + 1;
|
|
}
|
|
|
|
TotalLength += FileName->Length;
|
|
if (TotalLength >= MAXUSHORT) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Exit;
|
|
}
|
|
|
|
IFNOT_NTSUCCESS_EXIT(RtlAppendUnicodeStringBuffer(FullPathResult, FileName));
|
|
}
|
|
|
|
if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND) {
|
|
//
|
|
// Apply any environment strings as necessary (rare case),
|
|
// in this case, ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT must not been set
|
|
//
|
|
|
|
IFNOT_NTSUCCESS_EXIT(sxsisol_ExpandEnvironmentStrings_UEx(NULL, &(FullPathResult->String), &EnvironmentExpandedDllPathBuffer));
|
|
|
|
//
|
|
// TotalLength does not account for this. Jaykrell May 2002
|
|
//
|
|
IFNOT_NTSUCCESS_EXIT(RtlAssignUnicodeStringBuffer(FullPathResult, &EnvironmentExpandedDllPathBuffer.String));
|
|
}
|
|
|
|
if (OutFlags != NULL)
|
|
*OutFlags |= RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_ACTCTX_REDIRECT;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
Exit:
|
|
ASSERT(Status != STATUS_INTERNAL_ERROR);
|
|
|
|
RtlFreeUnicodeStringBuffer(&EnvironmentExpandedDllPathBuffer);
|
|
if (ActivationContext != NULL)
|
|
RtlReleaseActivationContext(ActivationContext);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
sxsisol_ExpandEnvironmentStrings_UEx(
|
|
IN PVOID Environment OPTIONAL,
|
|
IN PCUNICODE_STRING Source,
|
|
OUT PRTL_UNICODE_STRING_BUFFER Destination
|
|
)
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Wrapper around RtlExpandEnvironmentStrings_U which handles
|
|
// dynamic allocation/resizing of the output buffer.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Environment
|
|
// Optional environment block to query.
|
|
//
|
|
// Source
|
|
// Source string with replacement strings to use.
|
|
//
|
|
// Destination
|
|
// RTL_UNICODE_STRING_BUFFER into which the translated
|
|
// string is written.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// NTSTATUS indicating the overall success/failure of the call.
|
|
//
|
|
//--
|
|
|
|
{
|
|
ULONG RequiredLengthBytes;
|
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
|
UNICODE_STRING EmptyBuffer;
|
|
|
|
PARAMETER_CHECK(Source != NULL);
|
|
PARAMETER_CHECK(Destination != NULL);
|
|
PARAMETER_CHECK(Source != &Destination->String);
|
|
|
|
if (Source->Length == 0) {
|
|
Status = RtlAssignUnicodeStringBuffer(Destination, Source);
|
|
goto Exit;
|
|
}
|
|
|
|
RtlInitEmptyUnicodeString(&EmptyBuffer, NULL, 0);
|
|
|
|
RtlAcquirePebLock();
|
|
__try {
|
|
Status = RtlExpandEnvironmentStrings_U(Environment, Source, &EmptyBuffer, &RequiredLengthBytes);
|
|
if ((!NT_SUCCESS(Status)) &&
|
|
(Status != STATUS_BUFFER_TOO_SMALL)) {
|
|
__leave;
|
|
}
|
|
if (RequiredLengthBytes > UNICODE_STRING_MAX_BYTES) {
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
__leave;
|
|
}
|
|
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
|
|
Status = RtlEnsureUnicodeStringBufferSizeBytes(Destination, RequiredLengthBytes + sizeof(UNICODE_NULL));
|
|
if (!NT_SUCCESS(Status)) {
|
|
__leave;
|
|
}
|
|
Status = RtlExpandEnvironmentStrings_U(NULL, Source, &Destination->String, NULL);
|
|
ASSERT(NT_SUCCESS(Status)); // the environment variable changed with the peb lock held?
|
|
if (!NT_SUCCESS(Status)) {
|
|
__leave;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
} __finally {
|
|
RtlReleasePebLock();
|
|
}
|
|
|
|
Exit:
|
|
ASSERT(Status != STATUS_INTERNAL_ERROR);
|
|
return Status;
|
|
}
|