From: Nikos Mavrogiannopoulos Date: Sun, 27 Jan 2002 21:10:39 +0000 (+0000) Subject: Added support to select a certificate based on the peer's cipher X-Git-Tag: gnutls_0_3_90~176 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ff92d28ef7eee95b4136096e804b1cda7b0f6cb0;p=thirdparty%2Fgnutls.git Added support to select a certificate based on the peer's cipher suite list. (ie if DSS cipher suites are requested and a DSA certificate is available, then this will be used) --- diff --git a/lib/auth_rsa.c b/lib/auth_rsa.c index 8ac3e5064c..188fb5d347 100644 --- a/lib/auth_rsa.c +++ b/lib/auth_rsa.c @@ -79,7 +79,7 @@ int proc_rsa_client_kx(GNUTLS_STATE state, opaque * data, int data_size) gnutls_sdatum plaintext; gnutls_datum ciphertext; int ret, dsize; - MPI params[2]; + MPI params[RSA_PARAMS]; if ( gnutls_protocol_get_version(state) == GNUTLS_SSL3) { /* SSL 3.0 */ @@ -99,7 +99,7 @@ int proc_rsa_client_kx(GNUTLS_STATE state, opaque * data, int data_size) params[0] = state->gnutls_key->A; params[1] = state->gnutls_key->u; ret = - _gnutls_pkcs1_rsa_decrypt(&plaintext, ciphertext, params, 2); /* btype==2 */ + _gnutls_pkcs1_rsa_decrypt(&plaintext, ciphertext, params, 2); /* btype==2 */ if (ret < 0) { /* in case decryption fails then don't inform @@ -109,7 +109,7 @@ int proc_rsa_client_kx(GNUTLS_STATE state, opaque * data, int data_size) gnutls_assert(); - _gnutls_log( "RSA_auth: Possible PKCS-1 format attack\n"); + _gnutls_log( "RSA_AUTH: Possible PKCS-1 format attack\n"); RANDOMIZE_KEY(state->gnutls_key->key, gnutls_secure_malloc); } else { diff --git a/lib/auth_x509.c b/lib/auth_x509.c index 8b5dc4931f..68d524770a 100644 --- a/lib/auth_x509.c +++ b/lib/auth_x509.c @@ -1370,7 +1370,7 @@ int gnutls_x509pki_verify_certificate( const gnutls_datum* cert_list, int cert_l * The 'appropriate' is defined by the user. * (frontend to _gnutls_server_find_cert_index()) */ -const gnutls_cert *_gnutls_server_find_x509_cert(GNUTLS_STATE state) +const gnutls_cert *_gnutls_server_find_x509_cert(GNUTLS_STATE state, PKAlgorithm requested_algo) { int i; const GNUTLS_X509PKI_CREDENTIALS x509_cred; @@ -1382,7 +1382,7 @@ const gnutls_cert *_gnutls_server_find_x509_cert(GNUTLS_STATE state) return NULL; i = _gnutls_server_find_x509_cert_list_index(state, x509_cred->cert_list, - x509_cred->ncerts); + x509_cred->ncerts, requested_algo); if (i < 0) return NULL; @@ -1392,15 +1392,18 @@ const gnutls_cert *_gnutls_server_find_x509_cert(GNUTLS_STATE state) /* finds the most appropriate certificate in the cert list. * The 'appropriate' is defined by the user. + * + * requested_algo holds the parameters required by the peer (RSA, DSA + * or -1 for any). */ int _gnutls_server_find_x509_cert_list_index(GNUTLS_STATE state, gnutls_cert ** cert_list, - int cert_list_length) + int cert_list_length, + PKAlgorithm requested_algo) { - int i, index = -1; + int i, index = -1, j; const GNUTLS_X509PKI_CREDENTIALS cred; - - state->gnutls_internals.selected_cert_index = 0; + int my_certs_length; cred = _gnutls_get_cred(state->gnutls_key, GNUTLS_X509PKI, NULL); if (cred == NULL) { @@ -1408,9 +1411,21 @@ int _gnutls_server_find_x509_cert_list_index(GNUTLS_STATE state, return GNUTLS_E_INSUFICIENT_CRED; } - if (cred->ncerts > 0) + if (cred->ncerts > 0) { + state->gnutls_internals.selected_cert_index = 0; index = 0; /* default is use the first certificate */ + /* find one compatible certificate */ + if (requested_algo>0) { + for (i = 0; i < cred->ncerts; i++) { + if (requested_algo==cred->cert_list[i][0].subject_pk_algorithm) { + state->gnutls_internals.selected_cert_index = i; + index = i; + } + } + } + } + if (state->gnutls_internals.client_cert_callback != NULL && cred->ncerts > 0) { /* use the callback to get certificate */ gnutls_datum *my_certs = NULL; @@ -1418,15 +1433,25 @@ int _gnutls_server_find_x509_cert_list_index(GNUTLS_STATE state, gnutls_malloc(cred->ncerts * sizeof(gnutls_datum)); if (my_certs == NULL) goto clear; + my_certs_length = cred->ncerts; /* put our certificate's issuer and dn into cdn, idn */ - for (i = 0; i < cred->ncerts; i++) { - my_certs[i] = cred->cert_list[i][0].raw; + j=0; + for (i = 0; i < cred->ncerts; i++,j++) { + /* Does not add incompatible certificates */ + if (requested_algo>0) { + if (requested_algo!=cred->cert_list[i][0].subject_pk_algorithm) { + my_certs_length--; + j--; + continue; + } + } + my_certs[j] = cred->cert_list[i][0].raw; } index = state->gnutls_internals.server_cert_callback(state, my_certs, - cred->ncerts); + my_certs_length); clear: gnutls_free(my_certs); diff --git a/lib/auth_x509.h b/lib/auth_x509.h index a4126877e0..68967a075c 100644 --- a/lib/auth_x509.h +++ b/lib/auth_x509.h @@ -63,8 +63,8 @@ int _gnutls_proc_x509_client_cert_vrfy(GNUTLS_STATE, opaque *, int); int _gnutls_proc_x509_server_certificate(GNUTLS_STATE, opaque *, int); int _gnutls_find_apr_cert( GNUTLS_STATE state, gnutls_cert** apr_cert_list, int *apr_cert_list_length, gnutls_private_key** apr_pkey); int _gnutls_find_dn( gnutls_datum* odn, gnutls_cert* cert); -const gnutls_cert* _gnutls_server_find_x509_cert( struct GNUTLS_STATE_INT*); -int _gnutls_server_find_x509_cert_list_index( struct GNUTLS_STATE_INT*, gnutls_cert ** cert_list, int cert_list_length); +const gnutls_cert* _gnutls_server_find_x509_cert( struct GNUTLS_STATE_INT*, PKAlgorithm); +int _gnutls_server_find_x509_cert_list_index( struct GNUTLS_STATE_INT*, gnutls_cert ** cert_list, int cert_list_length, PKAlgorithm); #define _gnutls_proc_x509_client_certificate _gnutls_proc_x509_server_certificate diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 785d40d315..97dee40b7a 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -50,7 +50,6 @@ #define TRUE 1 #define FALSE 0 -static int _gnutls_server_select_suite(GNUTLS_STATE state, opaque *data, int datalen); int _gnutls_server_select_comp_method(GNUTLS_STATE state, opaque * data, int datalen); @@ -492,16 +491,42 @@ int _gnutls_recv_finished(GNUTLS_STATE state) return ret; } +/* returns PK_RSA if the given cipher suite list only supports, + * RSA algorithms, PK_DSA if DSS, and -1 if both or none. + */ +int _gnutls_find_pk_algos_in_ciphersuites( opaque* data, int datalen) { +int j; +PKAlgorithm algo=-1, prev_algo = 0; +KXAlgorithm kx; + + for (j = 0; j < datalen; j += 2) { + kx = _gnutls_cipher_suite_get_kx_algo(*((GNUTLS_CipherSuite *) & data[j])); + + if ( _gnutls_map_kx_get_cred( kx) == GNUTLS_X509PKI) { + algo = _gnutls_map_pk_get_pk( kx); + + if (algo!=prev_algo && prev_algo!=0) return -1; + prev_algo = algo; + } + } + + return algo; +} /* This selects the best supported ciphersuite from the ones supported. Then * it adds the suite into the state and performs some checks. */ -static int _gnutls_server_select_suite(GNUTLS_STATE state, opaque *data, int datalen) +int _gnutls_server_select_suite(GNUTLS_STATE state, opaque *data, int datalen) { int x, i, j; GNUTLS_CipherSuite *ciphers; int retval, err; + PKAlgorithm pk_algo; /* will hold the pk algorithms + * supported by the peer. + */ + + pk_algo = _gnutls_find_pk_algos_in_ciphersuites( data, datalen); x = _gnutls_supported_ciphersuites(state, &ciphers); @@ -509,7 +534,7 @@ static int _gnutls_server_select_suite(GNUTLS_STATE state, opaque *data, int dat * the certificate requested, or to the * authentication requested (eg SRP). */ - x = _gnutls_remove_unwanted_ciphersuites(state, &ciphers, x); + x = _gnutls_remove_unwanted_ciphersuites(state, &ciphers, x, pk_algo); #ifdef HANDSHAKE_DEBUG _gnutls_handshake_log("HSK: Requested cipher suites: \n"); @@ -1258,7 +1283,7 @@ static int _gnutls_read_server_hello(GNUTLS_STATE state, char *data, } /* This function copies the appropriate ciphersuites, to a localy allocated buffer - * Needed in hello messages. Returns the new data length. + * Needed in client hello messages. Returns the new data length. */ static int _gnutls_copy_ciphersuites(GNUTLS_STATE state, opaque ** ret_data) @@ -1280,7 +1305,7 @@ static int _gnutls_copy_ciphersuites(GNUTLS_STATE state, */ ret = _gnutls_remove_unwanted_ciphersuites(state, &cipher_suites, - ret); + ret, -1); if (ret < 0) { gnutls_assert(); return ret; @@ -2249,7 +2274,8 @@ int _gnutls_recv_hello_request(GNUTLS_STATE state, void *data, */ int _gnutls_remove_unwanted_ciphersuites(GNUTLS_STATE state, GNUTLS_CipherSuite ** - cipherSuites, int numCipherSuites) + cipherSuites, int numCipherSuites, + PKAlgorithm requested_pk_algo) { int ret = 0; @@ -2277,7 +2303,7 @@ int _gnutls_remove_unwanted_ciphersuites(GNUTLS_STATE state, cert = NULL; if (state->security_parameters.entity == GNUTLS_SERVER) - cert = _gnutls_server_find_x509_cert(state); + cert = _gnutls_server_find_x509_cert(state, requested_pk_algo); if (cert == NULL) { /* No certificate was found @@ -2304,6 +2330,8 @@ int _gnutls_remove_unwanted_ciphersuites(GNUTLS_STATE state, return GNUTLS_E_MEMORY_ERROR; } + /* now removes ciphersuites based on the KX algorithm + */ for (i = 0; i < numCipherSuites; i++) { /* finds the key exchange algorithm in * the ciphersuite diff --git a/lib/gnutls_handshake.h b/lib/gnutls_handshake.h index 4909a365ba..f337713c2d 100644 --- a/lib/gnutls_handshake.h +++ b/lib/gnutls_handshake.h @@ -34,8 +34,10 @@ int gnutls_handshake_server( GNUTLS_STATE state); void _gnutls_set_server_random( GNUTLS_STATE state, uint8* random); void _gnutls_set_client_random( GNUTLS_STATE state, uint8* random); int _gnutls_create_random( opaque* dst); -int _gnutls_remove_unwanted_ciphersuites( GNUTLS_STATE state, GNUTLS_CipherSuite ** cipherSuites, int numCipherSuites); +int _gnutls_remove_unwanted_ciphersuites( GNUTLS_STATE state, GNUTLS_CipherSuite ** cipherSuites, int numCipherSuites, PKAlgorithm); void gnutls_handshake_set_max_packet_length( GNUTLS_STATE state, int max); +int _gnutls_find_pk_algos_in_ciphersuites( opaque* data, int datalen); +int _gnutls_server_select_suite(GNUTLS_STATE state, opaque *data, int datalen); #define STATE state->gnutls_internals.handshake_state /* This returns true if we have got there diff --git a/lib/gnutls_v2_compat.c b/lib/gnutls_v2_compat.c index e5fbbb4356..4c77bc1789 100644 --- a/lib/gnutls_v2_compat.c +++ b/lib/gnutls_v2_compat.c @@ -35,62 +35,34 @@ #include "gnutls_random.h" #include "gnutls_auth_int.h" -int _gnutls_SelectCompMethod(GNUTLS_STATE state, CompressionMethod * ret, opaque * data, int datalen); - /* This selects the best supported ciphersuite from the ones provided */ -static int SelectSuite_v2(GNUTLS_STATE state, opaque ret[2], char *data, - int datalen) +static int _gnutls_handshake_select_v2_suite(GNUTLS_STATE state, char *data, int datalen) { - int x, i, j; - GNUTLS_CipherSuite *ciphers; - - x = _gnutls_supported_ciphersuites(state, &ciphers); - x = _gnutls_remove_unwanted_ciphersuites( state, &ciphers, x); - -#ifdef HANDSHAKE_DEBUG - _gnutls_handshake_log( "HSK: Requested cipher suites [v2 hello]: \n"); - for (j = 0; j < datalen; j += 3) { - if (data[j] == 0) { /* only print if in v2 compat mode */ - _gnutls_handshake_log( "\t%s\n", - _gnutls_cipher_suite_get_name(* - ((GNUTLS_CipherSuite *) & data[j+1]))); - } + int i, j, ret; + char* _data; + int _datalen; + + _data = gnutls_malloc( datalen); + if (_data==NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; } - _gnutls_handshake_log( "HSK: Supported cipher suites: \n"); - for (j = 0; j < x; j++) - _gnutls_handshake_log( "\t%s\n", - _gnutls_cipher_suite_get_name(ciphers[j])); -#endif - memset(ret, '\0', 2); + _gnutls_handshake_log( "HSK: Parsing a version 2.0 client hello.\n"); + + i = _datalen = 0; for (j = 0; j < datalen; j += 3) { - for (i = 0; i < x; i++) { - if (data[j] == 0) - if ( memcmp(ciphers[i].CipherSuite, &data[j+1], - 2) == 0) { - - _gnutls_handshake_log( - "HSK: Selected cipher suite: "); - _gnutls_handshake_log( "%s\n", - _gnutls_cipher_suite_get_name - (* - ((GNUTLS_CipherSuite *) & - data[j+1]))); - - memcpy(ret, - ciphers[i].CipherSuite, - 2); - gnutls_free(ciphers); - - return 0; - } + if (data[j] == 0) { + memcpy( &_data[i], &data[j+1], 2); + i+=2; + _datalen+=2; } } + ret = _gnutls_server_select_suite( state, _data, _datalen); + gnutls_free(_data); - gnutls_free(ciphers); - gnutls_assert(); - return GNUTLS_E_UNKNOWN_CIPHER_SUITE; + return ret; } @@ -118,7 +90,7 @@ int _gnutls_read_client_hello_v2(GNUTLS_STATE state, opaque * data, DECR_LEN(len, 2); - _gnutls_handshake_log( "HSK: V2 Hello: Client's version: %d.%d\n", data[pos], + _gnutls_handshake_log( "HSK: SSL 2.0 Hello: Client's version: %d.%d\n", data[pos], data[pos + 1]); set_adv_version( state, data[pos], data[pos+1]); @@ -170,9 +142,7 @@ int _gnutls_read_client_hello_v2(GNUTLS_STATE state, opaque * data, /* find an appropriate cipher suite */ DECR_LEN(len, sizeOfSuites); - ret = SelectSuite_v2(state, state->security_parameters. - current_cipher_suite.CipherSuite, - &data[pos], sizeOfSuites); + ret = _gnutls_handshake_select_v2_suite(state, &data[pos], sizeOfSuites); pos += sizeOfSuites; if (ret < 0) { @@ -198,7 +168,7 @@ int _gnutls_read_client_hello_v2(GNUTLS_STATE state, opaque * data, if (state->gnutls_internals.auth_struct == NULL) { _gnutls_handshake_log( - "HSK: V2 Hello: Cannot find the appropriate handler for the KX algorithm\n"); + "HSK: SSL 2.0 Hello: Cannot find the appropriate handler for the KX algorithm\n"); gnutls_assert(); return GNUTLS_E_UNKNOWN_CIPHER_TYPE;