Client: Added SMB 3.0 support

This commit is contained in:
TalAloni 2020-11-28 20:02:21 +02:00
parent d0b721d621
commit 1636079cc2
2 changed files with 53 additions and 10 deletions

View file

@ -44,6 +44,10 @@ namespace SMBLibrary.Client
private uint m_messageID = 0; private uint m_messageID = 0;
private SMB2Dialect m_dialect; private SMB2Dialect m_dialect;
private bool m_signingRequired; private bool m_signingRequired;
private byte[] m_signingKey;
private bool m_encryptSessionData;
private byte[] m_encryptionKey;
private byte[] m_decryptionKey;
private uint m_maxTransactSize; private uint m_maxTransactSize;
private uint m_maxReadSize; private uint m_maxReadSize;
private uint m_maxWriteSize; private uint m_maxWriteSize;
@ -155,10 +159,12 @@ namespace SMBLibrary.Client
{ {
NegotiateRequest request = new NegotiateRequest(); NegotiateRequest request = new NegotiateRequest();
request.SecurityMode = SecurityMode.SigningEnabled; request.SecurityMode = SecurityMode.SigningEnabled;
request.Capabilities = Capabilities.Encryption;
request.ClientGuid = Guid.NewGuid(); request.ClientGuid = Guid.NewGuid();
request.ClientStartTime = DateTime.Now; request.ClientStartTime = DateTime.Now;
request.Dialects.Add(SMB2Dialect.SMB202); request.Dialects.Add(SMB2Dialect.SMB202);
request.Dialects.Add(SMB2Dialect.SMB210); request.Dialects.Add(SMB2Dialect.SMB210);
request.Dialects.Add(SMB2Dialect.SMB300);
TrySendCommand(request); TrySendCommand(request);
NegotiateResponse response = WaitForCommand(SMB2CommandName.Negotiate) as NegotiateResponse; NegotiateResponse response = WaitForCommand(SMB2CommandName.Negotiate) as NegotiateResponse;
@ -217,6 +223,13 @@ namespace SMBLibrary.Client
if (response != null) if (response != null)
{ {
m_isLoggedIn = (response.Header.Status == NTStatus.STATUS_SUCCESS); m_isLoggedIn = (response.Header.Status == NTStatus.STATUS_SUCCESS);
m_signingKey = SMB2Cryptography.GenerateSigningKey(m_sessionKey, m_dialect, null);
if (m_dialect == SMB2Dialect.SMB300)
{
m_encryptSessionData = (((SessionSetupResponse)response).SessionFlags & SessionFlags.EncryptData) > 0;
m_encryptionKey = SMB2Cryptography.GenerateClientEncryptionKey(m_sessionKey, SMB2Dialect.SMB300, null);
m_decryptionKey = SMB2Cryptography.GenerateClientDecryptionKey(m_sessionKey, SMB2Dialect.SMB300, null);
}
return response.Header.Status; return response.Header.Status;
} }
} }
@ -283,7 +296,8 @@ namespace SMBLibrary.Client
status = response.Header.Status; status = response.Header.Status;
if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is TreeConnectResponse) if (response.Header.Status == NTStatus.STATUS_SUCCESS && response is TreeConnectResponse)
{ {
return new SMB2FileStore(this, response.Header.TreeID); bool encryptShareData = (((TreeConnectResponse)response).ShareFlags & ShareFlags.EncryptData) > 0;
return new SMB2FileStore(this, response.Header.TreeID, m_encryptSessionData || encryptShareData);
} }
} }
else else
@ -377,10 +391,22 @@ namespace SMBLibrary.Client
{ {
if (packet is SessionMessagePacket) if (packet is SessionMessagePacket)
{ {
byte[] messageBytes;
if (m_dialect == SMB2Dialect.SMB300 && SMB2TransformHeader.IsTransformHeader(packet.Trailer, 0))
{
SMB2TransformHeader transformHeader = new SMB2TransformHeader(packet.Trailer, 0);
byte[] encryptedMessage = ByteReader.ReadBytes(packet.Trailer, SMB2TransformHeader.Length, (int)transformHeader.OriginalMessageSize);
messageBytes = SMB2Cryptography.DecryptMessage(m_decryptionKey, transformHeader, encryptedMessage);
}
else
{
messageBytes = packet.Trailer;
}
SMB2Command command; SMB2Command command;
try try
{ {
command = SMB2Command.ReadResponse(packet.Trailer, 0); command = SMB2Command.ReadResponse(messageBytes, 0);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -487,6 +513,11 @@ namespace SMBLibrary.Client
} }
internal void TrySendCommand(SMB2Command request) internal void TrySendCommand(SMB2Command request)
{
TrySendCommand(request, m_encryptSessionData);
}
internal void TrySendCommand(SMB2Command request, bool encryptData)
{ {
if (m_dialect == SMB2Dialect.SMB202 || m_transport == SMBTransportType.NetBiosOverTCP) if (m_dialect == SMB2Dialect.SMB202 || m_transport == SMBTransportType.NetBiosOverTCP)
{ {
@ -516,19 +547,21 @@ namespace SMBLibrary.Client
request.Header.MessageID = m_messageID; request.Header.MessageID = m_messageID;
request.Header.SessionID = m_sessionID; request.Header.SessionID = m_sessionID;
if (m_signingRequired) // [MS-SMB2] If the client encrypts the message [..] then the client MUST set the Signature field of the SMB2 header to zero
if (m_signingRequired && !encryptData)
{ {
request.Header.IsSigned = (m_sessionID != 0 && (request.CommandName == SMB2CommandName.TreeConnect || request.Header.TreeID != 0)); request.Header.IsSigned = (m_sessionID != 0 && ((request.CommandName == SMB2CommandName.TreeConnect || request.Header.TreeID != 0) ||
(m_dialect == SMB2Dialect.SMB300 && request.CommandName == SMB2CommandName.Logoff)));
if (request.Header.IsSigned) if (request.Header.IsSigned)
{ {
request.Header.Signature = new byte[16]; // Request could be reused request.Header.Signature = new byte[16]; // Request could be reused
byte[] buffer = request.GetBytes(); byte[] buffer = request.GetBytes();
byte[] signature = SMB2Cryptography.CalculateSignature(m_sessionKey, m_dialect, buffer, 0, buffer.Length); byte[] signature = SMB2Cryptography.CalculateSignature(m_signingKey, m_dialect, buffer, 0, buffer.Length);
// [MS-SMB2] The first 16 bytes of the hash MUST be copied into the 16-byte signature field of the SMB2 Header. // [MS-SMB2] The first 16 bytes of the hash MUST be copied into the 16-byte signature field of the SMB2 Header.
request.Header.Signature = ByteReader.ReadBytes(signature, 0, 16); request.Header.Signature = ByteReader.ReadBytes(signature, 0, 16);
} }
} }
TrySendCommand(m_clientSocket, request); TrySendCommand(m_clientSocket, request, encryptData ? m_encryptionKey : null);
if (m_dialect == SMB2Dialect.SMB202 || m_transport == SMBTransportType.NetBiosOverTCP) if (m_dialect == SMB2Dialect.SMB202 || m_transport == SMBTransportType.NetBiosOverTCP)
{ {
m_messageID++; m_messageID++;
@ -563,10 +596,18 @@ namespace SMBLibrary.Client
} }
} }
public static void TrySendCommand(Socket socket, SMB2Command request) public static void TrySendCommand(Socket socket, SMB2Command request, byte[] encryptionKey)
{ {
SessionMessagePacket packet = new SessionMessagePacket(); SessionMessagePacket packet = new SessionMessagePacket();
packet.Trailer = request.GetBytes(); if (encryptionKey != null)
{
byte[] requestBytes = request.GetBytes();
packet.Trailer = SMB2Cryptography.TransformMessage(encryptionKey, requestBytes, request.Header.SessionID);
}
else
{
packet.Trailer = request.GetBytes();
}
TrySendPacket(socket, packet); TrySendPacket(socket, packet);
} }

View file

@ -17,11 +17,13 @@ namespace SMBLibrary.Client
private SMB2Client m_client; private SMB2Client m_client;
private uint m_treeID; private uint m_treeID;
private bool m_encryptShareData;
public SMB2FileStore(SMB2Client client, uint treeID) public SMB2FileStore(SMB2Client client, uint treeID, bool encryptShareData)
{ {
m_client = client; m_client = client;
m_treeID = treeID; m_treeID = treeID;
m_encryptShareData = encryptShareData;
} }
public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, SecurityContext securityContext) public NTStatus CreateFile(out object handle, out FileStatus fileStatus, string path, AccessMask desiredAccess, FileAttributes fileAttributes, ShareAccess shareAccess, CreateDisposition createDisposition, CreateOptions createOptions, SecurityContext securityContext)
@ -320,7 +322,7 @@ namespace SMBLibrary.Client
private void TrySendCommand(SMB2Command request) private void TrySendCommand(SMB2Command request)
{ {
request.Header.TreeID = m_treeID; request.Header.TreeID = m_treeID;
m_client.TrySendCommand(request); m_client.TrySendCommand(request, m_encryptShareData);
} }
public uint MaxReadSize public uint MaxReadSize