Blame SOURCES/0057-IPA-Always-re-fetch-the-keytab-from-the-IPA-server.patch

6cf099
From 366631d15723db68fdb5c47e18ff9253689648ab Mon Sep 17 00:00:00 2001
6cf099
From: Jakub Hrozek <jhrozek@redhat.com>
6cf099
Date: Fri, 24 Jul 2015 13:13:08 +0200
6cf099
Subject: [PATCH 57/57] IPA: Always re-fetch the keytab from the IPA server
6cf099
MIME-Version: 1.0
6cf099
Content-Type: text/plain; charset=UTF-8
6cf099
Content-Transfer-Encoding: 8bit
6cf099
6cf099
Even if a keytab for one-way trust exists, re-fetch the keytab again and
6cf099
try to use it. Fall back to the previous one if it exists.
6cf099
6cf099
This is in order to allow the admin to re-establish the trust keytabs
6cf099
with a simple sssd restart.
6cf099
6cf099
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
6cf099
---
6cf099
 Makefile.am                                   |   2 +
6cf099
 src/providers/ipa/ipa_subdomains.c            |   4 +-
6cf099
 src/providers/ipa/ipa_subdomains_server.c     |  83 +++++++++----
6cf099
 src/tests/cmocka/test_ipa_subdomains_server.c | 166 ++++++++++++++++++++++++--
6cf099
 4 files changed, 221 insertions(+), 34 deletions(-)
6cf099
6cf099
diff --git a/Makefile.am b/Makefile.am
6cf099
index 8b64317d6dce9a1ee8614916395b9afd9f11f382..ed107fd5dc76b768176a3d7236b0bf1c75f212bf 100644
6cf099
--- a/Makefile.am
6cf099
+++ b/Makefile.am
6cf099
@@ -2550,6 +2550,8 @@ test_ipa_subdom_server_LDFLAGS = \
6cf099
     -Wl,-wrap,krb5_kt_default \
6cf099
     -Wl,-wrap,execle \
6cf099
     -Wl,-wrap,execve \
6cf099
+    -Wl,-wrap,rename \
6cf099
+    -Wl,-wrap,sss_unique_filename \
6cf099
     $(NULL)
6cf099
 test_ipa_subdom_server_LDADD = \
6cf099
     $(PAM_LIBS) \
6cf099
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
6cf099
index cec8b3918b8f832e2c7376a867448fe876da6ffc..b2e2fec353f7b168d28a880cb0f1b6181abb1ccb 100644
6cf099
--- a/src/providers/ipa/ipa_subdomains.c
6cf099
+++ b/src/providers/ipa/ipa_subdomains.c
6cf099
@@ -264,7 +264,7 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
6cf099
         ret = get_idmap_data_from_range(r, domain_name, &name1, &sid1, &rid1,
6cf099
                                         &range1, &mapping1);
6cf099
         if (ret != EOK) {
6cf099
-            DEBUG(SSSDBG_OP_FAILURE, ("get_idmap_data_from_range failed.\n"));
6cf099
+            DEBUG(SSSDBG_OP_FAILURE, "get_idmap_data_from_range failed.\n");
6cf099
             goto done;
6cf099
         }
6cf099
         for (d = 0; d < c; d++) {
6cf099
@@ -272,7 +272,7 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
6cf099
                                             &sid2, &rid2, &range2, &mapping2);
6cf099
             if (ret != EOK) {
6cf099
                 DEBUG(SSSDBG_OP_FAILURE,
6cf099
-                      ("get_idmap_data_from_range failed.\n"));
6cf099
+                      "get_idmap_data_from_range failed.\n");
6cf099
                 goto done;
6cf099
             }
6cf099
 
6cf099
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
6cf099
index 4bfea61e6dd0a02f6b723a39f7ba236c914009b0..dfecab1bc362b5772379bae6d51f9cef8443f225 100644
6cf099
--- a/src/providers/ipa/ipa_subdomains_server.c
6cf099
+++ b/src/providers/ipa/ipa_subdomains_server.c
6cf099
@@ -445,6 +445,17 @@ static void ipa_getkeytab_exec(const char *ccache,
6cf099
         exit(1);
6cf099
     }
6cf099
 
6cf099
+    /* ipa-getkeytab cannot add keys to an empty file, let's unlink it and only
6cf099
+     * use the filename */
6cf099
+    ret = unlink(keytab_path);
6cf099
+    if (ret == -1) {
6cf099
+        ret = errno;
6cf099
+        DEBUG(SSSDBG_CRIT_FAILURE,
6cf099
+              "Failed to unlink the temporary ccname [%d][%s]\n",
6cf099
+              ret, sss_strerror(ret));
6cf099
+        exit(1);
6cf099
+    }
6cf099
+
6cf099
     errno = 0;
6cf099
     ret = execle(IPA_GETKEYTAB_PATH, IPA_GETKEYTAB_PATH,
6cf099
                  "-r", "-s", server, "-p", principal, "-k", keytab_path, NULL,
6cf099
@@ -561,6 +572,7 @@ struct ipa_server_trust_add_state {
6cf099
     uint32_t direction;
6cf099
     const char *forest;
6cf099
     const char *keytab;
6cf099
+    char *new_keytab;
6cf099
     const char *principal;
6cf099
     const char *forest_realm;
6cf099
     const char *ccache;
6cf099
@@ -660,21 +672,20 @@ static errno_t ipa_server_trust_add_1way(struct tevent_req *req)
6cf099
         return EIO;
6cf099
     }
6cf099
 
6cf099
-    ret = ipa_check_keytab(state->keytab,
6cf099
-                           state->id_ctx->server_mode->kt_owner_uid,
6cf099
-                           state->id_ctx->server_mode->kt_owner_gid);
6cf099
-    if (ret == EOK) {
6cf099
-        DEBUG(SSSDBG_TRACE_FUNC,
6cf099
-              "Keytab already present, can add the trust\n");
6cf099
-        return EOK;
6cf099
-    } else if (ret != ENOENT) {
6cf099
-        DEBUG(SSSDBG_OP_FAILURE,
6cf099
-              "Failed to check for keytab: %d\n", ret);
6cf099
+    state->new_keytab = talloc_asprintf(state, "%sXXXXXX", state->keytab);
6cf099
+    if (state->new_keytab == NULL) {
6cf099
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
6cf099
+        return ENOMEM;
6cf099
+    }
6cf099
+
6cf099
+    ret = sss_unique_filename(state, state->new_keytab);
6cf099
+    if (ret != EOK) {
6cf099
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create temporary keytab name\n");
6cf099
         return ret;
6cf099
     }
6cf099
 
6cf099
     DEBUG(SSSDBG_TRACE_FUNC,
6cf099
-          "No keytab for %s\n", state->subdom->name);
6cf099
+          "Will re-fetch keytab for %s\n", state->subdom->name);
6cf099
 
6cf099
     hostname = dp_opt_get_string(state->id_ctx->ipa_options->basic,
6cf099
                                  IPA_HOSTNAME);
6cf099
@@ -691,7 +702,7 @@ static errno_t ipa_server_trust_add_1way(struct tevent_req *req)
6cf099
                                 state->ccache,
6cf099
                                 hostname,
6cf099
                                 state->principal,
6cf099
-                                state->keytab);
6cf099
+                                state->new_keytab);
6cf099
     if (subreq == NULL) {
6cf099
         return ENOMEM;
6cf099
     }
6cf099
@@ -710,23 +721,49 @@ static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq)
6cf099
     ret = ipa_getkeytab_recv(subreq, NULL);
6cf099
     talloc_zfree(subreq);
6cf099
     if (ret != EOK) {
6cf099
-        DEBUG(SSSDBG_OP_FAILURE, "ipa_getkeytab_recv failed: %d\n", ret);
6cf099
-        tevent_req_error(req, ret);
6cf099
-        return;
6cf099
+        /* Do not fail here, but try to check and use the previous keytab,
6cf099
+         * if any */
6cf099
+        DEBUG(SSSDBG_MINOR_FAILURE, "ipa_getkeytab_recv failed: %d\n", ret);
6cf099
+    } else {
6cf099
+        DEBUG(SSSDBG_TRACE_FUNC,
6cf099
+              "Keytab successfully retrieved to %s\n", state->new_keytab);
6cf099
     }
6cf099
 
6cf099
-    DEBUG(SSSDBG_TRACE_FUNC,
6cf099
-          "Keytab successfully retrieved to %s\n", state->keytab);
6cf099
-
6cf099
-    ret = ipa_check_keytab(state->keytab,
6cf099
+    ret = ipa_check_keytab(state->new_keytab,
6cf099
                            state->id_ctx->server_mode->kt_owner_uid,
6cf099
                            state->id_ctx->server_mode->kt_owner_gid);
6cf099
-    if (ret != EOK) {
6cf099
-        DEBUG(SSSDBG_OP_FAILURE, "ipa_check_keytab failed: %d\n", ret);
6cf099
-        tevent_req_error(req, ret);
6cf099
-        return;
6cf099
+    if (ret == EOK) {
6cf099
+        ret = rename(state->new_keytab, state->keytab);
6cf099
+        if (ret == -1) {
6cf099
+            ret = errno;
6cf099
+            DEBUG(SSSDBG_CRIT_FAILURE,
6cf099
+                "rename failed [%d][%s].\n", ret, strerror(ret));
6cf099
+            tevent_req_error(req, ret);
6cf099
+            return;
6cf099
+        }
6cf099
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Keytab renamed to %s\n", state->keytab);
6cf099
+    } else if (ret != EOK) {
6cf099
+        DEBUG(SSSDBG_MINOR_FAILURE,
6cf099
+              "Trying to recover and use the previous keytab, if available\n");
6cf099
+        ret = ipa_check_keytab(state->keytab,
6cf099
+                               state->id_ctx->server_mode->kt_owner_uid,
6cf099
+                               state->id_ctx->server_mode->kt_owner_gid);
6cf099
+        if (ret == EOK) {
6cf099
+            DEBUG(SSSDBG_TRACE_FUNC,
6cf099
+                  "The previous keytab %s contains the expected principal\n",
6cf099
+                  state->keytab);
6cf099
+        } else {
6cf099
+            DEBUG(SSSDBG_OP_FAILURE,
6cf099
+                  "Cannot use the old keytab: %d\n", ret);
6cf099
+            /* Nothing we can do now */
6cf099
+            tevent_req_error(req, ret);
6cf099
+            return;
6cf099
+        }
6cf099
     }
6cf099
 
6cf099
+    DEBUG(SSSDBG_TRACE_FUNC,
6cf099
+          "Keytab %s contains the expected principals\n", state->new_keytab);
6cf099
+
6cf099
     ret = ipa_server_trust_add_step(req);
6cf099
     if (ret != EOK) {
6cf099
         DEBUG(SSSDBG_OP_FAILURE,
6cf099
diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
6cf099
index a4cb8c2b7538dc84b74e0227205b73780844b652..fb9bd80e299c05fa230599d442fa361ae757dcd3 100644
6cf099
--- a/src/tests/cmocka/test_ipa_subdomains_server.c
6cf099
+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
6cf099
@@ -66,33 +66,79 @@
6cf099
 #define ONEWAY_PRINC    DOM_FLAT"$"
6cf099
 #define ONEWAY_AUTHID   ONEWAY_PRINC"@"SUBDOM_REALM
6cf099
 
6cf099
+static bool global_rename_called;
6cf099
+
6cf099
 krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
6cf099
 {
6cf099
     return krb5_kt_resolve(context, KEYTAB_PATH, id);
6cf099
 }
6cf099
 
6cf099
-static void create_dummy_keytab(void)
6cf099
+static void create_dummy_keytab(const char *dummy_kt)
6cf099
 {
6cf099
     errno_t ret;
6cf099
 
6cf099
-    assert_non_null(ONEWAY_KEYTAB);
6cf099
+    assert_non_null(dummy_kt);
6cf099
     mock_keytab_with_contents(global_talloc_context,
6cf099
-                              ONEWAY_KEYTAB, ONEWAY_AUTHID);
6cf099
+                              dummy_kt, ONEWAY_AUTHID);
6cf099
 
6cf099
-    ret = access(ONEWAY_KEYTAB, R_OK);
6cf099
+    ret = access(dummy_kt, R_OK);
6cf099
     assert_int_equal(ret, 0);
6cf099
 }
6cf099
 
6cf099
+static int wrap_exec(void)
6cf099
+{
6cf099
+    const char *test_kt;
6cf099
+    const char *fail_creating_kt;
6cf099
+
6cf099
+    test_kt = getenv("TEST_KT_ENV");
6cf099
+    if (test_kt == NULL) {
6cf099
+        _exit(1);
6cf099
+    }
6cf099
+    unsetenv("TEST_KT_ENV");
6cf099
+
6cf099
+    fail_creating_kt = getenv("KT_CREATE_FAIL");
6cf099
+    if (fail_creating_kt != NULL) {
6cf099
+        _exit(1);
6cf099
+    }
6cf099
+
6cf099
+    create_dummy_keytab(test_kt);
6cf099
+    _exit(0);
6cf099
+
6cf099
+    return 1;   /* Should not happen */
6cf099
+}
6cf099
+
6cf099
 int __wrap_execle(const char *path, const char *arg, ...)
6cf099
 {
6cf099
-    create_dummy_keytab();
6cf099
-    _exit(0);
6cf099
+    return wrap_exec();
6cf099
 }
6cf099
 
6cf099
 int __wrap_execve(const char *path, const char *arg, ...)
6cf099
 {
6cf099
-    create_dummy_keytab();
6cf099
-    _exit(0);
6cf099
+    return wrap_exec();
6cf099
+}
6cf099
+
6cf099
+errno_t __real_sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl);
6cf099
+
6cf099
+errno_t __wrap_sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl)
6cf099
+{
6cf099
+    int ret;
6cf099
+    int sret;
6cf099
+
6cf099
+    ret = __real_sss_unique_filename(owner, path_tmpl);
6cf099
+    if (ret == EOK) {
6cf099
+
6cf099
+        sret = setenv("TEST_KT_ENV", path_tmpl, 1);
6cf099
+        assert_int_equal(sret, 0);
6cf099
+    }
6cf099
+    return ret;
6cf099
+}
6cf099
+
6cf099
+int __real_rename(const char *old, const char *new);
6cf099
+
6cf099
+int __wrap_rename(const char *old, const char *new)
6cf099
+{
6cf099
+    global_rename_called = true;
6cf099
+    return __real_rename(old, new);
6cf099
 }
6cf099
 
6cf099
 struct trust_test_ctx {
6cf099
@@ -100,6 +146,7 @@ struct trust_test_ctx {
6cf099
     struct be_ctx *be_ctx;
6cf099
 
6cf099
     struct ipa_id_ctx *ipa_ctx;
6cf099
+    bool expect_rename;
6cf099
 };
6cf099
 
6cf099
 static struct ipa_id_ctx *mock_ipa_ctx(TALLOC_CTX *mem_ctx,
6cf099
@@ -244,6 +291,8 @@ static int test_ipa_server_create_trusts_setup(void **state)
6cf099
 
6cf099
     mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC);
6cf099
 
6cf099
+    global_rename_called = false;
6cf099
+
6cf099
     *state = test_ctx;
6cf099
     return 0;
6cf099
 }
6cf099
@@ -260,6 +309,11 @@ static int test_ipa_server_create_trusts_teardown(void **state)
6cf099
     unlink(ONEWAY_KEYTAB);
6cf099
     /* Ignore failures */
6cf099
 
6cf099
+    /* If a test needs this variable, it should be set again in
6cf099
+     * each test
6cf099
+     */
6cf099
+    unsetenv("KT_CREATE_FAIL");
6cf099
+
6cf099
     talloc_free(test_ctx);
6cf099
     return 0;
6cf099
 }
6cf099
@@ -612,6 +666,8 @@ static void test_ipa_server_create_oneway(void **state)
6cf099
 
6cf099
     assert_null(test_ctx->ipa_ctx->server_mode->trusts);
6cf099
 
6cf099
+    test_ctx->expect_rename = true;
6cf099
+
6cf099
     req = ipa_server_create_trusts_send(test_ctx,
6cf099
                                         test_ctx->tctx->ev,
6cf099
                                         test_ctx->be_ctx,
6cf099
@@ -635,6 +691,8 @@ static void test_ipa_server_create_trusts_oneway(struct tevent_req *req)
6cf099
     talloc_zfree(req);
6cf099
     assert_int_equal(ret, EOK);
6cf099
 
6cf099
+    assert_true(test_ctx->expect_rename == global_rename_called);
6cf099
+
6cf099
     ret = access(ONEWAY_KEYTAB, R_OK);
6cf099
     assert_int_equal(ret, 0);
6cf099
 
6cf099
@@ -674,10 +732,46 @@ static void test_ipa_server_create_oneway_kt_exists(void **state)
6cf099
 
6cf099
     add_test_1way_subdomains(test_ctx);
6cf099
 
6cf099
-    create_dummy_keytab();
6cf099
+    create_dummy_keytab(ONEWAY_KEYTAB);
6cf099
     ret = access(ONEWAY_KEYTAB, R_OK);
6cf099
     assert_int_equal(ret, 0);
6cf099
 
6cf099
+    test_ctx->expect_rename = true;
6cf099
+
6cf099
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts);
6cf099
+
6cf099
+    req = ipa_server_create_trusts_send(test_ctx,
6cf099
+                                        test_ctx->tctx->ev,
6cf099
+                                        test_ctx->be_ctx,
6cf099
+                                        test_ctx->ipa_ctx,
6cf099
+                                        test_ctx->be_ctx->domain);
6cf099
+    assert_non_null(req);
6cf099
+
6cf099
+    tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, test_ctx);
6cf099
+
6cf099
+    ret = test_ev_loop(test_ctx->tctx);
6cf099
+    assert_int_equal(ret, ERR_OK);
6cf099
+}
6cf099
+
6cf099
+/* Test scenario where a keytab already exists, but refresh fails. In this case,
6cf099
+ * sssd should attempt to reuse the previous keytab
6cf099
+ */
6cf099
+static void test_ipa_server_create_oneway_kt_refresh_fallback(void **state)
6cf099
+{
6cf099
+    struct trust_test_ctx *test_ctx =
6cf099
+        talloc_get_type(*state, struct trust_test_ctx);
6cf099
+    struct tevent_req *req;
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    add_test_1way_subdomains(test_ctx);
6cf099
+
6cf099
+    create_dummy_keytab(ONEWAY_KEYTAB);
6cf099
+    ret = access(ONEWAY_KEYTAB, R_OK);
6cf099
+    assert_int_equal(ret, 0);
6cf099
+
6cf099
+    setenv("KT_CREATE_FAIL", "1", 1);
6cf099
+    test_ctx->expect_rename = false;
6cf099
+
6cf099
     assert_null(test_ctx->ipa_ctx->server_mode->trusts);
6cf099
 
6cf099
     req = ipa_server_create_trusts_send(test_ctx,
6cf099
@@ -693,6 +787,54 @@ static void test_ipa_server_create_oneway_kt_exists(void **state)
6cf099
     assert_int_equal(ret, ERR_OK);
6cf099
 }
6cf099
 
6cf099
+/* Tests case where there's no keytab and retrieving fails. Just fail the
6cf099
+ * request in that case
6cf099
+ */
6cf099
+static void test_ipa_server_create_trusts_oneway_fail(struct tevent_req *req);
6cf099
+
6cf099
+static void test_ipa_server_create_oneway_kt_refresh_fail(void **state)
6cf099
+{
6cf099
+    struct trust_test_ctx *test_ctx =
6cf099
+        talloc_get_type(*state, struct trust_test_ctx);
6cf099
+    struct tevent_req *req;
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    add_test_1way_subdomains(test_ctx);
6cf099
+
6cf099
+    setenv("KT_CREATE_FAIL", "1", 1);
6cf099
+    test_ctx->expect_rename = false;
6cf099
+
6cf099
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts);
6cf099
+
6cf099
+    req = ipa_server_create_trusts_send(test_ctx,
6cf099
+                                        test_ctx->tctx->ev,
6cf099
+                                        test_ctx->be_ctx,
6cf099
+                                        test_ctx->ipa_ctx,
6cf099
+                                        test_ctx->be_ctx->domain);
6cf099
+    assert_non_null(req);
6cf099
+
6cf099
+    tevent_req_set_callback(req,
6cf099
+                            test_ipa_server_create_trusts_oneway_fail,
6cf099
+                            test_ctx);
6cf099
+
6cf099
+    ret = test_ev_loop(test_ctx->tctx);
6cf099
+    assert_int_equal(ret, ERR_OK);
6cf099
+}
6cf099
+
6cf099
+static void test_ipa_server_create_trusts_oneway_fail(struct tevent_req *req)
6cf099
+{
6cf099
+    struct trust_test_ctx *test_ctx = \
6cf099
+        tevent_req_callback_data(req, struct trust_test_ctx);
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    ret = ipa_server_create_trusts_recv(req);
6cf099
+    assert_int_not_equal(ret, EOK);
6cf099
+
6cf099
+    assert_true(test_ctx->expect_rename == global_rename_called);
6cf099
+
6cf099
+    test_ev_done(test_ctx->tctx, EOK);
6cf099
+}
6cf099
+
6cf099
 static void test_ipa_server_trust_oneway_init(void **state)
6cf099
 {
6cf099
     struct trust_test_ctx *test_ctx =
6cf099
@@ -749,6 +891,12 @@ int main(int argc, const char *argv[])
6cf099
         cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_exists,
6cf099
                                         test_ipa_server_create_trusts_setup,
6cf099
                                         test_ipa_server_create_trusts_teardown),
6cf099
+        cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_refresh_fallback,
6cf099
+                                        test_ipa_server_create_trusts_setup,
6cf099
+                                        test_ipa_server_create_trusts_teardown),
6cf099
+        cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_refresh_fail,
6cf099
+                                        test_ipa_server_create_trusts_setup,
6cf099
+                                        test_ipa_server_create_trusts_teardown),
6cf099
         cmocka_unit_test_setup_teardown(test_ipa_server_trust_oneway_init,
6cf099
                                         test_ipa_server_create_trusts_setup,
6cf099
                                         test_ipa_server_create_trusts_teardown),
6cf099
-- 
6cf099
2.4.3
6cf099