diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-06-07 18:12:59 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-06-20 19:56:02 +0200 |
commit | 5b15314d535e6fb76d3aa50d7174fd240b52c525 (patch) | |
tree | e82c578037d4724c66e8e499845d2230002b5370 /lib | |
parent | 2b9810f19cec0cc474e56c11110c86809bf8ec02 (diff) | |
download | gnutls-5b15314d535e6fb76d3aa50d7174fd240b52c525.tar.gz |
Added support for an old version of the DTLS protocol
used by openconnect vpn client for compatibility with Cisco's AnyConnect
SSL VPN. It is marked as GNUTLS_DTLS0_9. Do not use it for newer protocols
as it has issues.
Conflicts:
NEWS
lib/libgnutls.map
Diffstat (limited to 'lib')
-rw-r--r-- | lib/algorithms.h | 5 | ||||
-rw-r--r-- | lib/algorithms/ciphersuites.c | 29 | ||||
-rw-r--r-- | lib/algorithms/protocols.c | 3 | ||||
-rw-r--r-- | lib/gnutls_constate.c | 2 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 57 | ||||
-rw-r--r-- | lib/gnutls_int.h | 4 | ||||
-rw-r--r-- | lib/gnutls_kx.c | 25 | ||||
-rw-r--r-- | lib/gnutls_session_pack.c | 70 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 13 | ||||
-rw-r--r-- | lib/libgnutls.map | 1 |
10 files changed, 186 insertions, 23 deletions
diff --git a/lib/algorithms.h b/lib/algorithms.h index 9fe0272541..748629c17c 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -69,6 +69,11 @@ gnutls_kx_algorithm_t _gnutls_cipher_suite_get_kx_algo (const uint8_t suite[2]); gnutls_mac_algorithm_t _gnutls_cipher_suite_get_mac_algo (const uint8_t suite[2]); +int +_gnutls_cipher_suite_get_id (gnutls_kx_algorithm_t kx_algorithm, + gnutls_cipher_algorithm_t cipher_algorithm, + gnutls_mac_algorithm_t mac_algorithm, uint8_t suite[2]); + /* Functions for ciphers. */ int _gnutls_cipher_is_block (gnutls_cipher_algorithm_t algorithm); int _gnutls_cipher_algo_is_aead (gnutls_cipher_algorithm_t algorithm); diff --git a/lib/algorithms/ciphersuites.c b/lib/algorithms/ciphersuites.c index 3c5a79d06d..678812e6dc 100644 --- a/lib/algorithms/ciphersuites.c +++ b/lib/algorithms/ciphersuites.c @@ -726,6 +726,35 @@ const gnutls_cipher_suite_entry * ce; return ce->name + sizeof ("GNUTLS_") - 1; } +/*- + * _gnutls_cipher_suite_get_id: + * @kx_algorithm: is a Key exchange algorithm + * @cipher_algorithm: is a cipher algorithm + * @mac_algorithm: is a MAC algorithm + * @suite: The id to be returned + * + * It fills @suite with the ID of the ciphersuite of the provided parameters. + * + * Returns: 0 on success or a negative error code otherwise. + -*/ +int +_gnutls_cipher_suite_get_id (gnutls_kx_algorithm_t kx_algorithm, + gnutls_cipher_algorithm_t cipher_algorithm, + gnutls_mac_algorithm_t mac_algorithm, uint8_t suite[2]) +{ +const gnutls_cipher_suite_entry * ce; + + ce = cipher_suite_get (kx_algorithm, cipher_algorithm, mac_algorithm); + if (ce == NULL) + return GNUTLS_E_INVALID_REQUEST; + else + { + suite[0] = ce->id[0]; + suite[1] = ce->id[1]; + } + return 0; +} + /** * gnutls_cipher_suite_info: * @idx: index of cipher suite to get information about, starts on 0. diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c index a152901a5d..01203d8903 100644 --- a/lib/algorithms/protocols.c +++ b/lib/algorithms/protocols.c @@ -43,6 +43,7 @@ static const gnutls_version_entry sup_versions[] = { {"TLS1.0", GNUTLS_TLS1, 3, 1, GNUTLS_STREAM, 1}, {"TLS1.1", GNUTLS_TLS1_1, 3, 2, GNUTLS_STREAM, 1}, {"TLS1.2", GNUTLS_TLS1_2, 3, 3, GNUTLS_STREAM, 1}, + {"DTLS0.9", GNUTLS_DTLS0_9, 1, 0, GNUTLS_DGRAM, 1}, /* Cisco AnyConnect (based on about OpenSSL 0.9.8e) */ {"DTLS1.0", GNUTLS_DTLS1_0, 254, 255, GNUTLS_DGRAM, 1}, /* 1.1 over datagram */ {0, 0, 0, 0, 0} }; @@ -242,6 +243,7 @@ _gnutls_version_has_selectable_prf (gnutls_protocol_t version) { switch (version) { + case GNUTLS_DTLS0_9: case GNUTLS_DTLS1_0: case GNUTLS_TLS1_1: case GNUTLS_TLS1_0: @@ -259,6 +261,7 @@ _gnutls_version_has_selectable_sighash (gnutls_protocol_t version) { switch (version) { + case GNUTLS_DTLS0_9: case GNUTLS_DTLS1_0: case GNUTLS_TLS1_1: case GNUTLS_TLS1_0: diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c index 5ef0c74f53..c4aa4bca21 100644 --- a/lib/gnutls_constate.c +++ b/lib/gnutls_constate.c @@ -466,7 +466,7 @@ _gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch) dst->timestamp = src->timestamp; \ dst->max_record_recv_size = src->max_record_recv_size; \ dst->max_record_send_size = src->max_record_send_size; \ - dst->version = src->version + dst->version = src->version; static void _gnutls_set_resumed_parameters (gnutls_session_t session) diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 25520c40b6..8256e141d0 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -1191,7 +1191,8 @@ _gnutls_handshake_hash_add_recvd (gnutls_session_t session, { int ret; - if (recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST || + if ((gnutls_protocol_get_version (session) != GNUTLS_DTLS0_9 && + recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) || recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST) return 0; @@ -1199,11 +1200,13 @@ _gnutls_handshake_hash_add_recvd (gnutls_session_t session, session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length; - ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer, - header, header_size); - if (ret < 0) - return gnutls_assert_val(ret); - + if (gnutls_protocol_get_version (session) != GNUTLS_DTLS0_9) + { + ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer, + header, header_size); + if (ret < 0) + return gnutls_assert_val(ret); + } if (datalen > 0) { ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer, @@ -1231,6 +1234,18 @@ _gnutls_handshake_hash_add_sent (gnutls_session_t session, { CHECK_SIZE(datalen); + if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) + { + /* Old DTLS doesn't include the header in the MAC */ + if (datalen <= 12) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + dataptr += 12; + datalen -= 12; + } + ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer, dataptr, datalen); if (ret < 0) @@ -1402,7 +1417,8 @@ _gnutls_client_set_ciphersuite (gnutls_session_t session, uint8_t suite[2]) /* check if the credentials (username, public key etc.) are ok. * Actually checks if they exist. */ - if (_gnutls_get_kx_cred + if (!session->internals.premaster_set && + _gnutls_get_kx_cred (session, _gnutls_cipher_suite_get_kx_algo (session->security_parameters.cipher_suite), &err) == NULL @@ -1823,6 +1839,8 @@ _gnutls_send_client_hello (gnutls_session_t session, int again) */ if (!IS_DTLS(session)) _gnutls_record_set_default_version (session, 3, 0); + else if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) + _gnutls_record_set_default_version (session, 1, 0); else _gnutls_record_set_default_version (session, 254, 255); } @@ -2580,12 +2598,20 @@ send_change_cipher_spec (gnutls_session_t session, int again) if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - _mbuffer_set_uhead_size(bufel, 1); + if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) + _mbuffer_set_uhead_size(bufel, 3); + else + _mbuffer_set_uhead_size(bufel, 1); _mbuffer_set_udata_size(bufel, 0); data = _mbuffer_get_uhead_ptr (bufel); data[0] = 1; + if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) + { + _gnutls_write_uint16 (session->internals.dtls.hsk_write_seq, &data[1]); + session->internals.dtls.hsk_write_seq++; + } ret = _gnutls_handshake_io_cache_int (session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, bufel); if (ret < 0) @@ -2669,7 +2695,7 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init) { int ret = 0; uint8_t ch; - + unsigned int ccs_len = 1; switch (STATE) { @@ -2689,7 +2715,10 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init) return gnutls_assert_val(ret); } - ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, 1, NULL); + if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) + ccs_len = 3; + + ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, ccs_len, NULL); if (ret <= 0) { ERR ("recv ChangeCipherSpec", ret); @@ -2697,6 +2726,9 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init) return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } + if (gnutls_protocol_get_version (session) == GNUTLS_DTLS0_9) + session->internals.dtls.hsk_read_seq++; + /* Initialize the connection session (start encryption) - in case of server */ if (init == TRUE) { @@ -2714,7 +2746,7 @@ _gnutls_recv_handshake_final (gnutls_session_t session, int init) gnutls_assert (); return ret; } - + case STATE31: STATE = STATE31; @@ -3149,7 +3181,8 @@ _gnutls_remove_unwanted_ciphersuites (gnutls_session_t session, /* if it is defined but had no credentials */ - if (_gnutls_get_kx_cred (session, kx, NULL) == NULL) + if (!session->internals.premaster_set && + _gnutls_get_kx_cred (session, kx, NULL) == NULL) { delete = 1; } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 4a60ae0d8f..02c4f9524c 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -863,6 +863,10 @@ typedef struct /* DTLS session state */ dtls_st dtls; + + /* if set it means that the master key was set using + * gnutls_session_set_master() rather than being negotiated. */ + unsigned int premaster_set:1; unsigned int cb_tls_unique_len; unsigned char cb_tls_unique[MAX_VERIFY_DATA_SIZE]; diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c index c38de186f2..0cebd385f2 100644 --- a/lib/gnutls_kx.c +++ b/lib/gnutls_kx.c @@ -73,27 +73,34 @@ send_handshake (gnutls_session_t session, uint8_t * data, size_t size, #define MASTER_SECRET "master secret" #define MASTER_SECRET_SIZE (sizeof(MASTER_SECRET)-1) -static int generate_normal_master (gnutls_session_t session, int); +static int generate_normal_master (gnutls_session_t session, gnutls_datum_t*, int); int _gnutls_generate_master (gnutls_session_t session, int keep_premaster) { if (session->internals.resumed == RESUME_FALSE) - return generate_normal_master (session, keep_premaster); + return generate_normal_master (session, &session->key->key, keep_premaster); + else if (session->internals.premaster_set) + { + gnutls_datum_t premaster; + premaster.size = sizeof(session->internals.resumed_security_parameters.master_secret); + premaster.data = session->internals.resumed_security_parameters.master_secret; + return generate_normal_master(session, &premaster, 1); + } return 0; } /* here we generate the TLS Master secret. */ -#define PREMASTER session->key->key static int -generate_normal_master (gnutls_session_t session, int keep_premaster) +generate_normal_master (gnutls_session_t session, gnutls_datum_t *premaster, + int keep_premaster) { int ret = 0; char buf[512]; - _gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", PREMASTER.size, - _gnutls_bin2hex (PREMASTER.data, PREMASTER.size, buf, + _gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", premaster->size, + _gnutls_bin2hex (premaster->data, premaster->size, buf, sizeof (buf), NULL)); _gnutls_hard_log ("INT: CLIENT RANDOM[%d]: %s\n", 32, _gnutls_bin2hex (session-> @@ -114,7 +121,7 @@ generate_normal_master (gnutls_session_t session, int keep_premaster) session->security_parameters.server_random, GNUTLS_RANDOM_SIZE); ret = - _gnutls_ssl3_generate_random (PREMASTER.data, PREMASTER.size, + _gnutls_ssl3_generate_random (premaster->data, premaster->size, rnd, 2 * GNUTLS_RANDOM_SIZE, GNUTLS_MASTER_SIZE, session-> @@ -131,14 +138,14 @@ generate_normal_master (gnutls_session_t session, int keep_premaster) session->security_parameters.server_random, GNUTLS_RANDOM_SIZE); ret = - _gnutls_PRF (session, PREMASTER.data, PREMASTER.size, + _gnutls_PRF (session, premaster->data, premaster->size, MASTER_SECRET, MASTER_SECRET_SIZE, rnd, 2 * GNUTLS_RANDOM_SIZE, GNUTLS_MASTER_SIZE, session->security_parameters.master_secret); } if (!keep_premaster) - _gnutls_free_datum (&PREMASTER); + _gnutls_free_datum (premaster); if (ret < 0) return ret; diff --git a/lib/gnutls_session_pack.c b/lib/gnutls_session_pack.c index 3e89b981e8..2588873453 100644 --- a/lib/gnutls_session_pack.c +++ b/lib/gnutls_session_pack.c @@ -40,6 +40,7 @@ #include <gnutls_num.h> #include <gnutls_extensions.h> #include <gnutls_constate.h> +#include <algorithms.h> static int pack_certificate_auth_info (gnutls_session_t, gnutls_buffer_st * packed_session); @@ -860,3 +861,72 @@ unpack_security_parameters (gnutls_session_t session, gnutls_buffer_st * ps) error: return ret; } + +/** + * gnutls_session_set_premaster: + * @session: is a #gnutls_session_t structure. + * @entity: GNUTLS_SERVER or GNUTLS_CLIENT + * @version: the TLS protocol version + * @kx: the key exchange method + * @cipher: the cipher + * @mac: the MAC algorithm + * @comp: the compression method + * @master: the master key to use + * @session_id: the session identifier + * + * This function sets the premaster secret in a session. This is + * a function intended for exceptional uses. Do not use this + * function unless you are implementing a legacy protocol. + * Use gnutls_session_set_data() instead. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned. + **/ +int +gnutls_session_set_premaster (gnutls_session_t session, unsigned int entity, + gnutls_protocol_t version, + gnutls_kx_algorithm_t kx, + gnutls_cipher_algorithm_t cipher, + gnutls_mac_algorithm_t mac, + gnutls_compression_method_t comp, + const gnutls_datum_t* master, + const gnutls_datum_t * session_id) +{ + int ret; + + memset (&session->internals.resumed_security_parameters, 0, + sizeof (session->internals.resumed_security_parameters)); + + session->internals.resumed_security_parameters.entity = entity; + session->internals.resumed_security_parameters.kx_algorithm = kx; + + ret = _gnutls_cipher_suite_get_id(kx, cipher, mac, session->internals.resumed_security_parameters.cipher_suite); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.resumed_security_parameters.compression_method = comp; + session->internals.resumed_security_parameters.cert_type = GNUTLS_CRT_UNKNOWN; + session->internals.resumed_security_parameters.version = version; + + if (master->size != GNUTLS_MASTER_SIZE) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + memcpy(session->internals.resumed_security_parameters.master_secret, master->data, master->size); + + if (session_id->size > GNUTLS_MAX_SESSION_ID) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + session->internals.resumed_security_parameters.session_id_size = session_id->size; + memcpy(session->internals.resumed_security_parameters.session_id, session_id->data, session_id->size); + + session->internals.resumed_security_parameters.max_record_send_size = + session->internals.resumed_security_parameters.max_record_recv_size = DEFAULT_MAX_RECORD_SIZE; + + session->internals.resumed_security_parameters.timestamp = time(0); + + session->internals.resumed_security_parameters.ecc_curve = GNUTLS_ECC_CURVE_INVALID; + + session->internals.premaster_set = 1; + + return 0; +} diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 9d78967681..02832bfcae 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -503,6 +503,7 @@ extern "C" * @GNUTLS_TLS1_1: TLS version 1.1. * @GNUTLS_TLS1_2: TLS version 1.2. * @GNUTLS_DTLS1_0: DTLS version 1.0. + * @GNUTLS_DTLS0_9: DTLS version 0.9 (Cisco AnyConnect / OpenSSL 0.9.8e). * @GNUTLS_VERSION_MAX: Maps to the highest supported TLS version. * @GNUTLS_VERSION_UNKNOWN: Unknown SSL/TLS version. * @@ -516,7 +517,8 @@ extern "C" GNUTLS_TLS1_1 = 3, GNUTLS_TLS1_2 = 4, GNUTLS_DTLS1_0 = 5, - GNUTLS_VERSION_MAX = GNUTLS_DTLS1_0, + GNUTLS_DTLS0_9 = 6, + GNUTLS_VERSION_MAX = GNUTLS_DTLS0_9, GNUTLS_VERSION_UNKNOWN = 0xff } gnutls_protocol_t; @@ -965,6 +967,15 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session); void gnutls_session_get_random (gnutls_session_t session, gnutls_datum_t* client, gnutls_datum_t* server); + int gnutls_session_set_premaster (gnutls_session_t session, unsigned int entity, + gnutls_protocol_t version, + gnutls_kx_algorithm_t kx, + gnutls_cipher_algorithm_t cipher, + gnutls_mac_algorithm_t mac, + gnutls_compression_method_t comp, + const gnutls_datum_t* master, + const gnutls_datum_t * session_id); + /* returns the session ID */ #define GNUTLS_MAX_SESSION_ID 32 int gnutls_session_get_id (gnutls_session_t session, void *session_id, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index b2b9825368..108c765808 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -788,6 +788,7 @@ GNUTLS_3_0_0 { gnutls_pubkey_verify_hash2; gnutls_pk_to_sign; gnutls_certificate_set_x509_system_trust; + gnutls_session_set_premaster; } GNUTLS_2_12; GNUTLS_PRIVATE { |