Hi,

the attached patches implement fetching the keytab for one-way trusts on
each sssd restart. This is in order for admin to be able to call service
sssd restart and have fresh keytabs in case the trust was re-established
in the meantime.

Even though retrieving the keytabs is quite expensive operation,
restarting the sssd instance on the IPA server should be quite rare.
>From c18113d310ef7a0f988739b8e031cc5e6c36a198 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 24 Jul 2015 13:12:43 +0200
Subject: [PATCH 1/2] UTIL: New utility function sss_unique_filename() to
 create a random filename

Instead of copying the same code using mkstemp over and over, provide a
common version.
---
 src/providers/ad/ad_gpo_child.c  |   8 +--
 src/providers/krb5/krb5_child.c  |  37 ++++++--------
 src/providers/krb5/krb5_common.c |   8 +--
 src/providers/ldap/ldap_child.c  |  15 ++----
 src/responder/ssh/sshsrv_cmd.c   |   6 +--
 src/tests/cmocka/test_utils.c    | 103 +++++++++++++++++++++++++++++++++++++++
 src/util/util.c                  |  50 +++++++++++++++++++
 src/util/util.h                  |  13 +++++
 8 files changed, 188 insertions(+), 52 deletions(-)

diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c
index 
03951af041463000d51695fe1a86609ed6c7c1c2..e0e0c068ff496bf28ec9fa4ab7b2105238719217
 100644
--- a/src/providers/ad/ad_gpo_child.c
+++ b/src/providers/ad/ad_gpo_child.c
@@ -276,7 +276,6 @@ static errno_t gpo_cache_store_file(const char *smb_path,
     int fd = -1;
     char *tmp_name = NULL;
     ssize_t written;
-    mode_t old_umask;
     char *filename = NULL;
     char *smb_path_with_suffix = NULL;
     TALLOC_CTX *tmp_ctx = NULL;
@@ -318,13 +317,10 @@ static errno_t gpo_cache_store_file(const char *smb_path,
         goto done;
     }
 
-    old_umask = umask(077);
-    fd = mkstemp(tmp_name);
-    umask(old_umask);
+    fd = sss_unique_file(tmp_name, &ret);
     if (fd == -1) {
-        ret = errno;
         DEBUG(SSSDBG_CRIT_FAILURE,
-              "mkstemp failed [%d][%s].\n", ret, strerror(ret));
+              "sss_unique_file failed [%d][%s].\n", ret, strerror(ret));
         goto done;
     }
 
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 
2c5e446a0e0c3f55261a39d8d3f3bc09aded3cb9..a37f912b64ec09f7c7f232ad6efd64926bfa8e74
 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -668,11 +668,8 @@ done:
 
 static errno_t handle_randomized(char *in)
 {
-    size_t ccname_len;
     char *ccname = NULL;
     int ret;
-    int fd;
-    mode_t old_umask;
 
     /* We only treat the FILE type case in a special way due to the history
      * of storing FILE type ccache in /tmp and associated security issues */
@@ -684,26 +681,20 @@ static errno_t handle_randomized(char *in)
         return EOK;
     }
 
-    ccname_len = strlen(ccname);
-    if (ccname_len >= 6 && strcmp(ccname + (ccname_len - 6), "XXXXXX") == 0) {
-        /* NOTE: this call is only used to create a unique name, as later
-         * krb5_cc_initialize() will unlink and recreate the file.
-         * This is ok because this part of the code is called with
-         * privileges already dropped when handling user ccache, or the ccache
-         * is stored in a private directory. So we do not have huge issues if
-         * something races, we mostly care only about not accidentally use
-         * an existing name and thus failing in the process of saving the
-         * cache. Malicious races can only be avoided by libkrb5 itself. */
-        old_umask = umask(077);
-        fd = mkstemp(ccname);
-        umask(old_umask);
-        if (fd == -1) {
-            ret = errno;
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "mkstemp(\"%s\") failed [%d]: %s!\n",
-                  ccname, ret, strerror(ret));
-            return ret;
-        }
+    /* NOTE: this call is only used to create a unique name, as later
+     * krb5_cc_initialize() will unlink and recreate the file.
+     * This is ok because this part of the code is called with
+     * privileges already dropped when handling user ccache, or the ccache
+     * is stored in a private directory. So we do not have huge issues if
+     * something races, we mostly care only about not accidentally use
+     * an existing name and thus failing in the process of saving the
+     * cache. Malicious races can only be avoided by libkrb5 itself. */
+    ret = sss_unique_filename(ccname);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                "mkstemp(\"%s\") failed [%d]: %s!\n",
+                ccname, ret, strerror(ret));
+        return ret;
     }
 
     return EOK;
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 
81d4048b63dba98706bbef1936df7f10f79e1ae5..9d3f1a20193285b49f7a55fef5deb806780cc662
 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -419,7 +419,6 @@ errno_t write_krb5info_file(const char *realm, const char 
*server,
     const char *name_tmpl = NULL;
     size_t server_len;
     ssize_t written;
-    mode_t old_umask;
 
     if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' ||
         service == NULL || *service == '\0') {
@@ -459,13 +458,10 @@ errno_t write_krb5info_file(const char *realm, const char 
*server,
         goto done;
     }
 
-    old_umask = umask(077);
-    fd = mkstemp(tmp_name);
-    umask(old_umask);
+    fd = sss_unique_file(tmp_name, &ret);
     if (fd == -1) {
-        ret = errno;
         DEBUG(SSSDBG_CRIT_FAILURE,
-              "mkstemp failed [%d][%s].\n", ret, strerror(ret));
+              "sss_unique_file failed [%d][%s].\n", ret, strerror(ret));
         goto done;
     }
 
diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
index 
82481d6e75c86f7be49625a669691b235589d9a7..338345d89fa0484eaa8fcf99f1e0f10eb61a1d91
 100644
--- a/src/providers/ldap/ldap_child.c
+++ b/src/providers/ldap/ldap_child.c
@@ -254,7 +254,6 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX 
*memctx,
                                                const char **ccname_out,
                                                time_t *expire_time_out)
 {
-    int fd;
     char *ccname;
     char *ccname_dummy;
     char *realm_name = NULL;
@@ -274,7 +273,6 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX 
*memctx,
     TALLOC_CTX *tmp_ctx;
     char *ccname_file_dummy = NULL;
     char *ccname_file;
-    mode_t old_umask;
 
     tmp_ctx = talloc_new(memctx);
     if (tmp_ctx == NULL) {
@@ -408,21 +406,14 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX 
*memctx,
         goto done;
     }
 
-    old_umask = umask(077);
-    fd = mkstemp(ccname_file_dummy);
-    umask(old_umask);
-    if (fd == -1) {
-        ret = errno;
+    ret = sss_unique_filename(ccname_file_dummy);
+    if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
-              "mkstemp failed: %s:[%d].\n",
+              "sss_unique_filename failed: %s:[%d].\n",
               strerror(ret), ret);
         krberr = KRB5KRB_ERR_GENERIC;
         goto done;
     }
-    /* We only care about creating a unique file name here, we don't
-     * need the fd
-     */
-    close(fd);
 
     krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc,
                                         keytab, 0, NULL, &options);
diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c
index 
4833587910cade32ecb0b5f65b417d58a498b01e..8c650d04030ed6984f064448320df2cddc66f6e8
 100644
--- a/src/responder/ssh/sshsrv_cmd.c
+++ b/src/responder/ssh/sshsrv_cmd.c
@@ -555,7 +555,6 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx 
*cmd_ctx)
     char *filename = NULL;
     char *entstr;
     ssize_t wret;
-    mode_t old_mask;
 
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) {
@@ -578,12 +577,9 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx 
*cmd_ctx)
         goto done;
     }
 
-    old_mask = umask(0133);
-    fd = mkstemp(filename);
-    umask(old_mask);
+    fd = sss_unique_file_ex(filename, 0133, &ret);
     if (fd == -1) {
         filename = NULL;
-        ret = errno;
         goto done;
     }
 
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index 
d3d00feda0bdd4048519f90ba48ae9d1042a95a1..94aa7675f3024d573fe75cdfedca37f6800b0513
 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -1212,6 +1212,102 @@ void test_fix_domain_in_name_list(void **state)
     talloc_free(dom);
 }
 
+struct unique_file_test_ctx {
+    char *filename;
+};
+
+static int unique_file_test_setup(void **state)
+{
+    struct unique_file_test_ctx *test_ctx;
+
+    assert_true(leak_check_setup());
+    check_leaks_push(global_talloc_context);
+
+    test_ctx = talloc_zero(global_talloc_context, struct unique_file_test_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->filename = talloc_strdup(test_ctx, "test_unique_file_XXXXXX");
+    assert_non_null(test_ctx);
+
+    *state = test_ctx;
+    return 0;
+}
+
+static int unique_file_test_teardown(void **state)
+{
+    struct unique_file_test_ctx *test_ctx;
+    errno_t ret;
+
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
+
+    errno = 0;
+    ret = unlink(test_ctx->filename);
+    if (ret != 0 && errno != ENOENT) {
+        fail();
+    }
+
+    talloc_free(test_ctx);
+    assert_true(check_leaks_pop(global_talloc_context) == true);
+    assert_true(leak_check_teardown());
+    return 0;
+}
+
+static void test_sss_unique_file(void **state)
+{
+    struct unique_file_test_ctx *test_ctx;
+    int fd;
+    errno_t ret;
+    struct stat sb;
+
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
+
+    fd = sss_unique_file(test_ctx->filename, &ret);
+    assert_int_not_equal(fd, -1);
+    assert_int_equal(ret, EOK);
+
+    ret = check_fd(fd, geteuid(), getegid(),
+                   (S_IRUSR | S_IWUSR | S_IFREG), 0, &sb);
+    close(fd);
+    assert_int_equal(ret, EOK);
+}
+
+static void test_sss_unique_file_neg(void **state)
+{
+    int fd;
+    errno_t ret;
+
+    fd = sss_unique_file(discard_const("badpattern"), &ret);
+    assert_int_equal(fd, -1);
+    assert_int_equal(ret, EINVAL);
+}
+
+static void test_sss_unique_filename(void **state)
+{
+    struct unique_file_test_ctx *test_ctx;
+    int fd;
+    errno_t ret;
+    char *tmp_filename;
+
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
+
+    tmp_filename = talloc_strdup(test_ctx, test_ctx->filename);
+    assert_non_null(tmp_filename);
+
+    ret = sss_unique_filename(test_ctx->filename);
+    assert_int_equal(ret, EOK);
+
+    assert_int_equal(strncmp(test_ctx->filename,
+                             tmp_filename,
+                             strlen(tmp_filename) - sizeof("XXXXXX")),
+                     0);
+    talloc_free(tmp_filename);
+
+    ret = check_and_open_readonly(test_ctx->filename, &fd,
+                                  geteuid(), getegid(),
+                                  (S_IRUSR | S_IWUSR | S_IFREG), 0);
+    close(fd);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -1275,6 +1371,13 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_fix_domain_in_name_list,
                                         confdb_test_setup,
                                         confdb_test_teardown),
+        cmocka_unit_test_setup_teardown(test_sss_unique_file,
+                                        unique_file_test_setup,
+                                        unique_file_test_teardown),
+        cmocka_unit_test(test_sss_unique_file_neg),
+        cmocka_unit_test_setup_teardown(test_sss_unique_filename,
+                                        unique_file_test_setup,
+                                        unique_file_test_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
diff --git a/src/util/util.c b/src/util/util.c
index 
782cd026b7928e607a8980fb5f333c794feb5b1a..d1210861b61365651fa949bd9b79284d6b19e2a4
 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -979,3 +979,53 @@ errno_t sss_utc_to_time_t(const char *str, const char 
*format, time_t *_unix_tim
     *_unix_time = ut;
     return EOK;
 }
+
+int sss_unique_file_ex(char *path_tmpl, mode_t file_umask, errno_t *_err)
+{
+    size_t tmpl_len;
+    errno_t ret;
+    int fd = -1;
+    mode_t old_umask;
+
+    tmpl_len = strlen(path_tmpl);
+    if (tmpl_len < 6 || strcmp(path_tmpl + (tmpl_len - 6), "XXXXXX") != 0) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Template too short or doesn't end with XXXXXX!\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    old_umask = umask(file_umask);
+    fd = mkstemp(path_tmpl);
+    umask(old_umask);
+    if (fd == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE,
+                "mkstemp(\"%s\") failed [%d]: %s!\n",
+                path_tmpl, ret, strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    if (_err) {
+        *_err = ret;
+    }
+    return fd;
+}
+
+int sss_unique_file(char *path_tmpl, errno_t *_err)
+{
+    return sss_unique_file_ex(path_tmpl, 077, _err);
+}
+
+errno_t sss_unique_filename(char *path_tmpl)
+{
+    int fd;
+    errno_t ret;
+
+    fd = sss_unique_file(path_tmpl, &ret);
+    /* We only care about a unique file name */
+    close(fd);
+    return ret;
+}
diff --git a/src/util/util.h b/src/util/util.h
index 
94a3ddea839f0998cb7796f1d2fe13f743de3aaf..ab72eed85fb9457f50acce9ba009425c1b04e59d
 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -658,4 +658,17 @@ int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name,
 /* convert time from generalized form to unix time */
 errno_t sss_utc_to_time_t(const char *str, const char *format, time_t 
*unix_time);
 
+/* Creates a unique file using mkstemp with provided umask. The template
+ * must end with XXXXXX. Returns the fd, sets _err to an errno value on error.
+ *
+ * Prefer using sss_unique_file() as it uses a secure umask internally.
+ */
+int sss_unique_file_ex(char *path_tmpl, mode_t file_umask, errno_t *_err);
+int sss_unique_file(char *path_tmpl, errno_t *_err);
+
+/* Creates a unique filename using mkstemp with secure umask. The template
+ * must end with XXXXXX
+ */
+int sss_unique_filename(char *path_tmpl);
+
 #endif /* __SSSD_UTIL_H__ */
-- 
2.4.3

>From bb67c34268196f78bc8e0258325cf349fdc97271 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 24 Jul 2015 13:13:08 +0200
Subject: [PATCH 2/2] Always re-fetch the keytab from the IPA server

Even if a keytab for one-way trust exists, re-fetch the keytab again and
try to use it. Fall back to the previous one if it exists.

This is in order to allow the admin to re-establish the trust keytabs
with a simple sssd restart.
---
 Makefile.am                                   |   2 +
 src/providers/ipa/ipa_subdomains.c            |   4 +-
 src/providers/ipa/ipa_subdomains_server.c     |  99 +++++++++++----
 src/tests/cmocka/test_ipa_subdomains_server.c | 166 ++++++++++++++++++++++++--
 4 files changed, 237 insertions(+), 34 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 
912bfc6641465ef5cd2ff2cce9975b4027c3218d..8d9b73c0d2099cb54de3f2b2bd6814a968f74705
 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2518,6 +2518,8 @@ test_ipa_subdom_server_LDFLAGS = \
     -Wl,-wrap,krb5_kt_default \
     -Wl,-wrap,execle \
     -Wl,-wrap,execve \
+    -Wl,-wrap,rename \
+    -Wl,-wrap,sss_unique_filename \
     $(NULL)
 test_ipa_subdom_server_LDADD = \
     $(PAM_LIBS) \
diff --git a/src/providers/ipa/ipa_subdomains.c 
b/src/providers/ipa/ipa_subdomains.c
index 
cec8b3918b8f832e2c7376a867448fe876da6ffc..b2e2fec353f7b168d28a880cb0f1b6181abb1ccb
 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -264,7 +264,7 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
         ret = get_idmap_data_from_range(r, domain_name, &name1, &sid1, &rid1,
                                         &range1, &mapping1);
         if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, ("get_idmap_data_from_range failed.\n"));
+            DEBUG(SSSDBG_OP_FAILURE, "get_idmap_data_from_range failed.\n");
             goto done;
         }
         for (d = 0; d < c; d++) {
@@ -272,7 +272,7 @@ static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
                                             &sid2, &rid2, &range2, &mapping2);
             if (ret != EOK) {
                 DEBUG(SSSDBG_OP_FAILURE,
-                      ("get_idmap_data_from_range failed.\n"));
+                      "get_idmap_data_from_range failed.\n");
                 goto done;
             }
 
diff --git a/src/providers/ipa/ipa_subdomains_server.c 
b/src/providers/ipa/ipa_subdomains_server.c
index 
4bfea61e6dd0a02f6b723a39f7ba236c914009b0..f03965d022a93ed56e0761dd940866c4f204de40
 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -445,6 +445,15 @@ static void ipa_getkeytab_exec(const char *ccache,
         exit(1);
     }
 
+    /* ipa-getkeytab cannot add keys to an empty file, let's unlink it and only
+     * use the filename */
+    ret = unlink(keytab_path);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unlink the temporary ccname!\n");
+        exit(1);
+    }
+
     errno = 0;
     ret = execle(IPA_GETKEYTAB_PATH, IPA_GETKEYTAB_PATH,
                  "-r", "-s", server, "-p", principal, "-k", keytab_path, NULL,
@@ -561,6 +570,7 @@ struct ipa_server_trust_add_state {
     uint32_t direction;
     const char *forest;
     const char *keytab;
+    char *new_keytab;
     const char *principal;
     const char *forest_realm;
     const char *ccache;
@@ -646,6 +656,22 @@ immediate:
     return req;
 }
 
+static int ipa_server_trust_rm_tmp_file(void *memptr)
+{
+    errno_t ret;
+    struct ipa_server_trust_add_state *state =
+            talloc_get_type(memptr, struct ipa_server_trust_add_state);
+
+    ret = unlink(state->new_keytab);
+    if (ret != 0 && errno != ENOENT) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE,
+                "Cannot remove temporary keytab: %d\n", ret);
+    }
+
+    return 0;
+}
+
 static errno_t ipa_server_trust_add_1way(struct tevent_req *req)
 {
     errno_t ret;
@@ -660,21 +686,22 @@ static errno_t ipa_server_trust_add_1way(struct 
tevent_req *req)
         return EIO;
     }
 
-    ret = ipa_check_keytab(state->keytab,
-                           state->id_ctx->server_mode->kt_owner_uid,
-                           state->id_ctx->server_mode->kt_owner_gid);
-    if (ret == EOK) {
-        DEBUG(SSSDBG_TRACE_FUNC,
-              "Keytab already present, can add the trust\n");
-        return EOK;
-    } else if (ret != ENOENT) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Failed to check for keytab: %d\n", ret);
+    state->new_keytab = talloc_asprintf(state, "%sXXXXXX", state->keytab);
+    if (state->new_keytab == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
+        return ENOMEM;
+    }
+
+    ret = sss_unique_filename(state->new_keytab);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create temporary keytab name\n");
         return ret;
     }
+    /* Make sure the file is always removed */
+    talloc_set_destructor((TALLOC_CTX *) state, ipa_server_trust_rm_tmp_file);
 
     DEBUG(SSSDBG_TRACE_FUNC,
-          "No keytab for %s\n", state->subdom->name);
+          "Will re-fetch keytab for %s\n", state->subdom->name);
 
     hostname = dp_opt_get_string(state->id_ctx->ipa_options->basic,
                                  IPA_HOSTNAME);
@@ -691,7 +718,7 @@ static errno_t ipa_server_trust_add_1way(struct tevent_req 
*req)
                                 state->ccache,
                                 hostname,
                                 state->principal,
-                                state->keytab);
+                                state->new_keytab);
     if (subreq == NULL) {
         return ENOMEM;
     }
@@ -710,23 +737,49 @@ static void ipa_server_trust_1way_kt_done(struct 
tevent_req *subreq)
     ret = ipa_getkeytab_recv(subreq, NULL);
     talloc_zfree(subreq);
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "ipa_getkeytab_recv failed: %d\n", ret);
-        tevent_req_error(req, ret);
-        return;
+        /* Do not fail here, but try to check and use the previous keytab,
+         * if any */
+        DEBUG(SSSDBG_MINOR_FAILURE, "ipa_getkeytab_recv failed: %d\n", ret);
+    } else {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Keytab successfully retrieved to %s\n", state->new_keytab);
     }
 
-    DEBUG(SSSDBG_TRACE_FUNC,
-          "Keytab successfully retrieved to %s\n", state->keytab);
-
-    ret = ipa_check_keytab(state->keytab,
+    ret = ipa_check_keytab(state->new_keytab,
                            state->id_ctx->server_mode->kt_owner_uid,
                            state->id_ctx->server_mode->kt_owner_gid);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "ipa_check_keytab failed: %d\n", ret);
-        tevent_req_error(req, ret);
-        return;
+    if (ret == EOK) {
+        ret = rename(state->new_keytab, state->keytab);
+        if (ret == -1) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                "rename failed [%d][%s].\n", ret, strerror(ret));
+            tevent_req_error(req, ret);
+            return;
+        }
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Keytab renamed to %s\n", state->keytab);
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Trying to recover and use the previous keytab, if available\n");
+        ret = ipa_check_keytab(state->keytab,
+                               state->id_ctx->server_mode->kt_owner_uid,
+                               state->id_ctx->server_mode->kt_owner_gid);
+        if (ret == EOK) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "The previous keytab %s contains the expected principal\n",
+                  state->keytab);
+        } else {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Cannot use the old keytab: %d\n", ret);
+            /* Nothing we can do now */
+            tevent_req_error(req, ret);
+            return;
+        }
     }
 
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Keytab %s contains the expected principals\n", state->new_keytab);
+
     ret = ipa_server_trust_add_step(req);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c 
b/src/tests/cmocka/test_ipa_subdomains_server.c
index 
a4cb8c2b7538dc84b74e0227205b73780844b652..1d433caa732d5685cc0d8ad6f07752c29674ce8b
 100644
--- a/src/tests/cmocka/test_ipa_subdomains_server.c
+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
@@ -66,33 +66,79 @@
 #define ONEWAY_PRINC    DOM_FLAT"$"
 #define ONEWAY_AUTHID   ONEWAY_PRINC"@"SUBDOM_REALM
 
+static bool global_rename_called;
+
 krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
 {
     return krb5_kt_resolve(context, KEYTAB_PATH, id);
 }
 
-static void create_dummy_keytab(void)
+static void create_dummy_keytab(const char *dummy_kt)
 {
     errno_t ret;
 
-    assert_non_null(ONEWAY_KEYTAB);
+    assert_non_null(dummy_kt);
     mock_keytab_with_contents(global_talloc_context,
-                              ONEWAY_KEYTAB, ONEWAY_AUTHID);
+                              dummy_kt, ONEWAY_AUTHID);
 
-    ret = access(ONEWAY_KEYTAB, R_OK);
+    ret = access(dummy_kt, R_OK);
     assert_int_equal(ret, 0);
 }
 
+static int wrap_exec(void)
+{
+    const char *test_kt;
+    const char *fail_creating_kt;
+
+    test_kt = getenv("TEST_KT_ENV");
+    if (test_kt == NULL) {
+        _exit(1);
+    }
+    unsetenv("TEST_KT_ENV");
+
+    fail_creating_kt = getenv("KT_CREATE_FAIL");
+    if (fail_creating_kt != NULL) {
+        _exit(1);
+    }
+
+    create_dummy_keytab(test_kt);
+    _exit(0);
+
+    return 1;   /* Should not happen */
+}
+
 int __wrap_execle(const char *path, const char *arg, ...)
 {
-    create_dummy_keytab();
-    _exit(0);
+    return wrap_exec();
 }
 
 int __wrap_execve(const char *path, const char *arg, ...)
 {
-    create_dummy_keytab();
-    _exit(0);
+    return wrap_exec();
+}
+
+errno_t __real_sss_unique_filename(char *path_tmpl);
+
+errno_t __wrap_sss_unique_filename(char *path_tmpl)
+{
+    int ret;
+
+    ret = __real_sss_unique_filename(path_tmpl);
+    if (ret == EOK) {
+        int sret;
+
+        sret = setenv("TEST_KT_ENV", path_tmpl, 1);
+        assert_int_equal(sret, 0);
+    }
+    return ret;
+}
+
+int __real_rename(const char *old, const char *new);
+
+int __wrap_rename(const char *old, const char *new)
+{
+    global_rename_called = true;
+    return __real_rename(old, new);
 }
 
 struct trust_test_ctx {
@@ -100,6 +146,7 @@ struct trust_test_ctx {
     struct be_ctx *be_ctx;
 
     struct ipa_id_ctx *ipa_ctx;
+    bool expect_rename;
 };
 
 static struct ipa_id_ctx *mock_ipa_ctx(TALLOC_CTX *mem_ctx,
@@ -244,6 +291,8 @@ static int test_ipa_server_create_trusts_setup(void **state)
 
     mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC);
 
+    global_rename_called = false;
+
     *state = test_ctx;
     return 0;
 }
@@ -260,6 +309,11 @@ static int test_ipa_server_create_trusts_teardown(void 
**state)
     unlink(ONEWAY_KEYTAB);
     /* Ignore failures */
 
+    /* If a test needs this variable, it should be set again in
+     * each test
+     */
+    unsetenv("KT_CREATE_FAIL");
+
     talloc_free(test_ctx);
     return 0;
 }
@@ -612,6 +666,8 @@ static void test_ipa_server_create_oneway(void **state)
 
     assert_null(test_ctx->ipa_ctx->server_mode->trusts);
 
+    test_ctx->expect_rename = true;
+
     req = ipa_server_create_trusts_send(test_ctx,
                                         test_ctx->tctx->ev,
                                         test_ctx->be_ctx,
@@ -635,6 +691,8 @@ static void test_ipa_server_create_trusts_oneway(struct 
tevent_req *req)
     talloc_zfree(req);
     assert_int_equal(ret, EOK);
 
+    assert_true(test_ctx->expect_rename == global_rename_called);
+
     ret = access(ONEWAY_KEYTAB, R_OK);
     assert_int_equal(ret, 0);
 
@@ -674,10 +732,46 @@ static void test_ipa_server_create_oneway_kt_exists(void 
**state)
 
     add_test_1way_subdomains(test_ctx);
 
-    create_dummy_keytab();
+    create_dummy_keytab(ONEWAY_KEYTAB);
     ret = access(ONEWAY_KEYTAB, R_OK);
     assert_int_equal(ret, 0);
 
+    test_ctx->expect_rename = true;
+
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    req = ipa_server_create_trusts_send(test_ctx,
+                                        test_ctx->tctx->ev,
+                                        test_ctx->be_ctx,
+                                        test_ctx->ipa_ctx,
+                                        test_ctx->be_ctx->domain);
+    assert_non_null(req);
+
+    tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, 
test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+/* Test scenario where a keytab already exists, but refresh fails. In this 
case,
+ * sssd should attempt to reuse the previous keytab
+ */
+static void test_ipa_server_create_oneway_kt_refresh_fallback(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    struct tevent_req *req;
+    errno_t ret;
+
+    add_test_1way_subdomains(test_ctx);
+
+    create_dummy_keytab(ONEWAY_KEYTAB);
+    ret = access(ONEWAY_KEYTAB, R_OK);
+    assert_int_equal(ret, 0);
+
+    setenv("KT_CREATE_FAIL", "1", 1);
+    test_ctx->expect_rename = false;
+
     assert_null(test_ctx->ipa_ctx->server_mode->trusts);
 
     req = ipa_server_create_trusts_send(test_ctx,
@@ -693,6 +787,54 @@ static void test_ipa_server_create_oneway_kt_exists(void 
**state)
     assert_int_equal(ret, ERR_OK);
 }
 
+/* Tests case where there's no keytab and retrieving fails. Just fail the
+ * request in that case
+ */
+static void test_ipa_server_create_trusts_oneway_fail(struct tevent_req *req);
+
+static void test_ipa_server_create_oneway_kt_refresh_fail(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    struct tevent_req *req;
+    errno_t ret;
+
+    add_test_1way_subdomains(test_ctx);
+
+    setenv("KT_CREATE_FAIL", "1", 1);
+    test_ctx->expect_rename = false;
+
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    req = ipa_server_create_trusts_send(test_ctx,
+                                        test_ctx->tctx->ev,
+                                        test_ctx->be_ctx,
+                                        test_ctx->ipa_ctx,
+                                        test_ctx->be_ctx->domain);
+    assert_non_null(req);
+
+    tevent_req_set_callback(req,
+                            test_ipa_server_create_trusts_oneway_fail,
+                            test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+static void test_ipa_server_create_trusts_oneway_fail(struct tevent_req *req)
+{
+    struct trust_test_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct trust_test_ctx);
+    errno_t ret;
+
+    ret = ipa_server_create_trusts_recv(req);
+    assert_int_not_equal(ret, EOK);
+
+    assert_true(test_ctx->expect_rename == global_rename_called);
+
+    test_ev_done(test_ctx->tctx, EOK);
+}
+
 static void test_ipa_server_trust_oneway_init(void **state)
 {
     struct trust_test_ctx *test_ctx =
@@ -749,6 +891,12 @@ int main(int argc, const char *argv[])
         
cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_exists,
                                         test_ipa_server_create_trusts_setup,
                                         
test_ipa_server_create_trusts_teardown),
+        
cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_refresh_fallback,
+                                        test_ipa_server_create_trusts_setup,
+                                        
test_ipa_server_create_trusts_teardown),
+        
cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_refresh_fail,
+                                        test_ipa_server_create_trusts_setup,
+                                        
test_ipa_server_create_trusts_teardown),
         cmocka_unit_test_setup_teardown(test_ipa_server_trust_oneway_init,
                                         test_ipa_server_create_trusts_setup,
                                         
test_ipa_server_create_trusts_teardown),
-- 
2.4.3

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to