From: Dr. Stephen Henson Date: Sat, 6 Apr 2013 14:50:12 +0000 (+0100) Subject: Dual DTLS version methods. X-Git-Tag: master-post-reformat~1330 X-Git-Url: https://git.openssl.org/?p=openssl.git;a=commitdiff_plain;h=c6913eeb762edffddecaaba5c84909d7a7962927 Dual DTLS version methods. 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. --- diff --git a/CHANGES b/CHANGES index e770a240b7..63a85fbc1d 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,11 @@ Changes between 1.0.x and 1.1.0 [xx XXX xxxx] + *) Support for DTLS 1.2. This adds two sets of DTLS methods: DTLS_*_method() + supports both DTLS 1.2 and 1.0 and should use whatever version the peer + supports and DTLSv1_2_*_method() which supports DTLS 1.2 only. + [Steve Henson] + *) Make openssl verify return errors. [Chris Palmer and Ben Laurie] diff --git a/apps/s_client.c b/apps/s_client.c index d09d6951a4..45f6ced044 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -881,6 +881,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(); diff --git a/apps/s_server.c b/apps/s_server.c index 3eaee82c78..657b042d07 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -1362,6 +1362,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(); diff --git a/ssl/d1_clnt.c b/ssl/d1_clnt.c index ec7ef0d817..40acbb756b 100644 --- a/ssl/d1_clnt.c +++ b/ssl/d1_clnt.c @@ -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++); diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index b70bce68ad..2b066e0165 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -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; } @@ -526,5 +528,3 @@ static int dtls1_handshake_write(SSL *s) { return dtls1_do_write(s, SSL3_RT_HANDSHAKE); } - - diff --git a/ssl/d1_meth.c b/ssl/d1_meth.c index 64a22d6b09..ac86dec40d 100644 --- a/ssl/d1_meth.c +++ b/ssl/d1_meth.c @@ -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) + diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c index 995e6576e0..9b600fdf53 100644 --- a/ssl/d1_pkt.c +++ b/ssl/d1_pkt.c @@ -1546,9 +1546,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; diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c index f18fb38a12..27f31b6762 100644 --- a/ssl/d1_srvr.c +++ b/ssl/d1_srvr.c @@ -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; @@ -884,8 +891,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, diff --git a/ssl/dtls1.h b/ssl/dtls1.h index 715749ae27..c6edbe39f9 100644 --- a/ssl/dtls1.h +++ b/ssl/dtls1.h @@ -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... */ diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 0a9bc1a99a..44ff247db2 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -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))) { diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 0c322cb2b1..200f4951fb 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -967,8 +967,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) @@ -1086,6 +1087,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); diff --git a/ssl/ssl.h b/ssl/ssl.h index 7072c2fbf3..cba5f4d748 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -617,6 +617,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) @@ -2115,6 +2118,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);