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