summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2017-01-10 23:02:28 +0000
committerMatt Caswell <matt@openssl.org>2017-01-30 10:17:00 +0000
commitc7f47786a5e5f68dc33091ffb2a42e51a73de3a1 (patch)
tree6ca73f81c1017d62f50a09cd130fdb013df8b0f4
parent0386aad1ab472a4059da85131cceca15aab5ebae (diff)
Move state machine knowledge out of the record layer
The record layer was making decisions that should really be left to the state machine around unexpected handshake messages that are received after the initial handshake (i.e. renegotiation related messages). This commit removes that code from the record layer and updates the state machine accordingly. This simplifies the state machine and paves the way for handling other messages post-handshake such as the NewSessionTicket in TLSv1.3. Reviewed-by: Rich Salz <rsalz@openssl.org> (Merged from https://github.com/openssl/openssl/pull/2259)
-rw-r--r--include/openssl/ssl.h7
-rw-r--r--ssl/record/rec_layer_d1.c84
-rw-r--r--ssl/record/rec_layer_s3.c89
-rw-r--r--ssl/s3_lib.c18
-rw-r--r--ssl/ssl_err.c2
-rw-r--r--ssl/ssl_lib.c2
-rw-r--r--ssl/ssl_locl.h7
-rw-r--r--ssl/statem/extensions_clnt.c10
-rw-r--r--ssl/statem/extensions_srvr.c17
-rw-r--r--ssl/statem/statem.c68
-rw-r--r--ssl/statem/statem.h7
-rw-r--r--ssl/statem/statem_clnt.c61
-rw-r--r--ssl/statem/statem_dtls.c5
-rw-r--r--ssl/statem/statem_lib.c49
-rw-r--r--ssl/statem/statem_locl.h1
-rw-r--r--ssl/statem/statem_srvr.c15
16 files changed, 206 insertions, 236 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 86ffcb978f..e95fdb4842 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -878,7 +878,8 @@ typedef enum {
TLS_ST_SW_ENCRYPTED_EXTENSIONS,
TLS_ST_CR_ENCRYPTED_EXTENSIONS,
TLS_ST_CR_CERT_VRFY,
- TLS_ST_SW_CERT_VRFY
+ TLS_ST_SW_CERT_VRFY,
+ TLS_ST_CR_HELLO_REQ
} OSSL_HANDSHAKE_STATE;
/*
@@ -1647,7 +1648,7 @@ __owur STACK_OF(SSL_CIPHER) *SSL_get1_supported_ciphers(SSL *s);
__owur int SSL_do_handshake(SSL *s);
int SSL_renegotiate(SSL *s);
-__owur int SSL_renegotiate_abbreviated(SSL *s);
+int SSL_renegotiate_abbreviated(SSL *s);
__owur int SSL_renegotiate_pending(SSL *s);
int SSL_shutdown(SSL *s);
@@ -2344,6 +2345,7 @@ int ERR_load_SSL_strings(void);
# define SSL_F_TLS_PROCESS_CLIENT_KEY_EXCHANGE 382
# define SSL_F_TLS_PROCESS_ENCRYPTED_EXTENSIONS 444
# define SSL_F_TLS_PROCESS_FINISHED 364
+# define SSL_F_TLS_PROCESS_HELLO_REQ 507
# define SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT 442
# define SSL_F_TLS_PROCESS_KEY_EXCHANGE 365
# define SSL_F_TLS_PROCESS_NEW_SESSION_TICKET 366
@@ -2356,6 +2358,7 @@ int ERR_load_SSL_strings(void);
# define SSL_F_TLS_PROCESS_SKE_PSK_PREAMBLE 421
# define SSL_F_TLS_PROCESS_SKE_SRP 422
# define SSL_F_TLS_SCAN_CLIENTHELLO_TLSEXT 450
+# define SSL_F_TLS_SETUP_HANDSHAKE 508
# define SSL_F_USE_CERTIFICATE_CHAIN_FILE 220
/* Reason codes. */
diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c
index 0a32f07c2a..67846bd19f 100644
--- a/ssl/record/rec_layer_d1.c
+++ b/ssl/record/rec_layer_d1.c
@@ -14,6 +14,7 @@
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include "record_locl.h"
+#include <assert.h>
int DTLS_RECORD_LAYER_new(RECORD_LAYER *rl)
{
@@ -632,70 +633,6 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
* (Possibly rr is 'empty' now, i.e. rr->length may be 0.)
*/
- /* If we are a client, check for an incoming 'Hello Request': */
- if ((!s->server) &&
- (s->rlayer.d->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) &&
- (s->rlayer.d->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
- (s->session != NULL) && (s->session->cipher != NULL)) {
- s->rlayer.d->handshake_fragment_len = 0;
-
- if ((s->rlayer.d->handshake_fragment[1] != 0) ||
- (s->rlayer.d->handshake_fragment[2] != 0) ||
- (s->rlayer.d->handshake_fragment[3] != 0)) {
- al = SSL_AD_DECODE_ERROR;
- SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_BAD_HELLO_REQUEST);
- goto f_err;
- }
-
- /*
- * no need to check sequence number on HELLO REQUEST messages
- */
-
- if (s->msg_callback)
- s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE,
- s->rlayer.d->handshake_fragment, 4, s,
- s->msg_callback_arg);
-
- if (SSL_is_init_finished(s) &&
- !s->s3->renegotiate) {
- s->d1->handshake_read_seq++;
- s->new_session = 1;
- ssl3_renegotiate(s);
- if (ssl3_renegotiate_check(s)) {
- i = s->handshake_func(s);
- if (i < 0)
- return i;
- if (i == 0) {
- SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_SSL_HANDSHAKE_FAILURE);
- return -1;
- }
-
- if (!(s->mode & SSL_MODE_AUTO_RETRY)) {
- if (SSL3_BUFFER_get_left(&s->rlayer.rbuf) == 0) {
- /* no read-ahead left? */
- BIO *bio;
- /*
- * In the case where we try to read application data,
- * but we trigger an SSL handshake, we return -1 with
- * the retry option set. Otherwise renegotiation may
- * cause nasty problems in the blocking world
- */
- s->rwstate = SSL_READING;
- bio = SSL_get_rbio(s);
- BIO_clear_retry_flags(bio);
- BIO_set_retry_read(bio);
- return -1;
- }
- }
- }
- }
- /*
- * we either finished a handshake or ignored the request, now try
- * again to obtain the (application) data we were asked for
- */
- goto start;
- }
-
if (s->rlayer.d->alert_fragment_len >= DTLS1_AL_HEADER_LENGTH) {
int alert_level = s->rlayer.d->alert_fragment[0];
int alert_descr = s->rlayer.d->alert_fragment[1];
@@ -837,11 +774,22 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
goto start;
}
- if (SSL_is_init_finished(s)) {
- ossl_statem_set_in_init(s, 1);
- s->renegotiate = 1;
- s->new_session = 1;
+ /*
+ * To get here we must be trying to read app data but found handshake
+ * data. But if we're trying to read app data, and we're not in init
+ * (which is tested for at the top of this function) then init must be
+ * finished
+ */
+ assert(SSL_is_init_finished(s));
+ if (!SSL_is_init_finished(s)) {
+ al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_DTLS1_READ_BYTES, ERR_R_INTERNAL_ERROR);
+ goto f_err;
}
+
+ /* We found handshake data, so we're going back into init */
+ ossl_statem_set_in_init(s, 1);
+
i = s->handshake_func(s);
if (i < 0)
return i;
diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index b4dee6e21d..59aa8d4cae 100644
--- a/ssl/record/rec_layer_s3.c
+++ b/ssl/record/rec_layer_s3.c
@@ -8,6 +8,7 @@
*/
#include <stdio.h>
+#include <assert.h>
#include <limits.h>
#include <errno.h>
#define USE_SOCKETS
@@ -1387,69 +1388,6 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
* (Possibly rr is 'empty' now, i.e. rr->length may be 0.)
*/
- /* If we are a client, check for an incoming 'Hello Request': */
- if ((!s->server) &&
- (s->rlayer.handshake_fragment_len >= 4) &&
- !SSL_IS_TLS13(s) &&
- (s->rlayer.handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
- (s->session != NULL) && (s->session->cipher != NULL)) {
- s->rlayer.handshake_fragment_len = 0;
-
- if ((s->rlayer.handshake_fragment[1] != 0) ||
- (s->rlayer.handshake_fragment[2] != 0) ||
- (s->rlayer.handshake_fragment[3] != 0)) {
- al = SSL_AD_DECODE_ERROR;
- SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_BAD_HELLO_REQUEST);
- goto f_err;
- }
-
- if (s->msg_callback)
- s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE,
- s->rlayer.handshake_fragment, 4, s,
- s->msg_callback_arg);
-
- if (SSL_is_init_finished(s) &&
- !s->s3->renegotiate) {
- ssl3_renegotiate(s);
- if (ssl3_renegotiate_check(s)) {
- i = s->handshake_func(s);
- if (i < 0)
- return i;
- if (i == 0) {
- SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_SSL_HANDSHAKE_FAILURE);
- return -1;
- }
-
- if (!(s->mode & SSL_MODE_AUTO_RETRY)) {
- if (SSL3_BUFFER_get_left(rbuf) == 0) {
- /* no read-ahead left? */
- BIO *bio;
- /*
- * In the case where we try to read application data,
- * but we trigger an SSL handshake, we return -1 with
- * the retry option set. Otherwise renegotiation may
- * cause nasty problems in the blocking world
- */
- s->rwstate = SSL_READING;
- bio = SSL_get_rbio(s);
- BIO_clear_retry_flags(bio);
- BIO_set_retry_read(bio);
- return -1;
- }
- }
- } else {
- SSL3_RECORD_set_read(rr);
- }
- } else {
- /* Does this ever happen? */
- SSL3_RECORD_set_read(rr);
- }
- /*
- * we either finished a handshake or ignored the request, now try
- * again to obtain the (application) data we were asked for
- */
- goto start;
- }
/*
* If we are a server and get a client hello when renegotiation isn't
* allowed send back a no renegotiation alert and carry on. WARNING:
@@ -1558,18 +1496,27 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
}
/*
- * Unexpected handshake message (Client Hello, NewSessionTicket (TLS1.3) or
+ * Unexpected handshake message (ClientHello, NewSessionTicket (TLS1.3) or
* protocol violation)
*/
if ((s->rlayer.handshake_fragment_len >= 4)
- && !ossl_statem_get_in_handshake(s)) {
- if (SSL_is_init_finished(s)) {
- ossl_statem_set_in_init(s, 1);
- if (!SSL_IS_TLS13(s)) {
- s->renegotiate = 1;
- s->new_session = 1;
- }
+ && !ossl_statem_get_in_handshake(s)) {
+ /*
+ * To get here we must be trying to read app data but found handshake
+ * data. But if we're trying to read app data, and we're not in init
+ * (which is tested for at the top of this function) then init must be
+ * finished
+ */
+ assert(SSL_is_init_finished(s));
+ if (!SSL_is_init_finished(s)) {
+ al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_SSL3_READ_BYTES, ERR_R_INTERNAL_ERROR);
+ goto f_err;
}
+
+ /* We found handshake data, so we're going back into init */
+ ossl_statem_set_in_init(s, 1);
+
i = s->handshake_func(s);
if (i < 0)
return i;
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index ff4a03b147..1655333b13 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3822,7 +3822,7 @@ int ssl3_write(SSL *s, const void *buf, size_t len, size_t *written)
{
clear_sys_error();
if (s->s3->renegotiate)
- ssl3_renegotiate_check(s);
+ ssl3_renegotiate_check(s, 0);
return s->method->ssl_write_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len,
written);
@@ -3835,7 +3835,7 @@ static int ssl3_read_internal(SSL *s, void *buf, size_t len, int peek,
clear_sys_error();
if (s->s3->renegotiate)
- ssl3_renegotiate_check(s);
+ ssl3_renegotiate_check(s, 0);
s->s3->in_read_app_data = 1;
ret =
s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len,
@@ -3878,14 +3878,22 @@ int ssl3_renegotiate(SSL *s)
return (1);
}
-int ssl3_renegotiate_check(SSL *s)
+/*
+ * Check if we are waiting to do a renegotiation and if so whether now is a
+ * good time to do it. If |initok| is true then we are being called from inside
+ * the state machine so ignore the result of SSL_in_init(s). Otherwise we
+ * should not do a renegotiation if SSL_in_init(s) is true. Returns 1 if we
+ * should do a renegotiation now and sets up the state machine for it. Otherwise
+ * returns 0.
+ */
+int ssl3_renegotiate_check(SSL *s, int initok)
{
int ret = 0;
if (s->s3->renegotiate) {
if (!RECORD_LAYER_read_pending(&s->rlayer)
&& !RECORD_LAYER_write_pending(&s->rlayer)
- && !SSL_in_init(s)) {
+ && (initok || !SSL_in_init(s))) {
/*
* if we are the server, and we have sent a 'RENEGOTIATE'
* message, we need to set the state machine into the renegotiate
@@ -3898,7 +3906,7 @@ int ssl3_renegotiate_check(SSL *s)
ret = 1;
}
}
- return (ret);
+ return ret;
}
/*
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index d380c86b9b..099af01371 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -402,6 +402,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
{ERR_FUNC(SSL_F_TLS_PROCESS_ENCRYPTED_EXTENSIONS),
"tls_process_encrypted_extensions"},
{ERR_FUNC(SSL_F_TLS_PROCESS_FINISHED), "tls_process_finished"},
+ {ERR_FUNC(SSL_F_TLS_PROCESS_HELLO_REQ), "tls_process_hello_req"},
{ERR_FUNC(SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT),
"tls_process_initial_server_flight"},
{ERR_FUNC(SSL_F_TLS_PROCESS_KEY_EXCHANGE), "tls_process_key_exchange"},
@@ -419,6 +420,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
{ERR_FUNC(SSL_F_TLS_PROCESS_SKE_SRP), "tls_process_ske_srp"},
{ERR_FUNC(SSL_F_TLS_SCAN_CLIENTHELLO_TLSEXT),
"tls_scan_clienthello_tlsext"},
+ {ERR_FUNC(SSL_F_TLS_SETUP_HANDSHAKE), "tls_setup_handshake"},
{ERR_FUNC(SSL_F_USE_CERTIFICATE_CHAIN_FILE),
"use_certificate_chain_file"},
{0, NULL}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 8ca1a3c778..8e6a14393c 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3087,7 +3087,7 @@ int SSL_do_handshake(SSL *s)
return -1;
}
- s->method->ssl_renegotiate_check(s);
+ s->method->ssl_renegotiate_check(s, 0);
if (SSL_in_init(s) || SSL_in_before(s)) {
if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) {
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 39e27eac1b..ef525fe6e1 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -356,6 +356,8 @@
&& (s)->method->version >= TLS1_3_VERSION \
&& (s)->method->version != TLS_ANY_VERSION)
+# define SSL_IS_FIRST_HANDSHAKE(S) ((s)->s3->tmp.finish_md_len == 0)
+
/* See if we need explicit IV */
# define SSL_USE_EXPLICIT_IV(s) \
(s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_EXPLICIT_IV)
@@ -456,7 +458,7 @@ struct ssl_method_st {
int (*ssl_write) (SSL *s, const void *buf, size_t len, size_t *written);
int (*ssl_shutdown) (SSL *s);
int (*ssl_renegotiate) (SSL *s);
- int (*ssl_renegotiate_check) (SSL *s);
+ int (*ssl_renegotiate_check) (SSL *s, int);
int (*ssl_read_bytes) (SSL *s, int type, int *recvd_type,
unsigned char *buf, size_t len, int peek,
size_t *readbytes);
@@ -1985,7 +1987,7 @@ __owur int ssl3_get_req_cert_type(SSL *s, WPACKET *pkt);
__owur int ssl3_num_ciphers(void);
__owur const SSL_CIPHER *ssl3_get_cipher(unsigned int u);
int ssl3_renegotiate(SSL *ssl);
-int ssl3_renegotiate_check(SSL *ssl);
+int ssl3_renegotiate_check(SSL *ssl, int initok);
__owur int ssl3_dispatch_alert(SSL *s);
__owur size_t ssl3_final_finish_mac(SSL *s, const char *sender, size_t slen,
unsigned char *p);
@@ -2014,6 +2016,7 @@ __owur long ssl3_default_timeout(void);
__owur int ssl3_set_handshake_header(SSL *s, WPACKET *pkt, int htype);
__owur int tls_close_construct_packet(SSL *s, WPACKET *pkt, int htype);
+__owur int tls_setup_handshake(SSL *s);
__owur int dtls1_set_handshake_header(SSL *s, WPACKET *pkt, int htype);
__owur int dtls1_close_construct_packet(SSL *s, WPACKET *pkt, int htype);
__owur int ssl3_handshake_write(SSL *s);
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index fe007492c5..1e2cc3f6f7 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -318,7 +318,7 @@ int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, X509 *x,
int tls_construct_ctos_npn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
int *al)
{
- if (s->ctx->ext.npn_select_cb == NULL || s->s3->tmp.finish_md_len != 0)
+ if (s->ctx->ext.npn_select_cb == NULL || !SSL_IS_FIRST_HANDSHAKE(s))
return 1;
/*
@@ -340,11 +340,7 @@ int tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
{
s->s3->alpn_sent = 0;
- /*
- * finish_md_len is non-zero during a renegotiation, so
- * this avoids sending ALPN during the renegotiation
- */
- if (s->ext.alpn == NULL || s->s3->tmp.finish_md_len != 0)
+ if (s->ext.alpn == NULL || !SSL_IS_FIRST_HANDSHAKE(s))
return 1;
if (!WPACKET_put_bytes_u16(pkt,
@@ -866,7 +862,7 @@ int tls_parse_stoc_npn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
PACKET tmppkt;
/* Check if we are in a renegotiation. If so ignore this extension */
- if (s->s3->tmp.finish_md_len != 0)
+ if (!SSL_IS_FIRST_HANDSHAKE(s))
return 1;
/* We must have requested it. */
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index d58eedda3a..357b3b7105 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -322,21 +322,8 @@ int tls_parse_ctos_npn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
/*
* We shouldn't accept this extension on a
* renegotiation.
- *
- * s->new_session will be set on renegotiation, but we
- * probably shouldn't rely that it couldn't be set on
- * the initial renegotiation too in certain cases (when
- * there's some other reason to disallow resuming an
- * earlier session -- the current code won't be doing
- * anything like that, but this might change).
- *
- * A valid sign that there's been a previous handshake
- * in this connection is if s->s3->tmp.finish_md_len >
- * 0. (We are talking about a check that will happen
- * in the Hello protocol round, well before a new
- * Finished message could have been computed.)
*/
- if (s->s3->tmp.finish_md_len == 0)
+ if (SSL_IS_FIRST_HANDSHAKE(s))
s->s3->npn_seen = 1;
return 1;
@@ -352,7 +339,7 @@ int tls_parse_ctos_alpn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
{
PACKET protocol_list, save_protocol_list, protocol;
- if (s->s3->tmp.finish_md_len != 0)
+ if (!SSL_IS_FIRST_HANDSHAKE(s))
return 1;
if (!PACKET_as_length_prefixed_2(pkt, &protocol_list)
diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c
index ac78d2dc24..bd7d89a461 100644
--- a/ssl/statem/statem.c
+++ b/ssl/statem/statem.c
@@ -105,7 +105,6 @@ void ossl_statem_clear(SSL *s)
*/
void ossl_statem_set_renegotiate(SSL *s)
{
- s->statem.state = MSG_FLOW_RENEGOTIATE;
s->statem.in_init = 1;
s->statem.request_state = TLS_ST_SW_HELLO_REQ;
}
@@ -190,10 +189,10 @@ static info_cb get_callback(SSL *s)
/*
* The main message flow state machine. We start in the MSG_FLOW_UNINITED or
- * MSG_FLOW_RENEGOTIATE state and finish in MSG_FLOW_FINISHED. Valid states and
+ * MSG_FLOW_FINISHED state and finish in MSG_FLOW_FINISHED. Valid states and
* transitions are as follows:
*
- * MSG_FLOW_UNINITED MSG_FLOW_RENEGOTIATE
+ * MSG_FLOW_UNINITED MSG_FLOW_FINISHED
* | |
* +-----------------------+
* v
@@ -253,15 +252,7 @@ static int state_machine(SSL *s, int server)
#endif
/* Initialise state machine */
-
- if (st->state == MSG_FLOW_RENEGOTIATE) {
- s->renegotiate = 1;
- if (!server)
- s->ctx->stats.sess_connect_renegotiate++;
- }
-
if (st->state == MSG_FLOW_UNINITED
- || st->state == MSG_FLOW_RENEGOTIATE
|| st->state == MSG_FLOW_FINISHED) {
if (st->state == MSG_FLOW_UNINITED) {
st->hand_state = TLS_ST_BEFORE;
@@ -322,53 +313,14 @@ static int state_machine(SSL *s, int server)
goto end;
}
- if (!SSL_IS_TLS13(s)) {
- if (!server || st->state != MSG_FLOW_RENEGOTIATE) {
- if (!ssl3_init_finished_mac(s)) {
- ossl_statem_set_error(s);
- goto end;
- }
- }
-
- if (server) {
- if (st->state != MSG_FLOW_RENEGOTIATE) {
- s->ctx->stats.sess_accept++;
- } else if (!s->s3->send_connection_binding &&
- !(s->options &
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) {
- /*
- * Server attempting to renegotiate with client that doesn't
- * support secure renegotiation.
- */
- SSLerr(SSL_F_STATE_MACHINE,
- SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
- ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
- ossl_statem_set_error(s);
- goto end;
- } else {
- /*
- * st->state == MSG_FLOW_RENEGOTIATE, we will just send a
- * HelloRequest
- */
- s->ctx->stats.sess_accept_renegotiate++;
-
- s->s3->tmp.cert_request = 0;
- }
- } else {
- s->ctx->stats.sess_connect++;
-
- /* mark client_random uninitialized */
- memset(s->s3->client_random, 0, sizeof(s->s3->client_random));
- s->hit = 0;
-
- s->s3->tmp.cert_req = 0;
-
- if (SSL_IS_DTLS(s)) {
- st->use_timer = 1;
- }
+ if (SSL_IS_FIRST_HANDSHAKE(s) || s->renegotiate) {
+ if (!tls_setup_handshake(s)) {
+ ossl_statem_set_error(s);
+ goto end;
}
- st->read_state_first_init = 1;
+ if (SSL_IS_FIRST_HANDSHAKE(s))
+ st->read_state_first_init = 1;
}
st->state = MSG_FLOW_WRITING;
@@ -826,7 +778,7 @@ int statem_flush(SSL *s)
/*
* Called by the record layer to determine whether application data is
- * allowed to be sent in the current handshake state or not.
+ * allowed to be received in the current handshake state or not.
*
* Return values are:
* 1: Yes (application data allowed)
@@ -836,7 +788,7 @@ int ossl_statem_app_data_allowed(SSL *s)
{
OSSL_STATEM *st = &s->statem;
- if (st->state == MSG_FLOW_UNINITED || st->state == MSG_FLOW_RENEGOTIATE)
+ if (st->state == MSG_FLOW_UNINITED)
return 0;
if (!s->s3->in_read_app_data || (s->s3->total_renegotiations == 0))
diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h
index 6765c304a9..021d2d06ce 100644
--- a/ssl/statem/statem.h
+++ b/ssl/statem/statem.h
@@ -46,8 +46,6 @@ typedef enum {
MSG_FLOW_UNINITED,
/* A permanent error with this connection */
MSG_FLOW_ERROR,
- /* We are about to renegotiate */
- MSG_FLOW_RENEGOTIATE,
/* We are reading messages */
MSG_FLOW_READING,
/* We are writing messages */
@@ -92,6 +90,11 @@ struct ossl_statem_st {
int read_state_first_init;
/* true when we are actually in SSL_accept() or SSL_connect() */
int in_handshake;
+ /*
+ * True when are processing a "real" handshake that needs cleaning up (not
+ * just a HelloRequest or similar).
+ */
+ int cleanuphand;
/* Should we skip the CertificateVerify message? */
unsigned int no_cert_verify;
int use_timer;
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 90e4df6fda..8a308f82bc 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -351,6 +351,13 @@ int ossl_statem_client_read_transition(SSL *s, int mt)
return 1;
}
break;
+
+ case TLS_ST_OK:
+ if (mt == SSL3_MT_HELLO_REQUEST) {
+ st->hand_state = TLS_ST_CR_HELLO_REQ;
+ return 1;
+ }
+ break;
}
err:
@@ -428,6 +435,13 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s)
return WRITE_TRAN_ERROR;
case TLS_ST_OK:
+ if (!s->renegotiate) {
+ /*
+ * We haven't requested a renegotiation ourselves so we must have
+ * received a message from the server. Better read it.
+ */
+ return WRITE_TRAN_FINISHED;
+ }
/* Renegotiation - fall through */
case TLS_ST_BEFORE:
st->hand_state = TLS_ST_CW_CLNT_HELLO;
@@ -515,6 +529,23 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s)
ossl_statem_set_in_init(s, 0);
return WRITE_TRAN_CONTINUE;
}
+
+ case TLS_ST_CR_HELLO_REQ:
+ /*
+ * If we can renegotiate now then do so, otherwise wait for a more
+ * convenient time.
+ */
+ if (ssl3_renegotiate_check(s, 1)) {
+ if (!tls_setup_handshake(s)) {
+ ossl_statem_set_error(s);
+ return WRITE_TRAN_ERROR;
+ }
+ st->hand_state = TLS_ST_CW_CLNT_HELLO;
+ return WRITE_TRAN_CONTINUE;
+ }
+ st->hand_state = TLS_ST_OK;
+ ossl_statem_set_in_init(s, 0);
+ return WRITE_TRAN_CONTINUE;
}
}
@@ -819,6 +850,9 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL *s, PACKET *pkt)
case TLS_ST_CR_FINISHED:
return tls_process_finished(s, pkt);
+ case TLS_ST_CR_HELLO_REQ:
+ return tls_process_hello_req(s, pkt);
+
case TLS_ST_CR_ENCRYPTED_EXTENSIONS:
return tls_process_encrypted_extensions(s, pkt);
}
@@ -893,6 +927,9 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
}
/* else use the pre-loaded session */
+ /* This is a real handshake so make sure we clean it up at the end */
+ s->statem.cleanuphand = 1;
+
p = s->s3->client_random;
/*
@@ -3112,6 +3149,30 @@ int tls_construct_next_proto(SSL *s, WPACKET *pkt)
}
#endif
+MSG_PROCESS_RETURN tls_process_hello_req(SSL *s, PACKET *pkt)
+{
+ if (PACKET_remaining(pkt) > 0) {
+ /* should contain no data */
+ SSLerr(SSL_F_TLS_PROCESS_HELLO_REQ, SSL_R_LENGTH_MISMATCH);
+ ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ ossl_statem_set_error(s);
+ return MSG_PROCESS_ERROR;
+ }
+
+ /*
+ * This is a historical discrepancy maintained for compatibility
+ * reasons. If a TLS client receives a HelloRequest it will attempt
+ * an abbreviated handshake. However if a DTLS client receives a
+ * HelloRequest it will do a full handshake.
+ */
+ if (SSL_IS_DTLS(s))
+ SSL_renegotiate(s);
+ else
+ SSL_renegotiate_abbreviated(s);
+
+ return MSG_PROCESS_FINISHED_READING;
+}
+
static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt)
{
int al = SSL_AD_INTERNAL_ERROR;
diff --git a/ssl/statem/statem_dtls.c b/ssl/statem/statem_dtls.c
index 1c1758b464..1bc82d1625 100644
--- a/ssl/statem/statem_dtls.c
+++ b/ssl/statem/statem_dtls.c
@@ -788,8 +788,9 @@ static int dtls_get_reassembled_message(SSL *s, int *errtype, size_t *len)
return 0;
}
- if (!s->server && s->d1->r_msg_hdr.frag_off == 0 &&
- wire[0] == SSL3_MT_HELLO_REQUEST) {
+ if (!s->server && s->d1->r_msg_hdr.frag_off == 0
+ && s->statem.hand_state != TLS_ST_OK
+ && wire[0] == SSL3_MT_HELLO_REQUEST) {
/*
* The server may always send 'Hello Request' messages -- we are
* doing a handshake anyway now, so ignore them if their format is
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 905a2cc460..c81c012488 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -72,6 +72,49 @@ int tls_close_construct_packet(SSL *s, WPACKET *pkt, int htype)
return 1;
}
+int tls_setup_handshake(SSL *s) {
+ if (!ssl3_init_finished_mac(s))
+ return 0;
+
+ if (s->server) {
+ if (SSL_IS_FIRST_HANDSHAKE(s)) {
+ s->ctx->stats.sess_accept++;
+ } else if (!s->s3->send_connection_binding &&
+ !(s->options &
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) {
+ /*
+ * Server attempting to renegotiate with client that doesn't
+ * support secure renegotiation.
+ */
+ SSLerr(SSL_F_TLS_SETUP_HANDSHAKE,
+ SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
+ ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+ return 0;
+ } else {
+ s->ctx->stats.sess_accept_renegotiate++;
+
+ s->s3->tmp.cert_request = 0;
+ }
+ } else {
+ if (SSL_IS_FIRST_HANDSHAKE(s))
+ s->ctx->stats.sess_connect++;
+ else
+ s->ctx->stats.sess_connect_renegotiate++;
+
+ /* mark client_random uninitialized */
+ memset(s->s3->client_random, 0, sizeof(s->s3->client_random));
+ s->hit = 0;
+
+ s->s3->tmp.cert_req = 0;
+
+ if (SSL_IS_DTLS(s)) {
+ s->statem.use_timer = 1;
+ }
+ }
+
+ return 1;
+}
+
/*
* Size of the to-be-signed TLS13 data, without the hash size itself:
* 64 bytes of value 32, 33 context bytes, 1 byte separator
@@ -807,10 +850,11 @@ WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst)
s->init_num = 0;
- if (!s->server || s->renegotiate == 2) {
+ if (s->statem.cleanuphand) {
/* skipped if we just sent a HelloRequest */
s->renegotiate = 0;
s->new_session = 0;
+ s->statem.cleanuphand = 0;
if (s->server) {
ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
@@ -891,7 +935,8 @@ int tls_get_message_header(SSL *s, int *mt)
skip_message = 0;
if (!s->server)
- if (p[0] == SSL3_MT_HELLO_REQUEST)
+ if (s->statem.hand_state != TLS_ST_OK
+ && p[0] == SSL3_MT_HELLO_REQUEST)
/*
* The server may always send 'Hello Request' messages --
* we are doing a handshake anyway now, so ignore them if
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index b52de70f0f..5e9f72dd69 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -132,6 +132,7 @@ __owur int ssl3_check_cert_and_algorithm(SSL *s);
#ifndef OPENSSL_NO_NEXTPROTONEG
__owur int tls_construct_next_proto(SSL *s, WPACKET