summaryrefslogtreecommitdiffstats
path: root/src/responder
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2016-05-09 16:36:36 +0200
committerJakub Hrozek <jhrozek@redhat.com>2016-06-29 21:46:49 +0200
commit1dd679584241a0f9b29072c7eed1c5c5e4a577e4 (patch)
tree6875afa02cbf58ce9db72019982c6b02d0d72d75 /src/responder
parenta8d1a344e580f29699aed9b88d87fc3c6f5d113b (diff)
downloadsssd-1dd679584241a0f9b29072c7eed1c5c5e4a577e4.tar.gz
sssd-1dd679584241a0f9b29072c7eed1c5c5e4a577e4.tar.xz
sssd-1dd679584241a0f9b29072c7eed1c5c5e4a577e4.zip
Add initial providers infrastructure.
Also adds support for the basic LOCAL provider that stores data on the local machine. Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
Diffstat (limited to 'src/responder')
-rw-r--r--src/responder/secrets/local.c427
-rw-r--r--src/responder/secrets/providers.c360
-rw-r--r--src/responder/secrets/secsrv_cmd.c181
-rw-r--r--src/responder/secrets/secsrv_local.h28
-rw-r--r--src/responder/secrets/secsrv_private.h121
5 files changed, 1058 insertions, 59 deletions
diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
new file mode 100644
index 000000000..288775839
--- /dev/null
+++ b/src/responder/secrets/local.c
@@ -0,0 +1,427 @@
+/*
+ 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 <ldb.h>
+
+
+
+int local_db_dn(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *req_path,
+ struct ldb_dn **req_dn)
+{
+ struct ldb_dn *dn;
+ const char *s, *e;
+ int ret;
+
+ dn = ldb_dn_new(mem_ctx, ldb, "cn=secrets");
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ s = req_path;
+
+ while (s && *s) {
+ e = strchr(s, '/');
+ if (e) {
+ if (e == s) {
+ s++;
+ continue;
+ }
+ if (!ldb_dn_add_child_fmt(dn, "cn=%.*s", (int)(e - s), s)) {
+ ret = ENOMEM;
+ goto done;
+ }
+ s = e + 1;
+ } else {
+ if (!ldb_dn_add_child_fmt(dn, "cn=%s", s)) {
+ ret = ENOMEM;
+ goto done;
+ }
+ s = NULL;
+ }
+ }
+
+ *req_dn = dn;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+char *local_dn_to_path(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ struct ldb_dn *dn)
+{
+ int basecomps;
+ int dncomps;
+ char *path = NULL;
+
+ basecomps = ldb_dn_get_comp_num(basedn);
+ dncomps = ldb_dn_get_comp_num(dn);
+
+ for (int i = dncomps - basecomps; i > 0; i--) {
+ const struct ldb_val *val;
+
+ val = ldb_dn_get_component_val(dn, i - 1);
+ if (!val) return NULL;
+
+ if (path) {
+ path = talloc_strdup_append_buffer(path, "/");
+ if (!path) return NULL;
+ path = talloc_strndup_append_buffer(path, (char *)val->data,
+ val->length);
+ } else {
+ path = talloc_strndup(mem_ctx, (char *)val->data, val->length);
+ }
+ if (!path) return NULL;
+ }
+
+ return path;
+}
+
+int local_db_get_simple(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *req_path,
+ char **secret)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { "secret", NULL };
+ const char *filter = "(type=simple)";
+ struct ldb_result *res;
+ struct ldb_dn *dn;
+ const char *attr_secret;
+ int ret;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) return ENOMEM;
+
+ ret = local_db_dn(tmp_ctx, ldb, req_path, &dn);
+ if (ret != EOK) goto done;
+
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, "%s", filter);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ switch (res->count) {
+ case 0:
+ ret = ENOENT;
+ goto done;
+ case 1:
+ break;
+ default:
+ ret = E2BIG;
+ goto done;
+ }
+
+ attr_secret = ldb_msg_find_attr_as_string(res->msgs[0], "secret", NULL);
+ if (!attr_secret) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *secret = talloc_strdup(mem_ctx, attr_secret);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int local_db_list_keys(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *req_path,
+ char ***_keys,
+ int *num_keys)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { "secret", NULL };
+ const char *filter = "(type=simple)";
+ struct ldb_result *res;
+ struct ldb_dn *dn;
+ char **keys;
+ int ret;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) return ENOMEM;
+
+ ret = local_db_dn(tmp_ctx, ldb, req_path, &dn);
+ if (ret != EOK) goto done;
+
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ attrs, "%s", filter);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ keys = talloc_array(mem_ctx, char *, res->count);
+ if (!keys) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < res->count; i++) {
+ keys[i] = local_dn_to_path(keys, dn, res->msgs[i]->dn);
+ if (!keys[i]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_keys = keys;
+ *num_keys = res->count;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int local_db_put_simple(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *req_path,
+ const char *secret)
+{
+ struct ldb_message *msg;
+ int ret;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = local_db_dn(msg, ldb, req_path, &msg->dn);
+ if (ret != EOK) goto done;
+
+ ret = ldb_msg_add_string(msg, "type", "simple");
+ if (ret != EOK) goto done;
+
+ ret = ldb_msg_add_string(msg, "secret", secret);
+ if (ret != EOK) goto done;
+
+ ret = ldb_add(ldb, msg);
+ if (ret != EOK) {
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) ret = EEXIST;
+ else ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(msg);
+ return ret;
+}
+
+int local_db_delete(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *req_path)
+{
+ struct ldb_dn *dn;
+ int ret;
+
+ ret = local_db_dn(mem_ctx, ldb, req_path, &dn);
+ if (ret != EOK) goto done;
+
+ ret = ldb_delete(ldb, dn);
+ if (ret != EOK) {
+ ret = EIO;
+ }
+
+done:
+ return ret;
+}
+
+int local_secrets_map_path(TALLOC_CTX *mem_ctx,
+ struct sec_req_ctx *secreq,
+ char **local_db_path)
+{
+ int ret;
+
+ /* be strict for now */
+ if (secreq->parsed_url.fragment != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unrecognized URI fragments: [%s]\n",
+ secreq->parsed_url.fragment);
+ return EINVAL;
+ }
+
+ if (secreq->parsed_url.userinfo != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unrecognized URI userinfo: [%s]\n",
+ secreq->parsed_url.userinfo);
+ return EINVAL;
+ }
+
+ /* only type simple for now */
+ if (secreq->parsed_url.query != NULL) {
+ ret = strcmp(secreq->parsed_url.query, "type=simple");
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid URI query: [%s]\n",
+ secreq->parsed_url.query);
+ return EINVAL;
+ }
+ }
+
+ /* drop SEC_BASEPATH prefix */
+ *local_db_path =
+ talloc_strdup(mem_ctx, &secreq->mapped_path[sizeof(SEC_BASEPATH) - 1]);
+ if (!*local_db_path) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to map request to local db path\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+
+struct local_secret_state {
+ struct tevent_context *ev;
+ struct sec_req_ctx *secreq;
+};
+
+struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ void *provider_ctx,
+ struct sec_req_ctx *secreq)
+{
+ struct tevent_req *req;
+ struct local_secret_state *state;
+ struct ldb_context *ldb;
+ struct sec_data body = { 0 };
+ char *req_path;
+ char *secret;
+ char **keys;
+ int nkeys;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct local_secret_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->secreq = secreq;
+
+ ldb = talloc_get_type(provider_ctx, struct ldb_context);
+ if (!ldb) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = local_secrets_map_path(state, secreq, &req_path);
+ if (ret) goto done;
+
+ switch (secreq->method) {
+ case HTTP_GET:
+ if (req_path[strlen(req_path) - 1] == '/') {
+ ret = local_db_list_keys(state, ldb, req_path, &keys, &nkeys);
+ if (ret) goto done;
+
+ ret = sec_array_to_json(state, keys, nkeys, &body.data);
+ if (ret) goto done;
+ } else {
+ ret = local_db_get_simple(state, ldb, req_path, &secret);
+ if (ret) goto done;
+
+ ret = sec_simple_secret_to_json(state, secret, &body.data);
+ if (ret) goto done;
+ }
+
+ body.length = strlen(body.data);
+ break;
+
+ case HTTP_PUT:
+ ret = sec_json_to_simple_secret(state, secreq->body.data, &secret);
+ if (ret) goto done;
+
+ ret = local_db_put_simple(state, ldb, req_path, secret);
+ if (ret) goto done;
+ break;
+
+ case HTTP_DELETE:
+ ret = local_db_delete(state, ldb, req_path);
+ if (ret) goto done;
+ break;
+
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (body.data) {
+ ret = sec_http_reply_with_body(secreq, &secreq->reply, STATUS_200,
+ "application/json", &body);
+ } else {
+ ret = sec_http_status_reply(secreq, &secreq->reply, STATUS_200);
+ }
+
+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 are made */
+ tevent_req_done(req);
+ }
+ return tevent_req_post(req, state->ev);
+}
+
+/* FIXME: allocate on the responder context */
+static struct provider_handle local_secrets_handle = {
+ .fn = local_secret_req,
+ .context = NULL,
+};
+
+int local_secrets_provider_handle(TALLOC_CTX *mem_ctx,
+ struct provider_handle **handle)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ if (local_secrets_handle.context == NULL) {
+ ldb = ldb_init(NULL, NULL);
+ if (!ldb) return EIO;
+
+ ret = ldb_connect(ldb, SECRETS_DB_PATH"/secrets.ldb", 0, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ldb);
+ return EIO;
+ }
+
+ local_secrets_handle.context = ldb;
+ }
+
+ *handle = &local_secrets_handle;
+ return EOK;
+}
diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
new file mode 100644
index 000000000..b641ebef8
--- /dev/null
+++ b/src/responder/secrets/providers.c
@@ -0,0 +1,360 @@
+/*
+ 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 "responder/secrets/secsrv_local.h"
+#include <jansson.h>
+
+int sec_map_url_to_user_path(struct sec_req_ctx *secreq, char **mapped_path)
+{
+ uid_t c_euid;
+
+ c_euid = client_euid(secreq->cctx->creds);
+
+ /* change path to be user specific */
+ *mapped_path =
+ talloc_asprintf(secreq, SEC_BASEPATH"users/%"SPRIuid"/%s",
+ c_euid,
+ &secreq->parsed_url.path[sizeof(SEC_BASEPATH) - 1]);
+ if (!*mapped_path) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to map request to user specific url\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
+ struct provider_handle **handle)
+{
+ char **sections;
+ char *def_provider;
+ char *provider;
+ const char *upath;
+ int ulen;
+ int num_sections;
+ int ret;
+
+ /* patch must start with /secrets/ for now */
+ ret = strncasecmp(secreq->parsed_url.path,
+ SEC_BASEPATH, sizeof(SEC_BASEPATH) - 1);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Path [%s] does not start with "SEC_BASEPATH"\n",
+ secreq->parsed_url.path);
+ return EPERM;
+ }
+
+ ret = sec_map_url_to_user_path(secreq, &secreq->mapped_path);
+ if (ret) return ret;
+
+ /* source default provider */
+ ret = confdb_get_string(secreq->cctx->rctx->cdb, mem_ctx,
+ CONFDB_SEC_CONF_ENTRY, "provider", "LOCAL",
+ &def_provider);
+ if (ret) return EIO;
+
+ ret = confdb_get_sub_sections(mem_ctx, secreq->cctx->rctx->cdb,
+ CONFDB_SEC_CONF_ENTRY, &sections,
+ &num_sections);
+ if (ret != EOK) return ret;
+
+ provider = def_provider;
+ upath = &secreq->parsed_url.path[sizeof(SEC_BASEPATH) - 1];
+ ulen = strlen(upath);
+
+ // TODO order by length ?
+ for (int i = 0; ulen > 0 && i < num_sections; i++) {
+ if (strncmp(sections[i], upath, ulen) == 0) {
+ char *secname;
+
+ secname = talloc_asprintf(mem_ctx, CONFDB_SEC_CONF_ENTRY"/%s",
+ sections[i]);
+ if (!secname) return ENOMEM;
+
+ provider = NULL;
+ ret = confdb_get_string(secreq->cctx->rctx->cdb, mem_ctx,
+ secname, "provider", def_provider,
+ &provider);
+ if (ret || !provider) return EIO;
+
+ secreq->cfg_section = talloc_steal(secreq, secname);
+ break;
+ }
+ }
+
+ if (strcasecmp(provider, "LOCAL") == 0) {
+ return local_secrets_provider_handle(mem_ctx, handle);
+ } else {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unknown provider type: %s\n", provider);
+ return EIO;
+ }
+
+ return EINVAL;
+}
+
+int sec_provider_recv(struct tevent_req *req) {
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct sec_http_status_format_table {
+ int status;
+ const char *text;
+ const char *description;
+} sec_http_status_format_table[] = {
+ { 200, "OK", "Success" },
+ { 400, "Bad Request",
+ "The request format is invalid." },
+ { 401, "Unauthorized",
+ "Access to the requested resource requires authentication." },
+ { 403, "Forbidden",
+ "Access to the requested resource is forbidden." },
+ { 404, "Not Found",
+ "The requested resource was not found." },
+ { 405, "Method Not Allowed",
+ "Request method not allowed for this resource." },
+ { 406, "Not Acceptable",
+ "The request cannot be accepted." },
+ { 409, "Conflict",
+ "The requested resource already exists." },
+ { 500, "Internal Server Error",
+ "The server encountered an internal error." },
+};
+
+int sec_http_status_reply(TALLOC_CTX *mem_ctx, struct sec_data *reply,
+ enum sec_http_status_codes code)
+{
+ char *body = talloc_asprintf(mem_ctx,
+ "<html>\r\n"
+ "<head>\r\n<title>%d %s</title></head>\r\n"
+ "<body>\r\n"
+ "<h1>%s</h1>\r\n"
+ "<p>%s</p>\r\n"
+ "</body>",
+ sec_http_status_format_table[code].status,
+ sec_http_status_format_table[code].text,
+ sec_http_status_format_table[code].text,
+ sec_http_status_format_table[code].description);
+ if (!body) return ENOMEM;
+
+ reply->data = talloc_asprintf(mem_ctx,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Length: %u\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "%s",
+ sec_http_status_format_table[code].status,
+ sec_http_status_format_table[code].text,
+ (unsigned)strlen(body), body);
+ talloc_free(body);
+ if (!reply->data) return ENOMEM;
+
+ reply->length = strlen(reply->data);
+
+ return EOK;
+}
+
+int sec_http_reply_with_body(TALLOC_CTX *mem_ctx, struct sec_data *reply,
+ enum sec_http_status_codes code,
+ const char *content_type,
+ struct sec_data *body)
+{
+ int head_size;
+
+ reply->data = talloc_asprintf(mem_ctx,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %lu\r\n"
+ "\r\n",
+ sec_http_status_format_table[code].status,
+ sec_http_status_format_table[code].text,
+ content_type, body->length);
+ if (!reply->data) return ENOMEM;
+
+ head_size = strlen(reply->data);
+
+ reply->data = talloc_realloc(mem_ctx, reply->data, char,
+ head_size + body->length);
+ if (!reply->data) return ENOMEM;
+
+ memcpy(&reply->data[head_size], body->data, body->length);
+ reply->length = head_size + body->length;
+
+ return EOK;
+}
+
+enum sec_http_status_codes sec_errno_to_http_status(errno_t err)
+{
+ switch (err) {
+ case EOK:
+ return STATUS_200;
+ case EINVAL:
+ return STATUS_400;
+ case EACCES:
+ return STATUS_401;
+ case EPERM:
+ return STATUS_403;
+ case ENOENT:
+ return STATUS_404;
+ case EISDIR:
+ return STATUS_405;
+ case EMEDIUMTYPE:
+ return STATUS_406;
+ case EEXIST:
+ return STATUS_409;
+ default:
+ return STATUS_500;
+ }
+}
+
+int sec_json_to_simple_secret(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **secret)
+{
+ json_t *root;
+ json_t *element;
+ json_error_t error;
+ int ret;
+
+ root = json_loads(input, 0, &error);
+ if (!root) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to parse JSON payload on line %d: %s\n",
+ error.line, error.text);
+ return EINVAL;
+ }
+
+ if (!json_is_object(root)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json data is not an object.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ element = json_object_get(root, "type");
+ if (!element) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json data key 'type' not found.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ if (!json_is_string(element)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json object 'type' is not a string.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ if (strcmp(json_string_value(element), "simple") != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Token type is not 'simple'.\n");
+ ret = EMEDIUMTYPE;
+ goto done;
+ }
+
+ element = json_object_get(root, "value");
+ if (!element) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json key 'value' not found.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ if (!json_is_string(element)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json object 'value' is not a string.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ *secret = talloc_strdup(mem_ctx, json_string_value(element));
+ if (!*secret) {
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+
+done:
+ json_decref(root);
+ return ret;
+}
+
+int sec_simple_secret_to_json(TALLOC_CTX *mem_ctx,
+ const char *secret,
+ char **output)
+{
+ char *jsonized = NULL;
+ json_t *root;
+ int ret;
+
+ root = json_pack("{s:s, s:s}", "type", "simple", "value", secret);
+ if (!root) return ENOMEM;
+
+ jsonized = json_dumps(root, JSON_INDENT(4));
+ if (!jsonized) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *output = talloc_strdup(mem_ctx, jsonized);
+ if (!*output) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ json_decref(root);
+ free(jsonized);
+ return ret;
+}
+
+int sec_array_to_json(TALLOC_CTX *mem_ctx,
+ char **array, int count,
+ char **output)
+{
+ char *jsonized = NULL;
+ json_t *root;
+ int ret;
+
+ root = json_array();
+
+ for (int i = 0; i < count; i++) {
+ // FIXME: json_string mem leak ?
+ // FIXME: Error checking
+ json_array_append_new(root, json_string(array[i]));
+ }
+
+ jsonized = json_dumps(root, JSON_INDENT(4));
+ if (!jsonized) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *output = talloc_strdup(mem_ctx, jsonized);
+ if (!*output) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ json_decref(root);
+ free(jsonized);
+ return ret;
+}
diff --git a/src/responder/secrets/secsrv_cmd.c b/src/responder/secrets/secsrv_cmd.c
index e96610017..00f0600d0 100644
--- a/src/responder/secrets/secsrv_cmd.c
+++ b/src/responder/secrets/secsrv_cmd.c
@@ -19,79 +19,33 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
#include "util/util.h"
#include "responder/common/responder.h"
#include "responder/secrets/secsrv.h"
-#include "confdb/confdb.h"
-#include <http_parser.h>
+#include "responder/secrets/secsrv_private.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 void sec_http_request_pipeline_done(struct tevent_req *subreq);
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 tevent_req *subreq;
struct sec_http_request_state *state;
- int ret;
+ struct provider_handle *provider_handle;
+ errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct sec_http_request_state);
if (!req) return NULL;
@@ -99,19 +53,45 @@ static struct tevent_req *sec_http_request_send(TALLOC_CTX *mem_ctx,
state->ev = ev;
state->secreq = secreq;
- /* Take it form here */
+ /* Go through the pipeline */
+
+ /* 1. mapping and path conversion */
+ ret = sec_req_routing(state, secreq, &provider_handle);
+ if (ret) goto done;
- /* For now, always return an error */
- ret = error_403_reply(state, &state->secreq->reply);
- if (ret != EOK) goto done;
+ /* 2. backend invocation */
+ subreq = provider_handle->fn(state, state->ev,
+ provider_handle->context, secreq);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sec_http_request_pipeline_done, req);
+ return req;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
+ }
+ return tevent_req_post(req, state->ev);
+}
+
+static void sec_http_request_pipeline_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ /* 3. reply construction */
+ ret = sec_provider_recv(subreq);
+
+ 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)
@@ -126,10 +106,21 @@ static int sec_http_request_recv(struct tevent_req *req)
static void
sec_http_request_done(struct tevent_req *req)
{
- struct cli_ctx *cctx = tevent_req_callback_data(req, struct cli_ctx);
+ struct sec_req_ctx *secreq;
+ struct cli_ctx *cctx;
int ret;
+ secreq = tevent_req_callback_data(req, struct sec_req_ctx);
+ cctx = secreq->cctx;
+
ret = sec_http_request_recv(req);
+
+ if (ret != EOK) {
+ /* Always return an error if we get here */
+ ret = sec_http_status_reply(secreq, &secreq->reply,
+ sec_errno_to_http_status(ret));
+ }
+
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE,
"Failed to find reply, aborting client!\n");
@@ -155,7 +146,7 @@ static void sec_cmd_execute(struct cli_ctx *cctx)
talloc_free(cctx);
return;
}
- tevent_req_set_callback(req, sec_http_request_done, cctx);
+ tevent_req_set_callback(req, sec_http_request_done, secreq);
}
@@ -300,10 +291,82 @@ static int sec_on_body(http_parser *parser,
return 0;
}
+static int sec_get_parsed_filed(TALLOC_CTX *mem_ctx, int field,
+ struct http_parser_url *parsed,
+ char *source_buf,
+ char **dest)
+{
+ uint16_t off = parsed->field_data[field].off;
+ uint16_t len = parsed->field_data[field].len;
+ *dest = talloc_strndup(mem_ctx, &source_buf[off], len);
+ if (!*dest) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to parse url, aborting client!\n");
+ return ENOMEM;
+ }
+ return EOK;
+}
+
static int sec_on_message_complete(http_parser *parser)
{
struct sec_req_ctx *req =
talloc_get_type(parser->data, struct sec_req_ctx);
+ struct http_parser_url parsed;
+ int ret;
+
+ /* parse url as well */
+ ret = http_parser_parse_url(req->request_url,
+ strlen(req->request_url),
+ 0, &parsed);
+ if (ret) return ret;
+
+ if (parsed.field_set & (1 << UF_SCHEMA)) {
+ ret = sec_get_parsed_filed(req, UF_SCHEMA, &parsed,
+ req->request_url,
+ &req->parsed_url.schema);
+ if (ret) return -1;
+ }
+
+ if (parsed.field_set & (1 << UF_HOST)) {
+ ret = sec_get_parsed_filed(req, UF_HOST, &parsed,
+ req->request_url,
+ &req->parsed_url.host);
+ if (ret) return -1;
+ }
+
+ if (parsed.field_set & (1 << UF_PORT)) {
+ req->parsed_url.port = parsed.port;
+ }
+
+ if (parsed.field_set & (1 << UF_PATH)) {
+ ret = sec_get_parsed_filed(req, UF_PATH, &parsed,
+ req->request_url,
+ &req->parsed_url.path);
+ if (ret) return -1;
+ }
+
+ if (parsed.field_set & (1 << UF_QUERY)) {
+ ret = sec_get_parsed_filed(req, UF_QUERY, &parsed,
+ req->request_url,
+ &req->parsed_url.query);
+ if (ret) return -1;
+ }
+
+ if (parsed.field_set & (1 << UF_FRAGMENT)) {
+ ret = sec_get_parsed_filed(req, UF_FRAGMENT, &parsed,
+ req->request_url,
+ &req->parsed_url.fragment);
+ if (ret) return -1;
+ }
+
+ if (parsed.field_set & (1 << UF_USERINFO)) {
+ ret = sec_get_parsed_filed(req, UF_USERINFO, &parsed,
+ req->request_url,
+ &req->parsed_url.userinfo);
+ if (ret) return -1;
+ }
+
+ req->method = parser->method;
req->complete = true;
diff --git a/src/responder/secrets/secsrv_local.h b/src/responder/secrets/secsrv_local.h
new file mode 100644
index 000000000..537f61e53
--- /dev/null
+++ b/src/responder/secrets/secsrv_local.h
@@ -0,0 +1,28 @@
+/*
+ SSSD
+
+ Secrets Local Provider
+
+ 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_LOCAL_H__
+#define __SECSRV_LOCAL_H__
+
+int local_secrets_provider_handle(TALLOC_CTX *mem_ctx,
+ struct provider_handle **handle);
+
+#endif /* __SECSRV_LOCAL_H__ */
diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
new file mode 100644
index 000000000..4b55f645a
--- /dev/null
+++ b/src/responder/secrets/secsrv_private.h
@@ -0,0 +1,121 @@
+/*
+ SSSD
+
+ Secrets Responder, private 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_PRIVATE_H__
+#define __SECSRV_PRIVATE_H__
+
+#include "config.h"
+#include "responder/common/responder.h"
+#include <http_parser.h>
+
+struct sec_kvp {
+ char *name;
+ char *value;
+};
+
+struct sec_data {
+ char *data;
+ size_t length;
+};
+
+enum sec_http_status_codes {
+ STATUS_200 = 0,
+ STATUS_400,
+ STATUS_401,
+ STATUS_403,
+ STATUS_404,
+ STATUS_405,
+ STATUS_406,
+ STATUS_409,
+ STATUS_500,
+};
+
+struct sec_proto_ctx {
+ http_parser_settings callbacks;
+ http_parser parser;
+};
+
+struct sec_url {
+ char *schema;
+ char *host;
+ int port;
+ char *path;
+ char *fragment;
+ char *userinfo;
+ char *query;
+};
+
+struct sec_req_ctx {
+ struct cli_ctx *cctx;
+ const char *cfg_section;
+ bool complete;
+
+ size_t total_size;
+
+ char *request_url;
+ char *mapped_path;
+
+ uint8_t method;
+ struct sec_url parsed_url;
+ struct sec_kvp *headers;
+ int num_headers;
+ struct sec_data body;
+
+ struct sec_data reply;
+};
+
+typedef struct tevent_req *(*sec_provider_req_t)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ void *provider_ctx,
+ struct sec_req_ctx *secreq);
+
+struct provider_handle {
+ sec_provider_req_t fn;
+ void *context;
+};
+
+#define SEC_BASEPATH "/secrets/"
+
+/* providers.c */
+int sec_req_routing(TALLOC_CTX *mem_ctx, struct sec_req_ctx *secreq,
+ struct provider_handle **handle);
+int sec_provider_recv(struct tevent_req *subreq);
+
+int sec_http_status_reply(TALLOC_CTX *mem_ctx, struct sec_data *reply,
+ enum sec_http_status_codes code);
+int sec_http_reply_with_body(TALLOC_CTX *mem_ctx, struct sec_data *reply,
+ enum sec_http_status_codes code,
+ const char *content_type,
+ struct sec_data *body);
+enum sec_http_status_codes sec_errno_to_http_status(errno_t err);
+
+int sec_json_to_simple_secret(TALLOC_CTX *mem_ctx,
+ const char *input,
+ char **secret);
+int sec_simple_secret_to_json(TALLOC_CTX *mem_ctx,
+ const char *secret,
+ char **output);
+
+int sec_array_to_json(TALLOC_CTX *mem_ctx,
+ char **array, int count,
+ char **output);
+
+#endif /* __SECSRV_PRIVATE_H__ */