From 8723588e1b9a13511ffd7b806c73293120bc1f44 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Mon, 7 Sep 2015 16:36:53 +0100 Subject: [PATCH] Implement Client TLS state machine This swaps the implementation of the client TLS state machine to use the new state machine code instead. Reviewed-by: Tim Hudson Reviewed-by: Richard Levitte --- ssl/d1_msg.c | 3 +- ssl/record/rec_layer_d1.c | 8 +- ssl/record/rec_layer_s3.c | 11 +- ssl/s3_both.c | 64 ++++ ssl/s3_clnt.c | 44 +++ ssl/ssl_locl.h | 10 +- ssl/statem.c | 682 +++++++++++++++++++++++++++++++++++++- 7 files changed, 788 insertions(+), 34 deletions(-) diff --git a/ssl/d1_msg.c b/ssl/d1_msg.c index 13bda46922..e35c9356a4 100644 --- a/ssl/d1_msg.c +++ b/ssl/d1_msg.c @@ -127,8 +127,7 @@ int dtls1_write_app_data_bytes(SSL *s, int type, const void *buf_, int len) */ if ((SSL_in_init(s) && !s->in_handshake) || (BIO_dgram_is_sctp(SSL_get_wbio(s)) && - (s->state == DTLS1_SCTP_ST_SR_READ_SOCK - || s->state == DTLS1_SCTP_ST_CR_READ_SOCK))) + statem_in_sctp_read_sock(s))) #else if (SSL_in_init(s) && !s->in_handshake) #endif diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c index d7d0093aec..d91de4d4a0 100644 --- a/ssl/record/rec_layer_d1.c +++ b/ssl/record/rec_layer_d1.c @@ -440,9 +440,8 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf, * SCTP. */ if ((!s->in_handshake && SSL_in_init(s)) || - (BIO_dgram_is_sctp(SSL_get_rbio(s)) && - (s->state == DTLS1_SCTP_ST_SR_READ_SOCK - || s->state == DTLS1_SCTP_ST_CR_READ_SOCK) + (BIO_dgram_is_sctp(SSL_get_rbio(s)) + && statem_in_sctp_read_sock(s) && s->s3->in_read_app_data != 2)) #else if (!s->in_handshake && SSL_in_init(s)) @@ -586,8 +585,7 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf, */ if (BIO_dgram_is_sctp(SSL_get_rbio(s)) && SSL3_RECORD_get_type(rr) == SSL3_RT_APPLICATION_DATA && - (s->state == DTLS1_SCTP_ST_SR_READ_SOCK - || s->state == DTLS1_SCTP_ST_CR_READ_SOCK)) { + statem_in_sctp_read_sock(s)) { s->rwstate = SSL_READING; BIO_clear_retry_flags(SSL_get_rbio(s)); BIO_set_retry_read(SSL_get_rbio(s)); diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index f5dd27aa7c..78e355a5d9 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -1450,16 +1450,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf, * application data at this point (session renegotiation not yet * started), we will indulge it. */ - if (s->s3->in_read_app_data && - (s->s3->total_renegotiations != 0) && - (((s->state & SSL_ST_CONNECT) && - (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && - (s->state <= SSL3_ST_CR_SRVR_HELLO_A) - ) || ((s->state & SSL_ST_ACCEPT) && - (s->state <= SSL3_ST_SW_HELLO_REQ_A) && - (s->state >= SSL3_ST_SR_CLNT_HELLO_A) - ) - )) { + if (statem_app_data_allowed(s)) { s->s3->in_read_app_data = 2; return (-1); } else { diff --git a/ssl/s3_both.c b/ssl/s3_both.c index e9ab4a6392..1829feb243 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c @@ -446,6 +446,70 @@ unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) return l + SSL_HM_HEADER_LENGTH(s); } +enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst) +{ + void (*cb) (const SSL *ssl, int type, int val) = NULL; + +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))) { + enum WORK_STATE ret; + ret = dtls_wait_for_dry(s); + if (ret != WORK_FINISHED_CONTINUE) + return ret; + } +#endif + + /* clean a few things up */ + ssl3_cleanup_key_block(s); + BUF_MEM_free(s->init_buf); + s->init_buf = NULL; + + ssl_free_wbio_buffer(s); + + s->init_num = 0; + + if (!s->server || s->renegotiate == 2) { + /* skipped if we just sent a HelloRequest */ + s->renegotiate = 0; + s->new_session = 0; + + if (s->server) { + s->renegotiate = 0; + s->new_session = 0; + + ssl_update_cache(s, SSL_SESS_CACHE_SERVER); + + s->ctx->stats.sess_accept_good++; + s->handshake_func = ssl3_accept; + } else { + ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); + if (s->hit) + s->ctx->stats.sess_hit++; + + s->handshake_func = ssl3_connect; + s->ctx->stats.sess_connect_good++; + } + + if (s->info_callback != NULL) + cb = s->info_callback; + else if (s->ctx->info_callback != NULL) + cb = s->ctx->info_callback; + + if (cb != NULL) + cb(s, SSL_CB_HANDSHAKE_DONE, 1); + + if (SSL_IS_DTLS(s)) { + /* done with handshaking */ + s->d1->handshake_read_seq = 0; + s->d1->handshake_write_seq = 0; + s->d1->next_handshake_write_seq = 0; + } + } + + return WORK_FINISHED_STOP; +} + + /* * Obtain handshake message of message type 'mt' (any if mt == -1), maximum * acceptable body length 'max'. The first four bytes (msg_type and length) diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index b967b97995..5cf47d21ff 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -165,11 +165,22 @@ static int ssl_set_version(SSL *s); static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b); +#if 0 +/* + * Temporarily disabled during development of new state machine code. + * TODO: Clean me up + */ static int ssl3_check_change(SSL *s); +#endif static int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, unsigned char *p); +#if 0 +/* + * Temporarily disabled during development of new state machine code. + * TODO: Clean me up + */ int ssl3_connect(SSL *s) { BUF_MEM *buf = NULL; @@ -631,6 +642,7 @@ int ssl3_connect(SSL *s) return (ret); } +#endif /* End temp disabled ssl3_connect */ /* * Work out what version we should be using for the initial ClientHello if * the version is currently set to (D)TLS_ANY_VERSION. @@ -1285,6 +1297,32 @@ enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, unsigned long n) goto f_err; } +#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) + goto err; + + BIO_ctrl(SSL_get_wbio(s), + BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); + } +#endif + return MSG_PROCESS_CONTINUE_READING; f_err: ssl3_send_alert(s, SSL3_AL_FATAL, al); @@ -3489,6 +3527,11 @@ int ssl3_check_cert_and_algorithm(SSL *s) * pre-shared secret, we have a "ticket" and the next server message * is CCS; and 0 otherwise. It returns -1 upon an error. */ + #if 0 + /* + * TODO: No longer required. Temporarily kept during state machine development + * To be deleted by later commits + */ static int ssl3_check_change(SSL *s) { int ok = 0; @@ -3518,6 +3561,7 @@ static int ssl3_check_change(SSL *s) return 0; } +#endif #ifndef OPENSSL_NO_NEXTPROTONEG int ssl3_send_next_proto(SSL *s) diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index e2e9ddf025..7cedb123aa 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -844,6 +844,9 @@ struct statem_st { enum HANDSHAKE_STATE hand_state; int read_state_first_init; int use_timer; +#ifndef OPENSSL_NO_SCTP + int in_sctp_read_sock; +#endif }; typedef struct statem_st STATEM; @@ -2071,6 +2074,7 @@ __owur int tls_get_message_header(SSL *s, int *mt); __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 int ssl3_num_ciphers(void); __owur const SSL_CIPHER *ssl3_get_cipher(unsigned int u); int ssl3_renegotiate(SSL *ssl); @@ -2092,7 +2096,11 @@ __owur int ssl3_connect(SSL *s); void statem_clear(SSL *s); void statem_set_renegotiate(SSL *s); void statem_set_error(SSL *s); -__owur int statem_client_app_data_allowed(SSL *s); +__owur int statem_app_data_allowed(SSL *s); +#ifndef OPENSSL_NO_SCTP +void statem_set_sctp_read_sock(SSL *s, int read_sock); +__owur int statem_in_sctp_read_sock(SSL *s); +#endif __owur int ssl3_read(SSL *s, void *buf, int len); __owur int ssl3_peek(SSL *s, void *buf, int len); __owur int ssl3_write(SSL *s, const void *buf, int len); diff --git a/ssl/statem.c b/ssl/statem.c index ef0799e407..2ebe2a644b 100644 --- a/ssl/statem.c +++ b/ssl/statem.c @@ -103,11 +103,22 @@ enum SUB_STATE_RETURN { SUB_STATE_END_HANDSHAKE }; -int state_machine(SSL *s, int server); +static int state_machine(SSL *s, int server); static void init_read_state_machine(SSL *s); static enum SUB_STATE_RETURN read_state_machine(SSL *s); static void init_write_state_machine(SSL *s); static enum SUB_STATE_RETURN write_state_machine(SSL *s); +static inline int cert_req_allowed(SSL *s); +static inline int key_exchange_skip_allowed(SSL *s); +static int client_read_transition(SSL *s, int mt); +static enum WRITE_TRAN client_write_transition(SSL *s); +static enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst); +static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst); +static int client_construct_message(SSL *s); +static unsigned long client_max_message_size(SSL *s); +static enum MSG_PROCESS_RETURN client_process_message(SSL *s, + unsigned long len); +static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst); /* * Clear the state machine state and reset back to MSG_FLOW_UNINITED @@ -136,6 +147,10 @@ void statem_set_error(SSL *s) s->state = SSL_ST_ERR; } +int ssl3_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 @@ -164,7 +179,7 @@ void statem_set_error(SSL *s) * 1: Success * <=0: NBIO or error */ -int state_machine(SSL *s, int server) { +static int state_machine(SSL *s, int server) { BUF_MEM *buf = NULL; unsigned long Time = (unsigned long)time(NULL); void (*cb) (const SSL *ssl, int type, int val) = NULL; @@ -433,11 +448,10 @@ static enum SUB_STATE_RETURN read_state_machine(SSL *s) { post_process_message = NULL; max_message_size = NULL; } else { - /* TODO: Fill these in later when we've implemented them */ - transition = NULL; - process_message = NULL; - post_process_message = NULL; - max_message_size = NULL; + transition = client_read_transition; + process_message = client_process_message; + max_message_size = client_max_message_size; + post_process_message = client_post_process_message; } if (st->read_state_first_init) { @@ -623,11 +637,10 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s) post_work = NULL; construct_message = NULL; } else { - /* TODO: Fill these in later when we've implemented them */ - transition = NULL; - pre_work = NULL; - post_work = NULL; - construct_message = NULL; + transition = client_write_transition; + pre_work = client_pre_work; + post_work = client_post_work; + construct_message = client_construct_message; } while(1) { @@ -704,6 +717,20 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s) } } +/* + * Flush the write BIO + */ +static int statem_flush(SSL *s) +{ + s->rwstate = SSL_WRITING; + if (BIO_flush(s->wbio) <= 0) { + return 0; + } + s->rwstate = SSL_NOTHING; + + return 1; +} + /* * Called by the record layer to determine whether application data is * allowed to be sent in the current handshake state or not. @@ -712,14 +739,637 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s) * 1: Yes (application data allowed) * 0: No (application data not allowed) */ -int statem_client_app_data_allowed(SSL *s) +int statem_app_data_allowed(SSL *s) { STATEM *st = &s->statem; - if(st->hand_state != TLS_ST_BEFORE && - st->hand_state != TLS_ST_OK && - st->hand_state != TLS_ST_CW_CLNT_HELLO) + if (!s->s3->in_read_app_data || (s->s3->total_renegotiations == 0)) + return 0; + + if (!s->server) { + if (st->state == MSG_FLOW_UNINITED || st->state == MSG_FLOW_RENEGOTIATE) + return 0; + + if(st->hand_state == TLS_ST_CW_CLNT_HELLO) + return 1; + + return 0; + } + + /* + * This is the old check for code still using the old state machine. This + * will be removed by later commits + */ + 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) && + (s->state <= SSL3_ST_SW_HELLO_REQ_A) && + (s->state >= SSL3_ST_SR_CLNT_HELLO_A)) + ) + return 1; + + return 0; +} + + +#ifndef OPENSSL_NO_SCTP +/* + * Set flag used by SCTP to determine whether we are in the read sock state + */ +void statem_set_sctp_read_sock(SSL *s, int read_sock) +{ + s->statem.in_sctp_read_sock = read_sock; +} + +/* + * Called by the record layer to determine whether we are in the read sock + * state or not. + * + * Return values are: + * 1: Yes (we are in the read sock state) + * 0: No (we are not in the read sock state) + */ +int statem_in_sctp_read_sock(SSL *s) +{ + return s->statem.in_sctp_read_sock; +} +#endif + +/* + * Is a CertificateRequest message allowed at the moment or not? + * + * Return values are: + * 1: Yes + * 0: No + */ +static inline int cert_req_allowed(SSL *s) +{ + /* TLS does not like anon-DH with client cert */ + if (s->version > SSL3_VERSION + && (s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL)) + return 0; + + return 1; +} + +/* + * Are we allowed to skip the ServerKeyExchange message? + * + * Return values are: + * 1: Yes + * 0: No + */ +static inline int key_exchange_skip_allowed(SSL *s) +{ + long alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + + /* + * Can't skip server key exchange if this is an ephemeral + * ciphersuite. + */ + if (alg_k & (SSL_kDHE | SSL_kECDHE)) { return 0; + } return 1; } + +/* + * client_read_transition() encapsulates the logic for the allowed handshake + * state transitions when the client is reading messages from the server. The + * message type that the server has sent is provided in |mt|. The current state + * is in |s->statem.hand_state|. + * + * Return values are: + * 1: Success (transition allowed) + * 0: Error (transition not allowed) + */ +static int client_read_transition(SSL *s, int mt) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_CW_CLNT_HELLO: + if (mt == SSL3_MT_SERVER_HELLO) { + st->hand_state = TLS_ST_CR_SRVR_HELLO; + return 1; + } + + if (SSL_IS_DTLS(s)) { + if (mt == DTLS1_MT_HELLO_VERIFY_REQUEST) { + st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST; + return 1; + } + } + break; + + case TLS_ST_CR_SRVR_HELLO: + if (s->hit) { + if (s->tlsext_ticket_expected) { + if (mt == SSL3_MT_NEWSESSION_TICKET) { + st->hand_state = TLS_ST_CR_SESSION_TICKET; + return 1; + } + } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_CR_CHANGE; + 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; + return 1; + } + } else { + if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) { + st->hand_state = TLS_ST_CR_KEY_EXCH; + return 1; + } else if (key_exchange_skip_allowed(s)) { + if (mt == SSL3_MT_CERTIFICATE_REQUEST + && cert_req_allowed(s)) { + st->hand_state = TLS_ST_CR_CERT_REQ; + return 1; + } else if (mt == SSL3_MT_SERVER_DONE) { + st->hand_state = TLS_ST_CR_SRVR_DONE; + return 1; + } + } + } + } + break; + + case TLS_ST_CR_CERT: + if (s->tlsext_status_expected) { + if (mt == SSL3_MT_CERTIFICATE_STATUS) { + st->hand_state = TLS_ST_CR_CERT_STATUS; + return 1; + } + } else { + if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) { + st->hand_state = TLS_ST_CR_KEY_EXCH; + return 1; + } else if (key_exchange_skip_allowed(s)) { + if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) { + st->hand_state = TLS_ST_CR_CERT_REQ; + return 1; + } else if (mt == SSL3_MT_SERVER_DONE) { + st->hand_state = TLS_ST_CR_SRVR_DONE; + return 1; + } + } + } + break; + + case TLS_ST_CR_CERT_STATUS: + if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) { + st->hand_state = TLS_ST_CR_KEY_EXCH; + return 1; + } else if (key_exchange_skip_allowed(s)) { + if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) { + st->hand_state = TLS_ST_CR_CERT_REQ; + return 1; + } else if (mt == SSL3_MT_SERVER_DONE) { + st->hand_state = TLS_ST_CR_SRVR_DONE; + return 1; + } + } + break; + + case TLS_ST_CR_KEY_EXCH: + if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) { + st->hand_state = TLS_ST_CR_CERT_REQ; + return 1; + } else if (mt == SSL3_MT_SERVER_DONE) { + st->hand_state = TLS_ST_CR_SRVR_DONE; + return 1; + } + break; + + case TLS_ST_CR_CERT_REQ: + if (mt == SSL3_MT_SERVER_DONE) { + st->hand_state = TLS_ST_CR_SRVR_DONE; + return 1; + } + break; + + case TLS_ST_CW_FINISHED: + if (mt == SSL3_MT_NEWSESSION_TICKET && s->tlsext_ticket_expected) { + st->hand_state = TLS_ST_CR_SESSION_TICKET; + return 1; + } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_CR_CHANGE; + return 1; + } + break; + + case TLS_ST_CR_SESSION_TICKET: + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_CR_CHANGE; + return 1; + } + break; + + case TLS_ST_CR_CHANGE: + if (mt == SSL3_MT_FINISHED) { + st->hand_state = TLS_ST_CR_FINISHED; + return 1; + } + break; + + default: + break; + } + + /* No valid transition found */ + return 0; +} + +/* + * client_write_transition() works out what handshake state to move to next + * when the client is writing messages to be sent to the server. + */ +static enum WRITE_TRAN client_write_transition(SSL *s) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_OK: + /* Renegotiation - fall through */ + case TLS_ST_BEFORE: + st->hand_state = TLS_ST_CW_CLNT_HELLO; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CLNT_HELLO: + /* + * No transition at the end of writing because we don't know what + * we will be sent + */ + return WRITE_TRAN_FINISHED; + + case TLS_ST_CR_SRVR_DONE: + if (s->s3->tmp.cert_req) + st->hand_state = TLS_ST_CW_CERT; + else + st->hand_state = TLS_ST_CW_KEY_EXCH; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CERT: + st->hand_state = TLS_ST_CW_KEY_EXCH; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_KEY_EXCH: + /* + * For TLS, cert_req is set to 2, so a cert chain of nothing is + * sent, but no verify packet is sent + */ + /* + * XXX: For now, we do not support client authentication in ECDH + * cipher suites with ECDH (rather than ECDSA) certificates. We + * need to skip the certificate verify message when client's + * ECDH public key is sent inside the client certificate. + */ + if (s->s3->tmp.cert_req == 1) { + st->hand_state = TLS_ST_CW_CERT_VRFY; + } else { + st->hand_state = TLS_ST_CW_CHANGE; + } + if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY) { + st->hand_state = TLS_ST_CW_CHANGE; + } + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CERT_VRFY: + st->hand_state = TLS_ST_CW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CHANGE: +#if defined(OPENSSL_NO_NEXTPROTONEG) + st->hand_state = TLS_ST_CW_FINISHED; +#else + if (s->s3->next_proto_neg_seen) + st->hand_state = TLS_ST_CW_NEXT_PROTO; + else + st->hand_state = TLS_ST_CW_FINISHED; +#endif + return WRITE_TRAN_CONTINUE; + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + case TLS_ST_CW_NEXT_PROTO: + st->hand_state = TLS_ST_CW_FINISHED; + return WRITE_TRAN_CONTINUE; +#endif + + case TLS_ST_CW_FINISHED: + if (s->hit) { + st->hand_state = TLS_ST_OK; + /* TODO: This needs removing */ + s->state = SSL_ST_OK; + return WRITE_TRAN_CONTINUE; + } else { + return WRITE_TRAN_FINISHED; + } + + case TLS_ST_CR_FINISHED: + if (s->hit) { + st->hand_state = TLS_ST_CW_CHANGE; + return WRITE_TRAN_CONTINUE; + } else { + st->hand_state = TLS_ST_OK; + /* TODO: This needs removing */ + s->state = SSL_ST_OK; + return WRITE_TRAN_CONTINUE; + } + + default: + /* Shouldn't happen */ + return WRITE_TRAN_ERROR; + } +} + +/* + * Perform any pre work that needs to be done prior to sending a message from + * the client to the server. + */ +static enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_CW_CLNT_HELLO: + s->shutdown = 0; + break; + + case TLS_ST_CW_CERT: + return tls_prepare_client_certificate(s, wst); + + case TLS_ST_OK: + return tls_finish_handshake(s, wst); + + default: + /* No pre work to be done */ + break; + } + + return WORK_FINISHED_CONTINUE; +} + +/* + * Perform any work that needs to be done after sending a message from the + * client to the server. + */ +static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst) +{ + STATEM *st = &s->statem; + + s->init_num = 0; + + 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); + break; + + case TLS_ST_CW_KEY_EXCH: + if (tls_client_key_exchange_post_work(s) == 0) + return WORK_ERROR; + break; + + case TLS_ST_CW_CHANGE: + s->session->cipher = s->s3->tmp.new_cipher; +#ifdef OPENSSL_NO_COMP + s->session->compress_meth = 0; +#else + if (s->s3->tmp.new_compression == NULL) + s->session->compress_meth = 0; + else + s->session->compress_meth = s->s3->tmp.new_compression->id; +#endif + if (!s->method->ssl3_enc->setup_key_block(s)) + return WORK_ERROR; + + if (!s->method->ssl3_enc->change_cipher_state(s, + SSL3_CHANGE_CIPHER_CLIENT_WRITE)) + return WORK_ERROR; + + if (SSL_IS_DTLS(s)) { +#ifndef OPENSSL_NO_SCTP + if (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 + + dtls1_reset_seq_numbers(s, SSL3_CC_WRITE); + } + break; + + case TLS_ST_CW_FINISHED: +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && s->hit == 0) { + /* + * 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 (statem_flush(s) != 1) + return WORK_MORE_B; + + if (s->hit && tls_finish_handshake(s, WORK_MORE_A) != 1) + return WORK_ERROR; + break; + + default: + /* No post work to be done */ + break; + } + + return WORK_FINISHED_CONTINUE; +} + +/* + * Construct a message to be sent from the client to the server. + * + * Valid return values are: + * 1: Success + * 0: Error + */ +static int client_construct_message(SSL *s) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_CW_CLNT_HELLO: + return tls_construct_client_hello(s); + + case TLS_ST_CW_CERT: + return tls_construct_client_certificate(s); + + case TLS_ST_CW_KEY_EXCH: + return tls_construct_client_key_exchange(s); + + case TLS_ST_CW_CERT_VRFY: + return tls_construct_client_verify(s); + + case TLS_ST_CW_CHANGE: + return tls_construct_change_cipher_spec(s); + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + case TLS_ST_CW_NEXT_PROTO: + return tls_construct_next_proto(s); +#endif + case TLS_ST_CW_FINISHED: + return tls_construct_finished(s, + s->method-> + ssl3_enc->client_finished_label, + s->method-> + ssl3_enc->client_finished_label_len); + + default: + /* Shouldn't happen */ + break; + } + + return 0; +} + +/* The spec allows for a longer length than this, but we limit it */ +#define SERVER_HELLO_MAX_LENGTH 20000 +#define SERVER_KEY_EXCH_MAX_LENGTH 102400 +#define SERVER_HELLO_DONE_MAX_LENGTH 0 +#define CCS_MAX_LENGTH 1 +/* Max should actually be 36 but we are generous */ +#define FINISHED_MAX_LENGTH 64 + +/* + * Returns the maximum allowed length for the current message that we are + * reading. Excludes the message header. + */ +static unsigned long client_max_message_size(SSL *s) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_CR_SRVR_HELLO: + return SERVER_HELLO_MAX_LENGTH; + + case TLS_ST_CR_CERT: + return s->max_cert_list; + + case TLS_ST_CR_CERT_STATUS: + return SSL3_RT_MAX_PLAIN_LENGTH; + + case TLS_ST_CR_KEY_EXCH: + return SERVER_KEY_EXCH_MAX_LENGTH; + + case TLS_ST_CR_CERT_REQ: + return SSL3_RT_MAX_PLAIN_LENGTH; + + case TLS_ST_CR_SRVR_DONE: + return SERVER_HELLO_DONE_MAX_LENGTH; + + case TLS_ST_CR_CHANGE: + return CCS_MAX_LENGTH; + + case TLS_ST_CR_SESSION_TICKET: + return SSL3_RT_MAX_PLAIN_LENGTH; + + case TLS_ST_CR_FINISHED: + return FINISHED_MAX_LENGTH; + + default: + /* Shouldn't happen */ + break; + } + + return 0; +} + +/* + * Process a message that the client has been received from the server. + */ +static enum MSG_PROCESS_RETURN client_process_message(SSL *s, unsigned long len) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_CR_SRVR_HELLO: + return tls_process_server_hello(s, len); + + case TLS_ST_CR_CERT: + return tls_process_server_certificate(s, len); + + case TLS_ST_CR_CERT_STATUS: + return tls_process_cert_status(s, len); + + case TLS_ST_CR_KEY_EXCH: + return tls_process_key_exchange(s, len); + + case TLS_ST_CR_CERT_REQ: + return tls_process_certificate_request(s, len); + + case TLS_ST_CR_SRVR_DONE: + return tls_process_server_done(s, len); + + case TLS_ST_CR_CHANGE: + return tls_process_change_cipher_spec(s, len); + + case TLS_ST_CR_SESSION_TICKET: + return tls_process_new_session_ticket(s, len); + + case TLS_ST_CR_FINISHED: + return tls_process_finished(s, len); + + default: + /* Shouldn't happen */ + break; + } + + return MSG_PROCESS_ERROR; +} + +/* + * Perform any further processing required following the receipt of a message + * from the server + */ +static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { +#ifndef OPENSSL_NO_SCTP + case TLS_ST_CR_SRVR_DONE: + /* We only get here if we are using SCTP and we are renegotiating */ + if (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; + } + statem_set_sctp_read_sock(s, 0); + return WORK_FINISHED_STOP; +#endif + + case TLS_ST_CR_FINISHED: + if (!s->hit) + return tls_finish_handshake(s, wst); + else + return WORK_FINISHED_STOP; + default: + break; + } + + /* Shouldn't happen */ + return WORK_ERROR; +} -- 2.34.1