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 <tjh@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
This commit is contained in:
Matt Caswell 2015-09-08 09:19:22 +01:00
parent e27f234a41
commit 94836de2ae
4 changed files with 721 additions and 27 deletions

View File

@ -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

View File

@ -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;

View File

@ -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),

View File

@ -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 (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 (!s->server) {
if (st->state == MSG_FLOW_UNINITED || st->state == MSG_FLOW_RENEGOTIATE)
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;
}