]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Improved the SRP support, to prevent attackers guessing the
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 22 Jan 2003 17:42:12 +0000 (17:42 +0000)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 22 Jan 2003 17:42:12 +0000 (17:42 +0000)
available usernames by brute force. The g,n values sent are now
obtained by the password conf file. (they were static ones)

NEWS
lib/gnutls_dh.h
lib/gnutls_dh_primes.c
libextra/auth_srp.c
libextra/auth_srp_passwd.c
libextra/auth_srp_passwd.h
libextra/gnutls_srp.c
src/cli.c
src/tests.c

diff --git a/NEWS b/NEWS
index ec09c70ff56c13fd9d769693cb70dafafceb81eb..bba1851d9ce22963efac05522c266e38c4749037 100644 (file)
--- 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.
index 5a455df18820a38a5d3ac400c9e38285a65fc92a..39b0f438765d1425f40d019d6a7efcf3a769a7f4 100644 (file)
@@ -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;
index 641de737f0173b736b3f28f1851f93ac9cdd966a..7ef003324ee73502a1a20af70910bb42bc4a03f0 100644 (file)
@@ -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,
index d6bdf1491a67553e8202e2faa42cf1377cc93f85..c613ea31d237ea72b776710b0811a6b5d98f077a 100644 (file)
@@ -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) */
index f5781a0692f7be7bb68ae9673c7306d7d4406586..22e78d452aa1ff844f68d2a44de90e876da0c8a5 100644 (file)
@@ -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) {
index 14b17c36f99d244e1a4e3b7fd6ca5bac7fdce8d4..93c2692c3e34b83cbde5251260f1b0b2a3ed68e5 100644 (file)
@@ -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);
 
index 5e84bb29dbc55333e6938b5f4f7e6143797dc9f1..59c955a9249907b531ae298fa3fd4cf4cb5e913e 100644 (file)
@@ -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.
index 98a9fd6f12be6da6ca82c61cce9da3f2437cd53c..f6f5f00997fa0684ce3a5b6965ec4e8641a141cc 100644 (file)
--- 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;
index 7d1639312653c5d002fe22fc953924c941594d48..8b3e590a288de5489e11df8826ddf78d62a164d3 100644 (file)
@@ -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) {