diff options
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | lib/gnutls_dh.h | 1 | ||||
-rw-r--r-- | lib/gnutls_dh_primes.c | 46 | ||||
-rw-r--r-- | libextra/auth_srp.c | 17 | ||||
-rw-r--r-- | libextra/auth_srp_passwd.c | 170 | ||||
-rw-r--r-- | libextra/auth_srp_passwd.h | 3 | ||||
-rw-r--r-- | libextra/gnutls_srp.c | 8 | ||||
-rw-r--r-- | src/cli.c | 2 | ||||
-rw-r--r-- | src/tests.c | 14 |
9 files changed, 133 insertions, 132 deletions
@@ -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. @@ -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) { |