From: Nikos Mavrogiannopoulos Date: Thu, 20 Mar 2003 17:11:46 +0000 (+0000) Subject: * The RSA premaster secret version check can no longer be disabled. X-Git-Tag: gnutls_0_9_3~20 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4e6da24cb4fa4fc260560898ed75d12a4140937e;p=thirdparty%2Fgnutls.git * The RSA premaster secret version check can no longer be disabled. * Implemented the counter measure discussed in the paper "Attacking RSA-based Sessions in SSL/TLS", against the attack discussed in the same paper. * Added the functions: gnutls_handshake_get_last_in(), gnutls_handshake_get_last_out(). --- diff --git a/NEWS b/NEWS index 6d20ef544b..9cc1b66277 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,12 @@ Version 0.9.3 by allowing debugging messages just by increasing the level. - The diffie Hellman ciphersuites are now of higher priority than the plain RSA. +- The RSA premaster secret version check can no longer be disabled. +- Implemented the counter measure discussed in the paper "Attacking + RSA-based Sessions in SSL/TLS", against the attack discussed in the + same paper. +- Added the functions: gnutls_handshake_get_last_in(), + gnutls_handshake_get_last_out(). Version 0.9.2 (15/03/2003) - Some corrections in the memory mapping code (file is unmapped after diff --git a/lib/auth_rsa.c b/lib/auth_rsa.c index 31f26f4e0c..5c99916b6a 100644 --- a/lib/auth_rsa.c +++ b/lib/auth_rsa.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2000,2001,2002,2003 Nikos Mavroyanopoulos + * Copyright (C) 2000,2001,2002,2003 Nikos Mavroyanopoulos * - * This file is part of GNUTLS. + * This file is part of GNUTLS. * * The GNUTLS library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -209,18 +209,22 @@ const gnutls_certificate_credentials cred; int _gnutls_proc_rsa_client_kx(gnutls_session session, opaque * data, size_t _data_size) { - gnutls_sdatum plaintext; + gnutls_sdatum plaintext = { NULL, 0 }; gnutls_datum ciphertext; int ret, dsize; GNUTLS_MPI *params; int params_len; + int randomize_key = 0; ssize_t data_size = _data_size; if (gnutls_protocol_get_version(session) == GNUTLS_SSL3) { - /* SSL 3.0 */ + /* SSL 3.0 + */ ciphertext.data = data; ciphertext.size = data_size; - } else { /* TLS 1 */ + } else { + /* TLS 1.0 + */ DECR_LEN( data_size, 2); ciphertext.data = &data[2]; dsize = _gnutls_read_uint16(data); @@ -242,32 +246,52 @@ int _gnutls_proc_rsa_client_kx(gnutls_session session, opaque * data, size_t _da params_len, 2); /* btype==2 */ if (ret < 0 || plaintext.size != TLS_MASTER_SIZE) { - /* in case decryption fails then don't inform + /* In case decryption fails then don't inform * the peer. Just use a random key. (in order to avoid * attack against pkcs-1 formating). */ - ret = 0; gnutls_assert(); - _gnutls_x509_log("auth_rsa: Possible PKCS-1 format attack\n"); + _gnutls_x509_log("auth_rsa: Possible PKCS #1 format attack\n"); + randomize_key = 1; + } else { + /* If the secret was properly formatted, then + * check the version number. + */ + if (_gnutls_get_adv_version_major(session) != plaintext.data[0] + || _gnutls_get_adv_version_minor(session) != plaintext.data[1]) + { + /* No error is returned here, if the version number check + * fails. We proceed normally. + * That is to defend against the attack described in the paper + * "Attacking RSA-based sessions in SSL/TLS" by Vlastimil Klima, + * Ondej Pokorny and Tomas Rosa. + */ + gnutls_assert(); + _gnutls_x509_log("auth_rsa: Possible PKCS #1 version check format attack\n"); + } + } + + if (randomize_key != 0) { + /* if the pkcs1 padding check failed, no need for + * that. That's why it has been initialized to zero. + */ + _gnutls_free_datum( &plaintext); RANDOMIZE_KEY(session->key->key, - gnutls_secure_malloc, GNUTLS_WEAK_RANDOM); - } else { - ret = 0; - if (session->internals.rsa_pms_check==0) - if (_gnutls_get_adv_version_major(session) != - plaintext.data[0] - || _gnutls_get_adv_version_minor(session) != - plaintext.data[1]) { - gnutls_assert(); - ret = GNUTLS_E_DECRYPTION_FAILED; - } + gnutls_secure_malloc, GNUTLS_STRONG_RANDOM); + } else { session->key->key.data = plaintext.data; session->key->key.size = plaintext.size; } - return ret; + /* This is here to avoid the version check attack + * discussed above. + */ + session->key->key.data[0] = _gnutls_get_adv_version_major(session); + session->key->key.data[1] = _gnutls_get_adv_version_minor(session); + + return 0; } @@ -294,8 +318,13 @@ int _gnutls_gen_rsa_client_kx(gnutls_session session, opaque ** data) ver = _gnutls_get_adv_version(session); - session->key->key.data[0] = _gnutls_version_get_major(ver); - session->key->key.data[1] = _gnutls_version_get_minor(ver); + if (session->internals.rsa_pms_version[0] == 0) { + session->key->key.data[0] = _gnutls_version_get_major(ver); + session->key->key.data[1] = _gnutls_version_get_minor(ver); + } else { /* use the version provided */ + session->key->key.data[0] = session->internals.rsa_pms_version[0]; + session->key->key.data[1] = session->internals.rsa_pms_version[1]; + } /* move RSA parameters to key (session). */ diff --git a/lib/gnutls.h.in.in b/lib/gnutls.h.in.in index b1b521f512..9b0f5416d1 100644 --- a/lib/gnutls.h.in.in +++ b/lib/gnutls.h.in.in @@ -84,8 +84,9 @@ typedef enum gnutls_connection_end { GNUTLS_SERVER=1, GNUTLS_CLIENT } gnutls_con typedef enum gnutls_alert_level { GNUTLS_AL_WARNING=1, GNUTLS_AL_FATAL } gnutls_alert_level; -typedef enum gnutls_alert_description { GNUTLS_A_CLOSE_NOTIFY, GNUTLS_A_UNEXPECTED_MESSAGE=10, GNUTLS_A_BAD_RECORD_MAC=20, - GNUTLS_A_DECRYPTION_FAILED, GNUTLS_A_RECORD_OVERFLOW, GNUTLS_A_DECOMPRESSION_FAILURE=30, +typedef enum gnutls_alert_description { GNUTLS_A_CLOSE_NOTIFY, + GNUTLS_A_UNEXPECTED_MESSAGE=10, GNUTLS_A_BAD_RECORD_MAC=20, + GNUTLS_A_DECRYPTION_FAILED, GNUTLS_A_RECORD_OVERFLOW, GNUTLS_A_DECOMPRESSION_FAILURE=30, GNUTLS_A_HANDSHAKE_FAILURE=40, GNUTLS_A_SSL3_NO_CERTIFICATE=41, GNUTLS_A_BAD_CERTIFICATE=42, GNUTLS_A_UNSUPPORTED_CERTIFICATE, GNUTLS_A_CERTIFICATE_REVOKED, GNUTLS_A_CERTIFICATE_EXPIRED, GNUTLS_A_CERTIFICATE_UNKNOWN, @@ -96,6 +97,14 @@ typedef enum gnutls_alert_description { GNUTLS_A_CLOSE_NOTIFY, GNUTLS_A_UNEXPECT GNUTLS_A_CERTIFICATE_UNOBTAINABLE=111, GNUTLS_A_UNRECOGNIZED_NAME=112 } gnutls_alert_description; +typedef enum gnutls_handshake_description { GNUTLS_HANDSHAKE_HELLO_REQUEST, + GNUTLS_HANDSHAKE_CLIENT_HELLO, GNUTLS_HANDSHAKE_SERVER_HELLO, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT=11, GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE, + GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, + GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE, + GNUTLS_HANDSHAKE_FINISHED=20 +} gnutls_handshake_description; + typedef enum gnutls_certificate_status { GNUTLS_CERT_NOT_TRUSTED=2, GNUTLS_CERT_INVALID=4, @@ -184,8 +193,11 @@ const char* gnutls_strerror( int error); /* Semi-internal functions. */ void gnutls_handshake_set_private_extensions(gnutls_session session, int allow); -void gnutls_record_set_cbc_protection(gnutls_session session, int prot); void gnutls_handshake_set_rsa_pms_check(gnutls_session session, int check); +gnutls_handshake_description gnutls_handshake_get_last_out( gnutls_session session); +gnutls_handshake_description gnutls_handshake_get_last_in( gnutls_session session); + +void gnutls_record_set_cbc_protection(gnutls_session session, int prot); /* Record layer functions. */ diff --git a/lib/gnutls_alert.c b/lib/gnutls_alert.c index 704024c23f..68ec163551 100644 --- a/lib/gnutls_alert.c +++ b/lib/gnutls_alert.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2000,2001,2002,2003 Nikos Mavroyanopoulos * - * This file is part of GNUTLS. + * This file is part of GNUTLS. * * The GNUTLS library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/lib/gnutls_global.c b/lib/gnutls_global.c index 237d1b0df5..f92b33ecd2 100644 --- a/lib/gnutls_global.c +++ b/lib/gnutls_global.c @@ -32,7 +32,7 @@ extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[]; extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; LOG_FUNC _gnutls_log_func; -int _gnutls_log_level = 2; /* default log level */ +int _gnutls_log_level = 1; /* default log level */ static ASN1_TYPE PKIX1_ASN; static ASN1_TYPE GNUTLS_ASN; diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 244fc1ca2f..0f8a95497d 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2000,2001,2002,2003 Nikos Mavroyanopoulos + * Copyright (C) 2000,2001,2002,2003 Nikos Mavroyanopoulos * - * This file is part of GNUTLS. + * This file is part of GNUTLS. * * The GNUTLS library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -766,6 +766,8 @@ int _gnutls_send_handshake(gnutls_session session, void *i_data, return ret; } + session->internals.last_handshake_out = type; + ret = _gnutls_handshake_io_send_int(session, GNUTLS_HANDSHAKE, type, data, datasize); @@ -979,6 +981,7 @@ int _gnutls_recv_handshake(gnutls_session session, uint8 ** data, return ret; } + session->internals.last_handshake_in = recv_type; length32 = ret; @@ -2251,18 +2254,17 @@ int _gnutls_handshake_common(gnutls_session session) int _gnutls_generate_session_id(char *session_id, uint8 * len) { char buf[64]; - opaque rand[TLS_RANDOM_SIZE]; - if (_gnutls_get_random(rand, TLS_RANDOM_SIZE, GNUTLS_WEAK_RANDOM) < + *len = TLS_RANDOM_SIZE; + + if (_gnutls_get_random(session_id, *len, GNUTLS_WEAK_RANDOM) < 0) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } - memcpy(session_id, rand, TLS_RANDOM_SIZE); - *len = TLS_RANDOM_SIZE; _gnutls_handshake_log("HSK: Generated SessionID: %s\n", - _gnutls_bin2hex(session_id, TLS_RANDOM_SIZE, buf, sizeof(buf))); + _gnutls_bin2hex(session_id, *len, buf, sizeof(buf))); return 0; } @@ -2444,3 +2446,34 @@ gnutls_protocol_version _gnutls_get_adv_version( gnutls_session session) { _gnutls_get_adv_version_minor( session)); } +/** + * gnutls_handshake_get_last_in - Returns the last handshake message received. + * @session: is a &gnutls_session structure. + * + * Returns the last handshake message received. This function is only useful + * to check where the last performed handshake failed. If the previous handshake + * succeed or was not performed at all then no meaningful value will be returned. + * + * Check gnutls.h for the available handshake descriptions. + **/ +gnutls_handshake_description gnutls_handshake_get_last_in( gnutls_session session) +{ + return session->internals.last_handshake_in; +} + +/** + * gnutls_handshake_get_last_out - Returns the last handshake message sent. + * @session: is a &gnutls_session structure. + * + * Returns the last handshake message sent. This function is only useful + * to check where the last performed handshake failed. If the previous handshake + * succeed or was not performed at all then no meaningful value will be returned. + * + * Check gnutls.h for the available handshake descriptions. + * + **/ +gnutls_handshake_description gnutls_handshake_get_last_out( gnutls_session session) +{ + return session->internals.last_handshake_out; +} + diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 95ceadeac7..e7edf79d76 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -132,6 +132,8 @@ typedef enum HandshakeType { GNUTLS_HELLO_REQUEST, GNUTLS_CLIENT_HELLO, GNUTLS_S GNUTLS_CERTIFICATE_VERIFY, GNUTLS_CLIENT_KEY_EXCHANGE, GNUTLS_FINISHED=20 } HandshakeType; +typedef HandshakeType gnutls_handshake_description; + typedef struct { opaque * data; unsigned int size; @@ -441,6 +443,12 @@ typedef struct { int may_write; int last_alert; /* last alert received */ + + /* The last handshake messages sent or received. + */ + int last_handshake_in; + int last_handshake_out; + /* this is the compression method we are going to use */ gnutls_compression_method compression_method; /* priorities */ @@ -575,8 +583,6 @@ typedef struct { int cbc_protection_hack; - int rsa_pms_check; /* 0 means enabled */ - void* user_ptr; int enable_private;/* non zero to @@ -599,6 +605,12 @@ typedef struct { */ int ignore_rdn_sequence; + /* This is used to set an arbitary version in the RSA + * PMS secret. Can be used by clients to test whether the + * server checks that version. + */ + opaque rsa_pms_version[2]; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } GNUTLS_INTERNALS; diff --git a/lib/gnutls_pk.c b/lib/gnutls_pk.c index ac8eab67dc..b4d4258b9c 100644 --- a/lib/gnutls_pk.c +++ b/lib/gnutls_pk.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "debug.h" static int _gnutls_pk_encrypt(int algo, GNUTLS_MPI * resarr, GNUTLS_MPI data, GNUTLS_MPI * pkey, int pkey_len); @@ -85,13 +86,35 @@ int _gnutls_pkcs1_rsa_encrypt(gnutls_datum * ciphertext, return GNUTLS_E_INTERNAL_ERROR; } - if ( (ret=_gnutls_get_random(ps, psize, GNUTLS_WEAK_RANDOM)) < 0) { + if ( (ret=_gnutls_get_random(ps, psize, GNUTLS_STRONG_RANDOM)) < 0) { gnutls_assert(); return ret; } for (i = 0; i < psize; i++) { - if (ps[i] == 0) - ps[i] = 0xff; + opaque rnd[3]; + + /* Read three random bytes that will be + * used to replace the zeros. + */ + if ( (ret=_gnutls_get_random( rnd, 3, GNUTLS_STRONG_RANDOM)) < 0) { + gnutls_assert(); + return ret; + } + /* use non zero values for + * the first two. + */ + if (rnd[0]==0) rnd[0] = 0xaf; + if (rnd[1]==0) rnd[1] = 0xae; + + if (ps[i] == 0) { + /* If the first one is zero then set it to rnd[0]. + * If the second one is zero then set it to rnd[1]. + * Otherwise add (mod 256) the two previous ones plus rnd[3], or use + * rnd[1] if the value == 0. + */ + if (i<2) ps[i] = rnd[i]; + else ps[i] = GMAX( rnd[3] + ps[i-1] + ps[i-2], rnd[1]); + } } break; case 1: diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index e10da9b335..624bdde8a0 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -135,6 +135,12 @@ void _gnutls_handshake_internal_state_clear( gnutls_session session) { session->internals.adv_version_minor = 0; session->internals.direction = 0; + /* use out of band data for the last + * handshake messages received. + */ + session->internals.last_handshake_in = -1; + session->internals.last_handshake_out = -1; + session->internals.resumable = RESUME_TRUE; } @@ -535,28 +541,6 @@ void gnutls_handshake_set_private_extensions(gnutls_session session, int allow) session->internals.enable_private = allow; } -/** - * gnutls_handshake_set_rsa_pms_check - Used to disable the RSA PMS check - * @session: is a &gnutls_session structure. - * @prot: is an integer (0 or 1) - * - * The TLS 1.0 handshake protocol includes a check in the in the RSA - * encrypted data (only in the case of RSA key exchange), which allows - * to detect version roll back attacks. - * - * However it seems that some broken TLS clients exist which do not - * use this check properly. The only solution is to disable this - * check completely. - * - * if check == 0 then the check is enabled (default), otherwise it - * is disabled. - * - **/ -void gnutls_handshake_set_rsa_pms_check(gnutls_session session, int check) -{ - session->internals.rsa_pms_check = check; -} - inline static void _gnutls_cal_PRF_A( gnutls_mac_algorithm algorithm, const void *secret, int secret_size, const void *seed, int seed_size, void* result) { @@ -783,3 +767,20 @@ int gnutls_record_get_direction(gnutls_session session) { return session->internals.direction; } +/*- + * _gnutls_rsa_pms_set_version - Sets a version to be used at the RSA PMS + * @session: is a &gnutls_session structure. + * @major: is the major version to use + * @minor: is the minor version to use + * + * This function will set the given version number to be used at the + * RSA PMS secret. This is only useful to clients, which want to + * test server's capabilities. + * + -*/ +void _gnutls_rsa_pms_set_version(gnutls_session session, unsigned char major, + unsigned char minor) +{ + session->internals.rsa_pms_version[0] = major; + session->internals.rsa_pms_version[1] = minor; +} diff --git a/src/tests.c b/src/tests.c index e05dbcc80b..ae3ba1f54e 100644 --- a/src/tests.c +++ b/src/tests.c @@ -44,6 +44,7 @@ static char *session_data = NULL; static char session_id[32]; static int session_data_size=0, session_id_size=0; static int sfree=0; +static int handshake_output = 0; int do_handshake( gnutls_session session) { int ret, alert; @@ -52,6 +53,8 @@ int ret, alert; ret = gnutls_handshake(session); } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); + + handshake_output = ret; if (ret < 0 && more_info != 0) { printf("\n"); @@ -496,7 +499,8 @@ int ret; * and we connect using a 3.1 client hello version, * and a 3.0 record version. Some implementations * are buggy (and vulnerable to man in the middle - * attacks) and this connection will fail. + * attacks which allow a version downgrade) and this + * connection will fail. */ ADD_ALL_CIPHERS(session); ADD_ALL_COMP(session); @@ -516,6 +520,31 @@ int ret; return SUCCEED; } +void _gnutls_rsa_pms_set_version(gnutls_session session, unsigned char major, + unsigned char minor); + +int test_rsa_pms_version_check( gnutls_session session) +{ +int ret; + /* here we use an arbitary version in the RSA PMS + * to see whether to server will check this version. + * + * A normal server would abort this handshake. + */ + ADD_ALL_CIPHERS(session); + ADD_ALL_COMP(session); + ADD_ALL_CERTTYPES(session); + ADD_ALL_PROTOCOLS(session); + ADD_ALL_MACS(session); + ADD_ALL_KX(session); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + _gnutls_rsa_pms_set_version( session, 5, 5); /* use SSL 5.5 version */ + + ret = do_handshake( session); + return ret; + +} + int test_anonymous( gnutls_session session) { int ret; diff --git a/src/tests.h b/src/tests.h index 7ae0b24ea7..52eed9e1dc 100644 --- a/src/tests.h +++ b/src/tests.h @@ -27,6 +27,7 @@ int test_bye( gnutls_session state); int test_certificate( gnutls_session state); int test_server_cas( gnutls_session state); int test_session_resume2( gnutls_session state); +int test_rsa_pms_version_check( gnutls_session session); #define GERR(ret) fprintf(stderr, "*** Error: %s\n", gnutls_strerror(ret)) diff --git a/src/tls_test.c b/src/tls_test.c index f13c3dda22..ac54d61012 100644 --- a/src/tls_test.c +++ b/src/tls_test.c @@ -86,6 +86,7 @@ static const TLS_TEST tls_tests[] = { * buggy */ { "whether we need to disable TLS 1.0", test_tls1_2, "no", "yes", "dunno" }, + { "whether the server ignores the RSA PMS version", test_rsa_pms_version_check, "yes", "no", "dunno"}, { "whether the server can accept Hello Extensions", test_hello_extension, "yes", "no", "dunno"}, { "whether the server can accept cipher suites not in SSL 3.0 spec", test_unknown_ciphersuites, "yes", "no", "dunno"}, { "for certificate information", test_certificate, "", "", "" },