From dd4839e2f11ec0afe684f91a16c92a227b54e0ba Mon Sep 17 00:00:00 2001 From: Tal Aloni Date: Wed, 18 Jan 2017 23:38:00 +0200 Subject: [PATCH] Improved NTFileSystemHelper's FindEntries implementation --- .../Server/Helpers/NTFileSystemHelper.Find.cs | 112 +++++++++++------- .../SMB1/Transaction2SubcommandHelper.cs | 14 +-- 2 files changed, 72 insertions(+), 54 deletions(-) diff --git a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs b/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs index d44fcb6..d2150bd 100644 --- a/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs +++ b/SMBLibrary/Server/Helpers/NTFileSystemHelper.Find.cs @@ -13,11 +13,6 @@ namespace SMBLibrary.Server { public partial class NTFileSystemHelper { - // Windows servers will return "." and ".." when enumerating directory files, Windows clients do not require it. - // It seems that Ubuntu 10.04.4 and 13.10 expect at least one entry in the response (so empty directory listing cause a problem when omitting both). - private const bool IncludeCurrentDirectoryInResults = true; - private const bool IncludeParentDirectoryInResults = true; - // Filename pattern examples: // '\Directory' - Get the directory entry // '\Directory\*' - List the directory files @@ -27,55 +22,75 @@ namespace SMBLibrary.Server /// The filename pattern to search for. This field MAY contain wildcard characters /// null if the path does not exist /// - public static List FindEntries(IFileSystem fileSystem, string path) + public static NTStatus FindEntries(out List entries, IFileSystem fileSystem, string fileNamePattern) { - bool isDirectoryEnumeration = false; - string searchPattern = String.Empty; - if (path.Contains("*") || path.Contains("<")) + int separatorIndex = fileNamePattern.LastIndexOf('\\'); + if (separatorIndex >= 0) { - isDirectoryEnumeration = true; - int separatorIndex = path.LastIndexOf('\\'); - searchPattern = path.Substring(separatorIndex + 1); - path = path.Substring(0, separatorIndex + 1); - } - bool exactNameWithoutExtension = searchPattern.Contains("\""); - - FileSystemEntry entry = fileSystem.GetEntry(path); - if (entry == null) - { - return null; - } - - List entries; - if (isDirectoryEnumeration) - { - entries = fileSystem.ListEntriesInDirectory(path); - - if (searchPattern != String.Empty) - { - entries = GetFiltered(entries, searchPattern); - } - - if (!exactNameWithoutExtension) - { - if (IncludeParentDirectoryInResults) - { - entries.Insert(0, fileSystem.GetEntry(FileSystem.GetParentDirectory(path))); - entries[0].Name = ".."; - } - if (IncludeCurrentDirectoryInResults) - { - entries.Insert(0, fileSystem.GetEntry(path)); - entries[0].Name = "."; - } - } + string path = fileNamePattern.Substring(0, separatorIndex + 1); + string expression = fileNamePattern.Substring(separatorIndex + 1); + return FindEntries(out entries, fileSystem, path, expression); } else { + entries = null; + return NTStatus.STATUS_INVALID_PARAMETER; + } + } + + /// Expression as described in [MS-FSA] 2.1.4.4 + /// null if the path does not exist + public static NTStatus FindEntries(out List entries, IFileSystem fileSystem, string path, string expression) + { + entries = null; + FileSystemEntry entry = fileSystem.GetEntry(path); + if (entry == null) + { + return NTStatus.STATUS_NO_SUCH_FILE; + } + + if (expression == String.Empty) + { + return NTStatus.STATUS_INVALID_PARAMETER; + } + + bool findExactName = !ContainsWildcardCharacters(expression); + + if (!findExactName) + { + try + { + entries = fileSystem.ListEntriesInDirectory(path); + } + catch (UnauthorizedAccessException) + { + return NTStatus.STATUS_ACCESS_DENIED; + } + + entries = GetFiltered(entries, expression); + + // Windows will return "." and ".." when enumerating directory files. + // The SMB1 / SMB2 specifications mandate that when zero entries are found, the server SHOULD / MUST return STATUS_NO_SUCH_FILE. + // For this reason, we MUST include the current directory and/or parent directory when enumerating a directory + // in order to diffrentiate between a directory that does not exist and a directory with no entries. + FileSystemEntry currentDirectory = fileSystem.GetEntry(path); + currentDirectory.Name = "."; + FileSystemEntry parentDirectory = fileSystem.GetEntry(FileSystem.GetParentDirectory(path)); + parentDirectory.Name = ".."; + entries.Insert(0, parentDirectory); + entries.Insert(0, currentDirectory); + } + else + { + entry = fileSystem.GetEntry(path + expression); + if (entry == null) + { + return NTStatus.STATUS_NO_SUCH_FILE; + } entries = new List(); entries.Add(entry); } - return entries; + return NTStatus.STATUS_SUCCESS; } /// Expression as described in [MS-FSA] 2.1.4.4 @@ -97,6 +112,11 @@ namespace SMBLibrary.Server return result; } + private static bool ContainsWildcardCharacters(string expression) + { + return (expression.Contains("?") || expression.Contains("*") || expression.Contains("\"") || expression.Contains(">") || expression.Contains("<")); + } + // [MS-FSA] 2.1.4.4 // The FileName is string compared with Expression using the following wildcard rules: // * (asterisk) Matches zero or more characters. diff --git a/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs b/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs index ee2cfe7..fc3bc46 100644 --- a/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs +++ b/SMBLibrary/Server/SMB1/Transaction2SubcommandHelper.cs @@ -22,19 +22,17 @@ namespace SMBLibrary.Server.SMB1 string fileNamePattern = subcommand.FileName; List entries; - try + NTStatus searchStatus = NTFileSystemHelper.FindEntries(out entries, fileSystem, fileNamePattern); + if (searchStatus != NTStatus.STATUS_SUCCESS) { - entries = NTFileSystemHelper.FindEntries(fileSystem, fileNamePattern); - } - catch (UnauthorizedAccessException) - { - header.Status = NTStatus.STATUS_ACCESS_DENIED; + state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', NTStatus: {1}", fileNamePattern, searchStatus.ToString()); + header.Status = searchStatus; return null; } - state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', found {1} matching entries", fileNamePattern, entries != null ? entries.Count.ToString() : "no"); + state.LogToServer(Severity.Verbose, "FindFirst2: Searched for '{0}', found {1} matching entries", fileNamePattern, entries.Count); // [MS-CIFS] If no matching entries are found, the server SHOULD fail the request with STATUS_NO_SUCH_FILE. - if (entries == null || entries.Count == 0) + if (entries.Count == 0) { header.Status = NTStatus.STATUS_NO_SUCH_FILE; return null;