Blame SOURCES/0074-tcurl-add-support-for-ssl-and-raw-output.patch

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