DTLSv1_listen rewrite
authorMatt Caswell <matt@openssl.org>
Mon, 14 Sep 2015 21:49:35 +0000 (22:49 +0100)
committerMatt Caswell <matt@openssl.org>
Wed, 23 Sep 2015 12:53:26 +0000 (13:53 +0100)
The existing implementation of DTLSv1_listen() is fundamentally flawed. This
function is used in DTLS solutions to listen for new incoming connections
from DTLS clients. A client will send an initial ClientHello. The server
will respond with a HelloVerifyRequest containing a unique cookie. The
client the responds with a second ClientHello - which this time contains the
cookie.

Once the cookie has been verified then DTLSv1_listen() returns to user code,
which is typically expected to continue the handshake with a call to (for
example) SSL_accept().

Whilst listening for incoming ClientHellos, the underlying BIO is usually in
an unconnected state. Therefore ClientHellos can come in from *any* peer.
The arrival of the first ClientHello without the cookie, and the second one
with it, could be interspersed with other intervening messages from
different clients.

The whole purpose of this mechanism is as a defence against DoS attacks. The
idea is to avoid allocating state on the server until the client has
verified that it is capable of receiving messages at the address it claims
to come from. However the existing DTLSv1_listen() implementation completely
fails to do this. It attempts to super-impose itself on the standard state
machine and reuses all of this code. However the standard state machine
expects to operate in a stateful manner with a single client, and this can
cause various problems.

A second more minor issue is that the return codes from this function are
quite confused, with no distinction made between fatal and non-fatal errors.
Most user code treats all errors as non-fatal, and simply retries the call
to DTLSv1_listen().

This commit completely rewrites the implementation of DTLSv1_listen() and
provides a stand alone implementation that does not rely on the existing
state machine. It also provides more consistent return codes.

Reviewed-by: Andy Polyakov <appro@openssl.org>
include/openssl/ssl.h
ssl/d1_both.c
ssl/d1_lib.c
ssl/d1_srvr.c
ssl/record/rec_layer_d1.c
ssl/record/record.h
ssl/ssl_err.c
ssl/ssl_locl.h

index 1334eb61610b76d63ce8045392b1094774c7cf78..192640e1dbee6ef7f04d63da63ff0203563191e0 100644 (file)
@@ -1913,6 +1913,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_DTLS1_GET_RECORD                           254
 # define SSL_F_DTLS1_HANDLE_TIMEOUT                       297
 # define SSL_F_DTLS1_HEARTBEAT                            305
+# define SSL_F_DTLS1_LISTEN                               350
 # define SSL_F_DTLS1_OUTPUT_CERT_CHAIN                    255
 # define SSL_F_DTLS1_PREPROCESS_FRAGMENT                  288
 # define SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE           256
@@ -2166,6 +2167,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE    307
 # define SSL_R_COMPRESSION_LIBRARY_ERROR                  142
 # define SSL_R_CONNECTION_TYPE_NOT_SET                    144
+# define SSL_R_COOKIE_GEN_CALLBACK_FAILURE                400
 # define SSL_R_COOKIE_MISMATCH                            308
 # define SSL_R_DATA_BETWEEN_CCS_AND_FINISHED              145
 # define SSL_R_DATA_LENGTH_TOO_LONG                       146
@@ -2189,6 +2191,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST              151
 # define SSL_R_EXCESSIVE_MESSAGE_SIZE                     152
 # define SSL_R_EXTRA_DATA_IN_MESSAGE                      153
+# define SSL_R_FRAGMENTED_CLIENT_HELLO                    401
 # define SSL_R_GOT_A_FIN_BEFORE_A_CCS                     154
 # define SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS                355
 # define SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION           356
@@ -2201,6 +2204,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_INVALID_COMPRESSION_ALGORITHM              341
 # define SSL_R_INVALID_NULL_CMD_NAME                      385
 # define SSL_R_INVALID_PURPOSE                            278
+# define SSL_R_INVALID_SEQUENCE_NUMBER                    402
 # define SSL_R_INVALID_SERVERINFO_DATA                    388
 # define SSL_R_INVALID_SRP_USERNAME                       357
 # define SSL_R_INVALID_STATUS_RESPONSE                    328
@@ -2250,6 +2254,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_NO_SHARED_SIGATURE_ALGORITHMS              376
 # define SSL_R_NO_SRTP_PROFILES                           359
 # define SSL_R_NO_VERIFY_CALLBACK                         194
+# define SSL_R_NO_VERIFY_COOKIE_CALLBACK                  403
 # define SSL_R_NULL_SSL_CTX                               195
 # define SSL_R_NULL_SSL_METHOD_PASSED                     196
 # define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED            197
index 52b7304af2a61de982dbb2bd5f0cc2a90bcfb7dc..a69622aea58783f2ce74968b9f205a3132a8c301 100644 (file)
@@ -454,15 +454,26 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
      * absence of an optional handshake message
      */
     if (s->s3->tmp.reuse_message) {
-        s->s3->tmp.reuse_message = 0;
         if ((mt >= 0) && (s->s3->tmp.message_type != mt)) {
             al = SSL_AD_UNEXPECTED_MESSAGE;
             SSLerr(SSL_F_DTLS1_GET_MESSAGE, SSL_R_UNEXPECTED_MESSAGE);
             goto f_err;
         }
         *ok = 1;
-        s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+
+
+        /*
+         * Messages reused from dtls1_listen also have the record header in
+         * the buffer which we need to skip over.
+         */
+        if (s->s3->tmp.reuse_message == DTLS1_SKIP_RECORD_HEADER) {
+            s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH
+                          + DTLS1_RT_HEADER_LENGTH;
+        } else {
+            s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+        }
         s->init_num = (int)s->s3->tmp.message_size;
+        s->s3->tmp.reuse_message = 0;
         return s->init_num;
     }
 
index d3b582a98cc810ec39cbcb723e7616ace75b8892..8a8ced8abb9ff0fdbc735fff4a4b44d1ddcda128 100644 (file)
@@ -499,23 +499,394 @@ static void get_current_time(struct timeval *t)
 #endif
 }
 
+
+#define LISTEN_SUCCESS              2
+#define LISTEN_SEND_VERIFY_REQUEST  1
+
+
 int dtls1_listen(SSL *s, struct sockaddr *client)
 {
-    int ret;
+    int next, n, ret = 0, clearpkt = 0;
+    unsigned char cookie[DTLS1_COOKIE_LENGTH];
+    unsigned char seq[SEQ_NUM_SIZE];
+    unsigned char *data, *p, *buf;
+    unsigned long reclen, fragoff, fraglen, msglen;
+    unsigned int rectype, versmajor, msgseq, msgtype, clientvers, cookielen;
+    BIO *rbio, *wbio;
+    BUF_MEM *bufm;
+    struct sockaddr_storage tmpclient;
+    PACKET pkt, msgpkt, msgpayload, session, cookiepkt;
 
     /* Ensure there is no state left over from a previous invocation */
     if (!SSL_clear(s))
         return -1;
 
+    ERR_clear_error();
+
+    rbio = SSL_get_rbio(s);
+    wbio = SSL_get_wbio(s);
+
+    if(!rbio || !wbio) {
+        SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_BIO_NOT_SET);
+        return -1;
+    }
+
+    /*
+     * We only peek at incoming ClientHello's until we're sure we are going to
+     * to respond with a HelloVerifyRequest. If its a ClientHello with a valid
+     * cookie then we leave it in the BIO for dtls1_accept to handle.
+     */
+    BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL);
+
+    /*
+     * Note: This check deliberately excludes DTLS1_BAD_VER because that version
+     * requires the MAC to be calculated *including* the first ClientHello
+     * (without the cookie). Since DTLSv1_listen is stateless that cannot be
+     * supported. DTLS1_BAD_VER must use cookies in a stateful manner (e.g. via
+     * SSL_accept)
+     */
+    if ((s->version & 0xff00) != (DTLS1_VERSION & 0xff00)) {
+        SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNSUPPORTED_SSL_VERSION);
+        return -1;
+    }
+
+    if (s->init_buf == NULL) {
+        if ((bufm = BUF_MEM_new()) == NULL) {
+            SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+
+        if (!BUF_MEM_grow(bufm, SSL3_RT_MAX_PLAIN_LENGTH)) {
+            BUF_MEM_free(bufm);
+            SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+        s->init_buf = bufm;
+    }
+    buf = (unsigned char *)s->init_buf->data;
+
+    do {
+        /* Get a packet */
+
+        clear_sys_error();
+        /*
+         * Technically a ClientHello could be SSL3_RT_MAX_PLAIN_LENGTH
+         * + DTLS1_RT_HEADER_LENGTH bytes long. Normally init_buf does not store
+         * the record header as well, but we do here. We've set up init_buf to
+         * be the standard size for simplicity. In practice we shouldn't ever
+         * receive a ClientHello as long as this. If we do it will get dropped
+         * in the record length check below.
+         */
+        n = BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH);
+
+        if (n <= 0) {
+            if(BIO_should_retry(rbio)) {
+                /* Non-blocking IO */
+                goto end;
+            }
+            return -1;
+        }
+
+        /* If we hit any problems we need to clear this packet from the BIO */
+        clearpkt = 1;
+
+        if (!PACKET_buf_init(&pkt, buf, n)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
+
+        /*
+         * Parse the received record. If there are any problems with it we just
+         * dump it - with no alert. RFC6347 says this "Unlike TLS, DTLS is
+         * resilient in the face of invalid records (e.g., invalid formatting,
+         * length, MAC, etc.).  In general, invalid records SHOULD be silently
+         * discarded, thus preserving the association; however, an error MAY be
+         * logged for diagnostic purposes."
+         */
+
+        /* this packet contained a partial record, dump it */
+        if (n < DTLS1_RT_HEADER_LENGTH) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_RECORD_TOO_SMALL);
+            goto end;
+        }
+
+        if (s->msg_callback)
+            s->msg_callback(0, 0, SSL3_RT_HEADER, buf,
+                            DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg);
+
+        /* Get the record header */
+        if (!PACKET_get_1(&pkt, &rectype)
+            || !PACKET_get_1(&pkt, &versmajor)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        if (rectype != SSL3_RT_HANDSHAKE)  {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+            goto end;
+        }
+
+        /*
+         * Check record version number. We only check that the major version is
+         * the same.
+         */
+        if (versmajor != DTLS1_VERSION_MAJOR) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_BAD_PROTOCOL_VERSION_NUMBER);
+            goto end;
+        }
+
+        if (!PACKET_forward(&pkt, 1)
+            /* Save the sequence number: 64 bits, with top 2 bytes = epoch */
+            || !PACKET_copy_bytes(&pkt, seq, SEQ_NUM_SIZE)
+            || !PACKET_get_length_prefixed_2(&pkt, &msgpkt)
+            || PACKET_remaining(&pkt) != 0) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        /* This is an initial ClientHello so the epoch has to be 0 */
+        if (seq[0] != 0 || seq[1] != 0) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+            goto end;
+        }
+
+        /* Get a pointer to the raw message for the later callback */
+        data = PACKET_data(&msgpkt);
+
+        /* Finished processing the record header, now process the message */
+        if (!PACKET_get_1(&msgpkt, &msgtype)
+            || !PACKET_get_net_3(&msgpkt, &msglen)
+            || !PACKET_get_net_2(&msgpkt, &msgseq)
+            || !PACKET_get_net_3(&msgpkt, &fragoff)
+            || !PACKET_get_net_3(&msgpkt, &fraglen)
+            || !PACKET_get_sub_packet(&msgpkt, &msgpayload, msglen)
+            || PACKET_remaining(&msgpkt) != 0) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        if (msgtype != SSL3_MT_CLIENT_HELLO) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+            goto end;
+        }
+
+        /* Message sequence number can only be 0 or 1 */
+        if(msgseq > 2) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_INVALID_SEQUENCE_NUMBER);
+            goto end;
+        }
+
+        /* We don't support a fragmented ClientHello whilst listening */
+        if (fragoff != 0 || fraglen != msglen) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_FRAGMENTED_CLIENT_HELLO);
+            goto end;
+        }
+
+        if (s->msg_callback)
+            s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, data,
+                            msglen + DTLS1_HM_HEADER_LENGTH, s,
+                            s->msg_callback_arg);
+
+        if (!PACKET_get_net_2(&msgpayload, &clientvers)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        /*
+         * Verify client version is supported
+         */
+        if ((clientvers > (unsigned int)s->method->version &&
+                              s->method->version != DTLS_ANY_VERSION)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_WRONG_VERSION_NUMBER);
+            goto end;
+        }
+
+        if (!PACKET_forward(&msgpayload, SSL3_RANDOM_SIZE)
+            || !PACKET_get_length_prefixed_1(&msgpayload, &session)
+            || !PACKET_get_length_prefixed_1(&msgpayload, &cookiepkt)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        /*
+         * Check if we have a cookie or not. If not we need to send a
+         * HelloVerifyRequest.
+         */
+        if (PACKET_remaining(&cookiepkt) == 0) {
+            next = LISTEN_SEND_VERIFY_REQUEST;
+        } else {
+            /*
+             * We have a cookie, so lets check it.
+             */
+            if (s->ctx->app_verify_cookie_cb == NULL) {
+                SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_NO_VERIFY_COOKIE_CALLBACK);
+                /* This is fatal */
+                return -1;
+            }
+            if (PACKET_remaining(&cookiepkt) > sizeof(s->d1->rcvd_cookie)
+                || s->ctx->app_verify_cookie_cb(s, PACKET_data(&cookiepkt),
+                    PACKET_remaining(&cookiepkt)) == 0) {
+                /*
+                 * We treat invalid cookies in the same was as no cookie as
+                 * per RFC6347
+                 */
+                next = LISTEN_SEND_VERIFY_REQUEST;
+            } else {
+                /* Cookie verification succeeded */
+                next = LISTEN_SUCCESS;
+            }
+        }
+
+        if (next == LISTEN_SEND_VERIFY_REQUEST) {
+            /*
+             * There was no cookie in the ClientHello so we need to send a
+             * HelloVerifyRequest. If this fails we do not worry about trying
+             * to resend, we just drop it.
+             */
+
+            /*
+             * Dump the read packet, we don't need it any more. Ignore return
+             * value
+             */
+            BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL);
+            BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH);
+            BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL);
+
+            /* Generate the cookie */
+            if (s->ctx->app_gen_cookie_cb == NULL ||
+                s->ctx->app_gen_cookie_cb(s, cookie, &cookielen) == 0) {
+                SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
+                /* This is fatal */
+                return -1;
+            }
+
+            p = &buf[DTLS1_RT_HEADER_LENGTH];
+            msglen = dtls1_raw_hello_verify_request(p + DTLS1_HM_HEADER_LENGTH,
+                                                    cookie, cookielen);
+
+            *p++ = DTLS1_MT_HELLO_VERIFY_REQUEST;
+
+            /* Message length */
+            l2n3(msglen, p);
+
+            /* Message sequence number is always 0 for a HelloVerifyRequest */
+            s2n(0, p);
+
+            /*
+             * We never fragment a HelloVerifyRequest, so fragment offset is 0
+             * and fragment length is message length
+             */
+            l2n3(0, p);
+            l2n3(msglen, p);
+
+            /* Set reclen equal to length of whole handshake message */
+            reclen = msglen + DTLS1_HM_HEADER_LENGTH;
+
+            /* Add the record header */
+            p = buf;
+
+            *(p++) = SSL3_RT_HANDSHAKE;
+            /*
+             * Special case: for hello verify request, client version 1.0 and we
+             * haven't decided which version to use yet send back using version
+             * 1.0 header: otherwise some clients will ignore it.
+             */
+            if (s->method->version == DTLS_ANY_VERSION) {
+                *(p++) = DTLS1_VERSION >> 8;
+                *(p++) = DTLS1_VERSION & 0xff;
+            } else {
+                *(p++) = s->version >> 8;
+                *(p++) = s->version & 0xff;
+            }
+
+            /*
+             * Record sequence number is always the same as in the received
+             * ClientHello
+             */
+            memcpy(p, seq, SEQ_NUM_SIZE);
+            p += SEQ_NUM_SIZE;
+
+            /* Length */
+            s2n(reclen, p);
+
+            /*
+             * Set reclen equal to length of whole record including record
+             * header
+             */
+            reclen += DTLS1_RT_HEADER_LENGTH;
+
+            if (s->msg_callback)
+                s->msg_callback(1, 0, SSL3_RT_HEADER, buf,
+                                DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg);
+
+            /*
+             * This is unneccessary if rbio and wbio are one and the same - but
+             * maybe they're not.
+             */
+            if(BIO_dgram_get_peer(rbio, &tmpclient) <= 0
+               || BIO_dgram_set_peer(wbio, &tmpclient) <= 0) {
+                SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+                goto end;
+            }
+
+            if (BIO_write(wbio, buf, reclen) < (int)reclen) {
+                if(BIO_should_retry(wbio)) {
+                    /*
+                     * Non-blocking IO...but we're stateless, so we're just
+                     * going to drop this packet.
+                     */
+                    goto end;
+                }
+                return -1;
+            }
+
+            if (BIO_flush(wbio) <= 0) {
+                if(BIO_should_retry(wbio)) {
+                    /*
+                     * Non-blocking IO...but we're stateless, so we're just
+                     * going to drop this packet.
+                     */
+                    goto end;
+                }
+                return -1;
+            }
+        }
+    } while (next != LISTEN_SUCCESS);
+
+    /*
+     * Set expected sequence numbers to continue the handshake.
+     */
+    s->d1->handshake_read_seq = 1;
+    s->d1->handshake_write_seq = 1;
+    s->d1->next_handshake_write_seq = 1;
+    DTLS_RECORD_LAYER_set_write_sequence(&s->rlayer, seq);
+
+    /*
+     * We are doing cookie exchange, so make sure we set that option in the
+     * SSL object
+     */
     SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE);
-    s->d1->listen = 1;
 
-    ret = SSL_accept(s);
-    if (ret <= 0)
-        return ret;
+    /*
+     * Put us into the "init" state so that dtls1_accept doesn't clear our
+     * state
+     */
+    s->state = SSL_ST_ACCEPT;
+
+    if(BIO_dgram_get_peer(rbio, client) <= 0) {
+        SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+        return -1;
+    }
 
-    (void)BIO_dgram_get_peer(SSL_get_rbio(s), client);
-    return 1;
+    ret = 1;
+    clearpkt = 0;
+end:
+    BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL);
+    if (clearpkt) {
+        /* Dump this packet. Ignore return value */
+        BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH);
+    }
+    return ret;
 }
 
 static int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len)
index f56bf5adef9d4bbb7d1af5e4f6b33a11898ac6a2..3d3d3f751f2461f10fb4c160c80d1885d534093c 100644 (file)
@@ -883,19 +883,35 @@ int dtls1_accept(SSL *s)
     return (ret);
 }
 
-int dtls1_send_hello_verify_request(SSL *s)
+unsigned int dtls1_raw_hello_verify_request(unsigned char *buf,
+                                            unsigned char *cookie,
+                                            unsigned char cookie_len)
 {
     unsigned int msg_len;
-    unsigned char *msg, *buf, *p;
+    unsigned char *p;
+
+    p = buf;
+    /* Always use DTLS 1.0 version: see RFC 6347 */
+    *(p++) = DTLS1_VERSION >> 8;
+    *(p++) = DTLS1_VERSION & 0xFF;
+
+    *(p++) = (unsigned char)cookie_len;
+    memcpy(p, cookie, cookie_len);
+    p += cookie_len;
+    msg_len = p - buf;
+
+    return msg_len;
+}
+
+
+int dtls1_send_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;
 
-        msg = p = &(buf[DTLS1_HM_HEADER_LENGTH]);
-        /* Always use DTLS 1.0 version: see RFC 6347 */
-        *(p++) = DTLS1_VERSION >> 8;
-        *(p++) = DTLS1_VERSION & 0xFF;
-
         if (s->ctx->app_gen_cookie_cb == NULL ||
             s->ctx->app_gen_cookie_cb(s, s->d1->cookie,
                                       &(s->d1->cookie_len)) == 0) {
@@ -905,18 +921,16 @@ int dtls1_send_hello_verify_request(SSL *s)
             return 0;
         }
 
-        *(p++) = (unsigned char)s->d1->cookie_len;
-        memcpy(p, s->d1->cookie, s->d1->cookie_len);
-        p += s->d1->cookie_len;
-        msg_len = p - msg;
+        len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH],
+                                             s->d1->cookie, s->d1->cookie_len);
 
-        dtls1_set_message_header(s, buf,
-                                 DTLS1_MT_HELLO_VERIFY_REQUEST, msg_len, 0,
-                                 msg_len);
+        dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0,
+                                 len);
+        len += DTLS1_HM_HEADER_LENGTH;
 
         s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B;
         /* number of bytes to write */
-        s->init_num = p - buf;
+        s->init_num = len;
         s->init_off = 0;
     }
 
index 74796bed6fb8921ada9dec652d74e260922191c5..b5548e626a50139abd4166bf1cf95647249eebea 100644 (file)
@@ -226,6 +226,12 @@ void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl)
     memcpy(rl->write_sequence, rl->read_sequence, sizeof(rl->write_sequence));
 }
 
+
+void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq)
+{
+    memcpy(rl->write_sequence, seq, SEQ_NUM_SIZE);
+}
+
 static int have_handshake_fragment(SSL *s, int type, unsigned char *buf,
                                    int len, int peek);
 
index 5c8fead869881176e1ceceaa3aa15aacc4ae41f5..96370710c4360017286ace791c6c02649be31abf 100644 (file)
@@ -288,8 +288,8 @@ typedef struct record_layer_st {
     int wpend_ret;
     const unsigned char *wpend_buf;
 
-    unsigned char read_sequence[8];
-    unsigned char write_sequence[8];
+    unsigned char read_sequence[SEQ_NUM_SIZE];
+    unsigned char write_sequence[SEQ_NUM_SIZE];
     
     DTLS_RECORD_LAYER *d;
 } RECORD_LAYER;
@@ -346,6 +346,7 @@ 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);
+void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq);
 __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);
index 21836d831db32b15dd9bbbee9cbda63584a08442..447bac6937822f73c61ca5718d4219207f2896d1 100644 (file)
@@ -87,6 +87,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_DTLS1_GET_RECORD), "dtls1_get_record"},
     {ERR_FUNC(SSL_F_DTLS1_HANDLE_TIMEOUT), "dtls1_handle_timeout"},
     {ERR_FUNC(SSL_F_DTLS1_HEARTBEAT), "dtls1_heartbeat"},
+    {ERR_FUNC(SSL_F_DTLS1_LISTEN), "DTLS1_LISTEN"},
     {ERR_FUNC(SSL_F_DTLS1_OUTPUT_CERT_CHAIN), "dtls1_output_cert_chain"},
     {ERR_FUNC(SSL_F_DTLS1_PREPROCESS_FRAGMENT), "DTLS1_PREPROCESS_FRAGMENT"},
     {ERR_FUNC(SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE),
@@ -406,6 +407,8 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "compression id not within private range"},
     {ERR_REASON(SSL_R_COMPRESSION_LIBRARY_ERROR), "compression library error"},
     {ERR_REASON(SSL_R_CONNECTION_TYPE_NOT_SET), "connection type not set"},
+    {ERR_REASON(SSL_R_COOKIE_GEN_CALLBACK_FAILURE),
+     "cookie gen callback failure"},
     {ERR_REASON(SSL_R_COOKIE_MISMATCH), "cookie mismatch"},
     {ERR_REASON(SSL_R_DATA_BETWEEN_CCS_AND_FINISHED),
      "data between ccs and finished"},
@@ -440,6 +443,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "error in received cipher list"},
     {ERR_REASON(SSL_R_EXCESSIVE_MESSAGE_SIZE), "excessive message size"},
     {ERR_REASON(SSL_R_EXTRA_DATA_IN_MESSAGE), "extra data in message"},
+    {ERR_REASON(SSL_R_FRAGMENTED_CLIENT_HELLO), "fragmented client hello"},
     {ERR_REASON(SSL_R_GOT_A_FIN_BEFORE_A_CCS), "got a fin before a ccs"},
     {ERR_REASON(SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS),
      "got next proto before a ccs"},
@@ -455,6 +459,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "invalid compression algorithm"},
     {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME), "invalid null cmd name"},
     {ERR_REASON(SSL_R_INVALID_PURPOSE), "invalid purpose"},
+    {ERR_REASON(SSL_R_INVALID_SEQUENCE_NUMBER), "invalid sequence number"},
     {ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA), "invalid serverinfo data"},
     {ERR_REASON(SSL_R_INVALID_SRP_USERNAME), "invalid srp username"},
     {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE), "invalid status response"},
@@ -510,6 +515,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "no shared sigature algorithms"},
     {ERR_REASON(SSL_R_NO_SRTP_PROFILES), "no srtp profiles"},
     {ERR_REASON(SSL_R_NO_VERIFY_CALLBACK), "no verify callback"},
+    {ERR_REASON(SSL_R_NO_VERIFY_COOKIE_CALLBACK), "no verify cookie callback"},
     {ERR_REASON(SSL_R_NULL_SSL_CTX), "null ssl ctx"},
     {ERR_REASON(SSL_R_NULL_SSL_METHOD_PASSED), "null ssl method passed"},
     {ERR_REASON(SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED),
index 32e6338adc099daed6070b7d09a2ee8a3c70304f..84b7458a56ac451cf8f15fa609eff2bd5e334c94 100644 (file)
@@ -1376,6 +1376,12 @@ typedef struct ssl3_state_st {
 /* Max MTU overhead we know about so far is 40 for IPv6 + 8 for UDP */
 #  define DTLS1_MAX_MTU_OVERHEAD                   48
 
+/*
+ * Flag used in message reuse to indicate the buffer contains the record
+ * header as well as the the handshake message header.
+ */
+#  define DTLS1_SKIP_RECORD_HEADER                 2
+
 struct dtls1_retransmit_state {
     EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
     EVP_MD_CTX *write_hash;     /* used for mac generation */
@@ -1990,6 +1996,9 @@ void dtls1_start_timer(SSL *s);
 void dtls1_stop_timer(SSL *s);
 __owur int dtls1_is_timer_expired(SSL *s);
 void dtls1_double_timeout(SSL *s);
+__owur unsigned int dtls1_raw_hello_verify_request(unsigned char *buf,
+                                                   unsigned char *cookie,
+                                                   unsigned char cookie_len);
 __owur int dtls1_send_newsession_ticket(SSL *s);
 __owur unsigned int dtls1_min_mtu(SSL *s);
 __owur unsigned int dtls1_link_min_mtu(void);