dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone

Blame SOURCES/0002-KCM-perf-improvements.patch

bf7bd7
From 19c0cfe38670cc56219f0d9acdc2b3363e92616c Mon Sep 17 00:00:00 2001
bf7bd7
From: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
Date: Fri, 4 Dec 2020 12:09:57 +0100
bf7bd7
Subject: [PATCH] Squashed commit of the following:
bf7bd7
MIME-Version: 1.0
bf7bd7
Content-Type: text/plain; charset=UTF-8
bf7bd7
Content-Transfer-Encoding: 8bit
bf7bd7
bf7bd7
commit 325de5a5bb97ba026be6d22492bea8ab2605f1b5
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Nov 26 12:07:06 2020 +0100
bf7bd7
bf7bd7
    secrets: remove base64 enctype
bf7bd7
bf7bd7
    This was added as part of KCM performance improvements but never used.
bf7bd7
    Ldb is fully capable of holding binary data without the need for base64
bf7bd7
    encoding so this is not needed.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 39277cdadd317b0ab86cdd37de0616bc3eecbe6a
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Nov 26 11:55:39 2020 +0100
bf7bd7
bf7bd7
    secrets: move attrs names to macros
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 9c1b51d057390fb5b26151f814a480911cda4cc9
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Nov 26 11:47:24 2020 +0100
bf7bd7
bf7bd7
    secrets: default to "plaintext" if "enctype" attr is missing
bf7bd7
bf7bd7
    This is a sane fallback behavior, however it should not happen since
bf7bd7
    the attribute should be always present.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit bf127d4f3f42e5b2afe25e512211439bc12a9904
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Tue Nov 3 13:35:33 2020 +0100
bf7bd7
bf7bd7
    secrets: fix may_payload_size exceeded debug message
bf7bd7
bf7bd7
    The unit is bytes (B) not bits (b) and the conversion of the input
bf7bd7
    payload size to KiB was wrong (multiplying bytes * 1024).
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit c3b314db57c34f64aaca7d74e76a9a955288bb51
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Mon Oct 19 12:40:07 2020 +0200
bf7bd7
bf7bd7
    kcm: store credentials list in hash table to avoid cache lookups
bf7bd7
bf7bd7
    Iteration over ccache requires CRED_UUID_LIST and then calling
bf7bd7
    CRED_BY_UUID for each uuid in the obtained list. Each CRED_BY_UUID
bf7bd7
    operation invoked ldb_search and decryption. This was a substantional
bf7bd7
    bottle neck.
bf7bd7
bf7bd7
    Resolves: https://github.com/SSSD/sssd/issues/5349
bf7bd7
bf7bd7
    :fixes: KCM performance has improved dramatically for cases where
bf7bd7
      large amount of credentials are stored in the ccache.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit a370553c90c2ed6df3b94c169c4960a6f978031f
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Oct 29 14:57:53 2020 +0100
bf7bd7
bf7bd7
    sss_ptr_hash: fix double free for circular dependencies
bf7bd7
bf7bd7
    If the hash table delete callback deletes the stored item,
bf7bd7
    we can end up in double free in case when we try to override
bf7bd7
    an existing item (hash_enter(key) where key already exists).
bf7bd7
bf7bd7
    ```c
bf7bd7
    static void delete_cb(hash_entry_t *item,
bf7bd7
                          hash_destroy_enum deltype,
bf7bd7
                          void *pvt)
bf7bd7
    {
bf7bd7
        talloc_free(item->value.ptr);
bf7bd7
    }
bf7bd7
bf7bd7
    hash_enter(key);
bf7bd7
    hash_enter(key);
bf7bd7
    ```
bf7bd7
bf7bd7
    The doble free it self is fine, since it is done via talloc destructor
bf7bd7
    and talloc can cope with that. However, the hash table fails to store
bf7bd7
    the new entry because hash_delete is called twice.
bf7bd7
bf7bd7
    ```
bf7bd7
    _sss_ptr_hash_add -> hash_enter -> hash_delete(old) -> delete_cb -> sss_ptr_hash_value_destructor -> hash_delete
bf7bd7
    ```
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 241ee30da12f564803793ee2b14c1522aabd9235
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Fri Oct 16 15:36:51 2020 +0200
bf7bd7
bf7bd7
    kcm: add per-connection data to be shared between requests
bf7bd7
bf7bd7
    Resolves: https://github.com/SSSD/sssd/issues/5349
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 194447d35c11eb914f54719491dc5cfaab01b9a1
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Tue Oct 27 16:21:31 2020 +0100
bf7bd7
bf7bd7
    kcm: use binary format to store ccache instead of json
bf7bd7
bf7bd7
    JSON is computationally complex and the parser is a bottleneck which
bf7bd7
    consumes about 10% of time. It also create the ccache unnecessary
bf7bd7
    large because it requires lots of unneded character and base64
bf7bd7
    encoding.
bf7bd7
bf7bd7
    Binary format is fast, simple and small.
bf7bd7
bf7bd7
    This is backwards compatible and there is no need to destroy existing
bf7bd7
    ccache. It will be stored in binary format at first write to the cache.
bf7bd7
bf7bd7
    Resolves: https://github.com/SSSD/sssd/issues/5349
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit f17740d831e16449495fff4ec57cc4800aaac83d
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Tue Oct 27 17:09:43 2020 +0100
bf7bd7
bf7bd7
    kcm: add spaces around operators in kcmsrv_ccache_key.c
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 15069a647ed6c7f1ead42baa1d421d953c9bc557
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Tue Oct 27 16:37:05 2020 +0100
bf7bd7
bf7bd7
    kcm: avoid suppression of cppcheck warning
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit e63a15038ac9c186626e4fdf681a6492031d1e40
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Tue Oct 27 16:18:11 2020 +0100
bf7bd7
bf7bd7
    kcm: move sec key parser to separate file so it can be shared
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 9b1631defdcaa3ea7e87889eb136e7fa935ab4ce
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Oct 22 13:34:52 2020 +0200
bf7bd7
bf7bd7
    kcm: add json suffix to existing searialization functions
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit b6cc661b9f4162e590137430e945aa321fc13121
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Fri Oct 23 13:10:13 2020 +0200
bf7bd7
bf7bd7
    iobuf: add more iobuf functions
bf7bd7
bf7bd7
    These will be used in later patches.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit ed08ba0023e63024bf1c52ae3f6596b9d804d0a5
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Oct 22 12:18:38 2020 +0200
bf7bd7
bf7bd7
    secrets: accept binary data instead of string
bf7bd7
bf7bd7
    Currently, both KCM and secrets responders store JSON formatted string
bf7bd7
    in the secrets database. One of the next commits makes KCM to store
bf7bd7
    binary format instead of JSON string to improve performance. We need
bf7bd7
    to be able to distinguish the formats to keep KCM update compatible
bf7bd7
    with existing ccache and also to keep secrets responder working.
bf7bd7
bf7bd7
    Secrets responder test had to be ammended to fit into a new maximum
bf7bd7
    payload which is now reduced by one byte for the secrets responder
bf7bd7
    to hold the ending zero of a secret string.
bf7bd7
bf7bd7
    This is a corner case in a long deprecated responder that is not even
bf7bd7
    built by default and has no known consumers so it is fine to fast fix
bf7bd7
    the test.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 908c15af9a9f8f0556a588e368e4a0b2e24ace1b
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Thu Oct 22 11:18:12 2020 +0200
bf7bd7
bf7bd7
    secrets: allow to specify secret's data format
bf7bd7
bf7bd7
    Currently, both KCM and secrets responders store JSON formatted string
bf7bd7
    in the secrets database. One of the next commits makes KCM to store
bf7bd7
    binary format instead of JSON string to improve performance. We need
bf7bd7
    to be able to distinguish the formats to keep KCM update compatible
bf7bd7
    with existing ccache and also to keep secrets responder working.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 74fdaa64b27e88a6e0f153f8cb59989c572d4294
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Tue Oct 27 16:45:22 2020 +0100
bf7bd7
bf7bd7
    kcm: avoid multiple debug messages if sss_sec_put fails
bf7bd7
bf7bd7
    sec_put() already logs a message if the underlaying function fails
bf7bd7
    so this debug message is really unnecessary.
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit b8f28d9aa9d862cf504691c9c3f92941a63fb0a4
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Mon Oct 19 12:59:48 2020 +0200
bf7bd7
bf7bd7
    kcm: disable encryption
bf7bd7
bf7bd7
    Encryption was a huge bottleneck for the secdb backend. This is
bf7bd7
    backwards compatible and there is no need to destroy existing
bf7bd7
    ccache. It will be stored unencrypted at first write to the cache.
bf7bd7
bf7bd7
    Note that the encryption did not provide any security as the cache
bf7bd7
    is accessible only by root and the master key is stored together
bf7bd7
    with the cache. So once someone gains access to the file it can
bf7bd7
    be easily decrypted. Additionaly, there was also no encryption at
bf7bd7
    the memory level.
bf7bd7
bf7bd7
    Resolves: https://github.com/SSSD/sssd/issues/5349
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 8edcea8c377e85d037e83065c1904fa4b92c4a39
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Fri Oct 16 15:33:42 2020 +0200
bf7bd7
bf7bd7
    kcm: avoid name confusion in GET_CRED_UUID_LIST handlers
bf7bd7
bf7bd7
    The function name did not follow best practices and it got easily confused
bf7bd7
    with `kcm_op_get_cred_by_uuid_getbyname_done`.
bf7bd7
bf7bd7
    ```
bf7bd7
    kcm_op_get_cred_uuid_getbyname_done
bf7bd7
    kcm_op_get_cred_by_uuid_getbyname_done
bf7bd7
    ```
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
bf7bd7
commit 47a316c850107f12d406f27abb216e26383dfab7
bf7bd7
Author: Pavel Březina <pbrezina@redhat.com>
bf7bd7
Date:   Mon Sep 14 12:44:57 2020 +0200
bf7bd7
bf7bd7
    kcm: fix typos in debug messages
bf7bd7
bf7bd7
    Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
bf7bd7
---
bf7bd7
 Makefile.am                                   |  14 +-
bf7bd7
 src/responder/kcm/kcmsrv_ccache.c             |  66 ++++
bf7bd7
 src/responder/kcm/kcmsrv_ccache.h             |  47 ++-
bf7bd7
 src/responder/kcm/kcmsrv_ccache_binary.c      | 308 ++++++++++++++++++
bf7bd7
 src/responder/kcm/kcmsrv_ccache_json.c        | 149 +--------
bf7bd7
 src/responder/kcm/kcmsrv_ccache_key.c         | 144 ++++++++
bf7bd7
 src/responder/kcm/kcmsrv_ccache_mem.c         |  30 +-
bf7bd7
 src/responder/kcm/kcmsrv_ccache_secdb.c       | 128 +++-----
bf7bd7
 src/responder/kcm/kcmsrv_ccache_secrets.c     |   9 +-
bf7bd7
 src/responder/kcm/kcmsrv_cmd.c                |  23 +-
bf7bd7
 src/responder/kcm/kcmsrv_ops.c                | 252 ++++++++++----
bf7bd7
 src/responder/kcm/kcmsrv_ops.h                |   8 +
bf7bd7
 src/responder/secrets/local.c                 |   5 +-
bf7bd7
 src/shared/safealign.h                        |   4 +
bf7bd7
 ...n_marshalling.c => test_kcm_marshalling.c} | 147 +++++++--
bf7bd7
 src/tests/cmocka/test_sss_ptr_hash.c          |  39 +++
bf7bd7
 src/tests/cmocka/test_utils.c                 |   3 +
bf7bd7
 src/tests/cmocka/test_utils.h                 |   1 +
bf7bd7
 src/tests/intg/test_secrets.py                |   3 +-
bf7bd7
 src/tests/multihost/basic/test_kcm.py         |  12 +-
bf7bd7
 src/util/secrets/sec_pvt.h                    |   2 +-
bf7bd7
 src/util/secrets/secrets.c                    | 290 ++++++++++++-----
bf7bd7
 src/util/secrets/secrets.h                    |  20 +-
bf7bd7
 src/util/sss_iobuf.c                          | 141 ++++++++
bf7bd7
 src/util/sss_iobuf.h                          |  46 +++
bf7bd7
 src/util/sss_ptr_hash.c                       |  20 ++
bf7bd7
 26 files changed, 1457 insertions(+), 454 deletions(-)
bf7bd7
 create mode 100644 src/responder/kcm/kcmsrv_ccache_binary.c
bf7bd7
 create mode 100644 src/responder/kcm/kcmsrv_ccache_key.c
bf7bd7
 rename src/tests/cmocka/{test_kcm_json_marshalling.c => test_kcm_marshalling.c} (71%)
bf7bd7
bf7bd7
diff --git a/Makefile.am b/Makefile.am
bf7bd7
index 97aa1ec66..430b4e842 100644
bf7bd7
--- a/Makefile.am
bf7bd7
+++ b/Makefile.am
bf7bd7
@@ -311,7 +311,7 @@ endif   # HAVE_INOTIFY
bf7bd7
 
bf7bd7
 if BUILD_KCM
bf7bd7
 non_interactive_cmocka_based_tests += \
bf7bd7
-	test_kcm_json \
bf7bd7
+	test_kcm_marshalling \
bf7bd7
 	test_kcm_queue \
bf7bd7
         $(NULL)
bf7bd7
 endif   # BUILD_KCM
bf7bd7
@@ -1817,8 +1817,10 @@ sssd_kcm_SOURCES = \
bf7bd7
     src/responder/kcm/kcm.c \
bf7bd7
     src/responder/kcm/kcmsrv_cmd.c \
bf7bd7
     src/responder/kcm/kcmsrv_ccache.c \
bf7bd7
+    src/responder/kcm/kcmsrv_ccache_binary.c \
bf7bd7
     src/responder/kcm/kcmsrv_ccache_mem.c \
bf7bd7
     src/responder/kcm/kcmsrv_ccache_json.c \
bf7bd7
+    src/responder/kcm/kcmsrv_ccache_key.c \
bf7bd7
     src/responder/kcm/kcmsrv_ccache_secdb.c \
bf7bd7
     src/responder/kcm/kcmsrv_ops.c \
bf7bd7
     src/responder/kcm/kcmsrv_op_queue.c \
bf7bd7
@@ -3927,18 +3929,20 @@ test_sssd_krb5_locator_plugin_LDADD = \
bf7bd7
     $(NULL)
bf7bd7
 
bf7bd7
 if BUILD_KCM
bf7bd7
-test_kcm_json_SOURCES = \
bf7bd7
-    src/tests/cmocka/test_kcm_json_marshalling.c \
bf7bd7
+test_kcm_marshalling_SOURCES = \
bf7bd7
+    src/tests/cmocka/test_kcm_marshalling.c \
bf7bd7
+    src/responder/kcm/kcmsrv_ccache_binary.c \
bf7bd7
     src/responder/kcm/kcmsrv_ccache_json.c \
bf7bd7
+    src/responder/kcm/kcmsrv_ccache_key.c \
bf7bd7
     src/responder/kcm/kcmsrv_ccache.c \
bf7bd7
     src/util/sss_krb5.c \
bf7bd7
     src/util/sss_iobuf.c \
bf7bd7
     $(NULL)
bf7bd7
-test_kcm_json_CFLAGS = \
bf7bd7
+test_kcm_marshalling_CFLAGS = \
bf7bd7
     $(AM_CFLAGS) \
bf7bd7
     $(UUID_CFLAGS) \
bf7bd7
     $(NULL)
bf7bd7
-test_kcm_json_LDADD = \
bf7bd7
+test_kcm_marshalling_LDADD = \
bf7bd7
     $(JANSSON_LIBS) \
bf7bd7
     $(UUID_LIBS) \
bf7bd7
     $(KRB5_LIBS) \
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
bf7bd7
index 66e2752ba..60eacd451 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ccache.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache.c
bf7bd7
@@ -28,6 +28,9 @@
bf7bd7
 #include "responder/kcm/kcmsrv_ccache_pvt.h"
bf7bd7
 #include "responder/kcm/kcmsrv_ccache_be.h"
bf7bd7
 
bf7bd7
+static struct kcm_cred *kcm_cred_dup(TALLOC_CTX *mem_ctx,
bf7bd7
+                                     struct kcm_cred *crd);
bf7bd7
+
bf7bd7
 static int kcm_cc_destructor(struct kcm_ccache *cc)
bf7bd7
 {
bf7bd7
     if (cc == NULL) {
bf7bd7
@@ -94,6 +97,33 @@ done:
bf7bd7
     return ret;
bf7bd7
 }
bf7bd7
 
bf7bd7
+struct kcm_ccache *kcm_cc_dup(TALLOC_CTX *mem_ctx,
bf7bd7
+                              const struct kcm_ccache *cc)
bf7bd7
+{
bf7bd7
+    struct kcm_ccache *dup;
bf7bd7
+    struct kcm_cred *crd_dup;
bf7bd7
+    struct kcm_cred *crd;
bf7bd7
+
bf7bd7
+    dup = talloc_zero(mem_ctx, struct kcm_ccache);
bf7bd7
+    if (dup == NULL) {
bf7bd7
+        return NULL;
bf7bd7
+    }
bf7bd7
+    memcpy(dup, cc, sizeof(struct kcm_ccache));
bf7bd7
+
bf7bd7
+    dup->creds = NULL;
bf7bd7
+    DLIST_FOR_EACH(crd, cc->creds) {
bf7bd7
+        crd_dup = kcm_cred_dup(dup, crd);
bf7bd7
+        if (crd_dup == NULL) {
bf7bd7
+            talloc_free(dup);
bf7bd7
+            return NULL;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        DLIST_ADD(dup->creds, crd_dup);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return dup;
bf7bd7
+}
bf7bd7
+
bf7bd7
 const char *kcm_cc_get_name(struct kcm_ccache *cc)
bf7bd7
 {
bf7bd7
     return cc ? cc->name : NULL;
bf7bd7
@@ -204,6 +234,22 @@ struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
bf7bd7
     return kcreds;
bf7bd7
 }
bf7bd7
 
bf7bd7
+static struct kcm_cred *kcm_cred_dup(TALLOC_CTX *mem_ctx,
bf7bd7
+                                     struct kcm_cred *crd)
bf7bd7
+{
bf7bd7
+    struct kcm_cred *dup;
bf7bd7
+
bf7bd7
+    dup = talloc_zero(mem_ctx, struct kcm_cred);
bf7bd7
+    if (dup == NULL) {
bf7bd7
+        return NULL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    uuid_copy(dup->uuid, crd->uuid);
bf7bd7
+    dup->cred_blob = crd->cred_blob;
bf7bd7
+
bf7bd7
+    return dup;
bf7bd7
+}
bf7bd7
+
bf7bd7
 /* Add a cred to ccache */
bf7bd7
 errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
bf7bd7
                            struct kcm_cred *crd)
bf7bd7
@@ -213,6 +259,26 @@ errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
bf7bd7
     return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
+errno_t kcm_cc_set_header(struct kcm_ccache *cc,
bf7bd7
+                          const char *sec_key,
bf7bd7
+                          struct cli_creds *client)
bf7bd7
+{
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    /* We rely on sssd-secrets only searching the user's subtree so we
bf7bd7
+     * set the ownership to the client
bf7bd7
+     */
bf7bd7
+    cc->owner.uid = cli_creds_get_uid(client);
bf7bd7
+    cc->owner.gid = cli_creds_get_gid(client);
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
 errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid)
bf7bd7
 {
bf7bd7
     if (crd == NULL) {
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
bf7bd7
index d629923fa..77cf8f61d 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ccache.h
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache.h
bf7bd7
@@ -72,6 +72,13 @@ errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
bf7bd7
                    krb5_principal princ,
bf7bd7
                    struct kcm_ccache **_cc);
bf7bd7
 
bf7bd7
+/*
bf7bd7
+ * Duplicate the ccache. Only ccache and credentials are duplicated,
bf7bd7
+ * but their data are a shallow copy.
bf7bd7
+ */
bf7bd7
+struct kcm_ccache *kcm_cc_dup(TALLOC_CTX *mem_ctx,
bf7bd7
+                              const struct kcm_ccache *cc);
bf7bd7
+
bf7bd7
 /*
bf7bd7
  * Returns true if a client can access a ccache.
bf7bd7
  *
bf7bd7
@@ -100,6 +107,11 @@ struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
bf7bd7
 errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
bf7bd7
                            struct kcm_cred *crd);
bf7bd7
 
bf7bd7
+/* Set cc header information from sec key and client */
bf7bd7
+errno_t kcm_cc_set_header(struct kcm_ccache *cc,
bf7bd7
+                          const char *sec_key,
bf7bd7
+                          struct cli_creds *client);
bf7bd7
+
bf7bd7
 errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid);
bf7bd7
 
bf7bd7
 /*
bf7bd7
@@ -320,6 +332,11 @@ bool sec_key_match_name(const char *sec_key,
bf7bd7
 bool sec_key_match_uuid(const char *sec_key,
bf7bd7
                         uuid_t uuid);
bf7bd7
 
bf7bd7
+errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
bf7bd7
+                      const char *sec_key,
bf7bd7
+                      const char **_name,
bf7bd7
+                      uuid_t uuid);
bf7bd7
+
bf7bd7
 const char *sec_key_get_name(const char *sec_key);
bf7bd7
 
bf7bd7
 errno_t sec_key_get_uuid(const char *sec_key,
bf7bd7
@@ -333,16 +350,30 @@ const char *sec_key_create(TALLOC_CTX *mem_ctx,
bf7bd7
  * sec_key is a concatenation of the ccache's UUID and name
bf7bd7
  * sec_value is the JSON dump of the ccache contents
bf7bd7
  */
bf7bd7
-errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
bf7bd7
-                         const char *sec_key,
bf7bd7
-                         const char *sec_value,
bf7bd7
-                         struct cli_creds *client,
bf7bd7
-                         struct kcm_ccache **_cc);
bf7bd7
+errno_t sec_kv_to_ccache_json(TALLOC_CTX *mem_ctx,
bf7bd7
+                              const char *sec_key,
bf7bd7
+                              const char *sec_value,
bf7bd7
+                              struct cli_creds *client,
bf7bd7
+                              struct kcm_ccache **_cc);
bf7bd7
 
bf7bd7
 /* Convert a kcm_ccache to a key-value pair to be stored in secrets */
bf7bd7
-errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
bf7bd7
-                                struct kcm_ccache *cc,
bf7bd7
+errno_t kcm_ccache_to_sec_input_json(TALLOC_CTX *mem_ctx,
bf7bd7
+                                     struct kcm_ccache *cc,
bf7bd7
+                                     struct sss_iobuf **_payload);
bf7bd7
+
bf7bd7
+/*
bf7bd7
+ * sec_key is a concatenation of the ccache's UUID and name
bf7bd7
+ * sec_value is the binary representation of ccache.
bf7bd7
+ */
bf7bd7
+errno_t sec_kv_to_ccache_binary(TALLOC_CTX *mem_ctx,
bf7bd7
+                                const char *sec_key,
bf7bd7
+                                struct sss_iobuf *sec_value,
bf7bd7
                                 struct cli_creds *client,
bf7bd7
-                                struct sss_iobuf **_payload);
bf7bd7
+                                struct kcm_ccache **_cc);
bf7bd7
+
bf7bd7
+/* Convert a kcm_ccache to its binary representation. */
bf7bd7
+errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx,
bf7bd7
+                                       struct kcm_ccache *cc,
bf7bd7
+                                       struct sss_iobuf **_payload);
bf7bd7
 
bf7bd7
 #endif /* _KCMSRV_CCACHE_H_ */
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache_binary.c b/src/responder/kcm/kcmsrv_ccache_binary.c
bf7bd7
new file mode 100644
bf7bd7
index 000000000..7bfdbf13b
bf7bd7
--- /dev/null
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache_binary.c
bf7bd7
@@ -0,0 +1,308 @@
bf7bd7
+/*
bf7bd7
+    Authors:
bf7bd7
+        Pavel Březina <pbrezina@redhat.com>
bf7bd7
+
bf7bd7
+    Copyright (C) 2020 Red Hat
bf7bd7
+
bf7bd7
+    This program is free software; you can redistribute it and/or modify
bf7bd7
+    it under the terms of the GNU General Public License as published by
bf7bd7
+    the Free Software Foundation; either version 3 of the License, or
bf7bd7
+    (at your option) any later version.
bf7bd7
+
bf7bd7
+    This program is distributed in the hope that it will be useful,
bf7bd7
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
bf7bd7
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
bf7bd7
+    GNU General Public License for more details.
bf7bd7
+
bf7bd7
+    You should have received a copy of the GNU General Public License
bf7bd7
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
bf7bd7
+*/
bf7bd7
+
bf7bd7
+#include "config.h"
bf7bd7
+
bf7bd7
+#include <stdio.h>
bf7bd7
+#include <talloc.h>
bf7bd7
+
bf7bd7
+#include "util/util.h"
bf7bd7
+#include "util/util_creds.h"
bf7bd7
+#include "util/crypto/sss_crypto.h"
bf7bd7
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
bf7bd7
+
bf7bd7
+static errno_t krb_data_to_bin(krb5_data *data, struct sss_iobuf *buf)
bf7bd7
+{
bf7bd7
+    return sss_iobuf_write_varlen(buf, (uint8_t *)data->data, data->length);
bf7bd7
+}
bf7bd7
+
bf7bd7
+static errno_t princ_to_bin(krb5_principal princ, struct sss_iobuf *buf)
bf7bd7
+{
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    if (princ == NULL) {
bf7bd7
+        return sss_iobuf_write_uint8(buf, 0);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    /* Mark that principal is not empty. */
bf7bd7
+    ret = sss_iobuf_write_uint8(buf, 1);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = krb_data_to_bin(&princ->realm, buf);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_write_int32(buf, princ->type);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_write_int32(buf, princ->length);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    for (krb5_int32 i = 0; i < princ->length; i++) {
bf7bd7
+        ret = krb_data_to_bin(&princ->data[i], buf);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+static errno_t creds_to_bin(struct kcm_cred *creds, struct sss_iobuf *buf)
bf7bd7
+{
bf7bd7
+    struct kcm_cred *crd;
bf7bd7
+    uint32_t count = 0;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    DLIST_FOR_EACH(crd, creds) {
bf7bd7
+        count++;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_write_uint32(buf, count);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    DLIST_FOR_EACH(crd, creds) {
bf7bd7
+        ret = sss_iobuf_write_len(buf, (uint8_t *)crd->uuid, sizeof(uuid_t));
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        ret = sss_iobuf_write_iobuf(buf, crd->cred_blob);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx,
bf7bd7
+                                       struct kcm_ccache *cc,
bf7bd7
+                                       struct sss_iobuf **_payload)
bf7bd7
+{
bf7bd7
+    struct sss_iobuf *buf;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    buf = sss_iobuf_init_empty(mem_ctx, sizeof(krb5_principal_data), 0);
bf7bd7
+    if (buf == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_write_int32(buf, cc->kdc_offset);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = princ_to_bin(cc->client, buf);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = creds_to_bin(cc->creds, buf);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    *_payload = buf;
bf7bd7
+
bf7bd7
+    ret = EOK;
bf7bd7
+
bf7bd7
+done:
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        talloc_free(buf);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return ret;
bf7bd7
+}
bf7bd7
+
bf7bd7
+static errno_t bin_to_krb_data(TALLOC_CTX *mem_ctx,
bf7bd7
+                               struct sss_iobuf *buf,
bf7bd7
+                               krb5_data *out)
bf7bd7
+{
bf7bd7
+    uint8_t *data;
bf7bd7
+    size_t len;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_varlen(mem_ctx, buf, &data, &len;;
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    out->magic = 0;
bf7bd7
+    out->data = (char*)data;
bf7bd7
+    out->length = len;
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+static errno_t bin_to_princ(TALLOC_CTX *mem_ctx,
bf7bd7
+                            struct sss_iobuf *buf,
bf7bd7
+                            krb5_principal *_princ)
bf7bd7
+{
bf7bd7
+    krb5_principal princ;
bf7bd7
+    uint8_t non_empty;
bf7bd7
+    krb5_int32 i;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_uint8(buf, &non_empty);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    if (non_empty == 0) {
bf7bd7
+        *_princ = NULL;
bf7bd7
+        return EOK;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    princ = talloc_zero(mem_ctx, struct krb5_principal_data);
bf7bd7
+    if (princ == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+    princ->magic = KV5M_PRINCIPAL;
bf7bd7
+
bf7bd7
+    ret = bin_to_krb_data(princ, buf, &princ->realm);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_int32(buf, &princ->type);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_int32(buf, &princ->length);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    princ->data = talloc_zero_array(princ, krb5_data, princ->length);
bf7bd7
+    if (princ->length > 0 && princ->data == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    for (i = 0; i < princ->length; i++) {
bf7bd7
+        ret = bin_to_krb_data(princ, buf, &princ->data[i]);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    *_princ = princ;
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+static errno_t bin_to_creds(TALLOC_CTX *mem_ctx,
bf7bd7
+                            struct sss_iobuf *buf,
bf7bd7
+                            struct kcm_cred **_creds)
bf7bd7
+{
bf7bd7
+    struct kcm_cred *creds = NULL;
bf7bd7
+    struct kcm_cred *crd;
bf7bd7
+    struct sss_iobuf *cred_blob;
bf7bd7
+    uint32_t count;
bf7bd7
+    uuid_t uuid;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_uint32(buf, &count);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    for (uint32_t i = 0; i < count; i++) {
bf7bd7
+        ret = sss_iobuf_read_len(buf, sizeof(uuid_t), (uint8_t*)uuid);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        ret = sss_iobuf_read_iobuf(NULL, buf, &cred_blob);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        crd = kcm_cred_new(mem_ctx, uuid, cred_blob);
bf7bd7
+        if (crd == NULL) {
bf7bd7
+            talloc_free(cred_blob);
bf7bd7
+            return ENOMEM;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        DLIST_ADD(creds, crd);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    *_creds = creds;
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sec_kv_to_ccache_binary(TALLOC_CTX *mem_ctx,
bf7bd7
+                                const char *sec_key,
bf7bd7
+                                struct sss_iobuf *sec_value,
bf7bd7
+                                struct cli_creds *client,
bf7bd7
+                                struct kcm_ccache **_cc)
bf7bd7
+{
bf7bd7
+    struct kcm_ccache *cc;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    cc = talloc_zero(mem_ctx, struct kcm_ccache);
bf7bd7
+    if (cc == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = kcm_cc_set_header(cc, sec_key, client);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot store ccache header [%d]: %s\n",
bf7bd7
+              ret, sss_strerror(ret));
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_int32(sec_value, &cc->kdc_offset);
bf7bd7
+    if  (ret != EOK) {
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = bin_to_princ(cc, sec_value, &cc->client);
bf7bd7
+    if  (ret != EOK) {
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = bin_to_creds(cc, sec_value, &cc->creds);
bf7bd7
+    if  (ret != EOK) {
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    *_cc = cc;
bf7bd7
+
bf7bd7
+    ret = EOK;
bf7bd7
+
bf7bd7
+done:
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        talloc_free(cc);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return ret;
bf7bd7
+}
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c
bf7bd7
index f78e9f58c..e790cbea3 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ccache_json.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache_json.c
bf7bd7
@@ -37,12 +37,6 @@
bf7bd7
  */
bf7bd7
 #define KS_JSON_VERSION     1
bf7bd7
 
bf7bd7
-/*
bf7bd7
- * The secrets store is a key-value store at heart. We store the UUID
bf7bd7
- * and the name in the key to allow easy lookups be either key
bf7bd7
- */
bf7bd7
-#define SEC_KEY_SEPARATOR   '-'
bf7bd7
-
bf7bd7
 /* Compat definition of json_array_foreach for older systems */
bf7bd7
 #ifndef json_array_foreach
bf7bd7
 #define json_array_foreach(array, idx, value) \
bf7bd7
@@ -51,119 +45,6 @@
bf7bd7
             idx++)
bf7bd7
 #endif
bf7bd7
 
bf7bd7
-const char *sec_key_create(TALLOC_CTX *mem_ctx,
bf7bd7
-                           const char *name,
bf7bd7
-                           uuid_t uuid)
bf7bd7
-{
bf7bd7
-    char uuid_str[UUID_STR_SIZE];
bf7bd7
-
bf7bd7
-    uuid_unparse(uuid, uuid_str);
bf7bd7
-    return talloc_asprintf(mem_ctx,
bf7bd7
-                           "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
bf7bd7
-}
bf7bd7
-
bf7bd7
-static bool sec_key_valid(const char *sec_key)
bf7bd7
-{
bf7bd7
-    if (sec_key == NULL) {
bf7bd7
-        return false;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    if (strlen(sec_key) < UUID_STR_SIZE + 1) {
bf7bd7
-        /* One char for separator (at UUID_STR_SIZE, because strlen doesn't
bf7bd7
-         * include the '\0', but UUID_STR_SIZE does) and at least one for
bf7bd7
-         * the name */
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
bf7bd7
-        return false;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
bf7bd7
-        return false;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    return true;
bf7bd7
-}
bf7bd7
-
bf7bd7
-static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
bf7bd7
-                             const char *sec_key,
bf7bd7
-                             const char **_name,
bf7bd7
-                             uuid_t uuid)
bf7bd7
-{
bf7bd7
-    char uuid_str[UUID_STR_SIZE];
bf7bd7
-
bf7bd7
-    if (!sec_key_valid(sec_key)) {
bf7bd7
-        return EINVAL;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    strncpy(uuid_str, sec_key, sizeof(uuid_str)-1);
bf7bd7
-    if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
bf7bd7
-        return EINVAL;
bf7bd7
-    }
bf7bd7
-    uuid_str[UUID_STR_SIZE-1] = '\0';
bf7bd7
-
bf7bd7
-    *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE);
bf7bd7
-    if (*_name == NULL) {
bf7bd7
-        return ENOMEM;
bf7bd7
-    }
bf7bd7
-    uuid_parse(uuid_str, uuid);
bf7bd7
-
bf7bd7
-    return EOK;
bf7bd7
-}
bf7bd7
-
bf7bd7
-errno_t sec_key_get_uuid(const char *sec_key,
bf7bd7
-                         uuid_t uuid)
bf7bd7
-{
bf7bd7
-    char uuid_str[UUID_STR_SIZE];
bf7bd7
-
bf7bd7
-    if (!sec_key_valid(sec_key)) {
bf7bd7
-        return EINVAL;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    strncpy(uuid_str, sec_key, UUID_STR_SIZE-1);
bf7bd7
-    uuid_str[UUID_STR_SIZE-1] = '\0';
bf7bd7
-    uuid_parse(uuid_str, uuid);
bf7bd7
-    return EOK;
bf7bd7
-}
bf7bd7
-
bf7bd7
-const char *sec_key_get_name(const char *sec_key)
bf7bd7
-{
bf7bd7
-    if (!sec_key_valid(sec_key)) {
bf7bd7
-        return NULL;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    return sec_key + UUID_STR_SIZE;
bf7bd7
-}
bf7bd7
-
bf7bd7
-bool sec_key_match_name(const char *sec_key,
bf7bd7
-                        const char *name)
bf7bd7
-{
bf7bd7
-    if (!sec_key_valid(sec_key) || name == NULL) {
bf7bd7
-        return false;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    return strcmp(sec_key + UUID_STR_SIZE, name) == 0;
bf7bd7
-}
bf7bd7
-
bf7bd7
-bool sec_key_match_uuid(const char *sec_key,
bf7bd7
-                        uuid_t uuid)
bf7bd7
-{
bf7bd7
-    errno_t ret;
bf7bd7
-    uuid_t key_uuid;
bf7bd7
-
bf7bd7
-    /* `key_uuid` is output arg and isn't read in sec_key_get_uuid() but
bf7bd7
-     * since libuuid is opaque for cppcheck it generates false positive here
bf7bd7
-     */
bf7bd7
-    /* cppcheck-suppress uninitvar */
bf7bd7
-    ret = sec_key_get_uuid(sec_key, key_uuid);
bf7bd7
-    if (ret != EOK) {
bf7bd7
-        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n");
bf7bd7
-        return false;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    return uuid_compare(key_uuid, uuid) == 0;
bf7bd7
-}
bf7bd7
-
bf7bd7
 /*
bf7bd7
  * Creates an array of principal elements that will be used later
bf7bd7
  * in the form of:
bf7bd7
@@ -460,10 +341,9 @@ static errno_t ccache_to_sec_val(TALLOC_CTX *mem_ctx,
bf7bd7
     return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
-errno_t kcm_ccache_to_sec_input(TALLOC_CTX *mem_ctx,
bf7bd7
-                                struct kcm_ccache *cc,
bf7bd7
-                                struct cli_creds *client,
bf7bd7
-                                struct sss_iobuf **_payload)
bf7bd7
+errno_t kcm_ccache_to_sec_input_json(TALLOC_CTX *mem_ctx,
bf7bd7
+                                     struct kcm_ccache *cc,
bf7bd7
+                                     struct sss_iobuf **_payload)
bf7bd7
 {
bf7bd7
     errno_t ret;
bf7bd7
     const char *value;
bf7bd7
@@ -897,11 +777,11 @@ static errno_t sec_json_value_to_ccache(struct kcm_ccache *cc,
bf7bd7
  * sec_key is a concatenation of the ccache's UUID and name
bf7bd7
  * sec_value is the JSON dump of the ccache contents
bf7bd7
  */
bf7bd7
-errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
bf7bd7
-                         const char *sec_key,
bf7bd7
-                         const char *sec_value,
bf7bd7
-                         struct cli_creds *client,
bf7bd7
-                         struct kcm_ccache **_cc)
bf7bd7
+errno_t sec_kv_to_ccache_json(TALLOC_CTX *mem_ctx,
bf7bd7
+                              const char *sec_key,
bf7bd7
+                              const char *sec_value,
bf7bd7
+                              struct cli_creds *client,
bf7bd7
+                              struct kcm_ccache **_cc)
bf7bd7
 {
bf7bd7
     errno_t ret;
bf7bd7
     json_t *root = NULL;
bf7bd7
@@ -911,7 +791,7 @@ errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
bf7bd7
     ret = sec_value_to_json(sec_value, &root);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_CRIT_FAILURE,
bf7bd7
-              "Cannot store secret to JSN [%d]: %s\n",
bf7bd7
+              "Cannot store secret to JSON [%d]: %s\n",
bf7bd7
               ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
@@ -928,16 +808,9 @@ errno_t sec_kv_to_ccache(TALLOC_CTX *mem_ctx,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    /* We rely on sssd-secrets only searching the user's subtree so we
bf7bd7
-     * set the ownership to the client
bf7bd7
-     */
bf7bd7
-    cc->owner.uid = cli_creds_get_uid(client);
bf7bd7
-    cc->owner.gid = cli_creds_get_gid(client);
bf7bd7
-
bf7bd7
-    ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid);
bf7bd7
+    ret = kcm_cc_set_header(cc, sec_key, client);
bf7bd7
     if (ret != EOK) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE,
bf7bd7
-              "Cannt parse secret key [%d]: %s\n",
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot store ccache header [%d]: %s\n",
bf7bd7
               ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache_key.c b/src/responder/kcm/kcmsrv_ccache_key.c
bf7bd7
new file mode 100644
bf7bd7
index 000000000..59d60453c
bf7bd7
--- /dev/null
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache_key.c
bf7bd7
@@ -0,0 +1,144 @@
bf7bd7
+/*
bf7bd7
+   SSSD
bf7bd7
+
bf7bd7
+   Copyright (C) Red Hat, 2020
bf7bd7
+
bf7bd7
+   This program is free software; you can redistribute it and/or modify
bf7bd7
+   it under the terms of the GNU General Public License as published by
bf7bd7
+   the Free Software Foundation; either version 3 of the License, or
bf7bd7
+   (at your option) any later version.
bf7bd7
+
bf7bd7
+   This program is distributed in the hope that it will be useful,
bf7bd7
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
bf7bd7
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
bf7bd7
+   GNU General Public License for more details.
bf7bd7
+
bf7bd7
+   You should have received a copy of the GNU General Public License
bf7bd7
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
bf7bd7
+*/
bf7bd7
+
bf7bd7
+#include "config.h"
bf7bd7
+
bf7bd7
+#include <stdio.h>
bf7bd7
+#include <talloc.h>
bf7bd7
+
bf7bd7
+#include "util/util.h"
bf7bd7
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
bf7bd7
+
bf7bd7
+/*
bf7bd7
+ * The secrets store is a key-value store at heart. We store the UUID
bf7bd7
+ * and the name in the key to allow easy lookups by either part.
bf7bd7
+ */
bf7bd7
+#define SEC_KEY_SEPARATOR   '-'
bf7bd7
+
bf7bd7
+const char *sec_key_create(TALLOC_CTX *mem_ctx,
bf7bd7
+                           const char *name,
bf7bd7
+                           uuid_t uuid)
bf7bd7
+{
bf7bd7
+    char uuid_str[UUID_STR_SIZE];
bf7bd7
+
bf7bd7
+    uuid_unparse(uuid, uuid_str);
bf7bd7
+    return talloc_asprintf(mem_ctx,
bf7bd7
+                           "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
bf7bd7
+}
bf7bd7
+
bf7bd7
+static bool sec_key_valid(const char *sec_key)
bf7bd7
+{
bf7bd7
+    if (sec_key == NULL) {
bf7bd7
+        return false;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    if (strlen(sec_key) < UUID_STR_SIZE + 1) {
bf7bd7
+        /* One char for separator (at UUID_STR_SIZE, because strlen doesn't
bf7bd7
+         * include the '\0', but UUID_STR_SIZE does) and at least one for
bf7bd7
+         * the name */
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
bf7bd7
+        return false;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
bf7bd7
+        return false;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return true;
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
bf7bd7
+                      const char *sec_key,
bf7bd7
+                      const char **_name,
bf7bd7
+                      uuid_t uuid)
bf7bd7
+{
bf7bd7
+    char uuid_str[UUID_STR_SIZE];
bf7bd7
+
bf7bd7
+    if (!sec_key_valid(sec_key)) {
bf7bd7
+        return EINVAL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    strncpy(uuid_str, sec_key, sizeof(uuid_str) - 1);
bf7bd7
+    if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
bf7bd7
+        return EINVAL;
bf7bd7
+    }
bf7bd7
+    uuid_str[UUID_STR_SIZE - 1] = '\0';
bf7bd7
+
bf7bd7
+    *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE);
bf7bd7
+    if (*_name == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+    uuid_parse(uuid_str, uuid);
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sec_key_get_uuid(const char *sec_key,
bf7bd7
+                         uuid_t uuid)
bf7bd7
+{
bf7bd7
+    char uuid_str[UUID_STR_SIZE];
bf7bd7
+
bf7bd7
+    if (!sec_key_valid(sec_key)) {
bf7bd7
+        return EINVAL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    strncpy(uuid_str, sec_key, UUID_STR_SIZE - 1);
bf7bd7
+    uuid_str[UUID_STR_SIZE - 1] = '\0';
bf7bd7
+    uuid_parse(uuid_str, uuid);
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+const char *sec_key_get_name(const char *sec_key)
bf7bd7
+{
bf7bd7
+    if (!sec_key_valid(sec_key)) {
bf7bd7
+        return NULL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return sec_key + UUID_STR_SIZE;
bf7bd7
+}
bf7bd7
+
bf7bd7
+bool sec_key_match_name(const char *sec_key,
bf7bd7
+                        const char *name)
bf7bd7
+{
bf7bd7
+    if (!sec_key_valid(sec_key) || name == NULL) {
bf7bd7
+        return false;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return strcmp(sec_key + UUID_STR_SIZE, name) == 0;
bf7bd7
+}
bf7bd7
+
bf7bd7
+bool sec_key_match_uuid(const char *sec_key,
bf7bd7
+                        uuid_t uuid)
bf7bd7
+{
bf7bd7
+    errno_t ret;
bf7bd7
+    uuid_t key_uuid;
bf7bd7
+
bf7bd7
+    /* Clear uuid value to avoid cppcheck warning. */
bf7bd7
+    uuid_clear(key_uuid);
bf7bd7
+
bf7bd7
+    ret = sec_key_get_uuid(sec_key, key_uuid);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n");
bf7bd7
+        return false;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return uuid_compare(key_uuid, uuid) == 0;
bf7bd7
+}
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache_mem.c b/src/responder/kcm/kcmsrv_ccache_mem.c
bf7bd7
index baa698054..0e3a7b239 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ccache_mem.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache_mem.c
bf7bd7
@@ -49,24 +49,6 @@ struct ccdb_mem {
bf7bd7
     unsigned int nextid;
bf7bd7
 };
bf7bd7
 
bf7bd7
-/* In order to provide a consistent interface, we need to let the caller
bf7bd7
- * of getbyXXX own the ccache, therefore the memory back end returns a shallow
bf7bd7
- * copy of the ccache
bf7bd7
- */
bf7bd7
-static struct kcm_ccache *kcm_ccache_dup(TALLOC_CTX *mem_ctx,
bf7bd7
-                                         struct kcm_ccache *in)
bf7bd7
-{
bf7bd7
-    struct kcm_ccache *out;
bf7bd7
-
bf7bd7
-    out = talloc_zero(mem_ctx, struct kcm_ccache);
bf7bd7
-    if (out == NULL) {
bf7bd7
-        return NULL;
bf7bd7
-    }
bf7bd7
-    memcpy(out, in, sizeof(struct kcm_ccache));
bf7bd7
-
bf7bd7
-    return out;
bf7bd7
-}
bf7bd7
-
bf7bd7
 static struct ccache_mem_wrap *memdb_get_by_uuid(struct ccdb_mem *memdb,
bf7bd7
                                                  struct cli_creds *client,
bf7bd7
                                                  uuid_t uuid)
bf7bd7
@@ -417,7 +399,11 @@ static struct tevent_req *ccdb_mem_getbyuuid_send(TALLOC_CTX *mem_ctx,
bf7bd7
 
bf7bd7
     ccwrap = memdb_get_by_uuid(memdb, client, uuid);
bf7bd7
     if (ccwrap != NULL) {
bf7bd7
-        state->cc = kcm_ccache_dup(state, ccwrap->cc);
bf7bd7
+        /* In order to provide a consistent interface, we need to let the caller
bf7bd7
+         * of getbyXXX own the ccache, therefore the memory back end returns a shallow
bf7bd7
+         * copy of the ccache
bf7bd7
+         */
bf7bd7
+        state->cc = kcm_cc_dup(state, ccwrap->cc);
bf7bd7
         if (state->cc == NULL) {
bf7bd7
             ret = ENOMEM;
bf7bd7
             goto immediate;
bf7bd7
@@ -470,7 +456,11 @@ static struct tevent_req *ccdb_mem_getbyname_send(TALLOC_CTX *mem_ctx,
bf7bd7
 
bf7bd7
     ccwrap = memdb_get_by_name(memdb, client, name);
bf7bd7
     if (ccwrap != NULL) {
bf7bd7
-        state->cc = kcm_ccache_dup(state, ccwrap->cc);
bf7bd7
+        /* In order to provide a consistent interface, we need to let the caller
bf7bd7
+         * of getbyXXX own the ccache, therefore the memory back end returns a shallow
bf7bd7
+         * copy of the ccache
bf7bd7
+         */
bf7bd7
+        state->cc = kcm_cc_dup(state, ccwrap->cc);
bf7bd7
         if (state->cc == NULL) {
bf7bd7
             ret = ENOMEM;
bf7bd7
             goto immediate;
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c
bf7bd7
index ed1c8247f..726711ac4 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ccache_secdb.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache_secdb.c
bf7bd7
@@ -35,15 +35,16 @@
bf7bd7
 #define KCM_SECDB_CCACHE_FMT  KCM_SECDB_BASE_FMT"ccache/"
bf7bd7
 #define KCM_SECDB_DFL_FMT     KCM_SECDB_BASE_FMT"default"
bf7bd7
 
bf7bd7
-static errno_t sec_get_b64(TALLOC_CTX *mem_ctx,
bf7bd7
-                           struct sss_sec_req *req,
bf7bd7
-                           struct sss_iobuf **_buf)
bf7bd7
+static errno_t sec_get(TALLOC_CTX *mem_ctx,
bf7bd7
+                       struct sss_sec_req *req,
bf7bd7
+                       struct sss_iobuf **_buf,
bf7bd7
+                       char **_datatype)
bf7bd7
 {
bf7bd7
     errno_t ret;
bf7bd7
     TALLOC_CTX *tmp_ctx;
bf7bd7
-    char *b64_sec;
bf7bd7
+    char *datatype;
bf7bd7
     uint8_t *data;
bf7bd7
-    size_t data_size;
bf7bd7
+    size_t len;
bf7bd7
     struct sss_iobuf *buf;
bf7bd7
 
bf7bd7
     tmp_ctx = talloc_new(mem_ctx);
bf7bd7
@@ -51,101 +52,61 @@ static errno_t sec_get_b64(TALLOC_CTX *mem_ctx,
bf7bd7
         return ENOMEM;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sss_sec_get(tmp_ctx, req, &b64_sec);
bf7bd7
+    ret = sss_sec_get(tmp_ctx, req, &data, &len, &datatype);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "Cannot retrieve the secret [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    data = sss_base64_decode(tmp_ctx, b64_sec, &data_size);
bf7bd7
-    if (data == NULL) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot decode secret from base64\n");
bf7bd7
-        ret = EIO;
bf7bd7
-        goto done;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    buf = sss_iobuf_init_readonly(tmp_ctx, data, data_size);
bf7bd7
+    buf = sss_iobuf_init_steal(tmp_ctx, data, len);
bf7bd7
     if (buf == NULL) {
bf7bd7
         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init the iobuf\n");
bf7bd7
         ret = EIO;
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = EOK;
bf7bd7
     *_buf = talloc_steal(mem_ctx, buf);
bf7bd7
+    if (_datatype != NULL) {
bf7bd7
+        *_datatype = talloc_steal(mem_ctx, datatype);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = EOK;
bf7bd7
+
bf7bd7
 done:
bf7bd7
     talloc_free(tmp_ctx);
bf7bd7
     return ret;
bf7bd7
 }
bf7bd7
 
bf7bd7
-static errno_t sec_put_b64(TALLOC_CTX *mem_ctx,
bf7bd7
-                           struct sss_sec_req *req,
bf7bd7
-                           struct sss_iobuf *buf)
bf7bd7
+static errno_t sec_put(TALLOC_CTX *mem_ctx,
bf7bd7
+                       struct sss_sec_req *req,
bf7bd7
+                       struct sss_iobuf *buf)
bf7bd7
 {
bf7bd7
     errno_t ret;
bf7bd7
-    TALLOC_CTX *tmp_ctx;
bf7bd7
-    char *secret;
bf7bd7
 
bf7bd7
-    tmp_ctx = talloc_new(mem_ctx);
bf7bd7
-    if (tmp_ctx == NULL) {
bf7bd7
-        return ENOMEM;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    secret = sss_base64_encode(tmp_ctx,
bf7bd7
-                               sss_iobuf_get_data(buf),
bf7bd7
-                               sss_iobuf_get_size(buf));
bf7bd7
-    if (secret == NULL) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot encode secret to base64\n");
bf7bd7
-        ret = EIO;
bf7bd7
-        goto done;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    ret = sss_sec_put(req, secret);
bf7bd7
+    ret = sss_sec_put(req, sss_iobuf_get_data(buf), sss_iobuf_get_size(buf),
bf7bd7
+                      SSS_SEC_PLAINTEXT, "binary");
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "Cannot write the secret [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
-        goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = EOK;
bf7bd7
-done:
bf7bd7
-    talloc_free(tmp_ctx);
bf7bd7
     return ret;
bf7bd7
 }
bf7bd7
 
bf7bd7
-static errno_t sec_update_b64(TALLOC_CTX *mem_ctx,
bf7bd7
-                              struct sss_sec_req *req,
bf7bd7
-                              struct sss_iobuf *buf)
bf7bd7
+static errno_t sec_update(TALLOC_CTX *mem_ctx,
bf7bd7
+                          struct sss_sec_req *req,
bf7bd7
+                          struct sss_iobuf *buf)
bf7bd7
 {
bf7bd7
     errno_t ret;
bf7bd7
-    TALLOC_CTX *tmp_ctx;
bf7bd7
-    char *secret;
bf7bd7
-
bf7bd7
-    tmp_ctx = talloc_new(mem_ctx);
bf7bd7
-    if (tmp_ctx == NULL) {
bf7bd7
-        return ENOMEM;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    secret = sss_base64_encode(tmp_ctx,
bf7bd7
-                               sss_iobuf_get_data(buf),
bf7bd7
-                               sss_iobuf_get_size(buf));
bf7bd7
-    if (secret == NULL) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot encode secret to base64\n");
bf7bd7
-        ret = EIO;
bf7bd7
-        goto done;
bf7bd7
-    }
bf7bd7
 
bf7bd7
-    ret = sss_sec_update(req, secret);
bf7bd7
+    ret = sss_sec_update(req, sss_iobuf_get_data(buf), sss_iobuf_get_size(buf),
bf7bd7
+                         SSS_SEC_PLAINTEXT, "binary");
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "Cannot write the secret [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
-        goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = EOK;
bf7bd7
-done:
bf7bd7
-    talloc_free(tmp_ctx);
bf7bd7
     return ret;
bf7bd7
 }
bf7bd7
 
bf7bd7
@@ -206,7 +167,7 @@ static errno_t kcm_ccache_to_secdb_kv(TALLOC_CTX *mem_ctx,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = kcm_ccache_to_sec_input(mem_ctx, cc, client, &payload);
bf7bd7
+    ret = kcm_ccache_to_sec_input_binary(mem_ctx, cc, &payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_CRIT_FAILURE,
bf7bd7
               "Cannot convert ccache to a secret [%d][%s]\n", ret, sss_strerror(ret));
bf7bd7
@@ -480,6 +441,7 @@ static errno_t secdb_get_cc(TALLOC_CTX *mem_ctx,
bf7bd7
     struct kcm_ccache *cc = NULL;
bf7bd7
     struct sss_sec_req *sreq = NULL;
bf7bd7
     struct sss_iobuf *ccbuf;
bf7bd7
+    char *datatype;
bf7bd7
 
bf7bd7
     tmp_ctx = talloc_new(mem_ctx);
bf7bd7
     if (tmp_ctx == NULL) {
bf7bd7
@@ -493,22 +455,23 @@ static errno_t secdb_get_cc(TALLOC_CTX *mem_ctx,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_get_b64(tmp_ctx, sreq, &ccbuf);
bf7bd7
+    ret = sec_get(tmp_ctx, sreq, &ccbuf, &datatype);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "Cannot get the secret [%d][%s]\n", ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_kv_to_ccache(tmp_ctx,
bf7bd7
-                           secdb_key,
bf7bd7
-                           (const char *) sss_iobuf_get_data(ccbuf),
bf7bd7
-                           client,
bf7bd7
-                           &cc);
bf7bd7
+    if (strcmp(datatype, "binary") == 0) {
bf7bd7
+        ret = sec_kv_to_ccache_binary(tmp_ctx, secdb_key, ccbuf, client, &cc);
bf7bd7
+    } else {
bf7bd7
+        ret = sec_kv_to_ccache_json(tmp_ctx, secdb_key,
bf7bd7
+                                    (const char *)sss_iobuf_get_data(ccbuf),
bf7bd7
+                                    client, &cc);
bf7bd7
+    }
bf7bd7
     if (ret != EOK) {
bf7bd7
-        DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "Cannot convert JSON keyval to ccache blob [%d]: %s\n",
bf7bd7
-              ret, sss_strerror(ret));
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot convert %s data to ccache "
bf7bd7
+              "[%d]: %s\n", datatype, ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
@@ -746,11 +709,11 @@ static struct tevent_req *ccdb_secdb_set_default_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sss_sec_get(state, sreq, &cur_default);
bf7bd7
+    ret = sss_sec_get(state, sreq, (uint8_t**)&cur_default, NULL, NULL);
bf7bd7
     if (ret == ENOENT) {
bf7bd7
-        ret = sec_put_b64(state, sreq, iobuf);
bf7bd7
+        ret = sec_put(state, sreq, iobuf);
bf7bd7
     } else if (ret == EOK) {
bf7bd7
-        ret = sec_update_b64(state, sreq, iobuf);
bf7bd7
+        ret = sec_update(state, sreq, iobuf);
bf7bd7
     }
bf7bd7
 
bf7bd7
     if (ret != EOK) {
bf7bd7
@@ -804,7 +767,7 @@ static struct tevent_req *ccdb_secdb_get_default_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_get_b64(state, sreq, &dfl_iobuf);
bf7bd7
+    ret = sec_get(state, sreq, &dfl_iobuf, NULL);
bf7bd7
     if (ret == ENOENT) {
bf7bd7
         uuid_clear(state->uuid);
bf7bd7
         ret = EOK;
bf7bd7
@@ -1230,9 +1193,8 @@ static struct tevent_req *ccdb_secdb_create_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_put_b64(state, ccache_req, ccache_payload);
bf7bd7
+    ret = sec_put(state, ccache_req, ccache_payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
-        DEBUG(SSSDBG_OP_FAILURE, "Failed to add the payload\n");
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
@@ -1298,7 +1260,7 @@ static struct tevent_req *ccdb_secdb_mod_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = kcm_ccache_to_sec_input(state, cc, client, &payload);
bf7bd7
+    ret = kcm_ccache_to_sec_input_binary(state, cc, &payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
@@ -1308,7 +1270,7 @@ static struct tevent_req *ccdb_secdb_mod_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_update_b64(state, sreq, payload);
bf7bd7
+    ret = sec_update(state, sreq, payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
@@ -1374,7 +1336,7 @@ static struct tevent_req *ccdb_secdb_store_cred_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = kcm_ccache_to_sec_input(state, cc, client, &payload);
bf7bd7
+    ret = kcm_ccache_to_sec_input_binary(state, cc, &payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
@@ -1384,7 +1346,7 @@ static struct tevent_req *ccdb_secdb_store_cred_send(TALLOC_CTX *mem_ctx,
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_update_b64(state, sreq, payload);
bf7bd7
+    ret = sec_update(state, sreq, payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ccache_secrets.c b/src/responder/kcm/kcmsrv_ccache_secrets.c
bf7bd7
index 440ab3bb9..f3d69842c 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ccache_secrets.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ccache_secrets.c
bf7bd7
@@ -195,7 +195,7 @@ static errno_t kcm_ccache_to_sec_kv(TALLOC_CTX *mem_ctx,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = kcm_ccache_to_sec_input(mem_ctx, cc, client, &payload);
bf7bd7
+    ret = kcm_ccache_to_sec_input_json(mem_ctx, cc, &payload);
bf7bd7
     if (ret != EOK) {
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
@@ -489,11 +489,8 @@ static void sec_get_done(struct tevent_req *subreq)
bf7bd7
         return;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sec_kv_to_ccache(state,
bf7bd7
-                           state->sec_key,
bf7bd7
-                           sec_value,
bf7bd7
-                           state->client,
bf7bd7
-                           &state->cc);
bf7bd7
+    ret = sec_kv_to_ccache_json(state, state->sec_key, sec_value, state->client,
bf7bd7
+                                &state->cc);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "Cannot convert JSON keyval to ccache blob [%d]: %s\n",
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
bf7bd7
index 421bf4bc5..a1aa9aa20 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_cmd.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_cmd.c
bf7bd7
@@ -314,7 +314,7 @@ static void kcm_reply_error(struct cli_ctx *cctx,
bf7bd7
     krb5_error_code kerr;
bf7bd7
 
bf7bd7
     DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-          "KCM operation returs failure [%d]: %s\n",
bf7bd7
+          "KCM operation returns failure [%d]: %s\n",
bf7bd7
           retcode, sss_strerror(retcode));
bf7bd7
     kerr = sss2krb5_error(retcode);
bf7bd7
 
bf7bd7
@@ -373,13 +373,16 @@ static errno_t kcm_cmd_dispatch(struct kcm_ctx *kctx,
bf7bd7
 {
bf7bd7
     struct tevent_req *req;
bf7bd7
     struct cli_ctx *cctx;
bf7bd7
+    struct kcm_conn_data *conn_data;
bf7bd7
 
bf7bd7
     cctx = req_ctx->cctx;
bf7bd7
+    conn_data = talloc_get_type(cctx->state_ctx, struct kcm_conn_data);
bf7bd7
 
bf7bd7
     req = kcm_cmd_send(req_ctx,
bf7bd7
                        cctx->ev,
bf7bd7
                        kctx->qctx,
bf7bd7
                        req_ctx->kctx->kcm_data,
bf7bd7
+                       conn_data,
bf7bd7
                        req_ctx->cctx->creds,
bf7bd7
                        &req_ctx->op_io.request,
bf7bd7
                        req_ctx->op_io.op);
bf7bd7
@@ -492,7 +495,7 @@ static void kcm_recv(struct cli_ctx *cctx)
bf7bd7
     int ret;
bf7bd7
 
bf7bd7
     kctx = talloc_get_type(cctx->rctx->pvt_ctx, struct kcm_ctx);
bf7bd7
-    req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
bf7bd7
+    req = talloc_get_type(cctx->protocol_ctx, struct kcm_req_ctx);
bf7bd7
     if (req == NULL) {
bf7bd7
         /* A new request comes in, setup data structures. */
bf7bd7
         req = kcm_new_req(cctx, kctx);
bf7bd7
@@ -503,7 +506,17 @@ static void kcm_recv(struct cli_ctx *cctx)
bf7bd7
             return;
bf7bd7
         }
bf7bd7
 
bf7bd7
-        cctx->state_ctx = req;
bf7bd7
+        cctx->protocol_ctx = req;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    /* Shared data between requests that originates in the same connection. */
bf7bd7
+    if (cctx->state_ctx == NULL) {
bf7bd7
+        cctx->state_ctx = talloc_zero(cctx, struct kcm_conn_data);
bf7bd7
+        if (cctx->state_ctx == NULL) {
bf7bd7
+            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up client state\n");
bf7bd7
+            talloc_free(cctx);
bf7bd7
+            return;
bf7bd7
+        }
bf7bd7
     }
bf7bd7
 
bf7bd7
     ret = kcm_recv_data(req, cctx->cfd, &req->reqbuf);
bf7bd7
@@ -558,7 +571,7 @@ static int kcm_send_data(struct cli_ctx *cctx)
bf7bd7
     struct kcm_req_ctx *req;
bf7bd7
     errno_t ret;
bf7bd7
 
bf7bd7
-    req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
bf7bd7
+    req = talloc_get_type(cctx->protocol_ctx, struct kcm_req_ctx);
bf7bd7
 
bf7bd7
     ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len);
bf7bd7
     if (ret != EOK) {
bf7bd7
@@ -604,7 +617,7 @@ static void kcm_send(struct cli_ctx *cctx)
bf7bd7
     DEBUG(SSSDBG_TRACE_INTERNAL, "All data sent!\n");
bf7bd7
     TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
bf7bd7
     TEVENT_FD_READABLE(cctx->cfde);
bf7bd7
-    talloc_zfree(cctx->state_ctx);
bf7bd7
+    talloc_zfree(cctx->protocol_ctx);
bf7bd7
     return;
bf7bd7
 }
bf7bd7
 
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
bf7bd7
index 6ac66c150..f458c724b 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ops.c
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ops.c
bf7bd7
@@ -22,9 +22,11 @@
bf7bd7
 #include "config.h"
bf7bd7
 
bf7bd7
 #include <krb5/krb5.h>
bf7bd7
+#include <dhash.h>
bf7bd7
 
bf7bd7
 #include "util/sss_iobuf.h"
bf7bd7
 #include "util/sss_krb5.h"
bf7bd7
+#include "util/sss_ptr_hash.h"
bf7bd7
 #include "util/util_creds.h"
bf7bd7
 #include "responder/kcm/kcm.h"
bf7bd7
 #include "responder/kcm/kcmsrv_pvt.h"
bf7bd7
@@ -38,6 +40,7 @@
bf7bd7
 
bf7bd7
 struct kcm_op_ctx {
bf7bd7
     struct kcm_resp_ctx *kcm_data;
bf7bd7
+    struct kcm_conn_data *conn_data;
bf7bd7
     struct cli_creds *client;
bf7bd7
 
bf7bd7
     struct sss_iobuf *input;
bf7bd7
@@ -86,6 +89,7 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
bf7bd7
                                 struct tevent_context *ev,
bf7bd7
                                 struct kcm_ops_queue_ctx *qctx,
bf7bd7
                                 struct kcm_resp_ctx *kcm_data,
bf7bd7
+                                struct kcm_conn_data *conn_data,
bf7bd7
                                 struct cli_creds *client,
bf7bd7
                                 struct kcm_data *input,
bf7bd7
                                 struct kcm_op *op)
bf7bd7
@@ -135,6 +139,7 @@ struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
bf7bd7
     }
bf7bd7
 
bf7bd7
     state->op_ctx->kcm_data = kcm_data;
bf7bd7
+    state->op_ctx->conn_data = conn_data;
bf7bd7
     state->op_ctx->client = client;
bf7bd7
 
bf7bd7
     state->op_ctx->input = sss_iobuf_init_readonly(state->op_ctx,
bf7bd7
@@ -1071,8 +1076,75 @@ static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq)
bf7bd7
     tevent_req_done(req);
bf7bd7
 }
bf7bd7
 
bf7bd7
+static void
bf7bd7
+kcm_creds_table_delete_cb(hash_entry_t *item,
bf7bd7
+                          hash_destroy_enum deltype,
bf7bd7
+                          void *pvt)
bf7bd7
+{
bf7bd7
+    /* Delete the old credential if it is being overwritten. */
bf7bd7
+    talloc_free(item->value.ptr);
bf7bd7
+}
bf7bd7
+
bf7bd7
+/* Store credentials in a hash table.
bf7bd7
+ *
bf7bd7
+ * If the table already exist we add the new credentials to the table and
bf7bd7
+ * overwrite the ones that already exist. This allows us to correctly serve
bf7bd7
+ * also parallel GET_CRED_UUID_LIST requests from the same connection since
bf7bd7
+ * it will have its own uuid list and cursor on the client side and we make
bf7bd7
+ * all uuid (old, updated and newly added) available.
bf7bd7
+ */
bf7bd7
+static errno_t
bf7bd7
+kcm_creds_to_table(TALLOC_CTX *mem_ctx,
bf7bd7
+                   struct kcm_cred *creds,
bf7bd7
+                   hash_table_t **_table)
bf7bd7
+{
bf7bd7
+    char str[UUID_STR_SIZE];
bf7bd7
+    uuid_t uuid;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    if (*_table == NULL) {
bf7bd7
+        *_table = sss_ptr_hash_create(mem_ctx, kcm_creds_table_delete_cb, NULL);
bf7bd7
+        if (*_table == NULL) {
bf7bd7
+            return ENOMEM;
bf7bd7
+        }
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    for (struct kcm_cred *crd = creds;
bf7bd7
+         crd != NULL;
bf7bd7
+         crd = kcm_cc_next_cred(crd)) {
bf7bd7
+        ret = kcm_cred_get_uuid(crd, uuid);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            DEBUG(SSSDBG_MINOR_FAILURE, "Credential has no UUID, skipping\n");
bf7bd7
+            continue;
bf7bd7
+        }
bf7bd7
+        uuid_unparse(uuid, str);
bf7bd7
+
bf7bd7
+        ret = sss_ptr_hash_add_or_override(*_table, str, crd, struct kcm_cred);
bf7bd7
+        if (ret != EOK) {
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        talloc_steal(*_table, crd);
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+static struct kcm_cred *
bf7bd7
+kcm_creds_lookup(hash_table_t *table, uuid_t uuid)
bf7bd7
+{
bf7bd7
+    char str[UUID_STR_SIZE];
bf7bd7
+
bf7bd7
+    if (uuid == NULL) {
bf7bd7
+        return NULL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    uuid_unparse(uuid, str);
bf7bd7
+    return sss_ptr_hash_lookup(table, str, struct kcm_cred);
bf7bd7
+}
bf7bd7
+
bf7bd7
 /* (name) -> (uuid, ...) */
bf7bd7
-static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq);
bf7bd7
+static void kcm_op_get_cred_uuid_list_getbyname_done(struct tevent_req *subreq);
bf7bd7
 
bf7bd7
 static struct tevent_req *
bf7bd7
 kcm_op_get_cred_uuid_list_send(TALLOC_CTX *mem_ctx,
bf7bd7
@@ -1106,7 +1178,7 @@ kcm_op_get_cred_uuid_list_send(TALLOC_CTX *mem_ctx,
bf7bd7
         ret = ENOMEM;
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
-    tevent_req_set_callback(subreq, kcm_op_get_cred_uuid_getbyname_done, req);
bf7bd7
+    tevent_req_set_callback(subreq, kcm_op_get_cred_uuid_list_getbyname_done, req);
bf7bd7
     return req;
bf7bd7
 
bf7bd7
 immediate:
bf7bd7
@@ -1115,17 +1187,20 @@ immediate:
bf7bd7
     return req;
bf7bd7
 }
bf7bd7
 
bf7bd7
-static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq)
bf7bd7
+static void kcm_op_get_cred_uuid_list_getbyname_done(struct tevent_req *subreq)
bf7bd7
 {
bf7bd7
     errno_t ret;
bf7bd7
     struct kcm_ccache *cc;
bf7bd7
     struct kcm_cred *crd;
bf7bd7
+    struct kcm_conn_data *conn_data;
bf7bd7
     uuid_t uuid;
bf7bd7
     struct tevent_req *req = tevent_req_callback_data(subreq,
bf7bd7
                                                       struct tevent_req);
bf7bd7
     struct kcm_op_common_state *state = tevent_req_data(req,
bf7bd7
                                                 struct kcm_op_common_state);
bf7bd7
 
bf7bd7
+    conn_data = state->op_ctx->conn_data;
bf7bd7
+
bf7bd7
     ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
bf7bd7
     talloc_zfree(subreq);
bf7bd7
     if (ret != EOK) {
bf7bd7
@@ -1137,12 +1212,20 @@ static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq)
bf7bd7
     }
bf7bd7
 
bf7bd7
     if (cc == NULL) {
bf7bd7
-        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
bf7bd7
+        DEBUG(SSSDBG_MINOR_FAILURE, "No ccache by that name\n");
bf7bd7
         state->op_ret = ERR_NO_CREDS;
bf7bd7
         tevent_req_done(req);
bf7bd7
         return;
bf7bd7
     }
bf7bd7
 
bf7bd7
+    ret = kcm_creds_to_table(conn_data, kcm_cc_get_cred(cc), &conn_data->creds);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE, "Unable to build credentials hash table "
bf7bd7
+              "[%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
+        tevent_req_error(req, ret);
bf7bd7
+        return;
bf7bd7
+    }
bf7bd7
+
bf7bd7
     for (crd = kcm_cc_get_cred(cc);
bf7bd7
          crd != NULL;
bf7bd7
          crd = kcm_cc_next_cred(crd)) {
bf7bd7
@@ -1169,6 +1252,34 @@ static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq)
bf7bd7
     tevent_req_done(req);
bf7bd7
 }
bf7bd7
 
bf7bd7
+static errno_t
bf7bd7
+kcm_op_get_cred_by_uuid_reply(struct kcm_cred *crd,
bf7bd7
+                              struct sss_iobuf *reply)
bf7bd7
+{
bf7bd7
+    struct sss_iobuf *cred_blob;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    cred_blob = kcm_cred_get_creds(crd);
bf7bd7
+    if (cred_blob == NULL) {
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
bf7bd7
+        return ERR_NO_CREDS;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_write_len(reply, sss_iobuf_get_data(cred_blob),
bf7bd7
+                              sss_iobuf_get_size(cred_blob));
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot write ccache blob [%d]: %s\n",
bf7bd7
+              ret, sss_strerror(ret));
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return ret;
bf7bd7
+}
bf7bd7
+
bf7bd7
+struct kcm_op_get_cred_by_uuid_state {
bf7bd7
+    struct kcm_op_common_state common;
bf7bd7
+    uuid_t uuid;
bf7bd7
+};
bf7bd7
+
bf7bd7
 /* (name, uuid) -> (cred) */
bf7bd7
 static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq);
bf7bd7
 
bf7bd7
@@ -1179,20 +1290,51 @@ kcm_op_get_cred_by_uuid_send(TALLOC_CTX *mem_ctx,
bf7bd7
 {
bf7bd7
     struct tevent_req *req = NULL;
bf7bd7
     struct tevent_req *subreq = NULL;
bf7bd7
-    struct kcm_op_common_state *state = NULL;
bf7bd7
+    struct kcm_op_get_cred_by_uuid_state *state;
bf7bd7
+    struct kcm_cred *crd;
bf7bd7
     errno_t ret;
bf7bd7
     const char *name;
bf7bd7
 
bf7bd7
-    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
bf7bd7
+    req = tevent_req_create(mem_ctx, &state,
bf7bd7
+                            struct kcm_op_get_cred_by_uuid_state);
bf7bd7
     if (req == NULL) {
bf7bd7
         return NULL;
bf7bd7
     }
bf7bd7
-    state->op_ctx = op_ctx;
bf7bd7
+    state->common.op_ctx = op_ctx;
bf7bd7
 
bf7bd7
     ret = sss_iobuf_read_stringz(op_ctx->input, &name);
bf7bd7
     if (ret != EOK) {
bf7bd7
         goto immediate;
bf7bd7
     }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_len(state->common.op_ctx->input, UUID_BYTES,
bf7bd7
+                             state->uuid);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read input UUID [%d]: %s\n",
bf7bd7
+              ret, sss_strerror(ret));
bf7bd7
+        goto immediate;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    if (op_ctx->conn_data->creds != NULL) {
bf7bd7
+        crd = kcm_creds_lookup(op_ctx->conn_data->creds, state->uuid);
bf7bd7
+        if (crd == NULL) {
bf7bd7
+            /* This should not happen, it can only happen if wrong UUID was
bf7bd7
+             * requested which suggests bug in the caller application. */
bf7bd7
+            DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
bf7bd7
+            kcm_debug_uuid(state->uuid);
bf7bd7
+            state->common.op_ret = ERR_KCM_CC_END;
bf7bd7
+            ret = EOK;
bf7bd7
+            goto immediate;
bf7bd7
+        } else {
bf7bd7
+            ret = kcm_op_get_cred_by_uuid_reply(crd, op_ctx->reply);
bf7bd7
+            if (ret == ERR_NO_CREDS) {
bf7bd7
+                state->common.op_ret = ret;
bf7bd7
+                ret = EOK;
bf7bd7
+            }
bf7bd7
+            goto immediate;
bf7bd7
+        }
bf7bd7
+    }
bf7bd7
+
bf7bd7
     DEBUG(SSSDBG_TRACE_LIBS, "Returning creds by UUID for %s\n", name);
bf7bd7
 
bf7bd7
     subreq = kcm_ccdb_getbyname_send(state, ev,
bf7bd7
@@ -1207,7 +1349,11 @@ kcm_op_get_cred_by_uuid_send(TALLOC_CTX *mem_ctx,
bf7bd7
     return req;
bf7bd7
 
bf7bd7
 immediate:
bf7bd7
-    tevent_req_error(req, ret);
bf7bd7
+    if (ret == EOK) {
bf7bd7
+        tevent_req_done(req);
bf7bd7
+    } else {
bf7bd7
+        tevent_req_error(req, ret);
bf7bd7
+    }
bf7bd7
     tevent_req_post(req, ev);
bf7bd7
     return req;
bf7bd7
 }
bf7bd7
@@ -1216,14 +1362,14 @@ static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq)
bf7bd7
 {
bf7bd7
     struct tevent_req *req = tevent_req_callback_data(subreq,
bf7bd7
                                                       struct tevent_req);
bf7bd7
-    struct kcm_op_common_state *state = tevent_req_data(req,
bf7bd7
-                                                struct kcm_op_common_state);
bf7bd7
+    struct kcm_op_get_cred_by_uuid_state *state = tevent_req_data(req,
bf7bd7
+                                        struct kcm_op_get_cred_by_uuid_state);
bf7bd7
     errno_t ret;
bf7bd7
     struct kcm_ccache *cc;
bf7bd7
     struct kcm_cred *crd;
bf7bd7
-    uuid_t uuid_in;
bf7bd7
-    uuid_t uuid;
bf7bd7
-    struct sss_iobuf *cred_blob;
bf7bd7
+    struct kcm_conn_data *conn_data;
bf7bd7
+
bf7bd7
+    conn_data = state->common.op_ctx->conn_data;
bf7bd7
 
bf7bd7
     ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
bf7bd7
     talloc_zfree(subreq);
bf7bd7
@@ -1235,67 +1381,43 @@ static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq)
bf7bd7
         return;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    if (cc == NULL) {
bf7bd7
-        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n");
bf7bd7
-        state->op_ret = ERR_NO_MATCHING_CREDS;
bf7bd7
-        tevent_req_done(req);
bf7bd7
-        return;
bf7bd7
-    }
bf7bd7
-
bf7bd7
-    ret = sss_iobuf_read_len(state->op_ctx->input,
bf7bd7
-                             UUID_BYTES, uuid_in);
bf7bd7
+    ret = kcm_creds_to_table(conn_data, kcm_cc_get_cred(cc), &conn_data->creds);
bf7bd7
     if (ret != EOK) {
bf7bd7
-        DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "Cannot read input UUID [%d]: %s\n",
bf7bd7
-              ret, sss_strerror(ret));
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE, "Unable to build credentials hash table "
bf7bd7
+              "[%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
         tevent_req_error(req, ret);
bf7bd7
         return;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    for (crd = kcm_cc_get_cred(cc);
bf7bd7
-         crd != NULL;
bf7bd7
-         crd = kcm_cc_next_cred(crd)) {
bf7bd7
-        ret = kcm_cred_get_uuid(crd, uuid);
bf7bd7
-        if (ret != EOK) {
bf7bd7
-            DEBUG(SSSDBG_MINOR_FAILURE,
bf7bd7
-                  "Cannot get UUID from creds, skipping\n");
bf7bd7
-            continue;
bf7bd7
+    if (conn_data->creds != NULL) {
bf7bd7
+        crd = kcm_creds_lookup(conn_data->creds, state->uuid);
bf7bd7
+        if (crd == NULL) {
bf7bd7
+            DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
bf7bd7
+            kcm_debug_uuid(state->uuid);
bf7bd7
+            state->common.op_ret = ERR_KCM_CC_END;
bf7bd7
+        } else {
bf7bd7
+            ret = kcm_op_get_cred_by_uuid_reply(crd, state->common.op_ctx->reply);
bf7bd7
+            if (ret != EOK && ret != ERR_NO_CREDS) {
bf7bd7
+                tevent_req_error(req, ret);
bf7bd7
+                return;
bf7bd7
+            }
bf7bd7
+            state->common.op_ret = ret;
bf7bd7
         }
bf7bd7
-
bf7bd7
-        if (uuid_compare(uuid, uuid_in) == 0) {
bf7bd7
-            break;
bf7bd7
-        }
bf7bd7
-        kcm_debug_uuid(uuid);
bf7bd7
     }
bf7bd7
 
bf7bd7
-    if (crd == NULL) {
bf7bd7
-        state->op_ret = ERR_KCM_CC_END;
bf7bd7
-        DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
bf7bd7
-        tevent_req_done(req);
bf7bd7
-        return;
bf7bd7
-    }
bf7bd7
+    tevent_req_done(req);
bf7bd7
+}
bf7bd7
 
bf7bd7
-    cred_blob = kcm_cred_get_creds(crd);
bf7bd7
-    if (cred_blob == NULL) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
bf7bd7
-        state->op_ret = ERR_NO_CREDS;
bf7bd7
-        tevent_req_done(req);
bf7bd7
-        return;
bf7bd7
-    }
bf7bd7
+static errno_t kcm_op_get_cred_by_uuid_recv(struct tevent_req *req,
bf7bd7
+                                            uint32_t *_op_ret)
bf7bd7
+{
bf7bd7
+    struct kcm_op_get_cred_by_uuid_state *state;
bf7bd7
 
bf7bd7
-    ret = sss_iobuf_write_len(state->op_ctx->reply,
bf7bd7
-                              sss_iobuf_get_data(cred_blob),
bf7bd7
-                              sss_iobuf_get_size(cred_blob));
bf7bd7
-    if (ret != EOK) {
bf7bd7
-        DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "Cannot write ccache blob [%d]: %s\n",
bf7bd7
-              ret, sss_strerror(ret));
bf7bd7
-        tevent_req_error(req, ret);
bf7bd7
-        return;
bf7bd7
-    }
bf7bd7
+    state = tevent_req_data(req, struct kcm_op_get_cred_by_uuid_state);
bf7bd7
 
bf7bd7
-    state->op_ret = EOK;
bf7bd7
-    tevent_req_done(req);
bf7bd7
+    TEVENT_REQ_RETURN_ON_ERROR(req);
bf7bd7
+    *_op_ret = state->common.op_ret;
bf7bd7
+    return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
 /* (name, flags, credtag) -> () */
bf7bd7
@@ -1468,7 +1590,7 @@ static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq)
bf7bd7
     talloc_zfree(subreq);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "Cannot get ccahe by UUID [%d]: %s\n",
bf7bd7
+              "Cannot get ccache by UUID [%d]: %s\n",
bf7bd7
               ret, sss_strerror(ret));
bf7bd7
         tevent_req_error(req, ret);
bf7bd7
         return;
bf7bd7
@@ -2153,7 +2275,7 @@ static struct kcm_op kcm_optable[] = {
bf7bd7
     { "RETRIEVE",            NULL, NULL },
bf7bd7
     { "GET_PRINCIPAL",       kcm_op_get_principal_send, NULL },
bf7bd7
     { "GET_CRED_UUID_LIST",  kcm_op_get_cred_uuid_list_send, NULL },
bf7bd7
-    { "GET_CRED_BY_UUID",    kcm_op_get_cred_by_uuid_send, NULL },
bf7bd7
+    { "GET_CRED_BY_UUID",    kcm_op_get_cred_by_uuid_send, kcm_op_get_cred_by_uuid_recv },
bf7bd7
     { "REMOVE_CRED",         kcm_op_remove_cred_send, NULL },
bf7bd7
     { "SET_FLAGS",           NULL, NULL },
bf7bd7
     { "CHOWN",               NULL, NULL },
bf7bd7
diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h
bf7bd7
index 67d9f8602..ab6c13791 100644
bf7bd7
--- a/src/responder/kcm/kcmsrv_ops.h
bf7bd7
+++ b/src/responder/kcm/kcmsrv_ops.h
bf7bd7
@@ -24,6 +24,7 @@
bf7bd7
 
bf7bd7
 #include "config.h"
bf7bd7
 
bf7bd7
+#include <dhash.h>
bf7bd7
 #include <sys/types.h>
bf7bd7
 #include "util/sss_iobuf.h"
bf7bd7
 #include "responder/kcm/kcmsrv_pvt.h"
bf7bd7
@@ -32,10 +33,17 @@ struct kcm_op;
bf7bd7
 struct kcm_op *kcm_get_opt(uint16_t opcode);
bf7bd7
 const char *kcm_opt_name(struct kcm_op *op);
bf7bd7
 
bf7bd7
+struct kcm_conn_data {
bf7bd7
+    /* Credentials obtained by GET_CRED_UUID_LIST. We use to improve performance
bf7bd7
+     * by avoiding ccache lookups in GET_CRED_BY_UUID. */
bf7bd7
+    hash_table_t *creds;
bf7bd7
+};
bf7bd7
+
bf7bd7
 struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
bf7bd7
                                 struct tevent_context *ev,
bf7bd7
                                 struct kcm_ops_queue_ctx *qctx,
bf7bd7
                                 struct kcm_resp_ctx *kcm_data,
bf7bd7
+                                struct kcm_conn_data *conn_data,
bf7bd7
                                 struct cli_creds *client,
bf7bd7
                                 struct kcm_data *input,
bf7bd7
                                 struct kcm_op *op);
bf7bd7
diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
bf7bd7
index eb37c08b7..252ef3a1d 100644
bf7bd7
--- a/src/responder/secrets/local.c
bf7bd7
+++ b/src/responder/secrets/local.c
bf7bd7
@@ -134,7 +134,7 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
bf7bd7
             break;
bf7bd7
         }
bf7bd7
 
bf7bd7
-        ret = sss_sec_get(state, ssec_req, &secret);
bf7bd7
+        ret = sss_sec_get(state, ssec_req, (uint8_t**)&secret, NULL, NULL);
bf7bd7
         if (ret) goto done;
bf7bd7
 
bf7bd7
         if (body_is_json) {
bf7bd7
@@ -168,7 +168,8 @@ static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
bf7bd7
         }
bf7bd7
         if (ret) goto done;
bf7bd7
 
bf7bd7
-        ret = sss_sec_put(ssec_req, secret);
bf7bd7
+        ret = sss_sec_put(ssec_req, (uint8_t *)secret, strlen(secret) + 1,
bf7bd7
+                          SSS_SEC_MASTERKEY, "simple");
bf7bd7
         if (ret) goto done;
bf7bd7
         break;
bf7bd7
 
bf7bd7
diff --git a/src/shared/safealign.h b/src/shared/safealign.h
bf7bd7
index b00c37f5b..35909faa2 100644
bf7bd7
--- a/src/shared/safealign.h
bf7bd7
+++ b/src/shared/safealign.h
bf7bd7
@@ -97,6 +97,10 @@ safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter)
bf7bd7
 #define SAFEALIGN_SETMEM_UINT16(dest, value, pctr) \
bf7bd7
     SAFEALIGN_SETMEM_VALUE(dest, value, uint16_t, pctr)
bf7bd7
 
bf7bd7
+/* SAFEALIGN_SETMEM_UINT8(void *dest, uint8_t value, size_t *pctr) */
bf7bd7
+#define SAFEALIGN_SETMEM_UINT8(dest, value, pctr) \
bf7bd7
+    SAFEALIGN_SETMEM_VALUE(dest, value, uint8_t, pctr)
bf7bd7
+
bf7bd7
 /* These macros are the same as their equivalents without _CHECK suffix,
bf7bd7
  * but additionally make the caller return EINVAL immediately if *pctr
bf7bd7
  * would exceed len. */
bf7bd7
diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_marshalling.c
bf7bd7
similarity index 71%
bf7bd7
rename from src/tests/cmocka/test_kcm_json_marshalling.c
bf7bd7
rename to src/tests/cmocka/test_kcm_marshalling.c
bf7bd7
index 48ee92bd6..cebebac80 100644
bf7bd7
--- a/src/tests/cmocka/test_kcm_json_marshalling.c
bf7bd7
+++ b/src/tests/cmocka/test_kcm_marshalling.c
bf7bd7
@@ -154,7 +154,7 @@ static void assert_cc_equal(struct kcm_ccache *cc1,
bf7bd7
     assert_cc_offset_equal(cc1, cc2);
bf7bd7
 }
bf7bd7
 
bf7bd7
-static void test_kcm_ccache_marshall_unmarshall(void **state)
bf7bd7
+static void test_kcm_ccache_marshall_unmarshall_json(void **state)
bf7bd7
 {
bf7bd7
     struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
bf7bd7
                                         struct kcm_marshalling_test_ctx);
bf7bd7
@@ -182,10 +182,7 @@ static void test_kcm_ccache_marshall_unmarshall(void **state)
bf7bd7
                      &cc);
bf7bd7
     assert_int_equal(ret, EOK);
bf7bd7
 
bf7bd7
-    ret = kcm_ccache_to_sec_input(test_ctx,
bf7bd7
-                                  cc,
bf7bd7
-                                  &owner,
bf7bd7
-                                  &payload);
bf7bd7
+    ret = kcm_ccache_to_sec_input_json(test_ctx, cc, &payload);
bf7bd7
     assert_int_equal(ret, EOK);
bf7bd7
 
bf7bd7
     data = sss_iobuf_get_data(payload);
bf7bd7
@@ -196,25 +193,19 @@ static void test_kcm_ccache_marshall_unmarshall(void **state)
bf7bd7
     key = sec_key_create(test_ctx, name, uuid);
bf7bd7
     assert_non_null(key);
bf7bd7
 
bf7bd7
-    ret = sec_kv_to_ccache(test_ctx,
bf7bd7
-                           key,
bf7bd7
-                           (const char *) data,
bf7bd7
-                           &owner,
bf7bd7
-                           &cc2;;
bf7bd7
+    ret = sec_kv_to_ccache_json(test_ctx, key, (const char *)data, &owner,
bf7bd7
+                                &cc2;;
bf7bd7
     assert_int_equal(ret, EOK);
bf7bd7
 
bf7bd7
     assert_cc_equal(cc, cc2);
bf7bd7
 
bf7bd7
     /* This key is exactly one byte shorter than it should be */
bf7bd7
-    ret = sec_kv_to_ccache(test_ctx,
bf7bd7
-                           TEST_UUID_STR"-",
bf7bd7
-                           (const char *) data,
bf7bd7
-                           &owner,
bf7bd7
-                           &cc2;;
bf7bd7
+    ret = sec_kv_to_ccache_json(test_ctx, TEST_UUID_STR "-", (const char *)data,
bf7bd7
+                                &owner, &cc2;;
bf7bd7
     assert_int_equal(ret, EINVAL);
bf7bd7
 }
bf7bd7
 
bf7bd7
-static void test_kcm_ccache_no_princ(void **state)
bf7bd7
+static void test_kcm_ccache_no_princ_json(void **state)
bf7bd7
 {
bf7bd7
     struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
bf7bd7
                                         struct kcm_marshalling_test_ctx);
bf7bd7
@@ -246,10 +237,7 @@ static void test_kcm_ccache_no_princ(void **state)
bf7bd7
     princ = kcm_cc_get_client_principal(cc);
bf7bd7
     assert_null(princ);
bf7bd7
 
bf7bd7
-    ret = kcm_ccache_to_sec_input(test_ctx,
bf7bd7
-                                  cc,
bf7bd7
-                                  &owner,
bf7bd7
-                                  &payload);
bf7bd7
+    ret = kcm_ccache_to_sec_input_json(test_ctx, cc, &payload);
bf7bd7
     assert_int_equal(ret, EOK);
bf7bd7
 
bf7bd7
     data = sss_iobuf_get_data(payload);
bf7bd7
@@ -260,11 +248,110 @@ static void test_kcm_ccache_no_princ(void **state)
bf7bd7
     key = sec_key_create(test_ctx, name, uuid);
bf7bd7
     assert_non_null(key);
bf7bd7
 
bf7bd7
-    ret = sec_kv_to_ccache(test_ctx,
bf7bd7
-                           key,
bf7bd7
-                           (const char *) data,
bf7bd7
-                           &owner,
bf7bd7
-                           &cc2;;
bf7bd7
+    ret = sec_kv_to_ccache_json(test_ctx, key, (const char *)data, &owner,
bf7bd7
+                                &cc2;;
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+
bf7bd7
+    assert_cc_equal(cc, cc2);
bf7bd7
+}
bf7bd7
+
bf7bd7
+static void test_kcm_ccache_marshall_unmarshall_binary(void **state)
bf7bd7
+{
bf7bd7
+    struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
bf7bd7
+                                        struct kcm_marshalling_test_ctx);
bf7bd7
+    errno_t ret;
bf7bd7
+    struct cli_creds owner;
bf7bd7
+    struct kcm_ccache *cc;
bf7bd7
+    struct kcm_ccache *cc2;
bf7bd7
+    struct sss_iobuf *payload;
bf7bd7
+    const char *name;
bf7bd7
+    const char *key;
bf7bd7
+    uint8_t *data;
bf7bd7
+    uuid_t uuid;
bf7bd7
+
bf7bd7
+    owner.ucred.uid = getuid();
bf7bd7
+    owner.ucred.gid = getuid();
bf7bd7
+
bf7bd7
+    name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid());
bf7bd7
+    assert_non_null(name);
bf7bd7
+
bf7bd7
+    ret = kcm_cc_new(test_ctx,
bf7bd7
+                     test_ctx->kctx,
bf7bd7
+                     &owner,
bf7bd7
+                     name,
bf7bd7
+                     test_ctx->princ,
bf7bd7
+                     &cc);
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+
bf7bd7
+    ret = kcm_ccache_to_sec_input_binary(test_ctx, cc, &payload);
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+
bf7bd7
+    data = sss_iobuf_get_data(payload);
bf7bd7
+    assert_non_null(data);
bf7bd7
+
bf7bd7
+    ret = kcm_cc_get_uuid(cc, uuid);
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+    key = sec_key_create(test_ctx, name, uuid);
bf7bd7
+    assert_non_null(key);
bf7bd7
+
bf7bd7
+    sss_iobuf_cursor_reset(payload);
bf7bd7
+    ret = sec_kv_to_ccache_binary(test_ctx, key, payload, &owner, &cc2;;
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+
bf7bd7
+    assert_cc_equal(cc, cc2);
bf7bd7
+
bf7bd7
+    /* This key is exactly one byte shorter than it should be */
bf7bd7
+    sss_iobuf_cursor_reset(payload);
bf7bd7
+    ret = sec_kv_to_ccache_binary(test_ctx, TEST_UUID_STR "-", payload, &owner,
bf7bd7
+                                  &cc2;;
bf7bd7
+    assert_int_equal(ret, EINVAL);
bf7bd7
+}
bf7bd7
+
bf7bd7
+static void test_kcm_ccache_no_princ_binary(void **state)
bf7bd7
+{
bf7bd7
+    struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state,
bf7bd7
+                                        struct kcm_marshalling_test_ctx);
bf7bd7
+    errno_t ret;
bf7bd7
+    struct cli_creds owner;
bf7bd7
+    const char *name;
bf7bd7
+    struct kcm_ccache *cc;
bf7bd7
+    krb5_principal princ;
bf7bd7
+    struct kcm_ccache *cc2;
bf7bd7
+    struct sss_iobuf *payload;
bf7bd7
+    const char *key;
bf7bd7
+    uint8_t *data;
bf7bd7
+    uuid_t uuid;
bf7bd7
+
bf7bd7
+    owner.ucred.uid = getuid();
bf7bd7
+    owner.ucred.gid = getuid();
bf7bd7
+
bf7bd7
+    name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid());
bf7bd7
+    assert_non_null(name);
bf7bd7
+
bf7bd7
+    ret = kcm_cc_new(test_ctx,
bf7bd7
+                     test_ctx->kctx,
bf7bd7
+                     &owner,
bf7bd7
+                     name,
bf7bd7
+                     NULL,
bf7bd7
+                     &cc);
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+
bf7bd7
+    princ = kcm_cc_get_client_principal(cc);
bf7bd7
+    assert_null(princ);
bf7bd7
+
bf7bd7
+    ret = kcm_ccache_to_sec_input_binary(test_ctx, cc, &payload);
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+
bf7bd7
+    data = sss_iobuf_get_data(payload);
bf7bd7
+    assert_non_null(data);
bf7bd7
+
bf7bd7
+    ret = kcm_cc_get_uuid(cc, uuid);
bf7bd7
+    assert_int_equal(ret, EOK);
bf7bd7
+    key = sec_key_create(test_ctx, name, uuid);
bf7bd7
+    assert_non_null(key);
bf7bd7
+
bf7bd7
+    sss_iobuf_cursor_reset(payload);
bf7bd7
+    ret = sec_kv_to_ccache_binary(test_ctx, key, payload, &owner, &cc2;;
bf7bd7
     assert_int_equal(ret, EOK);
bf7bd7
 
bf7bd7
     assert_cc_equal(cc, cc2);
bf7bd7
@@ -340,10 +427,16 @@ int main(int argc, const char *argv[])
bf7bd7
     };
bf7bd7
 
bf7bd7
     const struct CMUnitTest tests[] = {
bf7bd7
-        cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall,
bf7bd7
+        cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall_binary,
bf7bd7
+                                        setup_kcm_marshalling,
bf7bd7
+                                        teardown_kcm_marshalling),
bf7bd7
+        cmocka_unit_test_setup_teardown(test_kcm_ccache_no_princ_binary,
bf7bd7
+                                        setup_kcm_marshalling,
bf7bd7
+                                        teardown_kcm_marshalling),
bf7bd7
+        cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall_json,
bf7bd7
                                         setup_kcm_marshalling,
bf7bd7
                                         teardown_kcm_marshalling),
bf7bd7
-        cmocka_unit_test_setup_teardown(test_kcm_ccache_no_princ,
bf7bd7
+        cmocka_unit_test_setup_teardown(test_kcm_ccache_no_princ_json,
bf7bd7
                                         setup_kcm_marshalling,
bf7bd7
                                         teardown_kcm_marshalling),
bf7bd7
         cmocka_unit_test(test_sec_key_get_uuid),
bf7bd7
diff --git a/src/tests/cmocka/test_sss_ptr_hash.c b/src/tests/cmocka/test_sss_ptr_hash.c
bf7bd7
index 1458238f5..31cf8b705 100644
bf7bd7
--- a/src/tests/cmocka/test_sss_ptr_hash.c
bf7bd7
+++ b/src/tests/cmocka/test_sss_ptr_hash.c
bf7bd7
@@ -91,6 +91,45 @@ void test_sss_ptr_hash_with_free_cb(void **state)
bf7bd7
     assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT*2);
bf7bd7
 }
bf7bd7
 
bf7bd7
+void test_sss_ptr_hash_overwrite_with_free_cb(void **state)
bf7bd7
+{
bf7bd7
+    hash_table_t *table;
bf7bd7
+    int free_counter = 0;
bf7bd7
+    unsigned long count;
bf7bd7
+    char *payload;
bf7bd7
+    char *value;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    table = sss_ptr_hash_create(global_talloc_context,
bf7bd7
+                                free_payload_cb,
bf7bd7
+                                &free_counter);
bf7bd7
+    assert_non_null(table);
bf7bd7
+
bf7bd7
+    payload = talloc_strdup(table, "test_value1");
bf7bd7
+    assert_non_null(payload);
bf7bd7
+    talloc_set_name_const(payload, "char");
bf7bd7
+    ret = sss_ptr_hash_add_or_override(table, "test", payload, char);
bf7bd7
+    assert_int_equal(ret, 0);
bf7bd7
+    count = hash_count(table);
bf7bd7
+    assert_int_equal(count, 1);
bf7bd7
+    value = sss_ptr_hash_lookup(table, "test", char);
bf7bd7
+    assert_ptr_equal(value, payload);
bf7bd7
+
bf7bd7
+
bf7bd7
+    payload = talloc_strdup(table, "test_value2");
bf7bd7
+    assert_non_null(payload);
bf7bd7
+    talloc_set_name_const(payload, "char");
bf7bd7
+    ret = sss_ptr_hash_add_or_override(table, "test", payload, char);
bf7bd7
+    assert_int_equal(ret, 0);
bf7bd7
+    count = hash_count(table);
bf7bd7
+    assert_int_equal(count, 1);
bf7bd7
+    value = sss_ptr_hash_lookup(table, "test", char);
bf7bd7
+    assert_ptr_equal(value, payload);
bf7bd7
+
bf7bd7
+    talloc_free(table);
bf7bd7
+    assert_int_equal(free_counter, 2);
bf7bd7
+}
bf7bd7
+
bf7bd7
 struct table_wrapper
bf7bd7
 {
bf7bd7
     hash_table_t **table;
bf7bd7
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
bf7bd7
index d77a972c1..d258622fb 100644
bf7bd7
--- a/src/tests/cmocka/test_utils.c
bf7bd7
+++ b/src/tests/cmocka/test_utils.c
bf7bd7
@@ -2144,6 +2144,9 @@ int main(int argc, const char *argv[])
bf7bd7
         cmocka_unit_test_setup_teardown(test_sss_ptr_hash_with_free_cb,
bf7bd7
                                         setup_leak_tests,
bf7bd7
                                         teardown_leak_tests),
bf7bd7
+        cmocka_unit_test_setup_teardown(test_sss_ptr_hash_overwrite_with_free_cb,
bf7bd7
+                                        setup_leak_tests,
bf7bd7
+                                        teardown_leak_tests),
bf7bd7
         cmocka_unit_test_setup_teardown(test_sss_ptr_hash_with_lookup_cb,
bf7bd7
                                         setup_leak_tests,
bf7bd7
                                         teardown_leak_tests),
bf7bd7
diff --git a/src/tests/cmocka/test_utils.h b/src/tests/cmocka/test_utils.h
bf7bd7
index 44b9479f9..458bcb750 100644
bf7bd7
--- a/src/tests/cmocka/test_utils.h
bf7bd7
+++ b/src/tests/cmocka/test_utils.h
bf7bd7
@@ -35,6 +35,7 @@ void test_concatenate_string_array(void **state);
bf7bd7
 
bf7bd7
 /* from src/tests/cmocka/test_sss_ptr_hash.c */
bf7bd7
 void test_sss_ptr_hash_with_free_cb(void **state);
bf7bd7
+void test_sss_ptr_hash_overwrite_with_free_cb(void **state);
bf7bd7
 void test_sss_ptr_hash_with_lookup_cb(void **state);
bf7bd7
 void test_sss_ptr_hash_without_cb(void **state);
bf7bd7
 
bf7bd7
diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py
bf7bd7
index 00933fb34..18d722c13 100644
bf7bd7
--- a/src/tests/intg/test_secrets.py
bf7bd7
+++ b/src/tests/intg/test_secrets.py
bf7bd7
@@ -438,7 +438,8 @@ def run_quota_test(cli, max_secrets, max_payload_size):
bf7bd7
     KILOBYTE = 1024
bf7bd7
     kb_payload_size = max_payload_size * KILOBYTE
bf7bd7
 
bf7bd7
-    sec_value = "x" * kb_payload_size
bf7bd7
+    # Adjust payload size to hold terminal zero byte.
bf7bd7
+    sec_value = "x" * (kb_payload_size - 1)
bf7bd7
 
bf7bd7
     cli.set_secret("foo", sec_value)
bf7bd7
 
bf7bd7
diff --git a/src/tests/multihost/basic/test_kcm.py b/src/tests/multihost/basic/test_kcm.py
bf7bd7
index e5d315827..6f65431f8 100644
bf7bd7
--- a/src/tests/multihost/basic/test_kcm.py
bf7bd7
+++ b/src/tests/multihost/basic/test_kcm.py
bf7bd7
@@ -310,6 +310,12 @@ class TestSanityKCM(object):
bf7bd7
         set_param(multihost, 'kcm', 'max_ccache_size', '1')
bf7bd7
         self._restart_kcm(multihost)
bf7bd7
 
bf7bd7
-        with pytest.raises(paramiko.ssh_exception.AuthenticationException):
bf7bd7
-            ssh_foo3 = SSHClient(multihost.master[0].sys_hostname,
bf7bd7
-                                 username='foo3', password='Secret123')
bf7bd7
+        # We use kinit to exceed the maximum ccache size as it creates payload
bf7bd7
+        # of 1280 bytes by acquiring tgt and also some control credentials.
bf7bd7
+        # SSH authentication is not sufficient as it stores only tgt.
bf7bd7
+        ssh_foo3 = SSHClient(multihost.master[0].sys_hostname,
bf7bd7
+                             username='foo3', password='Secret123')
bf7bd7
+        (_, _, exit_status) = ssh_foo3.execute_cmd(
bf7bd7
+            'kinit foo3@EXAMPLE.TEST', 'Secret123'
bf7bd7
+        )
bf7bd7
+        assert exit_status != 0
bf7bd7
diff --git a/src/util/secrets/sec_pvt.h b/src/util/secrets/sec_pvt.h
bf7bd7
index 92e2b8b25..0e77a660e 100644
bf7bd7
--- a/src/util/secrets/sec_pvt.h
bf7bd7
+++ b/src/util/secrets/sec_pvt.h
bf7bd7
@@ -33,7 +33,7 @@
bf7bd7
 #define SSS_SEC_KCM_BASEPATH        "/kcm/"
bf7bd7
 
bf7bd7
 struct sss_sec_data {
bf7bd7
-    char *data;
bf7bd7
+    uint8_t *data;
bf7bd7
     size_t length;
bf7bd7
 };
bf7bd7
 
bf7bd7
diff --git a/src/util/secrets/secrets.c b/src/util/secrets/secrets.c
bf7bd7
index d701face0..c6310b585 100644
bf7bd7
--- a/src/util/secrets/secrets.c
bf7bd7
+++ b/src/util/secrets/secrets.c
bf7bd7
@@ -36,9 +36,14 @@
bf7bd7
 #define SECRETS_BASEDN  "cn=secrets"
bf7bd7
 #define KCM_BASEDN      "cn=kcm"
bf7bd7
 
bf7bd7
-#define LOCAL_SIMPLE_FILTER "(type=simple)"
bf7bd7
+#define LOCAL_SIMPLE_FILTER "(|(type=simple)(type=binary))"
bf7bd7
 #define LOCAL_CONTAINER_FILTER "(type=container)"
bf7bd7
 
bf7bd7
+#define SEC_ATTR_SECRET  "secret"
bf7bd7
+#define SEC_ATTR_ENCTYPE "enctype"
bf7bd7
+#define SEC_ATTR_TYPE    "type"
bf7bd7
+#define SEC_ATTR_CTIME   "creationTime"
bf7bd7
+
bf7bd7
 typedef int (*url_mapper_fn)(TALLOC_CTX *mem_ctx,
bf7bd7
                              const char *url,
bf7bd7
                              uid_t client,
bf7bd7
@@ -63,90 +68,136 @@ static struct sss_sec_quota default_kcm_quota = {
bf7bd7
     .containers_nest_level = DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
bf7bd7
 };
bf7bd7
 
bf7bd7
-static int local_decrypt(struct sss_sec_ctx *sctx, TALLOC_CTX *mem_ctx,
bf7bd7
-                         const char *secret, const char *enctype,
bf7bd7
-                         char **plain_secret)
bf7bd7
+static const char *sss_sec_enctype_to_str(enum sss_sec_enctype enctype)
bf7bd7
 {
bf7bd7
-    char *output;
bf7bd7
+    switch (enctype) {
bf7bd7
+    case SSS_SEC_PLAINTEXT:
bf7bd7
+        return "plaintext";
bf7bd7
+    case SSS_SEC_MASTERKEY:
bf7bd7
+        return "masterkey";
bf7bd7
+    default:
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Bug: unknown encryption type %d\n",
bf7bd7
+                enctype);
bf7bd7
+        return "unknown";
bf7bd7
+    }
bf7bd7
+}
bf7bd7
 
bf7bd7
-    if (enctype && strcmp(enctype, "masterkey") == 0) {
bf7bd7
-        DEBUG(SSSDBG_TRACE_INTERNAL, "Decrypting with masterkey\n");
bf7bd7
+static enum sss_sec_enctype sss_sec_str_to_enctype(const char *str)
bf7bd7
+{
bf7bd7
+    if (strcmp("plaintext", str) == 0) {
bf7bd7
+        return SSS_SEC_PLAINTEXT;
bf7bd7
+    }
bf7bd7
 
bf7bd7
-        struct sss_sec_data _secret;
bf7bd7
-        size_t outlen;
bf7bd7
-        int ret;
bf7bd7
+    if (strcmp("masterkey", str) == 0) {
bf7bd7
+        return SSS_SEC_MASTERKEY;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return SSS_SEC_ENCTYPE_SENTINEL;
bf7bd7
+}
bf7bd7
 
bf7bd7
-        _secret.data = (char *)sss_base64_decode(mem_ctx, secret,
bf7bd7
-                                                 &_secret.length);
bf7bd7
+static int local_decrypt(struct sss_sec_ctx *sctx,
bf7bd7
+                         TALLOC_CTX *mem_ctx,
bf7bd7
+                         uint8_t *secret,
bf7bd7
+                         size_t secret_len,
bf7bd7
+                         enum sss_sec_enctype enctype,
bf7bd7
+                         uint8_t **_output,
bf7bd7
+                         size_t *_output_len)
bf7bd7
+{
bf7bd7
+    struct sss_sec_data _secret;
bf7bd7
+    uint8_t *output;
bf7bd7
+    size_t output_len;
bf7bd7
+    int ret;
bf7bd7
+
bf7bd7
+    switch (enctype) {
bf7bd7
+    case SSS_SEC_PLAINTEXT:
bf7bd7
+        output = talloc_memdup(mem_ctx, secret, secret_len);
bf7bd7
+        output_len = secret_len;
bf7bd7
+        break;
bf7bd7
+    case SSS_SEC_MASTERKEY:
bf7bd7
+        _secret.data = (uint8_t *)sss_base64_decode(mem_ctx,
bf7bd7
+                                                    (const char *)secret,
bf7bd7
+                                                    &_secret.length);
bf7bd7
         if (!_secret.data) {
bf7bd7
             DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed\n");
bf7bd7
             return EINVAL;
bf7bd7
         }
bf7bd7
 
bf7bd7
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Decrypting with masterkey\n");
bf7bd7
         ret = sss_decrypt(mem_ctx, AES256CBC_HMAC_SHA256,
bf7bd7
-                          (uint8_t *)sctx->master_key.data,
bf7bd7
+                          sctx->master_key.data,
bf7bd7
                           sctx->master_key.length,
bf7bd7
-                          (uint8_t *)_secret.data, _secret.length,
bf7bd7
-                          (uint8_t **)&output, &outlen);
bf7bd7
+                          _secret.data, _secret.length,
bf7bd7
+                          &output, &output_len);
bf7bd7
         talloc_free(_secret.data);
bf7bd7
         if (ret) {
bf7bd7
             DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
                   "sss_decrypt failed [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
             return ret;
bf7bd7
         }
bf7bd7
+        break;
bf7bd7
+    default:
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown encryption type '%d'\n", enctype);
bf7bd7
+        return EINVAL;
bf7bd7
+    }
bf7bd7
 
bf7bd7
-        if (((strnlen(output, outlen) + 1) != outlen) ||
bf7bd7
-            output[outlen - 1] != '\0') {
bf7bd7
-            DEBUG(SSSDBG_CRIT_FAILURE,
bf7bd7
-                  "Output length mismatch or output not NULL-terminated\n");
bf7bd7
-            talloc_free(output);
bf7bd7
-            return EIO;
bf7bd7
-        }
bf7bd7
-    } else {
bf7bd7
-        DEBUG(SSSDBG_TRACE_INTERNAL, "Unexpected enctype (not 'masterkey')\n");
bf7bd7
-        output = talloc_strdup(mem_ctx, secret);
bf7bd7
-        if (!output) return ENOMEM;
bf7bd7
+    if (output == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    *plain_secret = output;
bf7bd7
+    *_output = output;
bf7bd7
+    *_output_len = output_len;
bf7bd7
+
bf7bd7
     return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
-static int local_encrypt(struct sss_sec_ctx *sec_ctx, TALLOC_CTX *mem_ctx,
bf7bd7
-                         const char *secret, const char *enctype,
bf7bd7
-                         char **ciphertext)
bf7bd7
+static int local_encrypt(struct sss_sec_ctx *sec_ctx,
bf7bd7
+                         TALLOC_CTX *mem_ctx,
bf7bd7
+                         uint8_t *secret,
bf7bd7
+                         size_t secret_len,
bf7bd7
+                         enum sss_sec_enctype enctype,
bf7bd7
+                         uint8_t **_output,
bf7bd7
+                         size_t *_output_len)
bf7bd7
 {
bf7bd7
     struct sss_sec_data _secret;
bf7bd7
-    char *output;
bf7bd7
+    uint8_t *output;
bf7bd7
+    size_t output_len;
bf7bd7
+    char *b64;
bf7bd7
     int ret;
bf7bd7
 
bf7bd7
-    if (enctype == NULL) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "No encryption type\n");
bf7bd7
-        return EINVAL;
bf7bd7
-    }
bf7bd7
+    switch (enctype) {
bf7bd7
+    case SSS_SEC_PLAINTEXT:
bf7bd7
+        output = talloc_memdup(mem_ctx, secret, secret_len);
bf7bd7
+        output_len = secret_len;
bf7bd7
+        break;
bf7bd7
+    case SSS_SEC_MASTERKEY:
bf7bd7
+        ret = sss_encrypt(mem_ctx, AES256CBC_HMAC_SHA256,
bf7bd7
+                          sec_ctx->master_key.data,
bf7bd7
+                          sec_ctx->master_key.length,
bf7bd7
+                          secret, secret_len,
bf7bd7
+                          &_secret.data, &_secret.length);
bf7bd7
+        if (ret) {
bf7bd7
+            DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
+                "sss_encrypt failed [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
+            return ret;
bf7bd7
+        }
bf7bd7
 
bf7bd7
-    if (strcmp(enctype, "masterkey") != 0) {
bf7bd7
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown encryption type '%s'\n", enctype);
bf7bd7
+        b64 = sss_base64_encode(mem_ctx, _secret.data, _secret.length);
bf7bd7
+        output = (uint8_t*)b64;
bf7bd7
+        output_len = strlen(b64) + 1;
bf7bd7
+        talloc_free(_secret.data);
bf7bd7
+        break;
bf7bd7
+    default:
bf7bd7
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown encryption type '%d'\n", enctype);
bf7bd7
         return EINVAL;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = sss_encrypt(mem_ctx, AES256CBC_HMAC_SHA256,
bf7bd7
-                      (uint8_t *)sec_ctx->master_key.data,
bf7bd7
-                      sec_ctx->master_key.length,
bf7bd7
-                      (const uint8_t *)secret, strlen(secret) + 1,
bf7bd7
-                      (uint8_t **)&_secret.data, &_secret.length);
bf7bd7
-    if (ret) {
bf7bd7
-        DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "sss_encrypt failed [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
-        return ret;
bf7bd7
+    if (output == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    output = sss_base64_encode(mem_ctx,
bf7bd7
-                               (uint8_t *)_secret.data, _secret.length);
bf7bd7
-    talloc_free(_secret.data);
bf7bd7
-    if (!output) return ENOMEM;
bf7bd7
+    *_output = output;
bf7bd7
+    *_output_len = output_len;
bf7bd7
 
bf7bd7
-    *ciphertext = output;
bf7bd7
     return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
@@ -338,14 +389,14 @@ static int local_check_max_payload_size(struct sss_sec_req *req,
bf7bd7
         return EOK;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    max_payload_size = req->quota->max_payload_size * 1024; /* kb */
bf7bd7
+    max_payload_size = req->quota->max_payload_size * 1024; /* KiB */
bf7bd7
     if (payload_size > max_payload_size) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "Secrets' payload size [%d kb (%d)] exceeds the maximum allowed "
bf7bd7
-              "payload size [%d kb (%d)]\n",
bf7bd7
-              payload_size * 1024, /* kb */
bf7bd7
+              "Secrets' payload size [%d KiB (%d B)] exceeds the maximum "
bf7bd7
+              "allowed payload size [%d KiB (%d B)]\n",
bf7bd7
+              payload_size / 1024, /* KiB */
bf7bd7
               payload_size,
bf7bd7
-              req->quota->max_payload_size, /* kb */
bf7bd7
+              req->quota->max_payload_size, /* KiB */
bf7bd7
               max_payload_size);
bf7bd7
 
bf7bd7
         return ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE;
bf7bd7
@@ -404,7 +455,7 @@ static int local_db_create(struct sss_sec_req *req)
bf7bd7
     ret = local_db_check_containers_nest_level(req, msg->dn);
bf7bd7
     if (ret != EOK) goto done;
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_string(msg, "type", "container");
bf7bd7
+    ret = ldb_msg_add_string(msg, SEC_ATTR_TYPE, "container");
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "ldb_msg_add_string failed adding type:container [%d]: %s\n",
bf7bd7
@@ -412,7 +463,7 @@ static int local_db_create(struct sss_sec_req *req)
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_fmt(msg, "creationTime", "%lu", time(NULL));
bf7bd7
+    ret = ldb_msg_add_fmt(msg, SEC_ATTR_CTIME, "%lu", time(NULL));
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "ldb_msg_add_string failed adding creationTime [%d]: %s\n",
bf7bd7
@@ -892,7 +943,7 @@ errno_t sss_sec_list(TALLOC_CTX *mem_ctx,
bf7bd7
                      size_t *_num_keys)
bf7bd7
 {
bf7bd7
     TALLOC_CTX *tmp_ctx;
bf7bd7
-    static const char *attrs[] = { "secret", NULL };
bf7bd7
+    static const char *attrs[] = { SEC_ATTR_SECRET, NULL };
bf7bd7
     struct ldb_result *res;
bf7bd7
     char **keys;
bf7bd7
     int ret;
bf7bd7
@@ -951,13 +1002,21 @@ done:
bf7bd7
 
bf7bd7
 errno_t sss_sec_get(TALLOC_CTX *mem_ctx,
bf7bd7
                     struct sss_sec_req *req,
bf7bd7
-                    char **_secret)
bf7bd7
+                    uint8_t **_secret,
bf7bd7
+                    size_t *_secret_len,
bf7bd7
+                    char **_datatype)
bf7bd7
 {
bf7bd7
     TALLOC_CTX *tmp_ctx;
bf7bd7
-    static const char *attrs[] = { "secret", "enctype", NULL };
bf7bd7
+    static const char *attrs[] = { SEC_ATTR_SECRET, SEC_ATTR_ENCTYPE,
bf7bd7
+                                   SEC_ATTR_TYPE, NULL };
bf7bd7
     struct ldb_result *res;
bf7bd7
-    const char *attr_secret;
bf7bd7
+    const struct ldb_val *attr_secret;
bf7bd7
     const char *attr_enctype;
bf7bd7
+    const char *attr_datatype;
bf7bd7
+    enum sss_sec_enctype enctype;
bf7bd7
+    char *datatype;
bf7bd7
+    uint8_t *secret;
bf7bd7
+    size_t secret_len;
bf7bd7
     int ret;
bf7bd7
 
bf7bd7
     if (req == NULL || _secret == NULL) {
bf7bd7
@@ -996,21 +1055,38 @@ errno_t sss_sec_get(TALLOC_CTX *mem_ctx,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    attr_secret = ldb_msg_find_attr_as_string(res->msgs[0], "secret", NULL);
bf7bd7
+    attr_secret = ldb_msg_find_ldb_val(res->msgs[0], SEC_ATTR_SECRET);
bf7bd7
     if (!attr_secret) {
bf7bd7
         DEBUG(SSSDBG_CRIT_FAILURE, "The 'secret' attribute is missing\n");
bf7bd7
         ret = ENOENT;
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    attr_enctype = ldb_msg_find_attr_as_string(res->msgs[0], "enctype", NULL);
bf7bd7
+    attr_enctype = ldb_msg_find_attr_as_string(res->msgs[0], SEC_ATTR_ENCTYPE,
bf7bd7
+                                               "plaintext");
bf7bd7
+    enctype = sss_sec_str_to_enctype(attr_enctype);
bf7bd7
+    ret = local_decrypt(req->sctx, tmp_ctx, attr_secret->data,
bf7bd7
+                        attr_secret->length, enctype, &secret, &secret_len);
bf7bd7
+    if (ret) goto done;
bf7bd7
 
bf7bd7
-    if (attr_enctype) {
bf7bd7
-        ret = local_decrypt(req->sctx, mem_ctx, attr_secret, attr_enctype, _secret);
bf7bd7
-        if (ret) goto done;
bf7bd7
-    } else {
bf7bd7
-        *_secret = talloc_strdup(mem_ctx, attr_secret);
bf7bd7
+    if (_datatype != NULL) {
bf7bd7
+        attr_datatype = ldb_msg_find_attr_as_string(res->msgs[0], SEC_ATTR_TYPE,
bf7bd7
+                                                    "simple");
bf7bd7
+        datatype = talloc_strdup(tmp_ctx, attr_datatype);
bf7bd7
+        if (datatype == NULL) {
bf7bd7
+            ret = ENOMEM;
bf7bd7
+            goto done;
bf7bd7
+        }
bf7bd7
+
bf7bd7
+        *_datatype = talloc_steal(mem_ctx, datatype);
bf7bd7
     }
bf7bd7
+
bf7bd7
+    *_secret = talloc_steal(mem_ctx, secret);
bf7bd7
+
bf7bd7
+    if (_secret_len) {
bf7bd7
+        *_secret_len = secret_len;
bf7bd7
+    }
bf7bd7
+
bf7bd7
     ret = EOK;
bf7bd7
 
bf7bd7
 done:
bf7bd7
@@ -1019,11 +1095,13 @@ done:
bf7bd7
 }
bf7bd7
 
bf7bd7
 errno_t sss_sec_put(struct sss_sec_req *req,
bf7bd7
-                    const char *secret)
bf7bd7
+                    uint8_t *secret,
bf7bd7
+                    size_t secret_len,
bf7bd7
+                    enum sss_sec_enctype enctype,
bf7bd7
+                    const char *datatype)
bf7bd7
 {
bf7bd7
     struct ldb_message *msg;
bf7bd7
-    const char *enctype = "masterkey";
bf7bd7
-    char *enc_secret;
bf7bd7
+    struct ldb_val enc_secret;
bf7bd7
     int ret;
bf7bd7
 
bf7bd7
     if (req == NULL || secret == NULL) {
bf7bd7
@@ -1064,7 +1142,7 @@ errno_t sss_sec_put(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = local_check_max_payload_size(req, strlen(secret));
bf7bd7
+    ret = local_check_max_payload_size(req, secret_len);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "local_check_max_payload_size failed [%d]: %s\n",
bf7bd7
@@ -1072,22 +1150,24 @@ errno_t sss_sec_put(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = local_encrypt(req->sctx, msg, secret, enctype, &enc_secret);
bf7bd7
+    ret = local_encrypt(req->sctx, msg, secret, secret_len, enctype,
bf7bd7
+                        &enc_secret.data, &enc_secret.length);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "local_encrypt failed [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_string(msg, "type", "simple");
bf7bd7
+    ret = ldb_msg_add_string(msg, SEC_ATTR_TYPE, datatype);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
-              "ldb_msg_add_string failed adding type:simple [%d]: %s\n",
bf7bd7
-              ret, sss_strerror(ret));
bf7bd7
+              "ldb_msg_add_string failed adding type:%s [%d]: %s\n",
bf7bd7
+              datatype, ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_string(msg, "enctype", enctype);
bf7bd7
+    ret = ldb_msg_add_string(msg, SEC_ATTR_ENCTYPE,
bf7bd7
+                             sss_sec_enctype_to_str(enctype));
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "ldb_msg_add_string failed adding enctype [%d]: %s\n",
bf7bd7
@@ -1095,7 +1175,7 @@ errno_t sss_sec_put(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_string(msg, "secret", enc_secret);
bf7bd7
+    ret = ldb_msg_add_value(msg, SEC_ATTR_SECRET, &enc_secret, NULL);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "ldb_msg_add_string failed adding secret [%d]: %s\n",
bf7bd7
@@ -1103,7 +1183,7 @@ errno_t sss_sec_put(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_fmt(msg, "creationTime", "%lu", time(NULL));
bf7bd7
+    ret = ldb_msg_add_fmt(msg, SEC_ATTR_CTIME, "%lu", time(NULL));
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "ldb_msg_add_string failed adding creationTime [%d]: %s\n",
bf7bd7
@@ -1132,11 +1212,13 @@ done:
bf7bd7
 }
bf7bd7
 
bf7bd7
 errno_t sss_sec_update(struct sss_sec_req *req,
bf7bd7
-                       const char *secret)
bf7bd7
+                       uint8_t *secret,
bf7bd7
+                       size_t secret_len,
bf7bd7
+                       enum sss_sec_enctype enctype,
bf7bd7
+                       const char *datatype)
bf7bd7
 {
bf7bd7
     struct ldb_message *msg;
bf7bd7
-    const char *enctype = "masterkey";
bf7bd7
-    char *enc_secret;
bf7bd7
+    struct ldb_val enc_secret;
bf7bd7
     int ret;
bf7bd7
 
bf7bd7
     if (req == NULL || secret == NULL) {
bf7bd7
@@ -1177,7 +1259,7 @@ errno_t sss_sec_update(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = local_check_max_payload_size(req, strlen(secret));
bf7bd7
+    ret = local_check_max_payload_size(req, secret_len);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "local_check_max_payload_size failed [%d]: %s\n",
bf7bd7
@@ -1185,15 +1267,49 @@ errno_t sss_sec_update(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = local_encrypt(req->sctx, msg, secret, enctype, &enc_secret);
bf7bd7
+    ret = local_encrypt(req->sctx, msg, secret, secret_len, enctype,
bf7bd7
+                        &enc_secret.data, &enc_secret.length);
bf7bd7
     if (ret != EOK) {
bf7bd7
         DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
               "local_encrypt failed [%d]: %s\n", ret, sss_strerror(ret));
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
+    ret = ldb_msg_add_empty(msg, SEC_ATTR_ENCTYPE, LDB_FLAG_MOD_REPLACE, NULL);
bf7bd7
+    if (ret != LDB_SUCCESS) {
bf7bd7
+        DEBUG(SSSDBG_MINOR_FAILURE,
bf7bd7
+              "ldb_msg_add_empty failed: [%s]\n", ldb_strerror(ret));
bf7bd7
+        ret = EIO;
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = ldb_msg_add_string(msg, SEC_ATTR_ENCTYPE,
bf7bd7
+                             sss_sec_enctype_to_str(enctype));
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
+              "ldb_msg_add_string failed adding enctype [%d]: %s\n",
bf7bd7
+              ret, sss_strerror(ret));
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = ldb_msg_add_empty(msg, SEC_ATTR_TYPE, LDB_FLAG_MOD_REPLACE, NULL);
bf7bd7
+    if (ret != LDB_SUCCESS) {
bf7bd7
+        DEBUG(SSSDBG_MINOR_FAILURE,
bf7bd7
+              "ldb_msg_add_empty failed: [%s]\n", ldb_strerror(ret));
bf7bd7
+        ret = EIO;
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = ldb_msg_add_string(msg, SEC_ATTR_TYPE, datatype);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        DEBUG(SSSDBG_OP_FAILURE,
bf7bd7
+              "ldb_msg_add_string failed adding type:%s [%d]: %s\n",
bf7bd7
+              datatype, ret, sss_strerror(ret));
bf7bd7
+        goto done;
bf7bd7
+    }
bf7bd7
+
bf7bd7
     /* FIXME - should we have a lastUpdate timestamp? */
bf7bd7
-    ret = ldb_msg_add_empty(msg, "secret", LDB_FLAG_MOD_REPLACE, NULL);
bf7bd7
+    ret = ldb_msg_add_empty(msg, SEC_ATTR_SECRET, LDB_FLAG_MOD_REPLACE, NULL);
bf7bd7
     if (ret != LDB_SUCCESS) {
bf7bd7
         DEBUG(SSSDBG_MINOR_FAILURE,
bf7bd7
               "ldb_msg_add_empty failed: [%s]\n", ldb_strerror(ret));
bf7bd7
@@ -1201,7 +1317,7 @@ errno_t sss_sec_update(struct sss_sec_req *req,
bf7bd7
         goto done;
bf7bd7
     }
bf7bd7
 
bf7bd7
-    ret = ldb_msg_add_string(msg, "secret", enc_secret);
bf7bd7
+    ret = ldb_msg_add_value(msg, SEC_ATTR_SECRET, &enc_secret, NULL);
bf7bd7
     if (ret != LDB_SUCCESS) {
bf7bd7
         DEBUG(SSSDBG_MINOR_FAILURE,
bf7bd7
               "ldb_msg_add_string failed: [%s]\n", ldb_strerror(ret));
bf7bd7
diff --git a/src/util/secrets/secrets.h b/src/util/secrets/secrets.h
bf7bd7
index 9cf397516..f79bfaa4b 100644
bf7bd7
--- a/src/util/secrets/secrets.h
bf7bd7
+++ b/src/util/secrets/secrets.h
bf7bd7
@@ -43,6 +43,12 @@
bf7bd7
 #define DEFAULT_SEC_KCM_MAX_UID_SECRETS  64
bf7bd7
 #define DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE 65536
bf7bd7
 
bf7bd7
+enum sss_sec_enctype {
bf7bd7
+    SSS_SEC_PLAINTEXT,
bf7bd7
+    SSS_SEC_MASTERKEY,
bf7bd7
+    SSS_SEC_ENCTYPE_SENTINEL
bf7bd7
+};
bf7bd7
+
bf7bd7
 struct sss_sec_ctx;
bf7bd7
 
bf7bd7
 struct sss_sec_req;
bf7bd7
@@ -88,13 +94,21 @@ errno_t sss_sec_list(TALLOC_CTX *mem_ctx,
bf7bd7
 
bf7bd7
 errno_t sss_sec_get(TALLOC_CTX *mem_ctx,
bf7bd7
                     struct sss_sec_req *req,
bf7bd7
-                    char **_secret);
bf7bd7
+                    uint8_t **_secret,
bf7bd7
+                    size_t *_secret_len,
bf7bd7
+                    char **_datatype);
bf7bd7
 
bf7bd7
 errno_t sss_sec_put(struct sss_sec_req *req,
bf7bd7
-                    const char *secret);
bf7bd7
+                    uint8_t *secret,
bf7bd7
+                    size_t secret_len,
bf7bd7
+                    enum sss_sec_enctype enctype,
bf7bd7
+                    const char *datatype);
bf7bd7
 
bf7bd7
 errno_t sss_sec_update(struct sss_sec_req *req,
bf7bd7
-                       const char *secret);
bf7bd7
+                       uint8_t *secret,
bf7bd7
+                       size_t secret_len,
bf7bd7
+                       enum sss_sec_enctype enctype,
bf7bd7
+                       const char *datatype);
bf7bd7
 
bf7bd7
 errno_t sss_sec_create_container(struct sss_sec_req *req);
bf7bd7
 
bf7bd7
diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c
bf7bd7
index 518713e4c..3056a7b0d 100644
bf7bd7
--- a/src/util/sss_iobuf.c
bf7bd7
+++ b/src/util/sss_iobuf.c
bf7bd7
@@ -66,6 +66,30 @@ struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx,
bf7bd7
     return iobuf;
bf7bd7
 }
bf7bd7
 
bf7bd7
+struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx,
bf7bd7
+                                       uint8_t *data,
bf7bd7
+                                       size_t size)
bf7bd7
+{
bf7bd7
+    struct sss_iobuf *iobuf;
bf7bd7
+
bf7bd7
+    iobuf = talloc_zero(mem_ctx, struct sss_iobuf);
bf7bd7
+    if (iobuf == NULL) {
bf7bd7
+        return NULL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    iobuf->data = talloc_steal(iobuf, data);
bf7bd7
+    iobuf->size = size;
bf7bd7
+    iobuf->capacity = size;
bf7bd7
+    iobuf->dp = 0;
bf7bd7
+
bf7bd7
+    return iobuf;
bf7bd7
+}
bf7bd7
+
bf7bd7
+void sss_iobuf_cursor_reset(struct sss_iobuf *iobuf)
bf7bd7
+{
bf7bd7
+    iobuf->dp = 0;
bf7bd7
+}
bf7bd7
+
bf7bd7
 size_t sss_iobuf_get_len(struct sss_iobuf *iobuf)
bf7bd7
 {
bf7bd7
     if (iobuf == NULL) {
bf7bd7
@@ -223,6 +247,109 @@ errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
bf7bd7
     return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
+errno_t sss_iobuf_read_varlen(TALLOC_CTX *mem_ctx,
bf7bd7
+                              struct sss_iobuf *iobuf,
bf7bd7
+                              uint8_t **_out,
bf7bd7
+                              size_t *_len)
bf7bd7
+{
bf7bd7
+    uint8_t *out;
bf7bd7
+    uint32_t len;
bf7bd7
+    size_t slen;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    if (iobuf == NULL || _out == NULL || _len == NULL) {
bf7bd7
+        return EINVAL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_uint32(iobuf, &len;;
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    if (len == 0) {
bf7bd7
+        *_out = NULL;
bf7bd7
+        *_len = 0;
bf7bd7
+        return EOK;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    out = talloc_array(mem_ctx, uint8_t, len);
bf7bd7
+    if (out == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    slen = len;
bf7bd7
+    ret = sss_iobuf_read_len(iobuf, slen, out);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        talloc_free(out);
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    *_out = out;
bf7bd7
+    *_len = slen;
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_write_varlen(struct sss_iobuf *iobuf,
bf7bd7
+                               uint8_t *data,
bf7bd7
+                               size_t len)
bf7bd7
+{
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    if (iobuf == NULL || (data == NULL && len != 0)) {
bf7bd7
+        return EINVAL;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_write_uint32(iobuf, len);
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    if (len == 0) {
bf7bd7
+        return EOK;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    return sss_iobuf_write_len(iobuf, data, len);
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_read_iobuf(TALLOC_CTX *mem_ctx,
bf7bd7
+                             struct sss_iobuf *iobuf,
bf7bd7
+                             struct sss_iobuf **_out)
bf7bd7
+{
bf7bd7
+    struct sss_iobuf *out;
bf7bd7
+    uint8_t *data;
bf7bd7
+    size_t len;
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    ret = sss_iobuf_read_varlen(NULL, iobuf, &data, &len;;
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    out = sss_iobuf_init_steal(mem_ctx, data, len);
bf7bd7
+    if (out == NULL) {
bf7bd7
+        return ENOMEM;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    *_out = out;
bf7bd7
+
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_write_iobuf(struct sss_iobuf *iobuf,
bf7bd7
+                              struct sss_iobuf *data)
bf7bd7
+{
bf7bd7
+    return sss_iobuf_write_varlen(iobuf, data->data, data->size);
bf7bd7
+}
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_read_uint8(struct sss_iobuf *iobuf,
bf7bd7
+                             uint8_t *_val)
bf7bd7
+{
bf7bd7
+    SAFEALIGN_COPY_UINT8_CHECK(_val, iobuf_ptr(iobuf),
bf7bd7
+                               iobuf->capacity, &iobuf->dp);
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
 errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf,
bf7bd7
                               uint32_t *_val)
bf7bd7
 {
bf7bd7
@@ -239,6 +366,20 @@ errno_t sss_iobuf_read_int32(struct sss_iobuf *iobuf,
bf7bd7
     return EOK;
bf7bd7
 }
bf7bd7
 
bf7bd7
+errno_t sss_iobuf_write_uint8(struct sss_iobuf *iobuf,
bf7bd7
+                              uint8_t val)
bf7bd7
+{
bf7bd7
+    errno_t ret;
bf7bd7
+
bf7bd7
+    ret = ensure_bytes(iobuf, sizeof(uint8_t));
bf7bd7
+    if (ret != EOK) {
bf7bd7
+        return ret;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    SAFEALIGN_SETMEM_UINT8(iobuf_ptr(iobuf), val, &iobuf->dp);
bf7bd7
+    return EOK;
bf7bd7
+}
bf7bd7
+
bf7bd7
 errno_t sss_iobuf_write_uint32(struct sss_iobuf *iobuf,
bf7bd7
                                uint32_t val)
bf7bd7
 {
bf7bd7
diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h
bf7bd7
index cc3dfd1e9..159fbc0b9 100644
bf7bd7
--- a/src/util/sss_iobuf.h
bf7bd7
+++ b/src/util/sss_iobuf.h
bf7bd7
@@ -50,6 +50,29 @@ struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx,
bf7bd7
                                           const uint8_t *data,
bf7bd7
                                           size_t size);
bf7bd7
 
bf7bd7
+/*
bf7bd7
+ * @brief Allocate an IO buffer with a fixed size, stealing input data.
bf7bd7
+ *
bf7bd7
+ * This function is useful for parsing an input buffer from an existing
bf7bd7
+ * buffer pointed to by data.
bf7bd7
+ *
bf7bd7
+ * The iobuf assumes ownership of the data buffer.
bf7bd7
+ *
bf7bd7
+ * @param[in]  mem_ctx      The talloc context that owns the iobuf
bf7bd7
+ * @param[in]  data         The data to initialize the IO buffer with.
bf7bd7
+ * @param[in]  size         The size of the data buffer
bf7bd7
+ *
bf7bd7
+ * @return The newly created buffer on success or NULL on an error.
bf7bd7
+ */
bf7bd7
+struct sss_iobuf *sss_iobuf_init_steal(TALLOC_CTX *mem_ctx,
bf7bd7
+                                       uint8_t *data,
bf7bd7
+                                       size_t size);
bf7bd7
+
bf7bd7
+/*
bf7bd7
+ * @brief Reset internal cursor of the IO buffer (seek to the start)
bf7bd7
+ */
bf7bd7
+void sss_iobuf_cursor_reset(struct sss_iobuf *iobuf);
bf7bd7
+
bf7bd7
 /*
bf7bd7
  * @brief Returns the number of bytes currently stored in the iobuf
bf7bd7
  *
bf7bd7
@@ -131,6 +154,28 @@ errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf,
bf7bd7
                             uint8_t *buf,
bf7bd7
                             size_t len);
bf7bd7
 
bf7bd7
+errno_t sss_iobuf_read_varlen(TALLOC_CTX *mem_ctx,
bf7bd7
+                              struct sss_iobuf *iobuf,
bf7bd7
+                              uint8_t **_out,
bf7bd7
+                              size_t *_len);
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_write_varlen(struct sss_iobuf *iobuf,
bf7bd7
+                               uint8_t *data,
bf7bd7
+                               size_t len);
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_read_iobuf(TALLOC_CTX *mem_ctx,
bf7bd7
+                             struct sss_iobuf *iobuf,
bf7bd7
+                             struct sss_iobuf **_out);
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_write_iobuf(struct sss_iobuf *iobuf,
bf7bd7
+                              struct sss_iobuf *data);
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_read_uint8(struct sss_iobuf *iobuf,
bf7bd7
+                             uint8_t *_val);
bf7bd7
+
bf7bd7
+errno_t sss_iobuf_write_uint8(struct sss_iobuf *iobuf,
bf7bd7
+                              uint8_t val);
bf7bd7
+
bf7bd7
 errno_t sss_iobuf_read_uint32(struct sss_iobuf *iobuf,
bf7bd7
                               uint32_t *_val);
bf7bd7
 
bf7bd7
@@ -148,4 +193,5 @@ errno_t sss_iobuf_read_stringz(struct sss_iobuf *iobuf,
bf7bd7
 
bf7bd7
 errno_t sss_iobuf_write_stringz(struct sss_iobuf *iobuf,
bf7bd7
                                 const char *str);
bf7bd7
+
bf7bd7
 #endif /* __SSS_IOBUF_H_ */
bf7bd7
diff --git a/src/util/sss_ptr_hash.c b/src/util/sss_ptr_hash.c
bf7bd7
index 6409236c7..e3805dac4 100644
bf7bd7
--- a/src/util/sss_ptr_hash.c
bf7bd7
+++ b/src/util/sss_ptr_hash.c
bf7bd7
@@ -54,6 +54,7 @@ struct sss_ptr_hash_value {
bf7bd7
     hash_table_t *table;
bf7bd7
     const char *key;
bf7bd7
     void *payload;
bf7bd7
+    bool delete_in_progress;
bf7bd7
 };
bf7bd7
 
bf7bd7
 static int
bf7bd7
@@ -61,12 +62,22 @@ sss_ptr_hash_value_destructor(struct sss_ptr_hash_value *value)
bf7bd7
 {
bf7bd7
     hash_key_t table_key;
bf7bd7
 
bf7bd7
+    /* Do not call hash_delete() if we got here from hash delete callback when
bf7bd7
+     * the callback calls talloc_free(payload) which frees the value. This
bf7bd7
+     * should not happen since talloc will avoid circular free but let's be
bf7bd7
+     * over protective here. */
bf7bd7
+    if (value->delete_in_progress) {
bf7bd7
+        return 0;
bf7bd7
+    }
bf7bd7
+
bf7bd7
+    value->delete_in_progress = true;
bf7bd7
     if (value->table && value->key) {
bf7bd7
         table_key.type = HASH_KEY_STRING;
bf7bd7
         table_key.str = discard_const_p(char, value->key);
bf7bd7
         if (hash_delete(value->table, &table_key) != HASH_SUCCESS) {
bf7bd7
             DEBUG(SSSDBG_CRIT_FAILURE,
bf7bd7
                   "failed to delete entry with key '%s'\n", value->key);
bf7bd7
+            value->delete_in_progress = false;
bf7bd7
         }
bf7bd7
     }
bf7bd7
 
bf7bd7
@@ -127,6 +138,15 @@ sss_ptr_hash_delete_cb(hash_entry_t *item,
bf7bd7
     callback_entry.key = item->key;
bf7bd7
     callback_entry.value.type = HASH_VALUE_PTR;
bf7bd7
     callback_entry.value.ptr = value->payload;
bf7bd7
+
bf7bd7
+    /* Delete the value in case this callback has been called directly
bf7bd7
+     * from dhash (overwriting existing entry) instead of hash_delete()
bf7bd7
+     * in value's destructor. */
bf7bd7
+    if (!value->delete_in_progress) {
bf7bd7
+        talloc_set_destructor(value, NULL);
bf7bd7
+        talloc_free(value);
bf7bd7
+    }
bf7bd7
+
bf7bd7
     /* Even if execution is already in the context of
bf7bd7
      * talloc_free(payload) -> talloc_free(value) -> ...
bf7bd7
      * there still might be legitimate reasons to execute callback.
bf7bd7
-- 
bf7bd7
2.21.3
bf7bd7