From a8d1a344e580f29699aed9b88d87fc3c6f5d113b Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 7 Jan 2016 10:17:35 -0500 Subject: [PATCH 13/19] 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 Related: https://fedorahosted.org/sssd/ticket/2913 Reviewed-by: Jakub Hrozek --- Makefile.am | 21 ++ 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 | 192 ++++++++++++ src/responder/secrets/secsrv.h | 40 +++ 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 4c8bea8a1dd318f8465d1e0c127c64b18bbf567c..139b280875a0a8424b80ec2c8246d4d206004698 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,6 +62,7 @@ sssdtapscriptdir = $(sssddatadir)/systemtap 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@ @@ -169,6 +170,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 @@ -462,6 +466,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\" \ @@ -616,6 +621,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 \ @@ -1340,6 +1346,21 @@ endif endif +if BUILD_SECRETS +sssd_secrets_SOURCES = \ + src/responder/secrets/secsrv.c \ + src/responder/secrets/secsrv_cmd.c \ + $(SSSD_RESPONDER_OBJ) \ + $(NULL) +sssd_secrets_LDADD = \ + $(HTTP_PARSER_LIBS) \ + $(TDB_LIBS) \ + $(SSSD_LIBS) \ + $(SYSTEMD_DAEMON_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ + $(NULL) +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 475a07e0bbb0eaab4e1ea6455139b2b9769ee1c2..0d5715083a6155dfa2fb7d5356b876f9d40e35e4 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -743,6 +743,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 9490e3e38b36fe35b7378c85cfcd2095a5f9ba99..b8ad827b796ecf88d3b8ff3e66e1ebd45913a880 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -222,6 +222,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 8db9e9894e159ad0a82e20a6c13509dec20cd7a7..23e7d46f81312c441409af81f4c645c9b61ee416 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -319,6 +319,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 6fb2ed365e1e88e65e478349ec8fff233ed8ac41..f519b8488b69278c50eacac5bd4db210e554fba7 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..523aa6469da9939abf9a7fbbd159234e7ed0982b --- /dev/null +++ b/src/responder/secrets/secsrv.c @@ -0,0 +1,192 @@ +/* + SSSD + + Secrets Responder + + Copyright (C) Simo Sorce 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 . +*/ + +#include "util/util.h" +#include +#include +#include + +#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..91535ae2bf6155483b3b0dc789b704291009e6cb --- /dev/null +++ b/src/responder/secrets/secsrv.h @@ -0,0 +1,40 @@ +/* + SSSD + + Secrets Responder, header file + + Copyright (C) Simo Sorce 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 . +*/ + +#ifndef __SECSRV_H__ +#define __SECSRV_H__ + +#include "config.h" + +#include +#include +#include +#include +#include + +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..e9661001705e9dc312e2bd244cf8ca45539d07e0 --- /dev/null +++ b/src/responder/secrets/secsrv_cmd.c @@ -0,0 +1,507 @@ +/* + SSSD + + Secrets Responder + + Copyright (C) Simo Sorce 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 . +*/ + +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/secrets/secsrv.h" +#include "confdb/confdb.h" +#include + +#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" + "\r\n" + "\r\n\r\n403 Forbidden\r\n" + "\r\n

Forbidden

\r\n" + "

You don't have permission to access the requested " + "resource.

\r\n
\r\n"); + 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_send(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_send(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) +{ + ssize_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) +{ + ssize_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.4.11