From 5378a50945441d2442d35e6caee97270c8ec2f60 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 01/13] 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.
---
 src/tests/tcurl_test_tool.c |  36 +-
 src/util/tev_curl.c         | 788 +++++++++++++++++++++++++-------------------
 src/util/tev_curl.h         | 154 ++++++---
 src/util/util_errors.c      |   4 +
 src/util/util_errors.h      |   4 +
 5 files changed, 587 insertions(+), 399 deletions(-)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 35ea979..18b4fa1 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,6 +85,7 @@ 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 },
+        { "post", 'o', POPT_ARG_NONE, NULL, 'p', "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
@@ -94,8 +93,9 @@ int main(int argc, const char *argv[])
 
     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 };
@@ -110,13 +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;
+            method = TCURL_HTTP_PUT;
+            break;
+        case 'o':
+            method = TCURL_HTTP_POST;
             break;
         case 'd':
-            req_type = TCURL_HTTP_DELETE;
+            method = TCURL_HTTP_DELETE;
             break;
         case 'v':
             pc_verbose = 1;
@@ -142,12 +145,13 @@ 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:
             urls[n_reqs++] = extra_arg_ptr;
             break;
         case TCURL_HTTP_PUT:
+        case TCURL_HTTP_POST:
             if (urls[n_reqs] == NULL) {
                 urls[n_reqs] = extra_arg_ptr;
             } else {
@@ -198,13 +202,15 @@ 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],
-                              10);
+        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);
diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c
index fd43665..fac91eb 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,51 @@ static errno_t curl_code2errno(CURLcode crv)
         return ENOMEM;
     case CURLE_OPERATION_TIMEDOUT:
         return ETIMEDOUT;
+    case CURLE_SSL_ISSUER_ERROR:
+    case CURLE_SSL_INVALIDCERTSTATUS:
+    case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+    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_ADDED_ALREADY:
+        return EEXIST;
+    case CURLM_INTERNAL_ERROR:
+        return ERR_INTERNAL;
     default:
         break;
     }
@@ -145,20 +163,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";
-    }
-
-    return "Uknown request type";
-}
-
 static int curl2tev_flags(int curlflags)
 {
     int flags = 0;
@@ -183,9 +187,9 @@ static void handle_curlmsg_done(CURLMsg *message)
     CURL *easy_handle;
     CURLcode crv;
     struct tevent_req *req;
+    int response_code;
     char *done_url;
     errno_t ret;
-    struct tcurl_http_state *state;
 
     easy_handle = message->easy_handle;
     if (easy_handle == NULL) {
@@ -196,9 +200,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);
@@ -207,38 +210,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)
@@ -549,385 +546,492 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
     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)
+#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;
+
+    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);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to write data to buffer [%d]: %s\n",
+              ret, sss_strerror(ret));
+        /* zero signifies an EOF */
+        return 0;
+    }
+
+    return realsize;
+}
+
+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;
+
+    inbuf = talloc_get_type(userdata, struct sss_iobuf);
+
+    if (inbuf == NULL) {
+        return CURL_READFUNC_ABORT;
+    }
+
+    ret = sss_iobuf_read(inbuf, size * nmemb, ptr, &readbytes);
+    if (ret != EOK) {
+        return CURL_READFUNC_ABORT;
+    }
+
+    return readbytes;
+}
+
+
+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_request_state *state;
     struct tevent_req *req;
-    struct tcurl_http_state *state;
+    CURLMcode curl_code;
+    errno_t ret;
 
-    req = tevent_req_create(mem_ctx, &state, struct tcurl_http_state);
+    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;
     }
 
-    state->tctx = tctx;
-    state->socket_path = socket_path;
-    state->url = url;
-    state->inbuf = req_data;
-    state->timeout = timeout;
+    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->outbuf = sss_iobuf_init_empty(state, IOBUF_CHUNK, IOBUF_MAX);
-    if (state->outbuf == NULL) {
+    state->response = sss_iobuf_init_empty(state, TCURL_IOBUF_CHUNK, TCURL_IOBUF_MAX);
+    if (state->response == NULL) {
         ret = ENOMEM;
-        goto fail;
+        goto done;
     }
 
-    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);
+    ret = tcurl_set_option(tcurl_req, CURLOPT_PRIVATE, req);
+    if (ret != EOK) {
+        goto done;
+    }
 
-    /* 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_set_option(tcurl_req, CURLOPT_TIMEOUT, timeout);
+    if (ret != EOK) {
+        goto done;
     }
 
-    ret = tcurl_add_headers(state, headers);
+    ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEFUNCTION, tcurl_write_data);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Failed to set CURL headers [%d]: %s\n", ret, sss_strerror(ret));
-        goto fail;
+        goto done;
     }
 
-    ret = tcurl_set_options(state, req, req_type);
+    ret = tcurl_set_option(tcurl_req, CURLOPT_WRITEDATA, state->response);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Failed to set CURL options [%d]: %s\n", ret, sss_strerror(ret));
-        goto fail;
+        goto done;
     }
 
-    /* Pass control to the curl handling which will mark the request as
-     * done
-     */
-    curl_multi_add_handle(tctx->multi_handle, state->http_handle);
+    if (tcurl_req->body != NULL) {
+        ret = tcurl_set_option(tcurl_req, CURLOPT_READFUNCTION, tcurl_read_data);
+        if (ret != EOK) {
+            goto done;
+        }
 
-    return req;
+        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);
+    }
 
-fail:
-    tevent_req_error(req, ret);
-    tevent_req_post(req, ev);
     return req;
 }
 
-static int tcurl_http_cleanup_handle(TALLOC_CTX *ptr)
+static void tcurl_request_done(struct tevent_req *req,
+                               errno_t process_error,
+                               int response_code)
 {
-    struct tcurl_http_state *state = talloc_get_type(ptr, struct tcurl_http_state);
+    struct tcurl_request_state *state;
 
-    if (state == NULL) {
-        return 0;
+    DEBUG(SSSDBG_TRACE_FUNC, "TCURL request finished [%d]: %s\n",
+          process_error, sss_strerror(process_error));
+
+    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;
     }
 
-    /* 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;
+    state->response_code = response_code;
+
+    tevent_req_done(req);
+    return;
 }
 
-static errno_t tcurl_add_headers(struct tcurl_http_state *state,
-                                 const char *headers[])
+errno_t tcurl_request_recv(TALLOC_CTX *mem_ctx,
+                           struct tevent_req *req,
+                           struct sss_iobuf **_response,
+                           int *_response_code)
 {
-    if (headers == NULL) {
-        return EOK;
-    }
+    struct tcurl_request_state *state;
+    state = tevent_req_data(req, struct tcurl_request_state);
 
-    /* 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;
-        }
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (_response != NULL) {
+        *_response = talloc_steal(mem_ctx, state->response);
     }
 
-    /* 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;
+    if (_response_code != NULL) {
+        *_response_code = state->response_code;
     }
 
     return EOK;
 }
 
-static errno_t tcurl_set_common_options(struct tcurl_http_state *state,
-                                        struct tevent_req *req)
+static struct curl_slist *
+tcurl_add_header(struct curl_slist *slist, const char *header)
 {
-    CURLcode crv;
+    struct curl_slist *new;
 
-    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;
-    }
+    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);
+        }
 
-    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;
+        return NULL;
     }
 
-    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;
-    }
+    return new;
+}
 
-    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;
+static struct curl_slist *
+tcurl_construct_headers(const char **headers)
+{
+    struct curl_slist *slist = NULL;
+    int i;
+
+    if (headers == NULL || headers[0] == NULL) {
+        return NULL;
     }
 
-    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;
+    for (i = 0; headers[i] != NULL; i++) {
+        slist = tcurl_add_header(slist, headers[i]);
+        if (slist == NULL) {
+            return NULL;
         }
     }
 
-    return EOK;
+    /* 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.
+     */
+    return tcurl_add_header(slist, "Expect: ");
 }
 
-static errno_t tcurl_set_write_options(struct tcurl_http_state *state)
+static int
+tcurl_request_destructor(struct tcurl_request *tcurl_req)
 {
-    CURLcode crv;
+    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);
+    }
 
-    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;
+    if (tcurl_req->headers != NULL) {
+        curl_slist_free_all(tcurl_req->headers);
     }
 
-    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;
+    if (tcurl_req->curl_easy_handle != NULL) {
+        curl_easy_cleanup(tcurl_req->curl_easy_handle);
     }
 
-    return EOK;
+    return 0;
 }
 
-static errno_t tcurl_set_read_options(struct tcurl_http_state *state)
+static struct tcurl_request *
+tcurl_request_create(TALLOC_CTX *mem_ctx,
+                     const char *socket,
+                     const char *url,
+                     const char **headers,
+                     struct sss_iobuf *body)
 {
-    CURLcode crv;
+    struct tcurl_request *tcurl_req;
+    errno_t ret;
 
-    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;
+    tcurl_req = talloc_zero(mem_ctx, struct tcurl_request);
+    if (tcurl_req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+        return NULL;
     }
 
-    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;
+    if (url == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "URL cannot be NULL!\n");
+        ret = EINVAL;
+        goto done;
     }
 
-    return EOK;
-}
+    /* 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");
+        goto done;
+    }
 
-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;
+    tcurl_req->url = talloc_strdup(tcurl_req, url);
+    if (tcurl_req->url == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+        goto done;
+    }
 
-    ret = tcurl_set_common_options(state, req);
-    if (ret != EOK) {
-        return ret;
+    tcurl_req->socket = talloc_strdup(tcurl_req, socket);
+    if (socket != NULL && tcurl_req->socket == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+        goto done;
     }
 
-    ret = tcurl_set_write_options(state);
+    tcurl_req->headers = tcurl_construct_headers(headers);
+    tcurl_req->body = body;
+
+    talloc_set_destructor(tcurl_req, tcurl_request_destructor);
+
+    ret = tcurl_set_option(tcurl_req, CURLOPT_URL, url);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Failed to set write callbacks [%d]: %s\n",
-              ret, sss_strerror(ret));
-        return ret;
+        goto done;
     }
 
-    switch (req_type) {
-    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;
+    if (socket != NULL) {
+        ret = tcurl_set_option(tcurl_req, CURLOPT_UNIX_SOCKET_PATH, socket);
+        if (ret != EOK) {
+            goto done;
         }
+    }
 
-        /* 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;
+    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,
+                                 const char *url,
+                                 const char **headers,
+                                 struct sss_iobuf *body)
+{
+    struct tcurl_request *tcurl_req;
+    errno_t ret;
 
-        ret = tcurl_set_read_options(state);
+    tcurl_req = tcurl_request_create(mem_ctx, socket, 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) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Failed to set write callbacks [%d]: %s\n",
-                  ret, sss_strerror(ret));
-            return ret;
+            goto done;
         }
         break;
-    case TCURL_HTTP_GET:
-        /* GET just needs the write callbacks, nothing to do here.. */
+    case TCURL_HTTP_POST:
+        ret = tcurl_set_option(tcurl_req, CURLOPT_POST, 1L);
+        if (ret != EOK) {
+            goto done;
+        }
         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;
+        ret = tcurl_set_option(tcurl_req, CURLOPT_CUSTOMREQUEST, "DELETE");
+        if (ret != EOK) {
+            goto done;
         }
         break;
-    default:
-        return EFAULT;
     }
 
-    return EOK;
-}
+    ret = EOK;
 
-static size_t tcurl_http_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);
-
-    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);
+done:
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Failed to write data to buffer [%d]: %s\n", ret, sss_strerror(ret));
-        /* zero signifies an EOF */
-        return 0;
+        talloc_free(tcurl_req);
+        return NULL;
     }
 
-    return realsize;
+    return tcurl_req;
 }
 
-static size_t tcurl_http_read_data(void *ptr,
-                                   size_t size,
-                                   size_t nmemb,
-                                   void *userdata)
+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;
-    size_t readbytes;
-    struct sss_iobuf *inbuf = (struct sss_iobuf *) userdata;
 
-    if (inbuf == NULL) {
-        return CURL_READFUNC_ABORT;
+    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 = sss_iobuf_read(inbuf, size * nmemb, ptr, &readbytes);
+    ret = tcurl_set_option(tcurl_req, CURLOPT_SSL_VERIFYHOST, host);
     if (ret != EOK) {
-        return CURL_READFUNC_ABORT;
+        return ret;
     }
 
-    return readbytes;
+    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;
 }
 
-int tcurl_http_recv(TALLOC_CTX *mem_ctx,
-                    struct tevent_req *req,
-                    int *_http_code,
-                    struct sss_iobuf **_outbuf)
+errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
+                                  const char *cert,
+                                  const char *key)
 {
-    struct tcurl_http_state *state = tevent_req_data(req, struct tcurl_http_state);
+    errno_t ret;
 
-    TEVENT_REQ_RETURN_ON_ERROR(req);
+    if (cert == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "You must specify client certificate!\n");
+        return EINVAL;
+    }
 
-    if (_http_code != NULL) {
-        *_http_code = state->http_code;
+    ret = tcurl_set_option(tcurl_req, CURLOPT_SSLCERT, cert);
+    if (ret != EOK) {
+        return ret;
     }
 
-    if (_outbuf != NULL) {
-        *_outbuf = talloc_steal(mem_ctx, state->outbuf);
+    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 0;
+    return EOK;
 }
diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h
index de0601d..d68faf9 100644
--- a/src/util/tev_curl.h
+++ b/src/util/tev_curl.h
@@ -27,12 +27,15 @@
 
 #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_POST,
     TCURL_HTTP_DELETE,
 };
 
@@ -45,67 +48,134 @@ struct tcurl_ctx *tcurl_init(TALLOC_CTX *mem_ctx,
                              struct tevent_context *ev);
 
 /**
- * @brief Run a single asynchronous HTTP request.
+ * @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.
  *
- * Currently only UNIX sockets at socket_path are supported.
+ * @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.
  *
- * 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.
+ * @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.
  *
- * 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,
  *   };
  *
- * @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]  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
- * @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.
  *
- * @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_http_recv to read the error code.
+ * @returns A tcurl_request that can be later started with tcurl_request_send
+ * or NULL on error.
  *
  * @see tcurl_init
- * @see tcurl_http_recv
+ * @see tcurl_request_send
+ * @see tcurl_request_recv
  */
-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);
+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 Receive a result of a single asynchronous HTTP request.
+ * @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]  mem_ctx      The talloc context that owns the outbuf
- * @param[in]  req          The request previously obtained with
- *                          tcurl_http_send
- * @param[out] _http_code   The HTTP code that the transfer ended with
- * @param[out] _outbuf      The raw data the HTTP request returned
+ * @param[in]  tcurl_request
  *
- * @returns The error code of the curl request (not the HTTP code!)
+ * @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
  */
-int tcurl_http_recv(TALLOC_CTX *mem_ctx,
-                    struct tevent_req *req,
-                    int *_http_code,
-                    struct sss_iobuf **_outbuf);
+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 17388c9..888fe59 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -104,6 +104,10 @@ struct err_string error_to_str[] = {
     { "The secret payload size is too large" }, /* ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE */
     { "No authentication methode available" }, /* ERR_NO_AUTH_METHOD_AVAILABLE */
     { "Smartcard authentication not supported" }, /* ERR_SC_AUTH_NOT_SUPPORTED */
+    { "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 7aacad2..fa9afcf 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -126,6 +126,10 @@ enum sssd_errors {
     ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE,
     ERR_NO_AUTH_METHOD_AVAILABLE,
     ERR_SC_AUTH_NOT_SUPPORTED,
+    ERR_INVALID_CERT,
+    ERR_SSL_FAILURE,
+    ERR_UNABLE_TO_VERIFY_PEER,
+    ERR_UNABLE_TO_RESOLVE_HOST,
     ERR_LAST            /* ALWAYS LAST */
 };
 

From 604df98bc3e7883baefe4c663297b24506b899bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 24 Feb 2017 12:23:01 +0100
Subject: [PATCH 02/13] tcurl test: refactor so new options can be added more
 easily

Just to make the tool a little bit nicer and more flexible.
---
 src/tests/tcurl_test_tool.c | 325 +++++++++++++++++++++++++++-----------------
 1 file changed, 203 insertions(+), 122 deletions(-)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 18b4fa1..3c60e06 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -28,26 +28,39 @@
 
 struct tool_ctx {
     bool verbose;
-
-    errno_t error;
     bool done;
 
     size_t nreqs;
 };
 
+struct tool_options {
+    int debug;
+    int verbose;
+
+    enum tcurl_http_method method;
+    const char *socket_path;
+};
+
 static void request_done(struct tevent_req *req)
 {
-    int http_code;
+    struct tool_ctx *tool_ctx;
     struct sss_iobuf *outbuf;
-    struct tool_ctx *tool_ctx = tevent_req_callback_data(req,
-                                                         struct tool_ctx);
+    int http_code;
+    errno_t ret;
+
+    tool_ctx = tevent_req_callback_data(req, struct tool_ctx);
 
-    tool_ctx->error = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code);
+    ret = tcurl_request_recv(tool_ctx, req, &outbuf, &http_code);
     talloc_zfree(req);
 
-    if (tool_ctx->error != EOK) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed: %d\n", tool_ctx->error);
+    tool_ctx->nreqs--;
+    if (tool_ctx->nreqs == 0) {
         tool_ctx->done = true;
+    }
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed [%d]: %s\n",
+              ret, sss_strerror(ret));
         return;
     } else if (tool_ctx->verbose) {
         printf("Request HTTP code: %d\n", http_code);
@@ -55,167 +68,167 @@ static void request_done(struct tevent_req *req)
                (const char *) sss_iobuf_get_data(outbuf));
         talloc_zfree(outbuf);
     }
-
-    tool_ctx->nreqs--;
-    if (tool_ctx->nreqs == 0) {
-        tool_ctx->done = true;
-    }
 }
 
-int main(int argc, const char *argv[])
+static errno_t
+parse_options(poptContext pc, struct tool_options *opts)
 {
     int opt;
-    poptContext pc;
-
-    int pc_debug = 0;
-    int pc_verbose = 0;
-    const char *socket_path = NULL;
-    const char *extra_arg_ptr;
-
-    static const char *headers[] = {
-        "Content-type: application/octet-stream",
-        NULL,
-    };
-
-    struct poptOption long_options[] = {
-        POPT_AUTOHELP
-        { "debug", '\0', POPT_ARG_INT, &pc_debug, 0,
-          "The debug level to run with", NULL },
-        { "socket-path", 's', POPT_ARG_STRING, &socket_path, 0,
-          "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 },
-        { "post", 'o', POPT_ARG_NONE, NULL, 'p', "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_method method = TCURL_HTTP_GET;
-    struct tcurl_ctx *ctx;
-    struct tcurl_request *tcurl_req;
-    struct tool_ctx *tool_ctx;
-
-    const char *urls[MAXREQ] = { 0 };
-    struct sss_iobuf **inbufs;
-
-    size_t n_reqs = 0;
-
-    debug_prg_name = argv[0];
-    pc = poptGetContext(NULL, argc, argv, long_options, 0);
-    poptSetOtherOptionHelp(pc, "HTTPDATA");
 
     while ((opt = poptGetNextOpt(pc)) > 0) {
         switch (opt) {
         case 'g':
-            method = TCURL_HTTP_GET;
+            opts->method = TCURL_HTTP_GET;
             break;
         case 'p':
-            method = TCURL_HTTP_PUT;
+            opts->method = TCURL_HTTP_PUT;
             break;
         case 'o':
-            method = TCURL_HTTP_POST;
+            opts->method = TCURL_HTTP_POST;
             break;
         case 'd':
-            method = TCURL_HTTP_DELETE;
-            break;
-        case 'v':
-            pc_verbose = 1;
+            opts->method = TCURL_HTTP_DELETE;
             break;
         default:
             DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected option\n");
-            return 1;
+            return EINVAL;
         }
     }
 
-    DEBUG_CLI_INIT(pc_debug);
-
-    tool_ctx = talloc_zero(NULL, struct tool_ctx);
-    if (tool_ctx == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n");
-        return 1;
+    if (opt != -1) {
+        poptPrintUsage(pc, stderr, 0);
+        fprintf(stderr, "%s", poptStrerror(opt));
+        return EINVAL;
     }
 
-    inbufs = talloc_zero_array(tool_ctx, struct sss_iobuf *, MAXREQ);
-    if (inbufs == NULL) {
-        talloc_zfree(tool_ctx);
-        return 1;
+    return EOK;
+}
+
+static errno_t
+prepare_requests(TALLOC_CTX *mem_ctx,
+                 poptContext pc,
+                 struct tool_options *opts,
+                 struct tcurl_request ***_requests,
+                 size_t *_num_requests)
+{
+    struct tcurl_request **requests;
+    const char *arg;
+    const char *url;
+    struct sss_iobuf *body;
+    errno_t ret;
+    size_t i;
+
+    static const char *headers[] = {
+        "Content-type: application/octet-stream",
+        NULL,
+    };
+
+    requests = talloc_zero_array(mem_ctx, struct tcurl_request *, MAXREQ + 1);
+    if (requests == NULL) {
+        return ENOMEM;
     }
 
-    while ((extra_arg_ptr = poptGetArg(pc)) != NULL) {
-        switch(method) {
+    i = 0;
+    while ((arg = poptGetArg(pc)) != NULL) {
+        if (i >= MAXREQ) {
+            fprintf(stderr, _("Too many requests!\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        switch(opts->method) {
         case TCURL_HTTP_GET:
         case TCURL_HTTP_DELETE:
-            urls[n_reqs++] = extra_arg_ptr;
+            url = arg;
+            body = NULL;
             break;
         case TCURL_HTTP_PUT:
         case TCURL_HTTP_POST:
-            if (urls[n_reqs] == NULL) {
-                urls[n_reqs] = extra_arg_ptr;
-            } else {
-                inbufs[n_reqs] = sss_iobuf_init_readonly(
-                                              inbufs,
-                                              (uint8_t *) discard_const(extra_arg_ptr),
-                                              strlen(extra_arg_ptr));
-                if (inbufs[n_reqs] == NULL) {
-                    DEBUG(SSSDBG_CRIT_FAILURE, "Could not init input buffer\n");
-                    talloc_zfree(tool_ctx);
-                    return 1;
-                }
-                n_reqs++;
+            url = arg;
+
+            arg = poptGetArg(pc);
+            if (arg == NULL) {
+                body = NULL;
+                break;
+            }
+
+            body = sss_iobuf_init_readonly(requests,
+                                           discard_const_p(uint8_t, arg),
+                                           strlen(arg));
+            if (body == NULL) {
+                ret = ENOMEM;
+                goto done;
             }
             break;
         }
+
+        requests[i] = tcurl_http(requests, opts->method, opts->socket_path,
+                                 url, headers, body);
+        if (requests[i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        i++;
     }
 
-    if (opt != -1) {
-        poptPrintUsage(pc, stderr, 0);
-        fprintf(stderr, "%s", poptStrerror(opt));
-        talloc_zfree(tool_ctx);
-        return 1;
+    *_requests = requests;
+    *_num_requests = i;
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_free(requests);
     }
 
-    if (!socket_path) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Please specify the socket path\n");
-        poptPrintUsage(pc, stderr, 0);
-        talloc_zfree(tool_ctx);
-        return 1;
+    return ret;
+}
+
+static errno_t
+run_requests(struct tool_ctx *tool_ctx,
+             struct tcurl_request **requests)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct tcurl_ctx *tcurl_ctx;
+    struct tevent_context *ev;
+    struct tevent_req *req;
+    errno_t ret;
+    int i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
     }
 
-    tool_ctx->nreqs = n_reqs;
-    tool_ctx->verbose = !!pc_verbose;
+    if (requests == NULL || requests[0] == NULL) {
+        ret = EOK;
+        goto done;
+    }
 
-    ev = tevent_context_init(tool_ctx);
+    ev = tevent_context_init(tmp_ctx);
     if (ev == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tevent context\n");
-        talloc_zfree(tool_ctx);
-        return 1;
+        ret = ENOMEM;
+        goto done;
     }
 
-    ctx = tcurl_init(tool_ctx, ev);
-    if (ctx == NULL) {
+    tcurl_ctx = tcurl_init(tmp_ctx, ev);
+    if (tcurl_ctx == NULL) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Could not init tcurl context\n");
-        talloc_zfree(tool_ctx);
-        return 1;
+        ret = ENOMEM;
+        goto done;
     }
 
-    for (size_t i = 0; i < n_reqs; i++) {
-        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;
+    for (i = 0; requests[i] != NULL; i++) {
+        req = tcurl_request_send(tmp_ctx, ev, tcurl_ctx, requests[i], 10);
+        if (req == NULL) {
+            DEBUG(SSSDBG_FATAL_FAILURE, "Could not create tevent request\n");
+            ret = ENOMEM;
+            goto done;
         }
 
-        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;
-        }
         tevent_req_set_callback(req, request_done, tool_ctx);
     }
 
@@ -230,7 +243,75 @@ int main(int argc, const char *argv[])
         return 1;
     }
 
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+    struct tool_options opts = {0};
+    struct tool_ctx *tool_ctx;
+    struct tcurl_request **requests;
+    poptContext pc;
+    errno_t ret;
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        { "debug", '\0', POPT_ARG_INT, &opts.debug, 0, "The debug level to run with", NULL },
+        { "socket-path", 's', POPT_ARG_STRING, &opts.socket_path, 0, "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 },
+        { "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, &opts.verbose, '\0', "Print response code and body", NULL },
+        POPT_TABLEEND
+    };
+
+    pc = poptGetContext(NULL, argc, argv, long_options, 0);
+    poptSetOtherOptionHelp(pc, "[URL HTTPDATA]*");
+
+    tool_ctx = talloc_zero(NULL, struct tool_ctx);
+    if (tool_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = parse_options(pc, &opts);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to parse options [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    DEBUG_CLI_INIT(opts.debug);
+    tool_ctx->verbose = opts.verbose;
+
+    ret = prepare_requests(tool_ctx, pc, &opts, &requests, &tool_ctx->nreqs);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to prepare requests [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = run_requests(tool_ctx, requests);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to issue requests [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+
+done:
     talloc_free(tool_ctx);
     poptFreeContext(pc);
-    return 0;
+
+    if (ret != EOK) {
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
 }

From fd8e3c6224bdf4df4b6d98d0e5991eda6d2596e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 24 Feb 2017 12:23:22 +0100
Subject: [PATCH 03/13] tcurl test: add support for raw output

---
 src/tests/tcurl_test_tool.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 3c60e06..a24b505 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -36,6 +36,7 @@ struct tool_ctx {
 struct tool_options {
     int debug;
     int verbose;
+    int raw;
 
     enum tcurl_http_method method;
     const char *socket_path;
@@ -169,6 +170,13 @@ prepare_requests(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
+        if (opts->raw) {
+            ret = tcurl_req_enable_rawoutput(requests[i]);
+            if (ret != EOK) {
+                goto done;
+            }
+        }
+
         i++;
     }
 
@@ -266,6 +274,7 @@ int main(int argc, const char *argv[])
         { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", 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 },
+        { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL },
         { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
         POPT_TABLEEND
     };

From 7273d0e8dffd8492ffa3ed5abe0417bc3a845ee1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Mon, 27 Feb 2017 12:58:06 +0100
Subject: [PATCH 04/13] tcurl test: add support for tls settings

---
 src/tests/tcurl_test_tool.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index a24b505..9310c23 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -37,9 +37,14 @@ struct tool_options {
     int debug;
     int verbose;
     int raw;
+    int tls;
+    int verify_peer;
+    int verify_host;
 
     enum tcurl_http_method method;
     const char *socket_path;
+    const char *capath;
+    const char *cacert;
 };
 
 static void request_done(struct tevent_req *req)
@@ -177,6 +182,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
             }
         }
 
+        if (opts->tls) {
+            ret = tcurl_req_verify_peer(requests[i], opts->capath, opts->cacert,
+                                        opts->verify_peer, opts->verify_host);
+            if (ret != EOK) {
+                goto done;
+            }
+        }
+
         i++;
     }
 
@@ -276,6 +289,12 @@ int main(int argc, const char *argv[])
         { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
         { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL },
         { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
+        /* TLS */
+        { "tls", '\0', POPT_ARG_NONE, &opts.tls, '\0', "Enable TLS", NULL },
+        { "verify-peer", '\0', POPT_ARG_NONE, &opts.verify_peer, '\0', "Verify peer when TLS is enabled", NULL },
+        { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL },
+        { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL },
+        { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL },
         POPT_TABLEEND
     };
 

From 7fbe87cb2afb50fd32c79c12760cee9b56a368e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 28 Feb 2017 13:32:31 +0100
Subject: [PATCH 05/13] tcurl: add support for http basic auth

---
 src/tests/tcurl_test_tool.c | 14 ++++++++++++++
 src/util/tev_curl.c         | 24 ++++++++++++++++++++++++
 src/util/tev_curl.h         | 15 +++++++++++++++
 3 files changed, 53 insertions(+)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 9310c23..908d332 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -45,6 +45,9 @@ struct tool_options {
     const char *socket_path;
     const char *capath;
     const char *cacert;
+
+    const char *username;
+    const char *password;
 };
 
 static void request_done(struct tevent_req *req)
@@ -190,6 +193,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
             }
         }
 
+        if (opts->username != NULL && opts->password != NULL) {
+            ret = tcurl_req_http_basic_auth(requests[i], opts->username,
+                                            opts->password);
+            if (ret != EOK) {
+                goto done;
+            }
+        }
+
         i++;
     }
 
@@ -295,6 +306,9 @@ int main(int argc, const char *argv[])
         { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL },
         { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL },
         { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL },
+        /* BASIC AUTH */
+        { "username", '\0', POPT_ARG_STRING, &opts.username, '\0', "Username for basic authentication", NULL },
+        { "password", '\0', POPT_ARG_STRING, &opts.password, '\0', "Password for basic authentication", NULL },
         POPT_TABLEEND
     };
 
diff --git a/src/util/tev_curl.c b/src/util/tev_curl.c
index fac91eb..52fbd46 100644
--- a/src/util/tev_curl.c
+++ b/src/util/tev_curl.c
@@ -1035,3 +1035,27 @@ errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
 
     return EOK;
 }
+
+errno_t tcurl_req_http_basic_auth(struct tcurl_request *tcurl_req,
+                                  const char *username,
+                                  const char *password)
+{
+    errno_t ret;
+
+    ret = tcurl_set_option(tcurl_req, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    ret = tcurl_set_option(tcurl_req, CURLOPT_USERNAME, username);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    ret = tcurl_set_option(tcurl_req, CURLOPT_PASSWORD, password);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    return EOK;
+}
diff --git a/src/util/tev_curl.h b/src/util/tev_curl.h
index d68faf9..fe75a80 100644
--- a/src/util/tev_curl.h
+++ b/src/util/tev_curl.h
@@ -178,4 +178,19 @@ errno_t tcurl_req_set_client_cert(struct tcurl_request *tcurl_req,
                                   const char *cert,
                                   const char *key);
 
+/**
+ * @brief Force HTTP basic authentication with @username and @password.
+ *
+ * @param[in]  tcurl_request
+ * @param[in]  username
+ * @param[in]  password
+ *
+ * @returns errno code
+ *
+ * @see tcurl_http
+ */
+errno_t tcurl_req_http_basic_auth(struct tcurl_request *tcurl_req,
+                                  const char *username,
+                                  const char *password);
+
 #endif /* __TEV_CURL_H */

From 4a736d09d4b100875cf08ea3f88ad4b18603664f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 10 Mar 2017 12:11:12 +0100
Subject: [PATCH 06/13] tcurl test: allow to set custom headers

---
 src/tests/tcurl_test_tool.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 908d332..93fcd6f 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -40,6 +40,7 @@ struct tool_options {
     int tls;
     int verify_peer;
     int verify_host;
+    const char **headers;
 
     enum tcurl_http_method method;
     const char *socket_path;
@@ -121,13 +122,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
                  size_t *_num_requests)
 {
     struct tcurl_request **requests;
+    struct sss_iobuf *body;
+    const char **headers;
     const char *arg;
     const char *url;
-    struct sss_iobuf *body;
     errno_t ret;
     size_t i;
 
-    static const char *headers[] = {
+    static const char *default_headers[] = {
         "Content-type: application/octet-stream",
         NULL,
     };
@@ -137,6 +139,8 @@ prepare_requests(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
+    headers = opts->headers == NULL ? default_headers : opts->headers;
+
     i = 0;
     while ((arg = poptGetArg(pc)) != NULL) {
         if (i >= MAXREQ) {
@@ -298,6 +302,7 @@ int main(int argc, const char *argv[])
         { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", 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 },
+        { "header", 'h', POPT_ARG_ARGV, &opts.headers, '\0', "Add HTTP header", NULL },
         { "raw", 'r', POPT_ARG_NONE, &opts.raw, '\0', "Print raw protocol output", NULL },
         { "verbose", 'v', POPT_ARG_NONE, &opts.verbose, '\0', "Print response code and body", NULL },
         /* TLS */

From e3c2249386499c3c58efb8d0cfd3aa861450f21c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Mon, 13 Mar 2017 13:30:48 +0100
Subject: [PATCH 07/13] tcurl test: add support for client certificate

---
 src/tests/tcurl_test_tool.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
index 93fcd6f..7c43ff5 100644
--- a/src/tests/tcurl_test_tool.c
+++ b/src/tests/tcurl_test_tool.c
@@ -47,6 +47,9 @@ struct tool_options {
     const char *capath;
     const char *cacert;
 
+    const char *clientcert;
+    const char *clientkey;
+
     const char *username;
     const char *password;
 };
@@ -197,6 +200,14 @@ prepare_requests(TALLOC_CTX *mem_ctx,
             }
         }
 
+        if (opts->clientcert != NULL) {
+            ret = tcurl_req_set_client_cert(requests[i], opts->clientcert,
+                                            opts->clientkey);
+            if (ret != EOK) {
+                goto done;
+            }
+        }
+
         if (opts->username != NULL && opts->password != NULL) {
             ret = tcurl_req_http_basic_auth(requests[i], opts->username,
                                             opts->password);
@@ -311,6 +322,8 @@ int main(int argc, const char *argv[])
         { "verify-host", '\0', POPT_ARG_NONE, &opts.verify_host, '\0', "Verify host when TLS is enabled", NULL },
         { "capath", '\0', POPT_ARG_STRING, &opts.capath, '\0', "Path to CA directory where peer certificate is stored", NULL },
         { "cacert", '\0', POPT_ARG_STRING, &opts.cacert, '\0', "Path to CA certificate", NULL },
+        { "clientcert", '\0', POPT_ARG_STRING, &opts.clientcert, '\0', "Path to client's certificate", NULL },
+        { "clientkey", '\0', POPT_ARG_STRING, &opts.clientkey, '\0', "Path to client's private key", NULL },
         /* BASIC AUTH */
         { "username", '\0', POPT_ARG_STRING, &opts.username, '\0', "Username for basic authentication", NULL },
         { "password", '\0', POPT_ARG_STRING, &opts.password, '\0', "Password for basic authentication", NULL },

From c85602f911f84bf0dd3ce76f226dd63e6304e091 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 22 Feb 2017 10:38:56 +0100
Subject: [PATCH 08/13] secrets: use tcurl in proxy provider

We switch from http-parser to libcurl for an http client. This gaves us many
features for free such as tls and http basic authentication support instead
of implementing it on our own.

Resolves:
https://pagure.io/SSSD/sssd/issue/3192
---
 Makefile.am                            |   3 +
 src/responder/secrets/providers.c      |  20 +++
 src/responder/secrets/proxy.c          | 245 ++++++++++++++++++++++-----------
 src/responder/secrets/secsrv_private.h |   5 +
 4 files changed, 189 insertions(+), 84 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 45b04de..6a06830 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1452,6 +1452,8 @@ sssd_secrets_SOURCES = \
     src/responder/secrets/local.c \
     src/responder/secrets/proxy.c \
     src/util/sss_sockets.c \
+    src/util/sss_iobuf.c \
+    src/util/tev_curl.c \
     $(SSSD_RESPONDER_OBJ) \
     $(SSSD_RESOLV_OBJ) \
     $(NULL)
@@ -1463,6 +1465,7 @@ sssd_secrets_LDADD = \
     $(SYSTEMD_DAEMON_LIBS) \
     $(CARES_LIBS) \
     $(SSSD_INTERNAL_LTLIBS) \
+    $(CURL_LIBS) \
     $(NULL)
 endif
 
diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
index eba555d..20b4ee3 100644
--- a/src/responder/secrets/providers.c
+++ b/src/responder/secrets/providers.c
@@ -22,6 +22,7 @@
 #include "responder/secrets/secsrv_private.h"
 #include "responder/secrets/secsrv_local.h"
 #include "responder/secrets/secsrv_proxy.h"
+#include "util/sss_iobuf.h"
 #include <jansson.h>
 
 static int sec_map_url_to_user_path(struct sec_req_ctx *secreq,
@@ -332,6 +333,25 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
     return EOK;
 }
 
+errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx,
+                             struct sec_data *reply,
+                             int response_code,
+                             struct sss_iobuf *response)
+{
+    DEBUG(SSSDBG_TRACE_LIBS, "HTTP reply %d\n", response_code);
+
+    reply->data = (char *)sss_iobuf_get_data(response);
+    reply->length = sss_iobuf_get_len(response);
+
+    talloc_steal(mem_ctx, reply->data);
+
+    if (reply->data == NULL) {
+        return EINVAL;
+    }
+
+    return EOK;
+}
+
 enum sec_http_status_codes sec_errno_to_http_status(errno_t err)
 {
     DEBUG(SSSDBG_TRACE_LIBS, "Request errno: %d\n", err);
diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
index 3ed03e6..342702d 100644
--- a/src/responder/secrets/proxy.c
+++ b/src/responder/secrets/proxy.c
@@ -23,10 +23,13 @@
 #include "util/crypto/sss_crypto.h"
 #include "resolv/async_resolv.h"
 #include "util/sss_sockets.h"
+#include "util/sss_iobuf.h"
+#include "util/tev_curl.h"
 
 struct proxy_context {
     struct resolv_ctx *resctx;
     struct confdb_ctx *cdb;
+    struct tcurl_ctx *tcurl;
 };
 
 enum proxy_auth_type {
@@ -216,103 +219,177 @@ int proxy_sec_map_url(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
     return EOK;
 }
 
-int proxy_sec_map_headers(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
-                          struct proxy_cfg *pcfg, char **req_headers)
+static errno_t proxy_http_append_header(TALLOC_CTX *mem_ctx,
+                                        const char *name,
+                                        const char *value,
+                                        const char ***_headers,
+                                        size_t *_num_headers)
 {
-    int ret;
+    const char **headers = *_headers;
+    size_t num_headers = *_num_headers;
+
+    num_headers++;
+    headers = talloc_realloc(mem_ctx, headers, const char *,
+                             num_headers + 1);
+    if (headers == NULL) {
+        return ENOMEM;
+    }
+
+    headers[num_headers - 1] = talloc_asprintf(headers, "%s: %s", name, value);
+    if (headers[num_headers - 1] == NULL) {
+        return ENOMEM;
+    }
+
+    headers[num_headers] = NULL;
+
+    *_headers = headers;
+    *_num_headers = num_headers;
+
+    return EOK;
+}
+
+static const char **
+proxy_http_create_headers(TALLOC_CTX *mem_ctx,
+                          struct sec_req_ctx *secreq,
+                          struct proxy_cfg *pcfg)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char **headers;
+    size_t num_headers;
+    errno_t ret;
+    int i, j;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return NULL;
+    }
+
+    headers = talloc_zero_array(tmp_ctx, const char *, 1);
+    if (headers == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    num_headers = 0;
+    for (i = 0; i < secreq->num_headers; i++) {
+        for (j = 0; pcfg->fwd_headers[j]; j++) {
+            if (strcasecmp(secreq->headers[i].name, pcfg->fwd_headers[j]) == 0) {
+                DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s: %s\n",
+                      secreq->headers[i].name, secreq->headers[i].value);
+
+                ret = proxy_http_append_header(tmp_ctx, secreq->headers[i].name,
+                                               secreq->headers[i].value,
+                                               &headers, &num_headers);
+                if (ret != EOK) {
+                    goto done;
+                }
 
-    for (int i = 0; i < secreq->num_headers; i++) {
-        bool forward = false;
-        for (int j = 0; pcfg->fwd_headers[j]; j++) {
-            if (strcasecmp(secreq->headers[i].name,
-                           pcfg->fwd_headers[j]) == 0) {
-                forward = true;
                 break;
             }
         }
-        if (forward) {
-            DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s:%s\n",
-                  secreq->headers[i].name, secreq->headers[i].value);
-
-            ret = sec_http_append_header(mem_ctx, req_headers,
-                                         secreq->headers[i].name,
-                                         secreq->headers[i].value);
-            if (ret) {
-                DEBUG(SSSDBG_CRIT_FAILURE,
-                      "Couldn't append header %s\n", secreq->headers[i].name);
-                return ret;
-            }
-        }
     }
 
     if (pcfg->auth_type == PAT_HEADER) {
-        DEBUG(SSSDBG_TRACE_LIBS,
-              "Forwarding header %s\n", pcfg->auth.header.name);
-
-        ret = sec_http_append_header(mem_ctx, req_headers,
-                                     pcfg->auth.header.name,
-                                     pcfg->auth.header.value);
-        if (ret) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Couldn't append header %s\n", pcfg->auth.header.name);
-            return ret;
+        DEBUG(SSSDBG_TRACE_LIBS, "Forwarding header %s\n",
+              pcfg->auth.header.name);
+
+        ret = proxy_http_append_header(tmp_ctx, pcfg->auth.header.name,
+                                       pcfg->auth.header.value,
+                                       &headers, &num_headers);
+        if (ret != EOK) {
+            goto done;
         }
     }
 
-    return EOK;
+    talloc_steal(mem_ctx, headers);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    if (ret != EOK) {
+        return NULL;
+    }
+
+    return headers;
 }
 
-static int proxy_http_create_request(TALLOC_CTX *mem_ctx,
-                                     struct sec_req_ctx *secreq,
-                                     struct proxy_cfg *pcfg,
-                                     const char *http_uri,
-                                     struct sec_data **http_req)
+static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
+                                         struct sec_req_ctx *secreq,
+                                         struct proxy_cfg *pcfg,
+                                         const char *url,
+                                         struct tcurl_request **_tcurl_req)
 {
-    struct sec_data *req;
-    int ret;
-
-    req = talloc_zero(mem_ctx, struct sec_data);
-    if (!req) return ENOMEM;
+    TALLOC_CTX *tmp_ctx;
+    struct tcurl_request *tcurl_req;
+    enum tcurl_http_method method;
+    struct sss_iobuf *body;
+    const char **headers;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+        return ENOMEM;
+    }
 
-    /* Request-Line */
-    req->data = talloc_asprintf(req, "%s %s HTTP/1.1\r\n",
-                                http_method_str(secreq->method), http_uri);
-    if (!req->data) {
+    headers = proxy_http_create_headers(tmp_ctx, secreq, pcfg);
+    if (headers == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct HTTP headers!\n");
         ret = ENOMEM;
         goto done;
     }
 
-    /* Headers */
-    ret = proxy_sec_map_headers(req, secreq, pcfg, &req->data);
-    if (ret) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't map headers\n");
+    body = sss_iobuf_init_readonly(tmp_ctx, (uint8_t*)secreq->body.data,
+                                   secreq->body.length);
+    if (body == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create HTTP body!\n");
+        ret = ENOMEM;
         goto done;
     }
 
-    /* CRLF separator before body */
-    req->data = talloc_strdup_append_buffer(req->data, "\r\n");
-
-    req->length = strlen(req->data);
+    switch (secreq->method) {
+    case HTTP_GET:
+        method = TCURL_HTTP_GET;
+        break;
+    case HTTP_PUT:
+        method = TCURL_HTTP_PUT;
+        break;
+    case HTTP_POST:
+        method = TCURL_HTTP_POST;
+        break;
+    case HTTP_DELETE:
+        method = TCURL_HTTP_DELETE;
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected HTTP method: %d\n",
+              secreq->method);
+        ret = EINVAL;
+        goto done;
+    }
 
-    /* Message-Body */
-    if (secreq->body.length > 0) {
-        req->data = talloc_realloc_size(req, req->data,
-                                        req->length + secreq->body.length);
-        if (!req->data) {
-            ret = ENOMEM;
-            goto done;
-        }
+    tcurl_req = tcurl_http(tmp_ctx, method, NULL, url, headers, body);
+    if (tcurl_req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create TCURL request!\n");
+        ret = ENOMEM;
+        goto done;
+    }
 
-        memcpy(&req->data[req->length],
-               secreq->body.data, secreq->body.length);
-        req->length += secreq->body.length;
+    /* TCURL will return response buffer also with headers. */
+    ret = tcurl_req_enable_rawoutput(tcurl_req);
+    if (ret != EOK) {
+        goto done;
     }
 
-    *http_req = req;
+    talloc_steal(tcurl_req, body);
+    *_tcurl_req = talloc_steal(mem_ctx, tcurl_req);
+
     ret = EOK;
 
 done:
-    if (ret) talloc_free(req);
+    talloc_free(tmp_ctx);
     return ret;
 }
 
@@ -911,8 +988,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
 {
     struct tevent_req *req, *subreq;
     struct proxy_secret_state *state;
+    struct tcurl_request *tcurl_req;
     struct proxy_context *pctx;
-    struct sec_data *http_req;
     char *http_uri;
     int ret;
 
@@ -942,9 +1019,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-
     ret = proxy_http_create_request(state, state->secreq, state->pcfg,
-                                    http_uri, &http_req);
+                                    http_uri, &tcurl_req);
     if (ret) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "proxy_http_create_request failed [%d]: %s\n",
@@ -952,10 +1028,8 @@ struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-
-    subreq = proxy_http_req_send(pctx, state, ev, state->secreq,
-                                 http_uri, http_req);
-    if (!subreq) {
+    subreq = tcurl_request_send(mem_ctx, ev, pctx->tcurl, tcurl_req, 5);
+    if (subreq == NULL) {
         ret = ENOMEM;
         goto done;
     }
@@ -981,32 +1055,30 @@ static void proxy_secret_req_done(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct proxy_secret_state *state;
-    struct proxy_http_reply *reply = NULL;
+    struct sss_iobuf *response;
+    int http_code;
     int ret;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct proxy_secret_state);
 
-    ret = proxy_http_req_recv(subreq, state, &reply);
+    ret = tcurl_request_recv(state, subreq, &response, &http_code);
     talloc_zfree(subreq);
 
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "proxy_http request failed [%d]: %s\n",
+        DEBUG(SSSDBG_OP_FAILURE, "proxy_http request failed [%d]: %s\n",
               ret, sss_strerror(ret));
         tevent_req_error(req, ret);
         return;
     }
 
-    ret = sec_http_reply_with_headers(state->secreq, &state->secreq->reply,
-                                      reply->status_code, reply->reason_phrase,
-                                      reply->headers, reply->num_headers,
-                                      &reply->body);
+    ret = sec_http_reply_iobuf(state->secreq, &state->secreq->reply,
+                               http_code, response);
     if (ret == EOK) {
         tevent_req_done(req);
     } else {
         DEBUG(SSSDBG_OP_FAILURE,
-              "sec_http_reply_with_headers request failed [%d]: %s\n",
+              "sec_http_reply_iobuf request failed [%d]: %s\n",
               ret, sss_strerror(ret));
         tevent_req_error(req, ret);
     }
@@ -1034,6 +1106,11 @@ int proxy_secrets_provider_handle(struct sec_ctx *sctx,
 
     pctx->resctx = sctx->resctx;
     pctx->cdb = sctx->rctx->cdb;
+    pctx->tcurl = tcurl_init(pctx, sctx->rctx->ev);
+    if (pctx->tcurl == NULL) {
+        talloc_free(pctx);
+        return ENOMEM;
+    }
 
     handle->context = pctx;
 
diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
index 1c3fbd8..307eda2 100644
--- a/src/responder/secrets/secsrv_private.h
+++ b/src/responder/secrets/secsrv_private.h
@@ -25,6 +25,7 @@
 #include "config.h"
 #include "responder/common/responder.h"
 #include "responder/secrets/secsrv.h"
+#include "util/sss_iobuf.h"
 #include <http_parser.h>
 
 struct sec_kvp {
@@ -121,6 +122,10 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
                                 int status_code, const char *reason,
                                 struct sec_kvp *headers, int num_headers,
                                 struct sec_data *body);
+errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx,
+                             struct sec_data *reply,
+                             int response_code,
+                             struct sss_iobuf *response);
 enum sec_http_status_codes sec_errno_to_http_status(errno_t err);
 
 int sec_json_to_simple_secret(TALLOC_CTX *mem_ctx,

From 264ed0fcfe584c0736187b5421e4b264f6ef3259 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 28 Feb 2017 14:14:40 +0100
Subject: [PATCH 09/13] secrets: remove http-parser code in proxy provider

We switche to libcurl in previous patch. This just removes the unused code.

Resolves:
https://pagure.io/SSSD/sssd/issue/3192
---
 src/responder/secrets/proxy.c | 581 ------------------------------------------
 1 file changed, 581 deletions(-)

diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
index 342702d..b646fe2 100644
--- a/src/responder/secrets/proxy.c
+++ b/src/responder/secrets/proxy.c
@@ -393,587 +393,6 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
-struct proxy_http_request {
-    struct sec_data *data;
-    size_t written;
-};
-
-struct proxy_http_reply {
-    http_parser parser;
-    bool complete;
-
-    int status_code;
-    char *reason_phrase;
-    struct sec_kvp *headers;
-    int num_headers;
-    struct sec_data body;
-
-    size_t received;
-};
-
-struct proxy_http_req_state {
-    struct tevent_context *ev;
-
-    char *proxyname;
-    int port;
-
-    struct resolv_hostent *hostent;
-    int hostidx;
-
-    int sd;
-    struct tevent_fd *fde;
-
-    struct proxy_http_request request;
-    struct proxy_http_reply *reply;
-};
-
-static int proxy_http_req_state_destroy(void *data);
-static void proxy_http_req_gethostname_done(struct tevent_req *subreq);
-static void proxy_http_req_connect_step(struct tevent_req *req);
-static void proxy_http_req_connect_done(struct tevent_req *subreq);
-static void proxy_fd_handler(struct tevent_context *ev, struct tevent_fd *fde,
-                             uint16_t flags, void *ptr);
-
-struct tevent_req *proxy_http_req_send(struct proxy_context *pctx,
-                                       TALLOC_CTX *mem_ctx,
-                                       struct tevent_context *ev,
-                                       struct sec_req_ctx *secreq,
-                                       const char *http_uri,
-                                       struct sec_data *http_req)
-{
-    struct proxy_http_req_state *state;
-    struct http_parser_url parsed;
-    struct tevent_req *req, *subreq;
-    int ret;
-
-    req = tevent_req_create(mem_ctx, &state, struct proxy_http_req_state);
-    if (!req) return NULL;
-
-    state->ev = ev;
-    state->request.data = http_req;
-    state->sd = -1;
-    talloc_set_destructor((TALLOC_CTX *)state,
-                          proxy_http_req_state_destroy);
-
-    /* STEP1: reparse URL to get hostname and port */
-    ret = http_parser_parse_url(http_uri, strlen(http_uri), 0, &parsed);
-    if (ret) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse URL [%s]: %d: %s\n",
-                                   http_uri, ret, sss_strerror(ret));
-        goto done;
-    }
-
-    if (!(parsed.field_set & (1 << UF_HOST))) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "No UF_HOST flag found\n");
-        ret = EINVAL;
-        goto done;
-    }
-    state->proxyname =
-        talloc_strndup(state,
-                       &http_uri[parsed.field_data[UF_HOST].off],
-                       parsed.field_data[UF_HOST].len);
-    if (!state->proxyname) {
-        ret = ENOMEM;
-        goto done;
-    }
-    DEBUG(SSSDBG_TRACE_LIBS, "proxy name: %s\n", state->proxyname);
-
-    if (parsed.field_set & (1 << UF_PORT)) {
-        state->port = parsed.port;
-    } else if (parsed.field_set & (1 << UF_SCHEMA)) {
-        uint16_t off = parsed.field_data[UF_SCHEMA].off;
-        uint16_t len = parsed.field_data[UF_SCHEMA].len;
-
-        if ((len == 5) &&
-            (strncmp("https", &http_uri[off], len) == 0)) {
-            state->port = 443;
-        } else if ((len == 4) &&
-                   (strncmp("http", &http_uri[off], len) == 0)) {
-            state->port = 80;
-        }
-    }
-    DEBUG(SSSDBG_TRACE_LIBS, "proxy port: %d\n", state->port);
-
-    /* STEP2: resolve hostname first */
-    subreq = resolv_gethostbyname_send(state, ev, pctx->resctx,
-                                       state->proxyname, IPV4_FIRST,
-                                       default_host_dbs);
-    if (subreq == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    tevent_req_set_callback(subreq, proxy_http_req_gethostname_done, req);
-
-    return req;
-
-done:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
-    tevent_req_post(req, ev);
-
-    return req;
-}
-
-static void proxy_http_req_gethostname_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req;
-    struct proxy_http_req_state *state;
-    int resolv_status;
-    int ret;
-
-    req = tevent_req_callback_data(subreq, struct tevent_req);
-    state = tevent_req_data(req, struct proxy_http_req_state);
-
-    ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL,
-                                    &state->hostent);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        if (ret == ENOENT) {
-            /* Empty result, just quit */
-            DEBUG(SSSDBG_TRACE_INTERNAL, "No hostent found\n");
-        } else {
-            DEBUG(SSSDBG_OP_FAILURE,
-                  "Could not resolve fqdn for this machine, error [%d]: %s, "
-                  "resolver returned: [%d]: %s\n", ret, strerror(ret),
-                  resolv_status, resolv_strerror(resolv_status));
-        }
-        goto done;
-    }
-
-    /* EOK */
-    DEBUG(SSSDBG_TRACE_INTERNAL, "Found fqdn: %s\n", state->hostent->name);
-
-    /* STEP3: connect to one of the servers */
-    proxy_http_req_connect_step(req);
-    return;
-
-done:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
-}
-
-static void proxy_http_req_connect_step(struct tevent_req *req)
-{
-    struct proxy_http_req_state *state;
-    struct sockaddr_storage *sockaddr;
-    char *ipaddr;
-    struct tevent_req *subreq;
-    int ret;
-
-    state = tevent_req_data(req, struct proxy_http_req_state);
-
-    if (!state->hostent->addr_list[state->hostidx]) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "No more addresses to try.\n");
-        ret = ERR_SEC_NO_PROXY;
-        goto done;
-    }
-
-    sockaddr = resolv_get_sockaddr_address_index(state, state->hostent,
-                                                 state->port, state->hostidx);
-    if (sockaddr == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "resolv_get_sockaddr_address() failed\n");
-        ret = EIO;
-        goto done;
-    }
-
-    if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
-        ipaddr = resolv_get_string_address_index(state, state->hostent,
-                                                 state->hostidx);
-        if (!ipaddr) {
-            ret = EFAULT;
-            goto done;
-        }
-        DEBUG(SSSDBG_TRACE_FUNC, "Connecting to %s:%d\n",
-              ipaddr, state->port);
-    }
-
-    /* increase idx for next attempt */
-    state->hostidx++;
-
-    subreq = sssd_async_socket_init_send(state, state->ev, sockaddr,
-                                         sizeof(struct sockaddr_storage),
-                                         SEC_NET_TIMEOUT);
-    if (!subreq) {
-        ret = EIO;
-        goto done;
-    }
-    tevent_req_set_callback(subreq, proxy_http_req_connect_done, req);
-    return;
-
-done:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
-}
-
-static void proxy_http_req_connect_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req;
-    struct proxy_http_req_state *state;
-    int ret;
-
-    req = tevent_req_callback_data(subreq, struct tevent_req);
-    state = tevent_req_data(req, struct proxy_http_req_state);
-
-    ret = sssd_async_socket_init_recv(subreq, &state->sd);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "sssd_async_socket_init request failed: [%d]: %s.\n",
-              ret, sss_strerror(ret));
-
-        /* try next server if any */
-        proxy_http_req_connect_step(req);
-        return;
-    }
-
-    /* EOK */
-    DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s\n", state->hostent->name);
-
-    state->fde = tevent_add_fd(state->ev, state, state->sd,
-                               TEVENT_FD_WRITE, proxy_fd_handler,
-                               req);
-    if (!state->fde) {
-        ret = EIO;
-        goto done;
-    }
-
-    return;
-
-done:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
-}
-
-
-int proxy_http_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
-                        struct proxy_http_reply **reply)
-{
-    struct proxy_http_req_state *state =
-                tevent_req_data(req, struct proxy_http_req_state);
-
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    *reply = talloc_move(mem_ctx, &state->reply);
-
-    return EOK;
-}
-
-static int proxy_http_req_state_destroy(void *data)
-{
-    struct proxy_http_req_state *state =
-        talloc_get_type(data, struct proxy_http_req_state);
-
-    if (!state) return 0;
-
-    if (state->sd != -1) {
-        DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd);
-        close(state->sd);
-        state->sd = -1;
-    }
-
-    return 0;
-}
-
-static int proxy_wire_send(int fd, struct proxy_http_request *req)
-{
-    struct sec_data data;
-    int ret;
-
-    data.data = req->data->data + req->written;
-    data.length = req->data->length - req->written;
-
-    ret = sec_send_data(fd, &data);
-    if (ret != EOK && ret != EAGAIN) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "sec_send_data failed [%d]: %s\n", ret, sss_strerror(ret));
-        return ret;
-    }
-
-    req->written = req->data->length - data.length;
-    return ret;
-}
-
-static void proxy_fd_send(void *data)
-{
-    struct proxy_http_req_state *state;
-    struct tevent_req * req;
-    int ret;
-
-    req = talloc_get_type(data, struct tevent_req);
-    state = tevent_req_data(req, struct proxy_http_req_state);
-
-    ret = proxy_wire_send(state->sd, &state->request);
-    if (ret == EAGAIN) {
-        /* not all data was sent, loop again */
-        return;
-    }
-    if (ret != EOK) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting!\n");
-        tevent_req_error(req, ret);
-        return;
-    }
-
-    /* ok all sent, wait for reply now */
-    TEVENT_FD_NOT_WRITEABLE(state->fde);
-    TEVENT_FD_READABLE(state->fde);
-    return;
-}
-
-static bool ph_received_data(struct proxy_http_reply *reply, size_t length)
-{
-    reply->received += length;
-    if (reply->received > SEC_REQUEST_MAX_SIZE) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Request too big, aborting!\n");
-        return true;
-    }
-    return false;
-}
-
-static void ph_append_string(TALLOC_CTX *memctx, char **dest,
-                             const char *src, size_t len)
-{
-    if (*dest) {
-        *dest = talloc_strndup_append_buffer(*dest, src, len);
-    } else {
-        *dest = talloc_strndup(memctx, src, len);
-    }
-}
-
-static int ph_on_message_begin(http_parser *parser)
-{
-    DEBUG(SSSDBG_TRACE_INTERNAL, "HTTP Message parsing begins\n");
-    return 0;
-}
-
-#if ((HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2))
-static int ph_on_status(http_parser *parser, const char *at, size_t length)
-{
-    struct proxy_http_reply *reply =
-        talloc_get_type(parser->data, struct proxy_http_reply);
-
-    if (ph_received_data(reply, length)) return -1;
-
-    ph_append_string(reply, &reply->reason_phrase, at, length);
-    if (!reply->reason_phrase) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to store reason phrase, aborting client!\n");
-        return -1;
-    }
-
-    return 0;
-}
-#endif
-
-static int ph_on_header_field(http_parser *parser,
-                              const char *at, size_t length)
-{
-    struct proxy_http_reply *reply =
-        talloc_get_type(parser->data, struct proxy_http_reply);
-    int n = reply->num_headers;
-
-    if (ph_received_data(reply, length)) return -1;
-
-    if (!reply->headers) {
-        reply->headers = talloc_zero_array(reply, struct sec_kvp, 10);
-    } else if ((n % 10 == 0) &&
-               (reply->headers[n - 1].value)) {
-        reply->headers = talloc_realloc(reply, reply->headers,
-                                        struct sec_kvp, n + 10);
-        if (reply->headers) {
-            memset(&reply->headers[n], 0, sizeof(struct sec_kvp) * 10);
-        }
-    }
-    if (!reply->headers) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to store headers, aborting client!\n");
-        return -1;
-    }
-
-    if (!n || reply->headers[n - 1].value) {
-        /* new field */
-        n++;
-    }
-    ph_append_string(reply->headers, &reply->headers[n - 1].name, at, length);
-    if (!reply->headers[n - 1].name) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to store header name, aborting client!\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int ph_on_header_value(http_parser *parser,
-                              const char *at, size_t length)
-{
-    struct proxy_http_reply *reply =
-        talloc_get_type(parser->data, struct proxy_http_reply);
-    int n = reply->num_headers;
-
-    if (ph_received_data(reply, length)) return -1;
-
-    if (!reply->headers) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Invalid headers pointer, aborting client!\n");
-        return -1;
-    }
-
-    if (reply->headers[n].name && !reply->headers[n].value) {
-        /* we increment on new value */
-        n = ++reply->num_headers;
-    }
-
-    ph_append_string(reply->headers, &reply->headers[n - 1].value, at, length);
-    if (!reply->headers[n - 1].value) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to store header value, aborting client!\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int ph_on_headers_complete(http_parser *parser)
-{
-    /* TODO: if message has no body we should return 1 */
-    return 0;
-}
-
-static int ph_on_body(http_parser *parser, const char *at, size_t length)
-{
-    struct proxy_http_reply *reply =
-        talloc_get_type(parser->data, struct proxy_http_reply);
-
-    if (ph_received_data(reply, length)) return -1;
-
-    /* FIXME: body may be binary */
-    ph_append_string(reply, &reply->body.data, at, length);
-    if (!reply->body.data) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to store body, aborting!\n");
-        return -1;
-    }
-    reply->body.length += length;
-
-    return 0;
-}
-
-static int ph_on_message_complete(http_parser *parser)
-{
-    struct proxy_http_reply *reply =
-        talloc_get_type(parser->data, struct proxy_http_reply);
-
-    reply->status_code = parser->status_code;
-    reply->complete = true;
-
-    return 0;
-}
-
-static http_parser_settings ph_callbacks = {
-    .on_message_begin = ph_on_message_begin,
-#if ((HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2))
-    .on_status = ph_on_status,
-#endif
-    .on_header_field = ph_on_header_field,
-    .on_header_value = ph_on_header_value,
-    .on_headers_complete = ph_on_headers_complete,
-    .on_body = ph_on_body,
-    .on_message_complete = ph_on_message_complete
-};
-
-static void proxy_fd_recv(void *data)
-{
-    char buffer[SEC_PACKET_MAX_RECV_SIZE];
-    struct sec_data packet = { buffer,
-                               SEC_PACKET_MAX_RECV_SIZE };
-    struct proxy_http_req_state *state;
-    struct tevent_req *req;
-    bool must_complete = false;
-    int ret;
-
-    req = talloc_get_type(data, struct tevent_req);
-    state = tevent_req_data(req, struct proxy_http_req_state);
-
-    if (!state->reply) {
-        /* A new reply */
-        state->reply = talloc_zero(state, struct proxy_http_reply);
-        if (!state->reply) {
-            DEBUG(SSSDBG_FATAL_FAILURE, "Failed to allocate reply, aborting!\n");
-            tevent_req_error(req, ENOMEM);
-            return;
-        }
-        http_parser_init(&state->reply->parser, HTTP_RESPONSE);
-        state->reply->parser.data = state->reply;
-    }
-
-    ret = sec_recv_data(state->sd, &packet);
-    switch (ret) {
-    case ENODATA:
-        DEBUG(SSSDBG_TRACE_ALL, "Server closed connection.\n");
-        /* if we got no content length and the request is not complete,
-         * then 0 length will indicate EOF to the parser, otherwise we
-         * have an error */
-        must_complete = true;
-        break;
-    case EAGAIN:
-        DEBUG(SSSDBG_TRACE_ALL,
-              "Interrupted before any data could be read, retry later\n");
-        return;
-    case EOK:
-        /* all fine */
-        break;
-    default:
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to receive data (%d, %s), aborting\n",
-              ret, sss_strerror(ret));
-        tevent_req_error(req, EIO);
-        return;
-    }
-
-    ret = http_parser_execute(&state->reply->parser, &ph_callbacks,
-                              packet.data, packet.length);
-    if (ret != packet.length) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Failed to parse request, aborting!\n");
-        tevent_req_error(req, EIO);
-        return;
-    }
-
-    if (!state->reply->complete) {
-        if (must_complete) {
-            tevent_req_error(req, EIO);
-        }
-        return;
-    }
-
-    /* do not read anymore, server is done sending */
-    TEVENT_FD_NOT_READABLE(state->fde);
-    tevent_req_done(req);
-}
-
-static void proxy_fd_handler(struct tevent_context *ev, struct tevent_fd *fde,
-                             uint16_t flags, void *data)
-{
-    if (flags & TEVENT_FD_READ) {
-        proxy_fd_recv(data);
-    } else if (flags & TEVENT_FD_WRITE) {
-        proxy_fd_send(data);
-    }
-}
-
 struct proxy_secret_state {
     struct tevent_context *ev;
     struct sec_req_ctx *secreq;

From 4a99f09eb9cd5dd9ef4ac7a53280298c008b3669 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 28 Feb 2017 11:47:32 +0100
Subject: [PATCH 10/13] secrets: allow to configure certificate check

Some users may want to use TLS with unverified peer (for example if
they use self-signed certificate) or if unverified hostname (if
certificate hostname does not match with the real hostname). On the
other side it may be useful to point to a directory containing custom
certificate authorities.

This patch add three new options to secrets responder:
verify_peer => peer's certificate must be valid
verify_host => hostnames must match
capath => path to directory containing CA certs
cacert => ca certificate
cert => client certificate
key => client private key

Resolves:
https://pagure.io/SSSD/sssd/issue/3192
---
 src/config/SSSDConfig/__init__.py.in |  6 +++
 src/config/cfg_rules.ini             |  6 +++
 src/config/etc/sssd.api.conf         |  6 +++
 src/man/sssd-secrets.5.xml           | 76 ++++++++++++++++++++++++++++++++++++
 src/responder/secrets/proxy.c        | 55 ++++++++++++++++++++++++++
 5 files changed, 149 insertions(+)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 03a1a43..3859fb8 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -135,6 +135,12 @@ option_strings = {
     'forward_headers': _('The list of the headers to forward to the Custodia server together with the request'),
     'username': _('The username to use when authenticating to a Custodia server using basic_auth'),
     'password': _('The password to use when authenticating to a Custodia server using basic_auth'),
+    'verify_peer': _('If true peer's certificate is verified if proxy_url uses https protocol'),
+    'verify_host': _('If false peer's certificate may contain different hostname then proxy_url when https protocol is used'),
+    'capath': _('Path to directory where certificate authority certificates are stored'),
+    'cacert': _('Path to file containing server's CA certificate'),
+    'cert': _('Path to file containing client's certificate'),
+    'key': _('Path to file containing client's private key'),
 
     # [provider]
     'id_provider' : _('Identity provider'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index c287328..aff2e0b 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -261,6 +261,12 @@ option = auth_header_value
 option = forward_headers
 option = username
 option = password
+option = verify_peer
+option = verify_host
+option = capath
+option = cacert
+option = cert
+option = key
 
 [rule/allowed_domain_options]
 validator = ini_allowed_options
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 08cecf0..5673ca5 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -112,6 +112,12 @@ auth_header_value = str, None, false
 forward_headers = list, None, false
 username = str, None, false
 password = str, None, false
+verify_peer = bool, None, false
+verify_host = bool, None, false
+capath = str, None, false
+cacert = str, None, false
+cert = str, None, false
+key = str, None, false
 
 [provider]
 #Available provider types
diff --git a/src/man/sssd-secrets.5.xml b/src/man/sssd-secrets.5.xml
index 80e9c40..44a86c3 100644
--- a/src/man/sssd-secrets.5.xml
+++ b/src/man/sssd-secrets.5.xml
@@ -273,6 +273,82 @@ systemctl enable sssd-secrets.service
                 </para>
                 </listitem>
             </varlistentry>
+            <varlistentry>
+                <term>verify_peer (boolean)</term>
+                <listitem>
+                <para>
+                    Whether peer's certificate should be verified and valid
+                    if HTTPS protocol is used with the proxy provider.
+                </para>
+                <para>
+                    Default: true
+                </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>verify_host (boolean)</term>
+                <listitem>
+                <para>
+                    Whether peer's hostname must match with hostname in
+                    its certificate if HTTPS protocol is used with the
+                    proxy provider.
+                </para>
+                <para>
+                    Default: true
+                </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>capath (string)</term>
+                <listitem>
+                <para>
+                    Path to directory containing stored certificate authority
+                    certificates. System default path is used if this option is
+                    not set.
+                </para>
+                <para>
+                    Default: not set
+                </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>cacert (string)</term>
+                <listitem>
+                <para>
+                    Path to file containing server's certificate authority
+                    certificate. If this option is not set then the CA's
+                    certificate is looked up in <quote>capath</quote>.
+                </para>
+                <para>
+                    Default: not set
+                </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>cert (string)</term>
+                <listitem>
+                <para>
+                    Path to file containing client's certificate if required
+                    by the server. This file may also contain private key or
+                    the private key may be in separate file set with
+                    <quote>key</quote>.
+                </para>
+                <para>
+                    Default: not set
+                </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>key (string)</term>
+                <listitem>
+                <para>
+                    Path to file containing client's private key.
+                </para>
+                <para>
+                    Default: not set
+                </para>
+                </listitem>
+            </varlistentry>
         </variablelist>
     </refsect1>
     <refsect1 id='restapi'>
diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
index b646fe2..59871b7 100644
--- a/src/responder/secrets/proxy.c
+++ b/src/responder/secrets/proxy.c
@@ -57,6 +57,13 @@ struct proxy_cfg {
         struct pat_basic_auth basic;
         struct pat_header header;
     } auth;
+
+    char *key;
+    char *cert;
+    char *cacert;
+    char *capath;
+    bool verify_peer;
+    bool verify_host;
 };
 
 static int proxy_get_config_string(struct proxy_context *pctx,
@@ -127,6 +134,38 @@ static int proxy_sec_get_cfg(struct proxy_context *pctx,
         }
     }
 
+    ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_peer",
+                          true, &cfg->verify_peer);
+    if (ret) goto done;
+    DEBUG(SSSDBG_CONF_SETTINGS, "verify_peer: %s\n",
+          (&cfg->verify_peer ? "true" : "false"));
+
+    ret = confdb_get_bool(pctx->cdb, secreq->cfg_section, "verify_host",
+                              true, &cfg->verify_host);
+    if (ret) goto done;
+    DEBUG(SSSDBG_CONF_SETTINGS, "verify_host: %s\n",
+          (&cfg->verify_host ? "true" : "false"));
+
+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
+                                  "capath", &cfg->capath);
+    if (ret) goto done;
+    DEBUG(SSSDBG_CONF_SETTINGS, "capath: %s\n", cfg->capath);
+
+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
+                                  "cacert", &cfg->cacert);
+    if (ret) goto done;
+    DEBUG(SSSDBG_CONF_SETTINGS, "cacert: %s\n", cfg->cacert);
+
+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
+                                  "cert", &cfg->cert);
+    if (ret) goto done;
+    DEBUG(SSSDBG_CONF_SETTINGS, "cert: %s\n", cfg->cert);
+
+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
+                                  "key", &cfg->key);
+    if (ret) goto done;
+    DEBUG(SSSDBG_CONF_SETTINGS, "key: %s\n", cfg->key);
+
     ret = confdb_get_string_as_list(pctx->cdb, cfg, secreq->cfg_section,
                                     "forward_headers", &cfg->fwd_headers);
     if ((ret != 0) && (ret != ENOENT)) goto done;
@@ -383,6 +422,22 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    /* Set TLS settings to verify peer.
+     * This has no effect for HTTP protocol so we can set it anyway. */
+    ret = tcurl_req_verify_peer(tcurl_req, pcfg->capath, pcfg->cacert,
+                                pcfg->verify_peer, pcfg->verify_host);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* Set clien's certificate if required. */
+    if (pcfg->cert != NULL) {
+        ret = tcurl_req_set_client_cert(tcurl_req, pcfg->cert, pcfg->key);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
     talloc_steal(tcurl_req, body);
     *_tcurl_req = talloc_steal(mem_ctx, tcurl_req);
 

From b1b32b94399d58bbce63755e4d8995fb048723b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 28 Feb 2017 13:58:20 +0100
Subject: [PATCH 11/13] secrets: support HTTP basic authentication with proxy
 provider

Even though configuration options auth_type = basic, username and password
are read they were not used anywhere prior this patch.
---
 src/responder/secrets/proxy.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
index 59871b7..8e5d9e7 100644
--- a/src/responder/secrets/proxy.c
+++ b/src/responder/secrets/proxy.c
@@ -438,6 +438,15 @@ static errno_t proxy_http_create_request(TALLOC_CTX *mem_ctx,
         }
     }
 
+    /* Set basic authentication if required. */
+    if (pcfg->auth_type == PAT_BASIC_AUTH) {
+        ret = tcurl_req_http_basic_auth(tcurl_req, pcfg->auth.basic.username,
+                                        pcfg->auth.basic.password);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
     talloc_steal(tcurl_req, body);
     *_tcurl_req = talloc_steal(mem_ctx, tcurl_req);
 

From c757a20a9c0b5ba9381681cb5dff4258a4459435 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 15 Mar 2017 13:27:59 +0100
Subject: [PATCH 12/13] secrets: fix debug message

---
 src/responder/secrets/secsrv_cmd.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/responder/secrets/secsrv_cmd.c b/src/responder/secrets/secsrv_cmd.c
index 70679ec..b88680c 100644
--- a/src/responder/secrets/secsrv_cmd.c
+++ b/src/responder/secrets/secsrv_cmd.c
@@ -451,7 +451,8 @@ int sec_send_data(int fd, struct sec_data *data)
 
     data->length -= len;
     data->data += len;
-    DEBUG(SSSDBG_TRACE_INTERNAL, "sent %zu bytes\n", data->length);
+    DEBUG(SSSDBG_TRACE_INTERNAL, "sent %zu bytes, %zu bytes remaining\n",
+          len, data->length);
     return EOK;
 }
 

From 582068cfa3349fffc6bef835bb45e7f0a581c99b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 15 Mar 2017 15:15:08 +0100
Subject: [PATCH 13/13] secrets: always add Content-Length header

If custodia server does not reply with Content-Length header, curl may
wait for non-existing body of http reply if such body does not exist
(for example during POST operation when creating a container).
---
 src/responder/secrets/providers.c | 72 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 68 insertions(+), 4 deletions(-)

diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
index 20b4ee3..df3e439 100644
--- a/src/responder/secrets/providers.c
+++ b/src/responder/secrets/providers.c
@@ -333,20 +333,84 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
     return EOK;
 }
 
+static errno_t
+sec_http_iobuf_split(struct sss_iobuf *response,
+                     const char **headers,
+                     const char **body)
+{
+    const char *data = (const char *)sss_iobuf_get_data(response);
+    char *delim;
+
+    /* The last header ends with \r\n and then comes \r\n again as a separator
+     * of body from headers. We can use this to find this point. */
+    delim = strstr(data, "\r\n\r\n");
+    if (delim == NULL) {
+        return EINVAL;
+    }
+
+    /* Skip to the body delimiter. */
+    delim = delim + sizeof("\r\n") - 1;
+
+    /* Replace \r\n with zeros turning data into:
+     * from HEADER\r\nBODY into HEADER\0\0BODY format. */
+    delim[0] = '\0';
+    delim[1] = '\0';
+
+    /* Split the buffer. */
+    *headers = data;
+    *body = delim + 2;
+
+    return 0;
+}
+
+static const char *
+sec_http_iobuf_add_content_length(TALLOC_CTX *mem_ctx,
+                                  const char *headers,
+                                  size_t body_len)
+{
+    /* If Content-Length is already present we do nothing. */
+    if (strstr(headers, "Content-Length:") != NULL) {
+        return headers;
+    }
+
+    return talloc_asprintf(mem_ctx, "%sContent-Length: %zu\r\n",
+                           headers, body_len);
+}
+
 errno_t sec_http_reply_iobuf(TALLOC_CTX *mem_ctx,
                              struct sec_data *reply,
                              int response_code,
                              struct sss_iobuf *response)
 {
+    const char *headers;
+    const char *body;
+    size_t body_len;
+    errno_t ret;
+
     DEBUG(SSSDBG_TRACE_LIBS, "HTTP reply %d\n", response_code);
 
-    reply->data = (char *)sss_iobuf_get_data(response);
-    reply->length = sss_iobuf_get_len(response);
+    ret = sec_http_iobuf_split(response, &headers, &body);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Unexpected HTTP reply, "
+              "returning what we got from server\n");
+        reply->data = (char *)sss_iobuf_get_data(response);
+        reply->length = sss_iobuf_get_len(response);
 
-    talloc_steal(mem_ctx, reply->data);
+        return EOK;
+    }
 
+    /* Add Content-Length header if not present so client does not await
+     * not-existing incoming data. */
+    body_len = strlen(body);
+    headers = sec_http_iobuf_add_content_length(mem_ctx, headers, body_len);
+    if (headers == NULL) {
+        return ENOMEM;
+    }
+
+    reply->length = strlen(headers) + sizeof("\r\n") - 1 + body_len;
+    reply->data = talloc_asprintf(mem_ctx, "%s\r\n%s", headers, body);
     if (reply->data == NULL) {
-        return EINVAL;
+        return ENOMEM;
     }
 
     return EOK;
