From 20dea10d129bda6512dfe412944b5fb78441c077 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Wed, 24 Feb 2016 14:45:03 -0500
Subject: [PATCH 12/15] Secrets: Add Proxy backend

Signed-off-by: Simo Sorce <simo@redhat.com>
---
 Makefile.am                                        |   7 +-
 src/responder/secrets/local.c                      |  77 +-
 src/responder/secrets/providers.c                  | 129 ++-
 src/responder/secrets/proxy.c                      | 977 +++++++++++++++++++++
 src/responder/secrets/secsrv.c                     |   7 +
 src/responder/secrets/secsrv.h                     |   7 +
 src/responder/secrets/secsrv_cmd.c                 |   8 +-
 src/responder/secrets/secsrv_local.h               |   4 +-
 src/responder/secrets/secsrv_private.h             |  24 +-
 .../secrets/{secsrv_local.h => secsrv_proxy.h}     |  10 +-
 10 files changed, 1183 insertions(+), 67 deletions(-)
 create mode 100644 src/responder/secrets/proxy.c
 copy src/responder/secrets/{secsrv_local.h => secsrv_proxy.h} (77%)

diff --git a/Makefile.am b/Makefile.am
index 1e1ebcef72e8a8eea1c079b6ee0faeb763131df4..839bf64e65ec8321a541057f7253929dc903067a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -595,6 +595,7 @@ dist_noinst_HEADERS = \
     src/responder/secrets/secsrv.h \
     src/responder/secrets/secsrv_private.h \
     src/responder/secrets/secsrv_local.h \
+    src/responder/secrets/secsrv_proxy.h \
     src/sbus/sbus_client.h \
     src/sbus/sssd_dbus.h \
     src/sbus/sssd_dbus_meta.h \
@@ -1251,12 +1252,16 @@ sssd_secrets_SOURCES = \
     src/responder/secrets/secsrv_cmd.c \
     src/responder/secrets/providers.c \
     src/responder/secrets/local.c \
-    $(SSSD_RESPONDER_OBJ)
+    src/responder/secrets/proxy.c \
+    src/util/sss_sockets.c \
+    $(SSSD_RESPONDER_OBJ) \
+    $(SSSD_RESOLV_OBJ)
 sssd_secrets_LDADD = \
     $(HTTP_PARSER_LIBS) \
     $(JANSSON_LIBS) \
     $(TDB_LIBS) \
     $(SSSD_LIBS) \
+    $(CARES_LIBS) \
     $(SSSD_INTERNAL_LTLIBS)
 endif
 
diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
index 94e1fac43c2d58fd7e54e6041b289684ec77d588..f95817d6805529e5fdeec30971183cf609ad872e 100644
--- a/src/responder/secrets/local.c
+++ b/src/responder/secrets/local.c
@@ -506,57 +506,56 @@ int generate_master_key(const char *filename, size_t size)
     return EOK;
 }
 
-/* FIXME: allocate on the responder context */
-static struct provider_handle local_secrets_handle = {
-    .fn = local_secret_req,
-    .context = NULL,
-};
-
-int local_secrets_provider_handle(TALLOC_CTX *mem_ctx,
-                                  struct provider_handle **handle)
+int local_secrets_provider_handle(struct sec_ctx *sctx,
+                                  struct provider_handle **out_handle)
 {
+    const char *mkey = SECRETS_DB_PATH"/.secrets.mkey";
+    const char *dbpath = SECRETS_DB_PATH"/secrets.ldb";
+    struct provider_handle *handle;
     struct local_context *lctx;
+    ssize_t size;
+    int mfd;
     int ret;
 
-    if (local_secrets_handle.context == NULL) {
-        const char *mkey = SECRETS_DB_PATH"/.secrets.mkey";
-        ssize_t size;
-        int mfd;
+    handle = talloc_zero(sctx, struct provider_handle);
+    if (!handle) return ENOMEM;
 
-        lctx = talloc_zero(NULL, struct local_context);
-        if (!lctx) return ENOMEM;
+    handle->name = "LOCAL";
+    handle->fn = local_secret_req;
 
-        lctx->ldb = ldb_init(lctx, NULL);
-        if (!lctx->ldb) return ENOMEM;
+    lctx = talloc_zero(handle, struct local_context);
+    if (!lctx) return ENOMEM;
 
-        ret = ldb_connect(lctx->ldb, SECRETS_DB_PATH"/secrets.ldb", 0, NULL);
-        if (ret != LDB_SUCCESS) {
-            talloc_free(lctx->ldb);
-            return EIO;
-        }
+    lctx->ldb = ldb_init(lctx, NULL);
+    if (!lctx->ldb) return ENOMEM;
 
-        lctx->master_key.data = talloc_size(lctx, MKEY_SIZE);
-        if (!lctx->master_key.data) return ENOMEM;
-        lctx->master_key.length = MKEY_SIZE;
+    ret = ldb_connect(lctx->ldb, dbpath, 0, NULL);
+    if (ret != LDB_SUCCESS) {
+        talloc_free(lctx->ldb);
+        return EIO;
+    }
 
+    lctx->master_key.data = talloc_size(lctx, MKEY_SIZE);
+    if (!lctx->master_key.data) return ENOMEM;
+    lctx->master_key.length = MKEY_SIZE;
+
+    ret = check_and_open_readonly(mkey, &mfd, 0, 0,
+                                  S_IFREG|S_IRUSR|S_IWUSR, 0);
+    if (ret == ENOENT) {
+        ret = generate_master_key(mkey, MKEY_SIZE);
+        if (ret) return EFAULT;
         ret = check_and_open_readonly(mkey, &mfd, 0, 0,
                                       S_IFREG|S_IRUSR|S_IWUSR, 0);
-        if (ret == ENOENT) {
-            ret = generate_master_key(mkey, MKEY_SIZE);
-            if (ret) return EFAULT;
-            ret = check_and_open_readonly(mkey, &mfd, 0, 0,
-                                          S_IFREG|S_IRUSR|S_IWUSR, 0);
-        }
-        if (ret) return EFAULT;
-
-        size = sss_atomic_io_s(mfd, lctx->master_key.data,
-                               lctx->master_key.length, true);
-        close(mfd);
-        if (size < 0 || size != lctx->master_key.length) return EIO;
-
-        local_secrets_handle.context = lctx;
     }
+    if (ret) return EFAULT;
 
-    *handle = &local_secrets_handle;
+    size = sss_atomic_io_s(mfd, lctx->master_key.data,
+                           lctx->master_key.length, true);
+    close(mfd);
+    if (size < 0 || size != lctx->master_key.length) return EIO;
+
+    handle->context = lctx;
+
+    *out_handle = handle;
     return EOK;
 }
diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
index 7b5245835fc53b2f876c4f5c62440b93c6f0a114..e83aedc9c544119334678b3bd404fa6e810b2eeb 100644
--- a/src/responder/secrets/providers.c
+++ b/src/responder/secrets/providers.c
@@ -21,6 +21,7 @@
 
 #include "responder/secrets/secsrv_private.h"
 #include "responder/secrets/secsrv_local.h"
+#include "responder/secrets/secsrv_proxy.h"
 #include <jansson.h>
 
 int sec_map_url_to_user_path(struct sec_req_ctx *secreq, char **mapped_path)
@@ -42,14 +43,15 @@ int sec_map_url_to_user_path(struct sec_req_ctx *secreq, char **mapped_path)
 int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
                     struct provider_handle **handle)
 {
+    struct sec_ctx *sctx;
     char **sections;
     char *def_provider;
     char *provider;
-    const char *upath;
-    int ulen;
     int num_sections;
     int ret;
 
+    sctx = talloc_get_type(secreq->cctx->rctx->pvt_ctx, struct sec_ctx);
+
     /* patch must start with /secrets/ for now */
     ret = strncasecmp(secreq->parsed_url.path,
                       SEC_BASEPATH, sizeof(SEC_BASEPATH) - 1);
@@ -75,12 +77,16 @@ int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
     if (ret != EOK) return ret;
 
     provider = def_provider;
-    upath = &secreq->parsed_url.path[sizeof(SEC_BASEPATH) - 1];
-    ulen = strlen(upath);
 
     // TODO order by length ?
-    for (int i = 0; ulen > 0 && i < num_sections; i++) {
-        if (strncmp(sections[i], upath, ulen) == 0) {
+    for (int i = 0; i < num_sections; i++) {
+        int slen;
+
+        secreq->base_path = talloc_asprintf(secreq, SEC_BASEPATH"%s/", sections[i]);
+        if (!secreq->base_path) return ENOMEM;
+        slen = strlen(secreq->base_path);
+
+        if (strncmp(secreq->base_path, secreq->mapped_path, slen) == 0) {
             char *secname;
 
             secname = talloc_asprintf(mem_ctx, CONFDB_SEC_CONF_ENTRY"/%s",
@@ -93,19 +99,31 @@ int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
             if (ret || !provider) return EIO;
 
             secreq->cfg_section = talloc_steal(secreq, secname);
+            if (!secreq->cfg_section) return ENOMEM;
             break;
         }
+        talloc_zfree(secreq->base_path);
     }
 
-    if (strcasecmp(provider, "LOCAL") == 0) {
-        return local_secrets_provider_handle(mem_ctx, handle);
-    } else {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Unknown provider type: %s\n", provider);
-        return EIO;
+    if (!secreq->base_path) secreq->base_path = SEC_BASEPATH;
+
+    ret = sec_get_provider(sctx, provider, handle);
+    if (ret == ENOENT) {
+        if (strcasecmp(provider, "LOCAL") == 0) {
+            ret = local_secrets_provider_handle(sctx, handle);
+        } else if (strcasecmp(provider, "PROXY") == 0) {
+            ret = proxy_secrets_provider_handle(sctx, handle);
+        } else {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "Unknown provider type: %s\n", provider);
+            ret = EIO;
+        }
+        if (ret == EOK) {
+            ret = sec_add_provider(sctx, *handle);
+        }
     }
 
-    return EINVAL;
+    return ret;
 }
 
 int sec_provider_recv(struct tevent_req *req) {
@@ -198,6 +216,58 @@ int sec_http_reply_with_body(TALLOC_CTX *mem_ctx, struct sec_data *reply,
     return EOK;
 }
 
+int sec_http_append_header(TALLOC_CTX *mem_ctx, char **dest,
+                           char *field, char *value)
+{
+    if (*dest == NULL) {
+        *dest = talloc_asprintf(mem_ctx, "%s: %s\r\n", field, value);
+    } else {
+        *dest = talloc_asprintf_append_buffer(*dest, "%s: %s\r\n",
+                                              field, value);
+    }
+    if (!*dest) return ENOMEM;
+
+    return EOK;
+}
+
+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)
+{
+    const char *reason_phrase = reason ? reason : "";
+    int ret;
+
+    /* Status-Line */
+    reply->data = talloc_asprintf(mem_ctx, "HTTP/1.1 %d %s\r\n",
+                                  status_code, reason_phrase);
+    if (!reply->data) return ENOMEM;
+
+    /* Headers */
+    for (int i = 0; i < num_headers; i++) {
+        ret = sec_http_append_header(mem_ctx, &reply->data,
+                                     headers[i].name, headers[i].value);
+        if (ret) return ret;
+    }
+
+    /* CRLF separator before body */
+    reply->data = talloc_strdup_append_buffer(reply->data, "\r\n");
+
+    reply->length = strlen(reply->data);
+
+    /* Message-Body */
+    if (body && body->length) {
+        reply->data = talloc_realloc(mem_ctx, reply->data, char,
+                                     reply->length + body->length);
+        if (!reply->data) return ENOMEM;
+
+        memcpy(&reply->data[reply->length], body->data, body->length);
+        reply->length += body->length;
+    }
+
+    return EOK;
+}
+
 enum sec_http_status_codes sec_errno_to_http_status(errno_t err)
 {
     switch (err) {
@@ -352,3 +422,36 @@ done:
     free(jsonized);
     return ret;
 }
+
+int sec_get_provider(struct sec_ctx *sctx, const char *name,
+                     struct provider_handle **out_handle)
+{
+    struct provider_handle *handle;
+
+    for (int i = 0; sctx->providers && sctx->providers[i]; i++) {
+        handle = sctx->providers[i];
+        if (strcasecmp(handle->name, name) != 0) {
+            continue;
+        }
+        *out_handle = handle;
+        return EOK;
+    }
+    return ENOENT;
+}
+
+int sec_add_provider(struct sec_ctx *sctx, struct provider_handle *handle)
+{
+    int c;
+
+    for (c = 0; sctx->providers && sctx->providers[c]; c++)
+        continue;
+
+    sctx->providers = talloc_realloc(sctx, sctx->providers,
+                                     struct provider_handle *, c + 2);
+    if (!sctx->providers) return ENOMEM;
+
+    sctx->providers[c] = talloc_steal(sctx, handle);
+    sctx->providers[c + 1] = NULL;
+
+    return EOK;
+}
diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c
new file mode 100644
index 0000000000000000000000000000000000000000..7a8318002fe3de1b063b20b8d2e96e01f8937a6a
--- /dev/null
+++ b/src/responder/secrets/proxy.c
@@ -0,0 +1,977 @@
+/*
+   SSSD
+
+   Secrets Responder
+
+   Copyright (C) Simo Sorce <ssorce@redhat.com>	2016
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "responder/secrets/secsrv_private.h"
+#include "util/crypto/sss_crypto.h"
+#include "resolv/async_resolv.h"
+#include "util/sss_sockets.h"
+
+struct proxy_context {
+    struct resolv_ctx *resctx;
+    struct confdb_ctx *cdb;
+};
+
+enum proxy_auth_type {
+    PAT_NONE = 0,
+    PAT_BASIC_AUTH = 1,
+    PAT_HEADER = 2,
+};
+
+struct pat_basic_auth {
+    char *username;
+    char *password;
+};
+
+struct pat_header {
+    char *name;
+    char *value;
+};
+
+struct proxy_cfg {
+    char *url;
+    char **fwd_headers;
+    int num_headers;
+    enum proxy_auth_type auth_type;
+    union {
+        struct pat_basic_auth basic;
+        struct pat_header header;
+    } auth;
+};
+
+static int proxy_get_config_string(struct proxy_context *pctx,
+                                   TALLOC_CTX *ctx, bool not_null,
+                                   struct sec_req_ctx *secreq,
+                                   const char *name, char **value)
+{
+    int ret;
+
+    ret = confdb_get_string(pctx->cdb, ctx,
+                            secreq->cfg_section, name, NULL, value);
+    if (not_null && (ret == 0) && (*value == NULL)) ret = EINVAL;
+    return ret;
+}
+
+static int proxy_sec_get_cfg(struct proxy_context *pctx,
+                             TALLOC_CTX *mem_ctx,
+                             struct sec_req_ctx *secreq,
+                             struct proxy_cfg **target)
+{
+    struct proxy_cfg *cfg;
+    char *auth_type;
+    int ret;
+
+    /* find matching remote and build the URI */
+    cfg = talloc_zero(mem_ctx, struct proxy_cfg);
+    if (!cfg) return ENOMEM;
+
+    ret = proxy_get_config_string(pctx, cfg, true, secreq,
+                                  "proxy_url", &cfg->url);
+    if (ret) goto done;
+
+    ret = proxy_get_config_string(pctx, cfg, false, secreq,
+                                  "auth_type", &auth_type);
+    if (ret) goto done;
+
+    if (auth_type) {
+        if (strcmp(auth_type, "basic_auth") == 0) {
+            cfg->auth_type = PAT_BASIC_AUTH;
+            ret = proxy_get_config_string(pctx, cfg, true, secreq, "username",
+                                          &cfg->auth.basic.username);
+            if (ret) goto done;
+            ret = proxy_get_config_string(pctx, cfg, true, secreq, "password",
+                                          &cfg->auth.basic.password);
+            if (ret) goto done;
+        } else if (strcmp(auth_type, "header") == 0) {
+            cfg->auth_type = PAT_HEADER;
+            ret = proxy_get_config_string(pctx, cfg, true, secreq,
+                                          "auth_header_name",
+                                          &cfg->auth.header.name);
+            if (ret) goto done;
+            ret = proxy_get_config_string(pctx, cfg, true, secreq,
+                                          "auth_header_value",
+                                          &cfg->auth.header.value);
+            if (ret) goto done;
+        } else {
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
+    ret = confdb_get_string_as_list(pctx->cdb, cfg, secreq->cfg_section,
+                                    "forward_headers", &cfg->fwd_headers);
+    if ((ret != 0) && (ret != ENOENT)) goto done;
+
+    while (cfg->fwd_headers && cfg->fwd_headers[cfg->num_headers]) {
+        cfg->num_headers++;
+    }
+
+    /* Always whitelist Content-Type and Content-Length */
+    cfg->fwd_headers = talloc_realloc(cfg, cfg->fwd_headers, char *,
+                                     cfg->num_headers + 3);
+    if (!cfg->fwd_headers) {
+        ret = ENOMEM;
+        goto done;
+    }
+    cfg->fwd_headers[cfg->num_headers] = talloc_strdup(cfg, "Content-Type");
+    if (!cfg->fwd_headers[cfg->num_headers]) {
+        ret = ENOMEM;
+        goto done;
+    }
+    cfg->num_headers++;
+    cfg->fwd_headers[cfg->num_headers] = talloc_strdup(cfg, "Content-Length");
+    if (!cfg->fwd_headers[cfg->num_headers]) {
+        ret = ENOMEM;
+        goto done;
+    }
+    cfg->num_headers++;
+    cfg->fwd_headers[cfg->num_headers] = NULL;
+    ret = EOK;
+
+done:
+    if (ret) talloc_free(cfg);
+    else *target = cfg;
+    return ret;
+}
+
+#define REQ_HAS_SCHEMA(secreq) ((secreq)->parsed_url.schema != NULL)
+#define REQ_HAS_HOST(secreq) ((secreq)->parsed_url.host != NULL)
+#define REQ_HAS_PORT(secreq) ((secreq)->parsed_url.port != 0)
+#define REQ_HAS_PATH(secreq) ((secreq)->parsed_url.path != NULL)
+#define REQ_HAS_QUERY(secreq) ((secreq)->parsed_url.query != NULL)
+#define REQ_HAS_FRAGMENT(secreq) ((secreq)->parsed_url.fragment != NULL)
+#define REQ_HAS_USERINFO(secreq) ((secreq)->parsed_url.userinfo != NULL)
+
+#define SECREQ_HAS_PORT(secreq) ((secreq)->parsed_url.port != 0)
+#define SECREQ_PORT(secreq) ((secreq)->parsed_url.port)
+
+#define SECREQ_HAS_PART(secreq, part) ((secreq)->parsed_url.part != NULL)
+#define SECREQ_PART(secreq, part) \
+    ((secreq)->parsed_url.part ? (secreq)->parsed_url.part : "")
+
+int proxy_sec_map_url(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
+                      struct proxy_cfg *pcfg, char **req_url)
+{
+    char port[6] = { 0 };
+    char *url;
+    int blen;
+    int ret;
+
+    if (SECREQ_HAS_PORT(secreq)) {
+        ret = snprintf(port, 6, "%d", SECREQ_PORT(secreq));
+        if (ret < 1 || ret > 5) return EINVAL;
+    }
+
+    blen = strlen(secreq->base_path);
+
+    url = talloc_asprintf(mem_ctx, "%s%s%s%s%s%s%s%s/%s%s%s%s%s",
+                          SECREQ_PART(secreq, schema),
+                          SECREQ_HAS_PART(secreq, schema) ? "://" : "",
+                          SECREQ_PART(secreq, userinfo),
+                          SECREQ_HAS_PART(secreq, userinfo) ? "@" : "",
+                          SECREQ_PART(secreq, host),
+                          SECREQ_HAS_PORT(secreq) ? ":" : "",
+                          SECREQ_HAS_PORT(secreq) ? port : "",
+                          pcfg->url, &secreq->mapped_path[blen],
+                          SECREQ_HAS_PART(secreq, query) ? "?" :"",
+                          SECREQ_PART(secreq, query),
+                          SECREQ_HAS_PART(secreq, fragment) ? "?" :"",
+                          SECREQ_PART(secreq, fragment));
+    if (!url) return ENOMEM;
+
+    *req_url = url;
+    return EOK;
+}
+
+int proxy_sec_map_headers(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
+                          struct proxy_cfg *pcfg, char **req_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;
+                break;
+            }
+        }
+        if (forward) {
+            ret = sec_http_append_header(mem_ctx, req_headers,
+                                         secreq->headers[i].name,
+                                         secreq->headers[i].value);
+            if (ret) return ret;
+        }
+    }
+
+    if (pcfg->auth_type == PAT_HEADER) {
+        ret = sec_http_append_header(mem_ctx, req_headers,
+                                     pcfg->auth.header.name,
+                                     pcfg->auth.header.value);
+        if (ret) return ret;
+    }
+
+    return EOK;
+}
+
+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)
+{
+    struct sec_data *req;
+    int ret;
+
+    req = talloc_zero(mem_ctx, struct sec_data);
+    if (!req) 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) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Headers */
+    ret = proxy_sec_map_headers(req, secreq, pcfg, &req->data);
+    if (ret) goto done;
+
+    /* CRLF separator before body */
+    req->data = talloc_strdup_append_buffer(req->data, "\r\n");
+
+    req->length = strlen(req->data);
+
+    /* 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;
+        }
+
+        memcpy(&req->data[req->length],
+               secreq->body.data, secreq->body.length);
+        req->length += secreq->body.length;
+    }
+
+    *http_req = req;
+    ret = EOK;
+
+done:
+    if (ret) talloc_free(req);
+    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;
+    const 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) goto done;
+
+    if (!(parsed.field_set & (1 << UF_HOST))) {
+        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;
+    }
+
+    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;
+        }
+    }
+
+    /* 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 = ENXIO;
+        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;
+    size_t 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) 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;
+    struct proxy_cfg *pcfg;
+};
+static void proxy_secret_req_done(struct tevent_req *subreq);
+
+struct tevent_req *proxy_secret_req(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    void *provider_ctx,
+                                    struct sec_req_ctx *secreq)
+{
+    struct tevent_req *req, *subreq;
+    struct proxy_secret_state *state;
+    struct proxy_context *pctx;
+    struct sec_data *http_req;
+    char *http_uri;
+    int ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct proxy_secret_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->secreq = secreq;
+
+    pctx = talloc_get_type(provider_ctx, struct proxy_context);
+    if (!pctx) {
+        ret = EIO;
+        goto done;
+    }
+
+    ret = proxy_sec_get_cfg(pctx, state, state->secreq, &state->pcfg);
+    if (ret) goto done;
+
+    ret = proxy_sec_map_url(state, secreq, state->pcfg, &http_uri);
+    if (ret) goto done;
+
+    ret = proxy_http_create_request(state, state->secreq, state->pcfg,
+                                    http_uri, &http_req);
+    if (ret) goto done;
+
+    subreq = proxy_http_req_send(pctx, state, ev, state->secreq,
+                                 http_uri, http_req);
+    if (!subreq) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, proxy_secret_req_done, req);
+
+    return req;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    } else {
+        /* shortcircuit the request here as all called functions are
+         * synchronous and final and no further subrequests have been
+         * made if we get here */
+        tevent_req_done(req);
+    }
+
+    return tevent_req_post(req, ev);
+}
+
+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;
+    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);
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        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);
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+}
+
+struct provider_handle proxy_secrets_handle = {
+    .fn = proxy_secret_req,
+    .context = NULL,
+};
+
+int proxy_secrets_provider_handle(struct sec_ctx *sctx,
+                                  struct provider_handle **out_handle)
+{
+    struct provider_handle *handle;
+    struct proxy_context *pctx;
+
+    handle = talloc_zero(sctx, struct provider_handle);
+    if (!handle) return ENOMEM;
+
+    handle->name = "PROXY";
+    handle->fn = proxy_secret_req;
+
+    pctx = talloc(handle, struct proxy_context);
+    if (!pctx) return ENOMEM;
+
+    pctx->resctx = sctx->resctx;
+    pctx->cdb = sctx->rctx->cdb;
+
+    handle->context = pctx;
+
+    *out_handle = handle;
+    return EOK;
+}
diff --git a/src/responder/secrets/secsrv.c b/src/responder/secrets/secsrv.c
index fa40b340ea8596f80dd661d0429c58b26343e50d..b87767d878c09fe3cd5f2aacab1d174ba04315ce 100644
--- a/src/responder/secrets/secsrv.c
+++ b/src/responder/secrets/secsrv.c
@@ -26,6 +26,7 @@
 
 #include "responder/common/responder.h"
 #include "responder/secrets/secsrv.h"
+#include "resolv/async_resolv.h"
 
 #define DEFAULT_SEC_FD_LIMIT 2048
 
@@ -115,6 +116,12 @@ static int sec_process_init(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
+    ret = resolv_init(sctx, ev, SEC_NET_TIMEOUT, &sctx->resctx);
+    if (ret != EOK) {
+        /* not fatal for now */
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize resolver library\n");
+    }
+
     /* Set up file descriptor limits */
     responder_set_fd_limit(sctx->fd_limit);
 
diff --git a/src/responder/secrets/secsrv.h b/src/responder/secrets/secsrv.h
index 91535ae2bf6155483b3b0dc789b704291009e6cb..ace30f86adb975d3c212685ebe70b4600c6f2776 100644
--- a/src/responder/secrets/secsrv.h
+++ b/src/responder/secrets/secsrv.h
@@ -30,9 +30,16 @@
 #include <tevent.h>
 #include <ldb.h>
 
+#define SEC_NET_TIMEOUT 5
+
+struct resctx;
+
 struct sec_ctx {
+    struct resolv_ctx *resctx;
     struct resp_ctx *rctx;
     int fd_limit;
+
+    struct provider_handle **providers;
 };
 
 int sec_connection_setup(struct cli_ctx *cctx);
diff --git a/src/responder/secrets/secsrv_cmd.c b/src/responder/secrets/secsrv_cmd.c
index bcbbb3b966471d66919c5b09d3f4ebdba8540834..adcb4ba48466df80814533b5348a45b106f5ea12 100644
--- a/src/responder/secrets/secsrv_cmd.c
+++ b/src/responder/secrets/secsrv_cmd.c
@@ -25,9 +25,6 @@
 #include "responder/secrets/secsrv.h"
 #include "responder/secrets/secsrv_private.h"
 
-#define SEC_REQUEST_MAX_SIZE 65536
-#define SEC_PACKET_MAX_RECV_SIZE 8192
-
 
 /* ##### Request Handling ##### */
 
@@ -376,7 +373,7 @@ static int sec_on_message_complete(http_parser *parser)
 
 /* ##### Communications ##### */
 
-static int sec_send_data(int fd, struct sec_data *data)
+int sec_send_data(int fd, struct sec_data *data)
 {
     size_t len;
 
@@ -424,7 +421,7 @@ static void sec_send(struct cli_ctx *cctx)
     return;
 }
 
-static int sec_recv_data(int fd, struct sec_data *data)
+int sec_recv_data(int fd, struct sec_data *data)
 {
     size_t len;
 
@@ -439,6 +436,7 @@ static int sec_recv_data(int fd, struct sec_data *data)
     }
 
     if (len == 0) {
+        data->length = 0;
         return ENODATA;
     }
 
diff --git a/src/responder/secrets/secsrv_local.h b/src/responder/secrets/secsrv_local.h
index d21b14953b0497a17f7b0b7730a3e1529f928aea..1ec1a343eff08b9377d1106035a99f8169e5d75e 100644
--- a/src/responder/secrets/secsrv_local.h
+++ b/src/responder/secrets/secsrv_local.h
@@ -22,7 +22,7 @@
 #ifndef __SECSRV_LOCAL_H__
 #define __SECSRV_LOCAL_H__
 
-int local_secrets_provider_handle(TALLOC_CTX *mem_ctx,
-                                  struct provider_handle **handle);
+int local_secrets_provider_handle(struct sec_ctx *sctx,
+                                  struct provider_handle **out_handle);
 
 #endif /* __SECSRV_LOCAL_H__ */
diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
index 4b55f645a140e7650750196f7706ba183765efb3..ce8cf821620ff25039d38e00eb91a6e4d24dd093 100644
--- a/src/responder/secrets/secsrv_private.h
+++ b/src/responder/secrets/secsrv_private.h
@@ -24,6 +24,7 @@
 
 #include "config.h"
 #include "responder/common/responder.h"
+#include "responder/secrets/secsrv.h"
 #include <http_parser.h>
 
 struct sec_kvp {
@@ -58,13 +59,14 @@ struct sec_url {
     char *host;
     int port;
     char *path;
+    char *query;
     char *fragment;
     char *userinfo;
-    char *query;
 };
 
 struct sec_req_ctx {
     struct cli_ctx *cctx;
+    const char *base_path;
     const char *cfg_section;
     bool complete;
 
@@ -73,7 +75,7 @@ struct sec_req_ctx {
     char *request_url;
     char *mapped_path;
 
-    uint8_t method;
+    enum http_method method;
     struct sec_url parsed_url;
     struct sec_kvp *headers;
     int num_headers;
@@ -88,9 +90,13 @@ typedef struct tevent_req *(*sec_provider_req_t)(TALLOC_CTX *mem_ctx,
                                                  struct sec_req_ctx *secreq);
 
 struct provider_handle {
+    const char *name;
     sec_provider_req_t fn;
     void *context;
 };
+int sec_get_provider(struct sec_ctx *sctx, const char *name,
+                     struct provider_handle **out_handle);
+int sec_add_provider(struct sec_ctx *sctx, struct provider_handle *handle);
 
 #define SEC_BASEPATH "/secrets/"
 
@@ -99,12 +105,19 @@ int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
                     struct provider_handle **handle);
 int sec_provider_recv(struct tevent_req *subreq);
 
+int sec_http_append_header(TALLOC_CTX *mem_ctx, char **dest,
+                           char *field, char *value);
+
 int sec_http_status_reply(TALLOC_CTX *mem_ctx, struct sec_data *reply,
                           enum sec_http_status_codes code);
 int sec_http_reply_with_body(TALLOC_CTX *mem_ctx, struct sec_data *reply,
                              enum sec_http_status_codes code,
                              const char *content_type,
                              struct sec_data *body);
+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);
 enum sec_http_status_codes sec_errno_to_http_status(errno_t err);
 
 int sec_json_to_simple_secret(TALLOC_CTX *mem_ctx,
@@ -118,4 +131,11 @@ int sec_array_to_json(TALLOC_CTX *mem_ctx,
                       char **array, int count,
                       char **output);
 
+/* secsrv_cmd.c */
+#define SEC_REQUEST_MAX_SIZE 65536
+#define SEC_PACKET_MAX_RECV_SIZE 8192
+
+int sec_send_data(int fd, struct sec_data *data);
+int sec_recv_data(int fd, struct sec_data *data);
+
 #endif /* __SECSRV_PRIVATE_H__ */
diff --git a/src/responder/secrets/secsrv_local.h b/src/responder/secrets/secsrv_proxy.h
similarity index 77%
copy from src/responder/secrets/secsrv_local.h
copy to src/responder/secrets/secsrv_proxy.h
index d21b14953b0497a17f7b0b7730a3e1529f928aea..cfce721b9071c61ef143e4b9c3cd16046dec19d9 100644
--- a/src/responder/secrets/secsrv_local.h
+++ b/src/responder/secrets/secsrv_proxy.h
@@ -19,10 +19,10 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef __SECSRV_LOCAL_H__
-#define __SECSRV_LOCAL_H__
+#ifndef __SECSRV_PROXY_H__
+#define __SECSRV_PROXY_H__
 
-int local_secrets_provider_handle(TALLOC_CTX *mem_ctx,
-                                  struct provider_handle **handle);
+int proxy_secrets_provider_handle(struct sec_ctx *sctx,
+                                  struct provider_handle **out_handle);
 
-#endif /* __SECSRV_LOCAL_H__ */
+#endif /* __SECSRV_PROXY_H__ */
-- 
2.5.5

