Reject stray post-Finished messages in DTLS.
This is in preparation for switching finish_handshake to a
release_current_message hook. finish_handshake in DTLS is also
responsible for releasing any memory associated with extra messages in
the handshake.
Except that's not right and we need to make it an error anyway. Given
that the rest of the DTLS dispatch layer already strongly assumes there
is only one message in epoch one, putting the check in the fragment
processing works fine enough. Add tests for this.
This will certainly need revising when DTLS 1.3 happens (perhaps just a
version check, perhaps bringing finish_handshake back as a function that
can fail... which means we need a state just before SSL_ST_OK), but DTLS
1.3 post-handshake messages haven't really been written down, so let's
do the easy thing for now and add a test for when it gets more
interesting.
This removes the sequence number reset in the DTLS code. That reset
never did anything becase we don't and never will renego. We should make
sure DTLS 1.3 does not bring the reset back for post-handshake stuff.
(It was wrong in 1.2 too. Penultimate-flight retransmits and renego
requests are ambiguous in DTLS.)
BUG=83
Change-Id: I33d645a8550f73e74606030b9815fdac0c9fb682
Reviewed-on: https://boringssl-review.googlesource.com/8988
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 331703a..1d9cd75 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -368,6 +368,13 @@
return -1;
}
+ /* The encrypted epoch in DTLS has only one handshake message. */
+ if (ssl->d1->r_epoch == 1 && msg_hdr.seq != ssl->d1->handshake_read_seq) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
if (msg_hdr.seq < ssl->d1->handshake_read_seq ||
msg_hdr.seq >
(unsigned)ssl->d1->handshake_read_seq + SSL_MAX_HANDSHAKE_FLIGHT) {
diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c
index 2376e96..ceb6e17 100644
--- a/ssl/d1_pkt.c
+++ b/ssl/d1_pkt.c
@@ -223,7 +223,8 @@
return -1;
}
- if (msg_hdr.type == SSL3_MT_FINISHED) {
+ if (msg_hdr.type == SSL3_MT_FINISHED &&
+ msg_hdr.seq == ssl->d1->handshake_read_seq) {
if (msg_hdr.frag_off == 0) {
/* Retransmit our last flight of messages. If the peer sends the second
* Finished, they may not have received ours. Only do this for the
diff --git a/ssl/dtls_method.c b/ssl/dtls_method.c
index 15d90f6..09bd455 100644
--- a/ssl/dtls_method.c
+++ b/ssl/dtls_method.c
@@ -93,8 +93,6 @@
}
static void dtls1_finish_handshake(SSL *ssl) {
- ssl->d1->handshake_read_seq = 0;
- ssl->d1->handshake_write_seq = 0;
dtls_clear_incoming_messages(ssl);
ssl->init_msg = NULL;
ssl->init_num = 0;
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 1014dea..141f160 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1009,6 +1009,10 @@
// PackHelloRequestWithFinished, if true, causes the TLS server to send
// HelloRequest in the same record as Finished.
PackHelloRequestWithFinished bool
+
+ // SendExtraFinished, if true, causes an extra Finished message to be
+ // sent.
+ SendExtraFinished bool
}
func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index b32be0e..36bd7e4 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -493,6 +493,7 @@
// leg of the handshake is retransmited upon re-receiving a
// Finished.
if err := c.simulatePacketLoss(func() {
+ c.sendHandshakeSeq--
c.writeRecord(recordTypeHandshake, hs.finishedBytes)
c.flushHandshake()
}); err != nil {
@@ -764,6 +765,9 @@
} else {
c.writeRecord(recordTypeHandshake, finished.marshal())
}
+ if c.config.Bugs.SendExtraFinished {
+ c.writeRecord(recordTypeHandshake, finished.marshal())
+ }
c.flushHandshake()
// Switch to application data keys.
@@ -1345,6 +1349,11 @@
for _, msg := range postCCSMsgs {
c.writeRecord(recordTypeHandshake, msg)
}
+
+ if c.config.Bugs.SendExtraFinished {
+ c.writeRecord(recordTypeHandshake, finished.marshal())
+ }
+
c.flushHandshake()
}
return nil
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 012c836..ff8005b 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -84,6 +84,7 @@
// leg of the handshake is retransmited upon re-receiving a
// Finished.
if err := c.simulatePacketLoss(func() {
+ c.sendHandshakeSeq--
c.writeRecord(recordTypeHandshake, hs.finishedBytes)
c.flushHandshake()
}); err != nil {
@@ -582,6 +583,9 @@
}
hs.writeServerHash(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
+ if c.config.Bugs.SendExtraFinished {
+ c.writeRecord(recordTypeHandshake, finished.marshal())
+ }
c.flushHandshake()
// The various secrets do not incorporate the client's final leg, so
@@ -1399,6 +1403,10 @@
if !c.config.Bugs.SkipFinished && len(postCCSBytes) > 0 {
c.writeRecord(recordTypeHandshake, postCCSBytes)
+ if c.config.Bugs.SendExtraFinished {
+ c.writeRecord(recordTypeHandshake, finished.marshal())
+ }
+
if !c.config.Bugs.PackHelloRequestWithFinished {
// Defer flushing until renegotiation.
c.flushHandshake()
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index d3cd77f..cde5592 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2086,6 +2086,30 @@
},
resumeSession: true,
},
+ {
+ protocol: dtls,
+ name: "DTLS-SendExtraFinished",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendExtraFinished: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ protocol: dtls,
+ name: "DTLS-SendExtraFinished-Reordered",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: 2,
+ ReorderHandshakeFragments: true,
+ SendExtraFinished: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
}
testCases = append(testCases, basicTests...)
}