mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-07-04 16:53:18 +02:00
Added SMB 2.0 / 2.1 server implementation
This commit is contained in:
parent
966096df5f
commit
55b83add4e
21 changed files with 1958 additions and 22 deletions
|
@ -4,7 +4,10 @@ namespace SMBLibrary
|
||||||
public enum NTStatus : uint
|
public enum NTStatus : uint
|
||||||
{
|
{
|
||||||
STATUS_SUCCESS = 0x00000000,
|
STATUS_SUCCESS = 0x00000000,
|
||||||
|
STATUS_OBJECT_NAME_EXISTS = 0x40000000,
|
||||||
|
STATUS_NO_MORE_FILES = 0x80000006,
|
||||||
STATUS_NOT_IMPLEMENTED = 0xC0000002,
|
STATUS_NOT_IMPLEMENTED = 0xC0000002,
|
||||||
|
STATUS_INVALID_INFO_CLASS = 0xC0000003,
|
||||||
STATUS_INVALID_HANDLE = 0xC0000008,
|
STATUS_INVALID_HANDLE = 0xC0000008,
|
||||||
STATUS_INVALID_PARAMETER = 0xC000000D,
|
STATUS_INVALID_PARAMETER = 0xC000000D,
|
||||||
STATUS_NO_SUCH_DEVICE = 0xC000000E,
|
STATUS_NO_SUCH_DEVICE = 0xC000000E,
|
||||||
|
@ -23,12 +26,16 @@ namespace SMBLibrary
|
||||||
STATUS_LOGON_FAILURE = 0xC000006D, // Authentication failure.
|
STATUS_LOGON_FAILURE = 0xC000006D, // Authentication failure.
|
||||||
STATUS_ACCOUNT_RESTRICTION = 0xC000006E, // The user has an empty password, which is not allowed
|
STATUS_ACCOUNT_RESTRICTION = 0xC000006E, // The user has an empty password, which is not allowed
|
||||||
STATUS_DISK_FULL = 0xC000007F,
|
STATUS_DISK_FULL = 0xC000007F,
|
||||||
|
STATUS_INSUFFICIENT_RESOURCES = 0xC000009A,
|
||||||
STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2,
|
STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2,
|
||||||
STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA,
|
STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA,
|
||||||
STATUS_NOT_SUPPORTED = 0xC00000BB,
|
STATUS_NOT_SUPPORTED = 0xC00000BB,
|
||||||
|
STATUS_NETWORK_NAME_DELETED = 0xC00000C9,
|
||||||
STATUS_TOO_MANY_SESSIONS = 0xC00000CE,
|
STATUS_TOO_MANY_SESSIONS = 0xC00000CE,
|
||||||
STATUS_TOO_MANY_OPENED_FILES = 0xC000011F,
|
STATUS_TOO_MANY_OPENED_FILES = 0xC000011F,
|
||||||
STATUS_CANNOT_DELETE = 0xC0000121,
|
STATUS_CANNOT_DELETE = 0xC0000121,
|
||||||
|
STATUS_FILE_CLOSED = 0xC0000128,
|
||||||
|
STATUS_FS_DRIVER_REQUIRED = 0xC000019C,
|
||||||
STATUS_USER_SESSION_DELETED = 0xC0000203,
|
STATUS_USER_SESSION_DELETED = 0xC0000203,
|
||||||
STATUS_INSUFF_SERVER_RESOURCES = 0xC0000205,
|
STATUS_INSUFF_SERVER_RESOURCES = 0xC0000205,
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace SMBLibrary
|
||||||
ERROR_ACCESS_DENIED = 0x0005,
|
ERROR_ACCESS_DENIED = 0x0005,
|
||||||
ERROR_SHARING_VIOLATION = 0x0020,
|
ERROR_SHARING_VIOLATION = 0x0020,
|
||||||
ERROR_DISK_FULL = 0x0070,
|
ERROR_DISK_FULL = 0x0070,
|
||||||
|
ERROR_ALREADY_EXISTS = 0x00B7,
|
||||||
ERROR_LOGON_FAILURE = 0x052E,
|
ERROR_LOGON_FAILURE = 0x052E,
|
||||||
ERROR_ACCOUNT_RESTRICTION = 0x052F,
|
ERROR_ACCOUNT_RESTRICTION = 0x052F,
|
||||||
ERROR_ACCOUNT_DISABLED = 0x0533,
|
ERROR_ACCOUNT_DISABLED = 0x0533,
|
||||||
|
|
|
@ -118,12 +118,17 @@
|
||||||
<Compile Include="Server\ConnectionState\ProcessStateObject.cs" />
|
<Compile Include="Server\ConnectionState\ProcessStateObject.cs" />
|
||||||
<Compile Include="Server\ConnectionState\SMB1ConnectionState.cs" />
|
<Compile Include="Server\ConnectionState\SMB1ConnectionState.cs" />
|
||||||
<Compile Include="Server\ConnectionState\SMB1Session.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\EmptyPasswordNotAllowedException.cs" />
|
||||||
<Compile Include="Server\Exceptions\InvalidRequestException.cs" />
|
<Compile Include="Server\Exceptions\InvalidRequestException.cs" />
|
||||||
<Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
|
<Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
|
||||||
<Compile Include="Server\Helpers\IOExceptionHelper.cs" />
|
<Compile Include="Server\Helpers\IOExceptionHelper.cs" />
|
||||||
<Compile Include="Server\Helpers\NTFileSystemHelper.cs" />
|
<Compile Include="Server\Helpers\NTFileSystemHelper.cs" />
|
||||||
<Compile Include="Server\Helpers\NTFileSystemHelper.Find.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\Helpers\ServerPathUtils.cs" />
|
||||||
<Compile Include="Server\IndependentUserCollection.cs" />
|
<Compile Include="Server\IndependentUserCollection.cs" />
|
||||||
<Compile Include="Server\INTLMAuthenticationProvider.cs" />
|
<Compile Include="Server\INTLMAuthenticationProvider.cs" />
|
||||||
|
@ -148,8 +153,18 @@
|
||||||
<Compile Include="Server\SMB1\TransactionHelper.cs" />
|
<Compile Include="Server\SMB1\TransactionHelper.cs" />
|
||||||
<Compile Include="Server\SMB1\TransactionSubcommandHelper.cs" />
|
<Compile Include="Server\SMB1\TransactionSubcommandHelper.cs" />
|
||||||
<Compile Include="Server\SMB1\TreeConnectHelper.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.cs" />
|
||||||
<Compile Include="Server\SMBServer.SMB1.cs" />
|
<Compile Include="Server\SMBServer.SMB1.cs" />
|
||||||
|
<Compile Include="Server\SMBServer.SMB2.cs" />
|
||||||
<Compile Include="Server\User.cs" />
|
<Compile Include="Server\User.cs" />
|
||||||
<Compile Include="Server\UserCollection.cs" />
|
<Compile Include="Server\UserCollection.cs" />
|
||||||
<Compile Include="Services\Enums\PlatformName.cs" />
|
<Compile Include="Services\Enums\PlatformName.cs" />
|
||||||
|
|
|
@ -19,6 +19,8 @@ namespace SMBLibrary.Server
|
||||||
{
|
{
|
||||||
NotSet,
|
NotSet,
|
||||||
NTLM012, // NT LM 0.12
|
NTLM012, // NT LM 0.12
|
||||||
|
SMB202, // SMB 2.0.2
|
||||||
|
SMB210, // SMB 2.1
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConnectionState
|
public class ConnectionState
|
||||||
|
|
71
SMBLibrary/Server/ConnectionState/SMB2ConnectionState.cs
Normal file
71
SMBLibrary/Server/ConnectionState/SMB2ConnectionState.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
SMBLibrary/Server/ConnectionState/SMB2Session.cs
Normal file
165
SMBLibrary/Server/ConnectionState/SMB2Session.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
191
SMBLibrary/Server/Helpers/NTFileSystemHelper.Query.cs
Normal file
191
SMBLibrary/Server/Helpers/NTFileSystemHelper.Query.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
SMBLibrary/Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs
Normal file
101
SMBLibrary/Server/Helpers/NTFileSystemHelper.QueryFileSystem.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
207
SMBLibrary/Server/Helpers/NTFileSystemHelper.Set.cs
Normal file
207
SMBLibrary/Server/Helpers/NTFileSystemHelper.Set.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
SMBLibrary/Server/SMB2/CreateHelper.cs
Normal file
149
SMBLibrary/Server/SMB2/CreateHelper.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
SMBLibrary/Server/SMB2/IOCtlHelper.cs
Normal file
51
SMBLibrary/Server/SMB2/IOCtlHelper.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
SMBLibrary/Server/SMB2/NegotiateHelper.cs
Normal file
101
SMBLibrary/Server/SMB2/NegotiateHelper.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
208
SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs
Normal file
208
SMBLibrary/Server/SMB2/QueryDirectoryHelper.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
SMBLibrary/Server/SMB2/QueryInfoHelper.cs
Normal file
75
SMBLibrary/Server/SMB2/QueryInfoHelper.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs
Normal file
57
SMBLibrary/Server/SMB2/ReadWriteResponseHelper.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
SMBLibrary/Server/SMB2/SessionSetupHelper.cs
Normal file
101
SMBLibrary/Server/SMB2/SessionSetupHelper.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
SMBLibrary/Server/SMB2/SetInfoHelper.cs
Normal file
65
SMBLibrary/Server/SMB2/SetInfoHelper.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
SMBLibrary/Server/SMB2/TreeConnectHelper.cs
Normal file
62
SMBLibrary/Server/SMB2/TreeConnectHelper.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ namespace SMBLibrary.Server
|
||||||
{
|
{
|
||||||
public partial class SMBServer
|
public partial class SMBServer
|
||||||
{
|
{
|
||||||
public void ProcessSMB1Message(SMB1Message message, SMB1ConnectionState state)
|
public void ProcessSMB1Message(SMB1Message message, ref ConnectionState state)
|
||||||
{
|
{
|
||||||
SMB1Message reply = new SMB1Message();
|
SMB1Message reply = new SMB1Message();
|
||||||
PrepareResponseHeader(reply, message);
|
PrepareResponseHeader(reply, message);
|
||||||
|
@ -23,7 +23,7 @@ namespace SMBLibrary.Server
|
||||||
|
|
||||||
foreach (SMB1Command command in message.Commands)
|
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)
|
if (response != null)
|
||||||
{
|
{
|
||||||
reply.Commands.Add(response);
|
reply.Commands.Add(response);
|
||||||
|
@ -51,7 +51,7 @@ namespace SMBLibrary.Server
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// May return null
|
/// May return null
|
||||||
/// </summary>
|
/// </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)
|
if (state.ServerDialect == SMBDialect.NotSet)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +60,7 @@ namespace SMBLibrary.Server
|
||||||
NegotiateRequest request = (NegotiateRequest)command;
|
NegotiateRequest request = (NegotiateRequest)command;
|
||||||
if (request.Dialects.Contains(SMBServer.NTLanManagerDialect))
|
if (request.Dialects.Contains(SMBServer.NTLanManagerDialect))
|
||||||
{
|
{
|
||||||
|
state = new SMB1ConnectionState(state);
|
||||||
state.ServerDialect = SMBDialect.NTLM012;
|
state.ServerDialect = SMBDialect.NTLM012;
|
||||||
if (EnableExtendedSecurity && header.ExtendedSecurityFlag)
|
if (EnableExtendedSecurity && header.ExtendedSecurityFlag)
|
||||||
{
|
{
|
||||||
|
@ -89,7 +90,15 @@ namespace SMBLibrary.Server
|
||||||
header.Status = NTStatus.STATUS_SMB_BAD_COMMAND;
|
header.Status = NTStatus.STATUS_SMB_BAD_COMMAND;
|
||||||
return new ErrorResponse(command.CommandName);
|
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;
|
SessionSetupAndXRequest request = (SessionSetupAndXRequest)command;
|
||||||
state.MaxBufferSize = request.MaxBufferSize;
|
state.MaxBufferSize = request.MaxBufferSize;
|
||||||
|
@ -329,12 +338,12 @@ namespace SMBLibrary.Server
|
||||||
return new ErrorResponse(command.CommandName);
|
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();
|
SessionMessagePacket packet = new SessionMessagePacket();
|
||||||
packet.Trailer = response.GetBytes();
|
packet.Trailer = response.GetBytes();
|
||||||
TrySendPacket(state, packet);
|
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)
|
private static void PrepareResponseHeader(SMB1Message response, SMB1Message request)
|
||||||
|
|
211
SMBLibrary/Server/SMBServer.SMB2.cs
Normal file
211
SMBLibrary/Server/SMBServer.SMB2.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ using System.Net.Sockets;
|
||||||
using SMBLibrary.NetBios;
|
using SMBLibrary.NetBios;
|
||||||
using SMBLibrary.Services;
|
using SMBLibrary.Services;
|
||||||
using SMBLibrary.SMB1;
|
using SMBLibrary.SMB1;
|
||||||
|
using SMBLibrary.SMB2;
|
||||||
using Utilities;
|
using Utilities;
|
||||||
|
|
||||||
namespace SMBLibrary.Server
|
namespace SMBLibrary.Server
|
||||||
|
@ -27,6 +28,8 @@ namespace SMBLibrary.Server
|
||||||
private NamedPipeShare m_services; // Named pipes
|
private NamedPipeShare m_services; // Named pipes
|
||||||
private IPAddress m_serverAddress;
|
private IPAddress m_serverAddress;
|
||||||
private SMBTransportType m_transport;
|
private SMBTransportType m_transport;
|
||||||
|
private bool m_enableSMB1;
|
||||||
|
private bool m_enableSMB2;
|
||||||
|
|
||||||
private Socket m_listenerSocket;
|
private Socket m_listenerSocket;
|
||||||
private bool m_listening;
|
private bool m_listening;
|
||||||
|
@ -34,13 +37,19 @@ namespace SMBLibrary.Server
|
||||||
|
|
||||||
public event EventHandler<LogEntry> OnLogEntry;
|
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_shares = shares;
|
||||||
m_users = users;
|
m_users = users;
|
||||||
m_serverAddress = serverAddress;
|
m_serverAddress = serverAddress;
|
||||||
m_serverGuid = Guid.NewGuid();
|
m_serverGuid = Guid.NewGuid();
|
||||||
m_transport = transport;
|
m_transport = transport;
|
||||||
|
m_enableSMB1 = enableSMB1;
|
||||||
|
m_enableSMB2 = enableSMB2;
|
||||||
|
|
||||||
m_services = new NamedPipeShare(shares.ListShares());
|
m_services = new NamedPipeShare(shares.ListShares());
|
||||||
}
|
}
|
||||||
|
@ -93,7 +102,7 @@ namespace SMBLibrary.Server
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SMB1ConnectionState state = new SMB1ConnectionState(new ConnectionState(Log));
|
ConnectionState state = new ConnectionState(Log);
|
||||||
// Disable the Nagle Algorithm for this tcp socket:
|
// Disable the Nagle Algorithm for this tcp socket:
|
||||||
clientSocket.NoDelay = true;
|
clientSocket.NoDelay = true;
|
||||||
state.ClientSocket = clientSocket;
|
state.ClientSocket = clientSocket;
|
||||||
|
@ -116,7 +125,7 @@ namespace SMBLibrary.Server
|
||||||
|
|
||||||
private void ReceiveCallback(IAsyncResult result)
|
private void ReceiveCallback(IAsyncResult result)
|
||||||
{
|
{
|
||||||
SMB1ConnectionState state = (SMB1ConnectionState)result.AsyncState;
|
ConnectionState state = (ConnectionState)result.AsyncState;
|
||||||
Socket clientSocket = state.ClientSocket;
|
Socket clientSocket = state.ClientSocket;
|
||||||
|
|
||||||
if (!m_listening)
|
if (!m_listening)
|
||||||
|
@ -149,7 +158,7 @@ namespace SMBLibrary.Server
|
||||||
|
|
||||||
NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer;
|
NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer;
|
||||||
receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived);
|
receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived);
|
||||||
ProcessConnectionBuffer(state);
|
ProcessConnectionBuffer(ref state);
|
||||||
|
|
||||||
if (clientSocket.Connected)
|
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;
|
Socket clientSocket = state.ClientSocket;
|
||||||
|
|
||||||
|
@ -185,12 +194,12 @@ namespace SMBLibrary.Server
|
||||||
|
|
||||||
if (packet != null)
|
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)
|
if (packet is SessionRequestPacket && m_transport == SMBTransportType.NetBiosOverTCP)
|
||||||
{
|
{
|
||||||
|
@ -203,6 +212,20 @@ namespace SMBLibrary.Server
|
||||||
}
|
}
|
||||||
else if (packet is SessionMessagePacket)
|
else if (packet is SessionMessagePacket)
|
||||||
{
|
{
|
||||||
|
// 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))
|
||||||
|
{
|
||||||
|
if (!acceptSMB1)
|
||||||
|
{
|
||||||
|
state.LogToServer(Severity.Verbose, "Rejected SMB1 message");
|
||||||
|
state.ClientSocket.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SMB1Message message = null;
|
SMB1Message message = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -214,8 +237,72 @@ namespace SMBLibrary.Server
|
||||||
state.ClientSocket.Close();
|
state.ClientSocket.Close();
|
||||||
return;
|
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);
|
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);
|
||||||
ProcessSMB1Message(message, state);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (SMB2Header.IsValidSMB2Header(packet.Trailer))
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
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;
|
Socket clientSocket = state.ClientSocket;
|
||||||
try
|
try
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue