From 8c30024303436d98818977288e28511ed74c018a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Mon, 20 Mar 2017 11:49:43 +0100
Subject: [PATCH 33/36] KCM: Store ccaches in secrets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds a new KCM responder ccache back end that forwards all requests to
sssd-secrets.
Reviewed-by: Michal Židek <mzidek@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
---
Makefile.am | 40 +-
contrib/sssd.spec.in | 1 +
src/responder/kcm/kcmsrv_ccache.h | 49 +
src/responder/kcm/kcmsrv_ccache_json.c | 921 +++++++++++
src/responder/kcm/kcmsrv_ccache_secrets.c | 2169 ++++++++++++++++++++++++++
src/tests/cmocka/test_kcm_json_marshalling.c | 234 +++
src/tests/intg/test_kcm.py | 132 +-
src/util/util_errors.c | 2 +
src/util/util_errors.h | 2 +
9 files changed, 3525 insertions(+), 25 deletions(-)
create mode 100644 src/responder/kcm/kcmsrv_ccache_json.c
create mode 100644 src/responder/kcm/kcmsrv_ccache_secrets.c
create mode 100644 src/tests/cmocka/test_kcm_json_marshalling.c
diff --git a/Makefile.am b/Makefile.am
index 49b4cabf9ee3ce1417f955c972376894f3709b33..e9eaa312c91e3aee40bcf13c90a0ad8c683045d5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -303,6 +303,10 @@ if HAVE_INOTIFY
non_interactive_cmocka_based_tests += test_inotify
endif # HAVE_INOTIFY
+if BUILD_KCM
+non_interactive_cmocka_based_tests += test_kcm_json
+endif # BUILD_KCM
+
if BUILD_SAMBA
non_interactive_cmocka_based_tests += \
ad_access_filter_tests \
@@ -1494,19 +1498,26 @@ sssd_kcm_SOURCES = \
src/responder/kcm/kcmsrv_cmd.c \
src/responder/kcm/kcmsrv_ccache.c \
src/responder/kcm/kcmsrv_ccache_mem.c \
+ src/responder/kcm/kcmsrv_ccache_json.c \
+ src/responder/kcm/kcmsrv_ccache_secrets.c \
src/responder/kcm/kcmsrv_ops.c \
src/util/sss_sockets.c \
src/util/sss_krb5.c \
src/util/sss_iobuf.c \
+ src/util/tev_curl.c \
$(SSSD_RESPONDER_OBJ) \
$(NULL)
sssd_kcm_CFLAGS = \
$(AM_CFLAGS) \
$(KRB5_CFLAGS) \
$(UUID_CFLAGS) \
+ $(CURL_CFLAGS) \
+ $(JANSSON_CFLAGS) \
$(NULL)
sssd_kcm_LDADD = \
$(KRB5_LIBS) \
+ $(CURL_LIBS) \
+ $(JANSSON_LIBS) \
$(SSSD_LIBS) \
$(UUID_LIBS) \
$(SYSTEMD_DAEMON_LIBS) \
@@ -3369,6 +3380,30 @@ sss_certmap_test_LDADD = \
libsss_certmap.la \
$(NULL)
endif
+
+if BUILD_KCM
+test_kcm_json_SOURCES = \
+ src/tests/cmocka/test_kcm_json_marshalling.c \
+ src/responder/kcm/kcmsrv_ccache_json.c \
+ src/responder/kcm/kcmsrv_ccache.c \
+ src/util/sss_krb5.c \
+ src/util/sss_iobuf.c \
+ $(NULL)
+test_kcm_json_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(UUID_CFLAGS) \
+ $(NULL)
+test_kcm_json_LDADD = \
+ $(JANSSON_LIBS) \
+ $(UUID_LIBS) \
+ $(KRB5_LIBS) \
+ $(CMOCKA_LIBS) \
+ $(SSSD_LIBS) \
+ $(SSSD_INTERNAL_LTLIBS) \
+ libsss_test_common.la \
+ $(NULL)
+endif # BUILD_KCM
+
endif # HAVE_CMOCKA
noinst_PROGRAMS = pam_test_client
@@ -3431,8 +3466,9 @@ intgcheck-prepare:
--enable-intgcheck-reqs \
--without-semanage \
--enable-files-domain \
- $(INTGCHECK_CONFIGURE_FLAGS); \
- $(MAKE) $(AM_MAKEFLAGS); \
+ $(INTGCHECK_CONFIGURE_FLAGS) \
+ CFLAGS="$$CFLAGS $(AM_CFLAGS) -DKCM_PEER_UID=$$(id -u)"; \
+ $(MAKE) $(AM_MAKEFLAGS) ; \
: Force single-thread install to workaround concurrency issues; \
$(MAKE) $(AM_MAKEFLAGS) -j1 install; \
: Remove .la files from LDB module directory to avoid loader warnings; \
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 1d4d020415ee28292bb4d88c78de205465d812f1..af14d4e3d6b9ffeb4696f1517113b8daa575cb99 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -223,6 +223,7 @@ BuildRequires: systemtap-sdt-devel
BuildRequires: http-parser-devel
BuildRequires: jansson-devel
BuildRequires: libuuid-devel
+BuildRequires: libcurl-devel
%description
Provides a set of daemons to manage access to remote directories and
diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
index 130ae48ae30d5e1e2ab238a647a9b9dc76cc4945..18c8c47ad4ecb24521a85a1833b239c7a2a8bb45 100644
--- a/src/responder/kcm/kcmsrv_ccache.h
+++ b/src/responder/kcm/kcmsrv_ccache.h
@@ -303,4 +303,53 @@ void kcm_debug_uuid(uuid_t uuid);
*/
errno_t kcm_check_name(const char *name, struct cli_creds *client);
+/*
+ * ccahe marshalling to and from JSON. This is used when the ccaches
+ * are stored in the secrets store
+ */
+
+/*
+ * The secrets store is a key-value store at heart. We store the UUID
+ * and the name in the key to allow easy lookups be either key
+ */
+bool sec_key_match_name(const char *sec_key,
+ const char *name);
+
+bool sec_key_match_uuid(const char *sec_key,
+ uuid_t uuid);
+
+const char *sec_key_get_name(const char *sec_key);
+
+errno_t sec_key_get_uuid(const char *sec_key,
+ uuid_t uuid);
+
+/* Create a URL for the default client's ccache */
+const char *sec_dfl_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client);
+
+/* Create a URL for the client's ccache container */
+const char *sec_container_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client);
+
+const char *sec_cc_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client,
+ const char *sec_key);
+
+/*
+ * sec_key is a concatenation of the ccache's UUID and name
+ * sec_value is the JSON dump of the ccache contents
+ */
+errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ const char *sec_value,
+ struct cli_creds *client,
+ struct kcm_ccache **_cc);
+
+/* Convert a kcm_ccache to a key-value pair to be stored in secrets */
+errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
+ struct kcm_ccache *cc,
+ struct cli_creds *client,
+ const char **_url,
+ struct sss_iobuf **_payload);
+
#endif /* _KCMSRV_CCACHE_H_ */
diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c
new file mode 100644
index 0000000000000000000000000000000000000000..40b64861c209206d6f60ccd0843857edee24a844
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_json.c
@@ -0,0 +1,921 @@
+/*
+ SSSD
+
+ KCM Server - ccache JSON (un)marshalling for storing ccaches in
+ sssd-secrets
+
+ Copyright (C) Red Hat, 2017
+
+ 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 "config.h"
+
+#include <stdio.h>
+#include <talloc.h>
+#include <jansson.h>
+
+#include "util/util.h"
+#include "util/util_creds.h"
+#include "util/crypto/sss_crypto.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+
+/* The base for storing secrets is:
+ * http://localhost/kcm/persistent/$uid
+ *
+ * Under $base, there are two containers:
+ * /ccache - stores the ccaches
+ * /ntlm - stores NTLM creds [Not implement yet]
+ *
+ * There is also a special entry that contains the UUID of the default
+ * cache for this UID:
+ * /default - stores the UUID of the default ccache for this UID
+ *
+ * Each ccache has a name and an UUID. On the secrets level, the 'secret'
+ * is a concatenation of the stringified UUID and the name separated
+ * by a plus-sign.
+ */
+#define KCM_SEC_URL "http://localhost/kcm/persistent"
+#define KCM_SEC_BASE_FMT KCM_SEC_URL"/%"SPRIuid"/"
+#define KCM_SEC_CCACHE_FMT KCM_SEC_BASE_FMT"ccache/"
+#define KCM_SEC_DFL_FMT KCM_SEC_BASE_FMT"default"
+
+
+/*
+ * We keep the JSON representation of the ccache versioned to allow
+ * us to modify the format in a future version
+ */
+#define KS_JSON_VERSION 1
+
+/*
+ * The secrets store is a key-value store at heart. We store the UUID
+ * and the name in the key to allow easy lookups be either key
+ */
+#define SEC_KEY_SEPARATOR '-'
+
+/* Compat definition of json_array_foreach for older systems */
+#ifndef json_array_foreach
+#define json_array_foreach(array, idx, value) \
+ for(idx = 0; \
+ idx < json_array_size(array) && (value = json_array_get(array, idx)); \
+ idx++)
+#endif
+
+const char *sec_container_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client)
+{
+ return talloc_asprintf(mem_ctx,
+ KCM_SEC_CCACHE_FMT,
+ cli_creds_get_uid(client));
+}
+
+const char *sec_cc_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client,
+ const char *sec_key)
+{
+ return talloc_asprintf(mem_ctx,
+ KCM_SEC_CCACHE_FMT"%s",
+ cli_creds_get_uid(client),
+ sec_key);
+}
+
+const char *sec_dfl_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client)
+{
+ return talloc_asprintf(mem_ctx,
+ KCM_SEC_DFL_FMT,
+ cli_creds_get_uid(client));
+}
+
+static const char *sec_key_create(TALLOC_CTX *mem_ctx,
+ const char *name,
+ uuid_t uuid)
+{
+ char uuid_str[UUID_STR_SIZE];
+
+ uuid_unparse(uuid, uuid_str);
+ return talloc_asprintf(mem_ctx,
+ "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
+}
+
+static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ const char **_name,
+ uuid_t uuid)
+{
+ char uuid_str[UUID_STR_SIZE];
+
+ if (strlen(sec_key) < UUID_STR_SIZE + 2) {
+ /* One char for separator and at least one for the name */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ return EINVAL;
+ }
+
+ strncpy(uuid_str, sec_key, sizeof(uuid_str)-1);
+ if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
+ return EINVAL;
+ }
+ uuid_str[UUID_STR_SIZE-1] = '\0';
+
+ *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE);
+ if (*_name == NULL) {
+ return ENOMEM;
+ }
+ uuid_parse(uuid_str, uuid);
+
+ return EOK;
+}
+
+errno_t sec_key_get_uuid(const char *sec_key,
+ uuid_t uuid)
+{
+ char uuid_str[UUID_STR_SIZE];
+
+ if (strlen(sec_key) < UUID_STR_SIZE + 2) {
+ /* One char for separator and at least one for the name */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ return EINVAL;
+ }
+
+ if (sec_key[UUID_STR_SIZE-1] != SEC_KEY_SEPARATOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
+ return EINVAL;
+ }
+
+ strncpy(uuid_str, sec_key, UUID_STR_SIZE-1);
+ uuid_str[UUID_STR_SIZE-1] = '\0';
+ uuid_parse(uuid_str, uuid);
+ return EOK;
+}
+
+const char *sec_key_get_name(const char *sec_key)
+{
+ if (strlen(sec_key) < UUID_STR_SIZE + 2) {
+ /* One char for separator and at least one for the name */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ return NULL;
+ }
+
+ return sec_key + UUID_STR_SIZE;
+}
+
+bool sec_key_match_name(const char *sec_key,
+ const char *name)
+{
+ if (strlen(sec_key) < UUID_STR_SIZE + 2) {
+ /* One char for separator and at least one for the name */
+ DEBUG(SSSDBG_MINOR_FAILURE, "Key %s is too short\n", sec_key);
+ return false;
+ }
+
+ return strcmp(sec_key + UUID_STR_SIZE, name) == 0;
+}
+
+bool sec_key_match_uuid(const char *sec_key,
+ uuid_t uuid)
+{
+ errno_t ret;
+ uuid_t key_uuid;
+
+ ret = sec_key_get_uuid(sec_key, key_uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n");
+ return false;
+ }
+
+ return uuid_compare(key_uuid, uuid) == 0;
+}
+
+/*
+ * Creates an array of principal elements that will be used later
+ * in the form of:
+ * "componenets": [ "elem1", "elem2", ...]
+ */
+static json_t *princ_data_to_json(TALLOC_CTX *mem_ctx,
+ krb5_principal princ)
+{
+ json_t *jdata = NULL;
+ json_t *data_array = NULL;
+ int ret;
+ char *str_princ_data;
+
+ data_array = json_array();
+ if (data_array == NULL) {
+ return NULL;
+ }
+
+ for (ssize_t i = 0; i < princ->length; i++) {
+ /* FIXME - it might be cleaner to use stringn here, but the libjansson
+ * version on RHEL-7 doesn't support that
+ */
+ str_princ_data = talloc_zero_array(mem_ctx,
+ char,
+ princ->data[i].length + 1);
+ if (str_princ_data == NULL) {
+ return NULL;
+ }
+ memcpy(str_princ_data, princ->data[i].data, princ->data[i].length);
+ str_princ_data[princ->data[i].length] = '\0';
+
+ jdata = json_string(str_princ_data);
+ talloc_free(str_princ_data);
+ if (jdata == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert principal data to string\n");
+ json_decref(data_array);
+ return NULL;
+ }
+
+ ret = json_array_append_new(data_array, jdata);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot append principal data to array\n");
+ json_decref(jdata);
+ json_decref(data_array);
+ return NULL;
+ }
+ /* data_array now owns the reference to jdata */
+ }
+
+ return data_array;
+}
+
+/* Creates:
+ * {
+ * "type": "number",
+ * "realm": "string",
+ * "componenents": [ "elem1", "elem2", ...]
+ * }
+ */
+static json_t *princ_to_json(TALLOC_CTX *mem_ctx,
+ krb5_principal princ)
+{
+ json_t *jprinc = NULL;
+ json_t *components = NULL;
+ json_error_t error;
+ char *str_realm_data;
+
+ components = princ_data_to_json(mem_ctx, princ);
+ if (components == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert principal data to JSON\n");
+ return NULL;
+ }
+
+ /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
+ * version on RHEL-7 doesn't support that
+ */
+ str_realm_data = talloc_zero_array(mem_ctx,
+ char,
+ princ->realm.length + 1);
+ if (str_realm_data == NULL) {
+ return NULL;
+ }
+ memcpy(str_realm_data, princ->realm.data, princ->realm.length);
+ str_realm_data[princ->realm.length] = '\0';
+
+ jprinc = json_pack_ex(&error,
+ JSON_STRICT,
+ "{s:i, s:s, s:o}",
+ "type", princ->type,
+ "realm", str_realm_data,
+ "components", components);
+ talloc_free(str_realm_data);
+ if (jprinc == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to pack JSON princ structure on line %d: %s\n",
+ error.line, error.text);
+ json_decref(components);
+ return NULL;
+ }
+
+ return jprinc;
+}
+
+/* Creates:
+ * {
+ * "uuid": <data>,
+ * "payload": <data>,
+ * },
+ */
+static json_t *cred_to_json(struct kcm_cred *crd)
+{
+ char uuid_str[UUID_STR_SIZE];
+ uint8_t *cred_blob_data;
+ size_t cred_blob_size;
+ json_t *jcred;
+ json_error_t error;
+ char *base64_cred_blob;
+
+ uuid_unparse(crd->uuid, uuid_str);
+ cred_blob_data = sss_iobuf_get_data(crd->cred_blob);
+ cred_blob_size = sss_iobuf_get_size(crd->cred_blob);
+
+ base64_cred_blob = sss_base64_encode(crd, cred_blob_data, cred_blob_size);
+ if (base64_cred_blob == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot base64 encode the certificate blob\n");
+ return NULL;
+ }
+
+ jcred = json_pack_ex(&error,
+ JSON_STRICT,
+ "{s:s, s:s}",
+ "uuid", uuid_str,
+ "payload", base64_cred_blob);
+ talloc_free(base64_cred_blob);
+ if (jcred == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to pack JSON cred structure on line %d: %s\n",
+ error.line, error.text);
+ return NULL;
+ }
+ return jcred;
+}
+
+/*
+ * Creates:
+ * [
+ * {
+ * "uuid": <data>,
+ * "payload": <data>,
+ * },
+ * ...
+ * ]
+ */
+static json_t *creds_to_json_array(struct kcm_cred *creds)
+{
+ struct kcm_cred *crd;
+ json_t *array;
+ json_t *jcred;
+
+ array = json_array();
+ if (array == NULL) {
+ return NULL;
+ }
+
+ DLIST_FOR_EACH(crd, creds) {
+ jcred = cred_to_json(crd);
+ if (jcred == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert credentials to JSON\n");
+ json_decref(array);
+ return NULL;
+ }
+
+ json_array_append_new(array, jcred);
+ /* array now owns jcred */
+ jcred = NULL;
+ }
+
+ return array;
+}
+
+/*
+ * The ccache is formatted in JSON as:
+ * {
+ * version: number
+ * kdc_offset: number
+ * principal : {
+ * "type": "number",
+ * "realm": "string",
+ * "componenents": [ "elem1", "elem2", ...]
+ * }
+ * creds : [
+ * {
+ * "uuid": <data>,
+ * "payload": <data>,
+ * },
+ * {
+ * ...
+ * }
+ * ]
+ * }
+ * }
+ */
+static json_t *ccache_to_json(struct kcm_ccache *cc)
+{
+ json_t *princ = NULL;
+ json_t *creds = NULL;
+ json_t *jcc = NULL;
+ json_error_t error;
+
+ princ = princ_to_json(cc, cc->client);
+ if (princ == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert princ to JSON\n");
+ return NULL;
+ }
+
+ creds = creds_to_json_array(cc->creds);
+ if (creds == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert creds to JSON array\n");
+ json_decref(princ);
+ return NULL;
+ }
+
+ jcc = json_pack_ex(&error,
+ JSON_STRICT,
+ "{s:i, s:i, s:o, s:o}",
+ "version", KS_JSON_VERSION,
+ "kdc_offset", cc->kdc_offset,
+ "principal", princ,
+ "creds", creds);
+ if (jcc == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to pack JSON ccache structure on line %d: %s\n",
+ error.line, error.text);
+ json_decref(creds);
+ json_decref(princ);
+ return NULL;
+ }
+
+ return jcc;
+}
+
+static errno_t ccache_to_sec_kv(TALLOC_CTX *mem_ctx,
+ struct kcm_ccache *cc,
+ const char **_sec_key,
+ const char **_sec_value)
+{
+ json_t *jcc = NULL;
+ char *jdump;
+
+ jcc = ccache_to_json(cc);
+ if (jcc == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert ccache to JSON\n");
+ return ERR_JSON_ENCODING;
+ }
+
+ /* it would be more efficient to learn the size with json_dumpb and
+ * a NULL buffer, but that's only available since 2.10
+ */
+ jdump = json_dumps(jcc, JSON_INDENT(4) | JSON_ENSURE_ASCII);
+ if (jdump == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot dump JSON\n");
+ return ERR_JSON_ENCODING;
+ }
+
+ *_sec_key = sec_key_create(mem_ctx, cc->name, cc->uuid);
+ *_sec_value = talloc_strdup(mem_ctx, jdump);
+ free(jdump);
+ json_decref(jcc);
+ if (*_sec_key == NULL || *_sec_value == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
+ struct kcm_ccache *cc,
+ struct cli_creds *client,
+ const char **_url,
+ struct sss_iobuf **_payload)
+{
+ errno_t ret;
+ const char *key;
+ const char *value;
+ const char *url;
+ struct sss_iobuf *payload;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ccache_to_sec_kv(mem_ctx, cc, &key, &value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert cache %s to JSON [%d]: %s\n",
+ cc->name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ url = sec_cc_url_create(tmp_ctx, client, key);
+ if (url == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ payload = sss_iobuf_init_readonly(tmp_ctx,
+ (const uint8_t *) value,
+ strlen(value)+1);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot create payload buffer\n");
+ goto done;
+ }
+
+ ret = EOK;
+ *_url = talloc_steal(mem_ctx, url);
+ *_payload = talloc_steal(mem_ctx, payload);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sec_value_to_json(const char *input,
+ json_t **_root)
+{
+ json_t *root = NULL;
+ json_error_t error;
+ int ok;
+
+ root = json_loads(input, 0, &error);
+ if (root == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to parse JSON payload on line %d: %s\n",
+ error.line, error.text);
+ return ERR_JSON_DECODING;
+ }
+
+ ok = json_is_object(root);
+ if (!ok) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json data is not an object.\n");
+ json_decref(root);
+ return ERR_JSON_DECODING;
+ }
+
+ *_root = root;
+ return EOK;
+}
+
+/*
+ * ccache unmarshalling from JSON
+ */
+static errno_t json_element_to_krb5_data(TALLOC_CTX *mem_ctx,
+ json_t *element,
+ krb5_data *data)
+{
+ const char *str_value;
+ size_t str_len;
+
+ /* FIXME - it might be cleaner to use stringn here, but the libjansson
+ * version on RHEL-7 doesn't support that
+ */
+ str_value = json_string_value(element);
+ if (str_value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "JSON element not a string\n");
+ return EINVAL;
+ }
+ str_len = strlen(str_value);
+
+ data->data = talloc_strndup(mem_ctx, str_value, str_len);
+ if (data->data == NULL) {
+ return ENOMEM;
+ }
+ data->length = str_len;
+
+ return EOK;
+}
+
+static errno_t json_array_to_krb5_data(TALLOC_CTX *mem_ctx,
+ json_t *array,
+ krb5_data **_data,
+ size_t *_len)
+{
+ errno_t ret;
+ int ok;
+ size_t len;
+ size_t idx;
+ json_t *element;
+ krb5_data *data;
+
+ ok = json_is_array(array);
+ if (!ok) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json object is not an array.\n");
+ return ERR_JSON_DECODING;
+ }
+
+ len = json_array_size(array);
+ if (len == 0) {
+ *_data = NULL;
+ *_len = 0;
+ return EOK;
+ }
+
+ data = talloc_zero_array(mem_ctx, krb5_data, len);
+ if (data == NULL) {
+ return ENOMEM;
+ }
+
+ json_array_foreach(array, idx, element) {
+ ret = json_element_to_krb5_data(data, element, &data[idx]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert krb5 data element from JSON");
+ talloc_free(data);
+ return ret;
+ }
+ }
+
+ *_data = data;
+ *_len = len;
+ return EOK;
+}
+
+static errno_t json_to_princ(TALLOC_CTX *mem_ctx,
+ json_t *js_princ,
+ krb5_principal *_princ)
+{
+ errno_t ret;
+ json_t *components = NULL;
+ int ok;
+ krb5_principal princ = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *realm_str;
+ size_t realm_size;
+ json_error_t error;
+
+ ok = json_is_object(js_princ);
+ if (!ok) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json principal is not an object.\n");
+ ret = ERR_JSON_DECODING;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ princ = talloc_zero(tmp_ctx, struct krb5_principal_data);
+ if (princ == NULL) {
+ return ENOMEM;
+ }
+ princ->magic = KV5M_PRINCIPAL;
+
+ /* FIXME - it might be cleaner to use the s% specifier here, but the libjansson
+ * version on RHEL-7 doesn't support that
+ */
+ ret = json_unpack_ex(js_princ,
+ &error,
+ JSON_STRICT,
+ "{s:i, s:s, s:o}",
+ "type", &princ->type,
+ "realm", &realm_str,
+ "components", &components);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to unpack JSON princ structure on line %d: %s\n",
+ error.line, error.text);
+ ret = EINVAL;
+ goto done;
+ }
+
+ realm_size = strlen(realm_str);
+
+ princ->realm.data = talloc_strndup(mem_ctx, realm_str, realm_size);
+ if (princ->realm.data == NULL) {
+ return ENOMEM;
+ }
+ princ->realm.length = realm_size;
+ princ->realm.magic = 0;
+
+ ret = json_array_to_krb5_data(princ, components,
+ &princ->data,
+ (size_t *) &princ->length);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert principal from JSON");
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_princ = talloc_steal(mem_ctx, princ);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t json_elem_to_cred(TALLOC_CTX *mem_ctx,
+ json_t *element,
+ struct kcm_cred **_crd)
+{
+ errno_t ret;
+ char *uuid_str;
+ json_error_t error;
+ uuid_t uuid;
+ struct sss_iobuf *cred_blob;
+ const char *base64_cred_blob;
+ struct kcm_cred *crd;
+ uint8_t *outbuf;
+ size_t outbuf_size;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ ret = json_unpack_ex(element,
+ &error,
+ JSON_STRICT,
+ "{s:s, s:s}",
+ "uuid", &uuid_str,
+ "payload", &base64_cred_blob);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to unpack JSON cred structure on line %d: %s\n",
+ error.line, error.text);
+ return EINVAL;
+ }
+
+ uuid_parse(uuid_str, uuid);
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ outbuf = sss_base64_decode(tmp_ctx, base64_cred_blob, &outbuf_size);
+ if (outbuf == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot decode cred blob\n");
+ ret = EIO;
+ goto done;
+ }
+
+ cred_blob = sss_iobuf_init_readonly(tmp_ctx, outbuf, outbuf_size);
+ if (cred_blob == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ crd = kcm_cred_new(tmp_ctx, uuid, cred_blob);
+ if (crd == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ *_crd = talloc_steal(mem_ctx, crd);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t json_to_creds(struct kcm_ccache *cc,
+ json_t *jcreds)
+{
+ errno_t ret;
+ int ok;
+ size_t idx;
+ json_t *value;
+ struct kcm_cred *crd;
+
+ ok = json_is_array(jcreds);
+ if (!ok) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json creds object is not an array.\n");
+ return ERR_JSON_DECODING;
+ }
+
+ json_array_foreach(jcreds, idx, value) {
+ ret = json_elem_to_cred(cc, value, &crd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert JSON cred element [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = kcm_cc_store_creds(cc, crd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot store creds in ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t sec_json_value_to_ccache(struct kcm_ccache *cc,
+ json_t *root)
+{
+ errno_t ret;
+ json_t *princ = NULL;
+ json_t *creds = NULL;
+ json_error_t error;
+ int version;
+
+ ret = json_unpack_ex(root,
+ &error,
+ JSON_STRICT,
+ "{s:i, s:i, s:o, s:o}",
+ "version", &version,
+ "kdc_offset", &cc->kdc_offset,
+ "principal", &princ,
+ "creds", &creds);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to unpack JSON creds structure on line %d: %s\n",
+ error.line, error.text);
+ return EINVAL;
+ }
+
+ if (version != KS_JSON_VERSION) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected version %d, received version %d\n",
+ KS_JSON_VERSION, version);
+ return EINVAL;
+ }
+
+ ret = json_to_princ(cc, princ, &cc->client);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot store JSON to principal [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = json_to_creds(cc, creds);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot store JSON to creds [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return EOK;
+ }
+
+ return EOK;
+}
+
+/*
+ * sec_key is a concatenation of the ccache's UUID and name
+ * sec_value is the JSON dump of the ccache contents
+ */
+errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ const char *sec_value,
+ struct cli_creds *client,
+ struct kcm_ccache **_cc)
+{
+ errno_t ret;
+ json_t *root = NULL;
+ struct kcm_ccache *cc = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ ret = sec_value_to_json(sec_value, &root);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot store secret to JSN [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cc = talloc_zero(tmp_ctx, struct kcm_ccache);
+ if (cc == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We rely on sssd-secrets only searching the user's subtree so we
+ * set the ownership to the client
+ */
+ cc->owner.uid = cli_creds_get_uid(client);
+ cc->owner.gid = cli_creds_get_gid(client);
+
+ ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannt parse secret key [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sec_json_value_to_ccache(cc, root);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannt parse secret value [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+ *_cc = talloc_steal(mem_ctx, cc);
+done:
+ talloc_free(tmp_ctx);
+ json_decref(root);
+ return ret;
+}
diff --git a/src/responder/kcm/kcmsrv_ccache_secrets.c b/src/responder/kcm/kcmsrv_ccache_secrets.c
new file mode 100644
index 0000000000000000000000000000000000000000..8be7daea59bd6e04ab8393aae9f8adc29df11c21
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_secrets.c
@@ -0,0 +1,2169 @@
+/*
+ SSSD
+
+ KCM Server - ccache storage in sssd-secrets
+
+ Copyright (C) Red Hat, 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 "config.h"
+
+#include <stdio.h>
+#include <talloc.h>
+#include <jansson.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/tev_curl.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+
+#ifndef SSSD_SECRETS_SOCKET
+#define SSSD_SECRETS_SOCKET VARDIR"/run/secrets.socket"
+#endif /* SSSD_SECRETS_SOCKET */
+
+#ifndef SEC_TIMEOUT
+#define SEC_TIMEOUT 5
+#endif /* SEC_TIMEOUT */
+
+/* Just to keep the name of the ccache readable */
+#define MAX_CC_NUM 99999
+
+/* Compat definition of json_array_foreach for older systems */
+#ifndef json_array_foreach
+#define json_array_foreach(array, idx, value) \
+ for(idx = 0; \
+ idx < json_array_size(array) && (value = json_array_get(array, idx)); \
+ idx++)
+#endif
+
+static const char *find_by_name(const char **sec_key_list,
+ const char *name)
+{
+ const char *sec_name = NULL;
+
+ if (sec_key_list == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; sec_key_list[i]; i++) {
+ if (sec_key_match_name(sec_key_list[i], name)) {
+ sec_name = sec_key_list[i];
+ break;
+ }
+ }
+
+ return sec_name;
+}
+
+static const char *find_by_uuid(const char **sec_key_list,
+ uuid_t uuid)
+{
+ const char *sec_name = NULL;
+
+ if (sec_key_list == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; sec_key_list[i]; i++) {
+ if (sec_key_match_uuid(sec_key_list[i], uuid)) {
+ sec_name = sec_key_list[i];
+ break;
+ }
+ }
+
+ return sec_name;
+}
+
+static const char *sec_headers[] = {
+ "Content-type: application/octet-stream",
+ NULL,
+};
+
+struct ccdb_sec {
+ struct tcurl_ctx *tctx;
+};
+
+static errno_t http2errno(int http_code)
+{
+ if (http_code != 200) {
+ DEBUG(SSSDBG_OP_FAILURE, "HTTP request returned %d\n", http_code);
+ }
+
+ switch (http_code) {
+ case 200:
+ return EOK;
+ case 404:
+ return ERR_NO_CREDS;
+ case 400:
+ return ERR_INPUT_PARSE;
+ case 403:
+ return EACCES;
+ case 409:
+ return EEXIST;
+ case 413:
+ return E2BIG;
+ case 507:
+ return ENOSPC;
+ }
+
+ return EIO;
+}
+
+/*
+ * Helper request to list all UUID+name pairs
+ */
+struct sec_list_state {
+ const char **sec_key_list;
+ size_t sec_key_list_len;
+};
+
+static void sec_list_done(struct tevent_req *subreq);
+static errno_t sec_list_parse(struct sss_iobuf *outbuf,
+ TALLOC_CTX *mem_ctx,
+ const char ***_list,
+ size_t *_list_len);
+
+static struct tevent_req *sec_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ccdb_sec *secdb,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct sec_list_state *state = NULL;
+ errno_t ret;
+ const char *container_url;
+
+ req = tevent_req_create(mem_ctx, &state, struct sec_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches in the secrets store\n");
+ container_url = sec_container_url_create(state, client);
+ if (container_url == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ subreq = tcurl_http_send(state, ev, secdb->tctx,
+ TCURL_HTTP_GET,
+ SSSD_SECRETS_SOCKET,
+ container_url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, sec_list_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sec_list_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct sec_list_state *state = tevent_req_data(req,
+ struct sec_list_state);
+ struct sss_iobuf *outbuf;
+ int http_code;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, &outbuf);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "list HTTP request failed [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code == 404) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Nothing to list\n");
+ /* If no ccaches are found, return an empty list */
+ state->sec_key_list = talloc_zero_array(state, const char *, 1);
+ if (state->sec_key_list == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ } else if (http_code == 200) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu items\n", state->sec_key_list_len);
+ ret = sec_list_parse(outbuf, state,
+ &state->sec_key_list,
+ &state->sec_key_list_len);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else {
+ tevent_req_error(req, http2errno(http_code));
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "list done\n");
+ tevent_req_done(req);
+}
+
+static errno_t sec_list_parse(struct sss_iobuf *outbuf,
+ TALLOC_CTX *mem_ctx,
+ const char ***_list,
+ size_t *_list_len)
+{
+ json_t *root;
+ uint8_t *sec_http_list;
+ json_error_t error;
+ json_t *element;
+ errno_t ret;
+ int ok;
+ size_t idx;
+ const char **list = NULL;
+ size_t list_len;
+
+ sec_http_list = sss_iobuf_get_data(outbuf);
+ if (sec_http_list == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No data in output buffer?\n");
+ return EINVAL;
+ }
+
+ root = json_loads((const char *) sec_http_list, 0, &error);
+ if (root == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to parse JSON payload on line %d: %s\n",
+ error.line, error.text);
+ return ERR_JSON_DECODING;
+ }
+
+ ok = json_is_array(root);
+ if (!ok) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "list reply is not an object.\n");
+ ret = ERR_JSON_DECODING;
+ goto done;
+ }
+
+ list_len = json_array_size(root);
+ list = talloc_zero_array(mem_ctx, const char *, list_len + 1);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ json_array_foreach(root, idx, element) {
+ list[idx] = talloc_strdup(list, json_string_value(element));
+ if (list[idx] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+ *_list = list;
+ *_list_len = list_len;
+done:
+ if (ret != EOK) {
+ talloc_free(list);
+ }
+ json_decref(root);
+ return ret;
+}
+
+static errno_t sec_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char ***_sec_key_list,
+ size_t *_sec_key_list_len)
+
+{
+ struct sec_list_state *state = tevent_req_data(req,
+ struct sec_list_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_sec_key_list != NULL) {
+ *_sec_key_list = talloc_steal(mem_ctx, state->sec_key_list);
+ }
+ if (_sec_key_list_len != NULL) {
+ *_sec_key_list_len = state->sec_key_list_len;
+ }
+ return EOK;
+}
+
+/*
+ * Helper request to get a ccache by key
+ */
+struct sec_get_state {
+ const char *sec_key;
+ struct cli_creds *client;
+
+ struct kcm_ccache *cc;
+};
+
+static void sec_get_done(struct tevent_req *subreq);
+
+static struct tevent_req *sec_get_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ccdb_sec *secdb,
+ struct cli_creds *client,
+ const char *sec_key)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct sec_get_state *state = NULL;
+ errno_t ret;
+ const char *cc_url;
+
+ req = tevent_req_create(mem_ctx, &state, struct sec_get_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->sec_key = sec_key;
+ state->client = client;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Retrieving ccache %s\n", sec_key);
+
+ cc_url = sec_cc_url_create(state, state->client, state->sec_key);
+ if (cc_url == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ subreq = tcurl_http_send(state,
+ ev,
+ secdb->tctx,
+ TCURL_HTTP_GET,
+ SSSD_SECRETS_SOCKET,
+ cc_url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, sec_get_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sec_get_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct sec_get_state *state = tevent_req_data(req,
+ struct sec_get_state);
+ struct sss_iobuf *outbuf;
+ const char *sec_value;
+ int http_code;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, &outbuf);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "GET HTTP request failed [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code != 200) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "GET operation returned HTTP error %d\n", http_code);
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sec_value = (const char *) sss_iobuf_get_data(outbuf);
+ if (sec_value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No data in output buffer\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ ret = sec_kv_to_ccache(state,
+ state->sec_key,
+ sec_value,
+ state->client,
+ &state->cc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot convert JSON keyval to ccache blob [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "GET done\n");
+ tevent_req_done(req);
+}
+
+static errno_t sec_get_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct sec_get_state *state = tevent_req_data(req, struct sec_get_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+/*
+ * Helper request to get a ccache name or ID
+ */
+struct sec_get_ccache_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+ struct cli_creds *client;
+ const char *name;
+ uuid_t uuid;
+
+ const char *sec_key;
+
+ struct kcm_ccache *cc;
+};
+
+static void sec_get_ccache_list_done(struct tevent_req *subreq);
+static void sec_get_ccache_done(struct tevent_req *subreq);
+
+static struct tevent_req *sec_get_ccache_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ccdb_sec *secdb,
+ struct cli_creds *client,
+ const char *name,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct sec_get_ccache_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sec_get_ccache_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+ state->client = client;
+ state->name = name;
+ uuid_copy(state->uuid, uuid);
+
+ if ((name == NULL && uuid_is_null(uuid))
+ || (name != NULL && !uuid_is_null(uuid))) {
+ DEBUG(SSSDBG_OP_FAILURE, "Expected one of name, uuid to be set\n");
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = sec_list_send(state, ev, secdb, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, sec_get_ccache_list_done, req);
+ return req;
+
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sec_get_ccache_list_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct sec_get_ccache_state *state = tevent_req_data(req,
+ struct sec_get_ccache_state);
+ const char **sec_key_list;
+
+ ret = sec_list_recv(subreq, state, &sec_key_list, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot list keys [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->name != NULL) {
+ state->sec_key = find_by_name(sec_key_list, state->name);
+ } else {
+ state->sec_key = find_by_uuid(sec_key_list, state->uuid);
+ }
+
+ if (state->sec_key == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot find item in the ccache list\n");
+ /* Don't error out, just return an empty list */
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = sec_get_send(state,
+ state->ev,
+ state->secdb,
+ state->client,
+ state->sec_key);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sec_get_ccache_done, req);
+}
+
+static void sec_get_ccache_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sec_get_ccache_state *state = tevent_req_data(req,
+ struct sec_get_ccache_state);
+
+ ret = sec_get_recv(subreq, state, &state->cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot resolve key to ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t sec_get_ccache_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct sec_get_ccache_state *state = tevent_req_data(req,
+ struct sec_get_ccache_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+/*
+ * The actual sssd-secrets back end
+ */
+static errno_t ccdb_sec_init(struct kcm_ccdb *db)
+{
+ struct ccdb_sec *secdb = NULL;
+
+ secdb = talloc_zero(db, struct ccdb_sec);
+ if (secdb == NULL) {
+ return ENOMEM;
+ }
+
+ secdb->tctx = tcurl_init(secdb, db->ev);
+ if (secdb->tctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot initialize tcurl\n");
+ talloc_zfree(secdb);
+ return ENOMEM;
+ }
+
+ /* We just need the random numbers to generate pseudo-random ccache names
+ * and avoid conflicts */
+ srand(time(NULL));
+
+ db->db_handle = secdb;
+ return EOK;
+}
+
+/*
+ * Helper request to get a ccache by key
+ */
+struct sec_patch_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+ struct cli_creds *client;
+
+ const char *sec_key_url;
+ struct sss_iobuf *sec_value;
+};
+
+static void sec_patch_del_done(struct tevent_req *subreq);
+static void sec_patch_put_done(struct tevent_req *subreq);
+
+static struct tevent_req *sec_patch_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ccdb_sec *secdb,
+ struct cli_creds *client,
+ const char *sec_key_url,
+ struct sss_iobuf *sec_value)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct sec_patch_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sec_patch_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+ state->client = client;
+ state->sec_key_url = sec_key_url;
+ state->sec_value = sec_value;
+
+ subreq = tcurl_http_send(state, state->ev,
+ state->secdb->tctx,
+ TCURL_HTTP_DELETE,
+ SSSD_SECRETS_SOCKET,
+ state->sec_key_url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, sec_patch_del_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sec_patch_del_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sec_patch_state *state = tevent_req_data(req,
+ struct sec_patch_state);
+ int http_code;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot delete key [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code == 404) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Key %s does not exist, moving on\n", state->sec_key_url);
+ } else if (http_code != 200) {
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Adding new payload\n");
+
+ subreq = tcurl_http_send(state,
+ state->ev,
+ state->secdb->tctx,
+ TCURL_HTTP_PUT,
+ SSSD_SECRETS_SOCKET,
+ state->sec_key_url,
+ sec_headers,
+ state->sec_value,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sec_patch_put_done, req);
+}
+
+static void sec_patch_put_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sec_patch_state *state = tevent_req_data(req,
+ struct sec_patch_state);
+ int http_code;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot put new value [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code != 200) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n");
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n");
+ tevent_req_done(req);
+}
+
+
+static errno_t sec_patch_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+/* The operations between the KCM and sssd-secrets */
+
+struct ccdb_sec_nextid_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+ struct cli_creds *client;
+
+ unsigned int nextid;
+ char *nextid_name;
+
+ int maxtries;
+ int numtry;
+};
+
+static errno_t ccdb_sec_nextid_generate(struct tevent_req *req);
+static void ccdb_sec_nextid_list_done(struct tevent_req *subreq);
+
+/* Generate a unique ID */
+/* GET the name from secrets, if doesn't exist, OK, if exists, try again */
+static struct tevent_req *ccdb_sec_nextid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_sec_nextid_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_nextid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+ state->client = client;
+
+ state->maxtries = 3;
+ state->numtry = 0;
+
+ ret = ccdb_sec_nextid_generate(req);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_sec_nextid_generate(struct tevent_req *req)
+{
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_nextid_state *state = tevent_req_data(req,
+ struct ccdb_sec_nextid_state);
+
+ if (state->numtry >= state->maxtries) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to find a random ccache in %d tries\n", state->numtry);
+ return EBUSY;
+ }
+
+ state->nextid = rand() % MAX_CC_NUM;
+ state->nextid_name = talloc_asprintf(state, "%"SPRIuid":%u",
+ cli_creds_get_uid(state->client),
+ state->nextid);
+ if (state->nextid_name == NULL) {
+ return ENOMEM;
+ }
+
+ subreq = sec_list_send(state, state->ev, state->secdb, state->client);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_nextid_list_done, req);
+
+ state->numtry++;
+ return EOK;
+}
+
+static void ccdb_sec_nextid_list_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_nextid_state *state = tevent_req_data(req,
+ struct ccdb_sec_nextid_state);
+ const char **sec_key_list;
+ size_t sec_key_list_len;
+ size_t i;
+
+ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot list keys [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ for (i = 0; i < sec_key_list_len; i++) {
+ if (sec_key_match_name(sec_key_list[i], state->nextid_name) == true) {
+ break;
+ }
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to find a random key, trying again..\n");
+ if (i < sec_key_list_len) {
+ /* Try again */
+ ret = ccdb_sec_nextid_generate(req);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Generated new ccache name %u\n", state->nextid);
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_nextid_recv(struct tevent_req *req,
+ unsigned int *_nextid)
+{
+ struct ccdb_sec_nextid_state *state = tevent_req_data(req,
+ struct ccdb_sec_nextid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_nextid = state->nextid;
+ return EOK;
+}
+
+/* IN: HTTP PUT $base/default -d 'uuid' */
+/* We chose only UUID here to avoid issues later with renaming */
+struct ccdb_sec_set_default_state {
+};
+
+static void ccdb_sec_set_default_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_set_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_set_default_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ struct sss_iobuf *uuid_iobuf;
+ errno_t ret;
+ const char *url;
+ char uuid_str[UUID_STR_SIZE];
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_set_default_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ uuid_unparse(uuid, uuid_str);
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Setting the default ccache to %s\n", uuid_str);
+
+ url = sec_dfl_url_create(state, client);
+ if (url == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ uuid_iobuf = sss_iobuf_init_readonly(state,
+ (uint8_t *) uuid_str,
+ UUID_STR_SIZE);
+ if (uuid_iobuf == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ subreq = sec_patch_send(state, ev, secdb, client, url, uuid_iobuf);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_set_default_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_set_default_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = sec_patch_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sec_patch request failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Set the default ccache\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_set_default_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+/* IN: HTTP GET $base/default */
+/* OUT: uuid */
+struct ccdb_sec_get_default_state {
+ uuid_t uuid;
+};
+
+static void ccdb_sec_get_default_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_get_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_get_default_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ const char *url;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_get_default_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting the default ccache\n");
+ url = sec_dfl_url_create(state, client);
+ if (url == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ subreq = tcurl_http_send(state, ev, secdb->tctx,
+ TCURL_HTTP_GET,
+ SSSD_SECRETS_SOCKET,
+ url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_get_default_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_get_default_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ int http_code;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct ccdb_sec_get_default_state *state = tevent_req_data(req,
+ struct ccdb_sec_get_default_state);
+ struct sss_iobuf *outbuf;
+ size_t uuid_size;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, &outbuf);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Communication with the secrets responder failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code == 404) {
+ /* Return a NULL uuid */
+ uuid_clear(state->uuid);
+ tevent_req_done(req);
+ return;
+ } else if (http_code != 200) {
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ uuid_size = sss_iobuf_get_len(outbuf);
+ if (uuid_size != UUID_STR_SIZE) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected UUID size %zu\n", uuid_size);
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ uuid_parse((const char *) sss_iobuf_get_data(outbuf), state->uuid);
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got the default ccache\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_get_default_recv(struct tevent_req *req,
+ uuid_t uuid)
+{
+ struct ccdb_sec_get_default_state *state = tevent_req_data(req,
+ struct ccdb_sec_get_default_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ uuid_copy(uuid, state->uuid);
+ return EOK;
+}
+
+/* HTTP GET $base/ccache/ */
+/* OUT: a list of <uuid:name, uuid:name> */
+struct ccdb_sec_list_state {
+ uuid_t *uuid_list;
+};
+
+static void ccdb_sec_list_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_list_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches\n");
+
+ subreq = sec_list_send(state, ev, secdb, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_list_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_list_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_list_state *state = tevent_req_data(req,
+ struct ccdb_sec_list_state);
+ const char **sec_key_list;
+ size_t sec_key_list_len;
+
+ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Communication with the secrets responder failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu ccaches\n", sec_key_list_len);
+
+ state->uuid_list = talloc_array(state, uuid_t, sec_key_list_len + 1);
+ if (state->uuid_list == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ for (size_t i = 0; i < sec_key_list_len; i++) {
+ ret = sec_key_get_uuid(sec_key_list[i],
+ state->uuid_list[i]);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ }
+ /* Sentinel */
+ uuid_clear(state->uuid_list[sec_key_list_len]);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all caches done\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t **_uuid_list)
+{
+ struct ccdb_sec_list_state *state = tevent_req_data(req,
+ struct ccdb_sec_list_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
+ return EOK;
+}
+
+struct ccdb_sec_getbyuuid_state {
+ struct kcm_ccache *cc;
+};
+
+
+/* HTTP GET $base/ccache/ */
+/* OUT: a list of <uuid:name, uuid:name> */
+/* for each item in list, compare with the uuid: portion */
+/* HTTP GET $base/ccache/uuid:name */
+/* return result */
+static void ccdb_sec_getbyuuid_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_getbyuuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_getbyuuid_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_getbyuuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by UUID\n");
+
+ subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_getbyuuid_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_getbyuuid_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_getbyuuid_state *state = tevent_req_data(req,
+ struct ccdb_sec_getbyuuid_state);
+
+ ret = sec_get_ccache_recv(subreq, state, &state->cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_getbyuuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct ccdb_sec_getbyuuid_state *state = tevent_req_data(req,
+ struct ccdb_sec_getbyuuid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+/* HTTP GET $base/ccache/ */
+/* OUT: a list of <uuid:name, uuid:name> */
+/* for each item in list, compare with the :name portion */
+/* HTTP GET $base/ccache/uuid:name */
+/* return result */
+struct ccdb_sec_getbyname_state {
+ struct kcm_ccache *cc;
+};
+
+static void ccdb_sec_getbyname_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_getbyname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_getbyname_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ uuid_t null_uuid;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_getbyname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ uuid_clear(null_uuid);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by name\n");
+
+ subreq = sec_get_ccache_send(state, ev, secdb, client, name, null_uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_getbyname_state *state = tevent_req_data(req,
+ struct ccdb_sec_getbyname_state);
+
+ ret = sec_get_ccache_recv(subreq, state, &state->cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_getbyname_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct ccdb_sec_getbyname_state *state = tevent_req_data(req,
+ struct ccdb_sec_getbyname_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+struct ccdb_sec_name_by_uuid_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+ struct cli_creds *client;
+
+ uuid_t uuid;
+
+ const char *name;
+};
+
+static void ccdb_sec_name_by_uuid_done(struct tevent_req *subreq);
+
+struct tevent_req *ccdb_sec_name_by_uuid_send(TALLOC_CTX *sec_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_name_by_uuid_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ errno_t ret;
+
+ req = tevent_req_create(sec_ctx, &state, struct ccdb_sec_name_by_uuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+ state->client = client;
+ uuid_copy(state->uuid, uuid);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Translating UUID to name\n");
+
+ subreq = sec_list_send(state, state->ev, state->secdb, state->client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_name_by_uuid_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_name_by_uuid_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_name_by_uuid_state *state = tevent_req_data(req,
+ struct ccdb_sec_name_by_uuid_state);
+ const char **sec_key_list;
+ const char *name;
+ size_t sec_key_list_len;
+ size_t i;
+
+ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ for (i = 0; i < sec_key_list_len; i++) {
+ if (sec_key_match_uuid(sec_key_list[i], state->uuid) == true) {
+ /* Match, copy name */
+ name = sec_key_get_name(sec_key_list[i]);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Malformed key, cannot get name\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ state->name = talloc_strdup(state, name);
+ if (state->name == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
+ tevent_req_done(req);
+ return;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such UUID\n");
+ tevent_req_error(req, ERR_NO_CREDS);
+ return;
+}
+
+errno_t ccdb_sec_name_by_uuid_recv(struct tevent_req *req,
+ TALLOC_CTX *sec_ctx,
+ const char **_name)
+{
+ struct ccdb_sec_name_by_uuid_state *state = tevent_req_data(req,
+ struct ccdb_sec_name_by_uuid_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_name = talloc_steal(sec_ctx, state->name);
+ return EOK;
+}
+
+struct ccdb_sec_uuid_by_name_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+ struct cli_creds *client;
+
+ const char *name;
+
+ uuid_t uuid;
+};
+
+static void ccdb_sec_uuid_by_name_done(struct tevent_req *subreq);
+
+struct tevent_req *ccdb_sec_uuid_by_name_send(TALLOC_CTX *sec_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_uuid_by_name_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ errno_t ret;
+
+ req = tevent_req_create(sec_ctx, &state, struct ccdb_sec_uuid_by_name_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+ state->client = client;
+ state->name = name;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Translating name to UUID\n");
+
+ subreq = sec_list_send(state, state->ev, state->secdb, state->client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_uuid_by_name_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_uuid_by_name_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_uuid_by_name_state *state = tevent_req_data(req,
+ struct ccdb_sec_uuid_by_name_state);
+ const char **sec_key_list;
+ size_t sec_key_list_len;
+ size_t i;
+
+ ret = sec_list_recv(subreq, state, &sec_key_list, &sec_key_list_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ for (i = 0; i < sec_key_list_len; i++) {
+ if (sec_key_match_name(sec_key_list[i], state->name) == true) {
+ /* Match, copy UUID */
+ ret = sec_key_get_uuid(sec_key_list[i], state->uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Malformed key, cannot get UUID\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by name\n");
+ tevent_req_done(req);
+ return;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No such name\n");
+ tevent_req_error(req, ERR_NO_CREDS);
+ return;
+}
+
+errno_t ccdb_sec_uuid_by_name_recv(struct tevent_req *req,
+ TALLOC_CTX *sec_ctx,
+ uuid_t _uuid)
+{
+ struct ccdb_sec_uuid_by_name_state *state = tevent_req_data(req,
+ struct ccdb_sec_uuid_by_name_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ uuid_copy(_uuid, state->uuid);
+ return EOK;
+}
+
+/* HTTP POST $base to create the container */
+/* HTTP PUT $base to create the container. Since PUT errors out on duplicates, at least
+ * we fail consistently here and don't overwrite the ccache on concurrent requests
+ */
+struct ccdb_sec_create_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+
+ const char *key_url;
+ struct sss_iobuf *ccache_payload;
+};
+
+static void ccdb_sec_container_done(struct tevent_req *subreq);
+static void ccdb_sec_ccache_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_create_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ struct kcm_ccache *cc)
+{
+ struct tevent_req *subreq = NULL;
+ struct tevent_req *req = NULL;
+ struct ccdb_sec_create_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+ errno_t ret;
+ const char *container_url;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_create_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Creating ccache storage for %s\n", cc->name);
+
+ /* Do the encoding asap so that if we fail, we don't even attempt any
+ * writes */
+ ret = kcm_ccache_to_sec_input(state, cc, client, &state->key_url, &state->ccache_payload);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert cache %s to JSON [%d]: %s\n",
+ cc->name, ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ container_url = sec_container_url_create(state, client);
+ if (container_url == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Creating the ccache container\n");
+ subreq = tcurl_http_send(state, ev, secdb->tctx,
+ TCURL_HTTP_POST,
+ SSSD_SECRETS_SOCKET,
+ container_url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_container_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_container_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ int http_code;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct ccdb_sec_create_state *state = tevent_req_data(req,
+ struct ccdb_sec_create_state);
+
+ ret = tcurl_http_recv(state, subreq, &http_code, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Communication with the secrets responder failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Conflict is not an error as multiple ccaches are under the same
+ * container */
+ if (http_code == 409) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Container already exists, ignoring\n");
+ } else if (http_code != 200) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to create the ccache container\n");
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache container created\n");
+ DEBUG(SSSDBG_TRACE_INTERNAL, "creating empty ccache payload\n");
+
+ subreq = tcurl_http_send(state,
+ state->ev,
+ state->secdb->tctx,
+ TCURL_HTTP_PUT,
+ SSSD_SECRETS_SOCKET,
+ state->key_url,
+ sec_headers,
+ state->ccache_payload,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_ccache_done, req);
+}
+
+static void ccdb_sec_ccache_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ int http_code;
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct ccdb_sec_create_state *state = tevent_req_data(req,
+ struct ccdb_sec_create_state);
+
+ ret = tcurl_http_recv(state, subreq, &http_code, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Communication with the secrets responder failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code != 200) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n");
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_create_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct ccdb_sec_mod_cred_state {
+ struct tevent_context *ev;
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+ struct kcm_mod_ctx *mod_cc;
+
+ struct ccdb_sec *secdb;
+};
+
+static void ccdb_sec_mod_cred_get_done(struct tevent_req *subreq);
+static void ccdb_sec_mod_cred_patch_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_mod_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct kcm_mod_ctx *mod_cc)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_mod_cred_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_mod_cred_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->db =db;
+ state->client = client;
+ state->secdb = secdb;
+ state->mod_cc = mod_cc;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Modifying ccache\n");
+
+ subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, *ccdb_sec_mod_cred_get_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_mod_cred_get_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_mod_cred_state *state = tevent_req_data(req,
+ struct ccdb_sec_mod_cred_state);
+ struct kcm_ccache *cc;
+ const char *url;
+ struct sss_iobuf *payload;
+
+ ret = sec_get_ccache_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot retrieve the ccache [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No such ccache\n");
+ tevent_req_error(req, ERR_NO_CREDS);
+ return;
+ }
+
+ kcm_mod_cc(cc, state->mod_cc);
+
+ ret = kcm_ccache_to_sec_input(state, cc, state->client, &url, &payload);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to marshall modified ccache to payload [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sec_patch_send(state,
+ state->ev,
+ state->secdb,
+ state->client,
+ url,
+ payload);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_mod_cred_patch_done, req);
+}
+
+static void ccdb_sec_mod_cred_patch_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+
+ ret = sec_patch_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sec_patch request failed [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache modified\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_mod_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct ccdb_sec_store_cred_state {
+ struct tevent_context *ev;
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+ struct sss_iobuf *cred_blob;
+
+ struct ccdb_sec *secdb;
+};
+
+static void ccdb_sec_store_cred_get_done(struct tevent_req *subreq);
+static void ccdb_sec_store_cred_patch_done(struct tevent_req *subreq);
+
+/* HTTP DEL/PUT $base/ccache/uuid:name */
+static struct tevent_req *ccdb_sec_store_cred_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_store_cred_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_store_cred_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->db =db;
+ state->client = client;
+ state->cred_blob = cred_blob;
+ state->secdb = secdb;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Storing creds in ccache\n");
+
+ subreq = sec_get_ccache_send(state, ev, secdb, client, NULL, uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, *ccdb_sec_store_cred_get_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_store_cred_get_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_store_cred_state *state = tevent_req_data(req,
+ struct ccdb_sec_store_cred_state);
+ struct kcm_ccache *cc;
+ const char *url;
+ struct sss_iobuf *payload;
+
+ ret = sec_get_ccache_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = kcm_cc_store_cred_blob(cc, state->cred_blob);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot store credentials to ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = kcm_ccache_to_sec_input(state, cc, state->client, &url, &payload);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to marshall modified ccache to payload [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sec_patch_send(state,
+ state->ev,
+ state->secdb,
+ state->client,
+ url,
+ payload);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_store_cred_patch_done, req);
+}
+
+static void ccdb_sec_store_cred_patch_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+
+ ret = sec_patch_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sec_patch request failed [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache creds stored\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_store_cred_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+/* HTTP DELETE $base/ccache/uuid:name */
+struct ccdb_sec_delete_state {
+ struct tevent_context *ev;
+ struct ccdb_sec *secdb;
+ struct cli_creds *client;
+ uuid_t uuid;
+
+ size_t sec_key_list_len;
+};
+
+static void ccdb_sec_delete_list_done(struct tevent_req *subreq);
+static void ccdb_sec_delete_cc_done(struct tevent_req *subreq);
+static void ccdb_sec_delete_container_done(struct tevent_req *subreq);
+
+static struct tevent_req *ccdb_sec_delete_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ccdb_sec_delete_state *state = NULL;
+ struct ccdb_sec *secdb = talloc_get_type(db->db_handle, struct ccdb_sec);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_sec_delete_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->secdb = secdb;
+ state->client = client;
+ uuid_copy(state->uuid, uuid);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Deleting ccache\n");
+
+ subreq = sec_list_send(state, ev, secdb, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_delete_list_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ccdb_sec_delete_list_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_delete_state *state = tevent_req_data(req,
+ struct ccdb_sec_delete_state);
+ const char **sec_key_list;
+ const char *sec_key;
+ const char *cc_url;
+
+ ret = sec_list_recv(subreq,
+ state,
+ &sec_key_list,
+ &state->sec_key_list_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (sec_key_list == 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No ccaches to delete\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ sec_key = find_by_uuid(sec_key_list, state->uuid);
+ if (sec_key == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find ccache by UUID\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ cc_url = sec_cc_url_create(state, state->client, sec_key);
+ if (cc_url == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ subreq = tcurl_http_send(state, state->ev,
+ state->secdb->tctx,
+ TCURL_HTTP_DELETE,
+ SSSD_SECRETS_SOCKET,
+ cc_url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_delete_cc_done, req);
+}
+
+static void ccdb_sec_delete_cc_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_delete_state *state = tevent_req_data(req,
+ struct ccdb_sec_delete_state);
+ int http_code;
+ const char *container_url;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot delete ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code != 200) {
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->sec_key_list_len != 1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "There are other ccaches, done\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing ccache container\n");
+
+ container_url = sec_container_url_create(state, state->client);
+ if (container_url == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ subreq = tcurl_http_send(state, state->ev,
+ state->secdb->tctx,
+ TCURL_HTTP_DELETE,
+ SSSD_SECRETS_SOCKET,
+ container_url,
+ sec_headers,
+ NULL,
+ SEC_TIMEOUT);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, ccdb_sec_delete_container_done, req);
+}
+
+static void ccdb_sec_delete_container_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ccdb_sec_delete_state *state = tevent_req_data(req,
+ struct ccdb_sec_delete_state);
+ int http_code;
+
+ ret = tcurl_http_recv(state, subreq, &http_code, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot delete ccache container [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (http_code != 200) {
+ ret = http2errno(http_code);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removed ccache container\n");
+ tevent_req_done(req);
+}
+
+static errno_t ccdb_sec_delete_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+const struct kcm_ccdb_ops ccdb_sec_ops = {
+ .init = ccdb_sec_init,
+
+ .nextid_send = ccdb_sec_nextid_send,
+ .nextid_recv = ccdb_sec_nextid_recv,
+
+ .set_default_send = ccdb_sec_set_default_send,
+ .set_default_recv = ccdb_sec_set_default_recv,
+
+ .get_default_send = ccdb_sec_get_default_send,
+ .get_default_recv = ccdb_sec_get_default_recv,
+
+ .list_send = ccdb_sec_list_send,
+ .list_recv = ccdb_sec_list_recv,
+
+ .getbyname_send = ccdb_sec_getbyname_send,
+ .getbyname_recv = ccdb_sec_getbyname_recv,
+
+ .getbyuuid_send = ccdb_sec_getbyuuid_send,
+ .getbyuuid_recv = ccdb_sec_getbyuuid_recv,
+
+ .name_by_uuid_send = ccdb_sec_name_by_uuid_send,
+ .name_by_uuid_recv = ccdb_sec_name_by_uuid_recv,
+
+ .uuid_by_name_send = ccdb_sec_uuid_by_name_send,
+ .uuid_by_name_recv = ccdb_sec_uuid_by_name_recv,
+
+ .create_send = ccdb_sec_create_send,
+ .create_recv = ccdb_sec_create_recv,
+
+ .mod_send = ccdb_sec_mod_send,
+ .mod_recv = ccdb_sec_mod_recv,
+
+ .store_cred_send = ccdb_sec_store_cred_send,
+ .store_cred_recv = ccdb_sec_store_cred_recv,
+
+ .delete_send = ccdb_sec_delete_send,
+ .delete_recv = ccdb_sec_delete_recv,
+};
diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c
new file mode 100644
index 0000000000000000000000000000000000000000..8eff2f501066c70a8730cd3d4dc41b92d7a03e4c
--- /dev/null
+++ b/src/tests/cmocka/test_kcm_json_marshalling.c
@@ -0,0 +1,234 @@
+/*
+ Copyright (C) 2017 Red Hat
+
+ SSSD tests: Test KCM JSON marshalling
+
+ 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 "config.h"
+
+#include <stdio.h>
+#include <popt.h>
+
+#include "util/util_creds.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+#include "tests/cmocka/common_mock.h"
+
+#define TEST_REALM "TESTREALM"
+#define TEST_PRINC_COMPONENT "PRINC_NAME"
+
+#define TEST_CREDS "TESTCREDS"
+
+const struct kcm_ccdb_ops ccdb_mem_ops;
+const struct kcm_ccdb_ops ccdb_sec_ops;
+
+struct kcm_marshalling_test_ctx {
+ krb5_context kctx;
+ krb5_principal princ;
+};
+
+static int setup_kcm_marshalling(void **state)
+{
+ struct kcm_marshalling_test_ctx *test_ctx;
+ krb5_error_code kerr;
+
+ test_ctx = talloc_zero(NULL, struct kcm_marshalling_test_ctx);
+ assert_non_null(test_ctx);
+
+ kerr = krb5_init_context(&test_ctx->kctx);
+ assert_int_equal(kerr, 0);
+
+ kerr = krb5_build_principal(test_ctx->kctx,
+ &test_ctx->princ,
+ sizeof(TEST_REALM)-1, TEST_REALM,
+ TEST_PRINC_COMPONENT, NULL);
+ assert_int_equal(kerr, 0);
+
+ *state = test_ctx;
+ return 0;
+}
+
+static int teardown_kcm_marshalling(void **state)
+{
+ struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
+ struct kcm_marshalling_test_ctx);
+ assert_non_null(test_ctx);
+
+ krb5_free_principal(test_ctx->kctx, test_ctx->princ);
+ krb5_free_context(test_ctx->kctx);
+ talloc_free(test_ctx);
+ return 0;
+}
+
+static void assert_cc_name_equal(struct kcm_ccache *cc1,
+ struct kcm_ccache *cc2)
+{
+ const char *name1, *name2;
+
+ name1 = kcm_cc_get_name(cc1);
+ name2 = kcm_cc_get_name(cc2);
+ assert_string_equal(name1, name2);
+}
+
+static void assert_cc_uuid_equal(struct kcm_ccache *cc1,
+ struct kcm_ccache *cc2)
+{
+ uuid_t u1, u2;
+ errno_t ret;
+
+ ret = kcm_cc_get_uuid(cc1, u1);
+ assert_int_equal(ret, EOK);
+ ret = kcm_cc_get_uuid(cc2, u2);
+ assert_int_equal(ret, EOK);
+ ret = uuid_compare(u1, u2);
+ assert_int_equal(ret, 0);
+}
+
+static void assert_cc_princ_equal(struct kcm_ccache *cc1,
+ struct kcm_ccache *cc2)
+{
+ krb5_principal p1;
+ krb5_principal p2;
+ char *name1;
+ char *name2;
+ krb5_error_code kerr;
+
+ p1 = kcm_cc_get_client_principal(cc1);
+ p2 = kcm_cc_get_client_principal(cc1);
+
+ kerr = krb5_unparse_name(NULL, p1, &name1);
+ assert_int_equal(kerr, 0);
+ kerr = krb5_unparse_name(NULL, p2, &name2);
+ assert_int_equal(kerr, 0);
+
+ assert_string_equal(name1, name2);
+ krb5_free_unparsed_name(NULL, name1);
+ krb5_free_unparsed_name(NULL, name2);
+}
+
+static void assert_cc_offset_equal(struct kcm_ccache *cc1,
+ struct kcm_ccache *cc2)
+{
+ int32_t off1;
+ int32_t off2;
+
+ off1 = kcm_cc_get_offset(cc1);
+ off2 = kcm_cc_get_offset(cc2);
+ assert_int_equal(off1, off2);
+}
+
+static void assert_cc_equal(struct kcm_ccache *cc1,
+ struct kcm_ccache *cc2)
+{
+ assert_cc_name_equal(cc1, cc2);
+ assert_cc_uuid_equal(cc1, cc2);
+ assert_cc_princ_equal(cc1, cc2);
+ assert_cc_offset_equal(cc1, cc2);
+}
+
+static void test_kcm_ccache_marshall_unmarshall(void **state)
+{
+ struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
+ struct kcm_marshalling_test_ctx);
+ errno_t ret;
+ struct cli_creds owner;
+ struct kcm_ccache *cc;
+ struct kcm_ccache *cc2;
+ const char *url;
+ struct sss_iobuf *payload;
+ const char *name;
+ const char *key;
+ uint8_t *data;
+
+ owner.ucred.uid = getuid();
+ owner.ucred.gid = getuid();
+
+ name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid());
+ assert_non_null(name);
+
+ ret = kcm_cc_new(test_ctx,
+ test_ctx->kctx,
+ &owner,
+ name,
+ test_ctx->princ,
+ &cc);
+ assert_int_equal(ret, EOK);
+
+ ret = kcm_ccache_to_sec_input(test_ctx,
+ cc,
+ &owner,
+ &url,
+ &payload);
+ assert_int_equal(ret, EOK);
+
+ key = strrchr(url, '/') + 1;
+ assert_non_null(key);
+
+ data = sss_iobuf_get_data(payload);
+ assert_non_null(data);
+
+ ret = sec_kv_to_ccache(test_ctx,
+ key,
+ (const char *) data,
+ &owner,
+ &cc2);
+ assert_int_equal(ret, EOK);
+
+ assert_cc_equal(cc, cc2);
+}
+
+int main(int argc, const char *argv[])
+{
+ poptContext pc;
+ int opt;
+ int rv;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_DEBUG_OPTS
+ POPT_TABLEEND
+ };
+
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall,
+ setup_kcm_marshalling,
+ teardown_kcm_marshalling),
+ };
+
+ /* Set debug level to invalid value so we can deside if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ 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_CLI_INIT(debug_level);
+
+ /* Even though normally the tests should clean up after themselves
+ * they might not after a failed run. Remove the old db to be sure */
+ tests_set_cwd();
+
+ rv = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rv;
+}
diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py
index ad1e4923bfe339cb040464757431d2ef3bf57ce1..11f80a1803b4ad9b8e8857bf9a8a244d4816f0a2 100644
--- a/src/tests/intg/test_kcm.py
+++ b/src/tests/intg/test_kcm.py
@@ -27,7 +27,8 @@ import signal
import kdc
import krb5utils
import config
-from util import unindent, run_shell
+from util import unindent
+from test_secrets import create_sssd_secrets_fixture
class KcmTestEnv(object):
def __init__(self, k5kdc, k5util):
@@ -107,15 +108,8 @@ def create_sssd_kcm_fixture(sock_path, request):
return kcm_pid
-@pytest.fixture
-def setup_for_kcm(request, kdc_instance):
- """
- Just set up the local provider for tests and enable the KCM
- responder
- """
- kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
-
- sssd_conf = unindent("""\
+def create_sssd_conf(kcm_path, ccache_storage):
+ return unindent("""\
[sssd]
domains = local
services = nss
@@ -125,8 +119,11 @@ def setup_for_kcm(request, kdc_instance):
[kcm]
socket_path = {kcm_path}
+ ccache_storage = {ccache_storage}
""").format(**locals())
+
+def common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf):
kcm_socket_include = unindent("""
[libdefaults]
default_ccache_name = KCM:
@@ -142,11 +139,35 @@ def setup_for_kcm(request, kdc_instance):
return KcmTestEnv(kdc_instance, k5util)
-def test_kcm_init_list_destroy(setup_for_kcm):
+@pytest.fixture
+def setup_for_kcm_mem(request, kdc_instance):
+ """
+ Just set up the local provider for tests and enable the KCM
+ responder
+ """
+ kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
+ sssd_conf = create_sssd_conf(kcm_path, "memory")
+ return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf)
+
+@pytest.fixture
+def setup_secrets(request):
+ create_sssd_secrets_fixture(request)
+
+@pytest.fixture
+def setup_for_kcm_sec(request, kdc_instance):
+ """
+ Just set up the local provider for tests and enable the KCM
+ responder
+ """
+ kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
+ sssd_conf = create_sssd_conf(kcm_path, "secrets")
+ return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf)
+
+
+def kcm_init_list_destroy(testenv):
"""
Test that kinit, kdestroy and klist work with KCM
"""
- testenv = setup_for_kcm
testenv.k5kdc.add_principal("kcmtest", "Secret123")
ok = testenv.k5util.has_principal("kcmtest@KCMTEST")
@@ -172,12 +193,22 @@ def test_kcm_init_list_destroy(setup_for_kcm):
assert nprincs == 0
-def test_kcm_overwrite(setup_for_kcm):
+def test_kcm_mem_init_list_destroy(setup_for_kcm_mem):
+ testenv = setup_for_kcm_mem
+ kcm_init_list_destroy(testenv)
+
+
+def test_kcm_sec_init_list_destroy(setup_for_kcm_sec,
+ setup_secrets):
+ testenv = setup_for_kcm_sec
+ kcm_init_list_destroy(testenv)
+
+
+def kcm_overwrite(testenv):
"""
That that reusing a ccache reinitializes the cache and doesn't
add the same principal twice
"""
- testenv = setup_for_kcm
testenv.k5kdc.add_principal("kcmtest", "Secret123")
exp_ccache = {'kcmtest@KCMTEST': ['krbtgt/KCMTEST@KCMTEST']}
@@ -192,12 +223,22 @@ def test_kcm_overwrite(setup_for_kcm):
assert exp_ccache == testenv.k5util.list_all_princs()
-def test_collection_init_list_destroy(setup_for_kcm):
+def test_kcm_mem_overwrite(setup_for_kcm_mem):
+ testenv = setup_for_kcm_mem
+ kcm_overwrite(testenv)
+
+
+def test_kcm_sec_overwrite(setup_for_kcm_sec,
+ setup_secrets):
+ testenv = setup_for_kcm_sec
+ kcm_overwrite(testenv)
+
+
+def collection_init_list_destroy(testenv):
"""
Test that multiple principals and service tickets can be stored
in a collection.
"""
- testenv = setup_for_kcm
testenv.k5kdc.add_principal("alice", "alicepw")
testenv.k5kdc.add_principal("bob", "bobpw")
testenv.k5kdc.add_principal("carol", "carolpw")
@@ -241,7 +282,11 @@ def test_collection_init_list_destroy(setup_for_kcm):
out = testenv.k5util.kdestroy()
assert out == 0
- assert testenv.k5util.default_principal() == 'bob@KCMTEST'
+ # If the default is removed, KCM just uses whetever is the first entry
+ # in the collection as the default. And sine the KCM back ends don't
+ # guarantee if they are FIFO or LIFO, just check for either alice or bob
+ assert testenv.k5util.default_principal() in \
+ ['alice@KCMTEST', 'bob@KCMTEST']
cc_coll = testenv.k5util.list_all_princs()
assert len(cc_coll) == 2
assert cc_coll['alice@KCMTEST'] == ['krbtgt/KCMTEST@KCMTEST']
@@ -255,11 +300,21 @@ def test_collection_init_list_destroy(setup_for_kcm):
#assert len(cc_coll) == 0
-def test_kswitch(setup_for_kcm):
+def test_kcm_mem_collection_init_list_destroy(setup_for_kcm_mem):
+ testenv = setup_for_kcm_mem
+ collection_init_list_destroy(testenv)
+
+
+def test_kcm_sec_collection_init_list_destroy(setup_for_kcm_sec,
+ setup_secrets):
+ testenv = setup_for_kcm_sec
+ collection_init_list_destroy(testenv)
+
+
+def exercise_kswitch(testenv):
"""
Test switching between principals
"""
- testenv = setup_for_kcm
testenv.k5kdc.add_principal("alice", "alicepw")
testenv.k5kdc.add_principal("bob", "bobpw")
testenv.k5kdc.add_principal("host/somehostname")
@@ -301,12 +356,22 @@ def test_kswitch(setup_for_kcm):
'host/differenthostname@KCMTEST'])
-def test_subsidiaries(setup_for_kcm):
+def test_kcm_mem_kswitch(setup_for_kcm_mem):
+ testenv = setup_for_kcm_mem
+ exercise_kswitch(testenv)
+
+
+def test_kcm_sec_kswitch(setup_for_kcm_sec,
+ setup_secrets):
+ testenv = setup_for_kcm_sec
+ exercise_kswitch(testenv)
+
+
+def exercise_subsidiaries(testenv):
"""
Test that subsidiary caches are usable and KCM: without specifying UID
can be used to identify the collection
"""
- testenv = setup_for_kcm
testenv.k5kdc.add_principal("alice", "alicepw")
testenv.k5kdc.add_principal("bob", "bobpw")
testenv.k5kdc.add_principal("host/somehostname")
@@ -346,11 +411,21 @@ def test_subsidiaries(setup_for_kcm):
'host/differenthostname@KCMTEST'])
-def test_kdestroy_nocache(setup_for_kcm):
+def test_kcm_mem_subsidiaries(setup_for_kcm_mem):
+ testenv = setup_for_kcm_mem
+ exercise_subsidiaries(testenv)
+
+
+def test_kcm_sec_subsidiaries(setup_for_kcm_sec,
+ setup_secrets):
+ testenv = setup_for_kcm_sec
+ exercise_subsidiaries(testenv)
+
+
+def kdestroy_nocache(testenv):
"""
Destroying a non-existing ccache should not throw an error
"""
- testenv = setup_for_kcm
testenv.k5kdc.add_principal("alice", "alicepw")
out, _, _ = testenv.k5util.kinit("alice", "alicepw")
assert out == 0
@@ -359,3 +434,14 @@ def test_kdestroy_nocache(setup_for_kcm):
assert out == 0
out = testenv.k5util.kdestroy()
assert out == 0
+
+
+def test_kcm_mem_kdestroy_nocache(setup_for_kcm_mem):
+ testenv = setup_for_kcm_mem
+ exercise_subsidiaries(testenv)
+
+
+def test_kcm_sec_kdestroy_nocache(setup_for_kcm_sec,
+ setup_secrets):
+ testenv = setup_for_kcm_sec
+ exercise_subsidiaries(testenv)
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 23cfdf9c6116a2c8e569a041e8289b65a112fd08..60c2f439b3e39b1dbff353e429114cb5a3070052 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -109,6 +109,8 @@ struct err_string error_to_str[] = {
{ "KCM operation not implemented" }, /* ERR_KCM_OP_NOT_IMPLEMENTED */
{ "End of credential cache reached" }, /* ERR_KCM_CC_END */
{ "Credential cache name not allowed" }, /* ERR_KCM_WRONG_CCNAME_FORMAT */
+ { "Cannot encode a JSON object to string" }, /* ERR_JSON_ENCODING */
+ { "Cannot decode a JSON object from string" }, /* ERR_JSON_DECODING */
{ "ERR_LAST" } /* ERR_LAST */
};
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 387d481616db1ed5e22b73fae82632a582fae946..4e9da814702e2cd46edc52fd5c2ae5f640602609 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -131,6 +131,8 @@ enum sssd_errors {
ERR_KCM_OP_NOT_IMPLEMENTED,
ERR_KCM_CC_END,
ERR_KCM_WRONG_CCNAME_FORMAT,
+ ERR_JSON_ENCODING,
+ ERR_JSON_DECODING,
ERR_LAST /* ALWAYS LAST */
};
--
2.9.3