diff --git a/src/sys/dirctl.c b/src/sys/dirctl.c index ab3fe685..025c969f 100644 --- a/src/sys/dirctl.c +++ b/src/sys/dirctl.c @@ -618,7 +618,7 @@ static NTSTATUS FspFsvolQueryDirectory( return STATUS_INVALID_PARAMETER; /* check that FileName is valid (if supplied) */ - if (0 != FileName && !FspUnicodePathIsValid(FileName, FALSE)) + if (0 != FileName && !FspUnicodePathIsValidPattern(FileName)) return STATUS_INVALID_PARAMETER; /* is this an allowed file information class? */ diff --git a/src/sys/driver.h b/src/sys/driver.h index c53e9e1a..dc81e8de 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -426,6 +426,7 @@ BOOLEAN FspExpirationTimeValid2(UINT64 ExpirationTime, UINT64 CurrentTime) PVOID FspAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag); PVOID FspAllocateIrpMustSucceed(CCHAR StackSize); BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams); +BOOLEAN FspUnicodePathIsValidPattern(PUNICODE_STRING Pattern); VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); NTSTATUS FspCreateGuid(GUID *Guid); NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, diff --git a/src/sys/util.c b/src/sys/util.c index 1989d231..d01031ec 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -18,6 +18,7 @@ #include BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams); +BOOLEAN FspUnicodePathIsValidPattern(PUNICODE_STRING Pattern); VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); NTSTATUS FspCreateGuid(GUID *Guid); NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, @@ -88,6 +89,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspUnicodePathIsValid) +#pragma alloc_text(PAGE, FspUnicodePathIsValidPattern) #pragma alloc_text(PAGE, FspUnicodePathSuffix) #pragma alloc_text(PAGE, FspCreateGuid) #pragma alloc_text(PAGE, FspGetDeviceObjectPointer) @@ -171,30 +173,91 @@ PVOID FspAllocateIrpMustSucceed(CCHAR StackSize) BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams) { - /* this does NOT check if the Path contains invalid file name chars (*, ?, etc.) */ - PAGED_CODE(); if (0 != Path->Length % sizeof(WCHAR)) return FALSE; PWSTR PathBgn, PathEnd, PathPtr; + UCHAR Flags = FSRTL_NTFS_LEGAL; + WCHAR Char; PathBgn = Path->Buffer; PathEnd = (PWSTR)((PUINT8)PathBgn + Path->Length); PathPtr = PathBgn; while (PathEnd > PathPtr) - if (L'\\' == *PathPtr) + { + Char = *PathPtr; + + if (L'\\' == Char) { + /* stream names can only appear as the last path component */ + if (BooleanFlagOn(Flags, FSRTL_OLE_LEGAL)) + return FALSE; + PathPtr++; + + /* don't like multiple backslashes */ if (PathEnd > PathPtr && L'\\' == *PathPtr) return FALSE; } - else if (!AllowStreams && L':' == *PathPtr) + else if (L':' == Char) + { + if (!AllowStreams) + return FALSE; + + /* + * Where are the docs on legal stream names? + */ + + PathPtr++; + + /* stream characters now allowed */ + SetFlag(Flags, FSRTL_OLE_LEGAL); + } + else if (0x80 > Char && !FsRtlTestAnsiCharacter(Char, TRUE, FALSE, Flags)) return FALSE; else PathPtr++; + } + + return TRUE; +} + +BOOLEAN FspUnicodePathIsValidPattern(PUNICODE_STRING Path) +{ + PAGED_CODE(); + + if (0 != Path->Length % sizeof(WCHAR)) + return FALSE; + + PWSTR PathBgn, PathEnd, PathPtr; + WCHAR Char; + + PathBgn = Path->Buffer; + PathEnd = (PWSTR)((PUINT8)PathBgn + Path->Length); + PathPtr = PathBgn; + + while (PathEnd > PathPtr) + { + Char = *PathPtr; + + /* + * A pattern is allowed to have wildcards. It cannot consist of multiple + * path components (have a backslash) and it cannot reference a stream (have + * a colon). + */ + + if (L'\\' == Char) + return FALSE; + else if (L':' == Char) + return FALSE; + else if (0x80 > Char && !FsRtlTestAnsiCharacter(Char, TRUE, TRUE, FSRTL_NTFS_LEGAL)) + return FALSE; + else + PathPtr++; + } return TRUE; } diff --git a/tst/winfsp-tests/create-test.c b/tst/winfsp-tests/create-test.c index 6e352529..d0c4c08f 100644 --- a/tst/winfsp-tests/create-test.c +++ b/tst/winfsp-tests/create-test.c @@ -68,6 +68,24 @@ void create_dotest(ULONG Flags, PWSTR Prefix) ASSERT(ERROR_INVALID_NAME == GetLastError()); } + /* invalid chars (wildcards) not allowed */ + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0*", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + ASSERT(INVALID_HANDLE_VALUE == Handle); + ASSERT(ERROR_INVALID_NAME == GetLastError()); + + /* stream names can only appear as the last path component */ + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\DOESNOTEXIST:foo\\file0*", + Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs)); + + Handle = CreateFileW(FilePath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); + ASSERT(INVALID_HANDLE_VALUE == Handle); + ASSERT(ERROR_INVALID_NAME == GetLastError()); + StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1", Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));