diff --git a/SMBLibrary/Client/SMB2Client.cs b/SMBLibrary/Client/SMB2Client.cs index bb1d436..aee5de1 100644 --- a/SMBLibrary/Client/SMB2Client.cs +++ b/SMBLibrary/Client/SMB2Client.cs @@ -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(); - 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); } diff --git a/SMBLibrary/Client/SMB2FileStore.cs b/SMBLibrary/Client/SMB2FileStore.cs index 1e0cf91..551958c 100644 --- a/SMBLibrary/Client/SMB2FileStore.cs +++ b/SMBLibrary/Client/SMB2FileStore.cs @@ -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