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:
parent
e27f234a41
commit
94836de2ae
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
652
ssl/statem.c
652
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 (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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user