Blob Blame History Raw
From 8c30024303436d98818977288e28511ed74c018a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Mon, 20 Mar 2017 11:49:43 +0100
Subject: [PATCH 33/36] KCM: Store ccaches in secrets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Adds a new KCM responder ccache back end that forwards all requests to
sssd-secrets.

Reviewed-by: Michal Židek <mzidek@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
---
 Makefile.am                                  |   40 +-
 contrib/sssd.spec.in                         |    1 +
 src/responder/kcm/kcmsrv_ccache.h            |   49 +
 src/responder/kcm/kcmsrv_ccache_json.c       |  921 +++++++++++
 src/responder/kcm/kcmsrv_ccache_secrets.c    | 2169 ++++++++++++++++++++++++++
 src/tests/cmocka/test_kcm_json_marshalling.c |  234 +++
 src/tests/intg/test_kcm.py                   |  132 +-
 src/util/util_errors.c                       |    2 +
 src/util/util_errors.h                       |    2 +
 9 files changed, 3525 insertions(+), 25 deletions(-)
 create mode 100644 src/responder/kcm/kcmsrv_ccache_json.c
 create mode 100644 src/responder/kcm/kcmsrv_ccache_secrets.c
 create mode 100644 src/tests/cmocka/test_kcm_json_marshalling.c

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