dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone

Blame SOURCES/0024-KCM-Implement-an-internal-ccache-storage-and-retriev.patch

bb7cd1
From 79f6ccd2dc3f0c1369d5a93678c88ee76ec761e0 Mon Sep 17 00:00:00 2001
bb7cd1
From: Jakub Hrozek <jhrozek@redhat.com>
bb7cd1
Date: Tue, 7 Mar 2017 13:49:21 +0100
bb7cd1
Subject: [PATCH 24/36] KCM: Implement an internal ccache storage and retrieval
bb7cd1
 API
bb7cd1
MIME-Version: 1.0
bb7cd1
Content-Type: text/plain; charset=UTF-8
bb7cd1
Content-Transfer-Encoding: 8bit
bb7cd1
bb7cd1
In order for the KCM server to work with ccaches stored in different
bb7cd1
locations, implement a middle-man between the KCM server and the ccache
bb7cd1
storage.
bb7cd1
bb7cd1
This module has asynchronous API because we can't assume anything about
bb7cd1
where the ccaches are stored.
bb7cd1
bb7cd1
Reviewed-by: Michal Židek <mzidek@redhat.com>
bb7cd1
Reviewed-by: Simo Sorce <simo@redhat.com>
bb7cd1
---
bb7cd1
 Makefile.am                           |    9 +
bb7cd1
 configure.ac                          |    1 +
bb7cd1
 contrib/sssd.spec.in                  |    1 +
bb7cd1
 src/external/libuuid.m4               |   17 +
bb7cd1
 src/responder/kcm/kcmsrv_ccache.c     | 1423 +++++++++++++++++++++++++++++++++
bb7cd1
 src/responder/kcm/kcmsrv_ccache.h     |  306 +++++++
bb7cd1
 src/responder/kcm/kcmsrv_ccache_be.h  |  204 +++++
bb7cd1
 src/responder/kcm/kcmsrv_ccache_pvt.h |   62 ++
bb7cd1
 src/responder/kcm/kcmsrv_pvt.h        |    1 +
bb7cd1
 9 files changed, 2024 insertions(+)
bb7cd1
 create mode 100644 src/external/libuuid.m4
bb7cd1
 create mode 100644 src/responder/kcm/kcmsrv_ccache.c
bb7cd1
 create mode 100644 src/responder/kcm/kcmsrv_ccache.h
bb7cd1
 create mode 100644 src/responder/kcm/kcmsrv_ccache_be.h
bb7cd1
 create mode 100644 src/responder/kcm/kcmsrv_ccache_pvt.h
bb7cd1
bb7cd1
diff --git a/Makefile.am b/Makefile.am
bb7cd1
index 4248536e90370c1aab59549a9c18408ef314e6d4..a2b9dc49e95fa2d025f5174d2902866fab180a78 100644
bb7cd1
--- a/Makefile.am
bb7cd1
+++ b/Makefile.am
bb7cd1
@@ -711,6 +711,9 @@ dist_noinst_HEADERS = \
bb7cd1
     src/responder/secrets/secsrv_proxy.h \
bb7cd1
     src/responder/kcm/kcm.h \
bb7cd1
     src/responder/kcm/kcmsrv_pvt.h \
bb7cd1
+    src/responder/kcm/kcmsrv_ccache.h \
bb7cd1
+    src/responder/kcm/kcmsrv_ccache_pvt.h \
bb7cd1
+    src/responder/kcm/kcmsrv_ccache_be.h \
bb7cd1
     src/sbus/sbus_client.h \
bb7cd1
     src/sbus/sssd_dbus.h \
bb7cd1
     src/sbus/sssd_dbus_meta.h \
bb7cd1
@@ -1488,16 +1491,22 @@ if BUILD_KCM
bb7cd1
 sssd_kcm_SOURCES = \
bb7cd1
     src/responder/kcm/kcm.c \
bb7cd1
     src/responder/kcm/kcmsrv_cmd.c \
bb7cd1
+    src/responder/kcm/kcmsrv_ccache.c \
bb7cd1
     src/util/sss_sockets.c \
bb7cd1
+    src/util/sss_krb5.c \
bb7cd1
+    src/util/sss_iobuf.c \
bb7cd1
     $(SSSD_RESPONDER_OBJ) \
bb7cd1
     $(NULL)
bb7cd1
 sssd_kcm_CFLAGS = \
bb7cd1
     $(AM_CFLAGS) \
bb7cd1
     $(KRB5_CFLAGS) \
bb7cd1
+    $(UUID_CFLAGS) \
bb7cd1
     $(NULL)
bb7cd1
 sssd_kcm_LDADD = \
bb7cd1
     $(KRB5_LIBS) \
bb7cd1
     $(SSSD_LIBS) \
bb7cd1
+    $(UUID_LIBS) \
bb7cd1
+    $(SYSTEMD_DAEMON_LIBS) \
bb7cd1
     $(SSSD_INTERNAL_LTLIBS) \
bb7cd1
     $(NULL)
bb7cd1
 endif
bb7cd1
diff --git a/configure.ac b/configure.ac
bb7cd1
index c363d48a806cc1998e85779a92b6b59b0e2a5c9c..cf5e2557ef0a1bd6374200aa33abea6c509d03aa 100644
bb7cd1
--- a/configure.ac
bb7cd1
+++ b/configure.ac
bb7cd1
@@ -202,6 +202,7 @@ fi
bb7cd1
 
bb7cd1
 if test x$with_kcm = xyes; then
bb7cd1
     m4_include([src/external/libcurl.m4])
bb7cd1
+    m4_include([src/external/libuuid.m4])
bb7cd1
 fi
bb7cd1
 # This variable is defined by external/libcurl.m4, but conditionals
bb7cd1
 # must be always evaluated
bb7cd1
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
bb7cd1
index 5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056..52d33b4de281dc1d91a9027ac1c8c878e66fb396 100644
bb7cd1
--- a/contrib/sssd.spec.in
bb7cd1
+++ b/contrib/sssd.spec.in
bb7cd1
@@ -222,6 +222,7 @@ BuildRequires: systemtap-sdt-devel
bb7cd1
 %endif
bb7cd1
 BuildRequires: http-parser-devel
bb7cd1
 BuildRequires: jansson-devel
bb7cd1
+BuildRequires: libuuid-devel
bb7cd1
 
bb7cd1
 %description
bb7cd1
 Provides a set of daemons to manage access to remote directories and
bb7cd1
diff --git a/src/external/libuuid.m4 b/src/external/libuuid.m4
bb7cd1
new file mode 100644
bb7cd1
index 0000000000000000000000000000000000000000..55411a2118bd787c9d50ba61f9cb791e1c76088d
bb7cd1
--- /dev/null
bb7cd1
+++ b/src/external/libuuid.m4
bb7cd1
@@ -0,0 +1,17 @@
bb7cd1
+AC_SUBST(UUID_LIBS)
bb7cd1
+AC_SUBST(UUID_CFLAGS)
bb7cd1
+
bb7cd1
+PKG_CHECK_MODULES([UUID], [uuid], [found_uuid=yes], [found_uuid=no])
bb7cd1
+
bb7cd1
+SSS_AC_EXPAND_LIB_DIR()
bb7cd1
+AS_IF([test x"$found_uuid" = xyes],
bb7cd1
+    [AC_CHECK_HEADERS([uuid/uuid.h],
bb7cd1
+        [AC_CHECK_LIB([uuid],
bb7cd1
+                      [uuid_generate],
bb7cd1
+                      [UUID_LIBS="-L$sss_extra_libdir -luuid"],
bb7cd1
+                      [AC_MSG_ERROR([libuuid missing uuid_generate])],
bb7cd1
+                      [-L$sss_extra_libdir -luuid])],
bb7cd1
+        [AC_MSG_ERROR([
bb7cd1
+You must have the header file uuid.h installed to build sssd
bb7cd1
+with KCM responder. If you want to build sssd without KCM responder
bb7cd1
+then specify --without-kcm when running configure.])])])
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
bb7cd1
new file mode 100644
bb7cd1
index 0000000000000000000000000000000000000000..2c565b8378e3ec297faf655d3c48d7ab902713d3
bb7cd1
--- /dev/null
bb7cd1
+++ b/src/responder/kcm/kcmsrv_ccache.c
bb7cd1
@@ -0,0 +1,1423 @@
bb7cd1
+/*
bb7cd1
+   SSSD
bb7cd1
+
bb7cd1
+   KCM Server - the KCM ccache operations
bb7cd1
+
bb7cd1
+   Copyright (C) Red Hat, 2016
bb7cd1
+
bb7cd1
+   This program is free software; you can redistribute it and/or modify
bb7cd1
+   it under the terms of the GNU General Public License as published by
bb7cd1
+   the Free Software Foundation; either version 3 of the License, or
bb7cd1
+   (at your option) any later version.
bb7cd1
+
bb7cd1
+   This program is distributed in the hope that it will be useful,
bb7cd1
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
bb7cd1
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
bb7cd1
+   GNU General Public License for more details.
bb7cd1
+
bb7cd1
+   You should have received a copy of the GNU General Public License
bb7cd1
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
bb7cd1
+*/
bb7cd1
+
bb7cd1
+#include "config.h"
bb7cd1
+
bb7cd1
+#include "util/crypto/sss_crypto.h"
bb7cd1
+#include "util/util.h"
bb7cd1
+#include "util/sss_krb5.h"
bb7cd1
+#include "responder/kcm/kcmsrv_ccache.h"
bb7cd1
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
bb7cd1
+#include "responder/kcm/kcmsrv_ccache_be.h"
bb7cd1
+
bb7cd1
+static int kcm_cc_destructor(struct kcm_ccache *cc)
bb7cd1
+{
bb7cd1
+    if (cc == NULL) {
bb7cd1
+        return 0;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    krb5_free_principal(NULL, cc->client);
bb7cd1
+    return 0;
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
bb7cd1
+                   krb5_context k5c,
bb7cd1
+                   struct cli_creds *owner,
bb7cd1
+                   const char *name,
bb7cd1
+                   krb5_principal princ,
bb7cd1
+                   struct kcm_ccache **_cc)
bb7cd1
+{
bb7cd1
+    struct kcm_ccache *cc;
bb7cd1
+    krb5_error_code kret;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    cc = talloc_zero(mem_ctx, struct kcm_ccache);
bb7cd1
+    if (cc == NULL) {
bb7cd1
+        return ENOMEM;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_check_name(name, owner);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE, "Name %s is malformed\n", name);
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    cc->name = talloc_strdup(cc, name);
bb7cd1
+    if (cc->name == NULL) {
bb7cd1
+        talloc_free(cc);
bb7cd1
+        return ENOMEM;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    uuid_generate(cc->uuid);
bb7cd1
+
bb7cd1
+    kret = krb5_copy_principal(k5c, princ, &cc->client);
bb7cd1
+    if (kret != 0) {
bb7cd1
+        const char *err_msg = sss_krb5_get_error_message(k5c, kret);
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg);
bb7cd1
+        sss_krb5_free_error_message(k5c, err_msg);
bb7cd1
+        talloc_free(cc);
bb7cd1
+        return ERR_INTERNAL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    cc->owner.uid = cli_creds_get_uid(owner);
bb7cd1
+    cc->owner.gid = cli_creds_get_gid(owner);
bb7cd1
+    cc->kdc_offset = INT32_MAX;
bb7cd1
+
bb7cd1
+    talloc_set_destructor(cc, kcm_cc_destructor);
bb7cd1
+    *_cc = cc;
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+const char *kcm_cc_get_name(struct kcm_ccache *cc)
bb7cd1
+{
bb7cd1
+    return cc ? cc->name : NULL;
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid)
bb7cd1
+{
bb7cd1
+    if (cc == NULL) {
bb7cd1
+        return EINVAL;
bb7cd1
+    }
bb7cd1
+    uuid_copy(_uuid, cc->uuid);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc)
bb7cd1
+{
bb7cd1
+    return cc ? cc->client : NULL;
bb7cd1
+}
bb7cd1
+
bb7cd1
+bool kcm_cc_access(struct kcm_ccache *cc,
bb7cd1
+                   struct cli_creds *client)
bb7cd1
+{
bb7cd1
+    bool ok;
bb7cd1
+    uid_t uid = cli_creds_get_uid(client);
bb7cd1
+    gid_t gid = cli_creds_get_gid(client);
bb7cd1
+
bb7cd1
+    if (cc == NULL) {
bb7cd1
+        return false;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (uid == 0 && gid == 0) {
bb7cd1
+        /* root can access any ccache */
bb7cd1
+        return true;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ok = ((cc->owner.uid == uid) && (cc->owner.gid == gid));
bb7cd1
+    if (!ok) {
bb7cd1
+        DEBUG(SSSDBG_MINOR_FAILURE,
bb7cd1
+              "Client %"SPRIuid":%"SPRIgid" has no access to ccache %s\n",
bb7cd1
+              cli_creds_get_uid(client),
bb7cd1
+              cli_creds_get_gid(client),
bb7cd1
+              cc->name);
bb7cd1
+    }
bb7cd1
+    return ok;
bb7cd1
+}
bb7cd1
+
bb7cd1
+int32_t kcm_cc_get_offset(struct kcm_ccache *cc)
bb7cd1
+{
bb7cd1
+    return cc ? cc->kdc_offset : INT32_MAX;
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
bb7cd1
+                               struct sss_iobuf *cred_blob)
bb7cd1
+{
bb7cd1
+    struct kcm_cred *kcreds;
bb7cd1
+    uuid_t uuid;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    if (cc == NULL || cred_blob == NULL) {
bb7cd1
+        return EINVAL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    uuid_generate(uuid);
bb7cd1
+    kcreds = kcm_cred_new(cc, uuid, cred_blob);
bb7cd1
+    if (kcreds == NULL) {
bb7cd1
+        return ENOMEM;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_cc_store_creds(cc, kcreds);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc)
bb7cd1
+{
bb7cd1
+    if (cc == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return cc->creds;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd)
bb7cd1
+{
bb7cd1
+    if (crd == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return crd->next;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
bb7cd1
+                              uuid_t uuid,
bb7cd1
+                              struct sss_iobuf *cred_blob)
bb7cd1
+{
bb7cd1
+    struct kcm_cred *kcreds;
bb7cd1
+
bb7cd1
+    kcreds = talloc_zero(mem_ctx, struct kcm_cred);
bb7cd1
+    if (kcreds == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    uuid_copy(kcreds->uuid, uuid);
bb7cd1
+    kcreds->cred_blob = talloc_steal(kcreds, cred_blob);
bb7cd1
+    return kcreds;
bb7cd1
+}
bb7cd1
+
bb7cd1
+/* Add a cred to ccache */
bb7cd1
+errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
bb7cd1
+                           struct kcm_cred *crd)
bb7cd1
+{
bb7cd1
+    DLIST_ADD(cc->creds, crd);
bb7cd1
+    talloc_steal(cc, crd);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid)
bb7cd1
+{
bb7cd1
+    if (crd == NULL) {
bb7cd1
+        return EINVAL;
bb7cd1
+    }
bb7cd1
+    uuid_copy(_uuid, crd->uuid);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd)
bb7cd1
+{
bb7cd1
+    return crd ? crd->cred_blob : NULL;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
bb7cd1
+                               struct tevent_context *ev,
bb7cd1
+                               enum kcm_ccdb_be cc_be)
bb7cd1
+{
bb7cd1
+    errno_t ret;
bb7cd1
+    struct kcm_ccdb *ccdb = NULL;
bb7cd1
+
bb7cd1
+    if (ev == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ccdb = talloc_zero(mem_ctx, struct kcm_ccdb);
bb7cd1
+    if (ccdb == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    ccdb->ev = ev;
bb7cd1
+
bb7cd1
+    switch (cc_be) {
bb7cd1
+    case CCDB_BE_MEMORY:
bb7cd1
+        DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n");
bb7cd1
+        /* Not implemented yet */
bb7cd1
+        break;
bb7cd1
+    case CCDB_BE_SECRETS:
bb7cd1
+        DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n");
bb7cd1
+        /* Not implemented yet */
bb7cd1
+        break;
bb7cd1
+    default:
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database\n");
bb7cd1
+        break;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (ccdb->ops == NULL) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE, "Ccache database not initialized\n");
bb7cd1
+        talloc_free(ccdb);
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = ccdb->ops->init(ccdb);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize ccache database\n");
bb7cd1
+        talloc_free(ccdb);
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return ccdb;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_nextid_state {
bb7cd1
+    char *next_cc;
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_nextid_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                        struct tevent_context *ev,
bb7cd1
+                                        struct kcm_ccdb *db,
bb7cd1
+                                        struct cli_creds *client)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_nextid_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_nextid_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->client = client;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->nextid_send(mem_ctx, ev, state->db, client);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_nextid_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_nextid_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_nextid_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_nextid_state);
bb7cd1
+    errno_t ret;
bb7cd1
+    unsigned int nextid;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->nextid_recv(subreq, &nextid);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to generate next UID [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    state->next_cc = talloc_asprintf(state, "%"SPRIuid":%u",
bb7cd1
+                                     cli_creds_get_uid(state->client),
bb7cd1
+                                     nextid);
bb7cd1
+    if (state->next_cc == NULL) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
bb7cd1
+        tevent_req_error(req, ENOMEM);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    DEBUG(SSSDBG_TRACE_LIBS, "generated %s\n", state->next_cc);
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_nextid_recv(struct tevent_req *req,
bb7cd1
+                             TALLOC_CTX *mem_ctx,
bb7cd1
+                             char **_next_cc)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_nextid_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_nextid_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    *_next_cc = talloc_steal(mem_ctx, state->next_cc);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_list_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+
bb7cd1
+    uuid_t *uuid_list;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_list_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                      struct tevent_context *ev,
bb7cd1
+                                      struct kcm_ccdb *db,
bb7cd1
+                                      struct cli_creds *client)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_list_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_list_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->client = client;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->list_send(mem_ctx,
bb7cd1
+                                       ev,
bb7cd1
+                                       state->db,
bb7cd1
+                                       client);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_list_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_list_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_list_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_list_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->list_recv(subreq, state, &state->uuid_list);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to list all ccaches [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_list_recv(struct tevent_req *req,
bb7cd1
+                           TALLOC_CTX *mem_ctx,
bb7cd1
+                           uuid_t **_uuid_list)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_list_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_list_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_get_default_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    uuid_t uuid;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_get_default_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                             struct tevent_context *ev,
bb7cd1
+                                             struct kcm_ccdb *db,
bb7cd1
+                                             struct cli_creds *client)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_get_default_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_get_default_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = db->ops->get_default_send(mem_ctx, ev, db, client);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_get_default_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_get_default_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_get_default_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_get_default_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->get_default_recv(subreq, state->uuid);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to get the default ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_get_default_recv(struct tevent_req *req,
bb7cd1
+                                  uuid_t *uuid)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_get_default_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_get_default_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+
bb7cd1
+    if (uuid != NULL) {
bb7cd1
+        /* The caller might supply a NULL dfl to just check if there is
bb7cd1
+         * some default ccache
bb7cd1
+         */
bb7cd1
+        uuid_copy(*uuid, state->uuid);
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_set_default_state {
bb7cd1
+    struct tevent_context *ev;
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+    uuid_t uuid;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq);
bb7cd1
+static void kcm_ccdb_set_default_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                             struct tevent_context *ev,
bb7cd1
+                                             struct kcm_ccdb *db,
bb7cd1
+                                             struct cli_creds *client,
bb7cd1
+                                             uuid_t uuid)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_set_default_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_set_default_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->ev = ev;
bb7cd1
+    state->client = client;
bb7cd1
+    uuid_copy(state->uuid, uuid);
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (uuid_is_null(uuid)) {
bb7cd1
+        /* NULL UUID means to just reset the default to 'no default' */
bb7cd1
+        subreq = state->db->ops->set_default_send(state,
bb7cd1
+                                                state->ev,
bb7cd1
+                                                state->db,
bb7cd1
+                                                state->client,
bb7cd1
+                                                state->uuid);
bb7cd1
+        if (subreq == NULL) {
bb7cd1
+            ret = ENOMEM;
bb7cd1
+            goto immediate;
bb7cd1
+        }
bb7cd1
+        tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req);
bb7cd1
+    } else {
bb7cd1
+        /* Otherwise we need to check if the client can access the UUID
bb7cd1
+         * about to be set as default
bb7cd1
+         */
bb7cd1
+        subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid);
bb7cd1
+        if (subreq == NULL) {
bb7cd1
+            ret = ENOMEM;
bb7cd1
+            goto immediate;
bb7cd1
+        }
bb7cd1
+        tevent_req_set_callback(subreq, kcm_ccdb_set_default_uuid_resolved, req);
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_set_default_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_set_default_state);
bb7cd1
+    errno_t ret;
bb7cd1
+    bool ok;
bb7cd1
+    struct kcm_ccache *cc;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->getbyuuid_recv(subreq, state, &cc);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to get cache by UUID [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (cc == NULL) {
bb7cd1
+        DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n");
bb7cd1
+        tevent_req_error(req, ERR_KCM_CC_END);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ok = kcm_cc_access(cc, state->client);
bb7cd1
+    if (!ok) {
bb7cd1
+        tevent_req_error(req, EACCES);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->set_default_send(state,
bb7cd1
+                                              state->ev,
bb7cd1
+                                              state->db,
bb7cd1
+                                              state->client,
bb7cd1
+                                              state->uuid);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        tevent_req_error(req, ENOMEM);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_set_default_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_set_default_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_set_default_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->set_default_recv(subreq);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to set the default ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_set_default_recv(struct tevent_req *req)
bb7cd1
+{
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_getbyname_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+
bb7cd1
+    struct kcm_ccache *cc;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_getbyname_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           const char *name)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_getbyname_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyname_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->client = client;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL || name == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = db->ops->getbyname_send(state, ev, db, client, name);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_getbyname_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_getbyname_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_getbyname_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_getbyname_state);
bb7cd1
+    errno_t ret;
bb7cd1
+    bool ok;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->getbyname_recv(subreq, state, &state->cc);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to get cache by name [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (state->cc == NULL) {
bb7cd1
+        DEBUG(SSSDBG_TRACE_LIBS, "No cache found by name\n");
bb7cd1
+        tevent_req_done(req);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ok = kcm_cc_access(state->cc, state->client);
bb7cd1
+    if (!ok) {
bb7cd1
+        tevent_req_error(req, EACCES);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req,
bb7cd1
+                                TALLOC_CTX *mem_ctx,
bb7cd1
+                                struct kcm_ccache **_cc)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_getbyname_state *state = tevent_req_data(req,
bb7cd1
+                                            struct kcm_ccdb_getbyname_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    *_cc = talloc_steal(mem_ctx, state->cc);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_getbyuuid_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+
bb7cd1
+    struct kcm_ccache *cc;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           uuid_t uuid)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_getbyuuid_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyuuid_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->client = client;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_getbyuuid_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_getbyuuid_state);
bb7cd1
+    errno_t ret;
bb7cd1
+    bool ok;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->getbyuuid_recv(subreq, state, &state->cc);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to get cache by UUID [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (state->cc == NULL) {
bb7cd1
+        DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n");
bb7cd1
+        tevent_req_done(req);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ok = kcm_cc_access(state->cc, state->client);
bb7cd1
+    if (!ok) {
bb7cd1
+        tevent_req_error(req, EACCES);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req,
bb7cd1
+                                TALLOC_CTX *mem_ctx,
bb7cd1
+                                struct kcm_ccache **_cc)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req,
bb7cd1
+                                            struct kcm_ccdb_getbyuuid_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    *_cc = talloc_steal(mem_ctx, state->cc);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_name_by_uuid_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+
bb7cd1
+    const char *name;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                              struct tevent_context *ev,
bb7cd1
+                                              struct kcm_ccdb *db,
bb7cd1
+                                              struct cli_creds *client,
bb7cd1
+                                              uuid_t uuid)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_name_by_uuid_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx,
bb7cd1
+                            &state,
bb7cd1
+                            struct kcm_ccdb_name_by_uuid_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->client = client;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL || uuid_is_null(uuid)) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = db->ops->name_by_uuid_send(state, ev, db, client, uuid);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_name_by_uuid_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req,
bb7cd1
+                                        struct kcm_ccdb_name_by_uuid_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->name_by_uuid_recv(subreq, state, &state->name);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to resolve cache by UUID [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req,
bb7cd1
+                                   TALLOC_CTX *mem_ctx,
bb7cd1
+                                   const char **_name)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req,
bb7cd1
+                                        struct kcm_ccdb_name_by_uuid_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    *_name = talloc_steal(mem_ctx, state->name);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_uuid_by_name_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+
bb7cd1
+    uuid_t uuid;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                              struct tevent_context *ev,
bb7cd1
+                                              struct kcm_ccdb *db,
bb7cd1
+                                              struct cli_creds *client,
bb7cd1
+                                              const char *name)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_uuid_by_name_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx,
bb7cd1
+                            &state,
bb7cd1
+                            struct kcm_ccdb_uuid_by_name_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->client = client;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL || name == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = db->ops->uuid_by_name_send(state, ev, db, client, name);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_uuid_by_name_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req,
bb7cd1
+                                        struct kcm_ccdb_uuid_by_name_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->uuid_by_name_recv(subreq, state, state->uuid);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to resolve cache by UUID [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req,
bb7cd1
+                                   TALLOC_CTX *mem_ctx,
bb7cd1
+                                   uuid_t _uuid)
bb7cd1
+{
bb7cd1
+    struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req,
bb7cd1
+                                        struct kcm_ccdb_uuid_by_name_state);
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    uuid_copy(_uuid, state->uuid);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_create_cc_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_create_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           struct kcm_ccache *cc)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_create_cc_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+    bool ok;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_create_cc_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL || cc == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ok = kcm_cc_access(cc, client);
bb7cd1
+    if (!ok) {
bb7cd1
+        ret = EACCES;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->create_send(mem_ctx,
bb7cd1
+                                         ev,
bb7cd1
+                                         state->db,
bb7cd1
+                                         client,
bb7cd1
+                                         cc);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_create_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_create_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_create_cc_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_create_cc_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->create_recv(subreq);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to create ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req)
bb7cd1
+{
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx)
bb7cd1
+{
bb7cd1
+    if (mod_ctx == NULL) {
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    mod_ctx->kdc_offset = INT32_MAX;
bb7cd1
+}
bb7cd1
+
bb7cd1
+void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx)
bb7cd1
+{
bb7cd1
+    if (cc == NULL || mod_ctx == NULL) {
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (mod_ctx->kdc_offset != INT32_MAX) {
bb7cd1
+        cc->kdc_offset = mod_ctx->kdc_offset;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_mod_cc_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_mod_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                        struct tevent_context *ev,
bb7cd1
+                                        struct kcm_ccdb *db,
bb7cd1
+                                        struct cli_creds *client,
bb7cd1
+                                        uuid_t uuid,
bb7cd1
+                                        struct kcm_mod_ctx *mod_cc)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_mod_cc_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_mod_cc_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL || mod_cc == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->mod_send(mem_ctx,
bb7cd1
+                                      ev,
bb7cd1
+                                      state->db,
bb7cd1
+                                      client,
bb7cd1
+                                      uuid,
bb7cd1
+                                      mod_cc);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_mod_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_mod_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_mod_cc_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_mod_cc_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->mod_recv(subreq);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to create ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req)
bb7cd1
+{
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_store_cred_blob_state {
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                                 struct tevent_context *ev,
bb7cd1
+                                                 struct kcm_ccdb *db,
bb7cd1
+                                                 struct cli_creds *client,
bb7cd1
+                                                 uuid_t uuid,
bb7cd1
+                                                 struct sss_iobuf *cred_blob)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_store_cred_blob_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_store_cred_blob_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL || cred_blob == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->store_cred_send(mem_ctx,
bb7cd1
+                                             ev,
bb7cd1
+                                             state->db,
bb7cd1
+                                             client,
bb7cd1
+                                             uuid,
bb7cd1
+                                             cred_blob);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        ret = ENOMEM;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_store_cred_blob_done, req);
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_store_cred_blob_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_store_cred_blob_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->store_cred_recv(subreq);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to create ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req)
bb7cd1
+{
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+struct kcm_ccdb_delete_cc_state {
bb7cd1
+    struct tevent_context *ev;
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
+    struct cli_creds *client;
bb7cd1
+    uuid_t uuid;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static void kcm_ccdb_delete_done(struct tevent_req *subreq);
bb7cd1
+static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq);
bb7cd1
+static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           uuid_t uuid)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = NULL;
bb7cd1
+    struct tevent_req *subreq = NULL;
bb7cd1
+    struct kcm_ccdb_delete_cc_state *state = NULL;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_delete_cc_state);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+    state->db = db;
bb7cd1
+    state->ev = ev;
bb7cd1
+    state->client = client;
bb7cd1
+    uuid_copy(state->uuid, uuid);
bb7cd1
+
bb7cd1
+    if (ev == NULL || db == NULL || client == NULL) {
bb7cd1
+        ret = EINVAL;
bb7cd1
+        goto immediate;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->delete_send(state,
bb7cd1
+                                         state->ev,
bb7cd1
+                                         state->db,
bb7cd1
+                                         state->client,
bb7cd1
+                                         state->uuid);
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_delete_done, req);
bb7cd1
+
bb7cd1
+    return req;
bb7cd1
+
bb7cd1
+immediate:
bb7cd1
+    tevent_req_error(req, ret);
bb7cd1
+    tevent_req_post(req, ev);
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_delete_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_delete_cc_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->delete_recv(subreq);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to delete ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    /* The delete operation must also check if the deleted ccache was
bb7cd1
+     * the default and reset the default if it was
bb7cd1
+     */
bb7cd1
+    subreq = state->db->ops->get_default_send(state,
bb7cd1
+                                              state->ev,
bb7cd1
+                                              state->db,
bb7cd1
+                                              state->client);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        tevent_req_error(req, ENOMEM);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_delete_get_default_done, req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_delete_cc_state);
bb7cd1
+    errno_t ret;
bb7cd1
+    uuid_t dfl_uuid;
bb7cd1
+    uuid_t null_uuid;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->get_default_recv(subreq, dfl_uuid);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to get the default ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (uuid_compare(dfl_uuid, state->uuid) != 0) {
bb7cd1
+        /* The ccache about to be deleted was not the default, quit */
bb7cd1
+        tevent_req_done(req);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    /* If we deleted the default ccache, reset the default ccache to 'none' */
bb7cd1
+    uuid_clear(null_uuid);
bb7cd1
+
bb7cd1
+    subreq = state->db->ops->set_default_send(state,
bb7cd1
+                                              state->ev,
bb7cd1
+                                              state->db,
bb7cd1
+                                              state->client,
bb7cd1
+                                              null_uuid);
bb7cd1
+    if (subreq == NULL) {
bb7cd1
+        tevent_req_error(req, ENOMEM);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+    tevent_req_set_callback(subreq, kcm_ccdb_delete_default_reset_done, req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq)
bb7cd1
+{
bb7cd1
+    struct tevent_req *req = tevent_req_callback_data(subreq,
bb7cd1
+                                                      struct tevent_req);
bb7cd1
+    struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
bb7cd1
+                                                struct kcm_ccdb_delete_cc_state);
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = state->db->ops->set_default_recv(subreq);
bb7cd1
+    talloc_zfree(subreq);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+              "Failed to NULL the default ccache [%d]: %s\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        tevent_req_error(req, ret);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    tevent_req_done(req);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req)
bb7cd1
+{
bb7cd1
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+void kcm_debug_uuid(uuid_t uuid)
bb7cd1
+{
bb7cd1
+    char dbgbuf[UUID_STR_SIZE];
bb7cd1
+
bb7cd1
+    if (!(debug_level & SSSDBG_TRACE_ALL) || uuid == NULL) {
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    uuid_unparse(uuid, dbgbuf);
bb7cd1
+    DEBUG(SSSDBG_TRACE_ALL, "UUID: %s\n", dbgbuf);
bb7cd1
+}
bb7cd1
+
bb7cd1
+errno_t kcm_check_name(const char *name, struct cli_creds *client)
bb7cd1
+{
bb7cd1
+    char prefix[64];
bb7cd1
+    size_t prefix_len;
bb7cd1
+
bb7cd1
+    prefix_len = snprintf(prefix, sizeof(prefix),
bb7cd1
+                          "%"SPRIuid, cli_creds_get_uid(client));
bb7cd1
+
bb7cd1
+    if (strncmp(name, prefix, prefix_len) != 0) {
bb7cd1
+        return ERR_KCM_WRONG_CCNAME_FORMAT;
bb7cd1
+    }
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
bb7cd1
new file mode 100644
bb7cd1
index 0000000000000000000000000000000000000000..130ae48ae30d5e1e2ab238a647a9b9dc76cc4945
bb7cd1
--- /dev/null
bb7cd1
+++ b/src/responder/kcm/kcmsrv_ccache.h
bb7cd1
@@ -0,0 +1,306 @@
bb7cd1
+/*
bb7cd1
+   SSSD
bb7cd1
+
bb7cd1
+   KCM Server - the KCM ccache operations
bb7cd1
+
bb7cd1
+   Copyright (C) Red Hat, 2016
bb7cd1
+
bb7cd1
+   This program is free software; you can redistribute it and/or modify
bb7cd1
+   it under the terms of the GNU General Public License as published by
bb7cd1
+   the Free Software Foundation; either version 3 of the License, or
bb7cd1
+   (at your option) any later version.
bb7cd1
+
bb7cd1
+   This program is distributed in the hope that it will be useful,
bb7cd1
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
bb7cd1
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
bb7cd1
+   GNU General Public License for more details.
bb7cd1
+
bb7cd1
+   You should have received a copy of the GNU General Public License
bb7cd1
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
bb7cd1
+*/
bb7cd1
+#ifndef _KCMSRV_CCACHE_H_
bb7cd1
+#define _KCMSRV_CCACHE_H_
bb7cd1
+
bb7cd1
+#include "config.h"
bb7cd1
+
bb7cd1
+#include <krb5/krb5.h>
bb7cd1
+#include <uuid/uuid.h>
bb7cd1
+
bb7cd1
+#include "util/util.h"
bb7cd1
+#include "util/sss_iobuf.h"
bb7cd1
+#include "util/util_creds.h"
bb7cd1
+
bb7cd1
+#define UUID_BYTES    16
bb7cd1
+#define UUID_STR_SIZE 37
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Credentials are opaque to the KCM server
bb7cd1
+ *
bb7cd1
+ * Each ccache has a unique UUID.
bb7cd1
+ */
bb7cd1
+struct kcm_cred;
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * An opaque ccache type and its operations
bb7cd1
+ *
bb7cd1
+ * Contains zero or some KCM credentials. One credential in the cache
bb7cd1
+ * is marked as the default one. The client can set and get the default
bb7cd1
+ * cache (e.g. with kswitch) but one cache is always the default -- we
bb7cd1
+ * fall back to the one created first.
bb7cd1
+ *
bb7cd1
+ * Each cache has a name and a UUID. Heimdal allows the name to be changed,
bb7cd1
+ * we don't (yet, because the MIT client doesn't allow that either)
bb7cd1
+ *
bb7cd1
+ * Each ccache also stores a client principal.
bb7cd1
+ */
bb7cd1
+struct kcm_ccache;
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Create a new KCM ccache owned by mem_ctx on the
bb7cd1
+ * memory level.
bb7cd1
+ *
bb7cd1
+ * When created, the ccache contains no credendials
bb7cd1
+ */
bb7cd1
+errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
bb7cd1
+                   krb5_context k5c,
bb7cd1
+                   struct cli_creds *owner,
bb7cd1
+                   const char *name,
bb7cd1
+                   krb5_principal princ,
bb7cd1
+                   struct kcm_ccache **_cc);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Returns true if a client can access a ccache.
bb7cd1
+ *
bb7cd1
+ * Note that root can access any ccache */
bb7cd1
+bool kcm_cc_access(struct kcm_ccache *cc,
bb7cd1
+                   struct cli_creds *client);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Since the kcm_ccache structure is opaque, the kcmsrv_ccache
bb7cd1
+ * layer contains a number of getsetters to read and write
bb7cd1
+ * properties of the kcm_ccache structure
bb7cd1
+ */
bb7cd1
+const char *kcm_cc_get_name(struct kcm_ccache *cc);
bb7cd1
+errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid);
bb7cd1
+krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc);
bb7cd1
+int32_t kcm_cc_get_offset(struct kcm_ccache *cc);
bb7cd1
+
bb7cd1
+/* Mainly useful for creating a cred structure from a persistent
bb7cd1
+ * storage
bb7cd1
+ */
bb7cd1
+struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
bb7cd1
+                              uuid_t uuid,
bb7cd1
+                              struct sss_iobuf *cred_blob);
bb7cd1
+
bb7cd1
+/* Add a cred to ccache */
bb7cd1
+errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
bb7cd1
+                           struct kcm_cred *crd);
bb7cd1
+
bb7cd1
+errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * At the moment, the credentials are stored without unmarshalling
bb7cd1
+ * them, just as the clients sends the credentials.
bb7cd1
+ */
bb7cd1
+struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd);
bb7cd1
+errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
bb7cd1
+                               struct sss_iobuf *cred_blob);
bb7cd1
+ /*
bb7cd1
+ * The KCM server can call kcm_cred_get_creds to fetch the first
bb7cd1
+ * credential, then iterate over the credentials with
bb7cd1
+ * kcm_cc_next_cred until it returns NULL
bb7cd1
+ */
bb7cd1
+struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc);
bb7cd1
+struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd);
bb7cd1
+
bb7cd1
+enum kcm_ccdb_be {
bb7cd1
+    CCDB_BE_MEMORY,
bb7cd1
+    CCDB_BE_SECRETS,
bb7cd1
+};
bb7cd1
+
bb7cd1
+/* An opaque database that contains all the ccaches */
bb7cd1
+struct kcm_ccdb;
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Initialize a ccache database of type cc_be
bb7cd1
+ */
bb7cd1
+struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
bb7cd1
+                               struct tevent_context *ev,
bb7cd1
+                               enum kcm_ccdb_be cc_be);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * In KCM, each ccache name is usually in the form of "UID:<num>
bb7cd1
+ *
bb7cd1
+ * The <num> is generated by the KCM ccache database. Use this function
bb7cd1
+ * to retrieve the next number
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                        struct tevent_context *ev,
bb7cd1
+                                        struct kcm_ccdb *db,
bb7cd1
+                                        struct cli_creds *client);
bb7cd1
+errno_t kcm_ccdb_nextid_recv(struct tevent_req *req,
bb7cd1
+                             TALLOC_CTX *mem_ctx,
bb7cd1
+                             char **_nextid);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * List all ccaches that belong to a given client
bb7cd1
+ *
bb7cd1
+ * The cc_list the recv function returns is NULL-terminated.
bb7cd1
+ *
bb7cd1
+ * NOTE: Contrary to how Heimdal behaves, root CAN NOT list all ccaches
bb7cd1
+ * of all users. This is a deliberate decision to treat root as any other
bb7cd1
+ * user, except it can access a ccache of another user by name, just not
bb7cd1
+ * list them.
bb7cd1
+ *
bb7cd1
+ * If a client has no ccaches, the function returns OK, but an empty list
bb7cd1
+ * containing just the NULL sentinel.
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                      struct tevent_context *ev,
bb7cd1
+                                      struct kcm_ccdb *db,
bb7cd1
+                                      struct cli_creds *client);
bb7cd1
+errno_t kcm_ccdb_list_recv(struct tevent_req *req,
bb7cd1
+                           TALLOC_CTX *mem_ctx,
bb7cd1
+                           uuid_t **_uuid_list);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Retrieve a ccache by name.
bb7cd1
+ *
bb7cd1
+ * If there is no such ccache, return EOK, but a NULL _cc pointer
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           const char *name);
bb7cd1
+errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req,
bb7cd1
+                                TALLOC_CTX *mem_ctx,
bb7cd1
+                                struct kcm_ccache **_cc);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Retrieve a ccache by UUID
bb7cd1
+ *
bb7cd1
+ * If there is no such ccache, return EOK, but a NULL _cc pointer
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           uuid_t uuid);
bb7cd1
+errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req,
bb7cd1
+                                TALLOC_CTX *mem_ctx,
bb7cd1
+                                struct kcm_ccache **_cc);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Retrieve the default ccache. If there is no default cache,
bb7cd1
+ * return EOK, but a NULL UUID.
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                             struct tevent_context *ev,
bb7cd1
+                                             struct kcm_ccdb *db,
bb7cd1
+                                             struct cli_creds *client);
bb7cd1
+errno_t kcm_ccdb_get_default_recv(struct tevent_req *req,
bb7cd1
+                                  uuid_t *uuid);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Translating name to UUID is often considerably faster than doing a full
bb7cd1
+ * CC retrieval, hence this function and the converse. If the UUID cannot
bb7cd1
+ * be found in the database, return ERR_KCM_CC_END
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                              struct tevent_context *ev,
bb7cd1
+                                              struct kcm_ccdb *db,
bb7cd1
+                                              struct cli_creds *client,
bb7cd1
+                                              uuid_t uuid);
bb7cd1
+errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req,
bb7cd1
+                                   TALLOC_CTX *mem_ctx,
bb7cd1
+                                   const char **_name);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Translating UUID to name is often considerably faster than doing a full
bb7cd1
+ * CC retrieval, hence this function and the converse. If the UUID cannot
bb7cd1
+ * be found in the database, return ERR_KCM_CC_END
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                              struct tevent_context *ev,
bb7cd1
+                                              struct kcm_ccdb *db,
bb7cd1
+                                              struct cli_creds *client,
bb7cd1
+                                              const char *name);
bb7cd1
+errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req,
bb7cd1
+                                   TALLOC_CTX *mem_ctx,
bb7cd1
+                                   uuid_t _uuid);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Set the default ccache. Passing a NULL UUID is a legal operation
bb7cd1
+ * that 'unsets' the default ccache.
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                             struct tevent_context *ev,
bb7cd1
+                                             struct kcm_ccdb *db,
bb7cd1
+                                             struct cli_creds *client,
bb7cd1
+                                             uuid_t uuid);
bb7cd1
+errno_t kcm_ccdb_set_default_recv(struct tevent_req *req);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Add a ccache to the database.
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           struct kcm_ccache *cc);
bb7cd1
+errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Modify cache properties in a db
bb7cd1
+ */
bb7cd1
+struct kcm_mod_ctx {
bb7cd1
+    int32_t kdc_offset;
bb7cd1
+    /* More settable properties (like name, when we support renames
bb7cd1
+     * will be added later
bb7cd1
+     */
bb7cd1
+};
bb7cd1
+
bb7cd1
+void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx);
bb7cd1
+void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx);
bb7cd1
+
bb7cd1
+struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                        struct tevent_context *ev,
bb7cd1
+                                        struct kcm_ccdb *db,
bb7cd1
+                                        struct cli_creds *client,
bb7cd1
+                                        uuid_t uuid,
bb7cd1
+                                        struct kcm_mod_ctx *mod_cc);
bb7cd1
+errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Store a credential in a cache
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                                 struct tevent_context *ev,
bb7cd1
+                                                 struct kcm_ccdb *db,
bb7cd1
+                                                 struct cli_creds *client,
bb7cd1
+                                                 uuid_t uuid,
bb7cd1
+                                                 struct sss_iobuf *cred_blob);
bb7cd1
+errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Delete a ccache from the database
bb7cd1
+ */
bb7cd1
+struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx,
bb7cd1
+                                           struct tevent_context *ev,
bb7cd1
+                                           struct kcm_ccdb *db,
bb7cd1
+                                           struct cli_creds *client,
bb7cd1
+                                           uuid_t uuid);
bb7cd1
+errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req);
bb7cd1
+
bb7cd1
+void kcm_debug_uuid(uuid_t uuid);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * The KCM clients are not allowed (except root) to create ccaches
bb7cd1
+ * with arbitrary names. Instead, we assert that the ccache name
bb7cd1
+ * begins with UID where UID is the stringified representation of
bb7cd1
+ * the client's UID number
bb7cd1
+ */
bb7cd1
+errno_t kcm_check_name(const char *name, struct cli_creds *client);
bb7cd1
+
bb7cd1
+#endif /* _KCMSRV_CCACHE_H_ */
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h
bb7cd1
new file mode 100644
bb7cd1
index 0000000000000000000000000000000000000000..1bd2b6981e227675866e82e0a5389445cac4df66
bb7cd1
--- /dev/null
bb7cd1
+++ b/src/responder/kcm/kcmsrv_ccache_be.h
bb7cd1
@@ -0,0 +1,204 @@
bb7cd1
+/*
bb7cd1
+   SSSD
bb7cd1
+
bb7cd1
+   KCM Server - the KCM ccache database interface
bb7cd1
+
bb7cd1
+   This file should only be included from the ccache.c module.
bb7cd1
+
bb7cd1
+   Copyright (C) Red Hat, 2016
bb7cd1
+
bb7cd1
+   This program is free software; you can redistribute it and/or modify
bb7cd1
+   it under the terms of the GNU General Public License as published by
bb7cd1
+   the Free Software Foundation; either version 3 of the License, or
bb7cd1
+   (at your option) any later version.
bb7cd1
+
bb7cd1
+   This program is distributed in the hope that it will be useful,
bb7cd1
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
bb7cd1
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
bb7cd1
+   GNU General Public License for more details.
bb7cd1
+
bb7cd1
+   You should have received a copy of the GNU General Public License
bb7cd1
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
bb7cd1
+*/
bb7cd1
+
bb7cd1
+#ifndef _KCMSRV_CCACHE_BE_
bb7cd1
+#define _KCMSRV_CCACHE_BE_
bb7cd1
+
bb7cd1
+#include "config.h"
bb7cd1
+
bb7cd1
+#include <talloc.h>
bb7cd1
+#include "responder/kcm/kcmsrv_ccache.h"
bb7cd1
+
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_init_fn)(struct kcm_ccdb *db);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                       struct tevent_context *ev,
bb7cd1
+                       struct kcm_ccdb *db,
bb7cd1
+                       struct cli_creds *client);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_nextid_recv_fn)(struct tevent_req *req,
bb7cd1
+                       unsigned int *_nextid);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_set_default_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                            struct tevent_context *ev,
bb7cd1
+                            struct kcm_ccdb *db,
bb7cd1
+                            struct cli_creds *client,
bb7cd1
+                            uuid_t uuid);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_set_default_recv_fn)(struct tevent_req *req);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_get_default_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                            struct tevent_context *ev,
bb7cd1
+                            struct kcm_ccdb *db,
bb7cd1
+                            struct cli_creds *client);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_get_default_recv_fn)(struct tevent_req *req,
bb7cd1
+                            uuid_t dfl);
bb7cd1
+
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_list_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                     struct tevent_context *ev,
bb7cd1
+                     struct kcm_ccdb *db,
bb7cd1
+                     struct cli_creds *client);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_list_recv_fn)(struct tevent_req *req,
bb7cd1
+                     TALLOC_CTX *mem_ctx,
bb7cd1
+                     uuid_t **_uuid_list);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_getbyname_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                          struct tevent_context *ev,
bb7cd1
+                          struct kcm_ccdb *db,
bb7cd1
+                          struct cli_creds *client,
bb7cd1
+                          const char *name);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_getbyname_recv_fn)(struct tevent_req *req,
bb7cd1
+                          TALLOC_CTX *mem_ctx,
bb7cd1
+                          struct kcm_ccache **_cc);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_getbyuuid_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                          struct tevent_context *ev,
bb7cd1
+                          struct kcm_ccdb *db,
bb7cd1
+                          struct cli_creds *client,
bb7cd1
+                          uuid_t uuid);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_getbyuuid_recv_fn)(struct tevent_req *req,
bb7cd1
+                          TALLOC_CTX *mem_ctx,
bb7cd1
+                          struct kcm_ccache **_cc);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_name_by_uuid_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                             struct tevent_context *ev,
bb7cd1
+                             struct kcm_ccdb *db,
bb7cd1
+                             struct cli_creds *client,
bb7cd1
+                             uuid_t uuid);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_name_by_uuid_recv_fn)(struct tevent_req *req,
bb7cd1
+                             TALLOC_CTX *mem_ctx,
bb7cd1
+                             const char **_name);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_uuid_by_name_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                             struct tevent_context *ev,
bb7cd1
+                             struct kcm_ccdb *db,
bb7cd1
+                             struct cli_creds *client,
bb7cd1
+                             const char *name);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_uuid_by_name_recv_fn)(struct tevent_req *req,
bb7cd1
+                             TALLOC_CTX *mem_ctx,
bb7cd1
+                             uuid_t _uuid);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_create_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                       struct tevent_context *ev,
bb7cd1
+                       struct kcm_ccdb *db,
bb7cd1
+                       struct cli_creds *client,
bb7cd1
+                       struct kcm_ccache *cc);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_create_recv_fn)(struct tevent_req *req);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_mod_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                    struct tevent_context *ev,
bb7cd1
+                    struct kcm_ccdb *db,
bb7cd1
+                    struct cli_creds *client,
bb7cd1
+                    uuid_t uuid,
bb7cd1
+                    struct kcm_mod_ctx *mod_cc);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_mod_recv_fn)(struct tevent_req *req);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*kcm_ccdb_store_cred_blob_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                                    struct tevent_context *ev,
bb7cd1
+                                    struct kcm_ccdb *db,
bb7cd1
+                                    struct cli_creds *client,
bb7cd1
+                                    uuid_t uuid,
bb7cd1
+                                    struct sss_iobuf *cred_blob);
bb7cd1
+typedef errno_t
bb7cd1
+(*kcm_ccdb_store_cred_blob_recv_fn)(struct tevent_req *req);
bb7cd1
+
bb7cd1
+typedef struct tevent_req *
bb7cd1
+(*ccdb_delete_send_fn)(TALLOC_CTX *mem_ctx,
bb7cd1
+                      struct tevent_context *ev,
bb7cd1
+                      struct kcm_ccdb *db,
bb7cd1
+                      struct cli_creds *client,
bb7cd1
+                      uuid_t uuid);
bb7cd1
+typedef errno_t
bb7cd1
+(*ccdb_delete_recv_fn)(struct tevent_req *req);
bb7cd1
+
bb7cd1
+/*
bb7cd1
+ * Each ccache back end (for example memory or secrets) must implement
bb7cd1
+ * all these functions. The functions are wrapped by the kcm_ccdb
bb7cd1
+ * interface that performs additional sanity checks or contains shared
bb7cd1
+ * logic such as access checks but in general doesn't assume anything
bb7cd1
+ * about how the operations work.
bb7cd1
+ */
bb7cd1
+struct kcm_ccdb_ops {
bb7cd1
+    ccdb_init_fn init;
bb7cd1
+
bb7cd1
+    ccdb_nextid_send_fn nextid_send;
bb7cd1
+    ccdb_nextid_recv_fn nextid_recv;
bb7cd1
+
bb7cd1
+    ccdb_set_default_send_fn set_default_send;
bb7cd1
+    ccdb_set_default_recv_fn set_default_recv;
bb7cd1
+
bb7cd1
+    ccdb_get_default_send_fn get_default_send;
bb7cd1
+    ccdb_get_default_recv_fn get_default_recv;
bb7cd1
+
bb7cd1
+    ccdb_list_send_fn list_send;
bb7cd1
+    ccdb_list_recv_fn list_recv;
bb7cd1
+
bb7cd1
+    ccdb_getbyname_send_fn getbyname_send;
bb7cd1
+    ccdb_getbyname_recv_fn getbyname_recv;
bb7cd1
+
bb7cd1
+    ccdb_getbyuuid_send_fn getbyuuid_send;
bb7cd1
+    ccdb_getbyuuid_recv_fn getbyuuid_recv;
bb7cd1
+
bb7cd1
+    ccdb_name_by_uuid_send_fn name_by_uuid_send;
bb7cd1
+    ccdb_name_by_uuid_recv_fn name_by_uuid_recv;
bb7cd1
+
bb7cd1
+    ccdb_uuid_by_name_send_fn uuid_by_name_send;
bb7cd1
+    ccdb_uuid_by_name_recv_fn uuid_by_name_recv;
bb7cd1
+
bb7cd1
+    ccdb_create_send_fn create_send;
bb7cd1
+    ccdb_create_recv_fn create_recv;
bb7cd1
+
bb7cd1
+    ccdb_mod_send_fn mod_send;
bb7cd1
+    ccdb_mod_recv_fn mod_recv;
bb7cd1
+
bb7cd1
+    kcm_ccdb_store_cred_blob_send_fn store_cred_send;
bb7cd1
+    kcm_ccdb_store_cred_blob_recv_fn store_cred_recv;
bb7cd1
+
bb7cd1
+    ccdb_delete_send_fn delete_send;
bb7cd1
+    ccdb_delete_recv_fn delete_recv;
bb7cd1
+};
bb7cd1
+
bb7cd1
+extern const struct kcm_ccdb_ops ccdb_mem_ops;
bb7cd1
+
bb7cd1
+#endif /* _KCMSRV_CCACHE_BE_ */
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_ccache_pvt.h b/src/responder/kcm/kcmsrv_ccache_pvt.h
bb7cd1
new file mode 100644
bb7cd1
index 0000000000000000000000000000000000000000..0cc24c2b8cd4d44080d2aa4384f7b2a73520c5a0
bb7cd1
--- /dev/null
bb7cd1
+++ b/src/responder/kcm/kcmsrv_ccache_pvt.h
bb7cd1
@@ -0,0 +1,62 @@
bb7cd1
+/*
bb7cd1
+   SSSD
bb7cd1
+
bb7cd1
+   KCM Server - the KCM ccache operations - private structures
bb7cd1
+
bb7cd1
+   Should be accessed only from the ccache layer.
bb7cd1
+
bb7cd1
+   Copyright (C) Red Hat, 2016
bb7cd1
+
bb7cd1
+   This program is free software; you can redistribute it and/or modify
bb7cd1
+   it under the terms of the GNU General Public License as published by
bb7cd1
+   the Free Software Foundation; either version 3 of the License, or
bb7cd1
+   (at your option) any later version.
bb7cd1
+
bb7cd1
+   This program is distributed in the hope that it will be useful,
bb7cd1
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
bb7cd1
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
bb7cd1
+   GNU General Public License for more details.
bb7cd1
+
bb7cd1
+   You should have received a copy of the GNU General Public License
bb7cd1
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
bb7cd1
+*/
bb7cd1
+#ifndef _KCMSRV_CCACHE_PVT_H
bb7cd1
+#define _KCMSRV_CCACHE_PVT_H
bb7cd1
+
bb7cd1
+#include "responder/kcm/kcmsrv_ccache.h"
bb7cd1
+#include "responder/kcm/kcmsrv_ccache_be.h"
bb7cd1
+
bb7cd1
+struct kcm_ccache_owner {
bb7cd1
+    uid_t uid;
bb7cd1
+    gid_t gid;
bb7cd1
+};
bb7cd1
+
bb7cd1
+struct kcm_cred {
bb7cd1
+    struct sss_iobuf *cred_blob;
bb7cd1
+    /* Randomly generated 16 bytes */
bb7cd1
+    uuid_t uuid;
bb7cd1
+
bb7cd1
+    struct kcm_cred *next;
bb7cd1
+    struct kcm_cred *prev;
bb7cd1
+};
bb7cd1
+
bb7cd1
+struct kcm_ccdb {
bb7cd1
+    enum kcm_ccdb_be cc_be_type;
bb7cd1
+    struct tevent_context *ev;
bb7cd1
+
bb7cd1
+    void *db_handle;
bb7cd1
+    const struct kcm_ccdb_ops *ops;
bb7cd1
+};
bb7cd1
+
bb7cd1
+struct kcm_ccache {
bb7cd1
+    const char *name;
bb7cd1
+    struct kcm_ccache_owner owner;
bb7cd1
+    uuid_t uuid;
bb7cd1
+
bb7cd1
+    krb5_principal client;
bb7cd1
+    int32_t kdc_offset;
bb7cd1
+
bb7cd1
+    struct kcm_cred *creds;
bb7cd1
+};
bb7cd1
+
bb7cd1
+#endif /* _KCMSRV_CCACHE_PVT_H */
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
bb7cd1
index fd1fd9fa32d59a323d465def68999f24f84e3923..a29680246c1e616da75e1bbff951ce2fad66fb65 100644
bb7cd1
--- a/src/responder/kcm/kcmsrv_pvt.h
bb7cd1
+++ b/src/responder/kcm/kcmsrv_pvt.h
bb7cd1
@@ -46,6 +46,7 @@ struct kcm_data {
bb7cd1
  */
bb7cd1
 struct kcm_resp_ctx {
bb7cd1
     krb5_context k5c;
bb7cd1
+    struct kcm_ccdb *db;
bb7cd1
 };
bb7cd1
 
bb7cd1
 /*
bb7cd1
-- 
bb7cd1
2.9.3
bb7cd1