]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Add test for one of the bounds checks fixed
authorForest <chocozilla@gmail.com>
Tue, 8 Jun 2021 01:42:29 +0000 (18:42 -0700)
committerForest <chocozilla@gmail.com>
Tue, 8 Jun 2021 01:42:29 +0000 (18:42 -0700)
Hazel.UnitTests/Dtls/ConnectionTests.cs
Hazel/Dtls/DtlsUnityConnection.cs
Hazel/FewerThreads/ThreadLimitedUdpConnectionListener.cs

index 0b9073e88f1d068e80adf60c62e6cf609e399534..5a3c37d9809d58dfc539cedd0b68151030023a0a 100644 (file)
@@ -172,6 +172,55 @@ IsdbLCwHYD3GVgk/D7NVxyU=
             }
         }
 
+        class MalformedDTLSClient : DtlsUnityConnection
+        {
+            public MalformedDTLSClient(ILogger logger, IPEndPoint remoteEndPoint, IPMode ipMode = IPMode.IPv4) : base(logger, remoteEndPoint, ipMode)
+            {
+                
+            }
+
+            protected override void SendClientHello()
+            {
+                Test_SendClientHello((clientHello, writer) =>
+                {
+                    ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)ProtocolVersion.DTLS1_2);
+                    writer = writer.Slice(2);
+
+                    clientHello.Random.CopyTo(writer);
+                    writer = writer.Slice(Hazel.Dtls.Random.Size);
+
+                    // Do not encode session ids
+                    writer[0] = (byte)0;
+                    writer = writer.Slice(1);
+
+                    writer[0] = (byte)clientHello.Cookie.Length;
+                    clientHello.Cookie.CopyTo(writer.Slice(1));
+                    writer = writer.Slice(1 + clientHello.Cookie.Length);
+
+                    ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)clientHello.CipherSuites.Length);
+                    clientHello.CipherSuites.CopyTo(writer.Slice(2));
+                    writer = writer.Slice(2 + clientHello.CipherSuites.Length);
+
+                    // ============ Here is the corruption. writer[0] should be 1. ============
+                    writer[0] = 255;
+                    writer[1] = (byte)CompressionMethod.Null;
+                    writer = writer.Slice(2);
+
+                    // Extensions size
+                    ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)(6 + clientHello.SupportedCurves.Length));
+                    writer = writer.Slice(2);
+
+                    // Supported curves extension
+                    ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)ExtensionType.EllipticCurves);
+                    ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)(2 + clientHello.SupportedCurves.Length), 2);
+                    ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)clientHello.SupportedCurves.Length, 4);
+                    clientHello.SupportedCurves.CopyTo(writer.Slice(6));
+
+                    return writer;
+                });
+            }
+        }
+
         [TestMethod]
         public void TestMalformedApplicationData()
         {
@@ -227,6 +276,47 @@ IsdbLCwHYD3GVgk/D7NVxyU=
             }
         }
 
+        [TestMethod]
+        public void TestMalformedConnectionData()
+        {
+            IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 27510);
+
+            IPEndPoint connectionEndPoint = ep;
+            DtlsConnectionListener.ConnectionId connectionId = new ThreadLimitedUdpConnectionListener.ConnectionId();
+
+            Semaphore signal = new Semaphore(0, int.MaxValue);
+
+            using (DtlsConnectionListener listener = new DtlsConnectionListener(2, new IPEndPoint(IPAddress.Any, ep.Port), new TestLogger()))
+            using (MalformedDTLSClient connection = new MalformedDTLSClient(new TestLogger(), ep))
+            {
+                listener.SetCertificate(GetCertificateForServer());
+                connection.SetValidServerCertificates(GetCertificateForClient());
+
+                listener.NewConnection += (evt) =>
+                {
+                    connectionEndPoint = evt.Connection.EndPoint;
+                    connectionId = ((ThreadLimitedUdpServerConnection)evt.Connection).ConnectionId;
+
+                    signal.Release();
+                    evt.Connection.Disconnected += (o, et) => {
+                    };
+                };
+                connection.Disconnected += (o, evt) => {
+                    signal.Release();
+                };
+
+                listener.Start();
+                connection.Connect();
+
+                Assert.IsTrue(listener.ReceiveThreadRunning, "Listener should be able to handle a malformed hello packet");
+                Assert.AreEqual(ConnectionState.NotConnected, connection.State);
+
+                // wait for the client to disconnect
+                listener.Dispose();
+                signal.WaitOne(100);
+            }
+        }
+
         [TestMethod]
         public void TestResentHandshakeConnects()
         {
index a11bd0dc63c36abb851843ca886a38c764603b6d..516cf693d82219f0f6d52764b4def6c3e217dea0 100644 (file)
@@ -867,7 +867,7 @@ namespace Hazel.Dtls
         /// <summary>
         /// Send (resend) a ClientHello message to the server
         /// </summary>
-        private void SendClientHello()
+        protected virtual void SendClientHello()
         {
             // Reset our verification stream
             this.nextEpoch.VerificationStream.Reset();
@@ -926,6 +926,66 @@ namespace Hazel.Dtls
             base.WriteBytesToConnection(packet.GetUnderlyingArray(), packet.Length);
         }
 
+        protected void Test_SendClientHello(Func<ClientHello, ByteSpan, ByteSpan> encodeCallback)
+        {
+            // Reset our verification stream
+            this.nextEpoch.VerificationStream.Reset();
+
+            // Describe our ClientHello flight
+            ClientHello clientHello = new ClientHello();
+            clientHello.Random = this.nextEpoch.ClientRandom;
+            clientHello.Cookie = this.nextEpoch.Cookie;
+            clientHello.CipherSuites = new byte[2];
+            clientHello.CipherSuites.WriteBigEndian16((ushort)CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
+            clientHello.SupportedCurves = new byte[2];
+            clientHello.SupportedCurves.WriteBigEndian16((ushort)NamedCurve.x25519);
+
+            Handshake handshake = new Handshake();
+            handshake.MessageType = HandshakeType.ClientHello;
+            handshake.Length = (uint)clientHello.CalculateSize();
+            handshake.MessageSequence = 0;
+            handshake.FragmentOffset = 0;
+            handshake.FragmentLength = handshake.Length;
+
+            // Describe the record
+            int plaintextLength = (int)(Handshake.Size + handshake.Length);
+            Record outgoingRecord = new Record();
+            outgoingRecord.ContentType = ContentType.Handshake;
+            outgoingRecord.Epoch = this.epoch;
+            outgoingRecord.SequenceNumber = this.currentEpoch.NextOutgoingSequence;
+            outgoingRecord.Length = (ushort)this.currentEpoch.RecordProtection.GetEncryptedSize(plaintextLength);
+            ++this.currentEpoch.NextOutgoingSequence;
+
+            // Convert the record to wire format
+            ByteSpan packet = new byte[Record.Size + outgoingRecord.Length];
+            ByteSpan writer = packet;
+            outgoingRecord.Encode(packet);
+            writer = writer.Slice(Record.Size);
+            handshake.Encode(writer);
+            writer = writer.Slice(Handshake.Size);
+
+            writer = encodeCallback(clientHello, writer);
+
+            // Write ClientHello to the verification stream
+            this.nextEpoch.VerificationStream.AddData(
+                packet.Slice(
+                      Record.Size
+                    , Handshake.Size + (int)handshake.Length
+                )
+            );
+
+            // Protect the record
+            this.currentEpoch.RecordProtection.EncryptClientPlaintext(
+                  packet.Slice(Record.Size, outgoingRecord.Length)
+                , packet.Slice(Record.Size, plaintextLength)
+                , ref outgoingRecord
+            );
+
+            this.nextEpoch.State = HandshakeState.ExpectingServerHello;
+            this.nextEpoch.NextPacketResendTime = DateTime.UtcNow + this.handshakeResendTimeout;
+            base.WriteBytesToConnection(packet.GetUnderlyingArray(), packet.Length);
+        }
+
         /// <summary>
         /// Send (resend) the ClientKeyExchange flight
         /// </summary>
index deda91d25744de8c5a505df7dbe8f8036fa4c2cf..e604765921f683a2e873fda9c3ec5eb1e0aa376c 100644 (file)
@@ -50,6 +50,8 @@ namespace Hazel.Udp.FewerThreads
         private Thread sendThread;
         private HazelThreadPool processThreads;
 
+        public bool ReceiveThreadRunning => this.receiveThread.ThreadState == ThreadState.Running;
+
         public struct ConnectionId : IEquatable<ConnectionId>
         {
             public IPEndPoint EndPoint;