387014f928
required for building freeipa-4.5.x in rawhide
3788 lines
115 KiB
Diff
3788 lines
115 KiB
Diff
From cac0db2f8004ae88b9263dc3888a11a2d3d3d114 Mon Sep 17 00:00:00 2001
|
|
From: Jakub Hrozek <jhrozek@redhat.com>
|
|
Date: Mon, 20 Mar 2017 11:49:43 +0100
|
|
Subject: [PATCH 35/97] 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.12.2
|
|
|