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

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