diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-01-12 19:39:50 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-01-12 19:49:01 +0100 |
commit | 1344b312402d3b831f2839f4a58190fd4fa380f0 (patch) | |
tree | ffb353c4c8cb09560f1996394f6deca218919e5d | |
parent | aee636d85d54d7d67e9dbaaa0b354f0ae3038b44 (diff) | |
download | gnutls-1344b312402d3b831f2839f4a58190fd4fa380f0.tar.gz |
Added Steve Dispensa's patch for safe renegotiation (with artistic changes).
Effectively reverted my previous patch 1a338cbaaeec11d958de8da4d1ae036979fccf3e.
-rw-r--r-- | lib/ext_safe_renegotiation.c | 129 | ||||
-rw-r--r-- | lib/ext_safe_renegotiation.h | 1 | ||||
-rw-r--r-- | lib/gnutls_alert.c | 1 | ||||
-rw-r--r-- | lib/gnutls_algorithms.c | 7 | ||||
-rw-r--r-- | lib/gnutls_algorithms.h | 3 | ||||
-rw-r--r-- | lib/gnutls_extensions.c | 3 | ||||
-rw-r--r-- | lib/gnutls_extensions.h | 2 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 365 | ||||
-rw-r--r-- | lib/gnutls_int.h | 24 | ||||
-rw-r--r-- | lib/gnutls_priority.c | 5 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 2 |
11 files changed, 340 insertions, 202 deletions
diff --git a/lib/ext_safe_renegotiation.c b/lib/ext_safe_renegotiation.c index 1020a49661..7cd362d15e 100644 --- a/lib/ext_safe_renegotiation.c +++ b/lib/ext_safe_renegotiation.c @@ -24,89 +24,96 @@ #include <gnutls_int.h> #include <ext_safe_renegotiation.h> -#include "gnutls_errors.h" +#include <gnutls_errors.h> -/* Each peer processes the extension in the same way - by moving the "current" - * value to "previous" and setting new "current" values. - */ int _gnutls_safe_renegotiation_recv_params (gnutls_session_t session, const opaque * data, size_t _data_size) { - ssize_t data_size = _data_size; - uint8_t len; + tls_ext_st *ext = &session->security_parameters.extensions; - DECR_LEN (data_size, 1); - len = data[0]; - DECR_LEN (data_size, len); - - if (len >= MAX_VERIFY_DATA_SIZE) - { - gnutls_assert(); - return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; - } - - memcpy (session->security_parameters.extensions.previous_verify_data, - session->security_parameters.extensions.current_verify_data, - session->security_parameters.extensions.current_verify_data_len); - - session->security_parameters.extensions.previous_verify_data_len = - session->security_parameters.extensions.current_verify_data_len; + int len = data[0]; + ssize_t data_size = _data_size; - memcpy (session->security_parameters.extensions.current_verify_data, - &data[1], len); + DECR_LEN (data_size, len+1 /* count the first byte and payload */); - if (session->security_parameters.entity == GNUTLS_SERVER) - len *= 2; + int conservative_len = len; + if (len > sizeof (ext->ri_extension_data)) + conservative_len = sizeof (ext->ri_extension_data); - session->security_parameters.extensions.current_verify_data_len = len; + memcpy (ext->ri_extension_data, &data[1], conservative_len); + ext->ri_extension_data_len = conservative_len; - session->security_parameters.extensions.safe_renegotiation_received = 1; - + /* "safe renegotiation received" means on *this* handshake; "connection using + * safe renegotiation" means that the initial hello received on the connection + * indicatd safe renegotiation. + */ + ext->safe_renegotiation_received = 1; + ext->connection_using_safe_renegotiation = 1; return 0; } -/* As a client, this sends the verify information that was saved during the - * previous finished message. As a server, echo back whatever we just received. - */ int _gnutls_safe_renegotiation_send_params (gnutls_session_t session, - opaque * data, size_t data_size) + opaque * data, size_t _data_size) { - uint8_t len = 0; /* return 0 if we're not sending this ext */ + /* The format of this extension is a one-byte length of verify data followed + * by the verify data itself. Note that the length byte does not include + * itself; IOW, empty verify data is represented as a length of 0. That means + * the minimum extension is one byte: 0x00. + */ + + ssize_t data_size = _data_size; + tls_ext_st *ext = &session->security_parameters.extensions; - if(session->security_parameters.extensions.safe_renegotiation_received || + /* Always offer the extension if we're a client */ + if (ext->connection_using_safe_renegotiation || session->security_parameters.entity == GNUTLS_CLIENT) { - if (!session->security_parameters.extensions.disable_safe_renegotiation) - { - len = session->security_parameters.extensions.current_verify_data_len; - - /* client only sends its verification data */ - if (session->security_parameters.entity == GNUTLS_CLIENT) - { - len /= 2; - } - - if (data_size < len + 1) /* save room for the length byte */ - { - gnutls_assert (); - return GNUTLS_E_SHORT_MEMORY_BUFFER; - } - - data[0] = len++; /* return total length = len + length byte */ - memcpy (&data[1], - session->security_parameters.extensions.current_verify_data, - session->security_parameters.extensions.current_verify_data_len); - } + DECR_LEN (data_size, 1); + data[0] = ext->client_verify_data_len; + + DECR_LEN (data_size, ext->client_verify_data_len); + + memcpy(&data[1], + ext->client_verify_data, + ext->client_verify_data_len); + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + data[0] += ext->server_verify_data_len; + + DECR_LEN (data_size, ext->server_verify_data_len); + + memcpy(&data[1 + ext->client_verify_data_len], + ext->server_verify_data, + ext->server_verify_data_len); + } } - return len; + return 1 + data[0]; /* don't forget the length byte */ } /** - * gnutls_safe_renegotiation_set - Used to enable and disable safe renegotiation + * gnutls_safe_negotiation_set_initial - Used to enable and disable initial safe renegotiation + * @session: is a #gnutls_session_t structure. + * @value: 0 to disable and 1 to enable + * + * Used to enable and disable initial safe renegotiation for the current + * session. By default it is allowed for a client to not advertise safe + * renegotiation capability but there might be cases where signalling + * a client of its insecurity by rejecting session might be beneficial. + * This option has meaning only in server side. + **/ +void +gnutls_safe_negotiation_set_initial (gnutls_session_t session, int value) +{ + session->internals.priorities.initial_safe_renegotiation = value; +} + +/** + * gnutls_safe_negotiation_set - Used to enable and disable safe renegotiation * @session: is a #gnutls_session_t structure. * @value: 0 to disable and 1 to enable * @@ -115,9 +122,7 @@ _gnutls_safe_renegotiation_send_params (gnutls_session_t session, * default (enable) is sufficient, but there might be servers that * cannot handle or correctly handle the extension. **/ -void -gnutls_safe_renegotiation_set (gnutls_session_t session, int value) +void gnutls_safe_renegotiation_set (gnutls_session_t session, int value) { - session->security_parameters.extensions.disable_safe_renegotiation = 1-value; + session->internals.priorities.unsafe_renegotiation = 1-value; } - diff --git a/lib/ext_safe_renegotiation.h b/lib/ext_safe_renegotiation.h index dab23a46cc..4551e8069a 100644 --- a/lib/ext_safe_renegotiation.h +++ b/lib/ext_safe_renegotiation.h @@ -26,4 +26,3 @@ int _gnutls_safe_renegotiation_recv_params (gnutls_session_t state, const opaque * data, size_t data_size); int _gnutls_safe_renegotiation_send_params (gnutls_session_t state, opaque * data, size_t); -void gnutls_safe_renegotiation_set (gnutls_session_t session, int value); diff --git a/lib/gnutls_alert.c b/lib/gnutls_alert.c index 0bb90a1e72..fa99a27eb2 100644 --- a/lib/gnutls_alert.c +++ b/lib/gnutls_alert.c @@ -200,6 +200,7 @@ gnutls_error_to_alert (int err, int *level) case GNUTLS_E_NO_CIPHER_SUITES: case GNUTLS_E_NO_COMPRESSION_ALGORITHMS: case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM: + case GNUTLS_E_SAFE_RENEGOTIATION_FAILED: ret = GNUTLS_A_HANDSHAKE_FAILURE; _level = GNUTLS_AL_FATAL; break; diff --git a/lib/gnutls_algorithms.c b/lib/gnutls_algorithms.c index 0faf4acdb7..35899ea4e2 100644 --- a/lib/gnutls_algorithms.c +++ b/lib/gnutls_algorithms.c @@ -470,6 +470,10 @@ typedef struct #define GNUTLS_DHE_RSA_AES_128_CBC_SHA256 { 0x00, 0x67 } #define GNUTLS_DHE_RSA_AES_256_CBC_SHA256 { 0x00, 0x6B } +/* Safe renegotiation */ + +#define GNUTLS_RENEGO_PROTECTION_REQUEST { GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR, GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR } + #define CIPHER_SUITES_COUNT sizeof(cs_algorithms)/sizeof(gnutls_cipher_suite_entry)-1 static const gnutls_cipher_suite_entry cs_algorithms[] = { @@ -661,6 +665,9 @@ static const gnutls_cipher_suite_entry cs_algorithms[] = { GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_AES_256_CBC_SHA256, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_RSA, GNUTLS_MAC_SHA256, GNUTLS_TLS1_2), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RENEGO_PROTECTION_REQUEST, + GNUTLS_CIPHER_UNKNOWN, GNUTLS_KX_UNKNOWN, + GNUTLS_MAC_UNKNOWN, GNUTLS_SSL3), {0, {{0, 0}}, 0, 0, 0, 0} }; diff --git a/lib/gnutls_algorithms.h b/lib/gnutls_algorithms.h index 2b59908846..307da61626 100644 --- a/lib/gnutls_algorithms.h +++ b/lib/gnutls_algorithms.h @@ -27,6 +27,9 @@ #include "gnutls_auth.h" +#define GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR 0x00 +#define GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR 0xFF + /* Functions for version handling. */ gnutls_protocol_t _gnutls_version_lowest (gnutls_session_t session); gnutls_protocol_t _gnutls_version_max (gnutls_session_t session); diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c index eccbc5af27..4e9db89e7c 100644 --- a/lib/gnutls_extensions.c +++ b/lib/gnutls_extensions.c @@ -38,6 +38,7 @@ #include <ext_session_ticket.h> #include <ext_safe_renegotiation.h> #include <ext_signature.h> +#include <ext_safe_renegotiation.h> #include <gnutls_num.h> typedef struct @@ -184,7 +185,7 @@ _gnutls_parse_extensions (gnutls_session_t session, * This list is used to check whether the (later) received * extensions are the ones we requested. */ -static void +void _gnutls_extension_list_add (gnutls_session_t session, uint16_t type) { diff --git a/lib/gnutls_extensions.h b/lib/gnutls_extensions.h index e049340793..cb672e34fb 100644 --- a/lib/gnutls_extensions.h +++ b/lib/gnutls_extensions.h @@ -29,3 +29,5 @@ int _gnutls_gen_extensions (gnutls_session_t session, opaque * data, size_t data_size); int _gnutls_ext_init (void); void _gnutls_ext_deinit (void); + +void _gnutls_extension_list_add (gnutls_session_t session, uint16_t type); diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 95d6233e5f..9830278762 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -384,26 +384,6 @@ _gnutls_user_hello_func (gnutls_session_t session, return 0; } -/* traverses the list of ciphersuites to find the - * SCSV (Secure Renegotiation indicator) ciphersuite. - */ -static int check_for_scsv(opaque* data, size_t datalen) -{ -int j; - /* check for Safe renegotiation ciphersuite - * FIXME: apply correct values here. - */ - for (j = 0; j < datalen; j += 2) - { - if (data[j] == 0xFF && data[j+1] == 0xFF) - { - return 1; - } - } - - return 0; -} - /* Read a client hello packet. * A client hello must be a known version client hello * or version 2.0 client hello (only for compatibility @@ -494,14 +474,6 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque * data, suite_ptr = &data[pos]; pos += suite_size; - - if (session->security_parameters.extensions.initial_negotiation_completed != 0 \ - && check_for_scsv(suite_ptr, suite_size) != 0) - { - gnutls_assert(); - return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; - } - /* Point to the compression methods */ DECR_LEN (len, 1); @@ -512,17 +484,17 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque * data, pos += comp_size; /* Parse the extensions (if any) + * + * Unconditionally try to parse extensions; safe renegotiation uses them in + * sslv3 and higher, even though sslv3 doesn't officially support them. */ - if (_gnutls_version_has_extensions (neg_version)) + ret = _gnutls_parse_extensions (session, GNUTLS_EXT_APPLICATION, + &data[pos], len); + /* len is the rest of the parsed length */ + if (ret < 0) { - ret = _gnutls_parse_extensions (session, GNUTLS_EXT_APPLICATION, - &data[pos], len); - /* len is the rest of the parsed length */ - if (ret < 0) - { - gnutls_assert (); - return ret; - } + gnutls_assert (); + return ret; } ret = _gnutls_user_hello_func (session, adv_version); @@ -532,36 +504,33 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque * data, return ret; } - if (_gnutls_version_has_extensions (neg_version)) + ret = _gnutls_parse_extensions (session, GNUTLS_EXT_TLS, + &data[pos], len); + /* len is the rest of the parsed length */ + if (ret < 0) { - ret = _gnutls_parse_extensions (session, GNUTLS_EXT_TLS, - &data[pos], len); - /* len is the rest of the parsed length */ - if (ret < 0) - { - gnutls_assert (); - return ret; - } + gnutls_assert (); + return ret; + } - /* resumed by session_ticket extension */ - if (session->internals.resumed == RESUME_TRUE) - { - /* to indicate the client that the current session is resumed */ - memcpy (session->internals.resumed_security_parameters.session_id, - session_id, session_id_len); - session->internals.resumed_security_parameters.session_id_size = - session_id_len; + /* resumed by session_ticket extension */ + if (session->internals.resumed == RESUME_TRUE) + { + /* to indicate the client that the current session is resumed */ + memcpy (session->internals.resumed_security_parameters.session_id, + session_id, session_id_len); + session->internals.resumed_security_parameters.session_id_size = + session_id_len; - session->internals. - resumed_security_parameters.max_record_recv_size = - session->security_parameters.max_record_recv_size; - session->internals. - resumed_security_parameters.max_record_send_size = - session->security_parameters.max_record_send_size; + session->internals. + resumed_security_parameters.max_record_recv_size = + session->security_parameters.max_record_recv_size; + session->internals. + resumed_security_parameters.max_record_send_size = + session->security_parameters.max_record_send_size; - resume_copy_required_values (session); - return 0; - } + resume_copy_required_values (session); + return 0; } /* select an appropriate cipher suite @@ -640,7 +609,7 @@ _gnutls_handshake_hash_pending (gnutls_session_t session) static int _gnutls_send_finished (gnutls_session_t session, int again) { - uint8_t data[36]; + uint8_t data[MAX_VERIFY_DATA_SIZE]; int ret; int data_size = 0; @@ -665,7 +634,7 @@ _gnutls_send_finished (gnutls_session_t session, int again) data_size = 36; } else - { /* TLS 1.0 */ + { /* TLS 1.0+ */ ret = _gnutls_finished (session, session->security_parameters.entity, data); data_size = 12; @@ -681,20 +650,30 @@ _gnutls_send_finished (gnutls_session_t session, int again) session->internals.finished_func (session, data, data_size); } - /* Save data for safe_renegotiation. + /* Save data for safe renegotiation. */ + if (data_size > MAX_VERIFY_DATA_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + if (session->security_parameters.entity == GNUTLS_CLIENT) { - memcpy (session->security_parameters.extensions.current_verify_data, + session->security_parameters.extensions.client_verify_data_len = + data_size; + + memcpy (session->security_parameters.extensions.client_verify_data, data, data_size); } else { - memcpy (&session->security_parameters.extensions.current_verify_data[data_size], + session->security_parameters.extensions.server_verify_data_len = + data_size; + + memcpy (session->security_parameters.extensions.server_verify_data, data, data_size); } - session->security_parameters.extensions.current_verify_data_len = - data_size*2; ret = _gnutls_send_handshake (session, data_size ? data : NULL, data_size, @@ -770,20 +749,27 @@ _gnutls_recv_finished (gnutls_session_t session) } gnutls_free (vrfy); - /* For safe renegotiation */ + /* Save peer's verify data for safe renegotiation */ + if (data_size > MAX_VERIFY_DATA_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + tls_ext_st *ext = &session->security_parameters.extensions; + if (session->security_parameters.entity == GNUTLS_CLIENT) { - memcpy (&session->security_parameters.extensions.current_verify_data[data_size], - data, data_size); + memcpy (ext->server_verify_data, data, data_size); + ext->server_verify_data_len = data_size; } - else /* server */ + else { - memcpy (session->security_parameters.extensions.current_verify_data, - data, data_size); + memcpy (ext->client_verify_data, data, data_size); + ext->client_verify_data_len = data_size; } - session->security_parameters.extensions.current_verify_data_len = data_size*2; - session->security_parameters.extensions.initial_negotiation_completed = 1; + ext->initial_negotiation_completed = 1; return ret; } @@ -838,6 +824,25 @@ _gnutls_server_select_suite (gnutls_session_t session, opaque * data, * supported by the peer. */ + /* First, check for safe renegotiation SCSV. + */ + { + int offset; + + for(offset = 0; offset < datalen; offset += 2) + { + /* TLS_RENEGO_PROTECTION_REQUEST = { 0x00, 0xff } */ + if (data[offset] == GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR && + data[offset+1] == GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR) + { + _gnutls_handshake_log ("HSK[%p]: Received safe renegotiation CS\n", session); + session->security_parameters.extensions.safe_renegotiation_received = 1; + session->security_parameters.extensions.connection_using_safe_renegotiation = 1; + break; + } + } + } + pk_algo = _gnutls_server_find_pk_algos_in_ciphersuites (data, datalen); x = _gnutls_supported_ciphersuites (session, &ciphers); @@ -888,11 +893,6 @@ _gnutls_server_select_suite (gnutls_session_t session, opaque * data, retval = GNUTLS_E_UNKNOWN_CIPHER_SUITE; - if (check_for_scsv(data, datalen) != 0) - { - session->security_parameters.extensions.safe_renegotiation_received = 1; - } - for (j = 0; j < datalen; j += 2) { for (i = 0; i < x; i++) @@ -1128,7 +1128,7 @@ _gnutls_send_handshake (gnutls_session_t session, void *i_data, if (i_datasize > 0) memcpy (&data[pos], i_data, i_datasize); - _gnutls_handshake_log ("HSK[%p]: %s was send [%ld bytes]\n", + _gnutls_handshake_log ("HSK[%p]: %s was sent [%ld bytes]\n", session, _gnutls_handshake2str (type), (long) datasize); @@ -1750,27 +1750,27 @@ _gnutls_read_server_hello (gnutls_session_t session, /* Parse extensions. */ - if (_gnutls_version_has_extensions (version)) + ret = _gnutls_parse_extensions (session, GNUTLS_EXT_ANY, + &data[pos], len); + /* len is the rest of the parsed length */ + if (ret < 0) { - ret = _gnutls_parse_extensions (session, GNUTLS_EXT_ANY, - &data[pos], len); - /* len is the rest of the parsed length */ - if (ret < 0) - { - gnutls_assert (); - return ret; - } + gnutls_assert (); + return ret; } + return ret; } /* This function copies the appropriate ciphersuites to a locally allocated buffer - * Needed in client hello messages. Returns the new data length. + * Needed in client hello messages. Returns the new data length. If add_scsv is + * true, add the special safe renegotiation CS. */ static int _gnutls_copy_ciphersuites (gnutls_session_t session, - opaque * ret_data, size_t ret_data_size) + opaque * ret_data, size_t ret_data_size, + int add_scsv) { int ret, i; cipher_suite_st *cipher_suites; @@ -1806,6 +1806,9 @@ _gnutls_copy_ciphersuites (gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } + if (add_scsv) + ++ret; + cipher_num = ret; cipher_num *= sizeof (uint16_t); /* in order to get bytes */ @@ -1823,11 +1826,21 @@ _gnutls_copy_ciphersuites (gnutls_session_t session, _gnutls_write_uint16 (cipher_num, ret_data); pos += 2; - for (i = 0; i < (cipher_num / 2); i++) + uint16_t loop_max = add_scsv ? cipher_num - 2 : cipher_num; + + for (i = 0; i < (loop_max / 2); i++) { memcpy (&ret_data[pos], cipher_suites[i].suite, 2); pos += 2; } + + if (add_scsv) + { + /* Safe renegotiation signalling CS value is { 0x00, 0xff } */ + ret_data[pos++] = 0x00; + ret_data[pos++] = 0xff; + } + gnutls_free (cipher_suites); return datalen; @@ -2002,8 +2015,20 @@ _gnutls_send_client_hello (gnutls_session_t session, int again) /* Copy the ciphersuites. + * + * If using SSLv3 Send TLS_RENEGO_PROTECTION_REQUEST SCSV for MITM + * prevention on initial negotiation (but not renegotiation; that's + * handled with the RI extension below). */ - ret = _gnutls_copy_ciphersuites (session, extdata, extdatalen); + if(!session->security_parameters.extensions.initial_negotiation_completed && + session->security_parameters.entity == GNUTLS_CLIENT) + { + ret = _gnutls_copy_ciphersuites (session, extdata, extdatalen, TRUE); + _gnutls_extension_list_add (session, GNUTLS_EXTENSION_SAFE_RENEGOTIATION); + } + else + ret = _gnutls_copy_ciphersuites (session, extdata, extdatalen, FALSE); + if (ret > 0) { datalen += ret; @@ -2085,6 +2110,53 @@ _gnutls_send_client_hello (gnutls_session_t session, int again) return ret; } } + else if(session->security_parameters.extensions.initial_negotiation_completed) + { + /* For SSLv3 only, we will (only) to send the RI extension; we must + * send it every time we renegotiate. We don't want to send anything + * else, out of concern for interoperability. + * + * If this is an initial negotiation, we already sent SCSV above. + */ + + opaque buf[256]; /* opaque renegotiated_connection<0..255> */ + ret = _gnutls_safe_renegotiation_send_params (session, buf, sizeof(buf)); + + if (ret < 0) + { + gnutls_assert (); + gnutls_free (data); + gnutls_free (extdata); + return ret; + } + + datalen += ret + 6; /* extlen(2) + type(2) + len(2) + ret */ + + data = gnutls_realloc_fast (data, datalen); + if (data == NULL) + { + gnutls_assert (); + gnutls_free (extdata); + return GNUTLS_E_MEMORY_ERROR; + } + + /* total extensions length (one extension, with type(2) + len(2)) */ + _gnutls_write_uint16 (4 + ret, &data[pos]); + pos += 2; + + /* TLS RI extension type is 0xff01 */ + data[pos++] = 0xff; + data[pos++] = 0x01; + + _gnutls_write_uint16 (ret, &data[pos]); + pos += 2; + + memcpy(&data[pos], buf, ret); + pos += ret; + + _gnutls_debug_log ("EXT[%p]: Sending extension safe renegotiation (SSLv3)\n", + session); + } gnutls_free (extdata); } @@ -2210,6 +2282,8 @@ _gnutls_send_hello (gnutls_session_t session, int again) { int ret; + session->security_parameters.extensions.safe_renegotiation_received = 0; + if (session->security_parameters.entity == GNUTLS_CLIENT) { ret = _gnutls_send_client_hello (session, again); @@ -2232,8 +2306,6 @@ _gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen) { int ret; - session->security_parameters.extensions.safe_renegotiation_received = 0; - if (session->security_parameters.entity == GNUTLS_CLIENT) { ret = _gnutls_read_server_hello (session, data, datalen); @@ -2253,43 +2325,80 @@ _gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen) return ret; } } + + /* Safe renegotiation */ + tls_ext_st *ext = &session->security_parameters.extensions; - if (!session->security_parameters.extensions.disable_safe_renegotiation) + if (ext->safe_renegotiation_received) { - if (session->security_parameters.extensions.safe_renegotiation_received) + if ((ext->ri_extension_data_len < ext->client_verify_data_len) || + (memcmp (ext->ri_extension_data, + ext->client_verify_data, + ext->client_verify_data_len))) + { + gnutls_assert(); + _gnutls_handshake_log ("Safe renegotiation failed (1)\n"); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + if (session->security_parameters.entity == GNUTLS_CLIENT) { - if ((session->security_parameters.extensions.current_verify_data_len != - session->security_parameters.extensions.previous_verify_data_len) || - (memcmp (session->security_parameters.extensions.current_verify_data, - session->security_parameters.extensions.previous_verify_data, - session->security_parameters.extensions.current_verify_data_len))) - { + if ((ext->ri_extension_data_len != + ext->client_verify_data_len + ext->server_verify_data_len) || + memcmp (ext->ri_extension_data + ext->client_verify_data_len, + ext->server_verify_data, ext->server_verify_data_len)) + { gnutls_assert(); - return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; - } + _gnutls_handshake_log ("Safe renegotiation failed (2)\n"); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + } + else /* Make sure there are 0 extra bytes */ + { + if (ext->ri_extension_data_len != ext->client_verify_data_len) + { + gnutls_assert(); + _gnutls_handshake_log ("Safe renegotiation failed (3)\n"); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + } + + _gnutls_handshake_log ("Safe renegotiation succeeded.\n"); + } + else /* safe renegotiation not received... */ + { + if (ext->connection_using_safe_renegotiation) + { + gnutls_assert(); + _gnutls_handshake_log ("Peer previously asked for safe renegotiation!\n"); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } + + /* Clients can't tell if it's an initial negotiation */ + if (ext->initial_negotiation_completed || + session->security_parameters.entity == GNUTLS_CLIENT) + { + if (session->internals.priorities.unsafe_renegotiation != 0) + { + _gnutls_handshake_log ("Allowing unsafe renegotiation!\n"); + } else - { - _gnutls_debug_log ("Safe renegotiation succeeded.\n"); - } + { + gnutls_assert(); + _gnutls_handshake_log ("Denying unsafe renegotiation.\n"); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; + } } else - { - if (session->security_parameters.extensions.initial_negotiation_completed) - { - if (session->internals.priorities.unsafe_renegotiation) - { - _gnutls_handshake_log ("Allowing unsafe renegotiation!\n"); - } - else - { - gnutls_assert(); - _gnutls_handshake_log ("Denying unsafe renegotiation.\n"); - return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; - } - } + { + if (session->internals.priorities.initial_safe_renegotiation==0) + { + _gnutls_handshake_log ("Allowing unsafe initial negotiation!\n"); + } else - { - _gnutls_handshake_log ("Allowing unsafe initial negotiation.\n"); + { + gnutls_assert(); + _gnutls_handshake_log ("Denying unsafe initial negotiation.\n"); + return GNUTLS_E_SAFE_RENEGOTIATION_FAILED; } } } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index bbb7d7f2a7..ba8ed9b626 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -178,7 +178,7 @@ typedef enum extensions_t GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS = 13, GNUTLS_EXTENSION_SESSION_TICKET = 35, GNUTLS_EXTENSION_INNER_APPLICATION = 37703, - GNUTLS_EXTENSION_SAFE_RENEGOTIATION = 0xff01, + GNUTLS_EXTENSION_SAFE_RENEGOTIATION = 65281, /* aka: 0xff01 */ } extensions_t; typedef enum @@ -336,20 +336,23 @@ typedef struct opaque *oprfi_server; uint16_t oprfi_server_len; - /* Safe renegotiation. */ - int disable_safe_renegotiation:1; - int safe_renegotiation_received:1; - int initial_negotiation_completed:1; - uint8_t current_verify_data[2*MAX_VERIFY_DATA_SIZE]; - size_t current_verify_data_len; - uint8_t previous_verify_data[2*MAX_VERIFY_DATA_SIZE]; - size_t previous_verify_data_len; - /* Session Ticket */ opaque *session_ticket; uint16_t session_ticket_len; struct gnutls_session_ticket_key_st *session_ticket_key; opaque session_ticket_IV[SESSION_TICKET_IV_SIZE]; + + /* Safe renegotiation. */ + int connection_using_safe_renegotiation:1; + int safe_renegotiation_received:1; + int initial_negotiation_completed:1; + uint8_t client_verify_data[MAX_VERIFY_DATA_SIZE]; + size_t client_verify_data_len; + uint8_t server_verify_data[MAX_VERIFY_DATA_SIZE]; + size_t server_verify_data_len; + uint8_t ri_extension_data[MAX_VERIFY_DATA_SIZE*2]; /* max signal is 72 bytes in s->c sslv3 */ + size_t ri_extension_data_len; + } tls_ext_st; /* auth_info_t structures now MAY contain malloced @@ -461,6 +464,7 @@ struct gnutls_priority_st /* to disable record padding */ int no_padding:1; int unsafe_renegotiation:1; + int initial_safe_renegotiation:1; int ssl3_record_version; int additional_verify_flags; }; diff --git a/lib/gnutls_priority.c b/lib/gnutls_priority.c index 88e454e52f..f12660b562 100644 --- a/lib/gnutls_priority.c +++ b/lib/gnutls_priority.c @@ -524,6 +524,8 @@ gnutls_priority_set (gnutls_session_t session, gnutls_priority_t priority) * * "%UNSAFE_RENEGOTIATION" will allow unsafe renegotiation. * + * "%INITIAL_SAFE_RENEGOTIATION" will force initial safe negotiation even if renegotiation wasn't requested. + * * "%SSL3_RECORD_VERSION" will use SSL3.0 record version in client hello. * * "%VERIFY_ALLOW_SIGN_RSA_MD5" will allow RSA-MD5 signatures in @@ -716,6 +718,9 @@ gnutls_priority_init (gnutls_priority_t * priority_cache, else if (strcasecmp (&broken_list[i][1], "UNSAFE_RENEGOTIATION") == 0) (*priority_cache)->unsafe_renegotiation = 1; + else if (strcasecmp (&broken_list[i][1], + "INITIAL_SAFE_RENEGOTIATION") == 0) + (*priority_cache)->initial_safe_renegotiation = 1; else goto error; } diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 74ab94eee6..cbcf493332 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -534,6 +534,8 @@ extern "C" { unsigned int *type, unsigned int indx); /* Safe renegotiation */ + void gnutls_safe_negotiation_set_initial (gnutls_session_t session, int value); + void gnutls_safe_renegotiation_set (gnutls_session_t session, int value); /* Opaque PRF Input |