From bf87edbf14eee450193ede0ce87de9e77f93e880 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Thu, 7 Jan 2016 10:17:35 -0500
Subject: [PATCH 09/15] Secrets: Add initial responder code for secrets service

Start implementing the Secrets Service Reponder core.
This commit implements stratup and basic conenction handling and HTTP
parsing (using the http-parser library).

Signed-off-by: Simo Sorce <simo@redhat.com>

Related:
https://fedorahosted.org/sssd/ticket/2913
---
 Makefile.am                             |  18 ++
 contrib/sssd.spec.in                    |   1 +
 src/confdb/confdb.h                     |   4 +
 src/responder/common/responder.h        |   6 +
 src/responder/common/responder_common.c |  17 +-
 src/responder/secrets/secsrv.c          | 193 ++++++++++++
 src/responder/secrets/secsrv.h          |  42 +++
 src/responder/secrets/secsrv_cmd.c      | 507 ++++++++++++++++++++++++++++++++
 8 files changed, 776 insertions(+), 12 deletions(-)
 create mode 100644 src/responder/secrets/secsrv.c
 create mode 100644 src/responder/secrets/secsrv.h
 create mode 100644 src/responder/secrets/secsrv_cmd.c

diff --git a/Makefile.am b/Makefile.am
index 1559548d35aafeb524215d1fd9fe5ae31a5c49d6..504cca61d5c5dd8c875a64b4704975d065ccb6bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,6 +55,7 @@ sssdapiplugindir = $(sssddatadir)/sssd.api.d
 dbuspolicydir = $(sysconfdir)/dbus-1/system.d
 dbusservicedir = $(datadir)/dbus-1/system-services
 sss_statedir = $(localstatedir)/lib/sss
+runstatedir = @runstatedir@
 localedir = @localedir@
 nsslibdir = @nsslibdir@
 pamlibdir = @pammoddir@
@@ -159,6 +160,9 @@ dist_polkit_rules_DATA = contrib/sssd-pcsc.rules
 endif
 endif
 endif
+if BUILD_SECRETS
+sssdlibexec_PROGRAMS += sssd_secrets
+endif
 
 if BUILD_PAC_RESPONDER
     sssdlibexec_PROGRAMS += sssd_pac
@@ -438,6 +442,7 @@ AM_CPPFLAGS = \
     -DSSS_PAM_SOCKET_NAME=\"$(pipepath)/pam\" \
     -DSSS_PAC_SOCKET_NAME=\"$(pipepath)/pac\" \
     -DSSS_PAM_PRIV_SOCKET_NAME=\"$(pipepath)/private/pam\" \
+    -DSSS_SEC_SOCKET_NAME=\"$(runstatedir)/secrets.socket\" \
     -DSSS_SUDO_SOCKET_NAME=\"$(pipepath)/sudo\" \
     -DSSS_AUTOFS_SOCKET_NAME=\"$(pipepath)/autofs\" \
     -DSSS_SSH_SOCKET_NAME=\"$(pipepath)/ssh\" \
@@ -584,6 +589,7 @@ dist_noinst_HEADERS = \
     src/responder/ifp/ifp_users.h \
     src/responder/ifp/ifp_groups.h \
     src/responder/ifp/ifp_cache.h \
+    src/responder/secrets/secsrv.h \
     src/sbus/sbus_client.h \
     src/sbus/sssd_dbus.h \
     src/sbus/sssd_dbus_meta.h \
@@ -1227,6 +1233,18 @@ endif
 
 endif
 
+if BUILD_SECRETS
+sssd_secrets_SOURCES = \
+    src/responder/secrets/secsrv.c \
+    src/responder/secrets/secsrv_cmd.c \
+    $(SSSD_RESPONDER_OBJ)
+sssd_secrets_LDADD = \
+    $(HTTP_PARSER_LIBS) \
+    $(TDB_LIBS) \
+    $(SSSD_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS)
+endif
+
 sssd_be_SOURCES = \
     src/providers/data_provider_be.c \
     src/providers/data_provider_req.c \
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 10a428e36514fb3f48bbf9f98077cb1ea2e8ced9..979f390169b2af9ef6dd4fc0cf0f78e0a8885b59 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -719,6 +719,7 @@ done
 %{_libexecdir}/%{servicename}/sssd_nss
 %{_libexecdir}/%{servicename}/sssd_pam
 %{_libexecdir}/%{servicename}/sssd_autofs
+%{_libexecdir}/%{servicename}/sssd_secrets
 %{_libexecdir}/%{servicename}/sssd_ssh
 %{_libexecdir}/%{servicename}/sssd_sudo
 %{_libexecdir}/%{servicename}/p11_child
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 778d2ce81c3d08ae2be3893c3afafef28f057f85..a2779fe8269df61f814bc50687cc7a4fa3c04963 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -217,6 +217,10 @@
 #define CONFDB_PROXY_PAM_TARGET "proxy_pam_target"
 #define CONFDB_PROXY_FAST_ALIAS "proxy_fast_alias"
 
+/* Secrets Service */
+#define CONFDB_SEC_CONF_ENTRY "config/secrets"
+
+
 struct confdb_ctx;
 struct config_file_ctx;
 
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index b3a0c5a8de7c81c3e46f6ec418e17affbce40690..c9522e0762a55f43beb8cf1375e16b4d0fb6da3a 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -316,6 +316,12 @@ bool sss_utf8_check(const uint8_t *s, size_t n);
 
 void responder_set_fd_limit(rlim_t fd_limit);
 
+errno_t reset_idle_timer(struct cli_ctx *cctx);
+void idle_handler(struct tevent_context *ev,
+                  struct tevent_timer *te,
+                  struct timeval current_time,
+                  void *data);
+
 #define GET_DOMAINS_DEFAULT_TIMEOUT 60
 
 struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index f37cadd95c66228c2d05148a1d277fee311947da..deeb4e4c88d2a3bf6ff6be42fa496b11e4b20762 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -347,8 +347,6 @@ static void client_recv(struct cli_ctx *cctx)
     return;
 }
 
-static errno_t reset_idle_timer(struct cli_ctx *cctx);
-
 static void client_fd_handler(struct tevent_context *ev,
                               struct tevent_fd *fde,
                               uint16_t flags, void *ptr)
@@ -381,11 +379,6 @@ struct accept_fd_ctx {
     connection_setup_t connection_setup;
 };
 
-static void idle_handler(struct tevent_context *ev,
-                         struct tevent_timer *te,
-                         struct timeval current_time,
-                         void *data);
-
 static void accept_fd_handler(struct tevent_context *ev,
                               struct tevent_fd *fde,
                               uint16_t flags, void *ptr)
@@ -521,7 +514,7 @@ static void accept_fd_handler(struct tevent_context *ev,
     return;
 }
 
-static errno_t reset_idle_timer(struct cli_ctx *cctx)
+errno_t reset_idle_timer(struct cli_ctx *cctx)
 {
     struct timeval tv =
             tevent_timeval_current_ofs(cctx->rctx->client_idle_timeout, 0);
@@ -538,10 +531,10 @@ static errno_t reset_idle_timer(struct cli_ctx *cctx)
     return EOK;
 }
 
-static void idle_handler(struct tevent_context *ev,
-                         struct tevent_timer *te,
-                         struct timeval current_time,
-                         void *data)
+void idle_handler(struct tevent_context *ev,
+                  struct tevent_timer *te,
+                  struct timeval current_time,
+                  void *data)
 {
     /* This connection is idle. Terminate it */
     struct cli_ctx *cctx =
diff --git a/src/responder/secrets/secsrv.c b/src/responder/secrets/secsrv.c
new file mode 100644
index 0000000000000000000000000000000000000000..fa40b340ea8596f80dd661d0429c58b26343e50d
--- /dev/null
+++ b/src/responder/secrets/secsrv.c
@@ -0,0 +1,193 @@
+/*
+   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 "util/util.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <popt.h>
+
+#include "responder/common/responder.h"
+#include "responder/secrets/secsrv.h"
+
+#define DEFAULT_SEC_FD_LIMIT 2048
+
+static int sec_get_config(struct sec_ctx *sctx)
+{
+    int ret;
+
+    ret = confdb_get_int(sctx->rctx->cdb,
+                         CONFDB_SEC_CONF_ENTRY,
+                         CONFDB_SERVICE_FD_LIMIT,
+                         DEFAULT_SEC_FD_LIMIT,
+                         &sctx->fd_limit);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to get file descriptors limit\n");
+        goto fail;
+    }
+
+    ret = confdb_get_int(sctx->rctx->cdb, sctx->rctx->confdb_service_path,
+                         CONFDB_RESPONDER_CLI_IDLE_TIMEOUT,
+                         CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT,
+                         &sctx->rctx->client_idle_timeout);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot get the client idle timeout [%d]: %s\n",
+               ret, strerror(ret));
+        goto fail;
+    }
+
+    /* Ensure that the client timeout is at least ten seconds */
+    if (sctx->rctx->client_idle_timeout < 10) {
+        sctx->rctx->client_idle_timeout = 10;
+    }
+
+    ret = EOK;
+
+fail:
+    return ret;
+}
+
+static int sec_responder_ctx_destructor(void *ptr)
+{
+    struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx);
+
+    /* mark that we are shutting down the responder, so it is propagated
+     * into underlying contexts that are freed right before rctx */
+    DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n");
+    rctx->shutting_down = true;
+
+    return 0;
+}
+
+static int sec_process_init(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct confdb_ctx *cdb)
+{
+    struct resp_ctx *rctx;
+    struct sec_ctx *sctx;
+    int ret;
+
+    rctx = talloc_zero(mem_ctx, struct resp_ctx);
+    if (!rctx) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n");
+        return ENOMEM;
+    }
+    rctx->ev = ev;
+    rctx->cdb = cdb;
+    rctx->sock_name = SSS_SEC_SOCKET_NAME;
+    rctx->confdb_service_path = CONFDB_SEC_CONF_ENTRY;
+    rctx->shutting_down = false;
+
+    talloc_set_destructor((TALLOC_CTX*)rctx, sec_responder_ctx_destructor);
+
+    sctx = talloc_zero(rctx, struct sec_ctx);
+    if (!sctx) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing sec_ctx\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    sctx->rctx = rctx;
+    sctx->rctx->pvt_ctx = sctx;
+
+    ret = sec_get_config(sctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting secrets config\n");
+        goto fail;
+    }
+
+    /* Set up file descriptor limits */
+    responder_set_fd_limit(sctx->fd_limit);
+
+    ret = activate_unix_sockets(rctx, sec_connection_setup);
+    if (ret != EOK) goto fail;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Secrets Initialization complete\n");
+
+    return EOK;
+
+fail:
+    talloc_free(rctx);
+    return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+    int opt;
+    poptContext pc;
+    struct main_context *main_ctx;
+    int ret;
+    uid_t uid;
+    gid_t gid;
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_MAIN_OPTS
+        SSSD_SERVER_OPTS(uid, gid)
+        POPT_TABLEEND
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    umask(DFL_RSP_UMASK);
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                  poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+
+    poptFreeContext(pc);
+
+    DEBUG_INIT(debug_level);
+
+    /* set up things like debug, signals, daemonization, etc... */
+    debug_log_file = "sssd_secrets";
+
+    ret = server_setup("sssd[secrets]", 0, uid, gid, CONFDB_SEC_CONF_ENTRY,
+                       &main_ctx);
+    if (ret != EOK) return 2;
+
+    ret = die_if_parent_died();
+    if (ret != EOK) {
+        /* This is not fatal, don't return */
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Could not set up to exit when parent process does\n");
+    }
+
+    ret = sec_process_init(main_ctx,
+                           main_ctx->event_ctx,
+                           main_ctx->confdb_ctx);
+    if (ret != EOK) return 3;
+
+    /* loop on main */
+    server_loop(main_ctx);
+
+    return 0;
+}
+
diff --git a/src/responder/secrets/secsrv.h b/src/responder/secrets/secsrv.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1bd8660f85cf21a6eaa38161c0aea5ccb9bc816
--- /dev/null
+++ b/src/responder/secrets/secsrv.h
@@ -0,0 +1,42 @@
+/*
+   SSSD
+
+   Secrets Responder, header file
+
+   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/>.
+*/
+
+#ifndef __SECSRV_H__
+#define __SECSRV_H__
+
+#include "config.h"
+
+#include <stdint.h>
+#include <sys/un.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <ldb.h>
+
+struct sec_ctx {
+    struct resp_ctx *rctx;
+    int fd_limit;
+};
+
+int sec_connection_setup(struct cli_ctx *cctx);
+
+#endif /* __SECSRV_H__ */
+
+
diff --git a/src/responder/secrets/secsrv_cmd.c b/src/responder/secrets/secsrv_cmd.c
new file mode 100644
index 0000000000000000000000000000000000000000..dae3b910e0144e2ee7112c4f30114bae781ecc26
--- /dev/null
+++ b/src/responder/secrets/secsrv_cmd.c
@@ -0,0 +1,507 @@
+/*
+   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 "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/secrets/secsrv.h"
+#include "confdb/confdb.h"
+#include <http_parser.h>
+
+#define SEC_REQUEST_MAX_SIZE 65536
+#define SEC_PACKET_MAX_RECV_SIZE 8192
+
+
+struct sec_kvp {
+    char *name;
+    char *value;
+};
+
+struct sec_data {
+    char *data;
+    size_t length;
+};
+
+struct sec_proto_ctx {
+    http_parser_settings callbacks;
+    http_parser parser;
+};
+
+struct sec_req_ctx {
+    struct cli_ctx *cctx;
+    bool complete;
+
+    size_t total_size;
+
+    char *request_url;
+    struct sec_kvp *headers;
+    int num_headers;
+    struct sec_data body;
+
+    struct sec_data reply;
+};
+
+
+/* ##### Request Handling ##### */
+
+static int error_403_reply(TALLOC_CTX *mem_ctx,
+                           struct sec_data *reply)
+{
+    reply->data = talloc_asprintf(mem_ctx,
+        "HTTP/1.1 403 Forbidden\r\n"
+        "Content-Length: 0\r\n"
+        "\r\n"
+        "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+        "<html>\r\n<head>\r\n<title>403 Forbidden</title></head>\r\n"
+        "<body>\r\n<h1>Forbidden</h1>\r\n"
+        "<p>You don't have permission to access the requested "
+        "resource.</p>\r\n<hr>\r\n</body>");
+    if (!reply->data) return ENOMEM;
+
+    reply->length = strlen(reply->data);
+
+    return EOK;
+}
+
+struct sec_http_request_state {
+    struct tevent_context *ev;
+    struct sec_req_ctx *secreq;
+};
+
+static struct tevent_req *sec_http_request(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct sec_req_ctx *secreq)
+{
+    struct tevent_req *req;
+    struct sec_http_request_state *state;
+    int ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct sec_http_request_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->secreq = secreq;
+
+    /* Take it form here */
+
+    /* For now, always return an error */
+    ret = error_403_reply(state, &state->secreq->reply);
+    if (ret != EOK) goto done;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    } else {
+        tevent_req_done(req);
+    }
+    return tevent_req_post(req, state->ev);
+}
+
+static int sec_http_request_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+/* --- */
+
+static void
+sec_http_request_done(struct tevent_req *req)
+{
+    struct cli_ctx *cctx = tevent_req_callback_data(req, struct cli_ctx);
+    int ret;
+
+    ret = sec_http_request_recv(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to find reply, aborting client!\n");
+        talloc_free(cctx);
+        return;
+    }
+
+    /* Turn writable on so we can write back the reply */
+    TEVENT_FD_WRITEABLE(cctx->cfde);
+}
+
+static void sec_cmd_execute(struct cli_ctx *cctx)
+{
+    struct sec_req_ctx *secreq;
+    struct tevent_req *req;
+
+    secreq = talloc_get_type(cctx->state_ctx, struct sec_req_ctx);
+
+    req = sec_http_request(secreq, cctx->ev, secreq);
+    if (!req) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to schedule secret retrieval\n.");
+        talloc_free(cctx);
+        return;
+    }
+    tevent_req_set_callback(req, sec_http_request_done, cctx);
+}
+
+
+/* ##### HTTP Parsing Callbacks ##### */
+
+static void sec_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 bool sec_too_much_data(struct sec_req_ctx *req, size_t length)
+{
+    req->total_size += length;
+    if (req->total_size > SEC_REQUEST_MAX_SIZE) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Request too big, aborting client!\n");
+        return true;
+    }
+    return false;
+}
+
+static int sec_on_message_begin(http_parser *parser)
+{
+    DEBUG(SSSDBG_TRACE_INTERNAL, "HTTP Message parsing begins\n");
+
+    return 0;
+}
+
+static int sec_on_url(http_parser *parser,
+                      const char *at, size_t length)
+{
+    struct sec_req_ctx *req =
+        talloc_get_type(parser->data, struct sec_req_ctx);
+
+    if (sec_too_much_data(req, length)) return -1;
+
+    sec_append_string(req, &req->request_url, at, length);
+    if (!req->request_url) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to store URL, aborting client!\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int sec_on_header_field(http_parser *parser,
+                               const char *at, size_t length)
+{
+    struct sec_req_ctx *req =
+        talloc_get_type(parser->data, struct sec_req_ctx);
+    int n = req->num_headers;
+
+    if (sec_too_much_data(req, length)) return -1;
+
+    if (!req->headers) {
+        req->headers = talloc_zero_array(req, struct sec_kvp, 10);
+    } else if ((n % 10 == 0) &&
+               (req->headers[n - 1].value)) {
+        req->headers = talloc_realloc(req, req->headers,
+                                      struct sec_kvp, n + 10);
+        if (req->headers) {
+            memset(&req->headers[n], 0, sizeof(struct sec_kvp) * 10);
+        }
+    }
+    if (!req->headers) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to store headers, aborting client!\n");
+        return -1;
+    }
+
+    if (!n || req->headers[n - 1].value) {
+        /* new field */
+        n++;
+    }
+    sec_append_string(req->headers, &req->headers[n - 1].name, at, length);
+    if (!req->headers[n - 1].name) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to store header name, aborting client!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int sec_on_header_value(http_parser *parser,
+                               const char *at, size_t length)
+{
+    struct sec_req_ctx *req =
+        talloc_get_type(parser->data, struct sec_req_ctx);
+    int n = req->num_headers;
+
+    if (sec_too_much_data(req, length)) return -1;
+
+    if (!req->headers) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Invalid headers pointer, aborting client!\n");
+        return -1;
+    }
+
+    if (req->headers[n].name && !req->headers[n].value) {
+        /* we increment on new value */
+        n = ++req->num_headers;
+    }
+
+    sec_append_string(req->headers, &req->headers[n - 1].value, at, length);
+    if (!req->headers[n - 1].value) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to store header value, aborting client!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int sec_on_headers_complete(http_parser *parser)
+{
+    /* TODO: if message has no body we should return 1 */
+    return 0;
+}
+
+static int sec_on_body(http_parser *parser,
+                       const char *at, size_t length)
+{
+    struct sec_req_ctx *req =
+        talloc_get_type(parser->data, struct sec_req_ctx);
+
+    if (sec_too_much_data(req, length)) return -1;
+
+    sec_append_string(req, &req->body.data, at, length);
+    if (!req->body.data) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to store body, aborting client!\n");
+        return -1;
+    }
+    req->body.length += length;
+
+    return 0;
+}
+
+static int sec_on_message_complete(http_parser *parser)
+{
+    struct sec_req_ctx *req =
+        talloc_get_type(parser->data, struct sec_req_ctx);
+
+    req->complete = true;
+
+    return 0;
+}
+
+
+/* ##### Communications ##### */
+
+static int sec_send_data(int fd, struct sec_data *data)
+{
+    size_t len;
+
+    errno = 0;
+    len = send(fd, data->data, data->length, 0);
+    if (len == -1) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+            return EAGAIN;
+        } else {
+            return errno;
+        }
+    }
+
+    if (len == 0) {
+        return EIO;
+    }
+
+    data->length -= len;
+    data->data += len;
+    return EOK;
+}
+
+static void sec_send(struct cli_ctx *cctx)
+{
+    struct sec_req_ctx *req;
+    int ret;
+
+    req = talloc_get_type(cctx->state_ctx, struct sec_req_ctx);
+
+    ret = sec_send_data(cctx->cfd, &req->reply);
+    if (ret == EAGAIN) {
+        /* not all data was sent, loop again */
+        return;
+    }
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
+        talloc_free(cctx);
+        return;
+    }
+
+    /* ok all sent */
+    TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
+    TEVENT_FD_READABLE(cctx->cfde);
+    talloc_zfree(cctx->state_ctx);
+    return;
+}
+
+static int sec_recv_data(int fd, struct sec_data *data)
+{
+    size_t len;
+
+    errno = 0;
+    len = recv(fd, data->data, data->length, 0);
+    if (len == -1) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+            return EAGAIN;
+        } else {
+            return errno;
+        }
+    }
+
+    if (len == 0) {
+        return ENODATA;
+    }
+
+    data->length = len;
+    return EOK;
+}
+
+static void sec_recv(struct cli_ctx *cctx)
+{
+    struct sec_proto_ctx *prctx;
+    struct sec_req_ctx *req;
+    char buffer[SEC_PACKET_MAX_RECV_SIZE];
+    struct sec_data data = { buffer,
+                             SEC_PACKET_MAX_RECV_SIZE };
+    size_t len;
+    int ret;
+
+    prctx = talloc_get_type(cctx->protocol_ctx, struct sec_proto_ctx);
+    req = talloc_get_type(cctx->state_ctx, struct sec_req_ctx);
+    if (!req) {
+        /* A new request comes in, setup data structures */
+        req = talloc_zero(cctx, struct sec_req_ctx);
+        if (!req) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "Failed to setup request handlers, aborting client\n");
+            talloc_free(cctx);
+            return;
+        }
+        req->cctx = cctx;
+        cctx->state_ctx = req;
+        http_parser_init(&prctx->parser, HTTP_REQUEST);
+        prctx->parser.data = req;
+    }
+
+    ret = sec_recv_data(cctx->cfd, &data);
+    switch (ret) {
+    case ENODATA:
+        DEBUG(SSSDBG_TRACE_ALL,
+              "Client closed connection.\n");
+        talloc_free(cctx);
+        return;
+    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 client\n",
+              ret, sss_strerror(ret));
+        talloc_free(cctx);
+        return;
+    }
+
+    len = http_parser_execute(&prctx->parser, &prctx->callbacks,
+                              data.data, data.length);
+    if (len != data.length) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to parse request, aborting client!\n");
+        talloc_free(cctx);
+        return;
+    }
+
+    if (!req->complete) {
+        return;
+    }
+
+    /* do not read anymore, client is done sending */
+    TEVENT_FD_NOT_READABLE(cctx->cfde);
+
+    sec_cmd_execute(cctx);
+}
+
+static void sec_fd_handler(struct tevent_context *ev,
+                           struct tevent_fd *fde,
+                           uint16_t flags, void *ptr)
+{
+    errno_t ret;
+    struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx);
+
+    /* Always reset the idle timer on any activity */
+    ret = reset_idle_timer(cctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not create idle timer for client. "
+               "This connection may not auto-terminate\n");
+        /* Non-fatal, continue */
+    }
+
+    if (flags & TEVENT_FD_READ) {
+        sec_recv(cctx);
+        return;
+    }
+    if (flags & TEVENT_FD_WRITE) {
+        sec_send(cctx);
+        return;
+    }
+}
+
+static http_parser_settings sec_callbacks = {
+    .on_message_begin = sec_on_message_begin,
+    .on_url = sec_on_url,
+    .on_header_field = sec_on_header_field,
+    .on_header_value = sec_on_header_value,
+    .on_headers_complete = sec_on_headers_complete,
+    .on_body = sec_on_body,
+    .on_message_complete = sec_on_message_complete
+};
+
+int sec_connection_setup(struct cli_ctx *cctx)
+{
+    struct sec_proto_ctx *protocol_ctx;
+
+    protocol_ctx = talloc_zero(cctx, struct sec_proto_ctx);
+    if (!protocol_ctx) return ENOMEM;
+    protocol_ctx->callbacks = sec_callbacks;
+
+    cctx->protocol_ctx = protocol_ctx;
+    cctx->cfd_handler = sec_fd_handler;
+    return EOK;
+}
+
+/* Dummy, not used here but required to link to other responder files */
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+    return NULL;
+}
-- 
2.5.5

