Implement Client TLS state machine
[openssl.git] / ssl / record / rec_layer_s3.c
index dc89be8fbb3505e76bd1ad56c186f1c5a7268a77..78e355a5d99d869d383c1331357acb2e8f463d5e 100644 (file)
@@ -142,35 +142,34 @@ void RECORD_LAYER_init(RECORD_LAYER *rl, SSL *s)
 
 void RECORD_LAYER_clear(RECORD_LAYER *rl)
 {
-    unsigned char *rp, *wp;
-    size_t rlen, wlen;
-    int read_ahead;
-    SSL *s;
-    DTLS_RECORD_LAYER *d;
-
-    s = rl->s;
-    d = rl->d;
-    read_ahead = rl->read_ahead;
-    rp = SSL3_BUFFER_get_buf(&rl->rbuf);
-    rlen = SSL3_BUFFER_get_len(&rl->rbuf);
-    wp = SSL3_BUFFER_get_buf(&rl->wbuf);
-    wlen = SSL3_BUFFER_get_len(&rl->wbuf);
-    memset(rl, 0, sizeof (RECORD_LAYER));
-    SSL3_BUFFER_set_buf(&rl->rbuf, rp);
-    SSL3_BUFFER_set_len(&rl->rbuf, rlen);
-    SSL3_BUFFER_set_buf(&rl->wbuf, wp);
-    SSL3_BUFFER_set_len(&rl->wbuf, wlen);
-
-    /* Do I need to do this? As far as I can tell read_ahead did not
+    rl->rstate = SSL_ST_READ_HEADER;
+
+    /* Do I need to clear read_ahead? As far as I can tell read_ahead did not
      * previously get reset by SSL_clear...so I'll keep it that way..but is
      * that right?
      */
-    rl->read_ahead = read_ahead;
-    rl->rstate = SSL_ST_READ_HEADER;
-    rl->s = s;
-    rl->d = d;
+
+    rl->packet = NULL;
+    rl->packet_length = 0;
+    rl->wnum = 0;
+    memset(rl->alert_fragment, 0, sizeof(rl->alert_fragment));
+    rl->alert_fragment_len = 0;
+    memset(rl->handshake_fragment, 0, sizeof(rl->handshake_fragment));
+    rl->handshake_fragment_len = 0;
+    rl->wpend_tot = 0;
+    rl->wpend_type = 0;
+    rl->wpend_ret = 0;
+    rl->wpend_buf = NULL;
+
+    SSL3_BUFFER_clear(&rl->rbuf);
+    SSL3_BUFFER_clear(&rl->wbuf);
+    SSL3_RECORD_clear(&rl->rrec);
+    SSL3_RECORD_clear(&rl->wrec);
+
+    RECORD_LAYER_reset_read_sequence(rl);
+    RECORD_LAYER_reset_write_sequence(rl);
     
-    if(d)
+    if (rl->d)
         DTLS_RECORD_LAYER_clear(rl);
 }
 
@@ -196,7 +195,7 @@ int RECORD_LAYER_write_pending(RECORD_LAYER *rl)
 int RECORD_LAYER_set_data(RECORD_LAYER *rl, const unsigned char *buf, int len)
 {
     rl->packet_length = len;
-    if(len != 0) {
+    if (len != 0) {
         rl->rstate = SSL_ST_READ_HEADER;
         if (!SSL3_BUFFER_is_initialised(&rl->rbuf))
             if (!ssl3_setup_read_buffer(rl->s))
@@ -220,12 +219,12 @@ void RECORD_LAYER_dup(RECORD_LAYER *dst, RECORD_LAYER *src)
 
 void RECORD_LAYER_reset_read_sequence(RECORD_LAYER *rl)
 {
-    memset(rl->read_sequence, 0, 8);
+    memset(rl->read_sequence, 0, sizeof(rl->read_sequence));
 }
 
 void RECORD_LAYER_reset_write_sequence(RECORD_LAYER *rl)
 {
-    memset(rl->write_sequence, 0, 8);
+    memset(rl->write_sequence, 0, sizeof(rl->write_sequence));
 }
 
 int RECORD_LAYER_setup_comp_buffer(RECORD_LAYER *rl)
@@ -294,7 +293,7 @@ int ssl3_read_n(SSL *s, int n, int max, int extend)
      * s->packet_length bytes if extend == 1].)
      */
     int i, len, left;
-    long align = 0;
+    size_t align = 0;
     unsigned char *pkt;
     SSL3_BUFFER *rb;
 
@@ -308,8 +307,8 @@ int ssl3_read_n(SSL *s, int n, int max, int extend)
 
     left = rb->left;
 #if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
-    align = (long)rb->buf + SSL3_RT_HEADER_LENGTH;
-    align = (-align) & (SSL3_ALIGN_PAYLOAD - 1);
+    align = (size_t)rb->buf + SSL3_RT_HEADER_LENGTH;
+    align = (0-align) & (SSL3_ALIGN_PAYLOAD - 1);
 #endif
 
     if (!extend) {
@@ -380,7 +379,7 @@ int ssl3_read_n(SSL *s, int n, int max, int extend)
     }
 
     /* We always act like read_ahead is set for DTLS */
-    if (&s->rlayer.read_ahead && !SSL_IS_DTLS(s))
+    if (!s->rlayer.read_ahead && !SSL_IS_DTLS(s))
         /* ignore max parameter */
         max = n;
     else {
@@ -445,10 +444,10 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
     unsigned int n, nw;
 #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
     unsigned int max_send_fragment;
+    unsigned int u_len = (unsigned int)len;
 #endif
     SSL3_BUFFER *wb = &s->rlayer.wbuf;
     int i;
-    unsigned int u_len = (unsigned int)len;
 
     if (len < 0) {
         SSLerr(SSL_F_SSL3_WRITE_BYTES, SSL_R_SSL_NEGATIVE_LENGTH);
@@ -531,7 +530,7 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
                 packlen *= 4;
 
             wb->buf = OPENSSL_malloc(packlen);
-            if(!wb->buf) {
+            if (!wb->buf) {
                 SSLerr(SSL_F_SSL3_WRITE_BYTES, ERR_R_MALLOC_FAILURE);
                 return -1;
             }
@@ -674,7 +673,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
     int i, mac_size, clear = 0;
     int prefix_len = 0;
     int eivlen;
-    long align = 0;
+    size_t align = 0;
     SSL3_RECORD *wr;
     SSL3_BUFFER *wb = &s->rlayer.wbuf;
     SSL_SESSION *sess;
@@ -683,7 +682,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
      * first check if there is a SSL3_BUFFER still being written out.  This
      * will happen with non blocking IO
      */
-    if (wb->left != 0)
+    if (SSL3_BUFFER_get_left(wb) != 0)
         return (ssl3_write_pending(s, type, buf, len));
 
     /* If we have an alert to send, lets send it */
@@ -694,7 +693,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
         /* if it went, fall through and send more stuff */
     }
 
-    if (wb->buf == NULL)
+    if (!SSL3_BUFFER_is_initialised(wb))
         if (!ssl3_setup_write_buffer(s))
             return -1;
 
@@ -754,26 +753,26 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
          * multiple of SSL3_ALIGN_PAYLOAD, so if we want to align the real
          * payload, then we can just pretent we simply have two headers.
          */
-        align = (long)wb->buf + 2 * SSL3_RT_HEADER_LENGTH;
-        align = (-align) & (SSL3_ALIGN_PAYLOAD - 1);
+        align = (size_t)SSL3_BUFFER_get_buf(wb) + 2 * SSL3_RT_HEADER_LENGTH;
+        align = (0-align) & (SSL3_ALIGN_PAYLOAD - 1);
 #endif
-        p = wb->buf + align;
-        wb->offset = align;
+        p = SSL3_BUFFER_get_buf(wb) + align;
+        SSL3_BUFFER_set_offset(wb, align);
     } else if (prefix_len) {
-        p = wb->buf + wb->offset + prefix_len;
+        p = SSL3_BUFFER_get_buf(wb) + SSL3_BUFFER_get_offset(wb) + prefix_len;
     } else {
 #if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
-        align = (long)wb->buf + SSL3_RT_HEADER_LENGTH;
-        align = (-align) & (SSL3_ALIGN_PAYLOAD - 1);
+        align = (size_t)SSL3_BUFFER_get_buf(wb) + SSL3_RT_HEADER_LENGTH;
+        align = (0-align) & (SSL3_ALIGN_PAYLOAD - 1);
 #endif
-        p = wb->buf + align;
-        wb->offset = align;
+        p = SSL3_BUFFER_get_buf(wb) + align;
+        SSL3_BUFFER_set_offset(wb, align);
     }
 
     /* write the header */
 
     *(p++) = type & 0xff;
-    wr->type = type;
+    SSL3_RECORD_set_type(wr, type);
 
     *(p++) = (s->version >> 8);
     /*
@@ -800,15 +799,18 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
         /* Need explicit part of IV for GCM mode */
         else if (mode == EVP_CIPH_GCM_MODE)
             eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN;
+        else if (mode == EVP_CIPH_CCM_MODE)
+            eivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN;
         else
             eivlen = 0;
     } else
         eivlen = 0;
 
     /* lets setup the record stuff. */
-    wr->data = p + eivlen;
-    wr->length = (int)len;
-    wr->input = (unsigned char *)buf;
+    SSL3_RECORD_set_data(wr, p + eivlen);
+    SSL3_RECORD_set_length(wr, (int)len);
+    SSL3_RECORD_set_input(wr, (unsigned char *)buf);
+
 
     /*
      * we now 'read' from wr->input, wr->length bytes into wr->data
@@ -822,7 +824,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
         }
     } else {
         memcpy(wr->data, wr->input, wr->length);
-        wr->input = wr->data;
+        SSL3_RECORD_reset_input(wr);
     }
 
     /*
@@ -834,17 +836,17 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
     if (!SSL_USE_ETM(s) && mac_size != 0) {
         if (s->method->ssl3_enc->mac(s, &(p[wr->length + eivlen]), 1) < 0)
             goto err;
-        wr->length += mac_size;
+        SSL3_RECORD_add_length(wr, mac_size);
     }
 
-    wr->input = p;
-    wr->data = p;
+    SSL3_RECORD_set_data(wr, p);
+    SSL3_RECORD_reset_input(wr);
 
     if (eivlen) {
         /*
          * if (RAND_pseudo_bytes(p, eivlen) <= 0) goto err;
          */
-        wr->length += eivlen;
+        SSL3_RECORD_add_length(wr, eivlen);
     }
 
     if (s->method->ssl3_enc->enc(s, 1) < 1)
@@ -853,11 +855,11 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
     if (SSL_USE_ETM(s) && mac_size != 0) {
         if (s->method->ssl3_enc->mac(s, p + wr->length, 1) < 0)
             goto err;
-        wr->length += mac_size;
+        SSL3_RECORD_add_length(wr, mac_size);
     }
 
     /* record length after mac and block padding */
-    s2n(wr->length, plen);
+    s2n(SSL3_RECORD_get_length(wr), plen);
 
     if (s->msg_callback)
         s->msg_callback(1, 0, SSL3_RT_HEADER, plen - 5, 5, s,
@@ -867,19 +869,19 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
      * we should now have wr->data pointing to the encrypted data, which is
      * wr->length long
      */
-    wr->type = type;            /* not needed but helps for debugging */
-    wr->length += SSL3_RT_HEADER_LENGTH;
+    SSL3_RECORD_set_type(wr, type);  /* not needed but helps for debugging */
+    SSL3_RECORD_add_length(wr, SSL3_RT_HEADER_LENGTH);
 
     if (create_empty_fragment) {
         /*
          * we are in a recursive call; just return the length, don't write
          * out anything here
          */
-        return wr->length;
+        return SSL3_RECORD_get_length(wr);
     }
 
     /* now let's set up wb */
-    wb->left = prefix_len + wr->length;
+    SSL3_BUFFER_set_left(wb, prefix_len + SSL3_RECORD_get_length(wr));
 
     /*
      * memorize arguments so that ssl3_write_pending can detect bad write
@@ -917,29 +919,29 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
         if (s->wbio != NULL) {
             s->rwstate = SSL_WRITING;
             i = BIO_write(s->wbio,
-                          (char *)&(wb->buf[wb->offset]),
-                          (unsigned int)wb->left);
+                (char *)&(SSL3_BUFFER_get_buf(wb)[SSL3_BUFFER_get_offset(wb)]),
+                (unsigned int)SSL3_BUFFER_get_left(wb));
         } else {
             SSLerr(SSL_F_SSL3_WRITE_PENDING, SSL_R_BIO_NOT_SET);
             i = -1;
         }
-        if (i == wb->left) {
-            wb->left = 0;
-            wb->offset += i;
+        if (i == SSL3_BUFFER_get_left(wb)) {
+            SSL3_BUFFER_set_left(wb, 0);
+            SSL3_BUFFER_add_offset(wb, i);
             s->rwstate = SSL_NOTHING;
             return (s->rlayer.wpend_ret);
         } else if (i <= 0) {
-            if (s->version == DTLS1_VERSION || s->version == DTLS1_BAD_VER) {
+            if (SSL_IS_DTLS(s)) {
                 /*
                  * For DTLS, just drop it. That's kind of the whole point in
                  * using a datagram service
                  */
-                wb->left = 0;
+                SSL3_BUFFER_set_left(wb, 0);
             }
             return (i);
         }
-        wb->offset += i;
-        wb->left -= i;
+        SSL3_BUFFER_add_offset(wb, i);
+        SSL3_BUFFER_add_left(wb, -i);
     }
 }
 
@@ -955,8 +957,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 +974,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 +1014,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;
     }
 
@@ -1039,7 +1047,8 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
     rr = &s->rlayer.rrec;
 
     /* get new packet if necessary */
-    if ((rr->length == 0) || (s->rlayer.rstate == SSL_ST_READ_BODY)) {
+    if ((SSL3_RECORD_get_length(rr) == 0)
+            || (s->rlayer.rstate == SSL_ST_READ_BODY)) {
         ret = ssl3_get_record(s);
         if (ret <= 0)
             return (ret);
@@ -1049,7 +1058,7 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
 
     if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
                                    * reset by ssl3_get_finished */
-        && (rr->type != SSL3_RT_HANDSHAKE)) {
+        && (SSL3_RECORD_get_type(rr) != SSL3_RT_HANDSHAKE)) {
         al = SSL_AD_UNEXPECTED_MESSAGE;
         SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
         goto f_err;
@@ -1060,13 +1069,19 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
      * 'peek' mode)
      */
     if (s->shutdown & SSL_RECEIVED_SHUTDOWN) {
-        rr->length = 0;
+        SSL3_RECORD_set_length(rr, 0);
         s->rwstate = SSL_NOTHING;
         return (0);
     }
 
-    if (type == rr->type) {     /* 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
@@ -1078,21 +1093,32 @@ 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);
 
-        if ((unsigned int)len > rr->length)
-            n = rr->length;
+        if ((unsigned int)len > SSL3_RECORD_get_length(rr))
+            n = SSL3_RECORD_get_length(rr);
         else
             n = (unsigned int)len;
 
         memcpy(buf, &(rr->data[rr->off]), n);
         if (!peek) {
-            rr->length -= n;
-            rr->off += n;
-            if (rr->length == 0) {
+            SSL3_RECORD_add_length(rr, -n);
+            SSL3_RECORD_add_off(rr, n);
+            if (SSL3_RECORD_get_length(rr) == 0) {
                 s->rlayer.rstate = SSL_ST_READ_HEADER;
-                rr->off = 0;
+                SSL3_RECORD_set_off(rr, 0);
                 if (s->mode & SSL_MODE_RELEASE_BUFFERS
                     && SSL3_BUFFER_get_left(&s->rlayer.rbuf) == 0)
                     ssl3_release_read_buffer(s);
@@ -1103,9 +1129,45 @@ 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
+     */
+    if (rr->rec_version == SSL2_VERSION) {
+        /*
+         * Should never happen. ssl3_get_record() should only give us an SSLv2
+         * record back if this is the first packet and we are looking for an
+         * initial ClientHello. Therefore |type| should always be equal to
+         * |rr->type|. If not then something has gone horribly wrong
+         */
+        al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_SSL3_READ_BYTES, ERR_R_INTERNAL_ERROR);
+        goto f_err;
+    }
+
+    if(s->method->version == TLS_ANY_VERSION
+            && (s->server || rr->type != SSL3_RT_ALERT)) {
+        /*
+         * If we've got this far and still haven't decided on what version
+         * we're using then this must be a client side alert we're dealing with
+         * (we don't allow heartbeats yet). We shouldn't be receiving anything
+         * other than a ClientHello if we are a server.
+         */
+        s->version = rr->rec_version;
+        al = SSL_AD_UNEXPECTED_MESSAGE;
+        SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_UNEXPECTED_MESSAGE);
+        goto f_err;
+    }
+
     /*
      * In case of record types for which we have 'fragment' storage, fill
      * that so that we can process the data at a fixed place.
@@ -1115,25 +1177,25 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
         unsigned char *dest = NULL;
         unsigned int *dest_len = NULL;
 
-        if (rr->type == SSL3_RT_HANDSHAKE) {
+        if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
             dest_maxlen = sizeof s->rlayer.handshake_fragment;
             dest = s->rlayer.handshake_fragment;
             dest_len = &s->rlayer.handshake_fragment_len;
-        } else if (rr->type == SSL3_RT_ALERT) {
+        } else if (SSL3_RECORD_get_type(rr) == SSL3_RT_ALERT) {
             dest_maxlen = sizeof s->rlayer.alert_fragment;
             dest = s->rlayer.alert_fragment;
             dest_len = &s->rlayer.alert_fragment_len;
         }
 #ifndef OPENSSL_NO_HEARTBEATS
-        else if (rr->type == TLS1_RT_HEARTBEAT) {
+        else if (SSL3_RECORD_get_type(rr)== TLS1_RT_HEARTBEAT) {
             /* We can ignore 0 return values */
-            if(tls1_process_heartbeat(s, SSL3_RECORD_get_data(&s->rlayer.rrec),
-                    SSL3_RECORD_get_length(&s->rlayer.rrec)) < 0) {
+            if (tls1_process_heartbeat(s, SSL3_RECORD_get_data(rr),
+                    SSL3_RECORD_get_length(rr)) < 0) {
                 return -1;
             }
 
             /* Exit and notify application to read again */
-            rr->length = 0;
+            SSL3_RECORD_set_length(rr, 0);
             s->rwstate = SSL_READING;
             BIO_clear_retry_flags(SSL_get_rbio(s));
             BIO_set_retry_read(SSL_get_rbio(s));
@@ -1143,13 +1205,15 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
 
         if (dest_maxlen > 0) {
             n = dest_maxlen - *dest_len; /* available space in 'dest' */
-            if (rr->length < n)
-                n = rr->length; /* available bytes */
+            if (SSL3_RECORD_get_length(rr) < n)
+                n = SSL3_RECORD_get_length(rr); /* available bytes */
 
             /* now move 'n' bytes: */
             while (n-- > 0) {
-                dest[(*dest_len)++] = rr->data[rr->off++];
-                rr->length--;
+                dest[(*dest_len)++] =
+                    SSL3_RECORD_get_data(rr)[SSL3_RECORD_get_off(rr)];
+                SSL3_RECORD_add_off(rr, 1);
+                SSL3_RECORD_add_length(rr, -1);
             }
 
             if (*dest_len < dest_maxlen)
@@ -1235,7 +1299,7 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
         (s->rlayer.handshake_fragment[0] == SSL3_MT_CLIENT_HELLO) &&
         (s->session != NULL) && (s->session->cipher != NULL) &&
         !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) {
-        rr->length = 0;
+        SSL3_RECORD_set_length(rr, 0);
         ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_NO_RENEGOTIATION);
         goto start;
     }
@@ -1306,48 +1370,14 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
     if (s->shutdown & SSL_SENT_SHUTDOWN) { /* but we have not received a
                                             * shutdown */
         s->rwstate = SSL_NOTHING;
-        rr->length = 0;
+        SSL3_RECORD_set_length(rr, 0);
         return (0);
     }
 
-    if (rr->type == 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 ((rr->length != 1) || (rr->off != 0) ||
-            (rr->data[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;
-
-        rr->length = 0;
-
-        if (s->msg_callback)
-            s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC,
-                            rr->data, 1, s, s->msg_callback_arg);
-
-        s->s3->change_cipher_spec = 1;
-        if (!ssl3_do_change_cipher_spec(s))
-            goto err;
-        else
-            goto start;
+    if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
+        al = SSL_AD_UNEXPECTED_MESSAGE;
+        SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_CCS_RECEIVED_EARLY);
+        goto f_err;
     }
 
     /*
@@ -1388,14 +1418,14 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
         goto start;
     }
 
-    switch (rr->type) {
+    switch (SSL3_RECORD_get_type(rr)) {
     default:
         /*
          * TLS up to v1.1 just ignores unknown message types: TLS v1.2 give
          * an unexpected message alert.
          */
         if (s->version >= TLS1_VERSION && s->version <= TLS1_1_VERSION) {
-            rr->length = 0;
+            SSL3_RECORD_set_length(rr, 0);
             goto start;
         }
         al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -1420,16 +1450,7 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
          * application data at this point (session renegotiation not yet
          * started), we will indulge it.
          */
-        if (s->s3->in_read_app_data &&
-            (s->s3->total_renegotiations != 0) &&
-            (((s->state & SSL_ST_CONNECT) &&
-              (s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
-              (s->state <= SSL3_ST_CR_SRVR_HELLO_A)
-             ) || ((s->state & SSL_ST_ACCEPT) &&
-                   (s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
-                   (s->state >= SSL3_ST_SR_CLNT_HELLO_A)
-             )
-            )) {
+        if (statem_app_data_allowed(s)) {
             s->s3->in_read_app_data = 2;
             return (-1);
         } else {
@@ -1442,7 +1463,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);
 }
 
@@ -1457,4 +1477,19 @@ void ssl3_record_sequence_update(unsigned char *seq)
     }
 }
 
+/*
+ * Returns true if the current rrec was sent in SSLv2 backwards compatible
+ * format and false otherwise.
+ */
+int RECORD_LAYER_is_sslv2_record(RECORD_LAYER *rl)
+{
+    return SSL3_RECORD_is_sslv2_record(&rl->rrec);
+}
 
+/*
+ * Returns the length in bytes of the current rrec
+ */
+unsigned int RECORD_LAYER_get_rrec_length(RECORD_LAYER *rl)
+{
+    return SSL3_RECORD_get_length(&rl->rrec);
+}