Added SMB 2.0 / 2.1 server implementation

This commit is contained in:
Tal Aloni 2017-01-20 22:04:07 +02:00
parent 966096df5f
commit 55b83add4e
21 changed files with 1958 additions and 22 deletions

View file

@ -4,7 +4,10 @@ namespace SMBLibrary
public enum NTStatus : uint
{
STATUS_SUCCESS = 0x00000000,
STATUS_OBJECT_NAME_EXISTS = 0x40000000,
STATUS_NO_MORE_FILES = 0x80000006,
STATUS_NOT_IMPLEMENTED = 0xC0000002,
STATUS_INVALID_INFO_CLASS = 0xC0000003,
STATUS_INVALID_HANDLE = 0xC0000008,
STATUS_INVALID_PARAMETER = 0xC000000D,
STATUS_NO_SUCH_DEVICE = 0xC000000E,
@ -23,12 +26,16 @@ namespace SMBLibrary
STATUS_LOGON_FAILURE = 0xC000006D, // Authentication failure.
STATUS_ACCOUNT_RESTRICTION = 0xC000006E, // The user has an empty password, which is not allowed
STATUS_DISK_FULL = 0xC000007F,
STATUS_INSUFFICIENT_RESOURCES = 0xC000009A,
STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2,
STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA,
STATUS_NOT_SUPPORTED = 0xC00000BB,
STATUS_NETWORK_NAME_DELETED = 0xC00000C9,
STATUS_TOO_MANY_SESSIONS = 0xC00000CE,
STATUS_TOO_MANY_OPENED_FILES = 0xC000011F,
STATUS_CANNOT_DELETE = 0xC0000121,
STATUS_FILE_CLOSED = 0xC0000128,
STATUS_FS_DRIVER_REQUIRED = 0xC000019C,
STATUS_USER_SESSION_DELETED = 0xC0000203,
STATUS_INSUFF_SERVER_RESOURCES = 0xC0000205,

View file

@ -8,6 +8,7 @@ namespace SMBLibrary
ERROR_ACCESS_DENIED = 0x0005,
ERROR_SHARING_VIOLATION = 0x0020,
ERROR_DISK_FULL = 0x0070,
ERROR_ALREADY_EXISTS = 0x00B7,
ERROR_LOGON_FAILURE = 0x052E,
ERROR_ACCOUNT_RESTRICTION = 0x052F,
ERROR_ACCOUNT_DISABLED = 0x0533,

View file

@ -118,12 +118,17 @@
<Compile Include="Server\ConnectionState\ProcessStateObject.cs" />
<Compile Include="Server\ConnectionState\SMB1ConnectionState.cs" />
<Compile Include="Server\ConnectionState\SMB1Session.cs" />
<Compile Include="Server\ConnectionState\SMB2ConnectionState.cs" />
<Compile Include="Server\ConnectionState\SMB2Session.cs" />
<Compile Include="Server\Exceptions\EmptyPasswordNotAllowedException.cs" />
<Compile Include="Server\Exceptions\InvalidRequestException.cs" />
<Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
<Compile Include="Server\Helpers\IOExceptionHelper.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.Find.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.Query.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.QueryFileSystem.cs" />
<Compile Include="Server\Helpers\NTFileSystemHelper.Set.cs" />
<Compile Include="Server\Helpers\ServerPathUtils.cs" />
<Compile Include="Server\IndependentUserCollection.cs" />
<Compile Include="Server\INTLMAuthenticationProvider.cs" />
@ -148,8 +153,18 @@
<Compile Include="Server\SMB1\TransactionHelper.cs" />
<Compile Include="Server\SMB1\TransactionSubcommandHelper.cs" />
<Compile Include="Server\SMB1\TreeConnectHelper.cs" />
<Compile Include="Server\SMB2\CreateHelper.cs" />
<Compile Include="Server\SMB2\IOCtlHelper.cs" />
<Compile Include="Server\SMB2\NegotiateHelper.cs" />
<Compile Include="Server\SMB2\QueryDirectoryHelper.cs" />
<Compile Include="Server\SMB2\QueryInfoHelper.cs" />
<Compile Include="Server\SMB2\ReadWriteResponseHelper.cs" />
<Compile Include="Server\SMB2\SessionSetupHelper.cs" />
<Compile Include="Server\SMB2\SetInfoHelper.cs" />
<Compile Include="Server\SMB2\TreeConnectHelper.cs" />
<Compile Include="Server\SMBServer.cs" />
<Compile Include="Server\SMBServer.SMB1.cs" />
<Compile Include="Server\SMBServer.SMB2.cs" />
<Compile Include="Server\User.cs" />
<Compile Include="Server\UserCollection.cs" />
<Compile Include="Services\Enums\PlatformName.cs" />

View file

@ -19,6 +19,8 @@ namespace SMBLibrary.Server
{
NotSet,
NTLM012, // NT LM 0.12
SMB202, // SMB 2.0.2
SMB210, // SMB 2.1
}
public class ConnectionState

View file

@ -0,0 +1,71 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server
{
public delegate ulong? AllocatePersistentFileID();
public class SMB2ConnectionState : ConnectionState
{
// Key is SessionID
private Dictionary<ulong, SMB2Session> m_sessions = new Dictionary<ulong, SMB2Session>();
private ulong m_nextSessionID = 1;
public AllocatePersistentFileID AllocatePersistentFileID;
public SMB2ConnectionState(ConnectionState state, AllocatePersistentFileID allocatePersistentFileID) : base(state)
{
AllocatePersistentFileID = allocatePersistentFileID;
}
public ulong? AllocateSessionID()
{
for (ulong offset = 0; offset < UInt64.MaxValue; offset++)
{
ulong sessionID = (ulong)(m_nextSessionID + offset);
if (sessionID == 0 || sessionID == 0xFFFFFFFF)
{
continue;
}
if (!m_sessions.ContainsKey(sessionID))
{
m_nextSessionID = (ulong)(sessionID + 1);
return sessionID;
}
}
return null;
}
public SMB2Session CreateSession(ulong sessionID, string userName)
{
SMB2Session session = new SMB2Session(this, sessionID, userName);
m_sessions.Add(sessionID, session);
return session;
}
public SMB2Session GetSession(ulong sessionID)
{
SMB2Session session;
m_sessions.TryGetValue(sessionID, out session);
return session;
}
public void RemoveSession(ulong sessionID)
{
m_sessions.Remove(sessionID);
}
public void ClearSessions()
{
m_sessions.Clear();
}
}
}

View file

@ -0,0 +1,165 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server
{
public class SMB2Session
{
private SMB2ConnectionState m_connection;
private ulong m_sessionID;
private string m_userName;
// Key is TreeID
private Dictionary<uint, ISMBShare> m_connectedTrees = new Dictionary<uint, ISMBShare>();
private uint m_nextTreeID = 1; // TreeID uniquely identifies a tree connect within the scope of the session
// Key is the persistent portion of the FileID
private Dictionary<ulong, OpenFileObject> m_openFiles = new Dictionary<ulong, OpenFileObject>();
// Key is the persistent portion of the FileID
private Dictionary<ulong, OpenSearch> m_openSearches = new Dictionary<ulong, OpenSearch>();
public SMB2Session(SMB2ConnectionState connecton, ulong sessionID, string userName)
{
m_connection = connecton;
m_sessionID = sessionID;
m_userName = userName;
}
private uint? AllocateTreeID()
{
for (uint offset = 0; offset < UInt32.MaxValue; offset++)
{
uint treeID = (uint)(m_nextTreeID + offset);
if (treeID == 0 || treeID == 0xFFFFFFFF)
{
continue;
}
if (!m_connectedTrees.ContainsKey(treeID))
{
m_nextTreeID = (uint)(treeID + 1);
return treeID;
}
}
return null;
}
public uint? AddConnectedTree(ISMBShare share)
{
uint? treeID = AllocateTreeID();
if (treeID.HasValue)
{
m_connectedTrees.Add(treeID.Value, share);
}
return treeID;
}
public ISMBShare GetConnectedTree(uint treeID)
{
if (m_connectedTrees.ContainsKey(treeID))
{
return m_connectedTrees[treeID];
}
else
{
return null;
}
}
public void RemoveConnectedTree(uint treeID)
{
m_connectedTrees.Remove(treeID);
}
public void RemoveConnectedTrees()
{
m_connectedTrees.Clear();
}
public bool IsTreeConnected(uint treeID)
{
return m_connectedTrees.ContainsKey(treeID);
}
/// <param name="relativePath">Should include the path relative to the share</param>
/// <returns>The persistent portion of the FileID</returns>
public ulong? AddOpenFile(string relativePath)
{
return AddOpenFile(relativePath, null);
}
public ulong? AddOpenFile(string relativePath, Stream stream)
{
return AddOpenFile(relativePath, stream, false);
}
public ulong? AddOpenFile(string relativePath, Stream stream, bool deleteOnClose)
{
ulong? persistentID = m_connection.AllocatePersistentFileID();
if (persistentID.HasValue)
{
m_openFiles.Add(persistentID.Value, new OpenFileObject(relativePath, stream, deleteOnClose));
}
return persistentID;
}
public OpenFileObject GetOpenFileObject(ulong fileID)
{
if (m_openFiles.ContainsKey(fileID))
{
return m_openFiles[fileID];
}
else
{
return null;
}
}
public void RemoveOpenFile(ulong fileID)
{
Stream stream = m_openFiles[fileID].Stream;
if (stream != null)
{
stream.Close();
}
m_openFiles.Remove(fileID);
m_openSearches.Remove(fileID);
}
public OpenSearch AddOpenSearch(ulong fileID, List<FileSystemEntry> entries, int enumerationLocation)
{
OpenSearch openSearch = new OpenSearch(entries, enumerationLocation);
m_openSearches.Add(fileID, openSearch);
return openSearch;
}
public OpenSearch GetOpenSearch(ulong fileID)
{
OpenSearch openSearch;
m_openSearches.TryGetValue(fileID, out openSearch);
return openSearch;
}
public void RemoveOpenSearch(ulong fileID)
{
m_openSearches.Remove(fileID);
}
public string UserName
{
get
{
return m_userName;
}
}
}
}

View file

@ -0,0 +1,191 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using Utilities;
namespace SMBLibrary.Server
{
public partial class NTFileSystemHelper
{
public static NTStatus GetNamedPipeInformation(out FileInformation result, FileInformationClass informationClass)
{
switch (informationClass)
{
case FileInformationClass.FileBasicInformation:
{
FileBasicInformation information = new FileBasicInformation();
information.FileAttributes = FileAttributes.Temporary;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FileStandardInformation:
{
FileStandardInformation information = new FileStandardInformation();
information.DeletePending = true;
result = information;
return NTStatus.STATUS_SUCCESS;
}
default:
result = null;
return NTStatus.STATUS_INVALID_INFO_CLASS;
}
}
public static NTStatus GetFileInformation(out FileInformation result, FileSystemEntry entry, bool deletePending, FileInformationClass informationClass)
{
switch (informationClass)
{
case FileInformationClass.FileBasicInformation:
{
FileBasicInformation information = new FileBasicInformation();
information.CreationTime = entry.CreationTime;
information.LastAccessTime = entry.LastAccessTime;
information.LastWriteTime = entry.LastWriteTime;
information.ChangeTime = entry.LastWriteTime;
information.FileAttributes = GetFileAttributes(entry);
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FileStandardInformation:
{
FileStandardInformation information = new FileStandardInformation();
information.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
information.EndOfFile = entry.Size;
information.Directory = entry.IsDirectory;
information.DeletePending = deletePending;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FileInternalInformation:
{
FileInternalInformation information = new FileInternalInformation();
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FileEaInformation:
{
FileEaInformation information = new FileEaInformation();
information.EaSize = 0;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FilePositionInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileFullEaInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileModeInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileAlignmentInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileAllInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileAlternateNameInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileStreamInformation:
{
// This information class is used to enumerate the data streams of a file or a directory.
// A buffer of FileStreamInformation data elements is returned by the server.
FileStreamInformation information = new FileStreamInformation();
information.StreamSize = entry.Size;
information.StreamAllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
information.StreamName = "::$DATA";
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FilePipeInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FilePipeLocalInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FilePipeRemoteInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileCompressionInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
case FileInformationClass.FileNetworkOpenInformation:
{
FileNetworkOpenInformation information = new FileNetworkOpenInformation();
information.CreationTime = entry.CreationTime;
information.LastAccessTime = entry.LastAccessTime;
information.LastWriteTime = entry.LastWriteTime;
information.ChangeTime = entry.LastWriteTime;
information.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
information.EndOfFile = entry.Size;
information.FileAttributes = GetFileAttributes(entry);
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileInformationClass.FileAttributeTagInformation:
{
result = null;
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
default:
result = null;
return NTStatus.STATUS_INVALID_INFO_CLASS;
}
}
public static FileAttributes GetFileAttributes(FileSystemEntry entry)
{
FileAttributes attributes = 0;
if (entry.IsHidden)
{
attributes |= FileAttributes.Hidden;
}
if (entry.IsReadonly)
{
attributes |= FileAttributes.ReadOnly;
}
if (entry.IsArchived)
{
attributes |= FileAttributes.Archive;
}
if (entry.IsDirectory)
{
attributes |= FileAttributes.Directory;
}
if (attributes == 0)
{
attributes = FileAttributes.Normal;
}
return attributes;
}
}
}

View file

@ -0,0 +1,101 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using Utilities;
namespace SMBLibrary.Server
{
public partial class NTFileSystemHelper
{
public static NTStatus GetFileSystemInformation(out FileSystemInformation result, FileSystemInformationClass informationClass, IFileSystem fileSystem)
{
switch (informationClass)
{
case FileSystemInformationClass.FileFsVolumeInformation:
{
FileFsVolumeInformation information = new FileFsVolumeInformation();
information.SupportsObjects = false;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileSystemInformationClass.FileFsSizeInformation:
{
FileFsSizeInformation information = new FileFsSizeInformation();
information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize;
information.AvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize;
information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector;
information.BytesPerSector = NTFileSystemHelper.BytesPerSector;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileSystemInformationClass.FileFsDeviceInformation:
{
FileFsDeviceInformation information = new FileFsDeviceInformation();
information.DeviceType = DeviceType.Disk;
information.Characteristics = DeviceCharacteristics.IsMounted;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileSystemInformationClass.FileFsAttributeInformation:
{
FileFsAttributeInformation information = new FileFsAttributeInformation();
information.FileSystemAttributes = FileSystemAttributes.UnicodeOnDisk;
information.MaximumComponentNameLength = 255;
information.FileSystemName = fileSystem.Name;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileSystemInformationClass.FileFsControlInformation:
{
FileFsControlInformation information = new FileFsControlInformation();
information.FileSystemControlFlags = FileSystemControlFlags.ContentIndexingDisabled;
information.DefaultQuotaThreshold = UInt64.MaxValue;
information.DefaultQuotaLimit = UInt64.MaxValue;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileSystemInformationClass.FileFsFullSizeInformation:
{
FileFsFullSizeInformation information = new FileFsFullSizeInformation();
information.TotalAllocationUnits = fileSystem.Size / NTFileSystemHelper.ClusterSize;
information.CallerAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize;
information.ActualAvailableAllocationUnits = fileSystem.FreeSpace / NTFileSystemHelper.ClusterSize;
information.SectorsPerAllocationUnit = NTFileSystemHelper.ClusterSize / NTFileSystemHelper.BytesPerSector;
information.BytesPerSector = NTFileSystemHelper.BytesPerSector;
result = information;
return NTStatus.STATUS_SUCCESS;
}
case FileSystemInformationClass.FileFsObjectIdInformation:
{
result = null;
// STATUS_INVALID_PARAMETER is returned when the file system does not implement object IDs
// See: https://msdn.microsoft.com/en-us/library/cc232106.aspx
return NTStatus.STATUS_INVALID_PARAMETER;
}
case FileSystemInformationClass.FileFsSectorSizeInformation:
{
FileFsSectorSizeInformation information = new FileFsSectorSizeInformation();
information.LogicalBytesPerSector = NTFileSystemHelper.BytesPerSector;
information.PhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector;
information.PhysicalBytesPerSectorForPerformance = NTFileSystemHelper.BytesPerSector;
information.FileSystemEffectivePhysicalBytesPerSectorForAtomicity = NTFileSystemHelper.BytesPerSector;
information.ByteOffsetForSectorAlignment = 0;
information.ByteOffsetForPartitionAlignment = 0;
result = information;
return NTStatus.STATUS_SUCCESS;
}
default:
{
result = null;
return NTStatus.STATUS_INVALID_INFO_CLASS;
}
}
}
}
}

View file

@ -0,0 +1,207 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using Utilities;
namespace SMBLibrary.Server
{
public partial class NTFileSystemHelper
{
public static NTStatus SetFileInformation(IFileSystem fileSystem, OpenFileObject openFile, FileInformation information, ConnectionState state)
{
if (information is FileBasicInformation)
{
FileBasicInformation basicInformation = (FileBasicInformation)information;
bool isHidden = ((basicInformation.FileAttributes & FileAttributes.Hidden) > 0);
bool isReadonly = (basicInformation.FileAttributes & FileAttributes.ReadOnly) > 0;
bool isArchived = (basicInformation.FileAttributes & FileAttributes.Archive) > 0;
try
{
fileSystem.SetAttributes(openFile.Path, isHidden, isReadonly, isArchived);
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file attributes on '{0}'. Access Denied.", openFile.Path);
return NTStatus.STATUS_ACCESS_DENIED;
}
try
{
fileSystem.SetDates(openFile.Path, basicInformation.CreationTime, basicInformation.LastWriteTime, basicInformation.LastAccessTime);
}
catch (IOException ex)
{
ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Sharing Violation.", openFile.Path);
return NTStatus.STATUS_SHARING_VIOLATION;
}
else
{
state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Data Error.", openFile.Path);
return NTStatus.STATUS_DATA_ERROR;
}
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Failed to set file dates on '{0}'. Access Denied.", openFile.Path);
return NTStatus.STATUS_ACCESS_DENIED;
}
return NTStatus.STATUS_SUCCESS;
}
else if (information is FileRenameInformationType2)
{
FileRenameInformationType2 renameInformation = (FileRenameInformationType2)information;
string destination = renameInformation.FileName;
if (!destination.StartsWith(@"\"))
{
destination = @"\" + destination;
}
if (openFile.Stream != null)
{
openFile.Stream.Close();
}
try
{
if (renameInformation.ReplaceIfExists && (fileSystem.GetEntry(destination) != null ))
{
fileSystem.Delete(destination);
}
fileSystem.Move(openFile.Path, destination);
}
catch (IOException ex)
{
ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Sharing Violation.", openFile.Path);
return NTStatus.STATUS_SHARING_VIOLATION;
}
if (errorCode == (ushort)Win32Error.ERROR_ALREADY_EXISTS)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Already Exists.", openFile.Path);
return NTStatus.STATUS_OBJECT_NAME_EXISTS;
}
else
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Data Error.", openFile.Path);
return NTStatus.STATUS_DATA_ERROR;
}
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot rename '{0}'. Access Denied.", openFile.Path);
return NTStatus.STATUS_ACCESS_DENIED;
}
openFile.Path = destination;
return NTStatus.STATUS_SUCCESS;
}
else if (information is FileDispositionInformation)
{
if (((FileDispositionInformation)information).DeletePending)
{
// We're supposed to delete the file on close, but it's too late to report errors at this late stage
if (openFile.Stream != null)
{
openFile.Stream.Close();
}
try
{
state.LogToServer(Severity.Information, "SetFileInformation: Deleting file '{0}'", openFile.Path);
fileSystem.Delete(openFile.Path);
}
catch (IOException ex)
{
ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
{
state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}'. Sharing Violation.", openFile.Path);
return NTStatus.STATUS_SHARING_VIOLATION;
}
else
{
state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}'. Data Error.", openFile.Path);
return NTStatus.STATUS_DATA_ERROR;
}
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Information, "SetFileInformation: Error deleting '{0}', Access Denied.", openFile.Path);
return NTStatus.STATUS_ACCESS_DENIED;
}
}
return NTStatus.STATUS_SUCCESS;
}
else if (information is FileAllocationInformation)
{
ulong allocationSize = ((FileAllocationInformation)information).AllocationSize;
try
{
openFile.Stream.SetLength((long)allocationSize);
}
catch (IOException ex)
{
ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Sharing Violation.", openFile.Path);
return NTStatus.STATUS_SHARING_VIOLATION;
}
else
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Data Error.", openFile.Path);
return NTStatus.STATUS_DATA_ERROR;
}
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set allocation for '{0}'. Access Denied.", openFile.Path);
return NTStatus.STATUS_ACCESS_DENIED;
}
return NTStatus.STATUS_SUCCESS;
}
else if (information is FileEndOfFileInformation)
{
ulong endOfFile = ((FileEndOfFileInformation)information).EndOfFile;
try
{
openFile.Stream.SetLength((long)endOfFile);
}
catch (IOException ex)
{
ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Sharing Violation.", openFile.Path);
return NTStatus.STATUS_SHARING_VIOLATION;
}
else
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Data Error.", openFile.Path);
return NTStatus.STATUS_DATA_ERROR;
}
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Debug, "SetFileInformation: Cannot set end of file for '{0}'. Access Denied.", openFile.Path);
return NTStatus.STATUS_ACCESS_DENIED;
}
return NTStatus.STATUS_SUCCESS;
}
else
{
return NTStatus.STATUS_NOT_IMPLEMENTED;
}
}
}
}

View file

@ -0,0 +1,149 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class CreateHelper
{
internal static SMB2Command GetCreateResponse(CreateRequest request, ISMBShare share, SMB2ConnectionState state)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
string path = request.Name;
if (!path.StartsWith(@"\"))
{
path = @"\" + path;
}
if (share is NamedPipeShare)
{
Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path);
if (pipeStream != null)
{
ulong? persistentFileID = session.AddOpenFile(path, pipeStream);
if (!persistentFileID.HasValue)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES);
}
return CreateResponseForNamedPipe(persistentFileID.Value);
}
else
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_OBJECT_PATH_NOT_FOUND);
}
}
else
{
FileSystemShare fileSystemShare = (FileSystemShare)share;
FileSystemEntry entry;
NTStatus createStatus = NTFileSystemHelper.CreateFile(out entry, (FileSystemShare)share, session.UserName, path, request.CreateDisposition, request.CreateOptions, request.DesiredAccess, state);
if (createStatus != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName, createStatus);
}
IFileSystem fileSystem = fileSystemShare.FileSystem;
FileAccess fileAccess = NTFileSystemHelper.ToFileAccess(request.DesiredAccess.File);
FileShare fileShare = NTFileSystemHelper.ToFileShare(request.ShareAccess);
Stream stream;
bool deleteOnClose = false;
if (fileAccess == (FileAccess)0 || entry.IsDirectory)
{
stream = null;
}
else
{
// When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point.
// FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception
// of a file copy operation (where the caller will attempt to simply copy the reparse point).
deleteOnClose = (request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0;
bool openReparsePoint = (request.CreateOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0;
bool disableBuffering = (request.CreateOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
bool buffered = (request.CreateOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
state.LogToServer(Severity.Verbose, "Create: Opening '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShare, buffered);
try
{
stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare);
}
catch (IOException ex)
{
ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
{
state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}'", path);
return new ErrorResponse(request.CommandName, NTStatus.STATUS_SHARING_VIOLATION);
}
else
{
state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}', Data Error", path);
return new ErrorResponse(request.CommandName, NTStatus.STATUS_DATA_ERROR);
}
}
catch (UnauthorizedAccessException)
{
state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}', Access Denied", path);
return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
}
if (buffered)
{
stream = new PrefetchedStream(stream);
}
}
ulong? persistentFileID = session.AddOpenFile(path, stream, deleteOnClose);
if (!persistentFileID.HasValue)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES);
}
CreateResponse response = CreateResponseFromFileSystemEntry(entry, persistentFileID.Value);
if (request.RequestedOplockLevel == OplockLevel.Batch)
{
response.OplockLevel = OplockLevel.Batch;
}
return response;
}
}
private static CreateResponse CreateResponseForNamedPipe(ulong persistentFileID)
{
CreateResponse response = new CreateResponse();
response.FileId.Persistent = persistentFileID;
response.CreateAction = CreateAction.FILE_OPENED;
response.FileAttributes = FileAttributes.Normal;
return response;
}
private static CreateResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ulong persistentFileID)
{
CreateResponse response = new CreateResponse();
if (entry.IsDirectory)
{
response.FileAttributes = FileAttributes.Directory;
}
else
{
response.FileAttributes = FileAttributes.Normal;
}
response.FileId.Persistent = persistentFileID;
response.CreateAction = CreateAction.FILE_OPENED;
response.CreationTime = entry.CreationTime;
response.LastWriteTime = entry.LastWriteTime;
response.ChangeTime = entry.LastWriteTime;
response.LastAccessTime = entry.LastAccessTime;
response.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
response.EndofFile = entry.Size;
return response;
}
}
}

View file

@ -0,0 +1,51 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class IOCtlHelper
{
private const uint FSCTL_DFS_GET_REFERRALS = 0x00060194;
private const uint FSCTL_DFS_GET_REFERRALS_EX = 0x000601B0;
private const uint FSCTL_PIPE_TRANSCEIVE = 0x0011C017;
internal static SMB2Command GetIOCtlResponse(IOCtlRequest request, ISMBShare share, SMB2ConnectionState state)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
if (request.CtlCode == FSCTL_DFS_GET_REFERRALS || request.CtlCode == FSCTL_DFS_GET_REFERRALS_EX)
{
// [MS-SMB2] 3.3.5.15.2 Handling a DFS Referral Information Request
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FS_DRIVER_REQUIRED);
}
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
if (share is NamedPipeShare)
{
if (request.CtlCode == FSCTL_PIPE_TRANSCEIVE)
{
IOCtlResponse response = new IOCtlResponse();
response.CtlCode = request.CtlCode;
openFile.Stream.Write(request.Input, 0, request.Input.Length);
response.Output = ByteReader.ReadAllBytes(openFile.Stream);
return response;
}
}
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
}
}

View file

@ -0,0 +1,101 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.Authentication;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
/// <summary>
/// Negotiate helper
/// </summary>
public class NegotiateHelper
{
public const string SMB2002Dialect = "SMB 2.002";
public const string SMB2xxxDialect = "SMB 2.???";
// Special case - SMB2 client initially connecting using SMB1
internal static SMB2Command GetNegotiateResponse(List<string> smb2Dialects, ConnectionState state, Guid serverGuid)
{
NegotiateResponse response = new NegotiateResponse();
response.Header.Credits = 1;
if (smb2Dialects.Contains(SMB2xxxDialect))
{
response.DialectRevision = SMB2Dialect.SMB2xx;
}
else if (smb2Dialects.Contains(SMB2002Dialect))
{
state.ServerDialect = SMBDialect.SMB202;
response.DialectRevision = SMB2Dialect.SMB202;
}
else
{
throw new ArgumentException("SMB2 dialect is not present");
}
response.ServerGuid = serverGuid;
response.MaxTransactSize = 65536;
response.MaxReadSize = 65536;
response.MaxWriteSize = 65536;
response.SystemTime = DateTime.Now;
response.ServerStartTime = DateTime.Today;
return response;
}
internal static SMB2Command GetNegotiateResponse(NegotiateRequest request, ConnectionState state, Guid serverGuid)
{
NegotiateResponse response = new NegotiateResponse();
if (request.Dialects.Contains(SMB2Dialect.SMB210))
{
state.ServerDialect = SMBDialect.SMB210;
response.DialectRevision = SMB2Dialect.SMB210;
}
else if (request.Dialects.Contains(SMB2Dialect.SMB202))
{
state.ServerDialect = SMBDialect.SMB202;
response.DialectRevision = SMB2Dialect.SMB202;
}
else
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
response.ServerGuid = serverGuid;
response.MaxTransactSize = 65536;
response.MaxReadSize = 65536;
response.MaxWriteSize = 65536;
response.SystemTime = DateTime.Now;
response.ServerStartTime = DateTime.Today;
return response;
}
internal static List<string> FindSMB2Dialects(SMBLibrary.SMB1.SMB1Message message)
{
if (message.Commands.Count > 0 && message.Commands[0] is SMBLibrary.SMB1.NegotiateRequest)
{
SMBLibrary.SMB1.NegotiateRequest request = (SMBLibrary.SMB1.NegotiateRequest)message.Commands[0];
return FindSMB2Dialects(request);
}
return new List<string>();
}
internal static List<string> FindSMB2Dialects(SMBLibrary.SMB1.NegotiateRequest request)
{
List<string> result = new List<string>();
if (request.Dialects.Contains(SMB2002Dialect))
{
result.Add(SMB2002Dialect);
}
if (request.Dialects.Contains(SMB2xxxDialect))
{
result.Add(SMB2xxxDialect);
}
return result;
}
}
}

View file

@ -0,0 +1,208 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.Authentication;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class QueryDirectoryHelper
{
internal static SMB2Command GetQueryDirectoryResponse(QueryDirectoryRequest request, ISMBShare share, SMB2ConnectionState state)
{
if (!(share is FileSystemShare))
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
SMB2Session session = state.GetSession(request.Header.SessionID);
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
FileSystemShare fileSystemShare = (FileSystemShare)share;
IFileSystem fileSystem = fileSystemShare.FileSystem;
if (!fileSystem.GetEntry(openFile.Path).IsDirectory)
{
if ((request.Flags & QueryDirectoryFlags.SMB2_REOPEN) > 0)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
else
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
}
}
ulong fileID = request.FileId.Persistent;
OpenSearch openSearch = session.GetOpenSearch(fileID);
if (openSearch == null || request.Reopen)
{
if (request.Reopen)
{
session.RemoveOpenSearch(fileID);
}
List<FileSystemEntry> entries;
NTStatus searchStatus = NTFileSystemHelper.FindEntries(out entries, fileSystemShare.FileSystem, openFile.Path, request.FileName);
if (searchStatus != NTStatus.STATUS_SUCCESS)
{
state.LogToServer(Severity.Verbose, "Query Directory: Path: '{0}', Searched for '{1}', NTStatus: {2}", openFile.Path, request.FileName, searchStatus.ToString());
return new ErrorResponse(request.CommandName, searchStatus);
}
state.LogToServer(Severity.Verbose, "Query Directory: Path: '{0}', Searched for '{1}', found {2} matching entries", openFile.Path, request.FileName, entries.Count);
openSearch = session.AddOpenSearch(fileID, entries, 0);
}
if (request.Restart || request.Reopen)
{
openSearch.EnumerationLocation = 0;
}
if (openSearch.Entries.Count == 0)
{
// [MS-SMB2] If there are no entries to return [..] the server MUST fail the request with STATUS_NO_SUCH_FILE.
session.RemoveOpenSearch(fileID);
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_SUCH_FILE);
}
if (openSearch.EnumerationLocation == openSearch.Entries.Count)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_MORE_FILES);
}
List<QueryDirectoryFileInformation> page = new List<QueryDirectoryFileInformation>();
int pageLength = 0;
for (int index = openSearch.EnumerationLocation; index < openSearch.Entries.Count; index++)
{
QueryDirectoryFileInformation fileInformation;
try
{
fileInformation = FromFileSystemEntry(openSearch.Entries[index], request.FileInformationClass);
}
catch (UnsupportedInformationLevelException)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS);
}
if (pageLength + fileInformation.Length <= request.OutputBufferLength)
{
page.Add(fileInformation);
pageLength += fileInformation.Length;
openSearch.EnumerationLocation = index + 1;
}
else
{
break;
}
if (request.ReturnSingleEntry)
{
break;
}
}
QueryDirectoryResponse response = new QueryDirectoryResponse();
response.SetFileInformationList(page);
return response;
}
internal static QueryDirectoryFileInformation FromFileSystemEntry(FileSystemEntry entry, FileInformationClass informationClass)
{
switch (informationClass)
{
case FileInformationClass.FileBothDirectoryInformation:
{
FileBothDirectoryInformation result = new FileBothDirectoryInformation();
result.CreationTime = entry.CreationTime;
result.LastAccessTime = entry.LastAccessTime;
result.LastWriteTime = entry.LastWriteTime;
result.ChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
result.EaSize = 0;
result.ShortName = NTFileSystemHelper.GetShortName(entry.Name);
result.FileName = entry.Name;
return result;
}
case FileInformationClass.FileDirectoryInformation:
{
FileDirectoryInformation result = new FileDirectoryInformation();
result.CreationTime = entry.CreationTime;
result.LastAccessTime = entry.LastAccessTime;
result.LastWriteTime = entry.LastWriteTime;
result.ChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
result.FileName = entry.Name;
return result;
}
case FileInformationClass.FileFullDirectoryInformation:
{
FileFullDirectoryInformation result = new FileFullDirectoryInformation();
result.CreationTime = entry.CreationTime;
result.LastAccessTime = entry.LastAccessTime;
result.LastWriteTime = entry.LastWriteTime;
result.ChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
result.EaSize = 0;
result.FileName = entry.Name;
return result;
}
case FileInformationClass.FileIdBothDirectoryInformation:
{
FileIdBothDirectoryInformation result = new FileIdBothDirectoryInformation();
result.CreationTime = entry.CreationTime;
result.LastAccessTime = entry.LastAccessTime;
result.LastWriteTime = entry.LastWriteTime;
result.ChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
result.EaSize = 0;
result.ShortName = NTFileSystemHelper.GetShortName(entry.Name);
result.FileId = 0;
result.FileName = entry.Name;
return result;
}
case FileInformationClass.FileIdFullDirectoryInformation:
{
FileIdFullDirectoryInformation result = new FileIdFullDirectoryInformation();
result.CreationTime = entry.CreationTime;
result.LastAccessTime = entry.LastAccessTime;
result.LastWriteTime = entry.LastWriteTime;
result.ChangeTime = entry.LastWriteTime;
result.EndOfFile = entry.Size;
result.AllocationSize = NTFileSystemHelper.GetAllocationSize(entry.Size);
result.FileAttributes = NTFileSystemHelper.GetFileAttributes(entry);
result.EaSize = 0;
result.FileId = 0;
result.FileName = entry.Name;
return result;
}
case FileInformationClass.FileNamesInformation:
{
FileNamesInformation result = new FileNamesInformation();
result.FileName = entry.Name;
return result;
}
default:
{
throw new UnsupportedInformationLevelException();
}
}
}
}
}

View file

@ -0,0 +1,75 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.Authentication;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class QueryInfoHelper
{
internal static SMB2Command GetQueryInfoResponse(QueryInfoRequest request, ISMBShare share, SMB2ConnectionState state)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
if (request.InfoType == InfoType.File)
{
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
FileInformation fileInformation;
NTStatus queryStatus;
if (share is NamedPipeShare)
{
queryStatus = NTFileSystemHelper.GetNamedPipeInformation(out fileInformation, request.FileInformationClass);
}
else // FileSystemShare
{
IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
FileSystemEntry entry = fileSystem.GetEntry(openFile.Path);
if (entry == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_SUCH_FILE);
}
queryStatus = NTFileSystemHelper.GetFileInformation(out fileInformation, entry, openFile.DeleteOnClose, request.FileInformationClass);
}
if (queryStatus != NTStatus.STATUS_SUCCESS)
{
state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, request.FileInformationClass, queryStatus);
return new ErrorResponse(request.CommandName, queryStatus);
}
QueryInfoResponse response = new QueryInfoResponse();
response.SetFileInformation(fileInformation);
return response;
}
else if (request.InfoType == InfoType.FileSystem)
{
if (share is FileSystemShare)
{
IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
FileSystemInformation fileSystemInformation;
NTStatus queryStatus = NTFileSystemHelper.GetFileSystemInformation(out fileSystemInformation, request.FileSystemInformationClass, fileSystem);
if (queryStatus != NTStatus.STATUS_SUCCESS)
{
state.LogToServer(Severity.Verbose, "GetFileSystemInformation failed. Information class: {0}, NTStatus: {1}", request.FileSystemInformationClass, queryStatus);
return new ErrorResponse(request.CommandName, queryStatus);
}
QueryInfoResponse response = new QueryInfoResponse();
response.SetFileSystemInformation(fileSystemInformation);
return response;
}
}
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
}
}

View file

@ -0,0 +1,57 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.Authentication;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class ReadWriteResponseHelper
{
internal static SMB2Command GetReadResponse(ReadRequest request, ISMBShare share, SMB2ConnectionState state)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
byte[] data;
NTStatus readStatus = NTFileSystemHelper.ReadFile(out data, openFile, (long)request.Offset, (int)request.ReadLength, state);
if (readStatus != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName, readStatus);
}
ReadResponse response = new ReadResponse();
response.Data = data;
return response;
}
internal static SMB2Command GetWriteResponse(WriteRequest request, ISMBShare share, SMB2ConnectionState state)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
int numberOfBytesWritten;
NTStatus writeStatus = NTFileSystemHelper.WriteFile(out numberOfBytesWritten, openFile, (long)request.Offset, request.Data, state);
if (writeStatus != NTStatus.STATUS_SUCCESS)
{
return new ErrorResponse(request.CommandName, writeStatus);
}
WriteResponse response = new WriteResponse();
response.Count = (uint)numberOfBytesWritten;
return response;
}
}
}

View file

@ -0,0 +1,101 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.Authentication;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
/// <summary>
/// Session Setup helper
/// </summary>
public class SessionSetupHelper
{
internal static SMB2Command GetSessionSetupResponse(SessionSetupRequest request, INTLMAuthenticationProvider users, SMB2ConnectionState state)
{
// [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
SessionSetupResponse response = new SessionSetupResponse();
byte[] messageBytes = request.SecurityBuffer;
bool isRawMessage = true;
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
{
messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBuffer);
isRawMessage = false;
}
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
// According to [MS-SMB2] 3.3.5.5.3, response.Header.SessionID must be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED
if (request.Header.SessionID == 0)
{
ulong? sessionID = state.AllocateSessionID();
if (!sessionID.HasValue)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_SESSIONS);
}
response.Header.SessionID = sessionID.Value;
}
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
if (messageType == MessageTypeName.Negotiate)
{
NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
ChallengeMessage challengeMessage = users.GetChallengeMessage(negotiateMessage);
if (isRawMessage)
{
response.SecurityBuffer = challengeMessage.GetBytes();
}
else
{
response.SecurityBuffer = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessage.GetBytes());
}
response.Header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
}
else // MessageTypeName.Authenticate
{
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
bool loginSuccess;
try
{
loginSuccess = users.Authenticate(authenticateMessage);
}
catch (EmptyPasswordNotAllowedException)
{
state.LogToServer(Severity.Information, "User '{0}' authentication using an empty password was rejected", authenticateMessage.UserName);
return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCOUNT_RESTRICTION);
}
if (loginSuccess)
{
state.LogToServer(Severity.Information, "User '{0}' authenticated successfully", authenticateMessage.UserName);
state.CreateSession(request.Header.SessionID, authenticateMessage.UserName);
}
else if (users.FallbackToGuest(authenticateMessage.UserName))
{
state.LogToServer(Severity.Information, "User '{0}' failed authentication. logged in as guest", authenticateMessage.UserName);
state.CreateSession(request.Header.SessionID, "Guest");
response.SessionFlags = SessionFlags.IsGuest;
}
else
{
state.LogToServer(Severity.Information, "User '{0}' failed authentication", authenticateMessage.UserName);
return new ErrorResponse(request.CommandName, NTStatus.STATUS_LOGON_FAILURE);
}
if (!isRawMessage)
{
response.SecurityBuffer = GSSAPIHelper.GetGSSTokenAcceptCompletedResponse();
}
}
return response;
}
}
}

View file

@ -0,0 +1,65 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.Authentication;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class SetInfoHelper
{
internal static SMB2Command GetSetInfoResponse(SetInfoRequest request, ISMBShare share, SMB2ConnectionState state)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
if (request.InfoType == InfoType.File)
{
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
if (share is FileSystemShare)
{
IFileSystem fileSystem = ((FileSystemShare)share).FileSystem;
if (!((FileSystemShare)share).HasWriteAccess(session.UserName))
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
}
FileInformation information;
try
{
information = FileInformation.GetFileInformation(request.Buffer, 0, request.FileInformationClass);
}
catch (UnsupportedInformationLevelException)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS);
}
catch (NotImplementedException)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
catch (Exception)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
}
NTStatus status = NTFileSystemHelper.SetFileInformation(fileSystem, openFile, information, state);
if (status != NTStatus.STATUS_SUCCESS)
{
state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", openFile.Path, information.FileInformationClass, status);
return new ErrorResponse(request.CommandName, status);
}
return new SetInfoResponse();
}
}
return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
}
}

View file

@ -0,0 +1,62 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.Text;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server.SMB2
{
public class TreeConnectHelper
{
internal static SMB2Command GetTreeConnectResponse(TreeConnectRequest request, SMB2ConnectionState state, NamedPipeShare services, ShareCollection shares)
{
SMB2Session session = state.GetSession(request.Header.SessionID);
TreeConnectResponse response = new TreeConnectResponse();
string shareName = ServerPathUtils.GetShareName(request.Path);
ISMBShare share;
ShareType shareType;
ShareFlags shareFlags = ShareFlags.ManualCaching;
if (String.Equals(shareName, NamedPipeShare.NamedPipeShareName, StringComparison.InvariantCultureIgnoreCase))
{
share = services;
shareType = ShareType.Pipe;
shareFlags = ShareFlags.NoCaching;
}
else
{
share = shares.GetShareFromName(shareName);
shareType = ShareType.Disk;
if (share == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_OBJECT_PATH_NOT_FOUND);
}
if (!((FileSystemShare)share).HasReadAccess(session.UserName))
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED);
}
}
uint? treeID = session.AddConnectedTree(share);
if (!treeID.HasValue)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INSUFF_SERVER_RESOURCES);
}
response.Header.TreeID = treeID.Value;
response.ShareType = shareType;
response.ShareFlags = shareFlags;
response.MaximalAccess.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA |
FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA |
FileAccessMask.FILE_EXECUTE |
FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES |
FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE;
return response;
}
}
}

View file

@ -15,7 +15,7 @@ namespace SMBLibrary.Server
{
public partial class SMBServer
{
public void ProcessSMB1Message(SMB1Message message, SMB1ConnectionState state)
public void ProcessSMB1Message(SMB1Message message, ref ConnectionState state)
{
SMB1Message reply = new SMB1Message();
PrepareResponseHeader(reply, message);
@ -23,7 +23,7 @@ namespace SMBLibrary.Server
foreach (SMB1Command command in message.Commands)
{
SMB1Command response = ProcessSMB1Command(reply.Header, command, state, sendQueue);
SMB1Command response = ProcessSMB1Command(reply.Header, command, ref state, sendQueue);
if (response != null)
{
reply.Commands.Add(response);
@ -51,7 +51,7 @@ namespace SMBLibrary.Server
/// <summary>
/// May return null
/// </summary>
public SMB1Command ProcessSMB1Command(SMB1Header header, SMB1Command command, SMB1ConnectionState state, List<SMB1Command> sendQueue)
public SMB1Command ProcessSMB1Command(SMB1Header header, SMB1Command command, ref ConnectionState state, List<SMB1Command> sendQueue)
{
if (state.ServerDialect == SMBDialect.NotSet)
{
@ -60,6 +60,7 @@ namespace SMBLibrary.Server
NegotiateRequest request = (NegotiateRequest)command;
if (request.Dialects.Contains(SMBServer.NTLanManagerDialect))
{
state = new SMB1ConnectionState(state);
state.ServerDialect = SMBDialect.NTLM012;
if (EnableExtendedSecurity && header.ExtendedSecurityFlag)
{
@ -89,7 +90,15 @@ namespace SMBLibrary.Server
header.Status = NTStatus.STATUS_SMB_BAD_COMMAND;
return new ErrorResponse(command.CommandName);
}
else if (command is SessionSetupAndXRequest)
else
{
return ProcessSMB1Command(header, command, (SMB1ConnectionState)state, sendQueue);
}
}
private SMB1Command ProcessSMB1Command(SMB1Header header, SMB1Command command, SMB1ConnectionState state, List<SMB1Command> sendQueue)
{
if (command is SessionSetupAndXRequest)
{
SessionSetupAndXRequest request = (SessionSetupAndXRequest)command;
state.MaxBufferSize = request.MaxBufferSize;
@ -329,12 +338,12 @@ namespace SMBLibrary.Server
return new ErrorResponse(command.CommandName);
}
public static void TrySendMessage(SMB1ConnectionState state, SMB1Message response)
public static void TrySendMessage(ConnectionState state, SMB1Message response)
{
SessionMessagePacket packet = new SessionMessagePacket();
packet.Trailer = response.GetBytes();
TrySendPacket(state, packet);
state.LogToServer(Severity.Verbose, "Response sent: {0} Commands, First Command: {1}, Packet length: {2}", response.Commands.Count, response.Commands[0].CommandName.ToString(), packet.Length);
state.LogToServer(Severity.Verbose, "SMB1 message sent: {0} responses, First response: {1}, Packet length: {2}", response.Commands.Count, response.Commands[0].CommandName.ToString(), packet.Length);
}
private static void PrepareResponseHeader(SMB1Message response, SMB1Message request)

View file

@ -0,0 +1,211 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using SMBLibrary.NetBios;
using SMBLibrary.Server.SMB2;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server
{
public partial class SMBServer
{
// Key is the persistent portion of the FileID
private Dictionary<ulong, OpenFileObject> m_globalOpenFiles = new Dictionary<ulong, OpenFileObject>();
private static ulong m_nextPersistentFileID = 1; // A numeric value that uniquely identifies the open handle to a file or a pipe within the scope of all opens granted by the server
private ulong? AllocatePersistentFileID()
{
for (ulong offset = 0; offset < UInt64.MaxValue; offset++)
{
ulong persistentID = (ulong)(m_nextPersistentFileID + offset);
if (persistentID == 0 || persistentID == 0xFFFFFFFFFFFFFFFF)
{
continue;
}
if (!m_globalOpenFiles.ContainsKey(persistentID))
{
m_nextPersistentFileID = (ulong)(persistentID + 1);
return persistentID;
}
}
return null;
}
/// <summary>
/// May return null
/// </summary>
public SMB2Command ProcessSMB2Command(SMB2Command command, ref ConnectionState state)
{
if (state.ServerDialect == SMBDialect.NotSet)
{
if (command is NegotiateRequest)
{
NegotiateRequest request = (NegotiateRequest)command;
state = new SMB2ConnectionState(state, AllocatePersistentFileID);
return NegotiateHelper.GetNegotiateResponse(request, state, m_serverGuid);
}
else
{
// [MS-SMB2] If the request being received is not an SMB2 NEGOTIATE Request [..]
// and Connection.NegotiateDialect is 0xFFFF or 0x02FF, the server MUST
// disconnect the connection.
state.LogToServer(Severity.Debug, "Invalid Connection State for command {0}", command.CommandName.ToString());
state.ClientSocket.Close();
return null;
}
}
else if (command is NegotiateRequest)
{
// [MS-SMB2] If Connection.NegotiateDialect is 0x0202, 0x0210, 0x0300, 0x0302, or 0x0311,
// the server MUST disconnect the connection.
state.LogToServer(Severity.Debug, "Rejecting NegotiateRequest. NegotiateDialect is already set");
state.ClientSocket.Close();
return null;
}
else
{
return ProcessSMB2Command(command, (SMB2ConnectionState)state);
}
}
public SMB2Command ProcessSMB2Command(SMB2Command command, SMB2ConnectionState state)
{
if (command is SessionSetupRequest)
{
return SessionSetupHelper.GetSessionSetupResponse((SessionSetupRequest)command, m_users, state);
}
else if (command is EchoRequest)
{
return new EchoResponse();
}
else
{
SMB2Session session = state.GetSession(command.Header.SessionID);
if (session == null)
{
return new ErrorResponse(command.CommandName, NTStatus.STATUS_USER_SESSION_DELETED);
}
if (command is TreeConnectRequest)
{
return TreeConnectHelper.GetTreeConnectResponse((TreeConnectRequest)command, state, m_services, m_shares);
}
else if (command is LogoffRequest)
{
state.RemoveSession(command.Header.SessionID);
return new LogoffResponse();
}
else
{
ISMBShare share = session.GetConnectedTree(command.Header.TreeID);
if (share == null)
{
return new ErrorResponse(command.CommandName, NTStatus.STATUS_NETWORK_NAME_DELETED);
}
if (command is TreeDisconnectRequest)
{
session.RemoveConnectedTree(command.Header.TreeID);
return new TreeDisconnectResponse();
}
else if (command is CreateRequest)
{
return CreateHelper.GetCreateResponse((CreateRequest)command, share, state);
}
else if (command is QueryInfoRequest)
{
return QueryInfoHelper.GetQueryInfoResponse((QueryInfoRequest)command, share, state);
}
else if (command is SetInfoRequest)
{
return SetInfoHelper.GetSetInfoResponse((SetInfoRequest)command, share, state);
}
else if (command is QueryDirectoryRequest)
{
return QueryDirectoryHelper.GetQueryDirectoryResponse((QueryDirectoryRequest)command, share, state);
}
else if (command is ReadRequest)
{
return ReadWriteResponseHelper.GetReadResponse((ReadRequest)command, share, state);
}
else if (command is WriteRequest)
{
return ReadWriteResponseHelper.GetWriteResponse((WriteRequest)command, share, state);
}
else if (command is FlushRequest)
{
FlushRequest request = (FlushRequest)command;
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
openFile.Stream.Flush();
return new FlushResponse();
}
else if (command is CloseRequest)
{
CloseRequest request = (CloseRequest)command;
OpenFileObject openFile = session.GetOpenFileObject(request.FileId.Persistent);
if (openFile == null)
{
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
}
session.RemoveOpenFile(request.FileId.Persistent);
return new CloseResponse();
}
else if (command is IOCtlRequest)
{
return IOCtlHelper.GetIOCtlResponse((IOCtlRequest)command, share, state);
}
else if (command is ChangeNotifyRequest)
{
// [MS-SMB2] If the underlying object store does not support change notifications, the server MUST fail this request with STATUS_NOT_SUPPORTED
return new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
}
}
return new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
}
public static void TrySendResponse(ConnectionState state, SMB2Command response)
{
SessionMessagePacket packet = new SessionMessagePacket();
packet.Trailer = response.GetBytes();
TrySendPacket(state, packet);
state.LogToServer(Severity.Verbose, "SMB2 response sent: {0}, Packet length: {1}", response.CommandName.ToString(), packet.Length);
}
public static void TrySendResponseChain(ConnectionState state, List<SMB2Command> responseChain)
{
SessionMessagePacket packet = new SessionMessagePacket();
packet.Trailer = SMB2Command.GetCommandChainBytes(responseChain);
TrySendPacket(state, packet);
state.LogToServer(Severity.Verbose, "SMB2 response chain sent: Response count: {0}, First response: {1}, Packet length: {2}", responseChain.Count, responseChain[0].CommandName.ToString(), packet.Length);
}
private static void UpdateSMB2Header(SMB2Command response, SMB2Command request)
{
response.Header.MessageID = request.Header.MessageID;
response.Header.CreditCharge = request.Header.CreditCharge;
response.Header.Credits = Math.Max((ushort)1, request.Header.Credits);
response.Header.IsRelatedOperations = request.Header.IsRelatedOperations;
response.Header.Reserved = request.Header.Reserved;
if (response.Header.SessionID == 0)
{
response.Header.SessionID = request.Header.SessionID;
}
if (response.Header.TreeID == 0)
{
response.Header.TreeID = request.Header.TreeID;
}
}
}
}

View file

@ -11,6 +11,7 @@ using System.Net.Sockets;
using SMBLibrary.NetBios;
using SMBLibrary.Services;
using SMBLibrary.SMB1;
using SMBLibrary.SMB2;
using Utilities;
namespace SMBLibrary.Server
@ -27,6 +28,8 @@ namespace SMBLibrary.Server
private NamedPipeShare m_services; // Named pipes
private IPAddress m_serverAddress;
private SMBTransportType m_transport;
private bool m_enableSMB1;
private bool m_enableSMB2;
private Socket m_listenerSocket;
private bool m_listening;
@ -34,13 +37,19 @@ namespace SMBLibrary.Server
public event EventHandler<LogEntry> OnLogEntry;
public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport)
public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport) : this(shares, users, serverAddress, transport, true, true)
{
}
public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport, bool enableSMB1, bool enableSMB2)
{
m_shares = shares;
m_users = users;
m_serverAddress = serverAddress;
m_serverGuid = Guid.NewGuid();
m_transport = transport;
m_enableSMB1 = enableSMB1;
m_enableSMB2 = enableSMB2;
m_services = new NamedPipeShare(shares.ListShares());
}
@ -93,7 +102,7 @@ namespace SMBLibrary.Server
return;
}
SMB1ConnectionState state = new SMB1ConnectionState(new ConnectionState(Log));
ConnectionState state = new ConnectionState(Log);
// Disable the Nagle Algorithm for this tcp socket:
clientSocket.NoDelay = true;
state.ClientSocket = clientSocket;
@ -116,7 +125,7 @@ namespace SMBLibrary.Server
private void ReceiveCallback(IAsyncResult result)
{
SMB1ConnectionState state = (SMB1ConnectionState)result.AsyncState;
ConnectionState state = (ConnectionState)result.AsyncState;
Socket clientSocket = state.ClientSocket;
if (!m_listening)
@ -149,7 +158,7 @@ namespace SMBLibrary.Server
NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer;
receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(state);
ProcessConnectionBuffer(ref state);
if (clientSocket.Connected)
{
@ -166,7 +175,7 @@ namespace SMBLibrary.Server
}
}
public void ProcessConnectionBuffer(SMB1ConnectionState state)
public void ProcessConnectionBuffer(ref ConnectionState state)
{
Socket clientSocket = state.ClientSocket;
@ -185,12 +194,12 @@ namespace SMBLibrary.Server
if (packet != null)
{
ProcessPacket(packet, state);
ProcessPacket(packet, ref state);
}
}
}
public void ProcessPacket(SessionPacket packet, SMB1ConnectionState state)
public void ProcessPacket(SessionPacket packet, ref ConnectionState state)
{
if (packet is SessionRequestPacket && m_transport == SMBTransportType.NetBiosOverTCP)
{
@ -203,19 +212,97 @@ namespace SMBLibrary.Server
}
else if (packet is SessionMessagePacket)
{
SMB1Message message = null;
try
// Note: To be compatible with SMB2 specifications, we must accept SMB_COM_NEGOTIATE.
// We will disconnect the connection if m_enableSMB1 == false and the client does not support SMB2.
bool acceptSMB1 = (state.ServerDialect == SMBDialect.NotSet || state.ServerDialect == SMBDialect.NTLM012);
bool acceptSMB2 = (m_enableSMB2 && (state.ServerDialect == SMBDialect.NotSet || state.ServerDialect == SMBDialect.SMB202 || state.ServerDialect == SMBDialect.SMB210));
if (SMB1Header.IsValidSMB1Header(packet.Trailer))
{
message = SMB1Message.GetSMB1Message(packet.Trailer);
if (!acceptSMB1)
{
state.LogToServer(Severity.Verbose, "Rejected SMB1 message");
state.ClientSocket.Close();
return;
}
SMB1Message message = null;
try
{
message = SMB1Message.GetSMB1Message(packet.Trailer);
}
catch (Exception ex)
{
state.LogToServer(Severity.Warning, "Invalid SMB1 message: " + ex.Message);
state.ClientSocket.Close();
return;
}
state.LogToServer(Severity.Verbose, "SMB1 message received: {0} requests, First request: {1}, Packet length: {2}", message.Commands.Count, message.Commands[0].CommandName.ToString(), packet.Length);
if (state.ServerDialect == SMBDialect.NotSet && m_enableSMB2)
{
// Check if the client supports SMB 2
List<string> smb2Dialects = SMB2.NegotiateHelper.FindSMB2Dialects(message);
if (smb2Dialects.Count > 0)
{
SMB2Command response = SMB2.NegotiateHelper.GetNegotiateResponse(smb2Dialects, state, m_serverGuid);
TrySendResponse(state, response);
return;
}
}
if (m_enableSMB1)
{
ProcessSMB1Message(message, ref state);
}
else
{
// [MS-SMB2] 3.3.5.3.2 If the string is not present in the dialect list and the server does not implement SMB,
// the server MUST disconnect the connection [..] without sending a response.
state.LogToServer(Severity.Verbose, "Rejected SMB1 message");
state.ClientSocket.Close();
}
}
catch (Exception ex)
else if (SMB2Header.IsValidSMB2Header(packet.Trailer))
{
state.LogToServer(Severity.Warning, "Invalid SMB1 message: " + ex.Message);
if (!acceptSMB2)
{
state.LogToServer(Severity.Verbose, "Rejected SMB2 message");
state.ClientSocket.Close();
return;
}
List<SMB2Command> requestChain;
try
{
requestChain = SMB2Command.ReadRequestChain(packet.Trailer, 0);
}
catch (Exception ex)
{
state.LogToServer(Severity.Warning, "Invalid SMB2 request chain: " + ex.Message);
state.ClientSocket.Close();
return;
}
state.LogToServer(Severity.Verbose, "SMB2 request chain received: {0} requests, First request: {1}, Packet length: {2}", requestChain.Count, requestChain[0].CommandName.ToString(), packet.Length);
List<SMB2Command> responseChain = new List<SMB2Command>();
foreach (SMB2Command request in requestChain)
{
SMB2Command response = ProcessSMB2Command(request, ref state);
if (response != null)
{
UpdateSMB2Header(response, request);
responseChain.Add(response);
}
}
if (responseChain.Count > 0)
{
TrySendResponseChain(state, responseChain);
}
}
else
{
state.LogToServer(Severity.Warning, "Invalid SMB message");
state.ClientSocket.Close();
return;
}
state.LogToServer(Severity.Verbose, "Message Received: {0} Commands, First Command: {1}, Packet length: {2}", message.Commands.Count, message.Commands[0].CommandName.ToString(), packet.Length);
ProcessSMB1Message(message, state);
}
else
{
@ -225,7 +312,7 @@ namespace SMBLibrary.Server
}
}
public static void TrySendPacket(SMB1ConnectionState state, SessionPacket response)
public static void TrySendPacket(ConnectionState state, SessionPacket response)
{
Socket clientSocket = state.ClientSocket;
try