Move server side DTLS to new state machine
authorMatt Caswell <matt@openssl.org>
Fri, 4 Sep 2015 12:51:49 +0000 (13:51 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 30 Oct 2015 08:38:18 +0000 (08:38 +0000)
Implement all of the necessary changes to make DTLS on the server work
with the new state machine code.

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

index 75bca7c..de3c3be 100644 (file)
@@ -1929,6 +1929,7 @@ void ERR_load_SSL_strings(void);
 # 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_CONSTRUCT_HELLO_VERIFY_REQUEST        385
 # define SSL_F_DTLS_GET_REASSEMBLED_MESSAGE               370
 # define SSL_F_READ_STATE_MACHINE                         352
 # define SSL_F_SSL3_ACCEPT                                128
index 22dbbfe..02a944d 100644 (file)
 #endif
 
 static const SSL_METHOD *dtls1_get_server_method(int ver);
-static int dtls1_send_hello_verify_request(SSL *s);
 
 static const SSL_METHOD *dtls1_get_server_method(int ver)
 {
@@ -157,6 +156,7 @@ IMPLEMENT_dtls1_meth_func(DTLS1_VERSION,
                           ssl_undefined_function,
                           dtls1_get_server_method, DTLSv1_2_enc_data)
 
+#if 0
 int dtls1_accept(SSL *s)
 {
     BUF_MEM *buf;
@@ -857,6 +857,7 @@ int dtls1_accept(SSL *s)
         cb(s, SSL_CB_ACCEPT_EXIT, ret);
     return (ret);
 }
+#endif
 
 unsigned int dtls1_raw_hello_verify_request(unsigned char *buf,
                                             unsigned char *cookie,
@@ -879,37 +880,33 @@ unsigned int dtls1_raw_hello_verify_request(unsigned char *buf,
 }
 
 
-int dtls1_send_hello_verify_request(SSL *s)
+int dtls_construct_hello_verify_request(SSL *s)
 {
     unsigned int len;
     unsigned char *buf;
 
-    if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A) {
-        buf = (unsigned char *)s->init_buf->data;
-
-        if (s->ctx->app_gen_cookie_cb == NULL ||
-            s->ctx->app_gen_cookie_cb(s, s->d1->cookie,
-                                      &(s->d1->cookie_len)) == 0 ||
-            s->d1->cookie_len > 255) {
-            SSLerr(SSL_F_DTLS1_SEND_HELLO_VERIFY_REQUEST,
-                   SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
-            s->state = SSL_ST_ERR;
-            return 0;
-        }
+    buf = (unsigned char *)s->init_buf->data;
 
-        len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH],
-                                             s->d1->cookie, s->d1->cookie_len);
+    if (s->ctx->app_gen_cookie_cb == NULL ||
+        s->ctx->app_gen_cookie_cb(s, s->d1->cookie,
+                                  &(s->d1->cookie_len)) == 0 ||
+        s->d1->cookie_len > 255) {
+        SSLerr(SSL_F_DTLS1_SEND_HELLO_VERIFY_REQUEST,
+               SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
+        statem_set_error(s);
+        return 0;
+    }
 
-        dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0,
-                                 len);
-        len += DTLS1_HM_HEADER_LENGTH;
+    len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH],
+                                         s->d1->cookie, s->d1->cookie_len);
 
-        s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B;
-        /* number of bytes to write */
-        s->init_num = len;
-        s->init_off = 0;
-    }
+    dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0,
+                             len);
+    len += DTLS1_HM_HEADER_LENGTH;
+
+    /* number of bytes to write */
+    s->init_num = len;
+    s->init_off = 0;
 
-    /* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */
-    return (dtls1_do_write(s, SSL3_RT_HANDSHAKE));
+    return 1;
 }
index 992df70..d390f14 100644 (file)
@@ -2876,27 +2876,47 @@ enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, long n)
 enum WORK_STATE tls_post_process_client_key_exchange(SSL *s,
                                                       enum WORK_STATE wst)
 {
-
 #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 (wst == WORK_MORE_A) {
+        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) {
+                statem_set_error(s);
+                return WORK_ERROR;;
+            }
 
-        if (SSL_export_keying_material(s, sctpauthkey,
-                                   sizeof(sctpauthkey), labelbuffer,
-                                   sizeof(labelbuffer), NULL, 0, 0) <= 0) {
-            statem_set_error(s);
-            return WORK_ERROR;;
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY,
+                     sizeof(sctpauthkey), sctpauthkey);
         }
+        wst = WORK_MORE_B;
+    }
 
-        BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY,
-                 sizeof(sctpauthkey), sctpauthkey);
+    if ((wst == WORK_MORE_B)
+            /* Is this SCTP? */
+            && BIO_dgram_is_sctp(SSL_get_wbio(s))
+            /* Are we renegotiating? */
+            && s->renegotiate
+            /* Are we going to skip the CertificateVerify? */
+            && (s->session->peer == NULL || s->no_cert_verify)
+            && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
+        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));
+        statem_set_sctp_read_sock(s, 1);
+        return WORK_MORE_B;
+    } else {
+        statem_set_sctp_read_sock(s, 0);
     }
 #endif
 
@@ -3169,7 +3189,7 @@ enum MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, long n)
         goto f_err;
     }
 
-    ret = MSG_PROCESS_CONTINUE_READING;
+    ret = MSG_PROCESS_CONTINUE_PROCESSING;
     if (0) {
  f_err:
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
index fe4201b..faee32f 100644 (file)
@@ -114,6 +114,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {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_CONSTRUCT_HELLO_VERIFY_REQUEST),
+     "dtls_construct_hello_verify_request"},
     {ERR_FUNC(SSL_F_DTLS_GET_REASSEMBLED_MESSAGE),
      "DTLS_GET_REASSEMBLED_MESSAGE"},
     {ERR_FUNC(SSL_F_READ_STATE_MACHINE), "READ_STATE_MACHINE"},
index 425d395..891030c 100644 (file)
@@ -2201,6 +2201,7 @@ __owur int tls_construct_server_hello(SSL *s);
 __owur int ssl3_send_hello_request(SSL *s);
 __owur int tls_construct_hello_request(SSL *s);
 __owur int ssl3_send_server_key_exchange(SSL *s);
+__owur int dtls_construct_hello_verify_request(SSL *s);
 __owur int tls_construct_server_key_exchange(SSL *s);
 __owur int ssl3_send_certificate_request(SSL *s);
 __owur int tls_construct_certificate_request(SSL *s);
index e2cf11e..2fe3df4 100644 (file)
@@ -171,6 +171,11 @@ int ssl3_accept(SSL *s)
     return state_machine(s, 1);
 }
 
+int dtls1_accept(SSL *s)
+{
+    return state_machine(s, 1);
+}
+
 /*
  * 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
@@ -1490,6 +1495,7 @@ static int server_read_transition(SSL *s, int mt)
 
     switch(st->hand_state) {
     case TLS_ST_BEFORE:
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
         if (mt == SSL3_MT_CLIENT_HELLO) {
             st->hand_state = TLS_ST_SR_CLNT_HELLO;
             return 1;
@@ -1726,9 +1732,16 @@ static enum WRITE_TRAN server_write_transition(SSL *s)
             return WRITE_TRAN_CONTINUE;
 
         case TLS_ST_SR_CLNT_HELLO:
-            st->hand_state = TLS_ST_SW_SRVR_HELLO;
+            if (SSL_IS_DTLS(s) && !s->d1->cookie_verified
+                    && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE))
+                st->hand_state = DTLS_ST_SW_HELLO_VERIFY_REQUEST;
+            else
+                st->hand_state = TLS_ST_SW_SRVR_HELLO;
             return WRITE_TRAN_CONTINUE;
 
+        case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+            return WRITE_TRAN_FINISHED;
+
         case TLS_ST_SW_SRVR_HELLO:
             if (s->hit) {
                 if (s->tlsext_ticket_expected)
@@ -1826,6 +1839,44 @@ static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst)
     switch(st->hand_state) {
     case TLS_ST_SW_HELLO_REQ:
         s->shutdown = 0;
+        if (SSL_IS_DTLS(s))
+            dtls1_clear_record_buffer(s);
+        break;
+
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        s->shutdown = 0;
+        if (SSL_IS_DTLS(s)) {
+            dtls1_clear_record_buffer(s);
+            /* We don't buffer this message so don't use the timer */
+            st->use_timer = 0;
+        }
+        break;
+
+    case TLS_ST_SW_SRVR_HELLO:
+        if (SSL_IS_DTLS(s)) {
+            /*
+             * Messages we write from now on should be bufferred and
+             * retransmitted if necessary, so we need to use the timer now
+             */
+            st->use_timer = 1;
+        }
+        break;
+
+    case TLS_ST_SW_SRVR_DONE:
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s)))
+            return dtls_wait_for_dry(s);
+#endif
+        return WORK_FINISHED_CONTINUE;
+
+    case TLS_ST_SW_SESSION_TICKET:
+        if (SSL_IS_DTLS(s)) {
+            /*
+             * We're into the last flight. We don't retransmit the last flight
+             * unless we need to, so we don't use the timer
+             */
+            st->use_timer = 0;
+        }
         break;
 
     case TLS_ST_SW_CHANGE:
@@ -1834,6 +1885,15 @@ static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst)
             statem_set_error(s);
             return WORK_ERROR;
         }
+        if (SSL_IS_DTLS(s)) {
+            /*
+             * We're into the last flight. We don't retransmit the last flight
+             * unless we need to, so we don't use the timer. This might have
+             * already been set to 0 if we sent a NewSessionTicket message,
+             * but we'll set it again here in case we didn't.
+             */
+            st->use_timer = 0;
+        }
         return WORK_FINISHED_CONTINUE;
 
     case TLS_ST_OK:
@@ -1864,12 +1924,64 @@ static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst)
         ssl3_init_finished_mac(s);
         break;
 
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+        /* HelloVerifyRequest resets Finished MAC */
+        if (s->version != DTLS1_BAD_VER)
+            ssl3_init_finished_mac(s);
+        /*
+         * The next message should be another ClientHello which we need to
+         * treat like it was the first packet
+         */
+        s->first_packet = 1;
+        break;
+
+    case TLS_ST_SW_SRVR_HELLO:
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && s->hit) {
+            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) {
+                statem_set_error(s);
+                return WORK_ERROR;
+            }
+
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY,
+                     sizeof(sctpauthkey), sctpauthkey);
+        }
+#endif
+        break;
+
     case TLS_ST_SW_CHANGE:
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && !s->hit) {
+            /*
+             * Change to new shared key of SCTP-Auth, will be ignored if
+             * no SCTP used.
+             */
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                     0, NULL);
+        }
+#endif
         if (!s->method->ssl3_enc->change_cipher_state(s,
                 SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
             statem_set_error(s);
             return WORK_ERROR;
         }
+
+        if (SSL_IS_DTLS(s))
+            dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
         break;
 
     case TLS_ST_SW_SRVR_DONE:
@@ -1880,6 +1992,16 @@ static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst)
     case TLS_ST_SW_FINISHED:
         if (statem_flush(s) != 1)
             return WORK_MORE_A;
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && s->hit) {
+            /*
+             * Change to new shared key of SCTP-Auth, will be ignored if
+             * no SCTP used.
+             */
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                     0, NULL);
+        }
+#endif
         break;
 
     default:
@@ -1902,6 +2024,9 @@ static int server_construct_message(SSL *s)
     STATEM *st = &s->statem;
 
     switch(st->hand_state) {
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        return dtls_construct_hello_verify_request(s);
+
     case TLS_ST_SW_HELLO_REQ:
         return tls_construct_hello_request(s);
 
@@ -2045,6 +2170,26 @@ static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst)
     case TLS_ST_SR_KEY_EXCH:
         return tls_post_process_client_key_exchange(s, wst);
 
+    case TLS_ST_SR_CERT_VRFY:
+#ifndef OPENSSL_NO_SCTP
+        if (    /* Is this SCTP? */
+                BIO_dgram_is_sctp(SSL_get_wbio(s))
+                /* Are we renegotiating? */
+                && s->renegotiate
+                && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
+            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));
+            statem_set_sctp_read_sock(s, 1);
+            return WORK_MORE_A;
+        } else {
+            statem_set_sctp_read_sock(s, 0);
+        }
+#endif
+        return WORK_FINISHED_CONTINUE;
+
+
     case TLS_ST_SR_FINISHED:
         if (s->hit)
             return tls_finish_handshake(s, wst);