From 61ae935a9831d2b132e50508ccc37f879c17a5c4 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Fri, 11 Sep 2015 11:23:20 +0100 Subject: [PATCH] More state machine reorg Move some function definitions around within the state machine to make sure they are in the correct files. Also create a statem_locl.h header for stuff entirely local to the state machine code and move various definitions into it. Reviewed-by: Tim Hudson Reviewed-by: Richard Levitte --- ssl/Makefile | 9 +- ssl/ssl_locl.h | 63 -- ssl/statem/statem.c | 1376 +------------------------------------- ssl/statem/statem.h | 8 - ssl/statem/statem_clnt.c | 623 +++++++++++++++++ ssl/statem/statem_dtls.c | 1 + ssl/statem/statem_lib.c | 1 + ssl/statem/statem_locl.h | 181 +++++ ssl/statem/statem_srvr.c | 721 ++++++++++++++++++++ 9 files changed, 1535 insertions(+), 1448 deletions(-) create mode 100644 ssl/statem/statem_locl.h diff --git a/ssl/Makefile b/ssl/Makefile index 9f1fe8d8c2..0865631d69 100644 --- a/ssl/Makefile +++ b/ssl/Makefile @@ -682,6 +682,7 @@ statem/statem.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h statem/statem.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h statem/statem.o: statem/../packet_locl.h statem/../record/record.h statem/statem.o: statem/../ssl_locl.h statem/../statem/statem.h statem/statem.c +statem/statem.o: statem/statem_locl.h statem/statem_clnt.o: ../e_os.h ../include/openssl/asn1.h statem/statem_clnt.o: ../include/openssl/bio.h ../include/openssl/bn.h statem/statem_clnt.o: ../include/openssl/buffer.h ../include/openssl/comp.h @@ -706,7 +707,7 @@ statem/statem_clnt.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h statem/statem_clnt.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h statem/statem_clnt.o: statem/../packet_locl.h statem/../record/record.h statem/statem_clnt.o: statem/../ssl_locl.h statem/../statem/statem.h -statem/statem_clnt.o: statem/statem_clnt.c +statem/statem_clnt.o: statem/statem_clnt.c statem/statem_locl.h statem/statem_dtls.o: ../e_os.h ../include/openssl/asn1.h statem/statem_dtls.o: ../include/openssl/bio.h ../include/openssl/bn.h statem/statem_dtls.o: ../include/openssl/buffer.h ../include/openssl/comp.h @@ -730,7 +731,7 @@ statem/statem_dtls.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h statem/statem_dtls.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h statem/statem_dtls.o: statem/../packet_locl.h statem/../record/record.h statem/statem_dtls.o: statem/../ssl_locl.h statem/../statem/statem.h -statem/statem_dtls.o: statem/statem_dtls.c +statem/statem_dtls.o: statem/statem_dtls.c statem/statem_locl.h statem/statem_lib.o: ../e_os.h ../include/openssl/asn1.h statem/statem_lib.o: ../include/openssl/bio.h ../include/openssl/bn.h statem/statem_lib.o: ../include/openssl/buffer.h ../include/openssl/comp.h @@ -754,7 +755,7 @@ statem/statem_lib.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h statem/statem_lib.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h statem/statem_lib.o: statem/../packet_locl.h statem/../record/record.h statem/statem_lib.o: statem/../ssl_locl.h statem/../statem/statem.h -statem/statem_lib.o: statem/statem_lib.c +statem/statem_lib.o: statem/statem_lib.c statem/statem_locl.h statem/statem_srvr.o: ../e_os.h ../include/internal/constant_time_locl.h statem/statem_srvr.o: ../include/openssl/asn1.h ../include/openssl/bio.h statem/statem_srvr.o: ../include/openssl/bn.h ../include/openssl/buffer.h @@ -779,7 +780,7 @@ statem/statem_srvr.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h statem/statem_srvr.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h statem/statem_srvr.o: statem/../packet_locl.h statem/../record/record.h statem/statem_srvr.o: statem/../ssl_locl.h statem/../statem/statem.h -statem/statem_srvr.o: statem/statem_srvr.c +statem/statem_srvr.o: statem/statem_locl.h statem/statem_srvr.c t1_enc.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h t1_enc.o: ../include/openssl/bn.h ../include/openssl/buffer.h t1_enc.o: ../include/openssl/comp.h ../include/openssl/crypto.h diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 25afff8c9b..d30663f43b 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1914,15 +1914,7 @@ __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen, __owur const SSL_CIPHER *ssl3_get_cipher_by_char(const unsigned char *p); __owur int ssl3_put_cipher_by_char(const SSL_CIPHER *c, unsigned char *p); void ssl3_init_finished_mac(SSL *s); -__owur int tls_construct_server_certificate(SSL *s); -__owur int tls_construct_new_session_ticket(SSL *s); -__owur int tls_construct_cert_status(SSL *s); -__owur enum MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, - PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt); __owur int ssl3_setup_key_block(SSL *s); -__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); @@ -1930,12 +1922,6 @@ int ssl3_send_alert(SSL *s, int level, int desc); __owur int ssl3_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p, int len); __owur int ssl3_get_req_cert_type(SSL *s, unsigned char *p); -__owur long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok); -__owur int tls_get_message_header(SSL *s, int *mt); -__owur int tls_get_message_body(SSL *s, unsigned long *len); -__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); @@ -2007,54 +1993,6 @@ __owur unsigned int dtls1_link_min_mtu(void); void dtls1_hm_fragment_free(hm_fragment *frag); __owur int dtls1_query_mtu(SSL *s); -/* some client-only functions */ -__owur int tls_construct_client_hello(SSL *s); -__owur enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, - PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s, - PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, - PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt); -__owur int tls_construct_client_verify(SSL *s); -__owur enum WORK_STATE tls_prepare_client_certificate(SSL *s, - enum WORK_STATE wst); -__owur int tls_construct_client_certificate(SSL *s); -__owur int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey); -__owur int tls_construct_client_key_exchange(SSL *s); -__owur int tls_client_key_exchange_post_work(SSL *s); -__owur enum MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s, - PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, - PACKET *pkt); -__owur int ssl3_check_cert_and_algorithm(SSL *s); -# ifndef OPENSSL_NO_NEXTPROTONEG -__owur int tls_construct_next_proto(SSL *s); -# endif -__owur enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, PACKET *pkt); - -/* some server-only functions */ -__owur enum MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt); -__owur enum WORK_STATE tls_post_process_client_hello(SSL *s, - enum WORK_STATE wst); -__owur int tls_construct_server_hello(SSL *s); -__owur int tls_construct_hello_request(SSL *s); -__owur int dtls_construct_hello_verify_request(SSL *s); -__owur int tls_construct_server_key_exchange(SSL *s); -__owur int tls_construct_certificate_request(SSL *s); -__owur int tls_construct_server_done(SSL *s); -__owur enum MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, - PACKET *pkt); -__owur enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, - PACKET *pkt); -__owur enum WORK_STATE tls_post_process_client_key_exchange(SSL *s, - enum WORK_STATE wst); -__owur enum MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, PACKET *pkt); -# ifndef OPENSSL_NO_NEXTPROTONEG -__owur enum MSG_PROCESS_RETURN tls_process_next_proto(SSL *s, PACKET *pkt); -# endif - __owur int tls1_new(SSL *s); void tls1_free(SSL *s); void tls1_clear(SSL *s); @@ -2067,7 +2005,6 @@ void dtls1_clear(SSL *s); long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg); __owur int dtls1_shutdown(SSL *s); -__owur int dtls_get_message(SSL *s, int *mt, unsigned long *len); __owur int dtls1_dispatch_alert(SSL *s); __owur int ssl_init_wbio_buffer(SSL *s, int push); diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index 7e4f524ddc..f0b3260829 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -58,6 +58,7 @@ #include #include "../ssl_locl.h" +#include "statem_locl.h" /* * This file implements the SSL/TLS/DTLS state machines. @@ -83,7 +84,7 @@ * | Message flow state machine | | | * | | | | * | -------------------- -------------------- | Transition | Handshake state | - * | | MSG_FLOW_READING | | MSG_FLOW_WRITING | | Event | machine | + * | | MSG_FLOW_READING | | MSG_FLOW_WRITING | | Event | machine | * | | sub-state | | sub-state | |----------->| | * | | machine for | | machine for | | | | * | | reading messages | | writing messages | | | | @@ -108,27 +109,6 @@ 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, PACKET *pkt); -static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst); -static int server_read_transition(SSL *s, int mt); -static inline int send_server_key_exchange(SSL *s); -static inline int send_certificate_request(SSL *s); -static enum WRITE_TRAN server_write_transition(SSL *s); -static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst); -static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst); -static int server_construct_message(SSL *s); -static unsigned long server_max_message_size(SSL *s); -static enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt); -static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst); - enum HANDSHAKE_STATE SSL_state(const SSL *ssl) { @@ -818,7 +798,7 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s) /* * Flush the write BIO */ -static int statem_flush(SSL *s) +int statem_flush(SSL *s) { s->rwstate = SSL_WRITING; if (BIO_flush(s->wbio) <= 0) { @@ -867,7 +847,6 @@ int statem_app_data_allowed(SSL *s) return 0; } - #ifndef OPENSSL_NO_SCTP /* * Set flag used by SCTP to determine whether we are in the read sock state @@ -890,1352 +869,3 @@ 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 (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; - 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 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; - 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 (!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; -#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; - statem_set_in_init(s, 0); - 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; - statem_set_in_init(s, 0); - 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; - 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); - - 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: - 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: - 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 (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. - */ - 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: - 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: - 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 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 -#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 DTLS_ST_CR_HELLO_VERIFY_REQUEST: - return HELLO_VERIFY_REQUEST_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, PACKET *pkt) -{ - STATEM *st = &s->statem; - - switch(st->hand_state) { - case TLS_ST_CR_SRVR_HELLO: - return tls_process_server_hello(s, pkt); - - case DTLS_ST_CR_HELLO_VERIFY_REQUEST: - return dtls_process_hello_verify(s, pkt); - - case TLS_ST_CR_CERT: - return tls_process_server_certificate(s, pkt); - - case TLS_ST_CR_CERT_STATUS: - return tls_process_cert_status(s, pkt); - - case TLS_ST_CR_KEY_EXCH: - return tls_process_key_exchange(s, pkt); - - case TLS_ST_CR_CERT_REQ: - return tls_process_certificate_request(s, pkt); - - case TLS_ST_CR_SRVR_DONE: - return tls_process_server_done(s, pkt); - - case TLS_ST_CR_CHANGE: - return tls_process_change_cipher_spec(s, pkt); - - case TLS_ST_CR_SESSION_TICKET: - return tls_process_new_session_ticket(s, pkt); - - case TLS_ST_CR_FINISHED: - return tls_process_finished(s, pkt); - - 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; -} - - -/* - * server_read_transition() encapsulates the logic for the allowed handshake - * state transitions when the server is reading messages from the client. The - * message type that the client has sent is provided in |mt|. The current state - * is in |s->statem.hand_state|. - * - * Valid return values are: - * 1: Success (transition allowed) - * 0: Error (transition not allowed) - */ -static int server_read_transition(SSL *s, int mt) -{ - STATEM *st = &s->statem; - - 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; - } - break; - - case TLS_ST_SW_SRVR_DONE: - /* - * If we get a CKE message after a ServerDone then either - * 1) We didn't request a Certificate - * OR - * 2) If we did request one then - * a) We allow no Certificate to be returned - * AND - * b) We are running SSL3 (in TLS1.0+ the client must return a 0 - * list if we requested a certificate) - */ - if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE - && (!s->s3->tmp.cert_request - || (!((s->verify_mode & SSL_VERIFY_PEER) && - (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) - && (s->version == SSL3_VERSION)))) { - st->hand_state = TLS_ST_SR_KEY_EXCH; - return 1; - } else if (s->s3->tmp.cert_request) { - if (mt == SSL3_MT_CERTIFICATE) { - st->hand_state = TLS_ST_SR_CERT; - return 1; - } - } - break; - - case TLS_ST_SR_CERT: - if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE) { - st->hand_state = TLS_ST_SR_KEY_EXCH; - return 1; - } - break; - - case TLS_ST_SR_KEY_EXCH: - /* - * We should only process a CertificateVerify message if we have - * received a Certificate from the client. If so then |s->session->peer| - * will be non NULL. In some instances a CertificateVerify message is - * not required even if the peer has sent a Certificate (e.g. such as in - * the case of static DH). In that case |s->no_cert_verify| should be - * set. - */ - if (s->session->peer == NULL || s->no_cert_verify) { - if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { - /* - * For the ECDH ciphersuites when the client sends its ECDH - * pub key in a certificate, the CertificateVerify message is - * not sent. Also for GOST ciphersuites when the client uses - * its key from the certificate for key exchange. - */ - st->hand_state = TLS_ST_SR_CHANGE; - return 1; - } - } else { - if (mt == SSL3_MT_CERTIFICATE_VERIFY) { - st->hand_state = TLS_ST_SR_CERT_VRFY; - return 1; - } - } - break; - - case TLS_ST_SR_CERT_VRFY: - if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { - st->hand_state = TLS_ST_SR_CHANGE; - return 1; - } - break; - - case TLS_ST_SR_CHANGE: -#ifndef OPENSSL_NO_NEXTPROTONEG - if (s->s3->next_proto_neg_seen) { - if (mt == SSL3_MT_NEXT_PROTO) { - st->hand_state = TLS_ST_SR_NEXT_PROTO; - return 1; - } - } else { -#endif - if (mt == SSL3_MT_FINISHED) { - st->hand_state = TLS_ST_SR_FINISHED; - return 1; - } -#ifndef OPENSSL_NO_NEXTPROTONEG - } -#endif - break; - -#ifndef OPENSSL_NO_NEXTPROTONEG - case TLS_ST_SR_NEXT_PROTO: - if (mt == SSL3_MT_FINISHED) { - st->hand_state = TLS_ST_SR_FINISHED; - return 1; - } - break; -#endif - - case TLS_ST_SW_FINISHED: - if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { - st->hand_state = TLS_ST_SR_CHANGE; - return 1; - } - break; - - default: - break; - } - - /* No valid transition found */ - return 0; -} - -/* - * Should we send a ServerKeyExchange message? - * - * Valid return values are: - * 1: Yes - * 0: No - */ -static inline int send_server_key_exchange(SSL *s) -{ - unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey; - - /* - * only send a ServerKeyExchange if DH, fortezza or RSA but we have a - * sign only certificate PSK: may send PSK identity hints For - * ECC ciphersuites, we send a serverKeyExchange message only if - * the cipher suite is either ECDH-anon or ECDHE. In other cases, - * the server certificate contains the server's public key for - * key exchange. - */ - if ( (alg_k & SSL_kDHE) - || (alg_k & SSL_kECDHE) - || ((alg_k & SSL_kRSA) - && (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL - || (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) - && EVP_PKEY_size(s->cert->pkeys - [SSL_PKEY_RSA_ENC].privatekey) * - 8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher) - ) - ) - ) - /* - * PSK: send ServerKeyExchange if PSK identity hint if - * provided - */ -#ifndef OPENSSL_NO_PSK - /* Only send SKE if we have identity hint for plain PSK */ - || ((alg_k & (SSL_kPSK | SSL_kRSAPSK)) - && s->cert->psk_identity_hint) - /* For other PSK always send SKE */ - || (alg_k & (SSL_PSK & (SSL_kDHEPSK | SSL_kECDHEPSK))) -#endif -#ifndef OPENSSL_NO_SRP - /* SRP: send ServerKeyExchange */ - || (alg_k & SSL_kSRP) -#endif - ) { - return 1; - } - - return 0; -} - -/* - * Should we send a CertificateRequest message? - * - * Valid return values are: - * 1: Yes - * 0: No - */ -static inline int send_certificate_request(SSL *s) -{ - if ( - /* don't request cert unless asked for it: */ - s->verify_mode & SSL_VERIFY_PEER - /* - * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert - * during re-negotiation: - */ - && ((s->session->peer == NULL) || - !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) - /* - * never request cert in anonymous ciphersuites (see - * section "Certificate request" in SSL 3 drafts and in - * RFC 2246): - */ - && (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) - /* - * ... except when the application insists on - * verification (against the specs, but s3_clnt.c accepts - * this for SSL 3) - */ - || (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) - /* don't request certificate for SRP auth */ - && !(s->s3->tmp.new_cipher->algorithm_auth & SSL_aSRP) - /* - * With normal PSK Certificates and Certificate Requests - * are omitted - */ - && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_PSK)) { - return 1; - } - - return 0; -} - -/* - * server_write_transition() works out what handshake state to move to next - * when the server is writing messages to be sent to the client. - */ -static enum WRITE_TRAN server_write_transition(SSL *s) -{ - STATEM *st = &s->statem; - - switch(st->hand_state) { - case TLS_ST_BEFORE: - /* Just go straight to trying to read from the client */; - return WRITE_TRAN_FINISHED; - - case TLS_ST_OK: - /* We must be trying to renegotiate */ - st->hand_state = TLS_ST_SW_HELLO_REQ; - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SW_HELLO_REQ: - st->hand_state = TLS_ST_OK; - statem_set_in_init(s, 0); - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SR_CLNT_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) - st->hand_state = TLS_ST_SW_SESSION_TICKET; - else - st->hand_state = TLS_ST_SW_CHANGE; - } else { - /* Check if it is anon DH or anon ECDH, */ - /* normal PSK or SRP */ - if (!(s->s3->tmp.new_cipher->algorithm_auth & - (SSL_aNULL | SSL_aSRP | SSL_aPSK))) { - st->hand_state = TLS_ST_SW_CERT; - } else if (send_server_key_exchange(s)) { - st->hand_state = TLS_ST_SW_KEY_EXCH; - } else if (send_certificate_request(s)) { - st->hand_state = TLS_ST_SW_CERT_REQ; - } else { - st->hand_state = TLS_ST_SW_SRVR_DONE; - } - } - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SW_CERT: - if (s->tlsext_status_expected) { - st->hand_state = TLS_ST_SW_CERT_STATUS; - return WRITE_TRAN_CONTINUE; - } - /* Fall through */ - - case TLS_ST_SW_CERT_STATUS: - if (send_server_key_exchange(s)) { - st->hand_state = TLS_ST_SW_KEY_EXCH; - return WRITE_TRAN_CONTINUE; - } - /* Fall through */ - - case TLS_ST_SW_KEY_EXCH: - if (send_certificate_request(s)) { - st->hand_state = TLS_ST_SW_CERT_REQ; - return WRITE_TRAN_CONTINUE; - } - /* Fall through */ - - case TLS_ST_SW_CERT_REQ: - st->hand_state = TLS_ST_SW_SRVR_DONE; - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SW_SRVR_DONE: - return WRITE_TRAN_FINISHED; - - case TLS_ST_SR_FINISHED: - if (s->hit) { - st->hand_state = TLS_ST_OK; - statem_set_in_init(s, 0); - return WRITE_TRAN_CONTINUE; - } else if (s->tlsext_ticket_expected) { - st->hand_state = TLS_ST_SW_SESSION_TICKET; - } else { - st->hand_state = TLS_ST_SW_CHANGE; - } - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SW_SESSION_TICKET: - st->hand_state = TLS_ST_SW_CHANGE; - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SW_CHANGE: - st->hand_state = TLS_ST_SW_FINISHED; - return WRITE_TRAN_CONTINUE; - - case TLS_ST_SW_FINISHED: - if (s->hit) { - return WRITE_TRAN_FINISHED; - } - st->hand_state = TLS_ST_OK; - statem_set_in_init(s, 0); - 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 server to the client. - */ -static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst) -{ - STATEM *st = &s->statem; - - 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: - s->session->cipher = s->s3->tmp.new_cipher; - if (!s->method->ssl3_enc->setup_key_block(s)) { - 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: - 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 - * server to the client. - */ -static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst) -{ - STATEM *st = &s->statem; - - s->init_num = 0; - - switch(st->hand_state) { - case TLS_ST_SW_HELLO_REQ: - if (statem_flush(s) != 1) - return WORK_MORE_A; - 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: - if (statem_flush(s) != 1) - return WORK_MORE_A; - break; - - 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: - /* No post work to be done */ - break; - } - - return WORK_FINISHED_CONTINUE; -} - -/* - * Construct a message to be sent from the server to the client. - * - * Valid return values are: - * 1: Success - * 0: Error - */ -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); - - case TLS_ST_SW_SRVR_HELLO: - return tls_construct_server_hello(s); - - case TLS_ST_SW_CERT: - return tls_construct_server_certificate(s); - - case TLS_ST_SW_KEY_EXCH: - return tls_construct_server_key_exchange(s); - - case TLS_ST_SW_CERT_REQ: - return tls_construct_certificate_request(s); - - case TLS_ST_SW_SRVR_DONE: - return tls_construct_server_done(s); - - case TLS_ST_SW_SESSION_TICKET: - return tls_construct_new_session_ticket(s); - - case TLS_ST_SW_CERT_STATUS: - return tls_construct_cert_status(s); - - case TLS_ST_SW_CHANGE: - if (SSL_IS_DTLS(s)) - return dtls_construct_change_cipher_spec(s); - else - return tls_construct_change_cipher_spec(s); - - case TLS_ST_SW_FINISHED: - return tls_construct_finished(s, - s->method-> - ssl3_enc->server_finished_label, - s->method-> - ssl3_enc->server_finished_label_len); - - default: - /* Shouldn't happen */ - break; - } - - return 0; -} - -#define CLIENT_KEY_EXCH_MAX_LENGTH 2048 -#define NEXT_PROTO_MAX_LENGTH 514 - -/* - * Returns the maximum allowed length for the current message that we are - * reading. Excludes the message header. - */ -static unsigned long server_max_message_size(SSL *s) -{ - STATEM *st = &s->statem; - - switch(st->hand_state) { - case TLS_ST_SR_CLNT_HELLO: - return SSL3_RT_MAX_PLAIN_LENGTH; - - case TLS_ST_SR_CERT: - return s->max_cert_list; - - case TLS_ST_SR_KEY_EXCH: - return CLIENT_KEY_EXCH_MAX_LENGTH; - - case TLS_ST_SR_CERT_VRFY: - return SSL3_RT_MAX_PLAIN_LENGTH; - -#ifndef OPENSSL_NO_NEXTPROTONEG - case TLS_ST_SR_NEXT_PROTO: - return NEXT_PROTO_MAX_LENGTH; -#endif - - case TLS_ST_SR_CHANGE: - return CCS_MAX_LENGTH; - - case TLS_ST_SR_FINISHED: - return FINISHED_MAX_LENGTH; - - default: - /* Shouldn't happen */ - break; - } - - return 0; -} - -/* - * Process a message that the server has received from the client. - */ -static enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt) -{ - STATEM *st = &s->statem; - - switch(st->hand_state) { - case TLS_ST_SR_CLNT_HELLO: - return tls_process_client_hello(s, pkt); - - case TLS_ST_SR_CERT: - return tls_process_client_certificate(s, pkt); - - case TLS_ST_SR_KEY_EXCH: - return tls_process_client_key_exchange(s, pkt); - - case TLS_ST_SR_CERT_VRFY: - return tls_process_cert_verify(s, pkt); - -#ifndef OPENSSL_NO_NEXTPROTONEG - case TLS_ST_SR_NEXT_PROTO: - return tls_process_next_proto(s, pkt); -#endif - - case TLS_ST_SR_CHANGE: - return tls_process_change_cipher_spec(s, pkt); - - case TLS_ST_SR_FINISHED: - return tls_process_finished(s, pkt); - - default: - /* Shouldn't happen */ - break; - } - - return MSG_PROCESS_ERROR; -} - -/* - * Perform any further processing required following the receipt of a message - * from the client - */ -static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst) -{ - STATEM *st = &s->statem; - - switch(st->hand_state) { - case TLS_ST_SR_CLNT_HELLO: - return tls_post_process_client_hello(s, 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); - else - return WORK_FINISHED_STOP; - default: - break; - } - - /* Shouldn't happen */ - return WORK_ERROR; -} diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h index b6256f9788..873ed0e7bf 100644 --- a/ssl/statem/statem.h +++ b/ssl/statem/statem.h @@ -86,14 +86,6 @@ enum WRITE_TRAN { WRITE_TRAN_FINISHED }; -/* Message processing return codes */ -enum MSG_PROCESS_RETURN { - MSG_PROCESS_ERROR, - MSG_PROCESS_FINISHED_READING, - MSG_PROCESS_CONTINUE_PROCESSING, - MSG_PROCESS_CONTINUE_READING -}; - /* Message flow states */ enum MSG_FLOW_STATE { /* No handshake in progress */ diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 3ff427650b..c68eecf24f 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -150,6 +150,7 @@ #include #include "../ssl_locl.h" +#include "statem_locl.h" #include #include #include @@ -163,11 +164,633 @@ # include #endif +static inline int cert_req_allowed(SSL *s); +static inline int key_exchange_skip_allowed(SSL *s); static int ssl_set_version(SSL *s); static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b); static int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, unsigned char *p); + +/* + * 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) + */ +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 (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; + 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. + */ +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 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; + 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 (!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; +#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; + statem_set_in_init(s, 0); + 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; + statem_set_in_init(s, 0); + 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. + */ +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; + 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); + + 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. + */ +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: + 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: + 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 (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. + */ + 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 + */ +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: + 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: + 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; +} + +/* + * Returns the maximum allowed length for the current message that we are + * reading. Excludes the message header. + */ +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 DTLS_ST_CR_HELLO_VERIFY_REQUEST: + return HELLO_VERIFY_REQUEST_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. + */ +enum MSG_PROCESS_RETURN client_process_message(SSL *s, PACKET *pkt) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_CR_SRVR_HELLO: + return tls_process_server_hello(s, pkt); + + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + return dtls_process_hello_verify(s, pkt); + + case TLS_ST_CR_CERT: + return tls_process_server_certificate(s, pkt); + + case TLS_ST_CR_CERT_STATUS: + return tls_process_cert_status(s, pkt); + + case TLS_ST_CR_KEY_EXCH: + return tls_process_key_exchange(s, pkt); + + case TLS_ST_CR_CERT_REQ: + return tls_process_certificate_request(s, pkt); + + case TLS_ST_CR_SRVR_DONE: + return tls_process_server_done(s, pkt); + + case TLS_ST_CR_CHANGE: + return tls_process_change_cipher_spec(s, pkt); + + case TLS_ST_CR_SESSION_TICKET: + return tls_process_new_session_ticket(s, pkt); + + case TLS_ST_CR_FINISHED: + return tls_process_finished(s, pkt); + + default: + /* Shouldn't happen */ + break; + } + + return MSG_PROCESS_ERROR; +} + +/* + * Perform any further processing required following the receipt of a message + * from the server + */ +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; +} + /* * Work out what version we should be using for the initial ClientHello if * the version is currently set to (D)TLS_ANY_VERSION. diff --git a/ssl/statem/statem_dtls.c b/ssl/statem/statem_dtls.c index e13ed8d247..34fd8b2008 100644 --- a/ssl/statem/statem_dtls.c +++ b/ssl/statem/statem_dtls.c @@ -117,6 +117,7 @@ #include #include #include "../ssl_locl.h" +#include "statem_locl.h" #include #include #include diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index 75d151e5e0..0f548d469b 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -118,6 +118,7 @@ #include #include #include "../ssl_locl.h" +#include "statem_locl.h" #include #include #include diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h new file mode 100644 index 0000000000..65787bd5b4 --- /dev/null +++ b/ssl/statem/statem_locl.h @@ -0,0 +1,181 @@ +/* ssl/statem/statem_locl.h */ +/* ==================================================================== + * Copyright (c) 1998-2015 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +/***************************************************************************** + * * + * The following definitions are PRIVATE to the state machine. They should * + * NOT be used outside of the state machine. * + * * + *****************************************************************************/ + +/* Max message length definitions */ + +/* 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 +#define CCS_MAX_LENGTH 1 +/* Max should actually be 36 but we are generous */ +#define FINISHED_MAX_LENGTH 64 + +/* Message processing return codes */ +enum MSG_PROCESS_RETURN { + /* Something bad happened */ + MSG_PROCESS_ERROR, + /* We've finished reading - swap to writing */ + MSG_PROCESS_FINISHED_READING, + /* + * We've completed the main processing of this message but there is some + * post processing to be done. + */ + MSG_PROCESS_CONTINUE_PROCESSING, + /* We've finished this message - read the next message */ + MSG_PROCESS_CONTINUE_READING +}; + +/* Flush the write BIO */ +int statem_flush(SSL *s); + +/* + * TLS/DTLS client state machine functions + */ +int client_read_transition(SSL *s, int mt); +enum WRITE_TRAN client_write_transition(SSL *s); +enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst); +enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst); +int client_construct_message(SSL *s); +unsigned long client_max_message_size(SSL *s); +enum MSG_PROCESS_RETURN client_process_message(SSL *s, PACKET *pkt); +enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst); + +/* + * TLS/DTLS server state machine functions + */ +int server_read_transition(SSL *s, int mt); +enum WRITE_TRAN server_write_transition(SSL *s); +enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst); +enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst); +int server_construct_message(SSL *s); +unsigned long server_max_message_size(SSL *s); +enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt); +enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst); + +/* Functions for getting new message data */ +__owur int tls_get_message_header(SSL *s, int *mt); +__owur int tls_get_message_body(SSL *s, unsigned long *len); +__owur int dtls_get_message(SSL *s, int *mt, unsigned long *len); + +/* Message construction and processing functions */ +__owur enum MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, + PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt); +__owur int tls_construct_change_cipher_spec(SSL *s); +__owur int dtls_construct_change_cipher_spec(SSL *s); + +__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); + +/* some client-only functions */ +__owur int tls_construct_client_hello(SSL *s); +__owur enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, + PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s, + PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, + PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt); +__owur int tls_construct_client_verify(SSL *s); +__owur enum WORK_STATE tls_prepare_client_certificate(SSL *s, + enum WORK_STATE wst); +__owur int tls_construct_client_certificate(SSL *s); +__owur int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey); +__owur int tls_construct_client_key_exchange(SSL *s); +__owur int tls_client_key_exchange_post_work(SSL *s); +__owur int tls_construct_cert_status(SSL *s); +__owur enum MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s, + PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, + PACKET *pkt); +__owur int ssl3_check_cert_and_algorithm(SSL *s); +# ifndef OPENSSL_NO_NEXTPROTONEG +__owur int tls_construct_next_proto(SSL *s); +# endif +__owur enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, PACKET *pkt); + +/* some server-only functions */ +__owur enum MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt); +__owur enum WORK_STATE tls_post_process_client_hello(SSL *s, + enum WORK_STATE wst); +__owur int tls_construct_server_hello(SSL *s); +__owur int tls_construct_hello_request(SSL *s); +__owur int dtls_construct_hello_verify_request(SSL *s); +__owur int tls_construct_server_certificate(SSL *s); +__owur int tls_construct_server_key_exchange(SSL *s); +__owur int tls_construct_certificate_request(SSL *s); +__owur int tls_construct_server_done(SSL *s); +__owur enum MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, + PACKET *pkt); +__owur enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, + PACKET *pkt); +__owur enum WORK_STATE tls_post_process_client_key_exchange(SSL *s, + enum WORK_STATE wst); +__owur enum MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, PACKET *pkt); +# ifndef OPENSSL_NO_NEXTPROTONEG +__owur enum MSG_PROCESS_RETURN tls_process_next_proto(SSL *s, PACKET *pkt); +# endif +__owur int tls_construct_new_session_ticket(SSL *s); diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 5f85a8c500..f0b79f037f 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -151,6 +151,7 @@ #include #include "../ssl_locl.h" +#include "statem_locl.h" #include "internal/constant_time_locl.h" #include #include @@ -169,6 +170,726 @@ static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, STACK_OF(SSL_CIPHER) **skp, int sslv2format, int *al); +/* + * server_read_transition() encapsulates the logic for the allowed handshake + * state transitions when the server is reading messages from the client. The + * message type that the client has sent is provided in |mt|. The current state + * is in |s->statem.hand_state|. + * + * Valid return values are: + * 1: Success (transition allowed) + * 0: Error (transition not allowed) + */ +int server_read_transition(SSL *s, int mt) +{ + STATEM *st = &s->statem; + + 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; + } + break; + + case TLS_ST_SW_SRVR_DONE: + /* + * If we get a CKE message after a ServerDone then either + * 1) We didn't request a Certificate + * OR + * 2) If we did request one then + * a) We allow no Certificate to be returned + * AND + * b) We are running SSL3 (in TLS1.0+ the client must return a 0 + * list if we requested a certificate) + */ + if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE + && (!s->s3->tmp.cert_request + || (!((s->verify_mode & SSL_VERIFY_PEER) && + (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) + && (s->version == SSL3_VERSION)))) { + st->hand_state = TLS_ST_SR_KEY_EXCH; + return 1; + } else if (s->s3->tmp.cert_request) { + if (mt == SSL3_MT_CERTIFICATE) { + st->hand_state = TLS_ST_SR_CERT; + return 1; + } + } + break; + + case TLS_ST_SR_CERT: + if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE) { + st->hand_state = TLS_ST_SR_KEY_EXCH; + return 1; + } + break; + + case TLS_ST_SR_KEY_EXCH: + /* + * We should only process a CertificateVerify message if we have + * received a Certificate from the client. If so then |s->session->peer| + * will be non NULL. In some instances a CertificateVerify message is + * not required even if the peer has sent a Certificate (e.g. such as in + * the case of static DH). In that case |s->no_cert_verify| should be + * set. + */ + if (s->session->peer == NULL || s->no_cert_verify) { + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + /* + * For the ECDH ciphersuites when the client sends its ECDH + * pub key in a certificate, the CertificateVerify message is + * not sent. Also for GOST ciphersuites when the client uses + * its key from the certificate for key exchange. + */ + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + } else { + if (mt == SSL3_MT_CERTIFICATE_VERIFY) { + st->hand_state = TLS_ST_SR_CERT_VRFY; + return 1; + } + } + break; + + case TLS_ST_SR_CERT_VRFY: + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + break; + + case TLS_ST_SR_CHANGE: +#ifndef OPENSSL_NO_NEXTPROTONEG + if (s->s3->next_proto_neg_seen) { + if (mt == SSL3_MT_NEXT_PROTO) { + st->hand_state = TLS_ST_SR_NEXT_PROTO; + return 1; + } + } else { +#endif + if (mt == SSL3_MT_FINISHED) { + st->hand_state = TLS_ST_SR_FINISHED; + return 1; + } +#ifndef OPENSSL_NO_NEXTPROTONEG + } +#endif + break; + +#ifndef OPENSSL_NO_NEXTPROTONEG + case TLS_ST_SR_NEXT_PROTO: + if (mt == SSL3_MT_FINISHED) { + st->hand_state = TLS_ST_SR_FINISHED; + return 1; + } + break; +#endif + + case TLS_ST_SW_FINISHED: + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + break; + + default: + break; + } + + /* No valid transition found */ + return 0; +} + +/* + * Should we send a ServerKeyExchange message? + * + * Valid return values are: + * 1: Yes + * 0: No + */ +static inline int send_server_key_exchange(SSL *s) +{ + unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + + /* + * only send a ServerKeyExchange if DH, fortezza or RSA but we have a + * sign only certificate PSK: may send PSK identity hints For + * ECC ciphersuites, we send a serverKeyExchange message only if + * the cipher suite is either ECDH-anon or ECDHE. In other cases, + * the server certificate contains the server's public key for + * key exchange. + */ + if ( (alg_k & SSL_kDHE) + || (alg_k & SSL_kECDHE) + || ((alg_k & SSL_kRSA) + && (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL + || (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) + && EVP_PKEY_size(s->cert->pkeys + [SSL_PKEY_RSA_ENC].privatekey) * + 8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher) + ) + ) + ) + /* + * PSK: send ServerKeyExchange if PSK identity hint if + * provided + */ +#ifndef OPENSSL_NO_PSK + /* Only send SKE if we have identity hint for plain PSK */ + || ((alg_k & (SSL_kPSK | SSL_kRSAPSK)) + && s->cert->psk_identity_hint) + /* For other PSK always send SKE */ + || (alg_k & (SSL_PSK & (SSL_kDHEPSK | SSL_kECDHEPSK))) +#endif +#ifndef OPENSSL_NO_SRP + /* SRP: send ServerKeyExchange */ + || (alg_k & SSL_kSRP) +#endif + ) { + return 1; + } + + return 0; +} + +/* + * Should we send a CertificateRequest message? + * + * Valid return values are: + * 1: Yes + * 0: No + */ +static inline int send_certificate_request(SSL *s) +{ + if ( + /* don't request cert unless asked for it: */ + s->verify_mode & SSL_VERIFY_PEER + /* + * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert + * during re-negotiation: + */ + && ((s->session->peer == NULL) || + !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) + /* + * never request cert in anonymous ciphersuites (see + * section "Certificate request" in SSL 3 drafts and in + * RFC 2246): + */ + && (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) + /* + * ... except when the application insists on + * verification (against the specs, but s3_clnt.c accepts + * this for SSL 3) + */ + || (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) + /* don't request certificate for SRP auth */ + && !(s->s3->tmp.new_cipher->algorithm_auth & SSL_aSRP) + /* + * With normal PSK Certificates and Certificate Requests + * are omitted + */ + && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_PSK)) { + return 1; + } + + return 0; +} + +/* + * server_write_transition() works out what handshake state to move to next + * when the server is writing messages to be sent to the client. + */ +enum WRITE_TRAN server_write_transition(SSL *s) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_BEFORE: + /* Just go straight to trying to read from the client */; + return WRITE_TRAN_FINISHED; + + case TLS_ST_OK: + /* We must be trying to renegotiate */ + st->hand_state = TLS_ST_SW_HELLO_REQ; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_HELLO_REQ: + st->hand_state = TLS_ST_OK; + statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SR_CLNT_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) + st->hand_state = TLS_ST_SW_SESSION_TICKET; + else + st->hand_state = TLS_ST_SW_CHANGE; + } else { + /* Check if it is anon DH or anon ECDH, */ + /* normal PSK or SRP */ + if (!(s->s3->tmp.new_cipher->algorithm_auth & + (SSL_aNULL | SSL_aSRP | SSL_aPSK))) { + st->hand_state = TLS_ST_SW_CERT; + } else if (send_server_key_exchange(s)) { + st->hand_state = TLS_ST_SW_KEY_EXCH; + } else if (send_certificate_request(s)) { + st->hand_state = TLS_ST_SW_CERT_REQ; + } else { + st->hand_state = TLS_ST_SW_SRVR_DONE; + } + } + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_CERT: + if (s->tlsext_status_expected) { + st->hand_state = TLS_ST_SW_CERT_STATUS; + return WRITE_TRAN_CONTINUE; + } + /* Fall through */ + + case TLS_ST_SW_CERT_STATUS: + if (send_server_key_exchange(s)) { + st->hand_state = TLS_ST_SW_KEY_EXCH; + return WRITE_TRAN_CONTINUE; + } + /* Fall through */ + + case TLS_ST_SW_KEY_EXCH: + if (send_certificate_request(s)) { + st->hand_state = TLS_ST_SW_CERT_REQ; + return WRITE_TRAN_CONTINUE; + } + /* Fall through */ + + case TLS_ST_SW_CERT_REQ: + st->hand_state = TLS_ST_SW_SRVR_DONE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_SRVR_DONE: + return WRITE_TRAN_FINISHED; + + case TLS_ST_SR_FINISHED: + if (s->hit) { + st->hand_state = TLS_ST_OK; + statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + } else if (s->tlsext_ticket_expected) { + st->hand_state = TLS_ST_SW_SESSION_TICKET; + } else { + st->hand_state = TLS_ST_SW_CHANGE; + } + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_SESSION_TICKET: + st->hand_state = TLS_ST_SW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_CHANGE: + st->hand_state = TLS_ST_SW_FINISHED; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_FINISHED: + if (s->hit) { + return WRITE_TRAN_FINISHED; + } + st->hand_state = TLS_ST_OK; + statem_set_in_init(s, 0); + 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 server to the client. + */ +enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst) +{ + STATEM *st = &s->statem; + + 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: + s->session->cipher = s->s3->tmp.new_cipher; + if (!s->method->ssl3_enc->setup_key_block(s)) { + 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: + 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 + * server to the client. + */ +enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst) +{ + STATEM *st = &s->statem; + + s->init_num = 0; + + switch(st->hand_state) { + case TLS_ST_SW_HELLO_REQ: + if (statem_flush(s) != 1) + return WORK_MORE_A; + 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: + if (statem_flush(s) != 1) + return WORK_MORE_A; + break; + + 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: + /* No post work to be done */ + break; + } + + return WORK_FINISHED_CONTINUE; +} + +/* + * Construct a message to be sent from the server to the client. + * + * Valid return values are: + * 1: Success + * 0: Error + */ +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); + + case TLS_ST_SW_SRVR_HELLO: + return tls_construct_server_hello(s); + + case TLS_ST_SW_CERT: + return tls_construct_server_certificate(s); + + case TLS_ST_SW_KEY_EXCH: + return tls_construct_server_key_exchange(s); + + case TLS_ST_SW_CERT_REQ: + return tls_construct_certificate_request(s); + + case TLS_ST_SW_SRVR_DONE: + return tls_construct_server_done(s); + + case TLS_ST_SW_SESSION_TICKET: + return tls_construct_new_session_ticket(s); + + case TLS_ST_SW_CERT_STATUS: + return tls_construct_cert_status(s); + + case TLS_ST_SW_CHANGE: + if (SSL_IS_DTLS(s)) + return dtls_construct_change_cipher_spec(s); + else + return tls_construct_change_cipher_spec(s); + + case TLS_ST_SW_FINISHED: + return tls_construct_finished(s, + s->method-> + ssl3_enc->server_finished_label, + s->method-> + ssl3_enc->server_finished_label_len); + + default: + /* Shouldn't happen */ + break; + } + + return 0; +} + +#define CLIENT_KEY_EXCH_MAX_LENGTH 2048 +#define NEXT_PROTO_MAX_LENGTH 514 + +/* + * Returns the maximum allowed length for the current message that we are + * reading. Excludes the message header. + */ +unsigned long server_max_message_size(SSL *s) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_SR_CLNT_HELLO: + return SSL3_RT_MAX_PLAIN_LENGTH; + + case TLS_ST_SR_CERT: + return s->max_cert_list; + + case TLS_ST_SR_KEY_EXCH: + return CLIENT_KEY_EXCH_MAX_LENGTH; + + case TLS_ST_SR_CERT_VRFY: + return SSL3_RT_MAX_PLAIN_LENGTH; + +#ifndef OPENSSL_NO_NEXTPROTONEG + case TLS_ST_SR_NEXT_PROTO: + return NEXT_PROTO_MAX_LENGTH; +#endif + + case TLS_ST_SR_CHANGE: + return CCS_MAX_LENGTH; + + case TLS_ST_SR_FINISHED: + return FINISHED_MAX_LENGTH; + + default: + /* Shouldn't happen */ + break; + } + + return 0; +} + +/* + * Process a message that the server has received from the client. + */ +enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_SR_CLNT_HELLO: + return tls_process_client_hello(s, pkt); + + case TLS_ST_SR_CERT: + return tls_process_client_certificate(s, pkt); + + case TLS_ST_SR_KEY_EXCH: + return tls_process_client_key_exchange(s, pkt); + + case TLS_ST_SR_CERT_VRFY: + return tls_process_cert_verify(s, pkt); + +#ifndef OPENSSL_NO_NEXTPROTONEG + case TLS_ST_SR_NEXT_PROTO: + return tls_process_next_proto(s, pkt); +#endif + + case TLS_ST_SR_CHANGE: + return tls_process_change_cipher_spec(s, pkt); + + case TLS_ST_SR_FINISHED: + return tls_process_finished(s, pkt); + + default: + /* Shouldn't happen */ + break; + } + + return MSG_PROCESS_ERROR; +} + +/* + * Perform any further processing required following the receipt of a message + * from the client + */ +enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_SR_CLNT_HELLO: + return tls_post_process_client_hello(s, 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); + else + return WORK_FINISHED_STOP; + default: + break; + } + + /* Shouldn't happen */ + return WORK_ERROR; +} + #ifndef OPENSSL_NO_SRP static int ssl_check_srp_ext_ClientHello(SSL *s, int *al) { -- GitLab