From: Nikos Mavrogiannopoulos Date: Wed, 22 Jan 2003 17:42:12 +0000 (+0000) Subject: Improved the SRP support, to prevent attackers guessing the X-Git-Tag: gnutls_0_8_1~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=207eddade48a93f73eb01298b7d3667a589aa2a7;p=thirdparty%2Fgnutls.git Improved the SRP support, to prevent attackers guessing the available usernames by brute force. The g,n values sent are now obtained by the password conf file. (they were static ones) --- diff --git a/NEWS b/NEWS index ec09c70ff5..bba1851d9c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +Version 0.8.1 +- Improved the SRP support, to prevent attackers guessing the + available usernames by brute force. + Version 0.8.0 (20/01/2002) - Added gnutls_x509_extract_dn_string() which returns a distinguished name in a single string. diff --git a/lib/gnutls_dh.h b/lib/gnutls_dh.h index 5a455df188..39b0f43876 100644 --- a/lib/gnutls_dh.h +++ b/lib/gnutls_dh.h @@ -24,6 +24,5 @@ MPI gnutls_calc_dh_key( MPI f, MPI x, MPI prime ); int _gnutls_dh_generate_prime(MPI *ret_g, MPI* ret_n, int bits); void _gnutls_dh_clear_mpis(void); int _gnutls_dh_calc_mpis(void); -int _gnutls_get_rnd_srp_params(gnutls_datum *g, gnutls_datum* p, int bits); extern _gnutls_dh_params _gnutls_dh_default_params; diff --git a/lib/gnutls_dh_primes.c b/lib/gnutls_dh_primes.c index 641de737f0..7ef003324e 100644 --- a/lib/gnutls_dh_primes.c +++ b/lib/gnutls_dh_primes.c @@ -417,52 +417,6 @@ GNUTLS_MPI gnutls_get_dh_params(gnutls_dh_params dh_primes, return g; } -/* returns g and p, depends on the requested bits. - * We only support limited key sizes. - */ -int _gnutls_get_rnd_srp_params(gnutls_datum *g, gnutls_datum* p, int bits) -{ - int i; - - if (_gnutls_dh_default_params == NULL) { - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; - } - - g->data = p->data = NULL; - g->size = p->size = 0; - - bits = normalize_bits(bits); - - i = 0; - do { - if (_gnutls_dh_default_params[i].bits == bits) { - if (_gnutls_set_datum( p, _gnutls_dh_default_params[i].prime.data, - _gnutls_dh_default_params[i].prime.size) < 0) { - return GNUTLS_E_MEMORY_ERROR; - } - - if (_gnutls_set_datum( g, _gnutls_dh_default_params[i].generator.data, - _gnutls_dh_default_params[i].generator.size) < 0) { - _gnutls_free_datum( p); - return GNUTLS_E_MEMORY_ERROR; - } - - break; - } - i++; - } while (_gnutls_dh_default_params[i].bits != 0); - - if (g->data == NULL || p->data == NULL) { - gnutls_assert(); - _gnutls_free_datum(g); - _gnutls_free_datum(p); - return GNUTLS_E_INTERNAL_ERROR; - } - - return 0; -} - /* These should be added in gcrypt.h */ GNUTLS_MPI _gcry_generate_elg_prime(int mode, unsigned pbits, unsigned qbits, GNUTLS_MPI g, diff --git a/libextra/auth_srp.c b/libextra/auth_srp.c index d6bdf1491a..c613ea31d2 100644 --- a/libextra/auth_srp.c +++ b/libextra/auth_srp.c @@ -91,18 +91,11 @@ int _gnutls_gen_srp_server_kx(gnutls_session state, opaque ** data) _gnutls_str_cpy( username, MAX_SRP_USERNAME, state->security_parameters.extensions.srp_username); - pwd_entry = _gnutls_srp_pwd_read_entry( state, username, &err); - - if (pwd_entry == NULL) { - if (err==0) { - gnutls_assert(); - /* in order to avoid informing the peer that - * username does not exist. - */ - pwd_entry = _gnutls_randomize_pwd_entry(); - } else { - return GNUTLS_E_SRP_PWD_ERROR; - } + ret = _gnutls_srp_pwd_read_entry( state, username, &pwd_entry); + + if (ret < 0) { + gnutls_assert(); + return ret; } /* copy from pwd_entry to local variables (actually in state) */ diff --git a/libextra/auth_srp_passwd.c b/libextra/auth_srp_passwd.c index f5781a0692..22e78d452a 100644 --- a/libextra/auth_srp_passwd.c +++ b/libextra/auth_srp_passwd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001 Nikos Mavroyanopoulos + * Copyright (C) 2001,2003 Nikos Mavroyanopoulos * * This file is part of GNUTLS. * @@ -177,12 +177,13 @@ int ret; } -/* this function opens the tpasswd.conf file +/* this function opens the tpasswd.conf file and reads the g and n + * values. They are put in the entry. */ static int pwd_read_conf( const char* pconf_file, SRP_PWD_ENTRY* entry, int index) { FILE * fd; char line[2*1024]; - uint i; + uint i, len; char indexstr[10]; sprintf( indexstr, "%d", index); /* Flawfinder: ignore */ @@ -193,13 +194,14 @@ static int pwd_read_conf( const char* pconf_file, SRP_PWD_ENTRY* entry, int inde return GNUTLS_E_FILE_ERROR; } + len = strlen(indexstr); while( fgets( line, sizeof(line), fd) != NULL) { - /* move to first ':' */ + /* move to first ':' */ i=0; while( (line[i]!=':') && (line[i]!='\0') && (i < sizeof(line)) ) { i++; } - if (strncmp( indexstr, line, strlen(indexstr)) == 0) { + if (strncmp( indexstr, line, (i>len)?i:len) == 0) { if ((index = pwd_put_values2( entry, line)) >= 0) return 0; else { @@ -211,54 +213,70 @@ static int pwd_read_conf( const char* pconf_file, SRP_PWD_ENTRY* entry, int inde } - -SRP_PWD_ENTRY *_gnutls_srp_pwd_read_entry( gnutls_session state, char* username, int *err) { +int _gnutls_srp_pwd_read_entry( gnutls_session state, char* username, + SRP_PWD_ENTRY** _entry) +{ const gnutls_srp_server_credentials cred; FILE * fd; char line[2*1024]; - uint i, len; - SRP_PWD_ENTRY * entry; - int index, pwd_index = 0, ret; + uint i, len, ret; + int index, pwd_index = 0, last_index; + SRP_PWD_ENTRY* entry; - entry = gnutls_calloc(1, sizeof(SRP_PWD_ENTRY)); - if (entry==NULL) { + *_entry = gnutls_calloc(1, sizeof(SRP_PWD_ENTRY)); + if (*_entry==NULL) { gnutls_assert(); - *err = 1; - return NULL; + return GNUTLS_E_MEMORY_ERROR; } + entry = *_entry; - *err = 0; /* normal exit */ - cred = _gnutls_get_cred( state->key, GNUTLS_CRD_SRP, NULL); if (cred==NULL) { - *err = 1; gnutls_assert(); _gnutls_srp_entry_free(entry); - return NULL; + return GNUTLS_E_INSUFICIENT_CREDENTIALS; } /* if the callback which sends the parameters is - * set. + * set, use it. */ if (cred->pwd_callback != NULL) { ret = cred->pwd_callback( state, username, &entry->salt, &entry->v, &entry->g, &entry->n); + + if (ret==1) { /* the user does not exist */ + if (entry->g.size!=0 && entry->n.size!=0) { + ret = _randomize_pwd_entry( entry); + if (ret < 0) { + _gnutls_srp_entry_free(entry); + return ret; + } + return 0; + } else { + gnutls_assert(); + ret = -1; /* error in the callback */ + } + } if (ret < 0) { gnutls_assert(); _gnutls_srp_entry_free(entry); - return NULL; + return GNUTLS_E_SRP_PWD_ERROR; } - - return entry; + + return 0; } + + /* The callback was not set. Proceed. + */ if (cred->password_files<=0) { gnutls_assert(); - return NULL; + return GNUTLS_E_SRP_PWD_ERROR; } - /* use the callback to select a password file */ + /* use the callback to select a password file. If set. + */ if (state->internals.server_srp_callback!=NULL) { pwd_index = state->internals.server_srp_callback( state, (const char**)cred->password_file, @@ -267,95 +285,113 @@ SRP_PWD_ENTRY *_gnutls_srp_pwd_read_entry( gnutls_session state, char* username, if (pwd_index < 0) { gnutls_assert(); - return NULL; + return GNUTLS_E_SRP_PWD_ERROR; } } + /* Open the selected password file. + */ fd = fopen( cred->password_file[pwd_index], "r"); if (fd==NULL) { - *err = 1; /* failed due to critical error */ gnutls_assert(); _gnutls_srp_entry_free(entry); - return NULL; + return GNUTLS_E_SRP_PWD_ERROR; } + last_index = 1; /* a default value */ + + len = strlen(username); while( fgets( line, sizeof(line), fd) != NULL) { /* move to first ':' */ i=0; while( (line[i]!=':') && (line[i]!='\0') && (i < sizeof(line)) ) { i++; } - len = strlen(username); + if (strncmp( username, line, (i>len)?i:len) == 0) { - if ((index = pwd_put_values( entry, line)) >= 0) + if ((index = pwd_put_values( entry, line)) >= 0) { + /* Keep the last index in memory, so we can retrieve fake parameters (g,n) + * when the user does not exist. + */ + last_index = index; if (pwd_read_conf( cred->password_conf_file[pwd_index], entry, index)==0) { - return entry; + return 0; } else { + gnutls_assert(); _gnutls_srp_entry_free(entry); - return NULL; + return GNUTLS_E_SRP_PWD_ERROR; } - else { + } else { + gnutls_assert(); _gnutls_srp_entry_free(entry); - return NULL; + return GNUTLS_E_SRP_PWD_ERROR; } } } - return NULL; + + /* user was not found. Fake him. Actually read the g,n values from + * the last index found and randomize the entry. + */ +fprintf(stderr, "username: %s\nindex: %d\n", username, last_index); + if (pwd_read_conf( cred->password_conf_file[pwd_index], entry, last_index)==0) { + ret = _randomize_pwd_entry( entry); + if (ret < 0) { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return ret; + } + return 0; + } + + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_SRP_PWD_ERROR; + } + +/* Randomizes the given password entry. It actually sets the verifier + * and the salt. Returns 0 on success. + */ #define RNDUSER "rnd" #define RND_SALT_SIZE 17 -SRP_PWD_ENTRY* _gnutls_randomize_pwd_entry() { +int _randomize_pwd_entry(SRP_PWD_ENTRY* entry) { int ret; - SRP_PWD_ENTRY * pwd_entry = gnutls_calloc(1, sizeof(SRP_PWD_ENTRY)); - if (pwd_entry == NULL) { - gnutls_assert(); - return NULL; - } - - ret = _gnutls_get_rnd_srp_params( &pwd_entry->g, &pwd_entry->n, 1024); - if (ret < 0) { + if (entry->g.size == 0 || entry->n.size == 0) { gnutls_assert(); - _gnutls_srp_entry_free( pwd_entry); - return NULL; + return GNUTLS_E_INTERNAL_ERROR; } - - pwd_entry->username = gnutls_malloc(strlen(RNDUSER)+1); - if (pwd_entry->username == NULL) { + + entry->username = gnutls_strdup(RNDUSER); + if (entry->username == NULL) { gnutls_assert(); - _gnutls_srp_entry_free( pwd_entry); - return NULL; + return GNUTLS_E_MEMORY_ERROR; } - _gnutls_str_cpy( pwd_entry->username, MAX_SRP_USERNAME, RNDUSER); /* Flawfinder: ignore */ - pwd_entry->v.data = gnutls_malloc(20); - pwd_entry->v.size = 20; - if (pwd_entry->v.data==NULL) { + entry->v.data = gnutls_malloc(20); + entry->v.size = 20; + if (entry->v.data==NULL) { gnutls_assert(); - _gnutls_srp_entry_free( pwd_entry); - return NULL; + return GNUTLS_E_MEMORY_ERROR; } - _gnutls_get_random( pwd_entry->v.data, 20, GNUTLS_WEAK_RANDOM); + _gnutls_get_random( entry->v.data, 20, GNUTLS_WEAK_RANDOM); - pwd_entry->salt.size = RND_SALT_SIZE; + entry->salt.size = RND_SALT_SIZE; - pwd_entry->salt.data = gnutls_malloc(RND_SALT_SIZE); - if (pwd_entry->salt.data==NULL) { + entry->salt.data = gnutls_malloc(RND_SALT_SIZE); + if (entry->salt.data==NULL) { gnutls_assert(); - _gnutls_srp_entry_free( pwd_entry); - return NULL; + return GNUTLS_E_MEMORY_ERROR; } - if (_gnutls_get_random(pwd_entry->salt.data, RND_SALT_SIZE, GNUTLS_WEAK_RANDOM) < 0) { + if (_gnutls_get_random(entry->salt.data, RND_SALT_SIZE, GNUTLS_WEAK_RANDOM) < 0) { gnutls_assert(); - _gnutls_srp_entry_free( pwd_entry); - return NULL; + return GNUTLS_E_MEMORY_ERROR; } - return pwd_entry; - + return 0; } void _gnutls_srp_entry_free( SRP_PWD_ENTRY * entry) { diff --git a/libextra/auth_srp_passwd.h b/libextra/auth_srp_passwd.h index 14b17c36f9..93c2692c3e 100644 --- a/libextra/auth_srp_passwd.h +++ b/libextra/auth_srp_passwd.h @@ -10,9 +10,8 @@ typedef struct { } SRP_PWD_ENTRY; /* this is localy alocated. It should be freed using the provided function */ -SRP_PWD_ENTRY *_gnutls_srp_pwd_read_entry( gnutls_session state, char* username, int* err); +int _gnutls_srp_pwd_read_entry( gnutls_session state, char* username, SRP_PWD_ENTRY**); void _gnutls_srp_entry_free( SRP_PWD_ENTRY * entry); -SRP_PWD_ENTRY* _gnutls_randomize_pwd_entry(void); int _gnutls_sbase64_encode(uint8 * data, size_t data_size, uint8 ** result); int _gnutls_sbase64_decode(uint8 * data, size_t data_size, uint8 ** result); diff --git a/libextra/gnutls_srp.c b/libextra/gnutls_srp.c index 5e84bb29db..59c955a924 100644 --- a/libextra/gnutls_srp.c +++ b/libextra/gnutls_srp.c @@ -534,14 +534,18 @@ void gnutls_srp_server_set_select_function(gnutls_session session, * gnutls_datum* salt, gnutls_datum *verifier, gnutls_datum* g, * gnutls_datum* n); * - * 'username' contains the actual username. - * + * 'username' contains the actual username. * The 'salt', 'verifier', 'generator' and 'prime' must be filled * in using the gnutls_malloc(). * * In case the callback returned a negative number then gnutls will * assume that the username does not exist. * + * In order to prevent allowing an attack to guess valid usernames, + * if username does not exist, the g, and n values should be filled in + * using a random user's parameters. In that case the callback should + * return the special value (1). + * * The callback function will only be called once per handshake. * The callback function should return 0 on success. * -1 indicates an error. diff --git a/src/cli.c b/src/cli.c index 98a9fd6f12..f6f5f00997 100644 --- a/src/cli.c +++ b/src/cli.c @@ -58,7 +58,7 @@ int crlf; int quiet = 0; extern int xml; -char *srp_passwd; +char *srp_passwd = NULL; char *srp_username; char *pgp_keyfile; char *pgp_certfile; diff --git a/src/tests.c b/src/tests.c index 7d16393126..8b3e590a28 100644 --- a/src/tests.c +++ b/src/tests.c @@ -32,6 +32,7 @@ extern gnutls_anon_client_credentials anon_cred; extern gnutls_certificate_credentials xcred; extern int more_info; +static int srp = 0; extern int tls1_ok; extern int ssl3_ok; @@ -62,6 +63,11 @@ int ret, alert; GERR(ret); } + if (srp) { + if (ret == GNUTLS_E_DECRYPTION_FAILED) + return SUCCEED; /* SRP was detected */ + } + if (ret < 0) return FAILED; gnutls_session_get_data(session, NULL, &session_data_size); @@ -152,6 +158,8 @@ static void ADD_PROTOCOL(gnutls_session session, int protocol) { int test_srp( gnutls_session session) { +int ret; + ADD_ALL_CIPHERS(session); ADD_ALL_COMP(session); ADD_ALL_CERTTYPES(session); @@ -159,10 +167,14 @@ int test_srp( gnutls_session session) { ADD_ALL_MACS(session); ADD_KX(session, GNUTLS_KX_SRP); + srp = 1; gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred); - return do_handshake( session); + ret = do_handshake( session); + srp = 0; + + return ret; } int test_export( gnutls_session session) {