533 lines
15 KiB
C
533 lines
15 KiB
C
/*++
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SafePath.c (WinSAFER Path Comparison)
|
|
|
|
Abstract:
|
|
|
|
This module implements the WinSAFER APIs that evaluate the system
|
|
policies to determine which Authorization Level has been configured
|
|
to apply restrictions for a specified application or code library.
|
|
|
|
Author:
|
|
|
|
Jeffrey Lawson (JLawson) - Nov 1999
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Exported Functions:
|
|
|
|
CodeAuthzpCompareImagePath
|
|
|
|
Revision History:
|
|
|
|
Created - Nov 1999
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
#include <winsafer.h>
|
|
#include <winsaferp.h>
|
|
#include "saferp.h"
|
|
|
|
|
|
|
|
//
|
|
// Define the following value to use the "new" comparison logic,
|
|
// that includes asterick and question mark matching.
|
|
//
|
|
#define USE_NEW_WILDCARD_EVALUATION
|
|
|
|
|
|
|
|
//
|
|
// Convenient macros for doing filename pattern matching.
|
|
//
|
|
#define IS_UCASE_CHARS_EQUAL_U(ch1, ch2) (((ch1) == (ch2)) || (RtlUpcaseUnicodeChar(ch1) == RtlUpcaseUnicodeChar(ch2)))
|
|
#define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
|
|
#define IS_WILDCARD_CHAR_U(ch) ((ch) == L'*')
|
|
#define IS_QUESTION_CHAR_U(ch) ((ch) == L'?')
|
|
#define IS_DOT_CHAR_U(ch) ((ch) == L'.')
|
|
|
|
|
|
|
|
FORCEINLINE LPCWSTR CodeAuthzpFindSlash (
|
|
IN LPCWSTR string,
|
|
IN USHORT length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a pointer to the first instance of a forward or
|
|
backward slash within the specified string buffer.
|
|
|
|
Arguments:
|
|
|
|
string -
|
|
|
|
length -
|
|
|
|
Return Value:
|
|
|
|
Returns NULL if no backslashes or forward-slashes were found within
|
|
the string. Otherwise returns a pointer to the matching char.
|
|
|
|
--*/
|
|
{
|
|
while (length-- > 0) {
|
|
if (IS_PATH_SEPARATOR_U(*string)) return string;
|
|
string++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef USE_NEW_WILDCARD_EVALUATION
|
|
LONG NTAPI
|
|
__CodeAuthzpCompareImagePathHelper(
|
|
IN LPCWSTR wild,
|
|
IN USHORT wildlen,
|
|
IN LPCWSTR actual,
|
|
IN USHORT actuallen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Evaluates a wildcard pattern against a specified pathname and
|
|
indicates if they match.
|
|
|
|
Arguments:
|
|
|
|
wild -
|
|
|
|
wildlen -
|
|
|
|
actual -
|
|
|
|
actuallen -
|
|
|
|
Return Value:
|
|
|
|
0 = no match
|
|
-1 = match exactly
|
|
1 = match with wildcard
|
|
|
|
--*/
|
|
{
|
|
LONG lMatchResult = -1;
|
|
|
|
ASSERT(ARGUMENT_PRESENT(wild) &&
|
|
!CodeAuthzpFindSlash(wild, wildlen));
|
|
ASSERT(ARGUMENT_PRESENT(actual) &&
|
|
!CodeAuthzpFindSlash(actual, actuallen));
|
|
|
|
for (;;) {
|
|
// Check for terminating conditions.
|
|
if (wildlen == 0) {
|
|
if (actuallen == 0) {
|
|
return lMatchResult;
|
|
} else {
|
|
ASSERT(actuallen > 0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Evaluate the wildcard pattern.
|
|
if (IS_WILDCARD_CHAR_U(*wild)) {
|
|
USHORT matchcount;
|
|
|
|
// Skip past the asterick (possibly multiple).
|
|
do {
|
|
wild++; wildlen--;
|
|
} while ( wildlen > 0 && IS_WILDCARD_CHAR_U(*wild) );
|
|
|
|
// Try expanding the asterick to be zero or more chars.
|
|
for (matchcount = 0; ; matchcount++) {
|
|
if (matchcount > actuallen) {
|
|
return 0; // match failed.
|
|
}
|
|
if (0 != __CodeAuthzpCompareImagePathHelper(
|
|
wild, wildlen,
|
|
&actual[matchcount], actuallen - matchcount))
|
|
{
|
|
actual += matchcount;
|
|
actuallen -= matchcount;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We've encountered a wildcard char, so remember
|
|
// that this is no longer an "exact" match.
|
|
lMatchResult = 1;
|
|
|
|
} else if (IS_QUESTION_CHAR_U(*wild)) {
|
|
// Question marks will match any single character, except
|
|
// periods. Question marks will also match nothing when
|
|
// we are already at the end of the filename or segment.
|
|
|
|
if (actuallen > 0 && !IS_DOT_CHAR_U(*actual)) {
|
|
actual++; actuallen--;
|
|
}
|
|
wild++; wildlen--;
|
|
|
|
// We've encountered a wildcard char, so remember
|
|
// that this is no longer an "exact" match.
|
|
lMatchResult = 1;
|
|
|
|
} else {
|
|
if (actuallen < 1 ||
|
|
!IS_UCASE_CHARS_EQUAL_U(*wild, *actual)) {
|
|
return 0;
|
|
}
|
|
wild++; wildlen--;
|
|
actual++; actuallen--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LONG NTAPI
|
|
CodeAuthzpCompareUnicodeImagePath(
|
|
IN PCUNICODE_STRING wildcard,
|
|
IN PCUNICODE_STRING actual
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Evaluates a wildcard pattern against a specified pathname and
|
|
indicates if they match.
|
|
|
|
Arguments:
|
|
|
|
wildcard -
|
|
|
|
actual -
|
|
|
|
Return Value:
|
|
|
|
Returns 0 if the path fragment does not match the specified imagepath.
|
|
Returns -1 if the fragment matches the imagepath _exactly_!
|
|
Otherwise returns a postive integer representing the "depth" of the
|
|
the match (number of matching subdirectories). Greater values
|
|
indicate a "deeper" directory match.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT wildindex = 0, actualindex = 0;
|
|
LONG matchquality = 0;
|
|
BOOLEAN bNoWildcardsFound = TRUE;
|
|
|
|
ASSERT(ARGUMENT_PRESENT(wildcard) && wildcard->Buffer != NULL);
|
|
ASSERT(ARGUMENT_PRESENT(actual) && actual->Buffer != NULL);
|
|
for (;;)
|
|
{
|
|
ASSERT(wildindex <= wildcard->Length / sizeof(WCHAR));
|
|
ASSERT(actualindex <= actual->Length / sizeof(WCHAR));
|
|
|
|
if (wildindex == wildcard->Length / sizeof(WCHAR))
|
|
{
|
|
// We've reached the end of the wildcard but the actual string has
|
|
// not ended.
|
|
if (actualindex < actual->Length / sizeof(WCHAR)) {
|
|
return matchquality;
|
|
}
|
|
|
|
// The wildcard matched the filename but with inexact matches.
|
|
// Return one more than the actual depth so that we can handle
|
|
// non-qualified path matches as worse then these.
|
|
if (!bNoWildcardsFound) {
|
|
return (matchquality + 1);
|
|
}
|
|
|
|
ASSERT(wildindex == wildcard->Length / sizeof(WCHAR));
|
|
return -1; // exact match.
|
|
}
|
|
else if (IS_PATH_SEPARATOR_U(wildcard->Buffer[wildindex]))
|
|
{
|
|
if (!IS_PATH_SEPARATOR_U(actual->Buffer[actualindex])) {
|
|
return 0; // no match
|
|
}
|
|
|
|
// Skip forward to the start of the next component.
|
|
do {
|
|
wildindex++;
|
|
} while ( wildindex < wildcard->Length / sizeof(WCHAR) &&
|
|
IS_PATH_SEPARATOR_U(wildcard->Buffer[wildindex]) );
|
|
|
|
// Skip forward to the start of the next component.
|
|
do {
|
|
actualindex++;
|
|
} while ( actualindex < actual->Length / sizeof(WCHAR) &&
|
|
IS_PATH_SEPARATOR_U(actual->Buffer[actualindex]) );
|
|
}
|
|
else
|
|
{
|
|
USHORT wildlen = 0, actuallen = 0;
|
|
|
|
// Count the length of this component of the wildcard.
|
|
while (wildindex + wildlen < (USHORT) (wildcard->Length / sizeof(WCHAR)) &&
|
|
!IS_PATH_SEPARATOR_U(wildcard->Buffer[wildindex + wildlen])) {
|
|
wildlen++;
|
|
}
|
|
ASSERT(wildlen > 0);
|
|
|
|
// Count the length of this component of the actual path.
|
|
while (actualindex + actuallen < (USHORT) (actual->Length / sizeof(WCHAR)) &&
|
|
!IS_PATH_SEPARATOR_U(actual->Buffer[actualindex + actuallen])) {
|
|
actuallen++;
|
|
}
|
|
|
|
// Otherwise require that this component matches.
|
|
switch (__CodeAuthzpCompareImagePathHelper(
|
|
&wildcard->Buffer[wildindex], wildlen,
|
|
&actual->Buffer[actualindex], actuallen)) {
|
|
case 0: // fails to match
|
|
return 0;
|
|
case -1: // matches exactly without wildcards
|
|
break;
|
|
default: // matches with wildcard expansion.
|
|
bNoWildcardsFound = FALSE; break;
|
|
}
|
|
|
|
// Increment pointers for next component.
|
|
wildindex += wildlen;
|
|
actualindex += actuallen;
|
|
matchquality++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LONG NTAPI
|
|
CodeAuthzpCompareImagePath(
|
|
IN LPCWSTR szPathFragment,
|
|
IN LPCWSTR szFullImagePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Evaluates a wildcard pattern against a specified pathname and
|
|
indicates if they match.
|
|
|
|
Arguments:
|
|
|
|
szPathFragment -
|
|
|
|
szFullImagePath -
|
|
|
|
Return Value:
|
|
|
|
Returns 0 if the path fragment does not match the specified imagepath.
|
|
Returns -1 if the fragment matches the imagepath _exactly_!
|
|
Otherwise returns a postive integer representing the "depth" of the
|
|
the match (number of matching subdirectories). Greater values
|
|
indicate a "deeper" directory match.
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING UnicodePathFragment;
|
|
UNICODE_STRING UnicodeFullImagePath;
|
|
ULONG i = 0;
|
|
USHORT Len = 0;
|
|
LONG lMatchDepth = 0;
|
|
|
|
RtlInitUnicodeString(&UnicodePathFragment, szPathFragment);
|
|
RtlInitUnicodeString(&UnicodeFullImagePath, szFullImagePath);
|
|
lMatchDepth = CodeAuthzpCompareUnicodeImagePath(
|
|
&UnicodePathFragment, &UnicodeFullImagePath);
|
|
|
|
// We did not get a match for fully qualified name. Let's try for just a
|
|
// basename match.
|
|
if (!lMatchDepth) {
|
|
// if the rule has a '\' in it, it's not a basename match rule.
|
|
// We only check for filename.ext rules allowing wildcards.
|
|
if (wcschr(szPathFragment, L'\\')) {
|
|
return 0;
|
|
}
|
|
|
|
Len = (UnicodeFullImagePath.Length/sizeof(WCHAR)) -1;
|
|
|
|
// Skip from rightmost character to the the character just after the
|
|
// last '\', if any or to the beginning of the string in absence of '\'.
|
|
while (Len > 0 && szFullImagePath[Len] != L'\\') {
|
|
Len--;
|
|
}
|
|
|
|
// A '\' exists. Move one character to the right.
|
|
if (szFullImagePath[Len] == L'\\') {
|
|
Len++;
|
|
}
|
|
|
|
// Check if there is a match of the file basename with the rule. We have
|
|
// already checked that the rule does not have '\'.
|
|
switch (__CodeAuthzpCompareImagePathHelper(
|
|
szPathFragment, UnicodePathFragment.Length/sizeof(WCHAR),
|
|
szFullImagePath+Len, (UnicodeFullImagePath.Length/sizeof(WCHAR))-Len)) {
|
|
case 0: // fails to match
|
|
return 0;
|
|
case -1: // matches exactly without wildcards
|
|
default: // matches with wildcard expansion.
|
|
|
|
// We treat exact matches the same as inexact matches here.
|
|
// Thus, abc.exe is == a*.exe = *.exe.
|
|
|
|
// Skip to the first non-'\' character.
|
|
while ((szFullImagePath[i] == L'\\') && (szFullImagePath[i] != L'\0')) {
|
|
i++;
|
|
}
|
|
|
|
// This string is bogus. It only has 0 or more '\'s in it.
|
|
if (szFullImagePath[i] == L'\0') {
|
|
return 0;
|
|
}
|
|
|
|
// Return the depth of the tree.
|
|
lMatchDepth = 1;
|
|
while (TRUE) {
|
|
|
|
// Skip to the first '\' while not end of string.
|
|
while ((szFullImagePath[i] != L'\\') && (szFullImagePath[i] != L'\0')) {
|
|
i++;
|
|
}
|
|
|
|
// We are at the end of the string. Return the depth.
|
|
if (szFullImagePath[i] == L'\0') {
|
|
return lMatchDepth;
|
|
}
|
|
|
|
// Skip to the first non-'\' while not end of string.
|
|
while ((szFullImagePath[i] == L'\\') && (szFullImagePath[i] != L'\0')) {
|
|
i++;
|
|
}
|
|
|
|
// We are at a non-'\' character. Increment the depth.
|
|
lMatchDepth++;
|
|
}
|
|
|
|
// Should never get here.
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
return lMatchDepth;
|
|
}
|
|
|
|
|
|
|
|
#else // #ifdef USE_NEW_WILDCARD_EVALUATION
|
|
|
|
|
|
LONG NTAPI
|
|
CodeAuthzpCompareImagePath(
|
|
IN LPCWSTR szPathFragment,
|
|
IN LPCWSTR szFullImagePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Evaluates a wildcard pattern against a specified pathname and
|
|
indicates if they match.
|
|
|
|
Arguments:
|
|
|
|
szPathFragment -
|
|
|
|
szFullImagePath -
|
|
|
|
Return Value:
|
|
|
|
Returns 0 if the path fragment does not match the specified imagepath.
|
|
Returns -1 if the fragment matches the imagepath _exactly_!
|
|
Otherwise returns a postive integer representing the "depth" of the
|
|
the match (number of matching subdirectories). Greater values
|
|
indicate a "deeper" directory match.
|
|
|
|
--*/
|
|
{
|
|
LONG MatchDepth = 0;
|
|
BOOLEAN bLastWasSlash = TRUE;
|
|
LPCWSTR pFragment = szPathFragment;
|
|
LPCWSTR pImage = szFullImagePath;
|
|
|
|
|
|
//
|
|
// Verify that our arguments were all supplied.
|
|
//
|
|
ASSERT(ARGUMENT_PRESENT(pFragment) && ARGUMENT_PRESENT(pImage));
|
|
if (!*pFragment || !*pImage) return 0; // empty strings.
|
|
|
|
|
|
|
|
//
|
|
// Perform the actual comparison loop.
|
|
//
|
|
for (;;) {
|
|
if (!*pFragment)
|
|
{
|
|
// We have reached the string terminator at the end of the
|
|
// wildcard fragment. If this was also the end of the
|
|
// actual filename, then this is a precise match. Otherwise
|
|
// we'll only consider this a positive partial match if this
|
|
// occurred on a path-separator boundary.
|
|
if (!*pImage) return -1; // matched exactly.
|
|
else if (bLastWasSlash) break;
|
|
else if (IS_PATH_SEPARATOR_U(*pImage)) break;
|
|
else return 0; // did not match.
|
|
}
|
|
else if (!*pImage)
|
|
{
|
|
// We have reached the end of the actual filename, but have
|
|
// not yet found the end of the wildcard fragment.
|
|
return 0; // did not match.
|
|
}
|
|
else if (!IS_UCASE_CHARS_EQUAL_U(*pFragment, *pImage))
|
|
{
|
|
// The two characters were unequal. However, this condition
|
|
// might occur if multiple path separators occur in one and
|
|
// not the other, so explicitly absorb multiple slashes.
|
|
if (bLastWasSlash) {
|
|
if (IS_PATH_SEPARATOR_U(*pFragment)) { pFragment++; continue; }
|
|
else if (IS_PATH_SEPARATOR_U(*pImage)) { pImage++; continue; }
|
|
}
|
|
return 0; // did not match.
|
|
}
|
|
else
|
|
{
|
|
// Both characters matched. Remember if they were slashes.
|
|
// If this is a transition to a non-separator portion of the
|
|
// filename, then increment our depth counter.
|
|
if (IS_PATH_SEPARATOR_U(*pFragment)) {
|
|
bLastWasSlash = TRUE;
|
|
} else {
|
|
if (bLastWasSlash) {
|
|
MatchDepth++;
|
|
bLastWasSlash = FALSE;
|
|
}
|
|
}
|
|
}
|
|
pFragment++;
|
|
pImage++;
|
|
}
|
|
return MatchDepth;
|
|
}
|
|
|
|
#endif //#ifdef USE_NEW_WILDCARD_EVALUATION
|
|
|
|
|