Blob Blame History Raw
From e7aa9061532b1ac139e155e7e9881c2447675e3c Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Tue, 7 Mar 2017 13:49:43 +0100
Subject: [PATCH 25/36] KCM: Add a in-memory credential storage
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Implements a simple back end for the ccache module that lets the KCM
server store credentials directly in memory.

Reviewed-by: Michal Židek <mzidek@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
---
 Makefile.am                           |   1 +
 src/responder/kcm/kcm.c               |  13 +-
 src/responder/kcm/kcmsrv_ccache.c     |   2 +-
 src/responder/kcm/kcmsrv_ccache_mem.c | 805 ++++++++++++++++++++++++++++++++++
 4 files changed, 817 insertions(+), 4 deletions(-)
 create mode 100644 src/responder/kcm/kcmsrv_ccache_mem.c

diff --git a/Makefile.am b/Makefile.am
index a2b9dc49e95fa2d025f5174d2902866fab180a78..5605c1a53c44fd9e83394e80b7f71828df1d39b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1492,6 +1492,7 @@ sssd_kcm_SOURCES = \
     src/responder/kcm/kcm.c \
     src/responder/kcm/kcmsrv_cmd.c \
     src/responder/kcm/kcmsrv_ccache.c \
+    src/responder/kcm/kcmsrv_ccache_mem.c \
     src/util/sss_sockets.c \
     src/util/sss_krb5.c \
     src/util/sss_iobuf.c \
diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
index 90a6999c5e39d48a1a2ea8168d171612a65077d5..2c12ef215ce3967df183e51c20590c5f439d278f 100644
--- a/src/responder/kcm/kcm.c
+++ b/src/responder/kcm/kcm.c
@@ -22,9 +22,9 @@
 #include "config.h"
 
 #include <popt.h>
-#include <krb5/krb5.h>
 
 #include "responder/kcm/kcm.h"
+#include "responder/kcm/kcmsrv_ccache.h"
 #include "responder/kcm/kcmsrv_pvt.h"
 #include "responder/common/responder.h"
 #include "util/util.h"
@@ -110,7 +110,8 @@ static int kcm_data_destructor(void *ptr)
     return 0;
 }
 
-static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx)
+static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev)
 {
     struct kcm_resp_ctx *kcm_data;
     krb5_error_code kret;
@@ -121,6 +122,12 @@ static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx)
         return NULL;
     }
 
+    kcm_data->db = kcm_ccdb_init(kcm_data, ev, CCDB_BE_MEMORY);
+    if (kcm_data->db == NULL) {
+        talloc_free(kcm_data);
+        return NULL;
+    }
+
     kret = krb5_init_context(&kcm_data->k5c);
     if (kret != EOK) {
         talloc_free(kcm_data);
@@ -169,7 +176,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
-    kctx->kcm_data = kcm_data_setup(kctx);
+    kctx->kcm_data = kcm_data_setup(kctx, ev);
     if (kctx->kcm_data == NULL) {
         DEBUG(SSSDBG_FATAL_FAILURE,
               "fatal error initializing responder data\n");
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
index 2c565b8378e3ec297faf655d3c48d7ab902713d3..2ae120269b0c62275ba2acdff6d6daa8b7077708 100644
--- a/src/responder/kcm/kcmsrv_ccache.c
+++ b/src/responder/kcm/kcmsrv_ccache.c
@@ -240,7 +240,7 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
     switch (cc_be) {
     case CCDB_BE_MEMORY:
         DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n");
-        /* Not implemented yet */
+        ccdb->ops = &ccdb_mem_ops;
         break;
     case CCDB_BE_SECRETS:
         DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n");
diff --git a/src/responder/kcm/kcmsrv_ccache_mem.c b/src/responder/kcm/kcmsrv_ccache_mem.c
new file mode 100644
index 0000000000000000000000000000000000000000..1c4f3df8d3b35b0428a143d4b545562d9cc0e574
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_mem.c
@@ -0,0 +1,805 @@
+/*
+   SSSD
+
+   KCM Server - ccache in-memory storage
+
+   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 <talloc.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+
+struct ccdb_mem;
+
+/*
+ * The KCM memory database is just a double-linked list of kcm_ccache structures
+ */
+struct ccache_mem_wrap {
+    struct kcm_ccache *cc;
+    bool is_default;
+
+    struct ccache_mem_wrap *next;
+    struct ccache_mem_wrap *prev;
+
+    struct ccdb_mem *mem_be;
+};
+
+struct ccdb_mem {
+    /* Both ccaches and the next-id are kept in memory */
+    struct ccache_mem_wrap *head;
+    unsigned int nextid;
+};
+
+/* In order to provide a consistent interface, we need to let the caller
+ * of getbyXXX own the ccache, therefore the memory back end returns a shallow
+ * copy of the ccache
+ */
+static struct kcm_ccache *kcm_ccache_dup(TALLOC_CTX *mem_ctx,
+                                         struct kcm_ccache *in)
+{
+    struct kcm_ccache *out;
+
+    out = talloc_zero(mem_ctx, struct kcm_ccache);
+    if (out == NULL) {
+        return NULL;
+    }
+    memcpy(out, in, sizeof(struct kcm_ccache));
+
+    return out;
+}
+
+static struct ccache_mem_wrap *memdb_get_by_uuid(struct ccdb_mem *memdb,
+                                                 struct cli_creds *client,
+                                                 uuid_t uuid)
+{
+    uid_t uid;
+    struct ccache_mem_wrap *ccwrap = NULL;
+    struct ccache_mem_wrap *out = NULL;
+
+    uid = cli_creds_get_uid(client);
+
+    DLIST_FOR_EACH(ccwrap, memdb->head) {
+        if (ccwrap->cc == NULL) {
+            /* since KCM stores ccaches, better not crash.. */
+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+            continue;
+        }
+
+        if (ccwrap->cc->owner.uid == uid) {
+            if (uuid_compare(uuid, ccwrap->cc->uuid) == 0) {
+                out = ccwrap;
+                break;
+            }
+        }
+    }
+
+    return out;
+}
+
+static struct ccache_mem_wrap *memdb_get_by_name(struct ccdb_mem *memdb,
+                                                 struct cli_creds *client,
+                                                 const char *name)
+{
+    uid_t uid;
+    struct ccache_mem_wrap *ccwrap = NULL;
+    struct ccache_mem_wrap *out = NULL;
+
+    uid = cli_creds_get_uid(client);
+
+    DLIST_FOR_EACH(ccwrap, memdb->head) {
+        if (ccwrap->cc == NULL) {
+            /* since KCM stores ccaches, better not crash.. */
+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+            continue;
+        }
+
+        if (ccwrap->cc->owner.uid == uid) {
+            if (strcmp(ccwrap->cc->name, name) == 0) {
+                out = ccwrap;
+                break;
+            }
+        }
+    }
+
+    return out;
+}
+
+/* Since with the in-memory database, the database operations are just
+ * fake-async wrappers around otherwise sync operations, we don't often
+ * need any state, so we use this empty structure instead
+ */
+struct ccdb_mem_dummy_state {
+};
+
+static int ccwrap_destructor(void *ptr)
+{
+    struct ccache_mem_wrap *ccwrap = talloc_get_type(ptr, struct ccache_mem_wrap);
+
+    if (ccwrap == NULL) {
+        return 0;
+    }
+
+    if (ccwrap->cc != NULL) {
+        if (ccwrap->cc->creds) {
+            safezero(sss_iobuf_get_data(ccwrap->cc->creds->cred_blob),
+                     sss_iobuf_get_size(ccwrap->cc->creds->cred_blob));
+        }
+    }
+
+
+    DLIST_REMOVE(ccwrap->mem_be->head, ccwrap);
+
+    return 0;
+}
+
+static errno_t ccdb_mem_init(struct kcm_ccdb *db)
+{
+    struct ccdb_mem *memdb = NULL;
+
+    memdb = talloc_zero(db, struct ccdb_mem);
+    if (memdb == NULL) {
+        return ENOMEM;
+    }
+    db->db_handle = memdb;
+
+    return EOK;
+}
+
+struct ccdb_mem_nextid_state {
+    unsigned int nextid;
+};
+
+static struct tevent_req *ccdb_mem_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_mem_nextid_state *state = NULL;
+    struct ccdb_mem *memdb = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_nextid_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    if (memdb == NULL) {
+        ret = EIO;
+        goto immediate;
+    }
+
+    state->nextid = memdb->nextid++;
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_nextid_recv(struct tevent_req *req,
+                                    unsigned int *_nextid)
+{
+    struct ccdb_mem_nextid_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_nextid_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    *_nextid = state->nextid;
+    return EOK;
+}
+
+struct ccdb_mem_list_state {
+    uuid_t *uuid_list;
+};
+
+static struct tevent_req *ccdb_mem_list_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct kcm_ccdb *db,
+                                             struct cli_creds *client)
+{
+    struct tevent_req *req = NULL;
+    struct ccache_mem_wrap *ccwrap = NULL;
+    struct ccdb_mem_list_state *state = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    size_t num_ccaches = 0;
+    size_t cc_index = 0;
+    errno_t ret;
+    uid_t uid;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_list_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    uid = cli_creds_get_uid(client);
+
+    DLIST_FOR_EACH(ccwrap, memdb->head) {
+        if (ccwrap->cc->owner.uid == uid) {
+            num_ccaches++;
+        }
+    }
+
+    state->uuid_list = talloc_zero_array(state, uuid_t, num_ccaches+1);
+    if (state->uuid_list == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+
+    cc_index = 0;
+    DLIST_FOR_EACH(ccwrap, memdb->head) {
+        if (ccwrap->cc->owner.uid == uid) {
+            uuid_copy(state->uuid_list[cc_index], ccwrap->cc->uuid);
+            cc_index++;
+        }
+    }
+    uuid_clear(state->uuid_list[num_ccaches]);
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_list_recv(struct tevent_req *req,
+                                  TALLOC_CTX *mem_ctx,
+                                  uuid_t **_uuid_list)
+{
+    struct ccdb_mem_list_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_list_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
+    return EOK;
+}
+
+static struct tevent_req *ccdb_mem_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 ccdb_mem_dummy_state *state = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    struct ccache_mem_wrap *ccwrap = NULL;
+    uid_t uid = cli_creds_get_uid(client);
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    /* Reset all ccache defaults first */
+    DLIST_FOR_EACH(ccwrap, memdb->head) {
+        if (ccwrap->cc == NULL) {
+            /* since KCM stores ccaches, better not crash.. */
+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+            continue;
+        }
+
+        if (ccwrap->cc->owner.uid == uid) {
+            ccwrap->is_default = false;
+        }
+    }
+
+    /* Then set the default for the right ccache. This also allows to
+     * pass a null uuid to just reset the old ccache (for example after
+     * deleting the default
+     */
+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+    if (ccwrap != NULL) {
+        ccwrap->is_default = true;
+    }
+
+    tevent_req_done(req);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_set_default_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+struct ccdb_mem_get_default_state {
+    uuid_t dfl_uuid;
+};
+
+static struct tevent_req *ccdb_mem_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 ccdb_mem_get_default_state *state = NULL;
+    struct ccache_mem_wrap *ccwrap = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    uid_t uid = cli_creds_get_uid(client);
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_get_default_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+
+    /* Reset all ccache defaults first */
+    DLIST_FOR_EACH(ccwrap, memdb->head) {
+        if (ccwrap->cc == NULL) {
+            /* since KCM stores ccaches, better not crash.. */
+            DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+            continue;
+        }
+
+        if (ccwrap->cc->owner.uid == uid && ccwrap->is_default == true) {
+            break;
+        }
+    }
+
+    if (ccwrap == NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+               "No ccache marked as default, returning null ccache\n");
+        uuid_clear(state->dfl_uuid);
+    } else {
+        uuid_copy(state->dfl_uuid, ccwrap->cc->uuid);
+    }
+
+    tevent_req_done(req);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_get_default_recv(struct tevent_req *req,
+                                         uuid_t dfl)
+{
+    struct ccdb_mem_get_default_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_get_default_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    uuid_copy(dfl, state->dfl_uuid);
+    return EOK;
+}
+
+struct ccdb_mem_getbyuuid_state {
+    struct kcm_ccache *cc;
+};
+
+static struct tevent_req *ccdb_mem_getbyuuid_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 ccdb_mem_getbyuuid_state *state = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    struct ccache_mem_wrap *ccwrap = NULL;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyuuid_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+    if (ccwrap != NULL) {
+        state->cc = kcm_ccache_dup(state, ccwrap->cc);
+    }
+
+    tevent_req_done(req);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_getbyuuid_recv(struct tevent_req *req,
+                                       TALLOC_CTX *mem_ctx,
+                                       struct kcm_ccache **_cc)
+{
+    struct ccdb_mem_getbyuuid_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_getbyuuid_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    *_cc = talloc_steal(mem_ctx, state->cc);
+    return EOK;
+}
+
+struct ccdb_mem_getbyname_state {
+    struct kcm_ccache *cc;
+};
+
+static struct tevent_req *ccdb_mem_getbyname_send(TALLOC_CTX *mem_ctx,
+                                                  struct tevent_context *ev,
+                                                  struct kcm_ccdb *db,
+                                                  struct cli_creds *client,
+                                                  const char *name)
+{
+    struct tevent_req *req = NULL;
+    struct ccdb_mem_getbyname_state *state = NULL;
+    struct ccache_mem_wrap *ccwrap = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyname_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = memdb_get_by_name(memdb, client, name);
+    if (ccwrap != NULL) {
+        state->cc = kcm_ccache_dup(state, ccwrap->cc);
+    }
+
+    tevent_req_done(req);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_getbyname_recv(struct tevent_req *req,
+                                       TALLOC_CTX *mem_ctx,
+                                       struct kcm_ccache **_cc)
+{
+    struct ccdb_mem_getbyname_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_getbyname_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    *_cc = talloc_steal(mem_ctx, state->cc);
+    return EOK;
+}
+
+struct ccdb_mem_name_by_uuid_state {
+    const char *name;
+};
+
+struct tevent_req *ccdb_mem_name_by_uuid_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 ccdb_mem_name_by_uuid_state *state = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    struct ccache_mem_wrap *ccwrap = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_name_by_uuid_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+    if (ccwrap == NULL) {
+        ret = ERR_KCM_CC_END;
+        goto immediate;
+    }
+
+    state->name = talloc_strdup(state, ccwrap->cc->name);
+    if (state->name == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+errno_t ccdb_mem_name_by_uuid_recv(struct tevent_req *req,
+                                   TALLOC_CTX *mem_ctx,
+                                   const char **_name)
+{
+    struct ccdb_mem_name_by_uuid_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_name_by_uuid_state);
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    *_name = talloc_steal(mem_ctx, state->name);
+    return EOK;
+}
+
+struct ccdb_mem_uuid_by_name_state {
+    uuid_t uuid;
+};
+
+struct tevent_req *ccdb_mem_uuid_by_name_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct kcm_ccdb *db,
+                                              struct cli_creds *client,
+                                              const char *name)
+{
+    struct tevent_req *req = NULL;
+    struct ccdb_mem_uuid_by_name_state *state = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    struct ccache_mem_wrap *ccwrap = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_uuid_by_name_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = memdb_get_by_name(memdb, client, name);
+    if (ccwrap == NULL) {
+        ret = ERR_KCM_CC_END;
+        goto immediate;
+    }
+
+    uuid_copy(state->uuid, ccwrap->cc->uuid);
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+errno_t ccdb_mem_uuid_by_name_recv(struct tevent_req *req,
+                                   TALLOC_CTX *mem_ctx,
+                                   uuid_t _uuid)
+{
+    struct ccdb_mem_uuid_by_name_state *state = tevent_req_data(req,
+                                                struct ccdb_mem_uuid_by_name_state);
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    uuid_copy(_uuid, state->uuid);
+    return EOK;
+}
+
+static struct tevent_req *ccdb_mem_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 *req = NULL;
+    struct ccdb_mem_dummy_state *state = NULL;
+    struct ccache_mem_wrap *ccwrap;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = talloc_zero(memdb, struct ccache_mem_wrap);
+    if (ccwrap == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+    ccwrap->cc = cc;
+    ccwrap->mem_be = memdb;
+    talloc_steal(ccwrap, cc);
+
+    DLIST_ADD(memdb->head, ccwrap);
+    talloc_set_destructor((TALLOC_CTX *) ccwrap, ccwrap_destructor);
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_create_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+static struct tevent_req *ccdb_mem_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 ccdb_mem_dummy_state *state = NULL;
+    struct ccache_mem_wrap *ccwrap = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    /* UUID is immutable, so search by that */
+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+    if (ccwrap == NULL) {
+        ret = ERR_KCM_CC_END;
+        goto immediate;
+    }
+
+    kcm_mod_cc(ccwrap->cc, mod_cc);
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_mod_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+static struct tevent_req *ccdb_mem_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)
+{
+    struct tevent_req *req = NULL;
+    struct ccdb_mem_dummy_state *state = NULL;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    struct ccache_mem_wrap *ccwrap = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+    if (ccwrap == NULL) {
+        ret = ERR_KCM_CC_END;
+        goto immediate;
+    }
+
+    ret = kcm_cc_store_cred_blob(ccwrap->cc, cred_blob);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot store credentials to ccache [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto immediate;
+    }
+
+    ret = EOK;
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_store_cred_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+static struct tevent_req *ccdb_mem_delete_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 ccdb_mem_dummy_state *state = NULL;
+    struct ccache_mem_wrap *ccwrap;
+    struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+    if (ccwrap == NULL) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "BUG: Attempting to free unknown ccache\n");
+        ret = ERR_KCM_CC_END;
+        goto immediate;
+    }
+
+    ret = EOK;
+    /* Destructor takes care of everything */
+    talloc_free(ccwrap);
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ccdb_mem_delete_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+const struct kcm_ccdb_ops ccdb_mem_ops = {
+    .init = ccdb_mem_init,
+
+    .nextid_send = ccdb_mem_nextid_send,
+    .nextid_recv = ccdb_mem_nextid_recv,
+
+    .set_default_send = ccdb_mem_set_default_send,
+    .set_default_recv = ccdb_mem_set_default_recv,
+
+    .get_default_send = ccdb_mem_get_default_send,
+    .get_default_recv = ccdb_mem_get_default_recv,
+
+    .list_send = ccdb_mem_list_send,
+    .list_recv = ccdb_mem_list_recv,
+
+    .getbyname_send = ccdb_mem_getbyname_send,
+    .getbyname_recv = ccdb_mem_getbyname_recv,
+
+    .getbyuuid_send = ccdb_mem_getbyuuid_send,
+    .getbyuuid_recv = ccdb_mem_getbyuuid_recv,
+
+    .name_by_uuid_send = ccdb_mem_name_by_uuid_send,
+    .name_by_uuid_recv = ccdb_mem_name_by_uuid_recv,
+
+    .uuid_by_name_send = ccdb_mem_uuid_by_name_send,
+    .uuid_by_name_recv = ccdb_mem_uuid_by_name_recv,
+
+    .create_send = ccdb_mem_create_send,
+    .create_recv = ccdb_mem_create_recv,
+
+    .mod_send = ccdb_mem_mod_send,
+    .mod_recv = ccdb_mem_mod_recv,
+
+    .store_cred_send = ccdb_mem_store_cred_send,
+    .store_cred_recv = ccdb_mem_store_cred_recv,
+
+    .delete_send = ccdb_mem_delete_send,
+    .delete_recv = ccdb_mem_delete_recv,
+};
-- 
2.9.3