From: Matt Caswell Date: Mon, 7 Sep 2015 21:00:36 +0000 (+0100) Subject: Implement DTLS client move to new state machine X-Git-Tag: OpenSSL_1_1_0-pre1~373 X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=473483d42db5d176946323ac390fcd3c80ea40e6 Implement DTLS client move to new state machine Move all DTLS client side processing into the new state machine code. A subsequent commit will clean up the old dead code. Reviewed-by: Tim Hudson Reviewed-by: Richard Levitte --- diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index cb60258035..b89b2a664c 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1928,6 +1928,7 @@ void ERR_load_SSL_strings(void); # define SSL_F_DTLS1_SEND_SERVER_HELLO 266 # 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_GET_REASSEMBLED_MESSAGE 370 # define SSL_F_READ_STATE_MACHINE 352 # define SSL_F_SSL3_ACCEPT 128 diff --git a/ssl/d1_both.c b/ssl/d1_both.c index 9d20dfe609..a9690380c8 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -582,6 +582,7 @@ int dtls_get_message(SSL *s, int *mt, unsigned long *len) /* * This isn't a real handshake message so skip the processing below. */ + *len = (unsigned long)tmplen; return 1; } @@ -1102,6 +1103,19 @@ static int dtls_get_reassembled_message(SSL *s, long *len) return 0; } + +int dtls1_send_change_cipher_spec(SSL *s, int a, int b) +{ + if (s->state == a) { + if (dtls_construct_change_cipher_spec(s) == 0) + return -1; + } + + /* SSL3_ST_CW_CHANGE_B */ + return (dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC)); +} + + /*- * for these 2 messages, we need to * ssl->enc_read_ctx re-init @@ -1111,39 +1125,55 @@ static int dtls_get_reassembled_message(SSL *s, long *len) * ssl->session->read_compression assign * ssl->session->read_hash assign */ -int dtls1_send_change_cipher_spec(SSL *s, int a, int b) +int dtls_construct_change_cipher_spec(SSL *s) { unsigned char *p; - if (s->state == a) { - p = (unsigned char *)s->init_buf->data; - *p++ = SSL3_MT_CCS; - s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; - s->init_num = DTLS1_CCS_HEADER_LENGTH; - - if (s->version == DTLS1_BAD_VER) { - s->d1->next_handshake_write_seq++; - s2n(s->d1->handshake_write_seq, p); - s->init_num += 2; - } + p = (unsigned char *)s->init_buf->data; + *p++ = SSL3_MT_CCS; + s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; + s->init_num = DTLS1_CCS_HEADER_LENGTH; - s->init_off = 0; + if (s->version == DTLS1_BAD_VER) { + s->d1->next_handshake_write_seq++; + s2n(s->d1->handshake_write_seq, p); + s->init_num += 2; + } - dtls1_set_message_header_int(s, SSL3_MT_CCS, 0, - s->d1->handshake_write_seq, 0, 0); + s->init_off = 0; - /* buffer the message to handle re-xmits */ - if (!dtls1_buffer_message(s, 1)) { - SSLerr(SSL_F_DTLS1_SEND_CHANGE_CIPHER_SPEC, ERR_R_INTERNAL_ERROR); - return -1; - } + dtls1_set_message_header_int(s, SSL3_MT_CCS, 0, + s->d1->handshake_write_seq, 0, 0); - s->state = b; + /* buffer the message to handle re-xmits */ + if (!dtls1_buffer_message(s, 1)) { + SSLerr(SSL_F_DTLS_CONSTRUCT_CHANGE_CIPHER_SPEC, ERR_R_INTERNAL_ERROR); + return 0; } - /* SSL3_ST_CW_CHANGE_B */ - return (dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC)); + return 1; +} + +#ifndef OPENSSL_NO_SCTP +enum WORK_STATE dtls_wait_for_dry(SSL *s) +{ + int ret; + + /* read app data until dry event */ + ret = BIO_dgram_sctp_wait_for_dry(SSL_get_wbio(s)); + if (ret < 0) + return WORK_ERROR; + + if (ret == 0) { + 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)); + return WORK_MORE_A; + } + return WORK_FINISHED_CONTINUE; } +#endif int dtls1_read_failed(SSL *s, int code) { diff --git a/ssl/d1_clnt.c b/ssl/d1_clnt.c index 083333eedb..ef8f25bbe6 100644 --- a/ssl/d1_clnt.c +++ b/ssl/d1_clnt.c @@ -126,7 +126,9 @@ #endif static const SSL_METHOD *dtls1_get_client_method(int ver); +#if 0 static int dtls1_get_hello_verify(SSL *s); +#endif static const SSL_METHOD *dtls1_get_client_method(int ver) { @@ -156,6 +158,7 @@ IMPLEMENT_dtls1_meth_func(DTLS1_VERSION, dtls1_connect, dtls1_get_client_method, DTLSv1_2_enc_data) +#if 0 int dtls1_connect(SSL *s) { BUF_MEM *buf = NULL; @@ -785,13 +788,16 @@ int dtls1_connect(SSL *s) cb(s, SSL_CB_CONNECT_EXIT, ret); return (ret); } +#endif +#if 0 static int dtls1_get_hello_verify(SSL *s) { int n, al, ok = 0; unsigned char *data; unsigned int cookie_len; + /* TODO: CHECK first_packet handling!!! */ s->first_packet = 1; n = s->method->ssl_get_message(s, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A, @@ -801,12 +807,14 @@ static int dtls1_get_hello_verify(SSL *s) if (!ok) return ((int)n); +} +#endif - if (s->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) { - s->d1->send_cookie = 0; - s->s3->tmp.reuse_message = 1; - return (1); - } +enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, unsigned long n) +{ + int al; + unsigned char *data; + unsigned int cookie_len; data = (unsigned char *)s->init_msg; data += 2; @@ -820,11 +828,9 @@ static int dtls1_get_hello_verify(SSL *s) memcpy(s->d1->cookie, data, cookie_len); s->d1->cookie_len = cookie_len; - s->d1->send_cookie = 1; - return 1; - + return MSG_PROCESS_FINISHED_READING; f_err: ssl3_send_alert(s, SSL3_AL_FATAL, al); - s->state = SSL_ST_ERR; - return -1; + statem_set_error(s); + return MSG_PROCESS_ERROR; } diff --git a/ssl/s3_both.c b/ssl/s3_both.c index 1829feb243..2afde72a63 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c @@ -461,8 +461,15 @@ enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst) /* clean a few things up */ ssl3_cleanup_key_block(s); - BUF_MEM_free(s->init_buf); - s->init_buf = NULL; + + if (!SSL_IS_DTLS(s)) { + /* + * We don't do this in DTLS because we may still need the init_buf + * in case there are any unexpected retransmits + */ + BUF_MEM_free(s->init_buf); + s->init_buf = NULL; + } ssl_free_wbio_buffer(s); diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 9a7382525b..715f08bbfa 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -465,6 +465,7 @@ int tls_construct_client_hello(SSL *s) return 0; } +#if 0 int ssl3_get_server_hello(SSL *s) { int ok, al; @@ -509,6 +510,7 @@ int ssl3_get_server_hello(SSL *s) s->state = SSL_ST_ERR; return (-1); } +#endif enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, unsigned long n) { @@ -1921,7 +1923,14 @@ enum MSG_PROCESS_RETURN tls_process_server_done(SSL *s, unsigned long n) } #endif - return MSG_PROCESS_FINISHED_READING; +#ifndef OPENSSL_NO_SCTP + /* Only applies to renegotiation */ + if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s)) + && s->renegotiate != 0) + return MSG_PROCESS_CONTINUE_PROCESSING; + else +#endif + return MSG_PROCESS_FINISHED_READING; } int ssl3_send_client_key_exchange(SSL *s) @@ -2554,6 +2563,29 @@ int tls_client_key_exchange_post_work(SSL *s) SSLerr(SSL_F_TLS_CLIENT_KEY_EXCHANGE_POST_WORK, ERR_R_INTERNAL_ERROR); goto err; } + +#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 (SSL_export_keying_material(s, sctpauthkey, + sizeof(sctpauthkey), labelbuffer, + sizeof(labelbuffer), NULL, 0, 0) <= 0) + goto err; + + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); + } +#endif + return 1; err: OPENSSL_clear_free(pms, pmslen); diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 87a31a657a..1615ced715 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -112,6 +112,8 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE), "dtls1_send_server_key_exchange"}, {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_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 018b8ff028..feed7e42a0 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1555,7 +1555,6 @@ typedef struct hm_fragment_st { } hm_fragment; typedef struct dtls1_state_st { - unsigned int send_cookie; unsigned char cookie[DTLS1_COOKIE_LENGTH]; unsigned int cookie_len; @@ -2062,6 +2061,7 @@ __owur enum MSG_PROCESS_RETURN tls_process_finished(SSL *s, unsigned long n); __owur int ssl3_setup_key_block(SSL *s); __owur int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b); __owur int tls_construct_change_cipher_spec(SSL *s); +__owur int dtls_construct_change_cipher_spec(SSL *s); __owur int ssl3_change_cipher_state(SSL *s, int which); void ssl3_cleanup_key_block(SSL *s); __owur int ssl3_do_write(SSL *s, int type); @@ -2075,6 +2075,7 @@ __owur int tls_get_message_body(SSL *s, unsigned long *len); __owur int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen); __owur int tls_construct_finished(SSL *s, const char *sender, int slen); __owur enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst); +__owur enum WORK_STATE dtls_wait_for_dry(SSL *s); __owur int ssl3_num_ciphers(void); __owur const SSL_CIPHER *ssl3_get_cipher(unsigned int u); int ssl3_renegotiate(SSL *ssl); @@ -2194,6 +2195,8 @@ __owur int ssl3_check_cert_and_algorithm(SSL *s); __owur int ssl3_send_next_proto(SSL *s); __owur int tls_construct_next_proto(SSL *s); # endif +__owur enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, + unsigned long n); int dtls1_client_hello(SSL *s); diff --git a/ssl/statem.c b/ssl/statem.c index f1df6a30da..7f2176f80d 100644 --- a/ssl/statem.c +++ b/ssl/statem.c @@ -151,6 +151,11 @@ int ssl3_connect(SSL *s) { return state_machine(s, 0); } +int dtls1_connect(SSL *s) +{ + return state_machine(s, 0); +} + /* * 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 @@ -207,6 +212,17 @@ static int state_machine(SSL *s, int server) { return -1; } +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s)) { + /* + * Notify SCTP BIO socket to enter handshake mode and prevent stream + * identifier other than 0. Will be ignored if no SCTP is used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, + s->in_handshake, NULL); + } +#endif + #ifndef OPENSSL_NO_HEARTBEATS /* * If we're awaiting a HeartbeatResponse, pretend we already got and @@ -259,10 +275,12 @@ static int state_machine(SSL *s, int server) { } } - if (s->version != TLS_ANY_VERSION && - !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { - SSLerr(SSL_F_STATE_MACHINE, SSL_R_VERSION_TOO_LOW); - goto end; + if (!SSL_IS_DTLS(s)) { + if (s->version != TLS_ANY_VERSION && + !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { + SSLerr(SSL_F_STATE_MACHINE, SSL_R_VERSION_TOO_LOW); + goto end; + } } if (server) @@ -380,6 +398,18 @@ static int state_machine(SSL *s, int server) { end: s->in_handshake--; + +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s)) { + /* + * Notify SCTP BIO socket to leave handshake mode and allow stream + * identifier other than 0. Will be ignored if no SCTP is used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, + s->in_handshake, NULL); + } +#endif + BUF_MEM_free(buf); if (cb != NULL) { if (server) @@ -765,14 +795,11 @@ int statem_app_data_allowed(SSL *s) /* * This is the old check for code still using the old state machine. This - * will be removed by later commits + * will be removed by a later commit */ - if (((s->state & SSL_ST_CONNECT) && SSL_IS_DTLS(s) && - (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && - (s->state <= SSL3_ST_CR_SRVR_HELLO_A)) || - ((s->state & SSL_ST_ACCEPT) && + if ((s->state & SSL_ST_ACCEPT) && (s->state <= SSL3_ST_SW_HELLO_REQ_A) && - (s->state >= SSL3_ST_SR_CLNT_HELLO_A)) + (s->state >= SSL3_ST_SR_CLNT_HELLO_A) ) return 1; @@ -883,7 +910,10 @@ static int client_read_transition(SSL *s, int mt) return 1; } } else { - if (!(s->s3->tmp.new_cipher->algorithm_auth + if (SSL_IS_DTLS(s) && mt == DTLS1_MT_HELLO_VERIFY_REQUEST) { + st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST; + return 1; + } else if (!(s->s3->tmp.new_cipher->algorithm_auth & (SSL_aNULL | SSL_aSRP | SSL_aPSK))) { if (mt == SSL3_MT_CERTIFICATE) { st->hand_state = TLS_ST_CR_CERT; @@ -1015,6 +1045,10 @@ static enum WRITE_TRAN client_write_transition(SSL *s) */ return WRITE_TRAN_FINISHED; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + st->hand_state = TLS_ST_CW_CLNT_HELLO; + return WRITE_TRAN_CONTINUE; + case TLS_ST_CR_SRVR_DONE: if (s->s3->tmp.cert_req) st->hand_state = TLS_ST_CW_CERT; @@ -1055,7 +1089,7 @@ static enum WRITE_TRAN client_write_transition(SSL *s) #if defined(OPENSSL_NO_NEXTPROTONEG) st->hand_state = TLS_ST_CW_FINISHED; #else - if (s->s3->next_proto_neg_seen) + if (!SSL_IS_DTLS(s) && s->s3->next_proto_neg_seen) st->hand_state = TLS_ST_CW_NEXT_PROTO; else st->hand_state = TLS_ST_CW_FINISHED; @@ -1106,11 +1140,31 @@ static enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst) switch(st->hand_state) { case TLS_ST_CW_CLNT_HELLO: s->shutdown = 0; + if (SSL_IS_DTLS(s)) { + /* every DTLS ClientHello resets Finished MAC */ + ssl3_init_finished_mac(s); + } break; case TLS_ST_CW_CERT: return tls_prepare_client_certificate(s, wst); + case TLS_ST_CW_CHANGE: + if (SSL_IS_DTLS(s)) { + if (s->hit) { + /* + * We're into the last flight so we don't retransmit these + * messages unless we need to. + */ + st->use_timer = 0; + } +#ifndef OPENSSL_NO_SCTP + if (BIO_dgram_is_sctp(SSL_get_wbio(s))) + return dtls_wait_for_dry(s); +#endif + } + return WORK_FINISHED_CONTINUE; + case TLS_ST_OK: return tls_finish_handshake(s, wst); @@ -1134,9 +1188,24 @@ static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst) switch(st->hand_state) { case TLS_ST_CW_CLNT_HELLO: - /* turn on buffering for the next lot of output */ - if (s->bbio != s->wbio) - s->wbio = BIO_push(s->bbio, s->wbio); + if (SSL_IS_DTLS(s) && s->d1->cookie_len > 0 && statem_flush(s) != 1) + return WORK_MORE_A; +#ifndef OPENSSL_NO_SCTP + /* Disable buffering for SCTP */ + if (!SSL_IS_DTLS(s) || !BIO_dgram_is_sctp(SSL_get_wbio(s))) { +#endif + /* + * turn on buffering for the next lot of output + */ + if (s->bbio != s->wbio) + s->wbio = BIO_push(s->bbio, s->wbio); +#ifndef OPENSSL_NO_SCTP + } +#endif + if (SSL_IS_DTLS(s)) { + /* Treat the next message as the first packet */ + s->first_packet = 1; + } break; case TLS_ST_CW_KEY_EXCH: @@ -1179,7 +1248,7 @@ static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst) case TLS_ST_CW_FINISHED: #ifndef OPENSSL_NO_SCTP - if (SSL_IS_DTLS(s) && s->hit == 0) { + if (wst == WORK_MORE_A && SSL_IS_DTLS(s) && s->hit == 0) { /* * Change to new shared key of SCTP-Auth, will be ignored if * no SCTP used. @@ -1228,7 +1297,10 @@ static int client_construct_message(SSL *s) return tls_construct_client_verify(s); case TLS_ST_CW_CHANGE: - return tls_construct_change_cipher_spec(s); + if (SSL_IS_DTLS(s)) + return dtls_construct_change_cipher_spec(s); + else + return tls_construct_change_cipher_spec(s); #if !defined(OPENSSL_NO_NEXTPROTONEG) case TLS_ST_CW_NEXT_PROTO: @@ -1250,6 +1322,7 @@ static int client_construct_message(SSL *s) } /* The spec allows for a longer length than this, but we limit it */ +#define HELLO_VERIFY_REQUEST_MAX_LENGTH 258 #define SERVER_HELLO_MAX_LENGTH 20000 #define SERVER_KEY_EXCH_MAX_LENGTH 102400 #define SERVER_HELLO_DONE_MAX_LENGTH 0 @@ -1269,6 +1342,9 @@ static unsigned long client_max_message_size(SSL *s) case TLS_ST_CR_SRVR_HELLO: return SERVER_HELLO_MAX_LENGTH; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + return HELLO_VERIFY_REQUEST_MAX_LENGTH; + case TLS_ST_CR_CERT: return s->max_cert_list; @@ -1312,6 +1388,9 @@ static enum MSG_PROCESS_RETURN client_process_message(SSL *s, unsigned long len) case TLS_ST_CR_SRVR_HELLO: return tls_process_server_hello(s, len); + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + return dtls_process_hello_verify(s, len); + case TLS_ST_CR_CERT: return tls_process_server_certificate(s, len);