summaryrefslogtreecommitdiffstats
path: root/ssl
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2015-09-08 09:19:22 +0100
committerMatt Caswell <matt@openssl.org>2015-10-30 08:38:18 +0000
commit94836de2aeab65869caf2aa9a260114a309aaf0a (patch)
tree934d928e488d9de7e0a8477e2f7f2b283837477f /ssl
parente27f234a4147a7bd621d2a439c2cc2cc9a6a8382 (diff)
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>
Diffstat (limited to 'ssl')
-rw-r--r--ssl/s3_srvr.c93
-rw-r--r--ssl/ssl_err.c2
-rw-r--r--ssl/statem.c652
3 files changed, 720 insertions, 27 deletions
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;
+}