Blob Blame History Raw
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