sssd/0077-tcurl-add-support-for-ssl-and-raw-output.patch
Lukas Slebodnik 387014f928 Backport upstream patches for 1.15.3 pre-release
required for building freeipa-4.5.x in rawhide
2017-04-04 16:22:51 +02:00

1478 lines
48 KiB
Diff

From 300b9e9217ee1ed8d845ed2370c5ccf5c87afb36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 24 Feb 2017 10:40:43 +0100
Subject: [PATCH 77/97] tcurl: add support for ssl and raw output
At first, this patch separates curl_easy handle from the multi-handle
processing and makes it encapsulated in custom tcurl_request structure.
This allows us to separate protocol initialization from its asynchonous
logic which gives us the ability to set different options for each
request without over-extending the parameter list.
In this patch we implement options for peer verification for TLS-enabled
protocols and to return response with body and headers together.
Reviewed-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
---
src/tests/tcurl_test_tool.c | 41 +-
src/util/tev_curl.c | 992 +++++++++++++++++++++++++-------------------
src/util/tev_curl.h | 172 +++++++-
src/util/util_errors.c | 4 +
src/util/util_errors.h | 4 +
5 files changed, 755 insertions(+), 458 deletions(-)
diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 2af950ebb76a22bdf4a6dfd58442b10486e64293..9a6266f89131ffd3a561e857af85df9854c44949 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -42,9 +42,7 @@ static void request_done(struct tevent_req *req)
struct tool_ctx *tool_ctx = tevent_req_callback_data(req,
struct tool_ctx);
- tool_ctx->error = tcurl_http_recv(tool_ctx, req,
- &http_code,
- &outbuf);
+ tool_ctx->error = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code);
talloc_zfree(req);
if (tool_ctx->error != EOK) {
@@ -87,16 +85,17 @@ int main(int argc, const char *argv[])
"The path to the HTTP server socket", NULL },
{ "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL },
{ "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
- { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
{ "post", 'o', POPT_ARG_NONE, NULL, 'o', "Perform a HTTP POST", NULL },
+ { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
{ "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL },
POPT_TABLEEND
};
struct tevent_req *req;
struct tevent_context *ev;
- enum tcurl_http_request req_type = TCURL_HTTP_GET;
+ enum tcurl_http_method method = TCURL_HTTP_GET;
struct tcurl_ctx *ctx;
+ struct tcurl_request *tcurl_req;
struct tool_ctx *tool_ctx;
const char *urls[MAXREQ] = { 0 };
@@ -111,16 +110,16 @@ int main(int argc, const char *argv[])
while ((opt = poptGetNextOpt(pc)) > 0) {
switch (opt) {
case 'g':
- req_type = TCURL_HTTP_GET;
+ method = TCURL_HTTP_GET;
break;
case 'p':
- req_type = TCURL_HTTP_PUT;
- break;
- case 'd':
- req_type = TCURL_HTTP_DELETE;
+ method = TCURL_HTTP_PUT;
break;
case 'o':
- req_type = TCURL_HTTP_POST;
+ method = TCURL_HTTP_POST;
+ break;
+ case 'd':
+ method = TCURL_HTTP_DELETE;
break;
case 'v':
pc_verbose = 1;
@@ -146,7 +145,7 @@ int main(int argc, const char *argv[])
}
while ((extra_arg_ptr = poptGetArg(pc)) != NULL) {
- switch (req_type) {
+ switch(method) {
case TCURL_HTTP_GET:
case TCURL_HTTP_DELETE:
case TCURL_HTTP_POST:
@@ -203,14 +202,16 @@ int main(int argc, const char *argv[])
}
for (size_t i = 0; i < n_reqs; i++) {
- req = tcurl_http_send(tool_ctx, ev, ctx,
- req_type,
- socket_path,
- urls[i],
- headers,
- inbufs[i],
- 5);
- if (req == NULL) {
+ tcurl_req = tcurl_http(tool_ctx, method, socket_path,
+ urls[i], headers, inbufs[i]);
+ if (tcurl_req == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create TCURL request\n");
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ req = tcurl_request_send(tool_ctx, ev, ctx, tcurl_req, 10);
+ if (ctx == NULL) {
DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n");
talloc_zfree(tool_ctx);
return 1;
diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c
index 645d1182d10f825f209f48e0ba7e6804dde1971c..c155f4c038d4215933ee30d41c694ad4a14ae132 100644
--- a/src/util/tev_curl.c
+++ b/src/util/tev_curl.c
@@ -34,8 +34,8 @@
#include "util/util.h"
#include "util/tev_curl.h"
-#define IOBUF_CHUNK 1024
-#define IOBUF_MAX 4096
+#define TCURL_IOBUF_CHUNK 1024
+#define TCURL_IOBUF_MAX 4096
static bool global_is_curl_initialized;
@@ -71,39 +71,12 @@ struct tcurl_sock {
struct tevent_fd *fde; /* tevent tracker of the fd events */
};
-/**
- * @brief A state of one curl transfer
- *
- * Intentionally breaking the tevent coding style here and making the struct available
- * in the whole module so that the structure is available to curl callbacks that
- * need to access the state of the transfer.
- *
- * @see handle_curlmsg_done()
- */
-struct tcurl_http_state {
- /* Input parameters */
- struct tcurl_ctx *tctx;
- const char *socket_path;
- const char *url;
- int timeout;
- struct sss_iobuf *inbuf;
-
- /* Internal state */
- CURL *http_handle;
- struct curl_slist *curl_headers;
-
- /* Output data */
- struct sss_iobuf *outbuf;
- long http_code;
-};
+static void tcurl_request_done(struct tevent_req *req,
+ errno_t process_error,
+ int response_code);
static errno_t curl_code2errno(CURLcode crv)
{
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "curl error %d: %s\n", crv, curl_easy_strerror(crv));
- }
-
switch (crv) {
/* HTTP error does not fail the whole request, just returns the error
* separately
@@ -121,6 +94,47 @@ static errno_t curl_code2errno(CURLcode crv)
return ENOMEM;
case CURLE_OPERATION_TIMEDOUT:
return ETIMEDOUT;
+ case CURLE_SSL_ISSUER_ERROR:
+ case CURLE_SSL_CACERT_BADFILE:
+ case CURLE_SSL_CACERT:
+ case CURLE_SSL_CERTPROBLEM:
+ return ERR_INVALID_CERT;
+
+ case CURLE_SSL_CRL_BADFILE:
+ case CURLE_SSL_SHUTDOWN_FAILED:
+ case CURLE_SSL_ENGINE_INITFAILED:
+ case CURLE_USE_SSL_FAILED:
+ case CURLE_SSL_CIPHER:
+ case CURLE_SSL_ENGINE_SETFAILED:
+ case CURLE_SSL_ENGINE_NOTFOUND:
+ case CURLE_SSL_CONNECT_ERROR:
+ return ERR_SSL_FAILURE;
+ case CURLE_PEER_FAILED_VERIFICATION:
+ return ERR_UNABLE_TO_VERIFY_PEER;
+ case CURLE_COULDNT_RESOLVE_HOST:
+ return ERR_UNABLE_TO_RESOLVE_HOST;
+ default:
+ break;
+ }
+
+ return EIO;
+}
+
+static errno_t curlm_code2errno(CURLcode crv)
+{
+ switch (crv) {
+ case CURLM_OK:
+ return EOK;
+ case CURLM_BAD_SOCKET:
+ return EPIPE;
+ case CURLM_OUT_OF_MEMORY:
+ return ENOMEM;
+ case CURLM_BAD_HANDLE:
+ case CURLM_BAD_EASY_HANDLE:
+ case CURLM_UNKNOWN_OPTION:
+ return EINVAL;
+ case CURLM_INTERNAL_ERROR:
+ return ERR_INTERNAL;
default:
break;
}
@@ -145,22 +159,6 @@ static errno_t tcurl_global_init(void)
return EOK;
}
-static const char *http_req2str(enum tcurl_http_request req)
-{
- switch (req) {
- case TCURL_HTTP_GET:
- return "GET";
- case TCURL_HTTP_PUT:
- return "PUT";
- case TCURL_HTTP_DELETE:
- return "DELETE";
- case TCURL_HTTP_POST:
- return "POST";
- }
-
- return "Uknown request type";
-}
-
static int curl2tev_flags(int curlflags)
{
int flags = 0;
@@ -185,9 +183,9 @@ static void handle_curlmsg_done(CURLMsg *message)
CURL *easy_handle;
CURLcode crv;
struct tevent_req *req;
+ long response_code = 0;
char *done_url;
errno_t ret;
- struct tcurl_http_state *state;
easy_handle = message->easy_handle;
if (easy_handle == NULL) {
@@ -198,9 +196,8 @@ static void handle_curlmsg_done(CURLMsg *message)
if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
crv = curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url);
if (crv != CURLE_OK) {
- DEBUG(SSSDBG_MINOR_FAILURE,
- "Cannot get CURLINFO_EFFECTIVE_URL [%d]: %s\n",
- crv, curl_easy_strerror(crv));
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get CURLINFO_EFFECTIVE_URL "
+ "[%d]: %s\n", crv, curl_easy_strerror(crv));
/* not fatal since we need this only for debugging */
} else {
DEBUG(SSSDBG_TRACE_FUNC, "Handled %s\n", done_url);
@@ -209,38 +206,32 @@ static void handle_curlmsg_done(CURLMsg *message)
crv = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (void *) &req);
if (crv != CURLE_OK) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Cannot get CURLINFO_PRIVATE [%d]: %s\n",
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get CURLINFO_PRIVATE [%d]: %s\n",
crv, curl_easy_strerror(crv));
- return;
- }
-
- state = tevent_req_data(req, struct tcurl_http_state);
- if (state == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "BUG: request has no state\n");
- tevent_req_error(req, EFAULT);
- return;
+ ret = curl_code2errno(crv);
+ goto done;
}
ret = curl_code2errno(message->data.result);
if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "curl operation failed [%d]: %s\n", ret, sss_strerror(ret));
- tevent_req_error(req, ret);
- return;
+ DEBUG(SSSDBG_OP_FAILURE, "CURL operation failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
}
- /* If there was no fatal error, let's read the HTTP error code and mark
- * the request as done
- */
- crv = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &state->http_code);
+ /* If there was no fatal error, let's read the response code
+ * and mark the request as done */
+ crv = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &response_code);
if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE, "Cannot get HTTP status code\n");
- tevent_req_error(req, EFAULT);
- return;
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get response code\n");
+ ret = curl_code2errno(crv);
+ goto done;
}
- tevent_req_done(req);
+ ret = EOK;
+
+done:
+ tcurl_request_done(req, ret, response_code);
}
static void process_curl_activity(struct tcurl_ctx *tctx)
@@ -551,346 +542,42 @@ fail:
return NULL;
}
-static errno_t tcurl_add_headers(struct tcurl_http_state *state,
- const char *headers[]);
-
-static errno_t tcurl_set_options(struct tcurl_http_state *state,
- struct tevent_req *req,
- enum tcurl_http_request req_type);
-
-static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr);
-
-static size_t tcurl_http_write_data(char *ptr,
- size_t size,
- size_t nmemb,
- void *userdata);
-
-static size_t tcurl_http_read_data(void *ptr,
- size_t size,
- size_t nmemb,
- void *userdata);
-
-struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct tcurl_ctx *tctx,
- enum tcurl_http_request req_type,
- const char *socket_path,
- const char *url,
- const char *headers[],
- struct sss_iobuf *req_data,
- int timeout)
-{
- errno_t ret;
- struct tevent_req *req;
- struct tcurl_http_state *state;
-
- req = tevent_req_create(mem_ctx, &state, struct tcurl_http_state);
- if (req == NULL) {
- return NULL;
- }
-
- state->tctx = tctx;
- state->socket_path = socket_path;
- state->url = url;
- state->inbuf = req_data;
- state->timeout = timeout;
-
- state->outbuf = sss_iobuf_init_empty(state, IOBUF_CHUNK, IOBUF_MAX);
- if (state->outbuf == NULL) {
- ret = ENOMEM;
- goto fail;
- }
-
- DEBUG(SSSDBG_TRACE_FUNC,
- "HTTP request %s for URL %s\n", http_req2str(req_type), url);
- talloc_set_destructor((TALLOC_CTX *) state, tcurl_http_cleanup_handle);
-
- /* All transfer share the same multi handle, but each trasfer has its own
- * easy handle we can use to set per-transfer options
- */
- state->http_handle = curl_easy_init();
- if (state->http_handle == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "curl_easy_init failed\n");
- ret = EIO;
- goto fail;
- }
-
- ret = tcurl_add_headers(state, headers);
- if (ret != EOK) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Failed to set CURL headers [%d]: %s\n", ret, sss_strerror(ret));
- goto fail;
- }
-
- ret = tcurl_set_options(state, req, req_type);
- if (ret != EOK) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Failed to set CURL options [%d]: %s\n", ret, sss_strerror(ret));
- goto fail;
- }
-
- /* Pass control to the curl handling which will mark the request as
- * done
- */
- curl_multi_add_handle(tctx->multi_handle, state->http_handle);
-
- return req;
-
-fail:
- tevent_req_error(req, ret);
- tevent_req_post(req, ev);
- return req;
-}
-
-static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr)
-{
- struct tcurl_http_state *state = talloc_get_type(ptr, struct tcurl_http_state);
-
- if (state == NULL) {
- return 0;
- }
-
- /* it is safe to pass NULL here */
- curl_multi_remove_handle(state->tctx->multi_handle, state->http_handle);
- curl_slist_free_all(state->curl_headers);
- curl_easy_cleanup(state->http_handle);
- return 0;
-}
-
-static errno_t tcurl_add_headers(struct tcurl_http_state *state,
- const char *headers[])
-{
- if (headers == NULL) {
- return EOK;
- }
-
- /* The headers will be freed later in tcurl_http_cleanup_handle */
- for (int i = 0; headers[i] != NULL; i++) {
- state->curl_headers = curl_slist_append(state->curl_headers, headers[i]);
- if (state->curl_headers == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add header %s\n", headers[i]);
- return ENOMEM;
- }
- }
-
- /* Add a dummy header to suppress libcurl adding Expect 100-continue which
- * was causing libcurl to always wait for the internal timeout when sending
- * a PUT/PATCH request
- */
- state->curl_headers = curl_slist_append(state->curl_headers, "Expect:");
- if (state->curl_headers == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add the dummy expect header\n");
- return ENOMEM;
- }
-
- return EOK;
-}
-
-static errno_t tcurl_set_common_options(struct tcurl_http_state *state,
- struct tevent_req *req)
-{
- CURLcode crv;
-
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_HTTPHEADER,
- state->curl_headers);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set HTTP headers [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_UNIX_SOCKET_PATH,
- state->socket_path);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set UNIX socket path %s [%d]: %s\n",
- state->socket_path, crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- crv = curl_easy_setopt(state->http_handle, CURLOPT_URL, state->url);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set URL %s [%d]: %s\n",
- state->url, crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- crv = curl_easy_setopt(state->http_handle, CURLOPT_PRIVATE, req);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set private data [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- if (state->timeout > 0) {
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_TIMEOUT,
- state->timeout);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set timeout [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
- }
-
- return EOK;
-}
-
-static errno_t tcurl_set_write_options(struct tcurl_http_state *state)
-{
- CURLcode crv;
-
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_WRITEFUNCTION,
- tcurl_http_write_data);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set write function [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_WRITEDATA,
- state->outbuf);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set write data [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- return EOK;
-}
-
-static errno_t tcurl_set_read_options(struct tcurl_http_state *state)
-{
- CURLcode crv;
-
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_READFUNCTION,
- tcurl_http_read_data);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set read function [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_READDATA,
- state->inbuf);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set read data [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- return EOK;
-}
-
-static errno_t tcurl_set_options(struct tcurl_http_state *state,
- struct tevent_req *req,
- enum tcurl_http_request req_type)
-{
- CURLcode crv;
- errno_t ret;
-
- ret = tcurl_set_common_options(state, req);
- if (ret != EOK) {
- return ret;
- }
-
- ret = tcurl_set_write_options(state);
- if (ret != EOK) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Failed to set write callbacks [%d]: %s\n",
- ret, sss_strerror(ret));
- return ret;
- }
-
- switch (req_type) {
- case TCURL_HTTP_POST:
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_CUSTOMREQUEST,
- "POST");
- break;
- case TCURL_HTTP_PUT:
- /* CURLOPT_UPLOAD enables HTTP_PUT */
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_UPLOAD,
- 1L);
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set the uplodad option [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- /* Causes libcurl to add a sane Content-Length header */
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_INFILESIZE_LARGE,
- (curl_off_t) sss_iobuf_get_size(state->inbuf));
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set CURLOPT_INFILESIZE_LARGE [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
-
- ret = tcurl_set_read_options(state);
- if (ret != EOK) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Failed to set write callbacks [%d]: %s\n",
- ret, sss_strerror(ret));
- return ret;
- }
- break;
- case TCURL_HTTP_GET:
- /* GET just needs the write callbacks, nothing to do here.. */
- break;
- case TCURL_HTTP_DELETE:
- crv = curl_easy_setopt(state->http_handle,
- CURLOPT_CUSTOMREQUEST,
- "DELETE");
- if (crv != CURLE_OK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Failed to set the uplodad option [%d]: %s\n",
- crv, curl_easy_strerror(crv));
- return EIO;
- }
- break;
- default:
- return EFAULT;
- }
-
- return EOK;
-}
-
-static size_t tcurl_http_write_data(char *ptr,
- size_t size,
- size_t nmemb,
- void *userdata)
+#define tcurl_set_option(tcurl_req, option, value) \
+({ \
+ CURLcode __curl_code; \
+ errno_t __ret; \
+ \
+ __curl_code = curl_easy_setopt((tcurl_req)->curl_easy_handle, \
+ (option), (value)); \
+ if (__curl_code == CURLE_OK) { \
+ __ret = EOK; \
+ } else { \
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to set CURL option %s [%d]: %s\n", \
+ #option, __curl_code, curl_easy_strerror(__curl_code)); \
+ __ret = curl_code2errno(__curl_code); \
+ } \
+ __ret; \
+})
+
+static size_t tcurl_write_data(char *ptr,
+ size_t size,
+ size_t nmemb,
+ void *userdata)
{
errno_t ret;
size_t realsize = size * nmemb;
- struct sss_iobuf *outbuf = talloc_get_type(userdata, struct sss_iobuf);
+ struct sss_iobuf *outbuf;
+
+ outbuf = talloc_get_type(userdata, struct sss_iobuf);
DEBUG(SSSDBG_TRACE_INTERNAL, "---> begin libcurl data\n");
DEBUG(SSSDBG_TRACE_INTERNAL, "%s\n", ptr);
DEBUG(SSSDBG_TRACE_INTERNAL, "<--- end libcurl data\n");
- ret = sss_iobuf_write_len(outbuf, (uint8_t *) ptr, realsize);
+ ret = sss_iobuf_write_len(outbuf, (uint8_t *)ptr, realsize);
if (ret != EOK) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "Failed to write data to buffer [%d]: %s\n", ret, sss_strerror(ret));
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to write data to buffer [%d]: %s\n",
+ ret, sss_strerror(ret));
/* zero signifies an EOF */
return 0;
}
@@ -898,14 +585,16 @@ static size_t tcurl_http_write_data(char *ptr,
return realsize;
}
-static size_t tcurl_http_read_data(void *ptr,
- size_t size,
- size_t nmemb,
- void *userdata)
+static size_t tcurl_read_data(void *ptr,
+ size_t size,
+ size_t nmemb,
+ void *userdata)
{
errno_t ret;
size_t readbytes;
- struct sss_iobuf *inbuf = (struct sss_iobuf *) userdata;
+ struct sss_iobuf *inbuf;
+
+ inbuf = talloc_get_type(userdata, struct sss_iobuf);
if (inbuf == NULL) {
return CURL_READFUNC_ABORT;
@@ -919,22 +608,487 @@ static size_t tcurl_http_read_data(void *ptr,
return readbytes;
}
-int tcurl_http_recv(TALLOC_CTX *mem_ctx,
- struct tevent_req *req,
- int *_http_code,
- struct sss_iobuf **_outbuf)
+
+struct tcurl_request {
+ CURL *curl_easy_handle;
+
+ struct sss_iobuf *body;
+ struct curl_slist *headers;
+
+ const char *url;
+ const char *socket;
+
+ /* Associated tcurl context if this request is in progress. */
+ struct tcurl_ctx *tcurl_ctx;
+};
+
+struct tcurl_request_state {
+ struct tcurl_request *tcurl_req;
+ struct sss_iobuf *response;
+ int response_code;
+};
+
+struct tevent_req *
+tcurl_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tcurl_ctx *tcurl_ctx,
+ struct tcurl_request *tcurl_req,
+ long int timeout)
{
- struct tcurl_http_state *state = tevent_req_data(req, struct tcurl_http_state);
+ struct tcurl_request_state *state;
+ struct tevent_req *req;
+ CURLMcode curl_code;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct tcurl_request_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Sending TCURL request for %s, at socket %s\n",
+ tcurl_req->url == NULL ? "<none>" : tcurl_req->url,
+ tcurl_req->socket == NULL ? "<none>" : tcurl_req->socket);
+
+ state->tcurl_req = talloc_steal(state, tcurl_req);
+
+ state->response = sss_iobuf_init_empty(state, TCURL_IOBUF_CHUNK, TCURL_IOBUF_MAX);
+ if (state->response == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_PRIVATE, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_TIMEOUT, timeout);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEFUNCTION, tcurl_write_data);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEDATA, state->response);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (tcurl_req->body != NULL) {
+ ret = tcurl_set_option(tcurl_req, CURLOPT_READFUNCTION, tcurl_read_data);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_READDATA, tcurl_req->body);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ curl_code = curl_multi_add_handle(tcurl_ctx->multi_handle,
+ tcurl_req->curl_easy_handle);
+ if (curl_code != CURLM_OK) {
+ ret = curlm_code2errno(curl_code);
+ goto done;
+ }
+
+ tcurl_req->tcurl_ctx = tcurl_ctx;
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tcurl_request_done(struct tevent_req *req,
+ errno_t process_error,
+ int response_code)
+{
+ struct tcurl_request_state *state;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "TCURL request finished [%d]: %s\n",
+ process_error, sss_strerror(process_error));
+
+ if (req == NULL) {
+ /* To handle case where we fail to obtain request from private data. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "No tevent request provided!\n");
+ return;
+ }
+
+ state = tevent_req_data(req, struct tcurl_request_state);
+
+ curl_multi_remove_handle(state->tcurl_req->tcurl_ctx->multi_handle,
+ state->tcurl_req->curl_easy_handle);
+
+ /* This request is no longer associated with tcurl context. */
+ state->tcurl_req->tcurl_ctx = NULL;
+
+ if (process_error != EOK) {
+ tevent_req_error(req, process_error);
+ return;
+ }
+
+ state->response_code = response_code;
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sss_iobuf **_response,
+ int *_response_code)
+{
+ struct tcurl_request_state *state;
+ state = tevent_req_data(req, struct tcurl_request_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
- if (_http_code != NULL) {
- *_http_code = state->http_code;
+ if (_response != NULL) {
+ *_response = talloc_steal(mem_ctx, state->response);
}
- if (_outbuf != NULL) {
- *_outbuf = talloc_steal(mem_ctx, state->outbuf);
+ if (_response_code != NULL) {
+ *_response_code = state->response_code;
+ }
+
+ return EOK;
+}
+
+static struct curl_slist *
+tcurl_add_header(struct curl_slist *slist, const char *header)
+{
+ struct curl_slist *new;
+
+ new = curl_slist_append(slist, header);
+ if (new == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add header %s\n", header);
+ if (slist != NULL) {
+ curl_slist_free_all(slist);
+ }
+
+ return NULL;
+ }
+
+ return new;
+}
+
+static errno_t
+tcurl_construct_headers(const char **headers,
+ struct curl_slist **_slist)
+{
+ struct curl_slist *slist = NULL;
+ int i;
+
+ if (headers == NULL || headers[0] == NULL) {
+ *_slist = NULL;
+ return EOK;
+ }
+
+ for (i = 0; headers[i] != NULL; i++) {
+ slist = tcurl_add_header(slist, headers[i]);
+ if (slist == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ /* Add a dummy header to suppress libcurl adding Expect 100-continue which
+ * was causing libcurl to always wait for the internal timeout when sending
+ * a PUT/POST request because secrets responder does not implement this.
+ */
+ slist = tcurl_add_header(slist, "Expect: ");
+ if (slist == NULL) {
+ return ENOMEM;
+ }
+
+ *_slist = slist;
+
+ return EOK;
+}
+
+static int
+tcurl_request_destructor(struct tcurl_request *tcurl_req)
+{
+ if (tcurl_req->tcurl_ctx != NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Terminating TCURL request...\n");
+ curl_multi_remove_handle(tcurl_req->tcurl_ctx->multi_handle,
+ tcurl_req->curl_easy_handle);
+ }
+
+ if (tcurl_req->headers != NULL) {
+ curl_slist_free_all(tcurl_req->headers);
+ }
+
+ if (tcurl_req->curl_easy_handle != NULL) {
+ curl_easy_cleanup(tcurl_req->curl_easy_handle);
}
return 0;
}
+
+static struct tcurl_request *
+tcurl_request_create(TALLOC_CTX *mem_ctx,
+ const char *socket_path,
+ const char *url,
+ const char **headers,
+ struct sss_iobuf *body)
+{
+ struct tcurl_request *tcurl_req;
+ errno_t ret;
+
+ tcurl_req = talloc_zero(mem_ctx, struct tcurl_request);
+ if (tcurl_req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ if (url == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "URL cannot be NULL!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Setup a curl easy handle. This handle contains state for the request
+ * and is later associated with curl multi handle which performs
+ * asynchronous processing. */
+ tcurl_req->curl_easy_handle = curl_easy_init();
+ if (tcurl_req->curl_easy_handle == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize curl easy handle!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tcurl_req->url = talloc_strdup(tcurl_req, url);
+ if (tcurl_req->url == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (socket_path != NULL) {
+ tcurl_req->socket = talloc_strdup(tcurl_req, socket_path);
+ if (tcurl_req->socket == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = tcurl_construct_headers(headers, &tcurl_req->headers);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct headers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tcurl_req->body = body;
+
+ talloc_set_destructor(tcurl_req, tcurl_request_destructor);
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_URL, url);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (socket_path != NULL) {
+ ret = tcurl_set_option(tcurl_req, CURLOPT_UNIX_SOCKET_PATH, socket_path);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (body != NULL) {
+ /* Curl will tell the underlying protocol about incoming data length.
+ * In case of HTTP it will add a sane Content-Length header. */
+ ret = tcurl_set_option(tcurl_req, CURLOPT_INFILESIZE_LARGE,
+ (curl_off_t)sss_iobuf_get_size(body));
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(tcurl_req);
+ return NULL;
+ }
+
+ return tcurl_req;
+}
+
+struct tcurl_request *tcurl_http(TALLOC_CTX *mem_ctx,
+ enum tcurl_http_method method,
+ const char *socket_path,
+ const char *url,
+ const char **headers,
+ struct sss_iobuf *body)
+{
+ struct tcurl_request *tcurl_req;
+ errno_t ret;
+
+ tcurl_req = tcurl_request_create(mem_ctx, socket_path, url, headers, body);
+ if (tcurl_req == NULL) {
+ return NULL;
+ }
+
+ /* Set HTTP specific options. */
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_HTTPHEADER, tcurl_req->headers);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ switch (method) {
+ case TCURL_HTTP_GET:
+ /* Nothing to do here. GET is default. */
+ break;
+ case TCURL_HTTP_PUT:
+ ret = tcurl_set_option(tcurl_req, CURLOPT_UPLOAD, 1L);
+ if (ret != EOK) {
+ goto done;
+ }
+ break;
+ case TCURL_HTTP_POST:
+ ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "POST");
+ if (ret != EOK) {
+ goto done;
+ }
+ break;
+ case TCURL_HTTP_DELETE:
+ ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "DELETE");
+ if (ret != EOK) {
+ goto done;
+ }
+ break;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(tcurl_req);
+ return NULL;
+ }
+
+ return tcurl_req;
+}
+
+struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tcurl_ctx *tcurl_ctx,
+ enum tcurl_http_method method,
+ const char *socket_path,
+ const char *url,
+ const char **headers,
+ struct sss_iobuf *body,
+ int timeout)
+{
+ struct tcurl_request *tcurl_req;
+ struct tevent_req *req;
+
+ tcurl_req = tcurl_http(mem_ctx, method, socket_path, url, headers, body);
+ if (tcurl_req == NULL) {
+ return NULL;
+ }
+
+ req = tcurl_request_send(mem_ctx, ev, tcurl_ctx, tcurl_req, timeout);
+ if (req == NULL) {
+ talloc_free(tcurl_req);
+ }
+
+ return req;
+}
+
+errno_t tcurl_http_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ int *_http_code,
+ struct sss_iobuf **_response)
+{
+ return tcurl_request_recv(mem_ctx, req, _response, _http_code);
+}
+
+errno_t tcurl_req_enable_rawoutput(struct tcurl_request *tcurl_req)
+{
+ return tcurl_set_option(tcurl_req, CURLOPT_HEADER, 1L);
+}
+
+errno_t tcurl_req_verify_peer(struct tcurl_request *tcurl_req,
+ const char *capath,
+ const char *cacert,
+ bool verify_peer,
+ bool verify_host)
+{
+ errno_t ret;
+
+ long peer = verify_peer ? 1L : 0L;
+ long host = verify_host ? 2L : 0L;
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYPEER, peer);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYHOST, host);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (capath != NULL) {
+ ret = tcurl_set_option(tcurl_req, CURLOPT_CAPATH, capath);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ if (cacert != NULL) {
+ ret = tcurl_set_option(tcurl_req, CURLOPT_CAINFO, cacert);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
+ const char *cert,
+ const char *key)
+{
+ errno_t ret;
+
+ if (cert == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "You must specify client certificate!\n");
+ return EINVAL;
+ }
+
+ ret = tcurl_set_option(tcurl_req, CURLOPT_SSLCERT, cert);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (key != NULL) {
+ /* If client's private key is in separate file. */
+ ret = tcurl_set_option(tcurl_req, CURLOPT_SSLKEY, key);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h
index 444eb286e09d189b4588e2b2152b5202df3914d8..933abcb9b531412737e8fcf391644d828b125cf8 100644
--- a/src/util/tev_curl.h
+++ b/src/util/tev_curl.h
@@ -27,14 +27,16 @@
#include "util/sss_iobuf.h"
+struct tcurl_request;
+
/**
- * @brief Supported HTTP requests
+ * @brief Supported HTTP methods
*/
-enum tcurl_http_request {
+enum tcurl_http_method {
TCURL_HTTP_GET,
TCURL_HTTP_PUT,
- TCURL_HTTP_DELETE,
TCURL_HTTP_POST,
+ TCURL_HTTP_DELETE,
};
/**
@@ -46,16 +48,95 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
struct tevent_context *ev);
/**
+ * @brief Run a single asynchronous TCURL request.
+ *
+ * If the libcurl processing succeeds but we obtain a protocol error we still
+ * mark the tevent request as successful. The protocol error is return from
+ * @tcurl_request_recv as an output parameter.
+ *
+ * @param[in] mem_ctx The talloc context that owns the request
+ * @param[in] ev Event loop context
+ * @param[in] tctx Use tcurl_init to get this context
+ * @param[in] tcurl_req TCURL request
+ * @param[in] timeout The request timeout in seconds. Use 0 if you want
+ * to use the default libcurl timeout.
+ *
+ * @returns A tevent request or NULL on allocation error. On other errors, we
+ * try to set the errno as event error code and run it to completion so that
+ * the programmer can use tcurl_request_recv to read the error code.
+ *
+ * @see tcurl_init
+ * @see tcurl_http
+ * @see tcurl_request_recv
+ */
+struct tevent_req *
+tcurl_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tcurl_ctx *tcurl_ctx,
+ struct tcurl_request *tcurl_req,
+ long int timeout);
+
+/**
+ * @brief Receive a result of a single asynchronous TCURL request.
+ *
+ * @param[in] mem_ctx The talloc context that owns the response
+ * @param[in] req The request previously obtained with tcurl_request_send
+ * @param[out] _response Response to the request
+ * @param[out] _response_code Protocol response code (may indicate a protocl error)
+ *
+ * @returns The error code of the curl request (not the HTTP code!)
+ */
+errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sss_iobuf **_response,
+ int *_response_code);
+
+/**
+ * @brief Create a HTTP request.
+ *
+ * Use this if you need better control over the request options.
+ *
+ * Headers are a NULL-terminated array of strings such as:
+ * static const char *headers[] = {
+ * "Content-type: application/octet-stream",
+ * NULL,
+ * };
+ *
+ * @param[in] mem_ctx The talloc context that owns the tcurl_request
+ * @param[in] method TCURL HTTP method
+ * @param[in] socket_path The path to the UNIX socket to forward the
+ * request to, may be NULL.
+ * @param[in] url The request URL, cannot be NULL.
+ * @param[in] headers A NULL-terminated array of strings to use
+ * as additional HTTP headers. Pass NULL if you
+ * don't need any additional headers.
+ * @param[in] body The HTTP request input data. For some request
+ * types like DELETE, this is OK to leave as NULL.
+ *
+ * @returns A tcurl_request that can be later started with tcurl_request_send
+ * or NULL on error.
+ *
+ * @see tcurl_init
+ * @see tcurl_request_send
+ * @see tcurl_request_recv
+ */
+struct tcurl_request *tcurl_http(TALLOC_CTX *mem_ctx,
+ enum tcurl_http_method method,
+ const char *socket_path,
+ const char *url,
+ const char **headers,
+ struct sss_iobuf *body);
+
+/**
* @brief Run a single asynchronous HTTP request.
*
- * Currently only UNIX sockets at socket_path are supported.
+ * Use this if you do not need control over additional request options.
*
* If the request runs into completion, but reports a failure with HTTP return
* code, the request will be marked as done. Only if the request cannot run at
* all (if e.g. the socket is unreachable), the request will fail completely.
*
- * Headers are a NULL-terminated
- * array of strings such as:
+ * Headers are a NULL-terminated array of strings such as:
* static const char *headers[] = {
* "Content-type: application/octet-stream",
* NULL,
@@ -63,15 +144,15 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
*
* @param[in] mem_ctx The talloc context that owns the iobuf
* @param[in] ev Event loop context
- * @param[in] tctx Use tcurl_init to get this context
- * @param[in] req_type The request type
+ * @param[in] tcurl_ctx Use tcurl_init to get this context
+ * @param[in] method HTTP method
* @param[in] socket_path The path to the UNIX socket to forward the
- * request to
- * @param[in] url The request URL
+ * request to, may be NULL.
+ * @param[in] url The request URL, cannot be NULL.
* @param[in] headers A NULL-terminated array of strings to use
* as additional HTTP headers. Pass NULL if you
* don't need any additional headers.
- * @param[in] req_data The HTTP request input data. For some request
+ * @param[in] body The HTTP request input data. For some request
* types like DELETE, this is OK to leave as NULL.
* @param[in] timeout The request timeout in seconds. Use 0 if you want
* to use the default libcurl timeout.
@@ -85,12 +166,12 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
*/
struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct tcurl_ctx *tctx,
- enum tcurl_http_request req_type,
+ struct tcurl_ctx *tcurl_ctx,
+ enum tcurl_http_method method,
const char *socket_path,
const char *url,
- const char *headers[],
- struct sss_iobuf *req_data,
+ const char **headers,
+ struct sss_iobuf *body,
int timeout);
/**
@@ -104,9 +185,62 @@ struct tevent_req *tcurl_http_send(TALLOC_CTX *mem_ctx,
*
* @returns The error code of the curl request (not the HTTP code!)
*/
-int tcurl_http_recv(TALLOC_CTX *mem_ctx,
- struct tevent_req *req,
- int *_http_code,
- struct sss_iobuf **_outbuf);
+errno_t tcurl_http_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ int *_http_code,
+ struct sss_iobuf **_response);
+
+/**
+ * @brief We are usually interested only in the reply body without protocol
+ * headers. Call this function on tcurl_request, if you want to include
+ * complete protocol response in the output buffer.
+ *
+ * @param[in] tcurl_request
+ *
+ * @returns errno code
+ *
+ * @see tcurl_http
+ */
+errno_t tcurl_req_enable_rawoutput(struct tcurl_request *tcurl_req);
+
+/**
+ * @brief TLS is enabled automatically by providing an URL that points to
+ * TLS-enabled protocol such as https. If you want to provide different
+ * path to CA directory or disable peer/hostname check explicitly, use
+ * this function on tcurl_request.
+ *
+ * @param[in] tcurl_request
+ * @param[in] capath Path to directory containing installed CA certificates.
+ * If not set, libcurl default is used.
+ * @param[ing cacert CA certificate. If NULL it is found in @capath.
+ * @param[in] verify_peer If false, the peer certificate is not verified.
+ * @param[in] verify_host If false, the host name provided in remote
+ * certificate may differ from the actual host name.
+ *
+ * @returns errno code
+ *
+ * @see tcurl_http
+ */
+errno_t tcurl_req_verify_peer(struct tcurl_request *tcurl_req,
+ const char *capath,
+ const char *cacert,
+ bool verify_peer,
+ bool verify_host);
+/**
+ * @brief Some server require client verification during TLS setup. You can
+ * provide path to client's certificate file. If this file does not contain
+ * private key, you can specify a different file the holds the private key.
+ *
+ * @param[in] tcurl_request
+ * @param[in] cert Path to client's certificate.
+ * @param[in] key Path to client's private key.
+ *
+ * @returns errno code
+ *
+ * @see tcurl_http
+ */
+errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
+ const char *cert,
+ const char *key);
#endif /* __TEV_CURL_H */
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 60c2f439b3e39b1dbff353e429114cb5a3070052..466a3b4062f39b29d831a5d8a62dc8d576eb2e97 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -111,6 +111,10 @@ struct err_string error_to_str[] = {
{ "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
{ "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */
{ "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */
+ { "Invalid certificate provided" }, /* ERR_INVALID_CERT */
+ { "Unable to initialize SSL" }, /* ERR_SSL_FAILURE */
+ { "Unable to verify peer" }, /* ERR_UNABLE_TO_VERIFY_PEER */
+ { "Unable to resolve host" }, /* ERR_UNABLE_TO_RESOLVE_HOST */
{ "ERR_LAST" } /* ERR_LAST */
};
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 4e9da814702e2cd46edc52fd5c2ae5f640602609..2f90c0a5d65325a431a8e4d9a480170808c9198e 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -133,6 +133,10 @@ enum sssd_errors {
ERR_KCM_WRONG_CCNAME_FORMAT,
ERR_JSON_ENCODING,
ERR_JSON_DECODING,
+ ERR_INVALID_CERT,
+ ERR_SSL_FAILURE,
+ ERR_UNABLE_TO_VERIFY_PEER,
+ ERR_UNABLE_TO_RESOLVE_HOST,
ERR_LAST /* ALWAYS LAST */
};
--
2.12.2