Move TLS CCS processing into the state machine
authorMatt Caswell <matt@openssl.org>
Mon, 11 May 2015 08:35:41 +0000 (09:35 +0100)
committerMatt Caswell <matt@openssl.org>
Mon, 3 Aug 2015 10:18:05 +0000 (11:18 +0100)
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>
12 files changed:
include/openssl/ssl.h
include/openssl/ssl3.h
ssl/d1_both.c
ssl/record/rec_layer_d1.c
ssl/record/rec_layer_s3.c
ssl/record/record.h
ssl/s3_both.c
ssl/s3_clnt.c
ssl/s3_lib.c
ssl/s3_srvr.c
ssl/ssl_err.c
ssl/ssl_locl.h

index 6b6560dc755ae82b268dc0eb50ac8a32b015b4f7..892e12961155732c5c004c6ec571040abeaaa55f 100644 (file)
@@ -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
index 43df925999bc0ef1be1c9d9e22ccda87dd0b2eb7..ec339de3518fde1120a0039bb5d58ce263facf3b 100644 (file)
@@ -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 */
index 155b8bffe0820792cff2fed5b9b2318b6cfa32ab..a1499da3eb9f44827e37bb69dc412727939b4458 100644 (file)
@@ -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);
 
         /*
index 52ef8f0834287e7f6a6c22cc4843e9221ff37c73..2c8b94f79b5e54271a4c17937e59d230b2037487 100644 (file)
@@ -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;
index d6e922c652ac6a2da7e250e17265f7f2b68a9e34..6feba4251884d895520c5164d53e853a2ccfd2c3 100644 (file)
@@ -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);
 }
 
index 6931bb4712d310fd13ff03886c310fd26da9f8a2..5c8fead869881176e1ceceaa3aa15aacc4ae41f5 100644 (file)
@@ -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);
index 17a805486828b381f494e9cf780360cf6204abb8..32193c3460d5b220ad4e1ef841af3ec2e2c6b99b 100644 (file)
@@ -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;
index 080dbf0f183dadb6cd730ddf19020593405d82bd..cd6918aa6b5dde1b14c443633be1dc5a1af2b510 100644 (file)
 
 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;
 }
 
index 0fc08819cacc8988b19cf6e7c932545935b55404..d39346af65ba5f3841501b96e9b0418925a99154 100644 (file)
@@ -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;
index bc7f84f2d1dea9ed0e8cfeb4b201564004189ac7..fd4c87e9e6eb53280ea21f9a1e168b89cfa22357 100644 (file)
@@ -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;
index 4b4d89ce7a1e8c6a596224a60588f1a6b314b051..539146ff08181aeb00e6b15bcbff8f807e6f5ba0 100644 (file)
@@ -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"},
index 09975664e9f22800d914f73f649afdec9ab6b518..d13aa05b69ed92338c0907e269c547b3a692c3a1 100644 (file)
@@ -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);