diff options
Diffstat (limited to 'src/responder/secrets/proxy.c')
-rw-r--r-- | src/responder/secrets/proxy.c | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/src/responder/secrets/proxy.c b/src/responder/secrets/proxy.c new file mode 100644 index 000000000..13321c13e --- /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; + 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; +} |