From 7c441a13215dfd87f9facdaf5f6bcc19a25ec472 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Wed, 16 Jan 2019 13:02:01 +0100
Subject: [PATCH 97/99] KCM: Allow representing ccaches with a NULL principal
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Related:
https://pagure.io/SSSD/sssd/issue/3873
We need to make it possible to create an internal ccache representation
without passing in a principal. The principal is only assigned to the
ccache with krb5_cc_initialize(), but some programs like openssh use the
following sequence of calls:
krb5_cc_new_unique
krb5_cc_switch
krb5_cc_initialize
Reviewed-by: Michal Židek <mzidek@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
---
src/responder/kcm/kcmsrv_ccache.c | 18 +++--
src/responder/kcm/kcmsrv_ccache_json.c | 79 ++++++++++++++++---
src/tests/cmocka/test_kcm_json_marshalling.c | 83 ++++++++++++++++++--
3 files changed, 153 insertions(+), 27 deletions(-)
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
index af2bcf8bb..e7800662a 100644
--- a/src/responder/kcm/kcmsrv_ccache.c
+++ b/src/responder/kcm/kcmsrv_ccache.c
@@ -68,14 +68,16 @@ errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
uuid_generate(cc->uuid);
- kret = krb5_copy_principal(k5c, princ, &cc->client);
- if (kret != 0) {
- const char *err_msg = sss_krb5_get_error_message(k5c, kret);
- DEBUG(SSSDBG_OP_FAILURE,
- "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg);
- sss_krb5_free_error_message(k5c, err_msg);
- ret = ERR_INTERNAL;
- goto done;
+ if (princ) {
+ kret = krb5_copy_principal(k5c, princ, &cc->client);
+ if (kret != 0) {
+ const char *err_msg = sss_krb5_get_error_message(k5c, kret);
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg);
+ sss_krb5_free_error_message(k5c, err_msg);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
}
cc->owner.uid = cli_creds_get_uid(owner);
diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c
index 6341530ee..72e24c430 100644
--- a/src/responder/kcm/kcmsrv_ccache_json.c
+++ b/src/responder/kcm/kcmsrv_ccache_json.c
@@ -229,6 +229,20 @@ static json_t *princ_to_json(TALLOC_CTX *mem_ctx,
json_error_t error;
char *str_realm_data;
+ if (princ == NULL) {
+ jprinc = json_pack_ex(&error,
+ JSON_STRICT,
+ "{}");
+ if (jprinc == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to pack JSON princ structure on line %d: %s\n",
+ error.line, error.text);
+ return NULL;
+ }
+
+ return jprinc;
+ }
+
components = princ_data_to_json(mem_ctx, princ);
if (components == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
@@ -587,13 +601,12 @@ static errno_t json_array_to_krb5_data(TALLOC_CTX *mem_ctx,
return EOK;
}
-static errno_t json_to_princ(TALLOC_CTX *mem_ctx,
- json_t *js_princ,
- krb5_principal *_princ)
+static errno_t json_to_nonempty_princ(TALLOC_CTX *mem_ctx,
+ json_t *js_princ,
+ krb5_principal *_princ)
{
errno_t ret;
json_t *components = NULL;
- int ok;
krb5_principal princ = NULL;
TALLOC_CTX *tmp_ctx = NULL;
char *realm_str;
@@ -601,13 +614,6 @@ static errno_t json_to_princ(TALLOC_CTX *mem_ctx,
size_t comp_count;
json_error_t error;
- ok = json_is_object(js_princ);
- if (!ok) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Json principal is not an object.\n");
- ret = ERR_JSON_DECODING;
- goto done;
- }
-
tmp_ctx = talloc_new(mem_ctx);
if (tmp_ctx == NULL) {
ret = ENOMEM;
@@ -684,6 +690,57 @@ done:
return ret;
}
+static bool is_nonempty_principal(json_t *js_princ)
+{
+ errno_t ret;
+ json_error_t error;
+
+ ret = json_unpack_ex(js_princ,
+ &error,
+ JSON_VALIDATE_ONLY,
+ "{s:i, s:s, s:o}",
+ "type",
+ "realm",
+ "components");
+
+ return ret == 0 ? true : false;
+}
+
+static bool is_empty_principal(json_t *js_princ)
+{
+ errno_t ret;
+ json_error_t error;
+
+ ret = json_unpack_ex(js_princ,
+ &error,
+ JSON_VALIDATE_ONLY,
+ "{}");
+
+ return ret == 0 ? true : false;
+}
+
+static errno_t json_to_princ(TALLOC_CTX *mem_ctx,
+ json_t *js_princ,
+ krb5_principal *_princ)
+{
+ int ok;
+
+ ok = json_is_object(js_princ);
+ if (!ok) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Json principal is not an object.\n");
+ return ERR_JSON_DECODING;
+ }
+
+ if (is_nonempty_principal(js_princ)) {
+ return json_to_nonempty_princ(mem_ctx, js_princ, _princ);
+ } else if (is_empty_principal(js_princ)) {
+ *_princ = NULL;
+ return EOK;
+ }
+
+ return ERR_JSON_DECODING;
+}
+
static errno_t json_elem_to_cred(TALLOC_CTX *mem_ctx,
json_t *element,
struct kcm_cred **_crd)
diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c
index 05d472499..48ee92bd6 100644
--- a/src/tests/cmocka/test_kcm_json_marshalling.c
+++ b/src/tests/cmocka/test_kcm_json_marshalling.c
@@ -116,14 +116,22 @@ static void assert_cc_princ_equal(struct kcm_ccache *cc1,
p1 = kcm_cc_get_client_principal(cc1);
p2 = kcm_cc_get_client_principal(cc2);
- kerr = krb5_unparse_name(NULL, p1, &name1);
- assert_int_equal(kerr, 0);
- kerr = krb5_unparse_name(NULL, p2, &name2);
- assert_int_equal(kerr, 0);
-
- assert_string_equal(name1, name2);
- krb5_free_unparsed_name(NULL, name1);
- krb5_free_unparsed_name(NULL, name2);
+ if (p1 != NULL && p2 != NULL) {
+ kerr = krb5_unparse_name(NULL, p1, &name1);
+ assert_int_equal(kerr, 0);
+ kerr = krb5_unparse_name(NULL, p2, &name2);
+ assert_int_equal(kerr, 0);
+
+ assert_string_equal(name1, name2);
+ krb5_free_unparsed_name(NULL, name1);
+ krb5_free_unparsed_name(NULL, name2);
+ } else {
+ /* Either both principals must be NULL or both
+ * non-NULL and represent the same principals
+ */
+ assert_null(p1);
+ assert_null(p2);
+ }
}
static void assert_cc_offset_equal(struct kcm_ccache *cc1,
@@ -206,6 +214,62 @@ static void test_kcm_ccache_marshall_unmarshall(void **state)
assert_int_equal(ret, EINVAL);
}
+static void test_kcm_ccache_no_princ(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(test_ctx,
+ cc,
+ &owner,
+ &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);
+
+ ret = sec_kv_to_ccache(test_ctx,
+ key,
+ (const char *) data,
+ &owner,
+ &cc2);
+ assert_int_equal(ret, EOK);
+
+ assert_cc_equal(cc, cc2);
+}
+
void test_sec_key_get_uuid(void **state)
{
errno_t ret;
@@ -279,6 +343,9 @@ int main(int argc, const char *argv[])
cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall,
setup_kcm_marshalling,
teardown_kcm_marshalling),
+ cmocka_unit_test_setup_teardown(test_kcm_ccache_no_princ,
+ setup_kcm_marshalling,
+ teardown_kcm_marshalling),
cmocka_unit_test(test_sec_key_get_uuid),
cmocka_unit_test(test_sec_key_get_name),
cmocka_unit_test(test_sec_key_match_name),
--
2.19.1