From: Nikos Mavrogiannopoulos Date: Thu, 7 Jun 2012 16:12:59 +0000 (+0200) Subject: Added support for an old version of the DTLS protocol X-Git-Tag: gnutls_3_0_21~57 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fd5ca1afb7b223f1ce0c5330f2611996491c6aae;p=thirdparty%2Fgnutls.git 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. --- diff --git a/NEWS b/NEWS index c0406d2fff..a62b9c2ca2 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,11 @@ See the end for copying conditions. ** gnutls-cli: --benchmark-tls was split to --benchmark-tls-kx and --benchmark-tls-ciphers +** libgnutls: 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. + ** libgnutls: requires libnettle 2.5. ** libgnutls: Use the PKCS #1 1.5 encoding provided by nettle (2.5) @@ -48,6 +53,7 @@ gnutls_x509_trust_list_add_system_trust: Added gnutls_x509_trust_list_add_trust_file: Added gnutls_x509_trust_list_add_trust_mem: Added gnutls_pk_to_sign: Added +gnutls_session_set_premaster: Added gnutls_pubkey_verify_hash: Deprecated (use gnutls_pubkey_verify_hash2) gnutls_pubkey_verify_data: Deprecated (use gnutls_pubkey_verify_data2) diff --git a/doc/Makefile.am b/doc/Makefile.am index c7346f57d4..65141c85d8 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -686,6 +686,7 @@ FUNCS += functions/gnutls_session_set_data FUNCS += functions/gnutls_session_get_data FUNCS += functions/gnutls_session_get_data2 FUNCS += functions/gnutls_session_get_random +FUNCS += functions/gnutls_session_set_premaster FUNCS += functions/gnutls_session_get_id FUNCS += functions/gnutls_session_channel_binding FUNCS += functions/gnutls_session_is_resumed 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 #include #include +#include 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 4bdf64bef6..3885e56576 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -505,6 +505,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. * @@ -518,7 +519,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; @@ -969,6 +971,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 23dc1a15b2..9faa8ccce9 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -787,16 +787,17 @@ GNUTLS_3_0_0 { gnutls_session_get_random; gnutls_pubkey_verify_hash2; gnutls_pk_to_sign; + gnutls_certificate_set_x509_system_trust; } GNUTLS_2_12; GNUTLS_3_1_0 { global: - gnutls_certificate_set_x509_system_trust; gnutls_pkcs11_obj_list_import_url2; gnutls_x509_trust_list_add_system_trust; gnutls_x509_trust_list_add_trust_file; gnutls_x509_trust_list_add_trust_mem; gnutls_pkcs12_simple_parse; + gnutls_session_set_premaster; } GNUTLS_3_0_0; GNUTLS_PRIVATE {