Browse Source

curl: Add client key/certificate to curl_connect
- Use the client key, certificate and cacert modparams when provided
- Use the verifyserver modparam (default enabled)
- Implement per-connection verifyserver parameter
- Add ciphersuites modparam to override libcurl defaults

Hugh Waite 9 năm trước cách đây
mục cha
commit
2d7a803d4d
5 tập tin đã thay đổi với 145 bổ sung26 xóa
  1. 2 0
      modules/curl/curl.c
  2. 4 2
      modules/curl/curl.h
  3. 23 1
      modules/curl/curlcon.c
  4. 26 0
      modules/curl/doc/curl_admin.xml
  5. 90 23
      modules/curl/functions.c

+ 2 - 0
modules/curl/curl.c

@@ -76,6 +76,7 @@ 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 */
 unsigned int	default_tls_verifyserver = 1;		/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
 char 		*default_http_proxy = NULL;		/*!< Default HTTP proxy to use */
 unsigned int	default_http_proxy_port = 0;		/*!< Default HTTP proxy port to use */
@@ -139,6 +140,7 @@ static param_export_t params[] = {
 	{"tlscacert", PARAM_STRING,  &default_tls_cacert },
 	{"tlsclientcert", PARAM_STRING, &default_tls_clientcert },
 	{"tlsclientkey", PARAM_STRING, &default_tls_clientkey },
+	{"tlscipherlist", PARAM_STRING, &default_cipher_suite_list },
 	{"tlsverifyserver", PARAM_INT, &default_tls_verifyserver },
 	{"httpproxyport", PARAM_INT, &default_http_proxy_port },
 	{"httpproxy", PARAM_STRING, &default_http_proxy},

+ 4 - 2
modules/curl/curl.h

@@ -39,6 +39,7 @@ 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 unsigned int	default_tls_verifyserver;		/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
 extern char 	*default_http_proxy;			/*!< Default HTTP proxy to use */
 extern unsigned int	default_http_proxy_port;		/*!< Default HTTP proxy port to use */
@@ -69,10 +70,11 @@ typedef struct _curl_con
 	str username;			/*!< The username to use for auth */
 	str 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 useragent;			/*!< Useragent to use for this connection */
-	int tls_verifyserver;		/*!< TRUE if server cert needs to be verified */
+	str clientkey;			/*!< File name of CA client key */
+	int verify_server;		/*!< TRUE if server cert 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 */

+ 23 - 1
modules/curl/curlcon.c

@@ -96,6 +96,7 @@ curl_con_t* curl_get_connection(str *name)
  *		useragent
  *		failover
  *		maxdatasize
+ *		verifyserver
  *
  */
 int curl_parse_param(char *val)
@@ -108,10 +109,14 @@ 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 };
+
 	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 verifyserver = default_tls_verifyserver;
 
 	str in;
 	char *p;
@@ -300,6 +305,17 @@ 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, "verifyserver", 7)==0) {
+				if(str2int(&tok, &verifyserver)!=0) {
+					/* Bad integer */
+					LM_DBG("curl connection [%.*s]: verifyserver bad value. Using default\n", name.len, name.s);
+					verifyserver = default_tls_verifyserver;
+				}
+				if (verifyserver != 0 && verifyserver != 1) {
+					LM_DBG("curl connection [%.*s]: verifyserver bad value. Using default\n", name.len, name.s);
+					verifyserver = default_tls_verifyserver;
+				}
+				LM_DBG("curl [%.*s] - verifyserver [%d]\n", pit->name.len, pit->name.s, verifyserver);
 			} else {
 				LM_ERR("curl Unknown parameter [%.*s] \n", pit->name.len, pit->name.s);
 			}
@@ -310,7 +326,10 @@ int curl_parse_param(char *val)
 
 	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);
+			password.len, password.s, failover.len, failover.s, timeout,
+			useragent.len, useragent.s, maxdatasize);
+	LM_DBG("cname: [%.*s] client_cert [%.*s] client_key [%.*s] verifyserver [%d]\n",
+			name.len, name.s, client_cert.len, client_cert.s, client_key.len, client_key.s, verifyserver);
 
 	if(conparams != NULL) {
 		free_params(conparams);
@@ -326,6 +345,9 @@ int curl_parse_param(char *val)
 	cc->failover = failover;
 	cc->useragent = useragent;
 	cc->url = url;
+	cc->clientcert = client_cert;
+	cc->clientkey = client_key;
+	cc->verify_server = verifyserver;
 	cc->timeout = timeout;
 	cc->maxdatasize = maxdatasize;
 	cc->http_follow_redirect = http_follow_redirect;

+ 26 - 0
modules/curl/doc/curl_admin.xml

@@ -219,6 +219,28 @@ modparam("curl", "tlsclientkey", "/var/certs/sollentuna.example.com.key")
 				<programlisting format="linespecific">
 ...
 modparam("curl", "tlscacert", "/var/certs/ca/edvina-sip-ca.pem")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="curl.p.tlscipherlist">
+			<title><varname>tlscipherlist</varname> (string)</title>
+			<para>
+			List of allowed cipher suites.
+			See http://curl.haxx.se/libcurl/c/CURLOPT_SSL_CIPHER_LIST.html for details
+			of the cipher list curl option.
+			</para>
+			<para>
+			<emphasis>
+				Default value is empty string, i.e.
+				the default list of ciphers in libcurl will be used.
+			</emphasis>
+			</para>
+			<example>
+			<title>Set <varname>tlscipherlist</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "tlscipherlist", "ecdhe_ecdsa_aes_128_gcm_sha_256,rsa_aes_128_gcm_sha_256")
 ...
 				</programlisting>
 			</example>
@@ -286,6 +308,10 @@ modparam("curl", "tlsverifyserver", 1)
 				<emphasis>useragent</emphasis> Useragent used for HTTP requests. Overrides
 				useragent modparam.
 				</para></listitem>
+				<listitem><para>
+				<emphasis>verifyserver</emphasis> Enables or disables server certificate verification.
+				Overrides tlsverifyserver modparam.
+				</para></listitem>
 			</itemizedlist>
 			</para>
 			<example>

+ 90 - 23
modules/curl/functions.c

@@ -45,10 +45,25 @@
 #include "curl.h"
 #include "curlcon.h"
 
+
+typedef struct {
+    char *username;
+    char *secret;
+    char *contenttype;
+    char *post;
+    char *clientcert;
+    char *clientkey;
+    char *cacert;
+    char *ciphersuites;
+    unsigned int verify_server;
+    unsigned int timeout;
+    unsigned int http_follow_redirect;
+    unsigned int oneline;
+    unsigned int maxdatasize;
+} curl_query_t;
+
 /* Forward declaration */
-static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const char *username,
-		const char *secret, const char *contenttype, const char* _post, const unsigned int timeout,
-		unsigned int http_follow_redirect, unsigned int oneline, unsigned int maxdatasize);
+static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const curl_query_t * const query_params);
 
 /* 
  * curl write function that saves received data as zero terminated
@@ -86,7 +101,7 @@ size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream_ptr)
 
 /*! Send query to server, optionally post data.
  */
-static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const char *_username, const char *_secret, const char *contenttype, const char* _post, unsigned int timeout, unsigned int http_follow_redirect, unsigned int oneline, unsigned int maxdatasize)
+static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const curl_query_t * const params)
 {
     CURL *curl;
     CURLcode res;  
@@ -99,7 +114,7 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const
     struct curl_slist *headerlist = NULL;
 
     memset(&stream, 0, sizeof(curl_res_stream_t));
-    stream.max_size = (size_t) maxdatasize;
+    stream.max_size = (size_t) params->maxdatasize;
 
     curl = curl_easy_init();
     if (curl == NULL) {
@@ -110,11 +125,11 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const
     LM_DBG("****** ##### CURL URL [%s] \n", _url);
     res = curl_easy_setopt(curl, CURLOPT_URL, _url);
 
-    if (_post) {
+    if (params->post) {
 	char ctype[256];
 
 	ctype[0] = '\0';
-	snprintf(ctype, sizeof(ctype), "Content-Type: %s", contenttype);
+	snprintf(ctype, sizeof(ctype), "Content-Type: %s", params->contenttype);
 
         /* Now specify we want to POST data */ 
 	res |= curl_easy_setopt(curl, CURLOPT_POST, 1L);
@@ -124,28 +139,49 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const
 
 	/* Tell CURL we want to upload using POST */
 
- 	res |= curl_easy_setopt(curl, CURLOPT_POSTFIELDS, _post);
+	res |= curl_easy_setopt(curl, CURLOPT_POSTFIELDS, params->post);
 
     }
 
-    if (maxdatasize) {
+    if (params->maxdatasize) {
 	/* Maximum data size to download - we always download full response, but
 	   cut it off before moving to pvar */
-    	LM_DBG("****** ##### CURL Max datasize %u\n", maxdatasize);
+	LM_DBG("****** ##### CURL Max datasize %u\n", params->maxdatasize);
     }
 
-    if (_username) {
- 	res |= curl_easy_setopt(curl, CURLOPT_USERNAME, _username);
+    if (params->username) {
+	res |= curl_easy_setopt(curl, CURLOPT_USERNAME, params->username);
 	res |= curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (CURLAUTH_DIGEST|CURLAUTH_BASIC));
     }
-    if (_secret) {
- 	res |= curl_easy_setopt(curl, CURLOPT_PASSWORD, _secret);
+    if (params->secret) {
+	res |= curl_easy_setopt(curl, CURLOPT_PASSWORD, params->secret);
+    }
+
+    /* 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);
+
+        res |= curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
+        res |= curl_easy_setopt(curl, CURLOPT_SSLKEY, params->clientkey);
+    }
+
+    if (params->cacert != NULL) {
+	LM_ERR("Setting ca cert %s\n", params->cacert);
+        res |= curl_easy_setopt(curl, CURLOPT_CAINFO, params->cacert);
     }
 
+    if (params->ciphersuites != NULL) {
+	LM_ERR("Setting ciphersuites %s\n", params->ciphersuites);
+        res |= curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, params->ciphersuites);
+    }
+
+    res |= curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long) params->verify_server);
 
     res |= curl_easy_setopt(curl, CURLOPT_NOSIGNAL, (long) 1);
-    res |= curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long) timeout);
-    res |= curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) http_follow_redirect);
+    res |= curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long) params->timeout);
+    res |= curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) params->http_follow_redirect);
 
 
     res |= curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function);
@@ -201,16 +237,16 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const
 
 	if (download_size > 0) {
 
-	if (oneline) {
+	if (params->oneline) {
 		/* search for line feed */
 		at = memchr(stream.buf, (char)10, download_size);
 		datasize = (double) (at - stream.buf);
 		LM_DBG("  -- curl download size cut to first line: %d \n", (int) datasize);
 	}
 	if (at == NULL) {
-		if (maxdatasize && ((unsigned int) download_size) > maxdatasize) {
+		if (params->maxdatasize && ((unsigned int) download_size) > params->maxdatasize) {
 			/* Limit at maximum data size */
-			datasize = (double) maxdatasize;
+			datasize = (double) params->maxdatasize;
 			LM_DBG("  -- curl download size cut to maxdatasize : %d \n", (int) datasize);
 		} else {
 			/* Limit at actual downloaded data size */
@@ -253,6 +289,7 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, const str* url
 	char *username_str = NULL;
 	char *password_str = NULL;
 	char *postdata = NULL;
+	curl_query_t query_params;
 
 	unsigned int maxdatasize = default_maxdatasize;
 	int res;
@@ -319,8 +356,22 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, const str* url
 		LM_DBG("***** #### ***** CURL POST data: %s Content-type %s\n", postdata, contenttype);
 	}
 
-	res = curL_query_url(_m, urlbuf, result, username_str, password_str, (contenttype ? contenttype : "text/plain"), postdata,
-		conn->timeout, conn->http_follow_redirect, 0, (unsigned int) maxdatasize );
+	memset(&query_params, 0, sizeof(curl_query_t));
+	query_params.username = username_str;
+	query_params.secret = password_str;
+	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.cacert = default_tls_cacert;
+	query_params.ciphersuites = default_cipher_suite_list;
+	query_params.verify_server = conn->verify_server;
+	query_params.timeout = conn->timeout;
+	query_params.http_follow_redirect = conn->http_follow_redirect;
+	query_params.oneline = 0;
+	query_params.maxdatasize = maxdatasize;
+
+	res = curL_query_url(_m, urlbuf, result, &query_params);
 
 	LM_DBG("***** #### ***** CURL DONE : %s \n", urlbuf);
 error:
@@ -348,8 +399,24 @@ error:
 int http_query(struct sip_msg* _m, char* _url, str* _dst, char* _post)
 {
 	int res;
-
-	res =  curL_query_url(_m, _url, _dst, NULL, NULL, "text/plain", _post, default_connection_timeout, default_http_follow_redirect, 1, 0);
+	curl_query_t query_params;
+
+	memset(&query_params, 0, sizeof(curl_query_t));
+	query_params.username = NULL;
+	query_params.secret = NULL;
+	query_params.contenttype = "text/plain";
+	query_params.post = _post;
+	query_params.clientcert = NULL;
+	query_params.clientkey = NULL;
+	query_params.cacert = NULL;
+	query_params.ciphersuites = NULL;
+	query_params.verify_server = default_tls_verifyserver;
+	query_params.timeout = default_connection_timeout;
+	query_params.http_follow_redirect = default_http_follow_redirect;
+	query_params.oneline = 1;
+	query_params.maxdatasize = 0;
+
+	res =  curL_query_url(_m, _url, _dst, &query_params);
 
 	return res;
 }