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 SMB2Dialect m_dialect;
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_maxReadSize;
private uint m_maxWriteSize;
@ -155,10 +159,12 @@ namespace SMBLibrary.Client
{
NegotiateRequest request = new NegotiateRequest();
request.SecurityMode = SecurityMode.SigningEnabled;
request.Capabilities = Capabilities.Encryption;
request.ClientGuid = Guid.NewGuid();
request.ClientStartTime = DateTime.Now;
request.Dialects.Add(SMB2Dialect.SMB202);
request.Dialects.Add(SMB2Dialect.SMB210);
request.Dialects.Add(SMB2Dialect.SMB300);
TrySendCommand(request);
NegotiateResponse response = WaitForCommand(SMB2CommandName.Negotiate) as NegotiateResponse;
@ -217,6 +223,13 @@ namespace SMBLibrary.Client
if (response != null)
{
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;
}
}
@ -283,7 +296,8 @@ namespace SMBLibrary.Client
status = response.Header.Status;
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
@ -377,10 +391,22 @@ namespace SMBLibrary.Client
{
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;
try
{
command = SMB2Command.ReadResponse(packet.Trailer, 0);
command = SMB2Command.ReadResponse(messageBytes, 0);
}
catch (Exception ex)
{
@ -487,6 +513,11 @@ namespace SMBLibrary.Client
}
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)
{
@ -516,19 +547,21 @@ namespace SMBLibrary.Client
request.Header.MessageID = m_messageID;
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)
{
request.Header.Signature = new byte[16]; // Request could be reused
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.
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)
{
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();
if (encryptionKey != null)
{
byte[] requestBytes = request.GetBytes();
packet.Trailer = SMB2Cryptography.TransformMessage(encryptionKey, requestBytes, request.Header.SessionID);
}
else
{
packet.Trailer = request.GetBytes();
}
TrySendPacket(socket, packet);
}

View file

@ -17,11 +17,13 @@ namespace SMBLibrary.Client
private SMB2Client m_client;
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_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)
@ -320,7 +322,7 @@ namespace SMBLibrary.Client
private void TrySendCommand(SMB2Command request)
{
request.Header.TreeID = m_treeID;
m_client.TrySendCommand(request);
m_client.TrySendCommand(request, m_encryptShareData);
}
public uint MaxReadSize