cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Patch for TLS-SRP support (using GnuTLS)

From: Quinn Slack <sqs_at_cs.stanford.edu>
Date: Sat, 25 Dec 2010 10:49:14 +0800

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
Received on 2010-12-25