Implement DTLS client move to new state machine
authorMatt Caswell <matt@openssl.org>
Mon, 7 Sep 2015 21:00:36 +0000 (22:00 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 30 Oct 2015 08:32:44 +0000 (08:32 +0000)
Move all DTLS client side processing into the new state machine code. A
subsequent commit will clean up the old dead code.

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
include/openssl/ssl.h
ssl/d1_both.c
ssl/d1_clnt.c
ssl/s3_both.c
ssl/s3_clnt.c
ssl/ssl_err.c
ssl/ssl_locl.h
ssl/statem.c

index cb60258..b89b2a6 100644 (file)
@@ -1928,6 +1928,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_DTLS1_SEND_SERVER_HELLO                    266
 # define SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE             267
 # define SSL_F_DTLS1_WRITE_APP_DATA_BYTES                 268
+# define SSL_F_DTLS_CONSTRUCT_CHANGE_CIPHER_SPEC          371
 # define SSL_F_DTLS_GET_REASSEMBLED_MESSAGE               370
 # define SSL_F_READ_STATE_MACHINE                         352
 # define SSL_F_SSL3_ACCEPT                                128
index 9d20dfe..a969038 100644 (file)
@@ -582,6 +582,7 @@ int dtls_get_message(SSL *s, int *mt, unsigned long *len)
         /*
          * This isn't a real handshake message so skip the processing below.
          */
+        *len = (unsigned long)tmplen;
         return 1;
     }
 
@@ -1102,6 +1103,19 @@ static int dtls_get_reassembled_message(SSL *s, long *len)
     return 0;
 }
 
+
+int dtls1_send_change_cipher_spec(SSL *s, int a, int b)
+{
+    if (s->state == a) {
+        if (dtls_construct_change_cipher_spec(s) == 0)
+            return -1;
+    }
+
+    /* SSL3_ST_CW_CHANGE_B */
+    return (dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC));
+}
+
+
 /*-
  * for these 2 messages, we need to
  * ssl->enc_read_ctx                    re-init
@@ -1111,39 +1125,55 @@ static int dtls_get_reassembled_message(SSL *s, long *len)
  * ssl->session->read_compression       assign
  * ssl->session->read_hash              assign
  */
-int dtls1_send_change_cipher_spec(SSL *s, int a, int b)
+int dtls_construct_change_cipher_spec(SSL *s)
 {
     unsigned char *p;
 
-    if (s->state == a) {
-        p = (unsigned char *)s->init_buf->data;
-        *p++ = SSL3_MT_CCS;
-        s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
-        s->init_num = DTLS1_CCS_HEADER_LENGTH;
-
-        if (s->version == DTLS1_BAD_VER) {
-            s->d1->next_handshake_write_seq++;
-            s2n(s->d1->handshake_write_seq, p);
-            s->init_num += 2;
-        }
+    p = (unsigned char *)s->init_buf->data;
+    *p++ = SSL3_MT_CCS;
+    s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
+    s->init_num = DTLS1_CCS_HEADER_LENGTH;
 
-        s->init_off = 0;
+    if (s->version == DTLS1_BAD_VER) {
+        s->d1->next_handshake_write_seq++;
+        s2n(s->d1->handshake_write_seq, p);
+        s->init_num += 2;
+    }
 
-        dtls1_set_message_header_int(s, SSL3_MT_CCS, 0,
-                                     s->d1->handshake_write_seq, 0, 0);
+    s->init_off = 0;
 
-        /* buffer the message to handle re-xmits */
-        if (!dtls1_buffer_message(s, 1)) {
-            SSLerr(SSL_F_DTLS1_SEND_CHANGE_CIPHER_SPEC, ERR_R_INTERNAL_ERROR);
-            return -1;
-        }
+    dtls1_set_message_header_int(s, SSL3_MT_CCS, 0,
+                                 s->d1->handshake_write_seq, 0, 0);
 
-        s->state = b;
+    /* buffer the message to handle re-xmits */
+    if (!dtls1_buffer_message(s, 1)) {
+        SSLerr(SSL_F_DTLS_CONSTRUCT_CHANGE_CIPHER_SPEC, ERR_R_INTERNAL_ERROR);
+        return 0;
     }
 
-    /* SSL3_ST_CW_CHANGE_B */
-    return (dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC));
+    return 1;
+}
+
+#ifndef OPENSSL_NO_SCTP
+enum WORK_STATE dtls_wait_for_dry(SSL *s)
+{
+    int ret;
+
+    /* read app data until dry event */
+    ret = BIO_dgram_sctp_wait_for_dry(SSL_get_wbio(s));
+    if (ret < 0)
+        return WORK_ERROR;
+
+    if (ret == 0) {
+        s->s3->in_read_app_data = 2;
+        s->rwstate = SSL_READING;
+        BIO_clear_retry_flags(SSL_get_rbio(s));
+        BIO_set_retry_read(SSL_get_rbio(s));
+        return WORK_MORE_A;
+    }
+    return WORK_FINISHED_CONTINUE;
 }
+#endif
 
 int dtls1_read_failed(SSL *s, int code)
 {
index 083333e..ef8f25b 100644 (file)
 #endif
 
 static const SSL_METHOD *dtls1_get_client_method(int ver);
+#if 0
 static int dtls1_get_hello_verify(SSL *s);
+#endif
 
 static const SSL_METHOD *dtls1_get_client_method(int ver)
 {
@@ -156,6 +158,7 @@ IMPLEMENT_dtls1_meth_func(DTLS1_VERSION,
                           dtls1_connect,
                           dtls1_get_client_method, DTLSv1_2_enc_data)
 
+#if 0
 int dtls1_connect(SSL *s)
 {
     BUF_MEM *buf = NULL;
@@ -785,13 +788,16 @@ int dtls1_connect(SSL *s)
         cb(s, SSL_CB_CONNECT_EXIT, ret);
     return (ret);
 }
+#endif
 
+#if 0
 static int dtls1_get_hello_verify(SSL *s)
 {
     int n, al, ok = 0;
     unsigned char *data;
     unsigned int cookie_len;
 
+    /* TODO: CHECK first_packet handling!!! */
     s->first_packet = 1;
     n = s->method->ssl_get_message(s,
                                    DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A,
@@ -801,12 +807,14 @@ static int dtls1_get_hello_verify(SSL *s)
 
     if (!ok)
         return ((int)n);
+}
+#endif
 
-    if (s->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
-        s->d1->send_cookie = 0;
-        s->s3->tmp.reuse_message = 1;
-        return (1);
-    }
+enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, unsigned long n)
+{
+    int al;
+    unsigned char *data;
+    unsigned int cookie_len;
 
     data = (unsigned char *)s->init_msg;
     data += 2;
@@ -820,11 +828,9 @@ static int dtls1_get_hello_verify(SSL *s)
     memcpy(s->d1->cookie, data, cookie_len);
     s->d1->cookie_len = cookie_len;
 
-    s->d1->send_cookie = 1;
-    return 1;
-
+    return MSG_PROCESS_FINISHED_READING;
  f_err:
     ssl3_send_alert(s, SSL3_AL_FATAL, al);
-    s->state = SSL_ST_ERR;
-    return -1;
+    statem_set_error(s);
+    return MSG_PROCESS_ERROR;
 }
index 1829feb..2afde72 100644 (file)
@@ -461,8 +461,15 @@ enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst)
 
     /* clean a few things up */
     ssl3_cleanup_key_block(s);
-    BUF_MEM_free(s->init_buf);
-    s->init_buf = NULL;
+
+    if (!SSL_IS_DTLS(s)) {
+        /*
+         * We don't do this in DTLS because we may still need the init_buf
+         * in case there are any unexpected retransmits
+         */
+        BUF_MEM_free(s->init_buf);
+        s->init_buf = NULL;
+    }
 
     ssl_free_wbio_buffer(s);
 
index 9a73825..715f08b 100644 (file)
@@ -465,6 +465,7 @@ int tls_construct_client_hello(SSL *s)
     return 0;
 }
 
+#if 0
 int ssl3_get_server_hello(SSL *s)
 {
     int ok, al;
@@ -509,6 +510,7 @@ int ssl3_get_server_hello(SSL *s)
     s->state = SSL_ST_ERR;
     return (-1);
 }
+#endif
 
 enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, unsigned long n)
 {
@@ -1921,7 +1923,14 @@ enum MSG_PROCESS_RETURN tls_process_server_done(SSL *s, unsigned long n)
     }
 #endif
 
-    return MSG_PROCESS_FINISHED_READING;
+#ifndef OPENSSL_NO_SCTP
+    /* Only applies to renegotiation */
+    if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))
+            && s->renegotiate != 0)
+        return MSG_PROCESS_CONTINUE_PROCESSING;
+    else
+#endif
+        return MSG_PROCESS_FINISHED_READING;
 }
 
 int ssl3_send_client_key_exchange(SSL *s)
@@ -2554,6 +2563,29 @@ int tls_client_key_exchange_post_work(SSL *s)
         SSLerr(SSL_F_TLS_CLIENT_KEY_EXCHANGE_POST_WORK, ERR_R_INTERNAL_ERROR);
         goto err;
     }
+
+#ifndef OPENSSL_NO_SCTP
+    if (SSL_IS_DTLS(s)) {
+        unsigned char sctpauthkey[64];
+        char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)];
+
+        /*
+         * Add new shared key for SCTP-Auth, will be ignored if no SCTP
+         * used.
+         */
+        snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL),
+                 DTLS1_SCTP_AUTH_LABEL);
+
+        if (SSL_export_keying_material(s, sctpauthkey,
+                                   sizeof(sctpauthkey), labelbuffer,
+                                   sizeof(labelbuffer), NULL, 0, 0) <= 0)
+            goto err;
+
+        BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY,
+                 sizeof(sctpauthkey), sctpauthkey);
+    }
+#endif
+
     return 1;
  err:
     OPENSSL_clear_free(pms, pmslen);
index 87a31a6..1615ced 100644 (file)
@@ -112,6 +112,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE),
      "dtls1_send_server_key_exchange"},
     {ERR_FUNC(SSL_F_DTLS1_WRITE_APP_DATA_BYTES), "dtls1_write_app_data_bytes"},
+    {ERR_FUNC(SSL_F_DTLS_CONSTRUCT_CHANGE_CIPHER_SPEC),
+     "dtls_construct_change_cipher_spec"},
     {ERR_FUNC(SSL_F_DTLS_GET_REASSEMBLED_MESSAGE),
      "DTLS_GET_REASSEMBLED_MESSAGE"},
     {ERR_FUNC(SSL_F_READ_STATE_MACHINE), "READ_STATE_MACHINE"},
index 018b8ff..feed7e4 100644 (file)
@@ -1555,7 +1555,6 @@ typedef struct hm_fragment_st {
 } hm_fragment;
 
 typedef struct dtls1_state_st {
-    unsigned int send_cookie;
     unsigned char cookie[DTLS1_COOKIE_LENGTH];
     unsigned int cookie_len;
 
@@ -2062,6 +2061,7 @@ __owur enum MSG_PROCESS_RETURN tls_process_finished(SSL *s, unsigned long n);
 __owur int ssl3_setup_key_block(SSL *s);
 __owur int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b);
 __owur int tls_construct_change_cipher_spec(SSL *s);
+__owur int dtls_construct_change_cipher_spec(SSL *s);
 __owur int ssl3_change_cipher_state(SSL *s, int which);
 void ssl3_cleanup_key_block(SSL *s);
 __owur int ssl3_do_write(SSL *s, int type);
@@ -2075,6 +2075,7 @@ __owur int tls_get_message_body(SSL *s, unsigned long *len);
 __owur int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen);
 __owur int tls_construct_finished(SSL *s, const char *sender, int slen);
 __owur enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst);
+__owur enum WORK_STATE dtls_wait_for_dry(SSL *s);
 __owur int ssl3_num_ciphers(void);
 __owur const SSL_CIPHER *ssl3_get_cipher(unsigned int u);
 int ssl3_renegotiate(SSL *ssl);
@@ -2194,6 +2195,8 @@ __owur int ssl3_check_cert_and_algorithm(SSL *s);
 __owur int ssl3_send_next_proto(SSL *s);
 __owur int tls_construct_next_proto(SSL *s);
 #  endif
+__owur enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s,
+                                                         unsigned long n);
 
 int dtls1_client_hello(SSL *s);
 
index f1df6a3..7f2176f 100644 (file)
@@ -151,6 +151,11 @@ int ssl3_connect(SSL *s) {
     return state_machine(s, 0);
 }
 
+int dtls1_connect(SSL *s)
+{
+    return state_machine(s, 0);
+}
+
 /*
  * The main message flow state machine. We start in the MSG_FLOW_UNINITED or
  * MSG_FLOW_RENEGOTIATE state and finish in MSG_FLOW_FINISHED. Valid states and
@@ -207,6 +212,17 @@ static int state_machine(SSL *s, int server) {
             return -1;
     }
 
+#ifndef OPENSSL_NO_SCTP
+    if (SSL_IS_DTLS(s)) {
+        /*
+         * Notify SCTP BIO socket to enter handshake mode and prevent stream
+         * identifier other than 0. Will be ignored if no SCTP is used.
+         */
+        BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE,
+                 s->in_handshake, NULL);
+    }
+#endif
+
 #ifndef OPENSSL_NO_HEARTBEATS
     /*
      * If we're awaiting a HeartbeatResponse, pretend we already got and
@@ -259,10 +275,12 @@ static int state_machine(SSL *s, int server) {
             }
         }
 
-        if (s->version != TLS_ANY_VERSION &&
-                !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) {
-            SSLerr(SSL_F_STATE_MACHINE, SSL_R_VERSION_TOO_LOW);
-            goto end;
+        if (!SSL_IS_DTLS(s)) {
+            if (s->version != TLS_ANY_VERSION &&
+                    !ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) {
+                SSLerr(SSL_F_STATE_MACHINE, SSL_R_VERSION_TOO_LOW);
+                goto end;
+            }
         }
 
         if (server)
@@ -380,6 +398,18 @@ static int state_machine(SSL *s, int server) {
 
  end:
     s->in_handshake--;
+
+#ifndef OPENSSL_NO_SCTP
+    if (SSL_IS_DTLS(s)) {
+        /*
+         * Notify SCTP BIO socket to leave handshake mode and allow stream
+         * identifier other than 0. Will be ignored if no SCTP is used.
+         */
+        BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE,
+                 s->in_handshake, NULL);
+    }
+#endif
+
     BUF_MEM_free(buf);
     if (cb != NULL) {
         if (server)
@@ -765,14 +795,11 @@ int statem_app_data_allowed(SSL *s)
 
     /*
      * This is the old check for code still using the old state machine. This
-     * will be removed by later commits
+     * will be removed by a later commit
      */
-    if (((s->state & SSL_ST_CONNECT) && SSL_IS_DTLS(s) &&
-          (s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
-          (s->state <= SSL3_ST_CR_SRVR_HELLO_A)) ||
-         ((s->state & SSL_ST_ACCEPT) &&
+    if ((s->state & SSL_ST_ACCEPT) &&
                (s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
-               (s->state >= SSL3_ST_SR_CLNT_HELLO_A))
+               (s->state >= SSL3_ST_SR_CLNT_HELLO_A)
         )
         return 1;
 
@@ -883,7 +910,10 @@ static int client_read_transition(SSL *s, int mt)
                 return 1;
             }
         } else {
-            if (!(s->s3->tmp.new_cipher->algorithm_auth
+            if (SSL_IS_DTLS(s) && mt == DTLS1_MT_HELLO_VERIFY_REQUEST) {
+                st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
+                return 1;
+            } else if (!(s->s3->tmp.new_cipher->algorithm_auth
                         & (SSL_aNULL | SSL_aSRP | SSL_aPSK))) {
                 if (mt == SSL3_MT_CERTIFICATE) {
                     st->hand_state = TLS_ST_CR_CERT;
@@ -1015,6 +1045,10 @@ static enum WRITE_TRAN client_write_transition(SSL *s)
              */
             return WRITE_TRAN_FINISHED;
 
+        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
+            st->hand_state = TLS_ST_CW_CLNT_HELLO;
+            return WRITE_TRAN_CONTINUE;
+
         case TLS_ST_CR_SRVR_DONE:
             if (s->s3->tmp.cert_req)
                 st->hand_state = TLS_ST_CW_CERT;
@@ -1055,7 +1089,7 @@ static enum WRITE_TRAN client_write_transition(SSL *s)
 #if defined(OPENSSL_NO_NEXTPROTONEG)
             st->hand_state = TLS_ST_CW_FINISHED;
 #else
-            if (s->s3->next_proto_neg_seen)
+            if (!SSL_IS_DTLS(s) && s->s3->next_proto_neg_seen)
                 st->hand_state = TLS_ST_CW_NEXT_PROTO;
             else
                 st->hand_state = TLS_ST_CW_FINISHED;
@@ -1106,11 +1140,31 @@ static enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst)
     switch(st->hand_state) {
     case TLS_ST_CW_CLNT_HELLO:
         s->shutdown = 0;
+        if (SSL_IS_DTLS(s)) {
+            /* every DTLS ClientHello resets Finished MAC */
+            ssl3_init_finished_mac(s);
+        }
         break;
 
     case TLS_ST_CW_CERT:
         return tls_prepare_client_certificate(s, wst);
 
+    case TLS_ST_CW_CHANGE:
+        if (SSL_IS_DTLS(s)) {
+            if (s->hit) {
+                /*
+                 * We're into the last flight so we don't retransmit these
+                 * messages unless we need to.
+                 */
+                st->use_timer = 0;
+            }
+#ifndef OPENSSL_NO_SCTP
+            if (BIO_dgram_is_sctp(SSL_get_wbio(s)))
+                return dtls_wait_for_dry(s);
+#endif
+        }
+        return WORK_FINISHED_CONTINUE;
+
     case TLS_ST_OK:
         return tls_finish_handshake(s, wst);
 
@@ -1134,9 +1188,24 @@ static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst)
 
     switch(st->hand_state) {
     case TLS_ST_CW_CLNT_HELLO:
-        /* turn on buffering for the next lot of output */
-        if (s->bbio != s->wbio)
-            s->wbio = BIO_push(s->bbio, s->wbio);
+        if (SSL_IS_DTLS(s) && s->d1->cookie_len > 0 && statem_flush(s) != 1)
+            return WORK_MORE_A;
+#ifndef OPENSSL_NO_SCTP
+        /* Disable buffering for SCTP */
+        if (!SSL_IS_DTLS(s) || !BIO_dgram_is_sctp(SSL_get_wbio(s))) {
+#endif
+            /*
+             * turn on buffering for the next lot of output
+             */
+            if (s->bbio != s->wbio)
+                s->wbio = BIO_push(s->bbio, s->wbio);
+#ifndef OPENSSL_NO_SCTP
+            }
+#endif
+        if (SSL_IS_DTLS(s)) {
+            /* Treat the next message as the first packet */
+            s->first_packet = 1;
+        }
         break;
 
     case TLS_ST_CW_KEY_EXCH:
@@ -1179,7 +1248,7 @@ static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst)
 
     case TLS_ST_CW_FINISHED:
 #ifndef OPENSSL_NO_SCTP
-        if (SSL_IS_DTLS(s) && s->hit == 0) {
+        if (wst == WORK_MORE_A && SSL_IS_DTLS(s) && s->hit == 0) {
             /*
              * Change to new shared key of SCTP-Auth, will be ignored if
              * no SCTP used.
@@ -1228,7 +1297,10 @@ static int client_construct_message(SSL *s)
         return tls_construct_client_verify(s);
 
     case TLS_ST_CW_CHANGE:
-        return tls_construct_change_cipher_spec(s);
+        if (SSL_IS_DTLS(s))
+            return dtls_construct_change_cipher_spec(s);
+        else
+            return tls_construct_change_cipher_spec(s);
 
 #if !defined(OPENSSL_NO_NEXTPROTONEG)
     case TLS_ST_CW_NEXT_PROTO:
@@ -1250,6 +1322,7 @@ static int client_construct_message(SSL *s)
 }
 
 /* The spec allows for a longer length than this, but we limit it */
+#define HELLO_VERIFY_REQUEST_MAX_LENGTH 258
 #define SERVER_HELLO_MAX_LENGTH         20000
 #define SERVER_KEY_EXCH_MAX_LENGTH      102400
 #define SERVER_HELLO_DONE_MAX_LENGTH    0
@@ -1269,6 +1342,9 @@ static unsigned long client_max_message_size(SSL *s)
         case TLS_ST_CR_SRVR_HELLO:
             return SERVER_HELLO_MAX_LENGTH;
 
+        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
+            return HELLO_VERIFY_REQUEST_MAX_LENGTH;
+
         case TLS_ST_CR_CERT:
             return s->max_cert_list;
 
@@ -1312,6 +1388,9 @@ static enum MSG_PROCESS_RETURN client_process_message(SSL *s, unsigned long len)
         case TLS_ST_CR_SRVR_HELLO:
             return tls_process_server_hello(s, len);
 
+        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
+            return dtls_process_hello_verify(s, len);
+
         case TLS_ST_CR_CERT:
             return tls_process_server_certificate(s, len);