From 7c441a13215dfd87f9facdaf5f6bcc19a25ec472 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Feb 08 2019 14:02:52 +0000 Subject: KCM: Allow representing ccaches with a NULL principal 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 Reviewed-by: Simo Sorce --- diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c index af2bcf8..e780066 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 6341530..72e24c4 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 05d4724..48ee92b 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),