From 94836de2aeab65869caf2aa9a260114a309aaf0a Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 8 Sep 2015 09:19:22 +0100 Subject: [PATCH] Move server side TLS to new state machine Implement all of the necessary changes for moving TLS server side processing into the new state machine code. Reviewed-by: Tim Hudson Reviewed-by: Richard Levitte --- include/openssl/ssl.h | 1 + ssl/s3_srvr.c | 93 ++++++ ssl/ssl_err.c | 2 + ssl/statem.c | 652 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 721 insertions(+), 27 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 6660883a2e..75bca7cdb4 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2118,6 +2118,7 @@ void ERR_load_SSL_strings(void); # define SSL_F_TLS_GET_MESSAGE_BODY 351 # define SSL_F_TLS_GET_MESSAGE_HEADER 350 # define SSL_F_TLS_POST_PROCESS_CLIENT_HELLO 378 +# define SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE 384 # define SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE 360 # define SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST 361 # define SSL_F_TLS_PROCESS_CERT_STATUS 362 diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 98e831c58e..992df70f4c 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -193,6 +193,7 @@ static int ssl_check_srp_ext_ClientHello(SSL *s, int *al) } #endif +#if 0 int ssl3_accept(SSL *s) { BUF_MEM *buf; @@ -811,6 +812,7 @@ int ssl3_accept(SSL *s) cb(s, SSL_CB_ACCEPT_EXIT, ret); return (ret); } +#endif int ssl3_send_hello_request(SSL *s) { @@ -2871,6 +2873,97 @@ enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, long n) return MSG_PROCESS_ERROR; } +enum WORK_STATE tls_post_process_client_key_exchange(SSL *s, + enum WORK_STATE wst) +{ + +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s)) { + unsigned char sctpauthkey[64]; + char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)]; + /* + * Add new shared key for SCTP-Auth, will be ignored if no SCTP + * used. + */ + snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL), + DTLS1_SCTP_AUTH_LABEL); + + if (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 + + if (s->no_cert_verify) { + /* No certificate verify so we no longer need the handshake_buffer */ + BIO_free(s->s3->handshake_buffer); + return WORK_FINISHED_CONTINUE; + } else if (SSL_USE_SIGALGS(s)) { + if (!s->session->peer) { + /* No peer certificate so we no longer need the handshake_buffer */ + BIO_free(s->s3->handshake_buffer); + return WORK_FINISHED_CONTINUE; + } + if (!s->s3->handshake_buffer) { + SSLerr(SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE, + ERR_R_INTERNAL_ERROR); + statem_set_error(s); + return WORK_ERROR; + } + /* + * For sigalgs freeze the handshake buffer. If we support + * extms we've done this already so this is a no-op + */ + if (!ssl3_digest_cached_records(s, 1)) { + statem_set_error(s); + return WORK_ERROR; + } + } else { + int offset = 0; + int dgst_num; + + /* + * We need to get hashes here so if there is a client cert, + * it can be verified FIXME - digest processing for + * CertificateVerify should be generalized. But it is next + * step + */ + if (!ssl3_digest_cached_records(s, 0)) { + statem_set_error(s); + return WORK_ERROR; + } + for (dgst_num = 0; dgst_num < SSL_MAX_DIGEST; dgst_num++) { + if (s->s3->handshake_dgst[dgst_num]) { + int dgst_size; + + s->method->ssl3_enc->cert_verify_mac(s, + EVP_MD_CTX_type + (s-> + s3->handshake_dgst + [dgst_num]), + &(s->s3-> + tmp.cert_verify_md + [offset])); + dgst_size = + EVP_MD_CTX_size(s->s3->handshake_dgst[dgst_num]); + if (dgst_size < 0) { + statem_set_error(s); + return WORK_ERROR; + } + offset += dgst_size; + } + } + } + + return WORK_FINISHED_CONTINUE; +} + int ssl3_get_cert_verify(SSL *s) { int ok, ret = 1; diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 12ad542fd4..fe4201ba66 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -361,6 +361,8 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_TLS_GET_MESSAGE_HEADER), "tls_get_message_header"}, {ERR_FUNC(SSL_F_TLS_POST_PROCESS_CLIENT_HELLO), "tls_post_process_client_hello"}, + {ERR_FUNC(SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE), + "tls_post_process_client_key_exchange"}, {ERR_FUNC(SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE), "tls_prepare_client_certificate"}, {ERR_FUNC(SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST), diff --git a/ssl/statem.c b/ssl/statem.c index 7f2176f80d..e2cf11e114 100644 --- a/ssl/statem.c +++ b/ssl/statem.c @@ -119,6 +119,16 @@ 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); +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, unsigned long len); +static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst); /* * Clear the state machine state and reset back to MSG_FLOW_UNINITED @@ -156,6 +166,11 @@ int dtls1_connect(SSL *s) return state_machine(s, 0); } +int ssl3_accept(SSL *s) +{ + return state_machine(s, 1); +} + /* * The main message flow state machine. We start in the MSG_FLOW_UNINITED or * MSG_FLOW_RENEGOTIATE state and finish in MSG_FLOW_FINISHED. Valid states and @@ -472,11 +487,10 @@ static enum SUB_STATE_RETURN read_state_machine(SSL *s) { cb = s->ctx->info_callback; if(s->server) { - /* TODO: Fill these in later when we've implemented them */ - transition = NULL; - process_message = NULL; - post_process_message = NULL; - max_message_size = NULL; + transition = server_read_transition; + process_message = server_process_message; + max_message_size = server_max_message_size; + post_process_message = server_post_process_message; } else { transition = client_read_transition; process_message = client_process_message; @@ -668,11 +682,10 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s) cb = s->ctx->info_callback; if(s->server) { - /* TODO: Fill these in later when we've implemented them */ - transition = NULL; - pre_work = NULL; - post_work = NULL; - construct_message = NULL; + transition = server_write_transition; + pre_work = server_pre_work; + post_work = server_post_work; + construct_message = server_construct_message; } else { transition = client_write_transition; pre_work = client_pre_work; @@ -780,29 +793,29 @@ int statem_app_data_allowed(SSL *s) { STATEM *st = &s->statem; - if (!s->s3->in_read_app_data || (s->s3->total_renegotiations == 0)) + if (st->state == MSG_FLOW_UNINITED || st->state == MSG_FLOW_RENEGOTIATE) return 0; - if (!s->server) { - if (st->state == MSG_FLOW_UNINITED || st->state == MSG_FLOW_RENEGOTIATE) - return 0; + if (!s->s3->in_read_app_data || (s->s3->total_renegotiations == 0)) + return 0; - if(st->hand_state == TLS_ST_CW_CLNT_HELLO) + if (s->server) { + /* + * If we're a server and we haven't got as far as writing our + * ServerHello yet then we allow app data + */ + if (st->hand_state == TLS_ST_BEFORE + || st->hand_state == TLS_ST_SR_CLNT_HELLO) + return 1; + } else { + /* + * If we're a client and we haven't read the ServerHello yet then we + * allow app data + */ + 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 a later commit - */ - if ((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; } @@ -1459,3 +1472,588 @@ static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst) /* 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: + 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; + /* TODO: This needs removing */ + s->state = SSL_ST_OK; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SR_CLNT_HELLO: + st->hand_state = TLS_ST_SW_SRVR_HELLO; + return WRITE_TRAN_CONTINUE; + + 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; + /* TODO: This needs removing */ + s->state = SSL_ST_OK; + 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; + /* 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 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; + 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; + } + 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 TLS_ST_SW_CHANGE: + if (!s->method->ssl3_enc->change_cipher_state(s, + SSL3_CHANGE_CIPHER_SERVER_WRITE)) { + statem_set_error(s); + return WORK_ERROR; + } + 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; + 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 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, + unsigned long len) +{ + STATEM *st = &s->statem; + + switch(st->hand_state) { + case TLS_ST_SR_CLNT_HELLO: + return tls_process_client_hello(s, len); + + case TLS_ST_SR_CERT: + return tls_process_client_certificate(s, len); + + case TLS_ST_SR_KEY_EXCH: + return tls_process_client_key_exchange(s, len); + + case TLS_ST_SR_CERT_VRFY: + return tls_process_cert_verify(s, len); + +#ifndef OPENSSL_NO_NEXTPROTONEG + case TLS_ST_SR_NEXT_PROTO: + return tls_process_next_proto(s, len); +#endif + + case TLS_ST_SR_CHANGE: + return tls_process_change_cipher_spec(s, len); + + case TLS_ST_SR_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 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_FINISHED: + if (s->hit) + return tls_finish_handshake(s, wst); + else + return WORK_FINISHED_STOP; + default: + break; + } + + /* Shouldn't happen */ + return WORK_ERROR; +} -- 2.40.0