Blob Blame History Raw
From a53c4afd13d92572b8c0ebb93d0dbe3f7c7bc680 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 83/90] 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

Reviewed-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked from commit df99d709c8cbef3c378c111944d83b7345e4c1ea)
---
 Makefile.am                            |   3 +
 src/responder/secrets/providers.c      |  20 +++
 src/responder/secrets/proxy.c          | 246 ++++++++++++++++++++++-----------
 src/responder/secrets/secsrv_private.h |   5 +
 4 files changed, 191 insertions(+), 83 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 573b37c52fdeab1add4ea057e1e1844ea4d348a5..4a414f77df999b8b1d81f663fcc18dbd2d6d2dc4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1486,6 +1486,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)
@@ -1497,6 +1499,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 94831c73036d269addca45c0117811a2c68873fd..80a443d91135447ec8ce8d424b692a6d7e26a907 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>
 
 typedef int (*url_mapper_fn)(struct sec_req_ctx *secreq,
@@ -387,6 +388,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 3ed03e6086d0de0f6f80de227ffc65ef4067db4f..fe2f0134e233d9a98f499fe563abe0af69762514 100644
--- a/src/responder/secrets/proxy.c
+++ b/src/responder/secrets/proxy.c
@@ -23,10 +23,15 @@
 #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"
+
+#define SEC_PROXY_TIMEOUT 5
 
 struct proxy_context {
     struct resolv_ctx *resctx;
     struct confdb_ctx *cdb;
+    struct tcurl_ctx *tcurl;
 };
 
 enum proxy_auth_type {
@@ -216,103 +221,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;
-
-    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;
+    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;
+                }
+
                 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);
+        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;
+        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;
+    TALLOC_CTX *tmp_ctx;
+    struct tcurl_request *tcurl_req;
+    enum tcurl_http_method method;
+    struct sss_iobuf *body;
+    const char **headers;
+    errno_t ret;
 
-    req = talloc_zero(mem_ctx, struct sec_data);
-    if (!req) return ENOMEM;
+    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 +990,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 +1021,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 +1030,9 @@ 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,
+                                SEC_PROXY_TIMEOUT);
+    if (subreq == NULL) {
         ret = ENOMEM;
         goto done;
     }
@@ -981,32 +1058,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 +1109,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 a8544f656517a17fe4576247779bff4850beaf97..2e68628f61a0a8e79cd48fb5a510221e6fc36c70 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 {
@@ -129,6 +130,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,
-- 
2.9.3