/* 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 "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; }