summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2015-05-11 09:35:41 +0100
committerMatt Caswell <matt@openssl.org>2015-08-03 11:18:05 +0100
commit657da85eea3a5825b2dd25ff25b99ec206c48136 (patch)
tree5ebbb703144f69c2f570b7cf66e8107b6897095b
parent9ceb2426b0a7972434a49a34e78bdcc6437e04ad (diff)
Move TLS CCS processing into the state machine
The handling of incoming CCS records is a little strange. Since CCS is not a handshake message it is handled differently to normal handshake messages. Unfortunately whilst technically it is not a handhshake message the reality is that it must be processed in accordance with the state of the handshake. Currently CCS records are processed entirely within the record layer. In order to ensure that it is handled in accordance with the handshake state a flag is used to indicate that it is an acceptable time to receive a CCS. Previously this flag did not exist (see CVE-2014-0224), but the flag should only really be considered a workaround for the problem that CCS is not visible to the state machine. Outgoing CCS messages are already handled within the state machine. This patch makes CCS visible to the TLS state machine. A separate commit will handle DTLS. Reviewed-by: Tim Hudson <tjh@openssl.org>
-rw-r--r--include/openssl/ssl.h1
-rw-r--r--include/openssl/ssl3.h8
-rw-r--r--ssl/d1_both.c12
-rw-r--r--ssl/record/rec_layer_d1.c3
-rw-r--r--ssl/record/rec_layer_s3.c82
-rw-r--r--ssl/record/record.h6
-rw-r--r--ssl/s3_both.c74
-rw-r--r--ssl/s3_clnt.c46
-rw-r--r--ssl/s3_lib.c8
-rw-r--r--ssl/s3_srvr.c69
-rw-r--r--ssl/ssl_err.c2
-rw-r--r--ssl/ssl_locl.h5
12 files changed, 174 insertions, 142 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 6b6560dc75..892e129611 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1943,6 +1943,7 @@ void ERR_load_SSL_strings(void);
# define SSL_F_SSL3_GET_CERTIFICATE_REQUEST 135
# define SSL_F_SSL3_GET_CERT_STATUS 289
# define SSL_F_SSL3_GET_CERT_VERIFY 136
+# define SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC 348
# define SSL_F_SSL3_GET_CLIENT_CERTIFICATE 137
# define SSL_F_SSL3_GET_CLIENT_HELLO 138
# define SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE 139
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index 43df925999..ec339de351 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -365,11 +365,6 @@ extern "C" {
# define TLS1_FLAGS_TLS_PADDING_BUG 0x0
# define TLS1_FLAGS_SKIP_CERT_VERIFY 0x0010
-/*
- * Set when the handshake is ready to process peer's ChangeCipherSpec message.
- * Cleared after the message has been processed.
- */
-# define SSL3_FLAGS_CCS_OK 0x0080
/* Set if we encrypt then mac instead of usual mac then encrypt */
# define TLS1_FLAGS_ENCRYPT_THEN_MAC 0x0100
@@ -499,6 +494,9 @@ extern "C" {
# endif
# define DTLS1_MT_HELLO_VERIFY_REQUEST 3
+/* Dummy message type for handling CCS like a normal handshake message */
+# define SSL3_MT_CHANGE_CIPHER_SPEC 0x0101
+
# define SSL3_MT_CCS 1
/* These are used when changing over to a new cipher */
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 155b8bffe0..a1499da3eb 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -679,7 +679,7 @@ dtls1_reassemble_fragment(SSL *s, const struct hm_header_st *msg_hdr, int *ok)
unsigned char devnull[256];
while (frag_len) {
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
devnull,
frag_len >
sizeof(devnull) ? sizeof(devnull) :
@@ -692,7 +692,7 @@ dtls1_reassemble_fragment(SSL *s, const struct hm_header_st *msg_hdr, int *ok)
}
/* read the body of the fragment (header has already been read */
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
frag->fragment + msg_hdr->frag_off,
frag_len, 0);
if ((unsigned long)i != frag_len)
@@ -775,7 +775,7 @@ dtls1_process_out_of_seq_message(SSL *s, const struct hm_header_st *msg_hdr,
unsigned char devnull[256];
while (frag_len) {
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
devnull,
frag_len >
sizeof(devnull) ? sizeof(devnull) :
@@ -801,7 +801,7 @@ dtls1_process_out_of_seq_message(SSL *s, const struct hm_header_st *msg_hdr,
/*
* read the body of the fragment (header has already been read
*/
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
frag->fragment, frag_len, 0);
if ((unsigned long)i != frag_len)
i = -1;
@@ -851,7 +851,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
}
/* read handshake message header */
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, wire,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL, wire,
DTLS1_HM_HEADER_LENGTH, 0);
if (i <= 0) { /* nbio, or an error */
s->rwstate = SSL_READING;
@@ -926,7 +926,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
unsigned char *p =
(unsigned char *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
&p[frag_off], frag_len, 0);
/*
diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c
index 52ef8f0834..2c8b94f79b 100644
--- a/ssl/record/rec_layer_d1.c
+++ b/ssl/record/rec_layer_d1.c
@@ -395,7 +395,8 @@ int dtls1_process_buffered_records(SSL *s)
* Application data protocol
* none of our business
*/
-int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
+int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
+ int len, int peek)
{
int al, i, j, ret;
unsigned int n;
diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index d6e922c652..6feba42518 100644
--- a/ssl/record/rec_layer_s3.c
+++ b/ssl/record/rec_layer_s3.c
@@ -955,8 +955,9 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
* (possibly multiple records if we still don't have anything to return).
*
* This function must handle any surprises the peer may have for us, such as
- * Alert records (e.g. close_notify), ChangeCipherSpec records (not really
- * a surprise, but handled as if it were), or renegotiation requests.
+ * Alert records (e.g. close_notify) or renegotiation requests. ChangeCipherSpec
+ * messages are treated as if they were handshake messages *if* the |recd_type|
+ * argument is non NULL.
* Also if record payloads contain fragments too small to process, we store
* them until there is enough for the respective protocol (the record protocol
* may use arbitrary fragmentation and even interleaving):
@@ -971,7 +972,8 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
* Application data protocol
* none of our business
*/
-int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
+int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
+ int len, int peek)
{
int al, i, j, ret;
unsigned int n;
@@ -1066,9 +1068,14 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
return (0);
}
- if (type == SSL3_RECORD_get_type(rr)) {
- /* SSL3_RT_APPLICATION_DATA or
- * SSL3_RT_HANDSHAKE */
+ if (type == SSL3_RECORD_get_type(rr)
+ || (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
+ && type == SSL3_RT_HANDSHAKE && recvd_type != NULL)) {
+ /*
+ * SSL3_RT_APPLICATION_DATA or
+ * SSL3_RT_HANDSHAKE or
+ * SSL3_RT_CHANGE_CIPHER_SPEC
+ */
/*
* make sure that we are not getting application data when we are
* doing a handshake for the first time
@@ -1080,6 +1087,17 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
goto f_err;
}
+ if (type == SSL3_RT_HANDSHAKE
+ && SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
+ && s->rlayer.handshake_fragment_len > 0) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
+ goto f_err;
+ }
+
+ if (recvd_type != NULL)
+ *recvd_type = SSL3_RECORD_get_type(rr);
+
if (len <= 0)
return (len);
@@ -1105,9 +1123,16 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
/*
* If we get here, then type != rr->type; if we have a handshake message,
- * then it was unexpected (Hello Request or Client Hello).
+ * then it was unexpected (Hello Request or Client Hello) or invalid (we
+ * were actually expecting a CCS).
*/
+ if (rr->type == SSL3_RT_HANDSHAKE && type == SSL3_RT_CHANGE_CIPHER_SPEC) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_UNEXPECTED_MESSAGE);
+ goto f_err;
+ }
+
/*
* Lets just double check that we've not got an SSLv2 record
*/
@@ -1344,45 +1369,9 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
}
if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
- /*
- * 'Change Cipher Spec' is just a single byte, so we know exactly
- * what the record payload has to look like
- */
- if ((SSL3_RECORD_get_length(rr) != 1)
- || (SSL3_RECORD_get_off(rr) != 0)
- || (SSL3_RECORD_get_data(rr)[0] != SSL3_MT_CCS)) {
- al = SSL_AD_ILLEGAL_PARAMETER;
- SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_BAD_CHANGE_CIPHER_SPEC);
- goto f_err;
- }
-
- /* Check we have a cipher to change to */
- if (s->s3->tmp.new_cipher == NULL) {
- al = SSL_AD_UNEXPECTED_MESSAGE;
- SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
- goto f_err;
- }
-
- if (!(s->s3->flags & SSL3_FLAGS_CCS_OK)) {
- al = SSL_AD_UNEXPECTED_MESSAGE;
- SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
- goto f_err;
- }
-
- s->s3->flags &= ~SSL3_FLAGS_CCS_OK;
-
- SSL3_RECORD_set_length(rr, 0);
-
- if (s->msg_callback)
- s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC,
- SSL3_RECORD_get_data(rr), 1, s,
- s->msg_callback_arg);
-
- s->s3->change_cipher_spec = 1;
- if (!ssl3_do_change_cipher_spec(s))
- goto err;
- else
- goto start;
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
+ goto f_err;
}
/*
@@ -1477,7 +1466,6 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
f_err:
ssl3_send_alert(s, SSL3_AL_FATAL, al);
- err:
return (-1);
}
diff --git a/ssl/record/record.h b/ssl/record/record.h
index 6931bb4712..5c8fead869 100644
--- a/ssl/record/record.h
+++ b/ssl/record/record.h
@@ -331,7 +331,8 @@ __owur int ssl3_pending(const SSL *s);
__owur int ssl3_write_bytes(SSL *s, int type, const void *buf, int len);
__owur int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
unsigned int len, int create_empty_fragment);
-__owur int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek);
+__owur int ssl3_read_bytes(SSL *s, int type, int *recvd_type,
+ unsigned char *buf, int len, int peek);
__owur int ssl3_setup_buffers(SSL *s);
__owur int ssl3_enc(SSL *s, int send_data);
__owur int n_ssl3_mac(SSL *ssl, unsigned char *md, int send_data);
@@ -345,7 +346,8 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl);
void DTLS_RECORD_LAYER_set_saved_w_epoch(RECORD_LAYER *rl, unsigned short e);
void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl);
void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl);
-__owur int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek);
+__owur int dtls1_read_bytes(SSL *s, int type, int *recvd_type,
+ unsigned char *buf, int len, int peek);
__owur int dtls1_write_bytes(SSL *s, int type, const void *buf, int len);
__owur int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
unsigned int len, int create_empty_fragement);
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 17a8054868..32193c3460 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -228,6 +228,47 @@ static void ssl3_take_mac(SSL *s)
}
#endif
+int ssl3_get_change_cipher_spec(SSL *s, int a, int b)
+{
+ int ok, al;
+ long n;
+
+ n = s->method->ssl_get_message(s, a, b, SSL3_MT_CHANGE_CIPHER_SPEC, 1, &ok);
+
+ if (!ok)
+ return ((int)n);
+
+ /*
+ * 'Change Cipher Spec' is just a single byte, which should already have
+ * been consumed by ssl_get_message() so there should be no bytes left
+ */
+ if (n != 0) {
+ al = SSL_AD_ILLEGAL_PARAMETER;
+ SSLerr(SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+ goto f_err;
+ }
+
+ /* Check we have a cipher to change to */
+ if (s->s3->tmp.new_cipher == NULL) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC, SSL_R_CCS_RECEIVED_EARLY);
+ goto f_err;
+ }
+
+ s->s3->change_cipher_spec = 1;
+ if (!ssl3_do_change_cipher_spec(s)) {
+ al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC, ERR_R_INTERNAL_ERROR);
+ goto f_err;
+ }
+
+ return 1;
+ f_err:
+ ssl3_send_alert(s, SSL3_AL_FATAL, al);
+ return 0;
+}
+
+
int ssl3_get_finished(SSL *s, int a, int b)
{
int al, i, ok;
@@ -345,7 +386,7 @@ long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
unsigned char *p;
unsigned long l;
long n;
- int i, al;
+ int i, al, recvd_type;
if (s->s3->tmp.reuse_message) {
s->s3->tmp.reuse_message = 0;
@@ -369,13 +410,38 @@ long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
do {
while (s->init_num < SSL3_HM_HEADER_LENGTH) {
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE,
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &recvd_type,
&p[s->init_num], SSL3_HM_HEADER_LENGTH - s->init_num, 0);
if (i <= 0) {
s->rwstate = SSL_READING;
*ok = 0;
return i;
}
+ if (s->init_num == 0
+ && recvd_type == SSL3_RT_CHANGE_CIPHER_SPEC
+ && (mt < 0 || mt == SSL3_MT_CHANGE_CIPHER_SPEC)) {
+ if (*p != SSL3_MT_CCS) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_GET_MESSAGE,
+ SSL_R_UNEXPECTED_MESSAGE);
+ goto f_err;
+ }
+ s->init_num = i - 1;
+ s->init_msg = p + 1;
+ s->s3->tmp.message_type = SSL3_MT_CHANGE_CIPHER_SPEC;
+ s->s3->tmp.message_size = i - 1;
+ s->state = stn;
+ *ok = 1;
+ if (s->msg_callback)
+ s->msg_callback(0, s->version,
+ SSL3_RT_CHANGE_CIPHER_SPEC, p, 1, s,
+ s->msg_callback_arg);
+ return i - 1;
+ } else if (recvd_type != SSL3_RT_HANDSHAKE) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_GET_MESSAGE, SSL_R_CCS_RECEIVED_EARLY);
+ goto f_err;
+ }
s->init_num += i;
}
@@ -458,8 +524,8 @@ long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
p = s->init_msg;
n = s->s3->tmp.message_size - s->init_num;
while (n > 0) {
- i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &p[s->init_num],
- n, 0);
+ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, NULL,
+ &p[s->init_num], n, 0);
if (i <= 0) {
s->rwstate = SSL_READING;
*ok = 0;
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 080dbf0f18..cd6918aa6b 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -165,7 +165,7 @@
static int ssl_set_version(SSL *s);
static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b);
-static int ssl3_check_finished(SSL *s);
+static int ssl3_check_change(SSL *s);
static int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk,
unsigned char *p,
int (*put_cb) (const SSL_CIPHER *,
@@ -276,7 +276,6 @@ int ssl3_connect(SSL *s)
s->state = SSL3_ST_CW_CLNT_HELLO_A;
s->ctx->stats.sess_connect++;
s->init_num = 0;
- s->s3->flags &= ~SSL3_FLAGS_CCS_OK;
/*
* Should have been reset by ssl3_get_finished, too.
*/
@@ -306,7 +305,7 @@ int ssl3_connect(SSL *s)
goto end;
if (s->hit) {
- s->state = SSL3_ST_CR_FINISHED_A;
+ s->state = SSL3_ST_CR_CHANGE_A;
if (s->tlsext_ticket_expected) {
/* receive renewed session ticket */
s->state = SSL3_ST_CR_SESSION_TICKET_A;
@@ -319,12 +318,12 @@ int ssl3_connect(SSL *s)
case SSL3_ST_CR_CERT_A:
case SSL3_ST_CR_CERT_B:
/* Noop (ret = 0) for everything but EAP-FAST. */
- ret = ssl3_check_finished(s);
+ ret = ssl3_check_change(s);
if (ret < 0)
goto end;
if (ret == 1) {
s->hit = 1;
- s->state = SSL3_ST_CR_FINISHED_A;
+ s->state = SSL3_ST_CR_CHANGE_A;
s->init_num = 0;
break;
}
@@ -525,7 +524,7 @@ int ssl3_connect(SSL *s)
if (s->tlsext_ticket_expected)
s->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A;
else
- s->s3->tmp.next_state = SSL3_ST_CR_FINISHED_A;
+ s->s3->tmp.next_state = SSL3_ST_CR_CHANGE_A;
}
s->init_num = 0;
break;
@@ -535,7 +534,7 @@ int ssl3_connect(SSL *s)
ret = ssl3_get_new_session_ticket(s);
if (ret <= 0)
goto end;
- s->state = SSL3_ST_CR_FINISHED_A;
+ s->state = SSL3_ST_CR_CHANGE_A;
s->init_num = 0;
break;
@@ -548,10 +547,19 @@ int ssl3_connect(SSL *s)
s->init_num = 0;
break;
+ case SSL3_ST_CR_CHANGE_A:
+ case SSL3_ST_CR_CHANGE_B:
+ ret = ssl3_get_change_cipher_spec(s, SSL3_ST_CR_CHANGE_A,
+ SSL3_ST_CR_CHANGE_B);
+ if (ret <= 0)
+ goto end;
+
+ s->state = SSL3_ST_CR_FINISHED_A;
+ s->init_num = 0;
+ break;
+
case SSL3_ST_CR_FINISHED_A:
case SSL3_ST_CR_FINISHED_B:
- if (!s->s3->change_cipher_spec)
- s->s3->flags |= SSL3_FLAGS_CCS_OK;
ret = ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A,
SSL3_ST_CR_FINISHED_B);
if (ret <= 0)
@@ -3368,11 +3376,11 @@ int ssl3_check_cert_and_algorithm(SSL *s)
* the session ID. EAP-FAST (RFC 4851), however, relies on the next server
* message after the ServerHello to determine if the server is resuming.
* Therefore, we allow EAP-FAST to peek ahead.
- * ssl3_check_finished returns 1 if we are resuming from an external
- * pre-shared secret, we have a "ticket" and the next server handshake message
- * is Finished; and 0 otherwise. It returns -1 upon an error.
+ * ssl3_check_change returns 1 if we are resuming from an external
+ * pre-shared secret, we have a "ticket" and the next server message
+ * is CCS; and 0 otherwise. It returns -1 upon an error.
*/
-static int ssl3_check_finished(SSL *s)
+static int ssl3_check_change(SSL *s)
{
int ok = 0;
@@ -3380,8 +3388,6 @@ static int ssl3_check_finished(SSL *s)
!s->session->tlsext_tick)
return 0;
- /* Need to permit this temporarily, in case the next message is Finished. */
- s->s3->flags |= SSL3_FLAGS_CCS_OK;
/*
* This function is called when we might get a Certificate message instead,
* so permit appropriate message length.
@@ -3392,23 +3398,15 @@ static int ssl3_check_finished(SSL *s)
SSL3_ST_CR_CERT_A,
SSL3_ST_CR_CERT_B,
-1, s->max_cert_list, &ok);
- s->s3->flags &= ~SSL3_FLAGS_CCS_OK;
if (!ok)
return -1;
s->s3->tmp.reuse_message = 1;
- if (s->s3->tmp.message_type == SSL3_MT_FINISHED)
+ if (s->s3->tmp.message_type == SSL3_MT_CHANGE_CIPHER_SPEC)
return 1;
- /* If we're not done, then the CCS arrived early and we should bail. */
- if (s->s3->change_cipher_spec) {
- SSLerr(SSL_F_SSL3_CHECK_FINISHED, SSL_R_CCS_RECEIVED_EARLY);
- ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
- return -1;
- }
-
return 0;
}
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 0fc08819ca..d39346af65 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -4808,7 +4808,7 @@ int ssl3_shutdown(SSL *s)
/*
* If we are waiting for a close from our peer, we are closed
*/
- s->method->ssl_read_bytes(s, 0, NULL, 0, 0);
+ s->method->ssl_read_bytes(s, 0, NULL, NULL, 0, 0);
if (!(s->shutdown & SSL_RECEIVED_SHUTDOWN)) {
return (-1); /* return WANT_READ */
}
@@ -4840,7 +4840,7 @@ static int ssl3_read_internal(SSL *s, void *buf, int len, int peek)
ssl3_renegotiate_check(s);
s->s3->in_read_app_data = 1;
ret =
- s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len,
+ s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len,
peek);
if ((ret == -1) && (s->s3->in_read_app_data == 2)) {
/*
@@ -4852,8 +4852,8 @@ static int ssl3_read_internal(SSL *s, void *buf, int len, int peek)
*/
s->in_handshake++;
ret =
- s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len,
- peek);
+ s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf,
+ len, peek);
s->in_handshake--;
} else
s->s3->in_read_app_data = 0;
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index bc7f84f2d1..fd4c87e9e6 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -281,7 +281,6 @@ int ssl3_accept(SSL *s)
s->init_num = 0;
s->s3->flags &= ~TLS1_FLAGS_SKIP_CERT_VERIFY;
- s->s3->flags &= ~SSL3_FLAGS_CCS_OK;
/*
* Should have been reset by ssl3_get_finished, too.
*/
@@ -576,14 +575,7 @@ int ssl3_accept(SSL *s)
* not sent. Also for GOST ciphersuites when the client uses
* its key from the certificate for key exchange.
*/
-#if defined(OPENSSL_NO_NEXTPROTONEG)
- s->state = SSL3_ST_SR_FINISHED_A;
-#else
- if (s->s3->next_proto_neg_seen)
- s->state = SSL3_ST_SR_NEXT_PROTO_A;
- else
- s->state = SSL3_ST_SR_FINISHED_A;
-#endif
+ s->state = SSL3_ST_SR_CHANGE_A;
s->init_num = 0;
} else if (SSL_USE_SIGALGS(s)) {
s->state = SSL3_ST_SR_CERT_VRFY_A;
@@ -650,32 +642,13 @@ int ssl3_accept(SSL *s)
if (ret <= 0)
goto end;
-#if defined(OPENSSL_NO_NEXTPROTONEG)
- s->state = SSL3_ST_SR_FINISHED_A;
-#else
- if (s->s3->next_proto_neg_seen)
- s->state = SSL3_ST_SR_NEXT_PROTO_A;
- else
- s->state = SSL3_ST_SR_FINISHED_A;
-#endif
+ s->state = SSL3_ST_SR_CHANGE_A;
s->init_num = 0;
break;
#if !defined(OPENSSL_NO_NEXTPROTONEG)
case SSL3_ST_SR_NEXT_PROTO_A:
case SSL3_ST_SR_NEXT_PROTO_B:
- /*
- * Enable CCS for NPN. Receiving a CCS clears the flag, so make
- * sure not to re-enable it to ban duplicates. This *should* be the
- * first time we have received one - but we check anyway to be
- * cautious.
- * s->s3->change_cipher_spec is set when a CCS is
- * processed in s3_pkt.c, and remains set until
- * the client's Finished message is read.
- */
- if (!s->s3->change_cipher_spec)
- s->s3->flags |= SSL3_FLAGS_CCS_OK;
-
ret = ssl3_get_next_proto(s);
if (ret <= 0)
goto end;
@@ -684,18 +657,27 @@ int ssl3_accept(SSL *s)
break;
#endif
+
+ case SSL3_ST_SR_CHANGE_A:
+ case SSL3_ST_SR_CHANGE_B:
+ ret = ssl3_get_change_cipher_spec(s, SSL3_ST_SR_CHANGE_A,
+ SSL3_ST_SR_CHANGE_B);
+ if (ret <= 0)
+ goto end;
+
+#if defined(OPENSSL_NO_NEXTPROTONEG)
+ s->state = SSL3_ST_SR_FINISHED_A;
+#else
+ if (s->s3->next_proto_neg_seen)
+ s->state = SSL3_ST_SR_NEXT_PROTO_A;
+ else
+ s->state = SSL3_ST_SR_FINISHED_A;
+#endif
+ s->init_num = 0;
+ break;
+
case SSL3_ST_SR_FINISHED_A:
case SSL3_ST_SR_FINISHED_B:
- /*
- * Enable CCS for handshakes without NPN. In NPN the CCS flag has
- * already been set. Receiving a CCS clears the flag, so make
- * sure not to re-enable it to ban duplicates.
- * s->s3->change_cipher_spec is set when a CCS is
- * processed in s3_pkt.c, and remains set until
- * the client's Finished message is read.
- */
- if (!s->s3->change_cipher_spec)
- s->s3->flags |= SSL3_FLAGS_CCS_OK;
ret = ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A,
SSL3_ST_SR_FINISHED_B);
if (ret <= 0)
@@ -769,14 +751,7 @@ int ssl3_accept(SSL *s)
goto end;
s->state = SSL3_ST_SW_FLUSH;
if (s->hit) {
-#if defined(OPENSSL_NO_NEXTPROTONEG)
- s->s3->tmp.next_state = SSL3_ST_SR_FINISHED_A;
-#else
- if (s->s3->next_proto_neg_seen) {
- s->s3->tmp.next_state = SSL3_ST_SR_NEXT_PROTO_A;
- } else
- s->s3->tmp.next_state = SSL3_ST_SR_FINISHED_A;
-#endif
+ s->s3->tmp.next_state = SSL3_ST_SR_CHANGE_A;
} else
s->s3->tmp.next_state = SSL_ST_OK;
s->init_num = 0;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 4b4d89ce7a..539146ff08 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -131,6 +131,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
"ssl3_get_certificate_request"},
{ERR_FUNC(SSL_F_SSL3_GET_CERT_STATUS), "ssl3_get_cert_status"},
{ERR_FUNC(SSL_F_SSL3_GET_CERT_VERIFY), "ssl3_get_cert_verify"},
+ {ERR_FUNC(SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC),
+ "ssl3_get_change_cipher_spec"},
{ERR_FUNC(SSL_F_SSL3_GET_CLIENT_CERTIFICATE),
"ssl3_get_client_certificate"},
{ERR_FUNC(SSL_F_SSL3_GET_CLIENT_HELLO), "ssl3_get_client_hello"},
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 09975664e9..d13aa05b69 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -563,8 +563,8 @@ struct ssl_method_st {
int (*ssl_renegotiate_check) (SSL *s);
long (*ssl_get_message) (SSL *s, int st1, int stn, int mt, long
max, int *ok);
- int (*ssl_read_bytes) (SSL *s, int type, unsigned char *buf, int len,
- int peek);
+ int (*ssl_read_bytes) (SSL *s, int type, int *recvd_type,
+ unsigned char *buf, int len, int peek);
int (*ssl_write_bytes) (SSL *s, int type, const void *buf_, int len);
int (*ssl_dispatch_alert) (SSL *s);
long (*ssl_ctrl) (SSL *s, int cmd, long larg, void *parg);
@@ -1912,6 +1912,7 @@ void ssl3_init_finished_mac(SSL *s);
__owur int ssl3_send_server_certificate(SSL *s);
__owur int ssl3_send_newsession_ticket(SSL *s);
__owur int ssl3_send_cert_status(SSL *s);
+__owur int ssl3_get_change_cipher_spec(SSL *s, int a, int b);
__owur int ssl3_get_finished(SSL *s, int state_a, int state_b);
__owur int ssl3_setup_key_block(SSL *s);
__owur int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b);