From c130dd8ea4d09cb708aac9e41bd25c2f5fa7ea38 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Fri, 4 Sep 2015 13:51:49 +0100 Subject: [PATCH] Move server side DTLS to new state machine Implement all of the necessary changes to make DTLS on the server work with the new state machine code. Reviewed-by: Tim Hudson Reviewed-by: Richard Levitte --- include/openssl/ssl.h | 1 + ssl/d1_srvr.c | 49 +++++++------- ssl/s3_srvr.c | 56 ++++++++++------ ssl/ssl_err.c | 2 + ssl/ssl_locl.h | 1 + ssl/statem.c | 147 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 211 insertions(+), 45 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 75bca7cdb4..de3c3beb24 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1929,6 +1929,7 @@ void ERR_load_SSL_strings(void); # define SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE 267 # define SSL_F_DTLS1_WRITE_APP_DATA_BYTES 268 # define SSL_F_DTLS_CONSTRUCT_CHANGE_CIPHER_SPEC 371 +# define SSL_F_DTLS_CONSTRUCT_HELLO_VERIFY_REQUEST 385 # define SSL_F_DTLS_GET_REASSEMBLED_MESSAGE 370 # define SSL_F_READ_STATE_MACHINE 352 # define SSL_F_SSL3_ACCEPT 128 diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c index 22dbbfe3d3..02a944d86e 100644 --- a/ssl/d1_srvr.c +++ b/ssl/d1_srvr.c @@ -127,7 +127,6 @@ #endif static const SSL_METHOD *dtls1_get_server_method(int ver); -static int dtls1_send_hello_verify_request(SSL *s); static const SSL_METHOD *dtls1_get_server_method(int ver) { @@ -157,6 +156,7 @@ IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, ssl_undefined_function, dtls1_get_server_method, DTLSv1_2_enc_data) +#if 0 int dtls1_accept(SSL *s) { BUF_MEM *buf; @@ -857,6 +857,7 @@ int dtls1_accept(SSL *s) cb(s, SSL_CB_ACCEPT_EXIT, ret); return (ret); } +#endif unsigned int dtls1_raw_hello_verify_request(unsigned char *buf, unsigned char *cookie, @@ -879,37 +880,33 @@ unsigned int dtls1_raw_hello_verify_request(unsigned char *buf, } -int dtls1_send_hello_verify_request(SSL *s) +int dtls_construct_hello_verify_request(SSL *s) { unsigned int len; unsigned char *buf; - if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A) { - buf = (unsigned char *)s->init_buf->data; - - if (s->ctx->app_gen_cookie_cb == NULL || - s->ctx->app_gen_cookie_cb(s, s->d1->cookie, - &(s->d1->cookie_len)) == 0 || - s->d1->cookie_len > 255) { - SSLerr(SSL_F_DTLS1_SEND_HELLO_VERIFY_REQUEST, - SSL_R_COOKIE_GEN_CALLBACK_FAILURE); - s->state = SSL_ST_ERR; - return 0; - } + buf = (unsigned char *)s->init_buf->data; - len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH], - s->d1->cookie, s->d1->cookie_len); + if (s->ctx->app_gen_cookie_cb == NULL || + s->ctx->app_gen_cookie_cb(s, s->d1->cookie, + &(s->d1->cookie_len)) == 0 || + s->d1->cookie_len > 255) { + SSLerr(SSL_F_DTLS1_SEND_HELLO_VERIFY_REQUEST, + SSL_R_COOKIE_GEN_CALLBACK_FAILURE); + statem_set_error(s); + return 0; + } - dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0, - len); - len += DTLS1_HM_HEADER_LENGTH; + len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH], + s->d1->cookie, s->d1->cookie_len); - s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B; - /* number of bytes to write */ - s->init_num = len; - s->init_off = 0; - } + dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0, + len); + len += DTLS1_HM_HEADER_LENGTH; + + /* number of bytes to write */ + s->init_num = len; + s->init_off = 0; - /* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */ - return (dtls1_do_write(s, SSL3_RT_HANDSHAKE)); + return 1; } diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 992df70f4c..d390f149a2 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -2876,27 +2876,47 @@ enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, long n) enum WORK_STATE tls_post_process_client_key_exchange(SSL *s, enum WORK_STATE wst) { - #ifndef OPENSSL_NO_SCTP - if (SSL_IS_DTLS(s)) { - unsigned char sctpauthkey[64]; - char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)]; - /* - * Add new shared key for SCTP-Auth, will be ignored if no SCTP - * used. - */ - snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL), - DTLS1_SCTP_AUTH_LABEL); + if (wst == WORK_MORE_A) { + if (SSL_IS_DTLS(s)) { + unsigned char sctpauthkey[64]; + char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)]; + /* + * Add new shared key for SCTP-Auth, will be ignored if no SCTP + * used. + */ + snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL), + DTLS1_SCTP_AUTH_LABEL); + + if (SSL_export_keying_material(s, sctpauthkey, + sizeof(sctpauthkey), labelbuffer, + sizeof(labelbuffer), NULL, 0, 0) <= 0) { + statem_set_error(s); + return WORK_ERROR;; + } - if (SSL_export_keying_material(s, sctpauthkey, - sizeof(sctpauthkey), labelbuffer, - sizeof(labelbuffer), NULL, 0, 0) <= 0) { - statem_set_error(s); - return WORK_ERROR;; + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); } + wst = WORK_MORE_B; + } - BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, - sizeof(sctpauthkey), sctpauthkey); + if ((wst == WORK_MORE_B) + /* Is this SCTP? */ + && BIO_dgram_is_sctp(SSL_get_wbio(s)) + /* Are we renegotiating? */ + && s->renegotiate + /* Are we going to skip the CertificateVerify? */ + && (s->session->peer == NULL || s->no_cert_verify) + && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) { + s->s3->in_read_app_data = 2; + s->rwstate = SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + statem_set_sctp_read_sock(s, 1); + return WORK_MORE_B; + } else { + statem_set_sctp_read_sock(s, 0); } #endif @@ -3169,7 +3189,7 @@ enum MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, long n) goto f_err; } - ret = MSG_PROCESS_CONTINUE_READING; + ret = MSG_PROCESS_CONTINUE_PROCESSING; if (0) { f_err: ssl3_send_alert(s, SSL3_AL_FATAL, al); diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index fe4201ba66..faee32fa5f 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -114,6 +114,8 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_DTLS1_WRITE_APP_DATA_BYTES), "dtls1_write_app_data_bytes"}, {ERR_FUNC(SSL_F_DTLS_CONSTRUCT_CHANGE_CIPHER_SPEC), "dtls_construct_change_cipher_spec"}, + {ERR_FUNC(SSL_F_DTLS_CONSTRUCT_HELLO_VERIFY_REQUEST), + "dtls_construct_hello_verify_request"}, {ERR_FUNC(SSL_F_DTLS_GET_REASSEMBLED_MESSAGE), "DTLS_GET_REASSEMBLED_MESSAGE"}, {ERR_FUNC(SSL_F_READ_STATE_MACHINE), "READ_STATE_MACHINE"}, diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 425d3956a0..891030c7c3 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -2201,6 +2201,7 @@ __owur int tls_construct_server_hello(SSL *s); __owur int ssl3_send_hello_request(SSL *s); __owur int tls_construct_hello_request(SSL *s); __owur int ssl3_send_server_key_exchange(SSL *s); +__owur int dtls_construct_hello_verify_request(SSL *s); __owur int tls_construct_server_key_exchange(SSL *s); __owur int ssl3_send_certificate_request(SSL *s); __owur int tls_construct_certificate_request(SSL *s); diff --git a/ssl/statem.c b/ssl/statem.c index e2cf11e114..2fe3df4b7e 100644 --- a/ssl/statem.c +++ b/ssl/statem.c @@ -171,6 +171,11 @@ int ssl3_accept(SSL *s) return state_machine(s, 1); } +int dtls1_accept(SSL *s) +{ + return state_machine(s, 1); +} + /* * 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 @@ -1490,6 +1495,7 @@ static int server_read_transition(SSL *s, int mt) switch(st->hand_state) { case TLS_ST_BEFORE: + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: if (mt == SSL3_MT_CLIENT_HELLO) { st->hand_state = TLS_ST_SR_CLNT_HELLO; return 1; @@ -1726,9 +1732,16 @@ static enum WRITE_TRAN server_write_transition(SSL *s) return WRITE_TRAN_CONTINUE; case TLS_ST_SR_CLNT_HELLO: - st->hand_state = TLS_ST_SW_SRVR_HELLO; + if (SSL_IS_DTLS(s) && !s->d1->cookie_verified + && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)) + st->hand_state = DTLS_ST_SW_HELLO_VERIFY_REQUEST; + else + st->hand_state = TLS_ST_SW_SRVR_HELLO; return WRITE_TRAN_CONTINUE; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + return WRITE_TRAN_FINISHED; + case TLS_ST_SW_SRVR_HELLO: if (s->hit) { if (s->tlsext_ticket_expected) @@ -1826,6 +1839,44 @@ static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst) switch(st->hand_state) { case TLS_ST_SW_HELLO_REQ: s->shutdown = 0; + if (SSL_IS_DTLS(s)) + dtls1_clear_record_buffer(s); + break; + + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + s->shutdown = 0; + if (SSL_IS_DTLS(s)) { + dtls1_clear_record_buffer(s); + /* We don't buffer this message so don't use the timer */ + st->use_timer = 0; + } + break; + + case TLS_ST_SW_SRVR_HELLO: + if (SSL_IS_DTLS(s)) { + /* + * Messages we write from now on should be bufferred and + * retransmitted if necessary, so we need to use the timer now + */ + st->use_timer = 1; + } + break; + + case TLS_ST_SW_SRVR_DONE: +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))) + return dtls_wait_for_dry(s); +#endif + return WORK_FINISHED_CONTINUE; + + case TLS_ST_SW_SESSION_TICKET: + if (SSL_IS_DTLS(s)) { + /* + * We're into the last flight. We don't retransmit the last flight + * unless we need to, so we don't use the timer + */ + st->use_timer = 0; + } break; case TLS_ST_SW_CHANGE: @@ -1834,6 +1885,15 @@ static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst) statem_set_error(s); return WORK_ERROR; } + if (SSL_IS_DTLS(s)) { + /* + * We're into the last flight. We don't retransmit the last flight + * unless we need to, so we don't use the timer. This might have + * already been set to 0 if we sent a NewSessionTicket message, + * but we'll set it again here in case we didn't. + */ + st->use_timer = 0; + } return WORK_FINISHED_CONTINUE; case TLS_ST_OK: @@ -1864,12 +1924,64 @@ static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst) ssl3_init_finished_mac(s); break; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + if (statem_flush(s) != 1) + return WORK_MORE_A; + /* HelloVerifyRequest resets Finished MAC */ + if (s->version != DTLS1_BAD_VER) + ssl3_init_finished_mac(s); + /* + * The next message should be another ClientHello which we need to + * treat like it was the first packet + */ + s->first_packet = 1; + break; + + case TLS_ST_SW_SRVR_HELLO: +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && s->hit) { + unsigned char sctpauthkey[64]; + char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)]; + + /* + * Add new shared key for SCTP-Auth, will be ignored if no + * SCTP used. + */ + snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL), + DTLS1_SCTP_AUTH_LABEL); + + if (SSL_export_keying_material(s, sctpauthkey, + sizeof(sctpauthkey), labelbuffer, + sizeof(labelbuffer), NULL, 0, 0) <= 0) { + statem_set_error(s); + return WORK_ERROR; + } + + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); + } +#endif + break; + case TLS_ST_SW_CHANGE: +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && !s->hit) { + /* + * Change to new shared key of SCTP-Auth, will be ignored if + * no SCTP used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY, + 0, NULL); + } +#endif if (!s->method->ssl3_enc->change_cipher_state(s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) { statem_set_error(s); return WORK_ERROR; } + + if (SSL_IS_DTLS(s)) + dtls1_reset_seq_numbers(s, SSL3_CC_WRITE); break; case TLS_ST_SW_SRVR_DONE: @@ -1880,6 +1992,16 @@ static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst) case TLS_ST_SW_FINISHED: if (statem_flush(s) != 1) return WORK_MORE_A; +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && s->hit) { + /* + * Change to new shared key of SCTP-Auth, will be ignored if + * no SCTP used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY, + 0, NULL); + } +#endif break; default: @@ -1902,6 +2024,9 @@ static int server_construct_message(SSL *s) STATEM *st = &s->statem; switch(st->hand_state) { + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + return dtls_construct_hello_verify_request(s); + case TLS_ST_SW_HELLO_REQ: return tls_construct_hello_request(s); @@ -2045,6 +2170,26 @@ static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst) case TLS_ST_SR_KEY_EXCH: return tls_post_process_client_key_exchange(s, wst); + case TLS_ST_SR_CERT_VRFY: +#ifndef OPENSSL_NO_SCTP + if ( /* Is this SCTP? */ + BIO_dgram_is_sctp(SSL_get_wbio(s)) + /* Are we renegotiating? */ + && s->renegotiate + && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) { + s->s3->in_read_app_data = 2; + s->rwstate = SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + statem_set_sctp_read_sock(s, 1); + return WORK_MORE_A; + } else { + statem_set_sctp_read_sock(s, 0); + } +#endif + return WORK_FINISHED_CONTINUE; + + case TLS_ST_SR_FINISHED: if (s->hit) return tls_finish_handshake(s, wst); -- GitLab