Dual DTLS version methods.
authorDr. Stephen Henson <steve@openssl.org>
Sat, 6 Apr 2013 14:50:12 +0000 (15:50 +0100)
committerDr. Stephen Henson <steve@openssl.org>
Wed, 18 Sep 2013 12:46:02 +0000 (13:46 +0100)
Add new methods DTLS_*_method() which support both DTLS 1.0 and DTLS 1.2 and
pick the highest version the peer supports during negotiation.

As with SSL/TLS options can change this behaviour specifically
SSL_OP_NO_DTLSv1 and SSL_OP_NO_DTLSv1_2.
(cherry picked from commit c6913eeb762edffddecaaba5c84909d7a7962927)

Conflicts:

CHANGES

apps/s_client.c
apps/s_server.c
ssl/d1_clnt.c
ssl/d1_lib.c
ssl/d1_meth.c
ssl/d1_pkt.c
ssl/d1_srvr.c
ssl/dtls1.h
ssl/s3_clnt.c
ssl/s3_srvr.c
ssl/ssl.h

index bec6dbfb4e23c1e835e5cfdc628a017d2e6e4b5c..1c9c8b89b861f3b3586b9f38ccafe5e95aecb26c 100644 (file)
@@ -912,6 +912,11 @@ static char *jpake_secret = NULL;
                        meth=TLSv1_client_method();
 #endif
 #ifndef OPENSSL_NO_DTLS1
+               else if (strcmp(*argv,"-dtls") == 0)
+                       {
+                       meth=DTLS_client_method();
+                       socket_type=SOCK_DGRAM;
+                       }
                else if (strcmp(*argv,"-dtls1") == 0)
                        {
                        meth=DTLSv1_client_method();
index 94500689bd9617e3f2ece358c11290e87c5a2852..f8bad32b23cbe299774d4dba545b44a9620816d1 100644 (file)
@@ -1369,6 +1369,11 @@ int MAIN(int argc, char *argv[])
                        { meth=TLSv1_2_server_method(); }
 #endif
 #ifndef OPENSSL_NO_DTLS1
+               else if (strcmp(*argv,"-dtls") == 0)
+                       { 
+                       meth=DTLS_server_method();
+                       socket_type = SOCK_DGRAM;
+                       }
                else if (strcmp(*argv,"-dtls1") == 0)
                        { 
                        meth=DTLSv1_server_method();
index ec7ef0d8177483fb10f34462198a212a91b19daf..40acbb756b321d7d18b1c817603ede293d8a8f17 100644 (file)
@@ -155,6 +155,13 @@ IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION,
                        dtls1_get_client_method,
                        DTLSv1_2_enc_data)
 
+IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
+                       DTLS_client_method,
+                       ssl_undefined_function,
+                       dtls1_connect,
+                       dtls1_get_client_method,
+                       DTLSv1_2_enc_data)
+
 int dtls1_connect(SSL *s)
        {
        BUF_MEM *buf=NULL;
@@ -785,12 +792,14 @@ static int dtls1_get_hello_verify(SSL *s)
        unsigned char *data;
        unsigned int cookie_len;
 
+       s->first_packet = 1;
        n=s->method->ssl_get_message(s,
                DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A,
                DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B,
                -1,
                s->max_cert_list,
                &ok);
+       s->first_packet = 0;
 
        if (!ok) return((int)n);
 
@@ -802,14 +811,16 @@ static int dtls1_get_hello_verify(SSL *s)
                }
 
        data = (unsigned char *)s->init_msg;
-
-       if ((data[0] != (s->version>>8)) || (data[1] != (s->version&0xff)))
+#if 0
+       if (s->method->version != DTLS_ANY_VERSION &&
+               ((data[0] != (s->version>>8)) || (data[1] != (s->version&0xff))))
                {
                SSLerr(SSL_F_DTLS1_GET_HELLO_VERIFY,SSL_R_WRONG_SSL_VERSION);
                s->version=(s->version&0xff00)|data[1];
                al = SSL_AD_PROTOCOL_VERSION;
                goto f_err;
                }
+#endif
        data+=2;
 
        cookie_len = *(data++);
index b07ee3551affb12b2253675510070e277010d7f0..d372a61bea3bbc07ce2135ba03d14d712b4801ab 100644 (file)
@@ -267,6 +267,8 @@ void dtls1_clear(SSL *s)
        ssl3_clear(s);
        if (s->options & SSL_OP_CISCO_ANYCONNECT)
                s->version=DTLS1_BAD_VER;
+       else if (s->method->version == DTLS_ANY_VERSION)
+               s->version=DTLS1_2_VERSION;
        else
                s->version=s->method->version;
        }
@@ -522,5 +524,3 @@ static int dtls1_handshake_write(SSL *s)
        {
        return dtls1_do_write(s, SSL3_RT_HANDSHAKE);
        }
-       
-       
index 64a22d6b090c1bd3cc06bc2c8ef8342ee1295964..ac86dec40d086eea774909c99f70517b88c591da 100644 (file)
@@ -86,3 +86,10 @@ IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION,
                        dtls1_get_method,
                        DTLSv1_2_enc_data)
 
+IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
+                       DTLS_method,
+                       dtls1_accept,
+                       dtls1_connect,
+                       dtls1_get_method,
+                       DTLSv1_2_enc_data)
+
index 8ace79a8acc6f412e28aafbc461cffcd5d6fc743..009a434943c95f0cf8162862adcb1df8f7b5f2f1 100644 (file)
@@ -1552,9 +1552,22 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf, unsigned int len,
 
        *(p++)=type&0xff;
        wr->type=type;
-
-       *(p++)=(s->version>>8);
-       *(p++)=s->version&0xff;
+       /* 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->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B
+                       && s->method->version == DTLS_ANY_VERSION
+                       && s->client_version == DTLS1_VERSION)
+               {
+               *(p++)=DTLS1_VERSION>>8;
+               *(p++)=DTLS1_VERSION&0xff;
+               }
+       else
+               {
+               *(p++)=s->version>>8;
+               *(p++)=s->version&0xff;
+               }
 
        /* field where we are to write out packet epoch, seq num and len */
        pseq=p; 
index e8a829b354391b6d6f0aa84228e5862c00c3c6b1..d6d71b929fd1b11e6e8602ab9fed4c0b90bfc1a0 100644 (file)
@@ -153,6 +153,13 @@ IMPLEMENT_dtls1_meth_func(DTLS1_2_VERSION,
                        dtls1_get_server_method,
                        DTLSv1_2_enc_data)
 
+IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
+                       DTLS_server_method,
+                       dtls1_accept,
+                       ssl_undefined_function,
+                       dtls1_get_server_method,
+                       DTLSv1_2_enc_data)
+
 int dtls1_accept(SSL *s)
        {
        BUF_MEM *buf;
@@ -885,8 +892,9 @@ int dtls1_send_hello_verify_request(SSL *s)
                buf = (unsigned char *)s->init_buf->data;
 
                msg = p = &(buf[DTLS1_HM_HEADER_LENGTH]);
-               *(p++) = s->version >> 8;
-               *(p++) = s->version & 0xFF;
+               /* 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,
index 715749ae2719df401d7c33071aa5fd0304b2edad..c6edbe39f9cc1e666db453ebfb67c086c2987dfb 100644 (file)
@@ -86,6 +86,8 @@ extern "C" {
 #define DTLS1_VERSION                  0xFEFF
 #define DTLS1_BAD_VER                  0x0100
 #define DTLS1_2_VERSION                        0xFEFD
+/* Special value for method supporting multiple versions */
+#define DTLS_ANY_VERSION               0x1FFFF
 
 #if 0
 /* this alert description is not specified anywhere... */
index fea40acefb67a3eb1dbbc554588a80a0e41ca616..88785bf652410384f6b27b55be849c1fd324dec1 100644 (file)
@@ -694,6 +694,36 @@ int ssl3_client_hello(SSL *s)
                        if (!ssl_get_new_session(s,0))
                                goto err;
                        }
+               if (s->method->version == DTLS_ANY_VERSION)
+                       {
+                       /* Determine which DTLS version to use */
+                       int options = s->options;
+                       /* If DTLS 1.2 disabled correct the version number */
+                       if (options & SSL_OP_NO_DTLSv1_2)
+                               {
+                               /* Disabling all versions is silly: return an
+                                * error.
+                                */
+                               if (options & SSL_OP_NO_DTLSv1)
+                                       {
+                                       SSLerr(SSL_F_SSL3_CLIENT_HELLO,SSL_R_WRONG_SSL_VERSION);
+                                       goto err;
+                                       }
+                               /* Update method so we don't use any DTLS 1.2
+                                * features.
+                                */
+                               s->method = DTLSv1_client_method();
+                               s->version = DTLS1_VERSION;
+                               }
+                       else
+                               {
+                               /* We only support one version: update method */
+                               if (options & SSL_OP_NO_DTLSv1)
+                                       s->method = DTLSv1_2_client_method();
+                               s->version = DTLS1_2_VERSION;
+                               }
+                       s->client_version = s->version;
+                       }
                /* else use the pre-loaded session */
 
                p=s->s3->client_random;
@@ -721,6 +751,7 @@ int ssl3_client_hello(SSL *s)
                        Time=(unsigned long)time(NULL); /* Time */
                        l2n(Time,p);
                        RAND_pseudo_bytes(p,sizeof(s->s3->client_random)-4);
+                                       
                        }
 
                /* Do the message type and length last */
@@ -873,6 +904,11 @@ int ssl3_get_server_hello(SSL *s)
 #ifndef OPENSSL_NO_COMP
        SSL_COMP *comp;
 #endif
+       /* Hello verify request and/or server hello version may not
+        * match so set first packet if we're negotiating version.
+        */
+       if (s->method->version == DTLS_ANY_VERSION)
+               s->first_packet = 1;
 
        n=s->method->ssl_get_message(s,
                SSL3_ST_CR_SRVR_HELLO_A,
@@ -885,6 +921,7 @@ int ssl3_get_server_hello(SSL *s)
 
        if (SSL_IS_DTLS(s))
                {
+               s->first_packet = 0;
                if ( s->s3->tmp.message_type == DTLS1_MT_HELLO_VERIFY_REQUEST)
                        {
                        if ( s->d1->send_cookie == 0)
@@ -909,6 +946,21 @@ int ssl3_get_server_hello(SSL *s)
                }
 
        d=p=(unsigned char *)s->init_msg;
+       if (s->method->version == DTLS_ANY_VERSION)
+               {
+               /* Work out correct protocol version to use */
+               int hversion = (p[0] << 8)|p[1];
+               int options = s->options;
+               if (hversion == DTLS1_2_VERSION
+                       && !(options & SSL_OP_NO_DTLSv1_2))
+                       s->method = DTLSv1_2_client_method();
+               else if (hversion == DTLS1_VERSION
+                       && !(options & SSL_OP_NO_DTLSv1))
+                       s->method = DTLSv1_client_method();
+               else
+                       SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_WRONG_SSL_VERSION);
+               s->version = s->client_version = s->method->version;
+               }
 
        if ((p[0] != (s->version>>8)) || (p[1] != (s->version&0xff)))
                {
index 27e745c2250ece08779e082254a963dc1bb64510..d9a21811e49dc699d82571802cb8809c8c884eda 100644 (file)
@@ -968,8 +968,9 @@ int ssl3_get_client_hello(SSL *s)
        s->client_version=(((int)p[0])<<8)|(int)p[1];
        p+=2;
 
-       if ((s->version == DTLS1_VERSION && s->client_version > s->version) ||
-           (s->version != DTLS1_VERSION && s->client_version < s->version))
+       if ((SSL_IS_DTLS(s) && s->client_version > s->version
+                       && s->method->version != DTLS_ANY_VERSION) ||
+           (!SSL_IS_DTLS(s) && s->client_version < s->version))
                {
                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_WRONG_VERSION_NUMBER);
                if ((s->client_version>>8) == SSL3_VERSION_MAJOR)
@@ -1087,6 +1088,30 @@ int ssl3_get_client_hello(SSL *s)
                        }
 
                p += cookie_len;
+               if (s->method->version == DTLS_ANY_VERSION)
+                       {
+                       /* Select version to use */
+                       if (s->client_version <= DTLS1_2_VERSION &&
+                               !(s->options & SSL_OP_NO_DTLSv1_2))
+                               {
+                               s->version = DTLS1_2_VERSION;
+                               s->method = DTLSv1_2_server_method();
+                               }
+                       else if (s->client_version <= DTLS1_VERSION &&
+                               !(s->options & SSL_OP_NO_DTLSv1))
+                               {
+                               s->version = DTLS1_VERSION;
+                               s->method = DTLSv1_server_method();
+                               }
+                       else
+                               {
+                               SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_WRONG_VERSION_NUMBER);
+                               s->version = s->client_version;
+                               al = SSL_AD_PROTOCOL_VERSION;
+                               goto f_err;
+                               }
+                       s->session->ssl_version = s->version;
+                       }
                }
 
        n2s(p,i);
index 67a008700e09b336c5b1c5a3cf06973577532194..450f951150028211edfee8c2516ef6c5659fafe4 100644 (file)
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -668,6 +668,9 @@ struct ssl_session_st
 #define SSL_OP_NO_TLSv1_2                              0x08000000L
 #define SSL_OP_NO_TLSv1_1                              0x10000000L
 
+#define SSL_OP_NO_DTLSv1                               0x04000000L
+#define SSL_OP_NO_DTLSv1_2                             0x08000000L
+
 #define SSL_OP_NO_SSL_MASK (SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|\
        SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2)
 
@@ -2209,6 +2212,10 @@ const SSL_METHOD *DTLSv1_2_method(void); /* DTLSv1.2 */
 const SSL_METHOD *DTLSv1_2_server_method(void);        /* DTLSv1.2 */
 const SSL_METHOD *DTLSv1_2_client_method(void);        /* DTLSv1.2 */
 
+const SSL_METHOD *DTLS_method(void);           /* DTLS 1.0 and 1.2 */
+const SSL_METHOD *DTLS_server_method(void);    /* DTLS 1.0 and 1.2 */
+const SSL_METHOD *DTLS_client_method(void);    /* DTLS 1.0 and 1.2 */
+
 STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s);
 
 int SSL_do_handshake(SSL *s);