summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2010-01-12 19:39:50 +0100
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2010-01-12 19:49:01 +0100
commit1344b312402d3b831f2839f4a58190fd4fa380f0 (patch)
treeffb353c4c8cb09560f1996394f6deca218919e5d
parentaee636d85d54d7d67e9dbaaa0b354f0ae3038b44 (diff)
downloadgnutls-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.c129
-rw-r--r--lib/ext_safe_renegotiation.h1
-rw-r--r--lib/gnutls_alert.c1
-rw-r--r--lib/gnutls_algorithms.c7
-rw-r--r--lib/gnutls_algorithms.h3
-rw-r--r--lib/gnutls_extensions.c3
-rw-r--r--lib/gnutls_extensions.h2
-rw-r--r--lib/gnutls_handshake.c365
-rw-r--r--lib/gnutls_int.h24
-rw-r--r--lib/gnutls_priority.c5
-rw-r--r--lib/includes/gnutls/gnutls.h.in2
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