Browse Source

curl: Implement additional TLS parameters
- Add per-connection useragent param
- Add sslversion modparam
- Add per-connection sslversion param
- Add per-connection client cert/key/ciphers
- Ensure all strings are null-terminated for libcurl

Hugh Waite 9 years ago
parent
commit
eb067dd336
4 changed files with 85 additions and 62 deletions
  1. 11 6
      modules/curl/curl.c
  2. 15 12
      modules/curl/curl.h
  3. 47 20
      modules/curl/curlcon.c
  4. 12 24
      modules/curl/functions.c

+ 11 - 6
modules/curl/curl.c

@@ -74,15 +74,16 @@ MODULE_VERSION
 /* Module parameter variables */
 unsigned int	default_connection_timeout = 4;
 char		*default_tls_cacert = NULL;		/*!< File name: Default CA cert to use for curl TLS connection */
-char		*default_tls_clientcert = NULL;		/*!< File name: Default client certificate to use for curl TLS connection */
-char		*default_tls_clientkey = NULL;		/*!< File name: Key in PEM format that belongs to client cert */
-char		*default_cipher_suite_list = NULL;		/*!< List of allowed cipher suites */
+str		default_tls_clientcert = STR_NULL;		/*!< File name: Default client certificate to use for curl TLS connection */
+str		default_tls_clientkey = STR_NULL;		/*!< File name: Key in PEM format that belongs to client cert */
+str		default_cipher_suite_list = STR_NULL;		/*!< List of allowed cipher suites */
+unsigned int	default_tls_version = 0;		/*!< 0 = Use libcurl default */
 unsigned int	default_tls_verify_peer = 1;		/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
 unsigned int	default_tls_verify_host = 2;		/*!< 0 = Do not verify TLS server CN/SAN  2 = Verify TLS server CN/SAN (default) */
 char 		*default_http_proxy = NULL;		/*!< Default HTTP proxy to use */
 unsigned int	default_http_proxy_port = 0;		/*!< Default HTTP proxy port to use */
 unsigned int	default_http_follow_redirect = 0;	/*!< Follow HTTP redirects CURLOPT_FOLLOWLOCATION */
-char 		*default_useragent = CURL_USER_AGENT;	/*!< Default CURL useragent. Default "Kamailio Curl " */
+str 		default_useragent = { CURL_USER_AGENT, CURL_USER_AGENT_LEN };	/*!< Default CURL useragent. Default "Kamailio Curl " */
 unsigned int	default_maxdatasize = 0;		/*!< Default download size. 0=disabled */
 
 static curl_version_info_data *curl_info;
@@ -142,6 +143,7 @@ static param_export_t params[] = {
 	{"tlsclientcert", PARAM_STRING, &default_tls_clientcert },
 	{"tlsclientkey", PARAM_STRING, &default_tls_clientkey },
 	{"tlscipherlist", PARAM_STRING, &default_cipher_suite_list },
+	{"tlsversion", PARAM_INT, &default_tls_version },
 	{"tlsverifypeer", PARAM_INT, &default_tls_verify_peer },
 	{"tlsverifyhost", PARAM_INT, &default_tls_verify_host },
 	{"httpproxyport", PARAM_INT, &default_http_proxy_port },
@@ -246,10 +248,13 @@ static int mod_init(void)
 
 	LM_DBG("**** init curl module done. Curl version: %s SSL %s\n", curl_info->version, curl_info->ssl_version);
 	LM_DBG("**** init curl: Number of connection objects: %d \n", curl_connection_count());
-	LM_DBG("**** init curl: User Agent: %s \n", default_useragent);
+	LM_DBG("**** init curl: User Agent: %.*s \n", default_useragent.len, default_useragent.s);
 	LM_DBG("**** init curl: HTTPredirect: %d \n", default_http_follow_redirect);
-	LM_DBG("**** init curl: Client Cert: %s Key %s\n", default_tls_clientcert, default_tls_clientkey);
+	LM_DBG("**** init curl: Client Cert: %.*s Key %.*s\n", default_tls_clientcert.len, default_tls_clientcert.s, default_tls_clientkey.len, default_tls_clientkey.s);
 	LM_DBG("**** init curl: CA Cert: %s \n", default_tls_cacert);
+	LM_DBG("**** init curl: Cipher Suites: %.*s \n", default_cipher_suite_list.len, default_cipher_suite_list.s);
+	LM_DBG("**** init curl: SSL Version: %d \n", default_tls_version);
+	LM_DBG("**** init curl: verifypeer: %d verifyhost: %d\n", default_tls_verify_peer, default_tls_verify_host);
 	LM_DBG("**** init curl: HTTP Proxy: %s Port %d\n", default_http_proxy, default_http_proxy_port);
 
 	LM_DBG("Extra: Curl supports %s %s %s \n",

+ 15 - 12
modules/curl/curl.h

@@ -37,15 +37,16 @@
 
 extern unsigned int	default_connection_timeout;
 extern char	*default_tls_cacert;			/*!< File name: Default CA cert to use for curl TLS connection */
-extern char	*default_tls_clientcert;		/*!< File name: Default client certificate to use for curl TLS connection */
-extern char	*default_tls_clientkey;			/*!< File name: Key in PEM format that belongs to client cert */
-extern char	*default_cipher_suite_list;			/*!< List of allowed cipher suites */
+extern str	default_tls_clientcert;		/*!< File name: Default client certificate to use for curl TLS connection */
+extern str	default_tls_clientkey;			/*!< File name: Key in PEM format that belongs to client cert */
+extern str	default_cipher_suite_list;			/*!< List of allowed cipher suites */
+extern unsigned int	default_tls_version;		/*!< 0 = Use libcurl default */
 extern unsigned int	default_tls_verify_peer;	/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
 extern unsigned int	default_tls_verify_host;	/*!< 0 = Do not verify TLS server CN/SAN. 2 = Verify TLS server CN/SAN (default) */
 extern char 	*default_http_proxy;			/*!< Default HTTP proxy to use */
 extern unsigned int	default_http_proxy_port;		/*!< Default HTTP proxy port to use */
 extern unsigned int	default_http_follow_redirect;	/*!< Follow HTTP redirects CURLOPT_FOLLOWLOCATION */
-extern char 	*default_useragent;			/*!< Default CURL useragent. Default "Kamailio Curl " */
+extern str 	default_useragent;			/*!< Default CURL useragent. Default "Kamailio Curl " */
 extern unsigned int	default_maxdatasize;			/*!< Default Maximum download size */
 
 extern counter_handle_t connections;	/* Number of connection definitions */
@@ -68,15 +69,17 @@ typedef struct _curl_con
 	unsigned int conid;		/*!< Connection ID */
 	str url;			/*!< The URL without schema (host + base URL)*/
 	str schema;			/*!< The URL schema */
-	str username;			/*!< The username to use for auth */
-	str password;			/*!< The password to use for auth */
+	char *username;			/*!< The username to use for auth */
+	char *password;			/*!< The password to use for auth */
 	str failover;			/*!< Another connection to use if this one fails */
-	str useragent;			/*!< Useragent to use for this connection */
-	str cacert;			/*!< File name of CA cert to use */
-	str clientcert;			/*!< File name of CA client cert */
-	str clientkey;			/*!< File name of CA client key */
-	int verify_peer;		/*!< TRUE if server cert to be verified */
-	int verify_host;		/*!< TRUE if server CN/SAN to be verified */
+	char *useragent;		/*!< Useragent to use for this connection */
+	char *cacert;			/*!< File name of CA cert to use */
+	char *clientcert;		/*!< File name of CA client cert */
+	char *clientkey;		/*!< File name of CA client key */
+	char *ciphersuites;		/*!< List of allowed cipher suites */
+	unsigned int sslversion;	/*!< SSL/TLS version to use */
+	unsigned int verify_peer;	/*!< TRUE if server cert to be verified */
+	unsigned int verify_host;	/*!< TRUE if server CN/SAN to be verified */
 	int http_follow_redirect;	/*!< TRUE if we should follow HTTP 302 redirects */
 	unsigned int port;		/*!< The port to connect to */
 	int timeout;			/*!< Timeout for this connection */

+ 47 - 20
modules/curl/curlcon.c

@@ -26,6 +26,8 @@
  * \ingroup curl
  */
 
+#include <curl/curl.h>
+
 #include "../../hashes.h"
 #include "../../dprint.h"
 #include "../../parser/parse_param.h"
@@ -110,15 +112,17 @@ int curl_parse_param(char *val)
 	str params	= STR_NULL;
 	str failover	= STR_NULL;
 
-	str client_cert = { default_tls_clientcert, default_tls_clientcert ? strlen(default_tls_clientcert) : 0 };
-	str client_key	= { default_tls_clientkey, default_tls_clientkey ? strlen(default_tls_clientkey) : 0 };
+	str client_cert  = default_tls_clientcert;
+	str client_key   = default_tls_clientkey;
+	str ciphersuites = default_cipher_suite_list;
+	str useragent    = default_useragent;
 
 	unsigned int maxdatasize = default_maxdatasize;
 	unsigned int timeout	= default_connection_timeout;
-	str useragent   = { default_useragent, strlen(default_useragent) };
 	unsigned int http_follow_redirect = default_http_follow_redirect;
 	unsigned int verify_peer = default_tls_verify_peer;
 	unsigned int verify_host = default_tls_verify_host;
+	unsigned int sslversion = default_tls_version;
 
 	str in;
 	char *p;
@@ -126,8 +130,6 @@ int curl_parse_param(char *val)
 	param_t *conparams = NULL;
 	curl_con_t *cc;
 
-	username.len = 0;
-	password.len = 0;
 	LM_INFO("curl modparam parsing starting\n");
 	LM_DBG("modparam curlcon: %s\n", val);
 
@@ -307,7 +309,7 @@ int curl_parse_param(char *val)
 					maxdatasize = default_maxdatasize;
 				}
 				LM_DBG("curl [%.*s] - timeout [%d]\n", pit->name.len, pit->name.s, maxdatasize);
-			} else if(pit->name.len==12 && strncmp(pit->name.s, "verifypeer", 7)==0) {
+			} else if(pit->name.len==10 && strncmp(pit->name.s, "verifypeer", 10)==0) {
 				if(str2int(&tok, &verify_peer)!=0) {
 					/* Bad integer */
 					LM_DBG("curl connection [%.*s]: verifypeer bad value. Using default\n", name.len, name.s);
@@ -318,7 +320,7 @@ int curl_parse_param(char *val)
 					verify_peer = default_tls_verify_peer;
 				}
 				LM_DBG("curl [%.*s] - verifypeer [%d]\n", pit->name.len, pit->name.s, verify_peer);
-			} else if(pit->name.len==12 && strncmp(pit->name.s, "verifyhost", 7)==0) {
+			} else if(pit->name.len==10 && strncmp(pit->name.s, "verifyhost", 10)==0) {
 				if(str2int(&tok, &verify_host)!=0) {
 					/* Bad integer */
 					LM_DBG("curl connection [%.*s]: verifyhost bad value. Using default\n", name.len, name.s);
@@ -329,6 +331,29 @@ int curl_parse_param(char *val)
 					verify_host = default_tls_verify_host;
 				}
 				LM_DBG("curl [%.*s] - verifyhost [%d]\n", pit->name.len, pit->name.s, verify_host);
+			} else if(pit->name.len==10 && strncmp(pit->name.s, "sslversion", 10)==0) {
+				if(str2int(&tok, &sslversion)!=0) {
+					/* Bad integer */
+					LM_DBG("curl connection [%.*s]: sslversion bad value. Using default\n", name.len, name.s);
+					sslversion = default_tls_version;
+				}
+				if (sslversion >= CURL_SSLVERSION_LAST) {
+					LM_DBG("curl connection [%.*s]: sslversion bad value. Using default\n", name.len, name.s);
+					sslversion = default_tls_version;
+				}
+				LM_DBG("curl [%.*s] - sslversion [%d]\n", pit->name.len, pit->name.s, sslversion);
+			} else if(pit->name.len==10 && strncmp(pit->name.s, "clientcert", 10)==0) {
+				client_cert = tok;
+				LM_DBG("curl [%.*s] - clientcert [%.*s]\n", pit->name.len, pit->name.s,
+						client_cert.len, client_cert.s);
+			} else if(pit->name.len==9 && strncmp(pit->name.s, "clientkey", 9)==0) {
+				client_key = tok;
+				LM_DBG("curl [%.*s] - clientkey [%.*s]\n", pit->name.len, pit->name.s,
+						client_key.len, client_key.s);
+			} else if(pit->name.len==12 && strncmp(pit->name.s, "ciphersuites", 12)==0) {
+				ciphersuites = tok;
+				LM_DBG("curl [%.*s] - ciphersuites [%.*s]\n", pit->name.len, pit->name.s,
+						ciphersuites.len, ciphersuites.s);
 			} else {
 				LM_ERR("curl Unknown parameter [%.*s] \n", pit->name.len, pit->name.s);
 			}
@@ -337,14 +362,6 @@ int curl_parse_param(char *val)
 
 	/* The URL ends either with nothing or parameters. Parameters start with ; */
 
-	LM_DBG("cname: [%.*s] url: [%.*s] username [%.*s] password [%.*s] failover [%.*s] timeout [%d] useragent [%.*s] maxdatasize [%d]\n", 
-			name.len, name.s, url.len, url.s, username.len, username.s,
-			password.len, password.s, failover.len, failover.s, timeout,
-			useragent.len, useragent.s, maxdatasize);
-	LM_DBG("cname: [%.*s] client_cert [%.*s] client_key [%.*s] verify_peer [%d] verify_host [%d]\n",
-			name.len, name.s, client_cert.len, client_cert.s, client_key.len, client_key.s,
-			verify_peer, verify_host);
-
 	if(conparams != NULL) {
 		free_params(conparams);
 	}
@@ -353,19 +370,29 @@ int curl_parse_param(char *val)
 	if (cc == NULL) {
 		return -1;
 	}
-	cc->username = username;
-	cc->password = password;
+
+	cc->username = username.s ? as_asciiz(&username) : NULL;
+	cc->password = password.s ? as_asciiz(&password) : NULL;
 	cc->schema = schema;
 	cc->failover = failover;
-	cc->useragent = useragent;
+	cc->useragent = as_asciiz(&useragent);
 	cc->url = url;
-	cc->clientcert = client_cert;
-	cc->clientkey = client_key;
+	cc->clientcert = client_cert.s ? as_asciiz(&client_cert) : NULL;
+	cc->clientkey = client_key.s ? as_asciiz(&client_key) : NULL;
+	cc->ciphersuites = ciphersuites.s ? as_asciiz(&ciphersuites) : NULL;
+	cc->sslversion = sslversion;
 	cc->verify_peer = verify_peer;
 	cc->verify_host = verify_host;
 	cc->timeout = timeout;
 	cc->maxdatasize = maxdatasize;
 	cc->http_follow_redirect = http_follow_redirect;
+
+	LM_DBG("cname: [%.*s] url: [%.*s] username [%s] password [%s] failover [%.*s] timeout [%d] useragent [%s] maxdatasize [%d]\n", 
+			name.len, name.s, cc->url.len, cc->url.s, cc->username ? cc->username : "", cc->password ? cc->password : "",
+			cc->failover.len, cc->failover.s, cc->timeout, cc->useragent, cc->maxdatasize);
+	LM_DBG("cname: [%.*s] client_cert [%s] client_key [%s] ciphersuites [%s] sslversion [%d] verify_peer [%d] verify_host [%d]\n",
+			name.len, name.s, cc->clientcert, cc->clientkey, cc->ciphersuites, cc->sslversion, cc->verify_peer, cc->verify_host);
+
 	return 0;
 
 error:

+ 12 - 24
modules/curl/functions.c

@@ -55,6 +55,7 @@ typedef struct {
     char *clientkey;
     char *cacert;
     char *ciphersuites;
+    unsigned int sslversion;
     unsigned int verify_peer;
     unsigned int verify_host;
     unsigned int timeout;
@@ -160,7 +161,6 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const
 
     /* Client certificate */
     if (params->clientcert != NULL && params->clientkey != NULL) {
-	LM_ERR("Setting curl cert %s key %s \n", params->clientcert, params->clientkey);
         res |= curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
         res |= curl_easy_setopt(curl, CURLOPT_SSLCERT, params->clientcert);
 
@@ -169,12 +169,14 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const
     }
 
     if (params->cacert != NULL) {
-	LM_ERR("Setting ca cert %s\n", params->cacert);
         res |= curl_easy_setopt(curl, CURLOPT_CAINFO, params->cacert);
     }
 
+    if (params->sslversion != CURL_SSLVERSION_DEFAULT) {
+        res |= curl_easy_setopt(curl, CURLOPT_SSLVERSION, (long) params->sslversion);
+    }
+
     if (params->ciphersuites != NULL) {
-	LM_ERR("Setting ciphersuites %s\n", params->ciphersuites);
         res |= curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, params->ciphersuites);
     }
 
@@ -288,8 +290,6 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, const str* url
 {
 	curl_con_t *conn = NULL;
 	char *urlbuf = NULL;
-	char *username_str = NULL;
-	char *password_str = NULL;
 	char *postdata = NULL;
 	curl_query_t query_params;
 
@@ -308,14 +308,6 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, const str* url
 		return -1;
 	}
 	LM_DBG("******** CURL Connection found %.*s\n", connection->len, connection->s);
-	if (conn->username.s != NULL && conn->username.len > 0)
-	{
-		username_str = as_asciiz(&conn->username);
-	}
-	if (conn->password.s != NULL && conn->password.len > 0)
-	{
-		password_str = as_asciiz(&conn->password);
-	}
 	maxdatasize = conn->maxdatasize;
 
 
@@ -359,14 +351,15 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, const str* url
 	}
 
 	memset(&query_params, 0, sizeof(curl_query_t));
-	query_params.username = username_str;
-	query_params.secret = password_str;
+	query_params.username = conn->username;
+	query_params.secret = conn->password;
 	query_params.contenttype = contenttype ? (char*)contenttype : "text/plain";
 	query_params.post = postdata;
-	query_params.clientcert = conn->clientcert.s;
-	query_params.clientkey = conn->clientkey.s;
+	query_params.clientcert = conn->clientcert;
+	query_params.clientkey = conn->clientkey;
 	query_params.cacert = default_tls_cacert;
-	query_params.ciphersuites = default_cipher_suite_list;
+	query_params.ciphersuites = conn->ciphersuites;
+	query_params.sslversion = conn->sslversion;
 	query_params.verify_peer = conn->verify_peer;
 	query_params.verify_host = conn->verify_host;
 	query_params.timeout = conn->timeout;
@@ -381,12 +374,6 @@ error:
 	if (urlbuf != NULL) {
 		pkg_free(urlbuf);
 	}
-	if (username_str != NULL) {
-		pkg_free(username_str);
-	}
-	if (password_str != NULL) {
-		pkg_free(password_str);
-	}
 	if (postdata != NULL) {
 		pkg_free(postdata);
 	}
@@ -413,6 +400,7 @@ int http_query(struct sip_msg* _m, char* _url, str* _dst, char* _post)
 	query_params.clientkey = NULL;
 	query_params.cacert = NULL;
 	query_params.ciphersuites = NULL;
+	query_params.sslversion = default_tls_version;
 	query_params.verify_peer = default_tls_verify_peer;
 	query_params.verify_host = default_tls_verify_host;
 	query_params.timeout = default_connection_timeout;