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_BODY 351
|
||||||
# define SSL_F_TLS_GET_MESSAGE_HEADER 350
|
# define SSL_F_TLS_GET_MESSAGE_HEADER 350
|
||||||
# define SSL_F_TLS_POST_PROCESS_CLIENT_HELLO 378
|
# 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_PREPARE_CLIENT_CERTIFICATE 360
|
||||||
# define SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST 361
|
# define SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST 361
|
||||||
# define SSL_F_TLS_PROCESS_CERT_STATUS 362
|
# define SSL_F_TLS_PROCESS_CERT_STATUS 362
|
||||||
|
@ -193,6 +193,7 @@ static int ssl_check_srp_ext_ClientHello(SSL *s, int *al)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
int ssl3_accept(SSL *s)
|
int ssl3_accept(SSL *s)
|
||||||
{
|
{
|
||||||
BUF_MEM *buf;
|
BUF_MEM *buf;
|
||||||
@ -811,6 +812,7 @@ int ssl3_accept(SSL *s)
|
|||||||
cb(s, SSL_CB_ACCEPT_EXIT, ret);
|
cb(s, SSL_CB_ACCEPT_EXIT, ret);
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int ssl3_send_hello_request(SSL *s)
|
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;
|
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 ssl3_get_cert_verify(SSL *s)
|
||||||
{
|
{
|
||||||
int ok, ret = 1;
|
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_GET_MESSAGE_HEADER), "tls_get_message_header"},
|
||||||
{ERR_FUNC(SSL_F_TLS_POST_PROCESS_CLIENT_HELLO),
|
{ERR_FUNC(SSL_F_TLS_POST_PROCESS_CLIENT_HELLO),
|
||||||
"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),
|
{ERR_FUNC(SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE),
|
||||||
"tls_prepare_client_certificate"},
|
"tls_prepare_client_certificate"},
|
||||||
{ERR_FUNC(SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST),
|
{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,
|
static enum MSG_PROCESS_RETURN client_process_message(SSL *s,
|
||||||
unsigned long len);
|
unsigned long len);
|
||||||
static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst);
|
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
|
* 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);
|
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
|
* 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
|
* 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;
|
cb = s->ctx->info_callback;
|
||||||
|
|
||||||
if(s->server) {
|
if(s->server) {
|
||||||
/* TODO: Fill these in later when we've implemented them */
|
transition = server_read_transition;
|
||||||
transition = NULL;
|
process_message = server_process_message;
|
||||||
process_message = NULL;
|
max_message_size = server_max_message_size;
|
||||||
post_process_message = NULL;
|
post_process_message = server_post_process_message;
|
||||||
max_message_size = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
transition = client_read_transition;
|
transition = client_read_transition;
|
||||||
process_message = client_process_message;
|
process_message = client_process_message;
|
||||||
@ -668,11 +682,10 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s)
|
|||||||
cb = s->ctx->info_callback;
|
cb = s->ctx->info_callback;
|
||||||
|
|
||||||
if(s->server) {
|
if(s->server) {
|
||||||
/* TODO: Fill these in later when we've implemented them */
|
transition = server_write_transition;
|
||||||
transition = NULL;
|
pre_work = server_pre_work;
|
||||||
pre_work = NULL;
|
post_work = server_post_work;
|
||||||
post_work = NULL;
|
construct_message = server_construct_message;
|
||||||
construct_message = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
transition = client_write_transition;
|
transition = client_write_transition;
|
||||||
pre_work = client_pre_work;
|
pre_work = client_pre_work;
|
||||||
@ -780,29 +793,29 @@ int statem_app_data_allowed(SSL *s)
|
|||||||
{
|
{
|
||||||
STATEM *st = &s->statem;
|
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))
|
if (!s->s3->in_read_app_data || (s->s3->total_renegotiations == 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!s->server) {
|
if (s->server) {
|
||||||
if (st->state == MSG_FLOW_UNINITED || st->state == MSG_FLOW_RENEGOTIATE)
|
/*
|
||||||
return 0;
|
* 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_CW_CLNT_HELLO)
|
*/
|
||||||
|
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 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1459,3 +1472,588 @@ static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst)
|
|||||||
/* Shouldn't happen */
|
/* Shouldn't happen */
|
||||||
return WORK_ERROR;
|
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