Skip to content

Commit

Permalink
curl: Add client key/certificate to curl_connect
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Hugh Waite committed Jan 19, 2016
1 parent 9fd99ef commit 2d7a803
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 26 deletions.
2 changes: 2 additions & 0 deletions modules/curl/curl.c
Expand Up @@ -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 */
Expand Down Expand Up @@ -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},
Expand Down
6 changes: 4 additions & 2 deletions modules/curl/curl.h
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down
24 changes: 23 additions & 1 deletion modules/curl/curlcon.c
Expand Up @@ -96,6 +96,7 @@ curl_con_t* curl_get_connection(str *name)
* useragent
* failover
* maxdatasize
* verifyserver
*
*/
int curl_parse_param(char *val)
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand All @@ -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;
Expand Down
26 changes: 26 additions & 0 deletions modules/curl/doc/curl_admin.xml
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
113 changes: 90 additions & 23 deletions modules/curl/functions.c
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -348,8 +399,24 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, const str* url
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;
}

0 comments on commit 2d7a803

Please sign in to comment.