diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2003-12-02 18:20:44 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2003-12-02 18:20:44 +0000 |
commit | 80ec4bd7800f0ba17a59c84e4c0c729b26c3fcd8 (patch) | |
tree | e4d828f40ef9b2a257b39be1e9d034424e4397bc | |
parent | 2542696aed50d8a3f8821e1f5913c615e62d240f (diff) | |
download | gnutls-80ec4bd7800f0ba17a59c84e4c0c729b26c3fcd8.tar.gz |
Improved the support for draft-ietf-tls-srp-05. The two-phase
handshake is now fully supported without any interaction with
the application layer (except for a callback).
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | doc/TODO | 2 | ||||
-rw-r--r-- | doc/tex/srp.tex | 17 | ||||
-rw-r--r-- | includes/gnutls/extra.h | 13 | ||||
-rw-r--r-- | lib/gnutls.h.in.in | 1 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 2 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 51 | ||||
-rw-r--r-- | lib/gnutls_int.h | 8 | ||||
-rw-r--r-- | lib/gnutls_state.c | 5 | ||||
-rw-r--r-- | libextra/auth_srp.c | 100 | ||||
-rw-r--r-- | libextra/auth_srp.h | 12 | ||||
-rw-r--r-- | libextra/ext_srp.c | 73 | ||||
-rw-r--r-- | libextra/ext_srp.h | 3 | ||||
-rw-r--r-- | libextra/gnutls_srp.c | 40 | ||||
-rw-r--r-- | src/cli.c | 27 |
15 files changed, 253 insertions, 104 deletions
@@ -1,6 +1,9 @@ Version 1.0.0 - Exported the static SRP group parameters. - Some fixes in the certificate authenticated SRP ciphersuites. +- Improved the support for draft-ietf-tls-srp-05. The two-phase + handshake is now fully supported without any interaction with + the application layer (except for a callback). Version 0.9.99 (28/11/2003) - Some fixes in the gnutls.h header for the gnutls_server_name_set() @@ -12,6 +12,8 @@ Current list: * Use subkeys with the 0x20 flag in openpgp keys (if present), instead of the main key. * Add support for generating and handling DSA keys +* Add support for extracting CRL distribution points. +* Add support for generating CRLs. * Convert documentation to texinfo format * Audit the code * Allow sending V2 Hello messages. It seems that some (old) broken diff --git a/doc/tex/srp.tex b/doc/tex/srp.tex index 6d806af19f..f1e15a85ee 100644 --- a/doc/tex/srp.tex +++ b/doc/tex/srp.tex @@ -11,7 +11,7 @@ authentication schemas, is that SRP does not require the server to hold the user's password. This kind of protection is similar to the one used traditionally in the \emph{UNIX} ``passwd'' file, where the contents of this file did not cause harm to the system security if they were revealed. -The SRP holds instead of the plain password something called a verifier, +The SRP needs instead of the plain password something called a verifier, which is calculated using the user's password, and if stolen cannot be used to impersonate the user. See \cite{TOMSRP} for a detailed description of the SRP protocol, and for the Stanford SRP libraries. @@ -45,11 +45,16 @@ authenticated using a certificate with RSA parameters. If clients supporting SRP know the username and password before the connection, should initialize the client credentials and call the function \printfunc{gnutls_srp_set_client_credentials}{gnutls\_srp\_set\_client\_credentials}. -Alternatively they could probe the server for SRP support, by enabling -the SRP key exchange method, and specifying empty credentials. If the server -supports SRP an alert of type GNUTLS\_A\_MISSING\_SRP\_USERNAME will be -received, which allows the client to read the username and password from the -user, set the credentials and repeat the handshake procedure. +Alternatively they could specify a callback function by using the +function \printfunc{gnutls_srp_set_client_credentials_function}{gnutls\_srp\_set\_client\_credentials\_function}. +This has the advantage that allows probing the server for SRP support. +In that case the callback function will be called twice per handshake. +The first time is before the ciphersuite is negotiated, and +if the callback returns a negative error code, the callback will be +called again if SRP has been negotiated. +This uses a special TLS-SRP handshake idiom in order to avoid, in +interactive applications, to ask the user for SRP password and username +if the server does not negotiate an SRP ciphersuite. \par In server side the default behaviour of \gnutls{} is to read the usernames and SRP verifiers from password files. These password files are the ones used diff --git a/includes/gnutls/extra.h b/includes/gnutls/extra.h index 47f5331870..330a29fe6f 100644 --- a/includes/gnutls/extra.h +++ b/includes/gnutls/extra.h @@ -38,15 +38,16 @@ int gnutls_srp_set_client_credentials( gnutls_srp_client_credentials res, char * void gnutls_srp_free_server_credentials( gnutls_srp_server_credentials sc); int gnutls_srp_allocate_server_credentials( gnutls_srp_server_credentials *sc); -int gnutls_srp_set_server_credentials_file( gnutls_srp_server_credentials res, char *password_file, char* password_conf_file); +int gnutls_srp_set_server_credentials_file( gnutls_srp_server_credentials res, + const char *password_file, const char* password_conf_file); const char* gnutls_srp_server_get_username( gnutls_session state); typedef int gnutls_srp_server_select_function(gnutls_session, const char **, const char**, unsigned int); - void gnutls_srp_server_set_select_function( gnutls_session, gnutls_srp_server_select_function *); -int gnutls_srp_verifier( char* username, char* password, const gnutls_datum *salt, + +int gnutls_srp_verifier( const char* username, const char* password, const gnutls_datum *salt, const gnutls_datum* g, const gnutls_datum* n, gnutls_datum * res); @@ -68,11 +69,15 @@ typedef int gnutls_srp_server_credentials_function( gnutls_datum* verifier, gnutls_datum* generator, gnutls_datum* prime ); - void gnutls_srp_set_server_credentials_function( gnutls_srp_server_credentials, gnutls_srp_server_credentials_function *); +typedef int gnutls_srp_client_credentials_function(gnutls_session, unsigned int, + char **, char**); +void gnutls_srp_set_client_credentials_function( gnutls_srp_client_credentials, + gnutls_srp_client_credentials_function *); + /* Openpgp certificate stuff */ diff --git a/lib/gnutls.h.in.in b/lib/gnutls.h.in.in index b2f768f397..d57263d18b 100644 --- a/lib/gnutls.h.in.in +++ b/lib/gnutls.h.in.in @@ -409,6 +409,7 @@ void gnutls_global_set_mem_functions( extern gnutls_alloc_function gnutls_malloc; extern gnutls_calloc_function gnutls_calloc; extern gnutls_free_function gnutls_free; +extern char* (*gnutls_strdup)( const char*); typedef void (*gnutls_log_func)( int, const char*); void gnutls_global_set_log_function( gnutls_log_func log_func); diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index c795ca2e60..0ea3581416 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -137,7 +137,7 @@ static gnutls_error_entry error_algorithms[] = { ERROR_ENTRY("No supported cipher suites have been found.", GNUTLS_E_NO_CIPHER_SUITES, 1 ), ERROR_ENTRY("Could not get OpenPGP key.", GNUTLS_E_OPENPGP_GETKEY_FAILED, 1), - ERROR_ENTRY("The SRP username supplied by the peer is illegal.", GNUTLS_E_ILLEGAL_SRP_USERNAME, 1), + ERROR_ENTRY("The SRP username supplied is illegal.", GNUTLS_E_ILLEGAL_SRP_USERNAME, 1), ERROR_ENTRY("The OpenPGP fingerprint is not supported.", GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED, 1), ERROR_ENTRY("The certificate has unsupported attributes.", GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE, 1), diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 65f39ff1a5..f37150d751 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -45,6 +45,7 @@ #include <gnutls_record.h> #include <gnutls_alert.h> #include <gnutls_state.h> +#include <ext_srp.h> #ifdef HANDSHAKE_DEBUG #define ERR(x, y) _gnutls_handshake_log( "HSK[%x]: %s (%d)\n", session, x,y) @@ -969,6 +970,18 @@ int _gnutls_recv_handshake(gnutls_session session, uint8 ** data, ret = _gnutls_recv_handshake_header(session, type, &recv_type); if (ret < 0) { + + /* In SRP when expecting the server hello we may receive + * an alert instead. Do as the draft demands. + */ + if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED && + gnutls_alert_get( session) == GNUTLS_A_MISSING_SRP_USERNAME && + type == GNUTLS_SERVER_HELLO) + { + gnutls_assert(); + return GNUTLS_E_INT_HANDSHAKE_AGAIN; + } + if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET && optional == OPTIONAL_PACKET) { if (datalen != NULL) @@ -977,7 +990,7 @@ int _gnutls_recv_handshake(gnutls_session session, uint8 ** data, *data = NULL; return 0; /* ok just ignore the packet */ } - /* gnutls_assert(); */ + return ret; } @@ -1611,6 +1624,27 @@ static int _gnutls_send_server_hello(gnutls_session session, int again) datalen = 0; + if (IS_SRP_KX( + _gnutls_cipher_suite_get_kx_algo( + session->security_parameters.current_cipher_suite))) + { + if (session->security_parameters.extensions.srp_username[0] == 0) { + /* The peer didn't send a valid SRP extension with the + * SRP username. The draft requires that we send an + * alert and start the handshake again. + */ + gnutls_assert(); + ret = gnutls_alert_send( session, GNUTLS_AL_WARNING, + GNUTLS_A_MISSING_SRP_USERNAME); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return GNUTLS_E_INT_HANDSHAKE_AGAIN; + } + } + if (again == 0) { datalen = 2 + session_id_len + 1 + TLS_RANDOM_SIZE + 3; extdatalen = _gnutls_gen_extensions(session, &extdata); @@ -1882,11 +1916,20 @@ int gnutls_handshake(gnutls_session session) return 0; } +/* Here if GNUTLS_E_INT_HANDSHAKE_AGAIN is received we go to + * restart. This works because this error code may only be + * received on the first 2 handshake packets. If for some reason + * this changes we should return GNUTLS_E_AGAIN. + */ #define IMED_RET( str, ret) do { \ if (ret < 0) { \ + if (ret == GNUTLS_E_INT_HANDSHAKE_AGAIN && \ + session->internals.handshake_restarted == 1) \ + ret = GNUTLS_E_INTERNAL_ERROR; \ if (ret == GNUTLS_E_INT_HANDSHAKE_AGAIN) { \ STATE = STATE0; \ - return GNUTLS_E_AGAIN; \ + session->internals.handshake_restarted = 1; \ + goto restart; \ } \ if (gnutls_error_is_fatal(ret)==0) return ret; \ gnutls_assert(); \ @@ -1918,6 +1961,7 @@ int _gnutls_handshake_client(gnutls_session session) resumed_security_parameters. session_id_size, buf, sizeof(buf))); #endif + restart: switch (STATE) { case STATE0: @@ -2124,6 +2168,8 @@ static int _gnutls_recv_handshake_final(gnutls_session session, int init) int _gnutls_handshake_server(gnutls_session session) { int ret = 0; + + restart: switch (STATE) { case STATE0: @@ -2217,6 +2263,7 @@ int _gnutls_handshake_common(gnutls_session session) { int ret = 0; + restart: /* send and recv the change cipher spec and finished messages */ if ((session->internals.resumed == RESUME_TRUE diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 1022e31c36..1b9e21dcfc 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -623,6 +623,14 @@ typedef struct { */ opaque rsa_pms_version[2]; + char* srp_username; + char* srp_password; + + /* This is only set in SRP, when the handshake is + * restarted if an username is not found. + */ + int handshake_restarted; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } GNUTLS_INTERNALS; diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index 5b0f8e18ab..a56a668077 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -141,6 +141,8 @@ void _gnutls_handshake_internal_state_clear( gnutls_session session) { session->internals.last_handshake_in = -1; session->internals.last_handshake_out = -1; + session->internals.handshake_restarted = 0; + session->internals.resumable = RESUME_TRUE; } @@ -312,6 +314,9 @@ void _gnutls_deinit(gnutls_session session) session->key = NULL; } + gnutls_free( session->internals.srp_username); + gnutls_free( session->internals.srp_password); + memset( session, 0, sizeof(struct gnutls_session_int)); gnutls_free(session); } diff --git a/libextra/auth_srp.c b/libextra/auth_srp.c index f887392bda..d0e55d668a 100644 --- a/libextra/auth_srp.c +++ b/libextra/auth_srp.c @@ -58,19 +58,19 @@ const MOD_AUTH_STRUCT srp_auth_struct = { }; -#define _b state->key->b -#define B state->key->B -#define _a state->key->a -#define A state->key->A -#define N state->key->client_p -#define G state->key->client_g -#define V state->key->x -#define S state->key->KEY +#define _b session->key->b +#define B session->key->B +#define _a session->key->a +#define A session->key->A +#define N session->key->client_p +#define G session->key->client_g +#define V session->key->x +#define S session->key->KEY /* Send the first key exchange message ( g, n, s) and append the verifier algorithm number * Data is allocated by the caller, and should have data_size size. */ -int _gnutls_gen_srp_server_kx(gnutls_session state, opaque ** data) +int _gnutls_gen_srp_server_kx(gnutls_session session, opaque ** data) { int ret; uint8 *data_n, *data_s; @@ -83,40 +83,24 @@ int _gnutls_gen_srp_server_kx(gnutls_session state, opaque ** data) char buf[64]; uint8 *data_b; - if (state->security_parameters.extensions.srp_username[0] == 0) { - /* The peer didn't send a valid SRP extension with the - * SRP username. The draft requires that we send an - * alert and start the handshake again. - */ - gnutls_assert(); - ret = gnutls_alert_send( state, GNUTLS_AL_WARNING, - GNUTLS_A_MISSING_SRP_USERNAME); - if (ret < 0) { - gnutls_assert(); - return ret; - } - - return GNUTLS_E_INT_HANDSHAKE_AGAIN; - } - - if ( (ret=_gnutls_auth_info_set( state, GNUTLS_CRD_SRP, sizeof( SRP_SERVER_AUTH_INFO_INT), 1)) < 0) { + if ( (ret=_gnutls_auth_info_set( session, GNUTLS_CRD_SRP, sizeof( SRP_SERVER_AUTH_INFO_INT), 1)) < 0) { gnutls_assert(); return ret; } - info = _gnutls_get_auth_info( state); + info = _gnutls_get_auth_info( session); username = info->username; - _gnutls_str_cpy( username, MAX_SRP_USERNAME, state->security_parameters.extensions.srp_username); + _gnutls_str_cpy( username, MAX_SRP_USERNAME, session->security_parameters.extensions.srp_username); - ret = _gnutls_srp_pwd_read_entry( state, username, &pwd_entry); + ret = _gnutls_srp_pwd_read_entry( session, username, &pwd_entry); if (ret < 0) { gnutls_assert(); return ret; } - /* copy from pwd_entry to local variables (actually in state) */ + /* copy from pwd_entry to local variables (actually in session) */ if (_gnutls_mpi_scan( &G, pwd_entry->g.data, &pwd_entry->g.size) < 0) { gnutls_assert(); return GNUTLS_E_MPI_SCAN_FAILED; @@ -194,7 +178,7 @@ int _gnutls_gen_srp_server_kx(gnutls_session state, opaque ** data) } /* return A = g^a % N */ -int _gnutls_gen_srp_client_kx(gnutls_session state, opaque ** data) +int _gnutls_gen_srp_client_kx(gnutls_session session, opaque ** data) { size_t n_a; int ret; @@ -203,15 +187,20 @@ int _gnutls_gen_srp_client_kx(gnutls_session state, opaque ** data) char buf[64]; char *password; const gnutls_srp_client_credentials cred = - _gnutls_get_cred(state->key, GNUTLS_CRD_SRP, NULL); + _gnutls_get_cred(session->key, GNUTLS_CRD_SRP, NULL); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - username = cred->username; - password = cred->password; + if (session->internals.srp_username == NULL) { + username = cred->username; + password = cred->password; + } else { + username = session->internals.srp_username; + password = session->internals.srp_password; + } if (username == NULL || password == NULL) { gnutls_assert(); @@ -235,16 +224,16 @@ int _gnutls_gen_srp_client_kx(gnutls_session state, opaque ** data) */ /* calculate u */ - state->key->u = _gnutls_calc_srp_u(A, B); - if ( state->key->u == NULL) { + session->key->u = _gnutls_calc_srp_u(A, B); + if ( session->key->u == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } - _gnutls_dump_mpi( "SRP U: ", state->key->u); + _gnutls_dump_mpi( "SRP U: ", session->key->u); /* S = (B - g^x) ^ (a + u * x) % N */ - S = _gnutls_calc_srp_S2( B, G, state->key->x, _a, state->key->u, N); + S = _gnutls_calc_srp_S2( B, G, session->key->x, _a, session->key->u, N); if (S==NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; @@ -254,10 +243,10 @@ int _gnutls_gen_srp_client_kx(gnutls_session state, opaque ** data) _gnutls_mpi_release(&_b); _gnutls_mpi_release(&V); - _gnutls_mpi_release(&state->key->u); + _gnutls_mpi_release(&session->key->u); _gnutls_mpi_release(&B); - ret = _gnutls_generate_session_key( state->key); + ret = _gnutls_generate_session_key( session->key); _gnutls_mpi_release(&S); if (ret < 0) @@ -290,8 +279,8 @@ int _gnutls_gen_srp_client_kx(gnutls_session state, opaque ** data) } -/* just read A and put it to state */ -int _gnutls_proc_srp_client_kx(gnutls_session state, opaque * data, size_t _data_size) +/* just read A and put it to session */ +int _gnutls_proc_srp_client_kx(gnutls_session session, opaque * data, size_t _data_size) { size_t _n_A; ssize_t data_size = _data_size; @@ -312,17 +301,17 @@ int _gnutls_proc_srp_client_kx(gnutls_session state, opaque * data, size_t _data /* Start the SRP calculations. * - Calculate u */ - state->key->u = _gnutls_calc_srp_u(A, B); - if (state->key->u==NULL) { + session->key->u = _gnutls_calc_srp_u(A, B); + if (session->key->u==NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } - _gnutls_dump_mpi( "SRP U: ", state->key->u); + _gnutls_dump_mpi( "SRP U: ", session->key->u); /* S = (A * v^u) ^ b % N */ - S = _gnutls_calc_srp_S1( A, _b, state->key->u, V, N); + S = _gnutls_calc_srp_S1( A, _b, session->key->u, V, N); if ( S==NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; @@ -333,10 +322,10 @@ int _gnutls_proc_srp_client_kx(gnutls_session state, opaque * data, size_t _data _gnutls_mpi_release(&A); _gnutls_mpi_release(&_b); _gnutls_mpi_release(&V); - _gnutls_mpi_release(&state->key->u); + _gnutls_mpi_release(&session->key->u); _gnutls_mpi_release(&B); - ret = _gnutls_generate_session_key( state->key); + ret = _gnutls_generate_session_key( session->key); _gnutls_mpi_release( &S); if (ret < 0) { @@ -520,7 +509,7 @@ static int group_check_g_n( GNUTLS_MPI g, GNUTLS_MPI n) /* receive the key exchange message ( n, g, s, B) */ -int _gnutls_proc_srp_server_kx(gnutls_session state, opaque * data, size_t _data_size) +int _gnutls_proc_srp_server_kx(gnutls_session session, opaque * data, size_t _data_size) { uint8 n_s; uint16 n_g, n_n, n_b; @@ -535,15 +524,20 @@ int _gnutls_proc_srp_server_kx(gnutls_session state, opaque * data, size_t _data ssize_t data_size = _data_size; const gnutls_srp_client_credentials cred = - _gnutls_get_cred(state->key, GNUTLS_CRD_SRP, NULL); + _gnutls_get_cred(session->key, GNUTLS_CRD_SRP, NULL); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - username = cred->username; - password = cred->password; + if (session->internals.srp_username == NULL) { + username = cred->username; + password = cred->password; + } else { + username = session->internals.srp_username; + password = session->internals.srp_password; + } if (username == NULL || password == NULL) { gnutls_assert(); @@ -641,7 +635,7 @@ int _gnutls_proc_srp_server_kx(gnutls_session state, opaque * data, size_t _data return ret; } - if (_gnutls_mpi_scan(&state->key->x, hd, &_n_g) != 0) { + if (_gnutls_mpi_scan(&session->key->x, hd, &_n_g) != 0) { gnutls_assert(); return GNUTLS_E_MPI_SCAN_FAILED; } diff --git a/libextra/auth_srp.h b/libextra/auth_srp.h index 7b42f0229f..bf4cd28c09 100644 --- a/libextra/auth_srp.h +++ b/libextra/auth_srp.h @@ -5,17 +5,17 @@ typedef int gnutls_srp_server_credentials_function(gnutls_session, - char *username, - gnutls_datum * salt, - gnutls_datum * verifier, - gnutls_datum * - generator, - gnutls_datum * prime); + const char *username, gnutls_datum * salt, gnutls_datum * verifier, + gnutls_datum * generator, gnutls_datum * prime); + +typedef int gnutls_srp_client_credentials_function(gnutls_session, + unsigned int times, char **username, char** password); typedef struct { char *username; char *password; + gnutls_srp_client_credentials_function *get_function; } SRP_CLIENT_CREDENTIALS_INT; #define gnutls_srp_client_credentials SRP_CLIENT_CREDENTIALS_INT* diff --git a/libextra/ext_srp.c b/libextra/ext_srp.c index c886d97d25..a9460a1e7d 100644 --- a/libextra/ext_srp.c +++ b/libextra/ext_srp.c @@ -27,30 +27,31 @@ #include "auth_srp.h" #include "gnutls_errors.h" #include "gnutls_algorithms.h" +#include <gnutls_num.h> -int _gnutls_srp_recv_params( gnutls_session state, const opaque* data, size_t _data_size) { +int _gnutls_srp_recv_params( gnutls_session session, const opaque* data, size_t _data_size) { uint8 len; ssize_t data_size = _data_size; - if (_gnutls_kx_priority( state, GNUTLS_KX_SRP) < 0 && - _gnutls_kx_priority( state, GNUTLS_KX_SRP_DSS) < 0 && - _gnutls_kx_priority( state, GNUTLS_KX_SRP_RSA) < 0) { + if (_gnutls_kx_priority( session, GNUTLS_KX_SRP) < 0 && + _gnutls_kx_priority( session, GNUTLS_KX_SRP_DSS) < 0 && + _gnutls_kx_priority( session, GNUTLS_KX_SRP_RSA) < 0) { /* algorithm was not allowed in this session */ return 0; } - if (state->security_parameters.entity == GNUTLS_SERVER) { + if (session->security_parameters.entity == GNUTLS_SERVER) { if (data_size > 0) { len = data[0]; DECR_LEN( data_size, len); - if ( sizeof( state->security_parameters.extensions.srp_username) <= len) { + if ( sizeof( session->security_parameters.extensions.srp_username) <= len) { gnutls_assert(); return GNUTLS_E_ILLEGAL_SRP_USERNAME; } - memcpy( state->security_parameters.extensions.srp_username, &data[1], len); - state->security_parameters.extensions.srp_username[len]=0; /* null terminated */ + memcpy( session->security_parameters.extensions.srp_username, &data[1], len); + session->security_parameters.extensions.srp_username[len]=0; /* null terminated */ } } return 0; @@ -61,39 +62,34 @@ int _gnutls_srp_recv_params( gnutls_session state, const opaque* data, size_t _d inline static int is_srp( GNUTLS_CipherSuite suite) { int kx = _gnutls_cipher_suite_get_kx_algo( suite); - if (kx == GNUTLS_KX_SRP || (kx == GNUTLS_KX_SRP_RSA) || - kx == GNUTLS_KX_SRP_DSS) { - return 1; - } - + if (IS_SRP_KX(kx)) return 1; return 0; } /* returns data_size or a negative number on failure * data is allocated locally */ -int _gnutls_srp_send_params( gnutls_session state, opaque* data, size_t data_size) { +int _gnutls_srp_send_params( gnutls_session session, opaque* data, size_t data_size) { uint len; - if (_gnutls_kx_priority( state, GNUTLS_KX_SRP) < 0 && - _gnutls_kx_priority( state, GNUTLS_KX_SRP_DSS) < 0 && - _gnutls_kx_priority( state, GNUTLS_KX_SRP_RSA) < 0) { + if (_gnutls_kx_priority( session, GNUTLS_KX_SRP) < 0 && + _gnutls_kx_priority( session, GNUTLS_KX_SRP_DSS) < 0 && + _gnutls_kx_priority( session, GNUTLS_KX_SRP_RSA) < 0) { /* algorithm was not allowed in this session */ return 0; } /* this function sends the client extension data (username) */ - if (state->security_parameters.entity == GNUTLS_CLIENT) { - const gnutls_srp_client_credentials cred = _gnutls_get_cred( state->key, GNUTLS_CRD_SRP, NULL); + if (session->security_parameters.entity == GNUTLS_CLIENT) { + const gnutls_srp_client_credentials cred = + _gnutls_get_cred( session->key, GNUTLS_CRD_SRP, NULL); if (cred==NULL) return 0; if (cred->username!=NULL) { /* send username */ - len = strlen(cred->username); + len = GMIN( strlen(cred->username), 255); - if (len > 255) len = 255; - if (data_size < len+1) { gnutls_assert(); return GNUTLS_E_SHORT_MEMORY_BUFFER; @@ -102,6 +98,39 @@ int _gnutls_srp_send_params( gnutls_session state, opaque* data, size_t data_siz data[0] = (uint8) len; memcpy( &data[1], cred->username, len); return len + 1; + } else if (cred->get_function != NULL) { + /* Try the callback + */ + char* username = NULL, *password = NULL; + + if (cred->get_function( session, session->internals.handshake_restarted, + &username, &password) < 0 || username == NULL || + password == NULL) + { + + if (session->internals.handshake_restarted) { + gnutls_assert(); + return GNUTLS_E_ILLEGAL_SRP_USERNAME; + } + + return 0; + } + + len = GMIN( strlen(username), 255); + + if (data_size < len+1) { + gnutls_free( username); + gnutls_free( password); + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + session->internals.srp_username = username; + session->internals.srp_password = password; + + data[0] = (uint8) len; + memcpy( &data[1], username, len); + return len + 1; } } return 0; diff --git a/libextra/ext_srp.h b/libextra/ext_srp.h index 63c85eac9a..7e231be3dc 100644 --- a/libextra/ext_srp.h +++ b/libextra/ext_srp.h @@ -1,5 +1,8 @@ #ifdef ENABLE_SRP +#define IS_SRP_KX(kx) ((kx == GNUTLS_KX_SRP || (kx == GNUTLS_KX_SRP_RSA) || \ + kx == GNUTLS_KX_SRP_DSS)?1:0) + int _gnutls_srp_recv_params( gnutls_session state, const opaque* data, size_t data_size); int _gnutls_srp_send_params( gnutls_session state, opaque* data, size_t); diff --git a/libextra/gnutls_srp.c b/libextra/gnutls_srp.c index feb64d2448..6807407950 100644 --- a/libextra/gnutls_srp.c +++ b/libextra/gnutls_srp.c @@ -455,7 +455,9 @@ FILE* fd; * Returns 0 on success. * **/ -int gnutls_srp_set_server_credentials_file( gnutls_srp_server_credentials res, char *password_file, char * password_conf_file) { +int gnutls_srp_set_server_credentials_file( gnutls_srp_server_credentials res, + const char *password_file, const char * password_conf_file) +{ int i; if (password_file==NULL || password_conf_file==NULL) { @@ -579,6 +581,42 @@ void gnutls_srp_set_server_credentials_function( cred->pwd_callback = func; } +/** + * gnutls_srp_set_client_credentials_function - Used to set a callback to retrieve the username and password + * @cred: is a &gnutls_srp_server_credentials structure. + * @func: is the callback function + * + * This function can be used to set a callback to retrieve the username and + * password for client SRP authentication. + * The callback's function form is: + * int (*callback)(gnutls_session, unsigned int times, char** username, + * char** password); + * + * The @username and @password must be allocated using gnutls_malloc(). + * @times will be 0 the first time called, and 1 the second. + * + * The callback function will be called once or twice per handshake. + * The first time called, is before the ciphersuite is negotiated. + * At that time if the callback returns a negative error code, + * the callback will be called again if SRP has been + * negotiated. This uses a special TLS-SRP idiom in order to avoid + * asking the user for SRP password and username if the server does + * not support SRP. + * + * The callback should not return a negative error code the second + * time called, since the handshake procedure will be aborted. + * + * The callback function should return 0 on success. + * -1 indicates an error. + * + **/ +void gnutls_srp_set_client_credentials_function( + gnutls_srp_client_credentials cred, + gnutls_srp_client_credentials_function * func) +{ + cred->get_function = func; +} + /** * gnutls_srp_server_get_username - This function returns the username of the peer @@ -671,6 +671,21 @@ static int do_handshake(socket_st * socket) return ret; } +static int srp_username_callback( gnutls_session session, unsigned int times, + char** username, char** password) +{ + /* We should ask here the user for his SRP username + * and password. + */ + if (times == 1 && srp_username && srp_passwd) { + *username = gnutls_strdup( srp_username); + *password = gnutls_strdup( srp_passwd); + + return 0; + } + + return -1; +} static void tls_log_func(int level, const char *str) { @@ -786,15 +801,9 @@ static void init_global_tls_stuff(void) fprintf(stderr, "SRP authentication error\n"); } - if (srp_username != NULL) { - if ((ret = - gnutls_srp_set_client_credentials(srp_cred, - srp_username, - srp_passwd)) < 0) { - fprintf(stderr, "SRP credentials set error [%d]\n", - ret); - } - } + + gnutls_srp_set_client_credentials_function(srp_cred, + srp_username_callback); #endif |