On Dec 21, 2010, at 6:12 AM, Daniel Stenberg wrote: > We're talking about adding support for TLS authentication, using the specific > auth type SRP, right? SRP is a concept that is not specificly bound to TLS.
Yes, good point. > Also, I figure there's a probability that we will add support for more/other > types in the future. > >> +if test "x$GNUTLS_ENABLED" = "x1"; then >> + SUPPORT_FEATURES="$SUPPORT_FEATURES SRP" >> +fi > > ... so I think this is either better called TLS-SRP or possibly without > specifying the type just "TLSAUTH" or something. > >> + CURLE_SRP_FAILED, /* 89 - Failed SRP auth */ >> +#define CURL_VERSION_SRP (1<<14) /* SRP authentication is supported */ > > ... and these feel like they are for TLSAUTH that failed and the bit would > be for TLASAUTH. OK, the configure.ac feature is now "TLS-SRP", the options are CURLOPT_TLSAUTH_*, the version is CURL_VERSION_TLSAUTH_SRP, and the error is CURLE_TLSAUTH_FAILED. Updated patch pasted below and at: http://stanford.edu/~sqs/curl-tls-srp-20101224.patch > BTW, does this TLSAUTH and SRP stuff depend on some particular > GnuTLS version? Our currently set "goal" is to work with GnuTLS 1.2. It doesn't work with GnuTLS 1.2, unfortunately. GnuTLS 2.0.1 (2007-09-20) changed the SRP cipher suite values to the official IANA-assigned values, which completely broke backwards compatibility. It definitely works with GnuTLS 2.3 releases (I tried 2.3.5, 2008-04-14, gnutls git 8460e8a3). The earliest release it works with is probably 2.2.0 (2007-12-14); I can figure out for sure in a week or so. Want me to add a check for a minimum GnuTLS version before enabling TLS-SRP in configure.ac? I'll also work on some tests (will have to figure out how to get stunnel working with TLS-SRP). -Quinn diff --git a/configure.ac b/configure.ac index ea51689..58649a2 100644 --- a/configure.ac +++ b/configure.ac @@ -2801,6 +2801,9 @@ if test "x$USE_SSLEAY" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" \ -o "x$GNUTLS_ENABLED" = "x1" -o "x$NSS_ENABLED" = "x1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM" fi +if test "x$GNUTLS_ENABLED" = "x1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES TLS-SRP" +fi AC_SUBST(SUPPORT_FEATURES) diff --git a/include/curl/curl.h b/include/curl/curl.h index fbd0d9b..bf65420 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -502,6 +502,7 @@ typedef enum { CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Identifiers */ CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_TLSAUTH_FAILED, /* 89 - Failed TLS authentication */ CURL_LAST /* never use! */ } CURLcode; @@ -1442,6 +1443,15 @@ typedef enum { /* send linked-list of name:port:address sets */ CINIT(RESOLVE, OBJECTPOINT, 203), + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -1538,6 +1548,12 @@ enum { CURL_SSLVERSION_LAST /* never use, keep last */ }; +enum CURL_TLSAUTH { + CURL_TLSAUTH_NONE, + CURL_TLSAUTH_SRP, + CURL_TLSAUTH_LAST /* never use, keep last */ +}; + /* symbols to use with CURLOPT_POSTREDIR. CURL_REDIR_POST_301 and CURL_REDIR_POST_302 can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 == CURL_REDIR_POST_ALL */ @@ -2043,6 +2059,7 @@ typedef struct { #define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ #define CURL_VERSION_CONV (1<<12) /* character conversions supported */ #define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ /* * NAME curl_version_info() diff --git a/lib/gtls.c b/lib/gtls.c index 845dbbb..8527c7b 100644 --- a/lib/gtls.c +++ b/lib/gtls.c @@ -346,6 +346,27 @@ gtls_connect_step1(struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } + if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { + infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username); + + rc = gnutls_srp_allocate_client_credentials( + &conn->ssl[sockindex].srp_client_cred); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_srp_allocate_client_cred() failed: %s", + gnutls_strerror(rc)); + return CURLE_TLSAUTH_FAILED; + } + + rc = gnutls_srp_set_client_credentials(conn->ssl[sockindex].srp_client_cred, + data->set.ssl.username, + data->set.ssl.password); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_srp_set_client_cred() failed: %s", + gnutls_strerror(rc)); + return CURLE_TLSAUTH_FAILED; + } + } + if(data->set.ssl.CAfile) { /* set the trusted CA cert bundle file */ gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred, @@ -432,8 +453,16 @@ gtls_connect_step1(struct connectdata *conn, } /* put the credentials to the current session */ - rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, - conn->ssl[sockindex].cred); + if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { + rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP, + conn->ssl[sockindex].srp_client_cred); + if (rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + } + } else { + rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + conn->ssl[sockindex].cred); + } /* set the connection handle (file descriptor for the socket) */ gnutls_transport_set_ptr(session, @@ -495,8 +524,18 @@ gtls_connect_step3(struct connectdata *conn, if(data->set.ssl.verifypeer || data->set.ssl.verifyhost || data->set.ssl.issuercert) { - failf(data, "failed to get server cert"); - return CURLE_PEER_FAILED_VERIFICATION; + + if(data->set.ssl.authtype == CURL_TLSAUTH_SRP + && data->set.ssl.username != NULL + && !data->set.ssl.verifypeer + && gnutls_cipher_get(session)) { + /* no peer cert, but auth is ok if we have SRP user and cipher and no + peer verify */ + } + else { + failf(data, "failed to get server cert"); + return CURLE_PEER_FAILED_VERIFICATION; + } } infof(data, "\t common name: WARNING couldn't obtain\n"); } @@ -529,8 +568,10 @@ gtls_connect_step3(struct connectdata *conn, else infof(data, "\t server certificate verification OK\n"); } - else + else { infof(data, "\t server certificate verification SKIPPED\n"); + goto after_server_cert_verification; + } /* initialize an X.509 certificate structure. */ gnutls_x509_crt_init(&x509_cert); @@ -660,6 +701,8 @@ gtls_connect_step3(struct connectdata *conn, gnutls_x509_crt_deinit(x509_cert); +after_server_cert_verification: + /* compression algorithm (if any) */ ptr = gnutls_compression_get_name(gnutls_compression_get(session)); /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */ @@ -813,6 +856,10 @@ static void close_one(struct connectdata *conn, gnutls_certificate_free_credentials(conn->ssl[idx].cred); conn->ssl[idx].cred = NULL; } + if (conn->ssl[idx].srp_client_cred) { + gnutls_srp_free_client_credentials(conn->ssl[idx].srp_client_cred); + conn->ssl[idx].srp_client_cred = NULL; + } } void Curl_gtls_close(struct connectdata *conn, int sockindex) @@ -881,6 +928,9 @@ int Curl_gtls_shutdown(struct connectdata *conn, int sockindex) gnutls_deinit(conn->ssl[sockindex].session); } gnutls_certificate_free_credentials(conn->ssl[sockindex].cred); + if(data->set.ssl.authtype == CURL_TLSAUTH_SRP + && data->set.ssl.username != NULL) + gnutls_srp_free_client_credentials(conn->ssl[sockindex].srp_client_cred); conn->ssl[sockindex].cred = NULL; conn->ssl[sockindex].session = NULL; diff --git a/lib/url.c b/lib/url.c index 95d024d..457cd18 100644 --- a/lib/url.c +++ b/lib/url.c @@ -751,6 +751,7 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) */ set->ssl.verifypeer = TRUE; set->ssl.verifyhost = 2; + set->ssl.authtype = CURL_TLSAUTH_NONE; set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth type */ set->ssl.sessionid = TRUE; /* session ID caching enabled by default */ @@ -2526,6 +2527,24 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, case CURLOPT_FNMATCH_DATA: data->set.fnmatch_data = va_arg(param, void *); break; + case CURLOPT_TLSAUTH_USERNAME: + result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], + va_arg(param, char *)); + if (data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype) + data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ + break; + case CURLOPT_TLSAUTH_PASSWORD: + result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], + va_arg(param, char *)); + if (data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype) + data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ + break; + case CURLOPT_TLSAUTH_TYPE: + if (strncmp((char *)va_arg(param, char *), "SRP", strlen("SRP")) == 0) + data->set.ssl.authtype = CURL_TLSAUTH_SRP; + else + data->set.ssl.authtype = CURL_TLSAUTH_NONE; + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_FAILED_INIT; /* correct this */ @@ -4905,6 +4924,8 @@ static CURLcode create_conn(struct SessionHandle *data, data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE]; data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; + data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME]; + data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD]; if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config)) return CURLE_OUT_OF_MEMORY; diff --git a/lib/urldata.h b/lib/urldata.h index 208ff4e..fc5011c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -251,6 +251,7 @@ struct ssl_connect_data { #ifdef USE_GNUTLS gnutls_session session; gnutls_certificate_credentials cred; + gnutls_srp_client_credentials srp_client_cred; ssl_connect_state connecting_state; #endif /* USE_GNUTLS */ #ifdef USE_POLARSSL @@ -300,6 +301,10 @@ struct ssl_config_data { void *fsslctxp; /* parameter for call back */ bool sessionid; /* cache session IDs or not */ bool certinfo; /* gather lots of certificate info */ + + char *username; /* TLS username (for, e.g., SRP) */ + char *password; /* TLS password (for, e.g., SRP) */ + enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */ }; /* information stored about one single SSL session */ @@ -1294,6 +1299,9 @@ enum dupstring { #endif STRING_MAIL_FROM, + STRING_TLSAUTH_USERNAME, /* TLS auth <username> */ + STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */ + /* -- end of strings -- */ STRING_LAST /* not used, just an end-of-list marker */ }; diff --git a/lib/version.c b/lib/version.c index 9ba2e33..6d665fa 100644 --- a/lib/version.c +++ b/lib/version.c @@ -260,6 +260,9 @@ static curl_version_info_data version_info = { #if defined(CURL_DOES_CONVERSIONS) | CURL_VERSION_CONV #endif +#if defined(USE_GNUTLS) + | CURL_VERSION_TLSAUTH_SRP +#endif , NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ diff --git a/src/main.c b/src/main.c index a38ad62..529a042 100644 --- a/src/main.c +++ b/src/main.c @@ -503,6 +503,9 @@ struct Configurable { long low_speed_time; bool showerror; char *userpwd; + char *tls_username; + char *tls_password; + char *tls_authtype; char *proxyuserpwd; char *proxy; int proxyver; /* set to CURLPROXY_HTTP* define */ @@ -903,6 +906,9 @@ static void help(void) " --url <URL> Set URL to work with", " -B/--use-ascii Use ASCII/text transfer", " -u/--user <user[:password]> Set server user and password", + " --tlsuser <user> Set TLS username", + " --tlspassword <string> Set TLS password", + " --tlsauthtype <string> Set TLS authentication type (default SRP)", " -A/--user-agent <string> User-Agent to send to server (H)", " -v/--verbose Make the operation more talkative", " -V/--version Show version number and quit", @@ -1916,6 +1922,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"Eh","pubkey", TRUE}, {"Ei", "hostpubmd5", TRUE}, {"Ej","crlfile", TRUE}, + {"Ek","tlsuser", TRUE}, + {"El","tlspassword", TRUE}, + {"Em","tlsauthtype", TRUE}, {"f", "fail", FALSE}, {"F", "form", TRUE}, {"Fs","form-string", TRUE}, @@ -2742,6 +2751,26 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ /* CRL file */ GetStr(&config->crlfile, nextarg); break; + case 'k': /* TLS username */ + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { + GetStr(&config->tls_username, nextarg); + } else + return PARAM_LIBCURL_DOESNT_SUPPORT; + break; + case 'l': /* TLS password */ + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { + GetStr(&config->tls_password, nextarg); + } else + return PARAM_LIBCURL_DOESNT_SUPPORT; + break; + case 'm': /* TLS authentication type */ + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { + GetStr(&config->tls_authtype, nextarg); + if (strncmp(config->tls_authtype, "SRP", strlen("SRP")) != 0) + return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } else + return PARAM_LIBCURL_DOESNT_SUPPORT; + break; default: /* certificate file */ { char *ptr = strchr(nextarg, ':'); @@ -3120,7 +3149,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"SSPI", CURL_VERSION_SSPI}, {"krb4", CURL_VERSION_KERBEROS4}, {"libz", CURL_VERSION_LIBZ}, - {"CharConv", CURL_VERSION_CONV} + {"CharConv", CURL_VERSION_CONV}, + {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP} }; printf("Features: "); for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) { @@ -5460,6 +5490,10 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) /* new in 7.21.3 */ my_setopt(curl, CURLOPT_RESOLVE, config->resolve); + /* TODO: new in ### */ + curl_easy_setopt(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username); + curl_easy_setopt(curl, CURLOPT_TLSAUTH_PASSWORD, config->tls_password); + retry_numretries = config->req_retry; retrystart = cutil_tvnow(); ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html