summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2003-12-02 18:20:44 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2003-12-02 18:20:44 +0000
commit80ec4bd7800f0ba17a59c84e4c0c729b26c3fcd8 (patch)
treee4d828f40ef9b2a257b39be1e9d034424e4397bc
parent2542696aed50d8a3f8821e1f5913c615e62d240f (diff)
downloadgnutls-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--NEWS3
-rw-r--r--doc/TODO2
-rw-r--r--doc/tex/srp.tex17
-rw-r--r--includes/gnutls/extra.h13
-rw-r--r--lib/gnutls.h.in.in1
-rw-r--r--lib/gnutls_errors.c2
-rw-r--r--lib/gnutls_handshake.c51
-rw-r--r--lib/gnutls_int.h8
-rw-r--r--lib/gnutls_state.c5
-rw-r--r--libextra/auth_srp.c100
-rw-r--r--libextra/auth_srp.h12
-rw-r--r--libextra/ext_srp.c73
-rw-r--r--libextra/ext_srp.h3
-rw-r--r--libextra/gnutls_srp.c40
-rw-r--r--src/cli.c27
15 files changed, 253 insertions, 104 deletions
diff --git a/NEWS b/NEWS
index bb8b771e77..6685cf088a 100644
--- a/NEWS
+++ b/NEWS
@@ -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()
diff --git a/doc/TODO b/doc/TODO
index 1dd7b4c11c..eef9007e1b 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -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
diff --git a/src/cli.c b/src/cli.c
index 1cbea2c384..d3042166b6 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -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