[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Mon Aug 3 12:02:37 UTC 2015


The branch master has been updated
       via  496dbe1855b486c39f42d673d56924d5f9ae3c78 (commit)
       via  e9f6b9a1a5ba9feaeeef88d9f45508996ce43468 (commit)
       via  c69f2adf71d888ba1a2090ec0be3319eb024efe3 (commit)
       via  657da85eea3a5825b2dd25ff25b99ec206c48136 (commit)
      from  9ceb2426b0a7972434a49a34e78bdcc6437e04ad (commit)


- Log -----------------------------------------------------------------
commit 496dbe1855b486c39f42d673d56924d5f9ae3c78
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jul 30 11:14:44 2015 +0100

    Fix make errors for the CCS changes
    
    The move of CCS into the state machine was causing make errors to fail. This
    fixes it.
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>

commit e9f6b9a1a5ba9feaeeef88d9f45508996ce43468
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 30 11:30:44 2015 +0100

    Fix ssl3_read_bytes handshake fragment bug
    
    The move of CCS into the state machine introduced a bug in ssl3_read_bytes.
    The value of |recvd_type| was not being set if we are satisfying the request
    from handshake fragment storage. This can occur, for example, with
    renegotiation and causes the handshake to fail.
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>

commit c69f2adf71d888ba1a2090ec0be3319eb024efe3
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 2 11:33:07 2015 +0100

    Move DTLS CCS processing into the state machine
    
    Continuing on from the previous commit this moves the processing of DTLS
    CCS messages out of the record layer and into the state machine.
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>

commit 657da85eea3a5825b2dd25ff25b99ec206c48136
Author: Matt Caswell <matt at openssl.org>
Date:   Mon May 11 09:35:41 2015 +0100

    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 at openssl.org>

-----------------------------------------------------------------------

Summary of changes:
 include/openssl/ssl.h     |   1 +
 include/openssl/ssl3.h    |   8 ++--
 ssl/d1_both.c             |  69 ++++++++++++++++++++++++++-----
 ssl/d1_clnt.c             |  19 ++++++---
 ssl/d1_srvr.c             |  31 +++++++-------
 ssl/record/rec_layer_d1.c |  73 ++++++++-------------------------
 ssl/record/rec_layer_s3.c |  86 ++++++++++++++++++---------------------
 ssl/record/record.h       |   6 ++-
 ssl/s3_both.c             | 101 ++++++++++++++++++++++++++++++++++++++++++++--
 ssl/s3_clnt.c             |  46 ++++++++++-----------
 ssl/s3_lib.c              |   8 ++--
 ssl/s3_srvr.c             |  69 ++++++++++---------------------
 ssl/ssl_err.c             |   2 +
 ssl/ssl_locl.h            |  10 ++---
 14 files changed, 301 insertions(+), 228 deletions(-)

diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 6b6560d..06ac5c1 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                349
 # 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 43df925..ec339de 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 155b8bf..ec47b94 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -160,8 +160,8 @@ static void dtls1_set_message_header_int(SSL *s, unsigned char mt,
                                          unsigned short seq_num,
                                          unsigned long frag_off,
                                          unsigned long frag_len);
-static long dtls1_get_message_fragment(SSL *s, int st1, int stn, long max,
-                                       int *ok);
+static long dtls1_get_message_fragment(SSL *s, int st1, int stn, int mt,
+                                       long max, int *ok);
 
 static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len,
                                           int reassembly)
@@ -470,7 +470,7 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
     memset(msg_hdr, 0, sizeof(*msg_hdr));
 
  again:
-    i = dtls1_get_message_fragment(s, st1, stn, max, ok);
+    i = dtls1_get_message_fragment(s, st1, stn, mt, max, ok);
     if (i == DTLS1_HM_BAD_FRAGMENT || i == DTLS1_HM_FRAGMENT_RETRY) {
         /* bad fragment received */
         goto again;
@@ -485,6 +485,20 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
     }
 
     p = (unsigned char *)s->init_buf->data;
+
+    if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+        if (s->msg_callback) {
+            s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC,
+                            p, 1, s, s->msg_callback_arg);
+        }
+        /*
+         * This isn't a real handshake message so skip the processing below.
+         * dtls1_get_message_fragment() will never return a CCS if mt == -1,
+         * so we are ok to continue in that case.
+         */
+        return i;
+    }
+
     msg_len = msg_hdr->msg_len;
 
     /* reconstruct message header */
@@ -679,7 +693,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 +706,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 +789,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 +815,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;
@@ -835,11 +849,11 @@ dtls1_process_out_of_seq_message(SSL *s, const struct hm_header_st *msg_hdr,
 }
 
 static long
-dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
+dtls1_get_message_fragment(SSL *s, int st1, int stn, int mt, long max, int *ok)
 {
     unsigned char wire[DTLS1_HM_HEADER_LENGTH];
     unsigned long len, frag_off, frag_len;
-    int i, al;
+    int i, al, recvd_type;
     struct hm_header_st msg_hdr;
 
  redo:
@@ -851,13 +865,46 @@ 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, &recvd_type, wire,
                                   DTLS1_HM_HEADER_LENGTH, 0);
     if (i <= 0) {               /* nbio, or an error */
         s->rwstate = SSL_READING;
         *ok = 0;
         return i;
     }
+    if(recvd_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
+        /* This isn't a real handshake message - its a CCS.
+         * There is no message sequence number in a CCS to give us confidence
+         * that this was really intended to be at this point in the handshake
+         * sequence. Therefore we only allow this if we were explicitly looking
+         * for it (i.e. if |mt| is -1 we still don't allow it).
+         */
+        if(mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+            if (wire[0] != SSL3_MT_CCS) {
+                al = SSL_AD_UNEXPECTED_MESSAGE;
+                SSLerr(SSL_F_DTLS1_GET_MESSAGE_FRAGMENT, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+                goto f_err;
+            }
+
+            memcpy(s->init_buf->data, wire, i);
+            s->init_num = i - 1;
+            s->init_msg = s->init_buf->data + 1;
+            s->s3->tmp.message_type = SSL3_MT_CHANGE_CIPHER_SPEC;
+            s->s3->tmp.message_size = i - 1;
+            s->state = stn;
+            *ok = 1;
+            return i-1;
+        } else {
+            /*
+             * We weren't expecting a CCS yet. Probably something got
+             * re-ordered or this is a retransmit. We should drop this and try
+             * again.
+             */
+            s->init_num = 0;
+            goto redo;
+        }
+    }
+
     /* Handshake fails if message header is incomplete */
     if (i != DTLS1_HM_HEADER_LENGTH) {
         al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -926,7 +973,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/d1_clnt.c b/ssl/d1_clnt.c
index fde0def..566c154 100644
--- a/ssl/d1_clnt.c
+++ b/ssl/d1_clnt.c
@@ -271,7 +271,6 @@ int dtls1_connect(SSL *s)
             memset(s->s3->client_random, 0, sizeof(s->s3->client_random));
             s->d1->send_cookie = 0;
             s->hit = 0;
-            s->d1->change_cipher_spec_ok = 0;
             /*
              * Should have been reset by ssl3_get_finished, too.
              */
@@ -376,7 +375,7 @@ int dtls1_connect(SSL *s)
                              sizeof(sctpauthkey), sctpauthkey);
 #endif
 
-                    s->state = SSL3_ST_CR_FINISHED_A;
+                    s->state = SSL3_ST_CR_CHANGE_A;
                 } else
                     s->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
             }
@@ -628,7 +627,7 @@ int dtls1_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;
@@ -638,7 +637,7 @@ int dtls1_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;
 
@@ -651,9 +650,19 @@ int dtls1_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:
-            s->d1->change_cipher_spec_ok = 1;
             ret = ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A,
                                     SSL3_ST_CR_FINISHED_B);
             if (ret <= 0)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 7a40d66..19562e1 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -257,7 +257,6 @@ int dtls1_accept(SSL *s)
             }
 
             s->init_num = 0;
-            s->d1->change_cipher_spec_ok = 0;
             /*
              * Should have been reset by ssl3_get_finished, too.
              */
@@ -378,7 +377,7 @@ int dtls1_accept(SSL *s)
                 goto end;
             }
 
-            s->state = SSL3_ST_SR_FINISHED_A;
+            s->state = SSL3_ST_SR_CHANGE_A;
             break;
 
         case DTLS1_SCTP_ST_SW_WRITE_SOCK:
@@ -624,7 +623,7 @@ int dtls1_accept(SSL *s)
                  * pub key in a certificate, the CertificateVerify message is
                  * not sent.
                  */
-                s->state = SSL3_ST_SR_FINISHED_A;
+                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;
@@ -675,23 +674,23 @@ int dtls1_accept(SSL *s)
                 s->state = DTLS1_SCTP_ST_SR_READ_SOCK;
             else
 #endif
-                s->state = SSL3_ST_SR_FINISHED_A;
+                s->state = SSL3_ST_SR_CHANGE_A;
+            s->init_num = 0;
+            break;
+
+        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;
+
+            s->state = SSL3_ST_SR_FINISHED_A;
             s->init_num = 0;
             break;
 
         case SSL3_ST_SR_FINISHED_A:
         case SSL3_ST_SR_FINISHED_B:
-            /*
-             * Enable CCS. 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 d1_pkt.c, and remains set until
-             * the client's Finished message is read.
-             */
-            if (!s->s3->change_cipher_spec)
-                s->d1->change_cipher_spec_ok = 1;
             ret = ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A,
                                     SSL3_ST_SR_FINISHED_B);
             if (ret <= 0)
@@ -779,7 +778,7 @@ int dtls1_accept(SSL *s)
                 goto end;
             s->state = SSL3_ST_SW_FLUSH;
             if (s->hit) {
-                s->s3->tmp.next_state = SSL3_ST_SR_FINISHED_A;
+                s->s3->tmp.next_state = SSL3_ST_SR_CHANGE_A;
 
 #ifndef OPENSSL_NO_SCTP
                 /*
diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c
index 52ef8f0..3da4f11 100644
--- a/ssl/record/rec_layer_d1.c
+++ b/ssl/record/rec_layer_d1.c
@@ -379,8 +379,9 @@ int dtls1_process_buffered_records(SSL *s)
  * (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):
@@ -395,7 +396,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;
@@ -537,9 +539,14 @@ int dtls1_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
@@ -551,6 +558,9 @@ int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
             goto f_err;
         }
 
+        if (recvd_type != NULL)
+            *recvd_type = SSL3_RECORD_get_type(rr);
+
         if (len <= 0)
             return (len);
 
@@ -857,59 +867,11 @@ int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
     }
 
     if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
-        unsigned int ccs_hdr_len = DTLS1_CCS_HEADER_LENGTH;
-
-        if (s->version == DTLS1_BAD_VER)
-            ccs_hdr_len = 3;
-
-        /*
-         * 'Change Cipher Spec' is just a single byte, so we know exactly
-         * what the record payload has to look like
-         */
-        /* XDTLS: check that epoch is consistent */
-        if ((SSL3_RECORD_get_length(rr) != ccs_hdr_len)
-                || (SSL3_RECORD_get_off(rr) != 0)
-                || (SSL3_RECORD_get_data(rr)[0] != SSL3_MT_CCS)) {
-            i = SSL_AD_ILLEGAL_PARAMETER;
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_BAD_CHANGE_CIPHER_SPEC);
-            goto err;
-        }
-
-        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);
-
         /*
          * We can't process a CCS now, because previous handshake messages
          * are still missing, so just drop it.
          */
-        if (!s->d1->change_cipher_spec_ok) {
-            goto start;
-        }
-
-        s->d1->change_cipher_spec_ok = 0;
-
-        s->s3->change_cipher_spec = 1;
-        if (!ssl3_do_change_cipher_spec(s))
-            goto err;
-
-        /* do this whenever CCS is processed */
-        dtls1_reset_seq_numbers(s, SSL3_CC_READ);
-
-        if (s->version == DTLS1_BAD_VER)
-            s->d1->handshake_read_seq++;
-
-#ifndef OPENSSL_NO_SCTP
-        /*
-         * Remember that a CCS has been received, so that an old key of
-         * SCTP-Auth can be deleted when a CCS is sent. Will be ignored if no
-         * SCTP is used
-         */
-        BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD, 1, NULL);
-#endif
-
+        SSL3_RECORD_set_length(rr, 0);
         goto start;
     }
 
@@ -1025,7 +987,6 @@ int dtls1_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/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index d6e922c..8a9e303 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;
@@ -1010,6 +1012,10 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
         /* move any remaining fragment bytes: */
         for (k = 0; k < s->rlayer.handshake_fragment_len; k++)
             s->rlayer.handshake_fragment[k] = *src++;
+
+        if (recvd_type != NULL)
+            *recvd_type = SSL3_RT_HANDSHAKE;
+
         return n;
     }
 
@@ -1066,9 +1072,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 +1091,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 +1127,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 +1373,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 +1470,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 6931bb4..5c8fead 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 17a8054..943cf73 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -228,6 +228,74 @@ 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,
+     * unless we're using DTLS1_BAD_VER, which has an extra 2 bytes
+     */
+    if (SSL_IS_DTLS(s)) {
+        if ((s->version == DTLS1_BAD_VER && n != DTLS1_CCS_HEADER_LENGTH + 1)
+                    || (s->version != DTLS1_BAD_VER
+                        && n != DTLS1_CCS_HEADER_LENGTH - 1)) {
+                al = SSL_AD_ILLEGAL_PARAMETER;
+                SSLerr(SSL_F_SSL3_GET_CHANGE_CIPHER_SPEC, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+                goto f_err;
+        }
+    } else {
+        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;
+    }
+
+    if (SSL_IS_DTLS(s)) {
+        dtls1_reset_seq_numbers(s, SSL3_CC_READ);
+
+        if (s->version == DTLS1_BAD_VER)
+            s->d1->handshake_read_seq++;
+
+#ifndef OPENSSL_NO_SCTP
+        /*
+         * Remember that a CCS has been received, so that an old key of
+         * SCTP-Auth can be deleted when a CCS is sent. Will be ignored if no
+         * SCTP is used
+         */
+        BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD, 1, NULL);
+#endif
+    }
+
+    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 +413,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 +437,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 +551,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 080dbf0..cd6918a 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 0fc0881..d39346a 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 bc7f84f..fd4c87e 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 4b4d89c..539146f 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 0997566..bc8388a 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);
@@ -1437,11 +1437,6 @@ typedef struct dtls1_state_st {
     unsigned short timeout_duration;
 
     unsigned int retransmitting;
-    /*
-     * Set when the handshake is ready to process peer's ChangeCipherSpec message.
-     * Cleared after the message has been processed.
-     */
-    unsigned int change_cipher_spec_ok;
 #  ifndef OPENSSL_NO_SCTP
     /* used when SSL_ST_XX_FLUSH is entered */
     int next_state;
@@ -1912,6 +1907,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);


More information about the openssl-commits mailing list