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: Mon, 20 Dec 2010 04:02:29 -0600

On Dec 18, 2010, at 11:58 AM, Daniel Stenberg wrote:
>> My patch exposes GnuTLS's existing TLS-SRP support to cURL,
> Can you please post it here so that we all can read and review it?

Sure, I just put up the TLS-SRP patch at
    http://stanford.edu/~sqs/curl-tls-srp-20101220.patch
and pasted it below. Patch is against 7f3b87d (up-to-date as of Dec 19).

-Quinn

diff --git a/configure.ac b/configure.ac
index ea51689..f65e421 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 SRP"
+fi
 
 AC_SUBST(SUPPORT_FEATURES)
 
diff --git a/include/curl/curl.h b/include/curl/curl.h
index fbd0d9b..d651821 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_SRP_FAILED, /* 89 - Failed SRP auth */
 
   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(TLS_USERNAME, OBJECTPOINT, 204),
+
+ /* Set a password for authenticated TLS */
+ CINIT(TLS_PASSWORD, OBJECTPOINT, 205),
+
+ /* Set authentication type for authenticated TLS */
+ CINIT(TLS_AUTHTYPE, OBJECTPOINT, 206),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -1538,6 +1548,12 @@ enum {
   CURL_SSLVERSION_LAST /* never use, keep last */
 };
 
+enum CURL_SSLAUTH {
+ CURL_SSLAUTH_NONE,
+ CURL_SSLAUTH_SRP,
+ CURL_SSLAUTH_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_SRP (1<<14) /* SRP authentication is supported */
 
 /*
  * NAME curl_version_info()
diff --git a/lib/gtls.c b/lib/gtls.c
index 845dbbb..fd487a9 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_SSLAUTH_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_SRP_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_SRP_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_SSLAUTH_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_SSLAUTH_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_SSLAUTH_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..02a5349 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_SSLAUTH_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_TLS_USERNAME:
+ result = setstropt(&data->set.str[STRING_TLS_USERNAME],
+ va_arg(param, char *));
+ if (data->set.str[STRING_TLS_USERNAME] && !data->set.ssl.authtype)
+ data->set.ssl.authtype = CURL_SSLAUTH_SRP; /* default to SRP */
+ break;
+ case CURLOPT_TLS_PASSWORD:
+ result = setstropt(&data->set.str[STRING_TLS_PASSWORD],
+ va_arg(param, char *));
+ if (data->set.str[STRING_TLS_USERNAME] && !data->set.ssl.authtype)
+ data->set.ssl.authtype = CURL_SSLAUTH_SRP; /* default to SRP */
+ break;
+ case CURLOPT_TLS_AUTHTYPE:
+ if (strncmp((char *)va_arg(param, char *), "SRP", strlen("SRP")) == 0)
+ data->set.ssl.authtype = CURL_SSLAUTH_SRP;
+ else
+ data->set.ssl.authtype = CURL_SSLAUTH_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_TLS_USERNAME];
+ data->set.ssl.password = data->set.str[STRING_TLS_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..0d6fbaf 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_SSLAUTH authtype; /* TLS authentication type (default SRP) */
 };
 
 /* information stored about one single SSL session */
@@ -1294,6 +1299,9 @@ enum dupstring {
 #endif
   STRING_MAIL_FROM,
 
+ STRING_TLS_USERNAME, /* TLS auth <username> */
+ STRING_TLS_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..40b48ed 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_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..b6c57e9 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_SRP) {
+ GetStr(&config->tls_username, nextarg);
+ } else
+ return PARAM_LIBCURL_DOESNT_SUPPORT;
+ break;
+ case 'l': /* TLS password */
+ if(curlinfo->features & CURL_VERSION_SRP) {
+ GetStr(&config->tls_password, nextarg);
+ } else
+ return PARAM_LIBCURL_DOESNT_SUPPORT;
+ break;
+ case 'm': /* TLS authentication type */
+ if(curlinfo->features & CURL_VERSION_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},
+ {"SRP", CURL_VERSION_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_TLS_USERNAME, config->tls_username);
+ curl_easy_setopt(curl, CURLOPT_TLS_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-20