Exemplo n.º 1
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());
                    }
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Verify the signature of a Smb2CompoundPacket
        /// </summary>
        /// <param name="packet">The compound packet to be verified</param>
        private void TryVerifySignature(Smb2CompoundPacket packet, byte[] messageBytes)
        {
            try
            {
                ulong firstSessionId = packet.Packets[0].Header.SessionId;

                uint offset = 0;
                for (int i = 0; i < packet.Packets.Count; i++)
                {
                    Smb2SinglePacket singlePacket = packet.Packets[i];
                    // NextCommand is the offset, in bytes, from the beginning of this SMB2 header to the start of the subsequent 8-byte aligned SMB2 header.
                    uint   packetLen   = singlePacket.Header.NextCommand != 0 ? singlePacket.Header.NextCommand : (uint)(messageBytes.Length - offset);
                    byte[] packetBytes = new byte[packetLen];
                    Array.Copy(messageBytes, offset, packetBytes, 0, packetLen);
                    offset += packetLen;

                    // For Related operations, the sessinId is in the first packet of the compound packet.
                    ulong sessionId = singlePacket.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS) ? firstSessionId : singlePacket.Header.SessionId;

                    TryVerifySignature(singlePacket, sessionId, packetBytes);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Error happened during signature verification of compound 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)
        {
            if (cryptoInfo.DisableVerifySignature)
            {
                // Skip the verification.
                return(true);
            }

            try
            {
                if (IsErrorPacket(packet.Header))
                {
                    packet = packet.Error;
                }
                //save the 16-byte signature from the Signature field in the SMB2 Header
                byte[] originalSignature = packet.Header.Signature;

                //zero out the 16-byte signature field in the SMB2 Header of the incoming message.
                packet.Header.Signature = new byte[Smb2Consts.SignatureSize];
                byte[] bytesToCompute = packet.ToBytes();

                //Compute the message with signing key
                byte[] computedSignature = new byte[] { };
                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);
                }
                packet.Header.Signature = originalSignature;

                //[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(originalSignature.SequenceEqual(computedSignature.Take(16)));
            }
            catch (Exception ex)
            {
                throw new Exception("Error happened during signature verification of packet: " + packet.ToString() + ". Exception message: " + ex.Message);
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Try to verify the signature of a singlePacket which is a Session Setup Response.
        /// </summary>
        public void TryVerifySessionSetupResponseSignature(Smb2SinglePacket singlePacket, ulong sessionId, byte[] messageBytes)
        {
            if (singlePacket.Header.Command != Smb2Command.SESSION_SETUP)
            {
                return;
            }

            TryVerifySignature(singlePacket, sessionId, messageBytes);
        }
Exemplo n.º 5
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()}\"");
                }
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Check if the compound packet needs to be encrypted but actually not encrypted.
        /// </summary>
        private void CheckIfNeedEncrypt(Smb2CompoundPacket packet)
        {
            if (!CheckEncrypt)
            {
                return;
            }
            ulong firstSessionId = packet.Packets[0].Header.SessionId;

            for (int i = 0; i < packet.Packets.Count; i++)
            {
                Smb2SinglePacket singlePacket = packet.Packets[i];

                // For Related operations, the sessionId is in the first packet of the compound packet.
                ulong sessionId = singlePacket.Header.Flags.HasFlag(Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS) ? firstSessionId : singlePacket.Header.SessionId;
                CheckIfNeedEncrypt(singlePacket, sessionId);
            }
        }
        /// <summary>
        /// Update the context from a SessioinSetup request and response messages.
        /// The caller should ensure the messages are input in the right order.
        /// </summary>
        /// <param name="packet">The packet used to update this context. It must be the one of the following types:
        /// Smb2SessionSetupRequestPacket, Smb2SessionSetupResponsePacket</param>
        public void UpdateSessionState(ulong sessionId, Smb2SinglePacket packet)
        {
            //Check the packet type
            if (!(packet is Smb2SessionSetupRequestPacket) &&
                !(packet is Smb2SessionSetupResponsePacket))
            {
                throw new InvalidCastException("packet must be one of the types: Smb2SessionSetupRequestPacket, Smb2SessionSetupResponsePacket.");
            }

            if (completedSessionSet.Contains(sessionId))
            {
                return; // Don't update session hash value for reauthetication
            }

            //Refer to [MS-SMB2] 3.2.5.3	Receiving an SMB2 SESSION_SETUP Response
            if (packet is Smb2SessionSetupRequestPacket)
            {
                if (!sessionPreauthIntegrityHashValueTable.ContainsKey(sessionId))
                {
                    sessionPreauthIntegrityHashValueTable.Add(sessionId, connectionPreauthIntegrityHashValue);
                }
                byte[] messageBytes = packet.ToBytes();
                byte[] hashData     = sessionPreauthIntegrityHashValueTable[sessionId].Concat(messageBytes).ToArray();
                sessionPreauthIntegrityHashValueTable[sessionId] = CreateHashAlgorithm().ComputeHash(hashData);
            }
            else if (packet is Smb2SessionSetupResponsePacket)
            {
                if (packet.Header.Status == Smb2Status.STATUS_MORE_PROCESSING_REQUIRED)
                {
                    byte[] messageBytes = packet.ToBytes();
                    byte[] hashData     = sessionPreauthIntegrityHashValueTable[sessionId].Concat(messageBytes).ToArray();
                    sessionPreauthIntegrityHashValueTable[sessionId] = CreateHashAlgorithm().ComputeHash(hashData);
                }
                else if (packet.Header.Status == Smb2Status.STATUS_SUCCESS)
                {
                    completedSessionSet.Add(sessionId);
                }
            }
        }
        /// <summary>
        /// Verify the signature of a Smb2CompoundPacket
        /// </summary>
        /// <param name="packet">The compound packet to be verified</param>
        /// <returns>True when signature verification succeeds and false when fails</returns>
        private bool TryVerifySignature(Smb2CompoundPacket packet)
        {
            try
            {
                Packet_Header header = TypeMarshal.ToStruct <Packet_Header>(packet.ToBytes());

                if ((header.Flags & Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS)
                    == Packet_Header_Flags_Values.FLAGS_RELATED_OPERATIONS)
                {
                    //The realSessionId is the sessionId in the first pacekt of the compoundpacket for related packets.
                    ulong realSessionId = packet.Packets[0].Header.SessionId;
                    for (int i = 0; i < packet.Packets.Count; i++)
                    {
                        Smb2SinglePacket singlePacket = packet.Packets[i];
                        if (!TryVerifySignature(singlePacket, realSessionId))
                        {
                            break;
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < packet.Packets.Count; i++)
                    {
                        Smb2SinglePacket singlePacket = packet.Packets[i];
                        if (!TryVerifySignature(singlePacket, singlePacket.Header.SessionId))
                        {
                            break;
                        }
                    }
                }
                return(true);
            }
            catch (Exception ex)
            {
                throw new Exception("Error happened during signature verification of compound packet: " + packet.ToString() + ". Exception message:" + ex.Message);
            }
        }
Exemplo n.º 9
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>
        /// Update the context from a Negotiate Request or Response messages.
        /// The caller should ensure the messages are input in the right order.
        /// </summary>
        /// <param name="packet">The packet used to update this context. It must be the one of the following types:
        /// Smb2NegotiateRequestPacket, Smb2NegotiateResponsePacket</param>
        public void UpdateConnectionState(Smb2SinglePacket packet)
        {
            //Check the packet type
            if (!(packet is Smb2NegotiateRequestPacket) &&
                !(packet is Smb2NegotiateResponsePacket))
            {
                throw new InvalidCastException("packet must be one of the types: Smb2NegotiateRequestPacket, Smb2NegotiateResponsePacket.");
            }

            //Refer to [MS-SMB2] 3.2.5.2	Receiving an SMB2 NEGOTIATE Response
            if (packet is Smb2NegotiateRequestPacket)
            {
                connectionPreauthIntegrityHashValue = ZeroHashValue;
                byte[] messageBytes = packet.ToBytes();
                byte[] hashData     = connectionPreauthIntegrityHashValue.Concat(messageBytes).ToArray();
                connectionPreauthIntegrityHashValue = CreateHashAlgorithm().ComputeHash(hashData);
            }
            else if (packet is Smb2NegotiateResponsePacket)
            {
                byte[] messageBytes = packet.ToBytes();
                byte[] hashData     = connectionPreauthIntegrityHashValue.Concat(messageBytes).ToArray();
                connectionPreauthIntegrityHashValue = CreateHashAlgorithm().ComputeHash(hashData);
            }
        }
 /// <summary>
 /// Test if the response packet is an interim response packet
 /// </summary>
 /// <param name="singlePacket">The single response packet</param>
 /// <returns>True if it is an interim packet, otherwise false.</returns>
 private bool IsInterimResponsePacket(Smb2SinglePacket singlePacket)
 {
     return Smb2Utility.IsInterimResponsePacket(singlePacket);
 }
        /// <summary>
        /// modify the header of packet because it is a async packet
        /// </summary>
        /// <param name="packet">The packet</param>
        /// <param name="endpoint">The endpoint of client</param>
        /// <param name="asyncId">The asyncId</param>
        private void ModifyAsyncHeader(Smb2SinglePacket packet, Smb2Endpoint endpoint, ulong asyncId)
        {
            packet.Header.Flags |= Packet_Header_Flags_Values.FLAGS_ASYNC_COMMAND;

            packet.Header.ProcessId = (uint)asyncId;
            packet.Header.TreeId = (uint)(asyncId >> 32);

            //for finnal async response, if Interim Response has been send, it does not grand
            //any credits because credits has been granded in Interim Response
            if (context.connectionList[endpoint.EndpointId].asyncCommandList.ContainsKey(asyncId))
            {
                packet.Header.CreditRequest_47_Response = 0;
            }
            else
            {
                //grand credits as normal
            }
        }
 /// <summary>
 /// Set packet header field
 /// </summary>
 /// <param name="packet">The packet</param>
 /// <param name="endpoint">The client endpoint</param>
 /// <param name="messageId">The messageId of request packet</param>
 private void SetHeader(Smb2SinglePacket packet, Smb2Endpoint endpoint, ulong messageId)
 {
     SetHeader(packet, 0, endpoint, messageId);
 }
Exemplo n.º 14
0
        private Smb2Packet DecodeSingleResponsePacket(
            byte[] messageBytes,
            bool ignoreCompoundFlag,
            ulong realSessionId,
            uint realTreeId,
            out int consumedLength,
            out int expectedLength
            )
        {
            Packet_Header smb2Header;

            bool isLeaseBreakPacket = false;

            int offset = 0;

            smb2Header = TypeMarshal.ToStruct <Packet_Header>(messageBytes, ref offset);

            if (smb2Header.Command == Smb2Command.OPLOCK_BREAK)
            {
                ushort structureSize = TypeMarshal.ToStruct <ushort>(messageBytes, ref offset);

                if (structureSize == (ushort)OplockLeaseBreakStructureSize.LeaseBreakNotification ||
                    structureSize == (ushort)OplockLeaseBreakStructureSize.LeaseBreakResponse ||
                    structureSize == 9)    // Add this condition temporally to handle LeaseBreakResponse is error response (i.e. structureSize == 9), but this will still hide the condition when OplockBreakResponse is error response
                {
                    isLeaseBreakPacket = true;
                }
            }


            Smb2SinglePacket packet     = null;
            ushort           structSize = BitConverter.ToUInt16(messageBytes, Smb2Consts.Smb2HeaderLen);

            switch (smb2Header.Command)
            {
            case Smb2Command.CANCEL:
                packet = new Smb2CancelResponsePacket();
                break;

            case Smb2Command.CHANGE_NOTIFY:
                packet = new Smb2ChangeNotifyResponsePacket();
                break;

            case Smb2Command.CLOSE:
                packet = new Smb2CloseResponsePacket();
                break;

            case Smb2Command.CREATE:
                packet = new Smb2CreateResponsePacket();
                break;

            case Smb2Command.ECHO:
                packet = new Smb2EchoResponsePacket();
                break;

            case Smb2Command.FLUSH:
                packet = new Smb2FlushResponsePacket();
                break;

            case Smb2Command.IOCTL:
                packet = new Smb2IOCtlResponsePacket();
                break;

            case Smb2Command.LOCK:
                packet = new Smb2LockResponsePacket();
                break;

            case Smb2Command.LOGOFF:
                packet = new Smb2LogOffResponsePacket();
                break;

            case Smb2Command.NEGOTIATE:
                packet = new Smb2NegotiateResponsePacket();
                break;

            case Smb2Command.OPLOCK_BREAK:
                if (smb2Header.MessageId == ulong.MaxValue)
                {
                    if (!isLeaseBreakPacket)
                    {
                        packet = new Smb2OpLockBreakNotificationPacket();
                    }
                    else
                    {
                        packet = new Smb2LeaseBreakNotificationPacket();
                    }
                }
                else
                {
                    if (!isLeaseBreakPacket)
                    {
                        packet = new Smb2OpLockBreakResponsePacket();
                    }
                    else
                    {
                        packet = new Smb2LeaseBreakResponsePacket();
                    }
                }
                break;

            case Smb2Command.QUERY_DIRECTORY:
                packet = new Smb2QueryDirectoryResponePacket();
                break;

            case Smb2Command.QUERY_INFO:
                packet = new Smb2QueryInfoResponsePacket();
                break;

            case Smb2Command.READ:
                packet = new Smb2ReadResponsePacket();
                break;

            case Smb2Command.SESSION_SETUP:
                packet = new Smb2SessionSetupResponsePacket();
                ((Smb2SessionSetupResponsePacket)packet).MessageBytes = messageBytes;
                break;

            case Smb2Command.SET_INFO:
                packet = new Smb2SetInfoResponsePacket();
                break;

            case Smb2Command.TREE_CONNECT:
                packet = new Smb2TreeConnectResponsePacket();
                break;

            case Smb2Command.TREE_DISCONNECT:
                packet = new Smb2TreeDisconnectResponsePacket();
                break;

            case Smb2Command.WRITE:
                packet = new Smb2WriteResponsePacket();
                break;

            default:
                throw new InvalidOperationException("Received an unknown packet! the type of the packet is "
                                                    + smb2Header.Command.ToString());
            }

            if (IsErrorPacket(smb2Header))
            {
                var error = new Smb2ErrorResponsePacket();
                error.FromBytes(messageBytes, out consumedLength, out expectedLength);

                packet.Header = error.Header;
                packet.Error  = error;
            }
            else
            {
                packet.FromBytes(messageBytes, out consumedLength, out expectedLength);
            }

            //if ignoreCompoundFlag is false, means the process of decoding this packet
            //is not part of the process of decoding a compound packet. We will update
            //context here.
            if (!ignoreCompoundFlag)
            {
            }

            return(packet);
        }
Exemplo n.º 15
0
        /// <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;

                    TryVerifySignatureExceptSessionSetupResponse(singlePacket, singlePacket.Header.SessionId, messageBytes);
                }
                else if (decodedPacket is Smb2CompoundPacket)//For Compound packet signature verification
                {
                    //verify signature of the compound packet
                    TryVerifySignature(decodedPacket as Smb2CompoundPacket, messageBytes);
                }

                return(decodedPacket);
            }
        }
        /// <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);
            }
        }
        private void SetHeader(Smb2SinglePacket packet, uint status, Smb2Endpoint endpoint, ulong messageId)
        {
            packet.Endpoint = endpoint;

            Smb2SinglePacket singleRequestPacket = context.FindRequestPacket(endpoint.EndpointId, messageId)
                as Smb2SinglePacket;

            bool isRequestSigned = false;
            ushort clientRequestCredits = 0;

            if (singleRequestPacket == null)
            {
                packet.Header.MessageId = 0;
                packet.Header.Command = Smb2Command.NEGOTIATE;
            }
            else
            {
                packet.Header.MessageId = singleRequestPacket.Header.MessageId;
                packet.Header.SessionId = singleRequestPacket.Header.SessionId;
                packet.Header.TreeId = singleRequestPacket.Header.TreeId;
                packet.Header.ProcessId = singleRequestPacket.Header.ProcessId;
                packet.Header.Command = singleRequestPacket.Header.Command;

                if (((singleRequestPacket).Header.Flags & Packet_Header_Flags_Values.FLAGS_SIGNED)
                    == Packet_Header_Flags_Values.FLAGS_SIGNED)
                {
                    isRequestSigned = true;
                }

                clientRequestCredits = singleRequestPacket.Header.CreditRequest_47_Response;
            }

            packet.Header.CreditRequest_47_Response = Smb2Utility.CaculateResponseCredits(clientRequestCredits,
                context.connectionList[endpoint.EndpointId].commandSequenceWindow.Count);

            packet.Header.ProtocolId = Smb2Consts.Smb2ProtocolId;
            packet.Header.Signature = new byte[Smb2Consts.SignatureSize];
            packet.Header.StructureSize = Packet_Header_StructureSize_Values.V1;
            packet.Header.Status = status;

            packet.Header.Flags |= Packet_Header_Flags_Values.FLAGS_SERVER_TO_REDIR;

            if (packet.Header.SessionId != 0 &&
                (isRequestSigned || context.ShouldPacketBeSigned(singleRequestPacket.GetSessionId())))
            {
                packet.Header.Flags |= Packet_Header_Flags_Values.FLAGS_SIGNED;

                (packet as Smb2SinglePacket).SessionKey = context.globalSessionTable[singleRequestPacket.GetSessionId()].sessionKey;
            }
        }
        /// <summary>
        /// Update the context from a Negotiate Request or Response messages.
        /// The caller should ensure the messages are input in the right order.
        /// </summary>
        /// <param name="packet">The packet used to update this context. It must be the one of the following types:
        /// Smb2NegotiateRequestPacket, Smb2NegotiateResponsePacket</param>
        public void UpdateConnectionState(Smb2SinglePacket packet)
        {
            //Check the packet type
            if (!(packet is Smb2NegotiateRequestPacket) &&
                !(packet is Smb2NegotiateResponsePacket))
            {
                throw new InvalidCastException("packet must be one of the types: Smb2NegotiateRequestPacket, Smb2NegotiateResponsePacket.");
            }

            //Refer to [MS-SMB2] 3.2.5.2	Receiving an SMB2 NEGOTIATE Response
            if (packet is Smb2NegotiateRequestPacket)
            {
                connectionPreauthIntegrityHashValue = ZeroHashValue;
                byte[] messageBytes = packet.ToBytes();
                byte[] hashData = connectionPreauthIntegrityHashValue.Concat(messageBytes).ToArray();
                connectionPreauthIntegrityHashValue = CreateHashAlgorithm().ComputeHash(hashData);
            }
            else if (packet is Smb2NegotiateResponsePacket)
            {
                byte[] messageBytes = packet.ToBytes();
                byte[] hashData = connectionPreauthIntegrityHashValue.Concat(messageBytes).ToArray();
                connectionPreauthIntegrityHashValue = CreateHashAlgorithm().ComputeHash(hashData);
            }
        }
Exemplo n.º 19
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

                    if (cryptoInfo.SigningId == SigningAlgorithm.AES_GMAC)
                    {
                        var nonce = Smb2Utility.ComputeNonce(packet, this.decodeRole);
                        var(_ciphertext, tag) = AesGmac.ComputeHash(cryptoInfo.SigningKey, nonce, bytesToCompute);

                        computedSignature = tag;
                    }
                    else
                    {
                        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>
        /// Update the context from a SessioinSetup request and response messages.
        /// The caller should ensure the messages are input in the right order.
        /// </summary>
        /// <param name="packet">The packet used to update this context. It must be the one of the following types:
        /// Smb2SessionSetupRequestPacket, Smb2SessionSetupResponsePacket</param>
        public void UpdateSessionState(ulong sessionId, Smb2SinglePacket packet)
        {
            //Check the packet type
            if (!(packet is Smb2SessionSetupRequestPacket) &&
                !(packet is Smb2SessionSetupResponsePacket))
            {
                throw new InvalidCastException("packet must be one of the types: Smb2SessionSetupRequestPacket, Smb2SessionSetupResponsePacket.");
            }

            if (completedSessionSet.Contains(sessionId))
            {
                return; // Don't update session hash value for reauthetication
            }

            //Refer to [MS-SMB2] 3.2.5.3	Receiving an SMB2 SESSION_SETUP Response
            if (packet is Smb2SessionSetupRequestPacket)
            {
                if (!sessionPreauthIntegrityHashValueTable.ContainsKey(sessionId))
                {
                    sessionPreauthIntegrityHashValueTable.Add(sessionId, connectionPreauthIntegrityHashValue);
                }
                byte[] messageBytes = packet.ToBytes();
                byte[] hashData = sessionPreauthIntegrityHashValueTable[sessionId].Concat(messageBytes).ToArray();
                sessionPreauthIntegrityHashValueTable[sessionId] = CreateHashAlgorithm().ComputeHash(hashData);
            }
            else if (packet is Smb2SessionSetupResponsePacket)
            {
                if (packet.Header.Status == Smb2Status.STATUS_MORE_PROCESSING_REQUIRED)
                {
                    byte[] messageBytes = packet.ToBytes();
                    byte[] hashData = sessionPreauthIntegrityHashValueTable[sessionId].Concat(messageBytes).ToArray();
                    sessionPreauthIntegrityHashValueTable[sessionId] = CreateHashAlgorithm().ComputeHash(hashData);
                }
                else if (packet.Header.Status == Smb2Status.STATUS_SUCCESS)
                {
                    completedSessionSet.Add(sessionId);
                }
            }
        }
        /// <summary>
        /// Build a Smb2Packet from a byte array
        /// </summary>
        /// <param name="data">The byte array</param>
        /// <param name="consumedLen">The consumed data length</param>
        /// <param name="expectedLen">The expected data length</param>
        /// <returns>The Smb2Packet</returns>
        internal override void FromBytes(byte[] data, out int consumedLen, out int expectedLen)
        {
            this.Packets = new List <Smb2SinglePacket>();

            Packet_Header header = TypeMarshal.ToStruct <Packet_Header>(data);

            int innerConsumedLen = 0;
            int innerExpectedLen = 0;

            consumedLen = 0;

            byte[]           temp         = null;
            Smb2SinglePacket singlePacket = null;

            while (header.NextCommand != 0)
            {
                temp = new byte[header.NextCommand];

                Array.Copy(data, consumedLen, temp, 0, temp.Length);
                singlePacket = decoder.DecodeSinglePacket(
                    temp,
                    decoder.DecodeRole,
                    GetRealSessionId(header),
                    GetRealTreeId(header),
                    out innerConsumedLen,
                    out innerExpectedLen
                    ) as Smb2SinglePacket;
                singlePacket.OuterCompoundPacket = this;
                singlePacket.IsInCompoundPacket  = true;
                singlePacket.IsLast = false;

                Packets.Add(singlePacket);

                //If a packet is in compound packet, there may be some padding at the end,
                //here we do not rely on the innerConsumedLen but header.NextCommand
                consumedLen += temp.Length;

                header = TypeMarshal.ToStruct <Packet_Header>(ArrayUtility.SubArray(data, consumedLen));
            }

            temp = new byte[data.Length - consumedLen];
            Array.Copy(data, consumedLen, temp, 0, temp.Length);

            singlePacket = decoder.DecodeSinglePacket(
                temp,
                decoder.DecodeRole,
                GetRealSessionId(header),
                GetRealTreeId(header),
                out innerConsumedLen,
                out innerExpectedLen
                ) as Smb2SinglePacket;

            singlePacket.OuterCompoundPacket = this;
            singlePacket.IsInCompoundPacket  = true;
            singlePacket.IsLast = true;

            Packets.Add(singlePacket);

            consumedLen += innerConsumedLen;
            expectedLen  = 0;
        }
        /// <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;
        }
 /// <summary>
 /// Grand credit to client
 /// </summary>
 /// <param name="packet">The response packet</param>
 /// <param name="connectionId">Used to find the connection</param>
 private void GrandCredit(Smb2SinglePacket packet, int connectionId)
 {
     connectionList[connectionId].GrandCredit(packet.Header.CreditRequest_47_Response);
 }