Beispiel #1
0
        /// <summary>
        /// Check if the packet needs to be encrypted but actually not encrypted.
        /// </summary>
        private void CheckIfNeedEncrypt(Smb2SinglePacket packet, ulong sessionId = 0)
        {
            if (!CheckEncrypt)
            {
                return;
            }
            var realSessionId = sessionId == 0 ? packet.Header.SessionId : sessionId;
            var cryptoInfo    = cryptoInfoTable.ContainsKey(realSessionId) ? cryptoInfoTable[realSessionId] : null;

            if (cryptoInfo != null)
            {
                if (cryptoInfo.EnableSessionEncryption || (cryptoInfo.EnableTreeEncryption.Contains(packet.Header.TreeId) && packet.Header.Command != Smb2Command.TREE_CONNECT))
                {
                    throw new Exception($"The packet should be encrypted: \"{packet.ToString()}\"");
                }
            }
        }
Beispiel #2
0
        private void TryVerifySignature(Smb2SinglePacket singlePacket, ulong sessionId, byte[] messageBytes)
        {
            // [MS-SMB2] 3.2.5.1.3
            //If the MessageId is 0xFFFFFFFFFFFFFFFF, no verification is necessary.
            if (singlePacket.Header.MessageId == 0xFFFFFFFFFFFFFFFF)
            {
                return;
            }

            Smb2CryptoInfo cryptoInfo = null;

            //[MS-SMB2] 3.2.5.1.3
            //If the SMB2 header of the response has SMB2_FLAGS_SIGNED set in the Flags field, the client MUST verify the signature
            if (singlePacket.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_SIGNED))
            {
                //This is for the session binding success situation.
                //After the session setup success, the cryptoInfoTable will be updated with new signingkey
                //The new signingkey is not in the cryptoInfoTable now. Cannot verify the signature before the table is updated.

                if (cryptoInfoTable.TryGetValue(sessionId, out cryptoInfo))
                {
                    if (!VerifySignature(singlePacket, cryptoInfo, messageBytes))
                    {
                        //If signature verification fails, the client MUST discard the received message and do no further processing for it.
                        //The client MAY also choose to disconnect the connection.
                        //Throw exception here.
                        throw new InvalidOperationException("Incorrect signed packet: " + singlePacket.ToString());
                    }
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Verify the signature of a Smb2SinglePacket
        /// </summary>
        /// <param name="packet">The packet to be verified</param>
        /// <param name="cryptoInfo">The cryptoInfo of smb2client</param>
        /// <returns>True when signature verification succeeds and false when fails</returns>
        private bool VerifySignature(Smb2SinglePacket packet, Smb2CryptoInfo cryptoInfo, byte[] messageBytes)
        {
            if (cryptoInfo.DisableVerifySignature)
            {
                // Skip the verification.
                return(true);
            }

            try
            {
                if (IsErrorPacket(packet.Header))
                {
                    packet = packet.Error;
                }

                byte[] bytesToCompute = messageBytes;
                // Zero out the 16-byte signature field in the SMB2 Header of the incoming message.
                Array.Clear(bytesToCompute, System.Runtime.InteropServices.Marshal.SizeOf(packet.Header) - Smb2Consts.SignatureSize, Smb2Consts.SignatureSize);

                //Compute the message with signing key
                byte[] computedSignature = null;
                if (Smb2Utility.IsSmb3xFamily(cryptoInfo.Dialect))
                {
                    //[MS-SMB2] 3.1.5.1
                    //If Session.Connection.Dialect belongs to the SMB 3.x dialect family,
                    //the receiver MUST compute a 16-byte hash by using AES-128-CMAC over the entire message,
                    //beginning with the SMB2 Header from step 2, and using the key provided.
                    //The AES-128-CMAC is specified in [RFC4493].

                    //TD has mentioned to use Session.SigningKey for SESSION_SETUP Response and Channel.SigningKey for other responses
                    //In the current SDK, the SigningKey is the Channel.SigningKey
                    computedSignature = AesCmac128.ComputeHash(cryptoInfo.SigningKey, bytesToCompute);
                }
                else
                {
                    //[MS-SMB2] 3.1.5.1
                    //If Session.Connection.Dialect is "2.002" or "2.100", the receiver MUST compute a 32-byte hash by using HMAC-SHA256 over the entire message,
                    //beginning with the SMB2 Header from step 2, and using the key provided.
                    //The HMAC-SHA256 hash is specified in [FIPS180-2] and [RFC2104].

                    HMACSHA256 hmacSha = new HMACSHA256(cryptoInfo.SigningKey);
                    computedSignature = hmacSha.ComputeHash(bytesToCompute);
                }

                //[MS-SMB2] 3.1.5.1
                //If the first 16 bytes (the high-order portion) of the computed signature from step 3 or step 4 matches the saved signature from step 1, the message is signed correctly
                // compare the first 16 bytes of the originalSignature and computedSignature
                return(packet.Header.Signature.SequenceEqual(computedSignature.Take(Smb2Consts.SignatureSize)));
            }
            catch (Exception ex)
            {
                throw new Exception("Error happened during signature verification of packet: " + packet.ToString() + ". Exception message: " + ex.Message);
            }
        }
        /// <summary>
        /// Verify the signature of a Smb2SinglePacket
        /// </summary>
        /// <param name="packet">The packet to be verified</param>
        /// <param name="cryptoInfo">The cryptoInfo of smb2client</param>        
        /// <returns>True when signature verification succeeds and false when fails</returns>
        private bool VerifySignature(Smb2SinglePacket packet, Smb2CryptoInfo cryptoInfo, byte[] messageBytes)
        {
            if (cryptoInfo.DisableVerifySignature)
            {
                // Skip the verification.
                return true;
            }

            try
            {
                if (IsErrorPacket(packet.Header))
                {
                    packet = packet.Error;
                }

                byte[] bytesToCompute = messageBytes;
                // Zero out the 16-byte signature field in the SMB2 Header of the incoming message.
                Array.Clear(bytesToCompute, System.Runtime.InteropServices.Marshal.SizeOf(packet.Header) - Smb2Consts.SignatureSize, Smb2Consts.SignatureSize);

                //Compute the message with signing key
                byte[] computedSignature = null;
                if (Smb2Utility.IsSmb3xFamily(cryptoInfo.Dialect))
                {
                    //[MS-SMB2] 3.1.5.1
                    //If Session.Connection.Dialect belongs to the SMB 3.x dialect family,
                    //the receiver MUST compute a 16-byte hash by using AES-128-CMAC over the entire message,
                    //beginning with the SMB2 Header from step 2, and using the key provided.
                    //The AES-128-CMAC is specified in [RFC4493].

                    //TD has mentioned to use Session.SigningKey for SESSION_SETUP Response and Channel.SigningKey for other responses
                    //In the current SDK, the SigningKey is the Channel.SigningKey
                    computedSignature = AesCmac128.ComputeHash(cryptoInfo.SigningKey, bytesToCompute);
                }
                else
                {
                    //[MS-SMB2] 3.1.5.1
                    //If Session.Connection.Dialect is "2.002" or "2.100", the receiver MUST compute a 32-byte hash by using HMAC-SHA256 over the entire message,
                    //beginning with the SMB2 Header from step 2, and using the key provided.
                    //The HMAC-SHA256 hash is specified in [FIPS180-2] and [RFC2104].

                    HMACSHA256 hmacSha = new HMACSHA256(cryptoInfo.SigningKey);
                    computedSignature = hmacSha.ComputeHash(bytesToCompute);
                }

                //[MS-SMB2] 3.1.5.1
                //If the first 16 bytes (the high-order portion) of the computed signature from step 3 or step 4 matches the saved signature from step 1, the message is signed correctly
                // compare the first 16 bytes of the originalSignature and computedSignature
                return packet.Header.Signature.SequenceEqual(computedSignature.Take(Smb2Consts.SignatureSize));
            }
            catch (Exception ex)
            {
                throw new Exception("Error happened during signature verification of packet: " + packet.ToString() + ". Exception message: " + ex.Message);
            }
        }
        /// <summary>
        /// Try to verify the signature of a singlePacket
        /// </summary>
        /// <param name="singlePacket">The singlepacket to verified</param>
        /// <param name="sessionId">The sessionId to retrieve the cryptoinfo of the session.</param>
        private void TryVerifySignature(Smb2SinglePacket singlePacket, ulong sessionId, byte[] messageBytes)
        {
            // [MS-SMB2] 3.2.5.1.3
            //If the MessageId is 0xFFFFFFFFFFFFFFFF, no verification is necessary.
            if (singlePacket.Header.MessageId == 0xFFFFFFFFFFFFFFFF)
            {
                return;
            }

            Smb2CryptoInfo cryptoInfo = null;
            //[MS-SMB2] 3.2.5.1.3
            //If the SMB2 header of the response has SMB2_FLAGS_SIGNED set in the Flags field, the client MUST verify the signature
            if (singlePacket.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_SIGNED))
            {
                //This is for the session binding success situation.
                //After the session setup success, the cryptoInfoTable will be updated with new signingkey
                //The new signingkey is not in the cryptoInfoTable now. Cannot verify the signature before the table is updated.

                if (singlePacket.Header.Command == Smb2Command.SESSION_SETUP
                    && singlePacket.Header.Status == Smb2Status.STATUS_SUCCESS)
                {
                    //skip
                    return;
                }

                if (cryptoInfoTable.TryGetValue(sessionId, out cryptoInfo))
                {
                    if (!VerifySignature(singlePacket, cryptoInfo, messageBytes))
                    {
                        //If signature verification fails, the client MUST discard the received message and do no further processing for it.
                        //The client MAY also choose to disconnect the connection.
                        //Throw exception here.
                        throw new InvalidOperationException("Incorrect signed packet: " + singlePacket.ToString());
                    }
                }
            }

            return;
        }
Beispiel #6
0
        /// <summary>
        /// Check if the packet needs to be encrypted but actually not encrypted.
        /// </summary>
        private void CheckIfNeedEncrypt(Smb2SinglePacket packet, ulong sessionId = 0)
        {
            if (!CheckEncrypt)
            {
                return;
            }
            var realSessionId = sessionId == 0 ? packet.Header.SessionId : sessionId;
            var cryptoInfo    = cryptoInfoTable.ContainsKey(realSessionId) ? cryptoInfoTable[realSessionId] : null;

            if (cryptoInfo != null)
            {
                if (cryptoInfo.EnableSessionEncryption)
                {
                    // MS-SMB2 3.3.4.1.4 Encrypting the Message
                    // If Connection.Dialect belongs to the SMB 3.x dialect family and Connection.ClientCapabilities includes the SMB2_GLOBAL_CAP_ENCRYPTION bit, the server MUST encrypt the message before sending, if IsEncryptionSupported is TRUE and any of the following conditions are satisfied:
                    // If the message being sent is any response to a client request for which Request.IsEncrypted is TRUE.
                    // If Session.EncryptData is TRUE and the response being sent is not SMB2_NEGOTIATE or SMB2 SESSION_SETUP.
                    // If Session.EncryptData is FALSE, the response being sent is not SMB2_NEGOTIATE or SMB2 SESSION_SETUP or SMB2 TREE_CONNECT, and Share.EncryptData for the share associated with the TreeId in the SMB2 header of the response is TRUE.
                    // The server MUST encrypt the message as specified in section 3.1.4.3, before sending it to the client.
                    // above description means SMB2_NEGOTIATE or SMB2 SESSION_SETUP never encrypts the message.
                    if (packet.Header.Command == Smb2Command.SESSION_SETUP && (packet is Smb2SessionSetupResponsePacket))
                    {
                        return;
                    }
                }

                if (cryptoInfo.EnableSessionEncryption)
                {
                    throw new Exception($"The packet should be encrypted for session: \"{packet.ToString()}\"");
                }

                if ((cryptoInfo.EnableTreeEncryption.Contains(packet.Header.TreeId) && packet.Header.Command != Smb2Command.TREE_CONNECT))
                {
                    throw new Exception($"The packet should be encrypted for tree: \"{packet.ToString()}\"");
                }
            }
        }
        /// <summary>
        /// Decode the  message except length field which may exist if transport is tcp
        /// </summary>
        /// <param name="messageBytes">The received packet</param>
        /// <param name="role">The role of this decoder, client or server</param>
        /// <param name="ignoreCompoundFlag">indicate whether decode the packet as a single packet or a compound packet
        /// when compound flag is set</param>
        /// <param name="realSessionId">The real sessionId for this packet</param>
        /// <param name="realTreeId">The real treeId for this packet</param>
        /// <param name="consumedLength">[OUT]The consumed length of the message</param>
        /// <param name="expectedLength">[OUT]The expected length</param>
        /// <returns>A Smb2Packet</returns>
        public Smb2Packet DecodeCompletePacket(
            byte[] messageBytes,
            Smb2Role role,
            bool ignoreCompoundFlag,
            ulong realSessionId,
            uint realTreeId,
            out int consumedLength,
            out int expectedLength
            )
        {
            //protocol version is of 4 bytes len
            byte[] protocolVersion = new byte[sizeof(uint)];
            Array.Copy(messageBytes, 0, protocolVersion, 0, protocolVersion.Length);

            SmbVersion version = DecodeVersion(protocolVersion);

            if (version == SmbVersion.Version1)
            {
                // SMB Negotiate packet
                return(DecodeSmbPacket(messageBytes, role, out consumedLength, out expectedLength));
            }
            else if (version == SmbVersion.Version2Encrypted)
            {
                // SMB2 encrypted packet
                return(DecodeEncryptedSmb2Packet(
                           messageBytes,
                           role,
                           ignoreCompoundFlag,
                           realSessionId,
                           realTreeId,
                           out consumedLength,
                           out expectedLength
                           ));
            }
            else
            {
                // SMB2 packet not encrypted
                Smb2Packet decodedPacket = DecodeSmb2Packet(
                    messageBytes,
                    role,
                    ignoreCompoundFlag,
                    realSessionId,
                    realTreeId,
                    out consumedLength,
                    out expectedLength
                    );

                //For single packet signature verification
                if (decodedPacket is Smb2SinglePacket)
                {
                    //verify signature of a single packet
                    Smb2SinglePacket singlePacket = decodedPacket as Smb2SinglePacket;

                    if (!TryVerifySignature(singlePacket, singlePacket.Header.SessionId))
                    {
                        throw new Exception("Error happened during signature verification of single packet: " + singlePacket.ToString());
                    }
                }
                else if (decodedPacket is Smb2CompoundPacket)//For Compound packet signature verification
                {
                    //verify signature of the compound packet
                    if (!TryVerifySignature(decodedPacket as Smb2CompoundPacket))
                    {
                        throw new Exception("Error happened during signature verification of compound packet: " + decodedPacket.ToString());
                    }
                }

                return(decodedPacket);
            }
        }