Blame SOURCES/0056-UTIL-Provide-a-common-interface-to-safely-create-tem.patch

6cf099
From 2b13d8192f0d7a08bb16a3e488e772d54b10d81a Mon Sep 17 00:00:00 2001
6cf099
From: Jakub Hrozek <jhrozek@redhat.com>
6cf099
Date: Wed, 12 Aug 2015 12:41:44 +0200
6cf099
Subject: [PATCH 56/57] UTIL: Provide a common interface to safely create
6cf099
 temporary files
6cf099
MIME-Version: 1.0
6cf099
Content-Type: text/plain; charset=UTF-8
6cf099
Content-Transfer-Encoding: 8bit
6cf099
6cf099
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
6cf099
---
6cf099
 src/tests/cmocka/test_utils.c | 175 ++++++++++++++++++++++++++++++++++++++++++
6cf099
 src/util/util.c               | 127 ++++++++++++++++++++++++++++++
6cf099
 src/util/util.h               |  21 +++++
6cf099
 3 files changed, 323 insertions(+)
6cf099
6cf099
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
6cf099
index d3d00feda0bdd4048519f90ba48ae9d1042a95a1..c7ebe0997ec00197e8852bedbcf26ef1f6394fc3 100644
6cf099
--- a/src/tests/cmocka/test_utils.c
6cf099
+++ b/src/tests/cmocka/test_utils.c
6cf099
@@ -1212,6 +1212,168 @@ void test_fix_domain_in_name_list(void **state)
6cf099
     talloc_free(dom);
6cf099
 }
6cf099
 
6cf099
+struct unique_file_test_ctx {
6cf099
+    char *filename;
6cf099
+};
6cf099
+
6cf099
+static int unique_file_test_setup(void **state)
6cf099
+{
6cf099
+    struct unique_file_test_ctx *test_ctx;
6cf099
+
6cf099
+    assert_true(leak_check_setup());
6cf099
+    check_leaks_push(global_talloc_context);
6cf099
+
6cf099
+    test_ctx = talloc_zero(global_talloc_context, struct unique_file_test_ctx);
6cf099
+    assert_non_null(test_ctx);
6cf099
+
6cf099
+    test_ctx->filename = talloc_strdup(test_ctx, "test_unique_file_XXXXXX");
6cf099
+    assert_non_null(test_ctx);
6cf099
+
6cf099
+    *state = test_ctx;
6cf099
+    return 0;
6cf099
+}
6cf099
+
6cf099
+static int unique_file_test_teardown(void **state)
6cf099
+{
6cf099
+    struct unique_file_test_ctx *test_ctx;
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
6cf099
+
6cf099
+    errno = 0;
6cf099
+    ret = unlink(test_ctx->filename);
6cf099
+    if (ret != 0 && errno != ENOENT) {
6cf099
+        fail();
6cf099
+    }
6cf099
+
6cf099
+    talloc_free(test_ctx);
6cf099
+    assert_true(check_leaks_pop(global_talloc_context) == true);
6cf099
+    assert_true(leak_check_teardown());
6cf099
+    return 0;
6cf099
+}
6cf099
+
6cf099
+static void assert_destructor(TALLOC_CTX *owner,
6cf099
+                              struct unique_file_test_ctx *test_ctx)
6cf099
+{
6cf099
+    int fd;
6cf099
+    errno_t ret;
6cf099
+    char *check_filename;
6cf099
+
6cf099
+    /* Test that the destructor works */
6cf099
+    if (owner == NULL) {
6cf099
+        return;
6cf099
+    }
6cf099
+
6cf099
+    check_filename = talloc_strdup(test_ctx, test_ctx->filename);
6cf099
+    assert_non_null(check_filename);
6cf099
+
6cf099
+    talloc_free(owner);
6cf099
+
6cf099
+    ret = check_and_open_readonly(test_ctx->filename, &fd,
6cf099
+                                  geteuid(), getegid(),
6cf099
+                                  (S_IRUSR | S_IWUSR | S_IFREG), 0);
6cf099
+    close(fd);
6cf099
+    assert_int_not_equal(ret, EOK);
6cf099
+}
6cf099
+
6cf099
+static void sss_unique_file_test(struct unique_file_test_ctx *test_ctx,
6cf099
+                                 bool test_destructor)
6cf099
+{
6cf099
+    int fd;
6cf099
+    errno_t ret;
6cf099
+    struct stat sb;
6cf099
+    TALLOC_CTX *owner = NULL;
6cf099
+
6cf099
+    if (test_destructor) {
6cf099
+        owner = talloc_new(test_ctx);
6cf099
+        assert_non_null(owner);
6cf099
+    }
6cf099
+
6cf099
+    fd = sss_unique_file(owner, test_ctx->filename, &ret;;
6cf099
+    assert_int_not_equal(fd, -1);
6cf099
+    assert_int_equal(ret, EOK);
6cf099
+
6cf099
+    ret = check_fd(fd, geteuid(), getegid(),
6cf099
+                   (S_IRUSR | S_IWUSR | S_IFREG), 0, &sb);
6cf099
+    close(fd);
6cf099
+    assert_int_equal(ret, EOK);
6cf099
+
6cf099
+    assert_destructor(owner, test_ctx);
6cf099
+}
6cf099
+
6cf099
+static void test_sss_unique_file(void **state)
6cf099
+{
6cf099
+    struct unique_file_test_ctx *test_ctx;
6cf099
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
6cf099
+    sss_unique_file_test(test_ctx, false);
6cf099
+}
6cf099
+
6cf099
+static void test_sss_unique_file_destruct(void **state)
6cf099
+{
6cf099
+    struct unique_file_test_ctx *test_ctx;
6cf099
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
6cf099
+    sss_unique_file_test(test_ctx, true);
6cf099
+}
6cf099
+
6cf099
+static void test_sss_unique_file_neg(void **state)
6cf099
+{
6cf099
+    int fd;
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    fd = sss_unique_file(NULL, discard_const("badpattern"), &ret;;
6cf099
+    assert_int_equal(fd, -1);
6cf099
+    assert_int_equal(ret, EINVAL);
6cf099
+}
6cf099
+
6cf099
+static void sss_unique_filename_test(struct unique_file_test_ctx *test_ctx,
6cf099
+                                     bool test_destructor)
6cf099
+{
6cf099
+    int fd;
6cf099
+    errno_t ret;
6cf099
+    char *tmp_filename;
6cf099
+    TALLOC_CTX *owner = NULL;
6cf099
+
6cf099
+    tmp_filename = talloc_strdup(test_ctx, test_ctx->filename);
6cf099
+    assert_non_null(tmp_filename);
6cf099
+
6cf099
+    if (test_destructor) {
6cf099
+        owner = talloc_new(test_ctx);
6cf099
+        assert_non_null(owner);
6cf099
+    }
6cf099
+
6cf099
+    ret = sss_unique_filename(owner, test_ctx->filename);
6cf099
+    assert_int_equal(ret, EOK);
6cf099
+
6cf099
+    assert_int_equal(strncmp(test_ctx->filename,
6cf099
+                             tmp_filename,
6cf099
+                             strlen(tmp_filename) - sizeof("XXXXXX")),
6cf099
+                     0);
6cf099
+
6cf099
+    ret = check_and_open_readonly(test_ctx->filename, &fd,
6cf099
+                                  geteuid(), getegid(),
6cf099
+                                  (S_IRUSR | S_IWUSR | S_IFREG), 0);
6cf099
+    close(fd);
6cf099
+    assert_int_equal(ret, EOK);
6cf099
+
6cf099
+    assert_destructor(owner, test_ctx);
6cf099
+}
6cf099
+
6cf099
+static void test_sss_unique_filename(void **state)
6cf099
+{
6cf099
+    struct unique_file_test_ctx *test_ctx;
6cf099
+
6cf099
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
6cf099
+    sss_unique_filename_test(test_ctx, false);
6cf099
+}
6cf099
+
6cf099
+static void test_sss_unique_filename_destruct(void **state)
6cf099
+{
6cf099
+    struct unique_file_test_ctx *test_ctx;
6cf099
+
6cf099
+    test_ctx = talloc_get_type(*state, struct unique_file_test_ctx);
6cf099
+    sss_unique_filename_test(test_ctx, true);
6cf099
+}
6cf099
+
6cf099
 int main(int argc, const char *argv[])
6cf099
 {
6cf099
     poptContext pc;
6cf099
@@ -1275,6 +1437,19 @@ int main(int argc, const char *argv[])
6cf099
         cmocka_unit_test_setup_teardown(test_fix_domain_in_name_list,
6cf099
                                         confdb_test_setup,
6cf099
                                         confdb_test_teardown),
6cf099
+        cmocka_unit_test_setup_teardown(test_sss_unique_file,
6cf099
+                                        unique_file_test_setup,
6cf099
+                                        unique_file_test_teardown),
6cf099
+        cmocka_unit_test_setup_teardown(test_sss_unique_file_destruct,
6cf099
+                                        unique_file_test_setup,
6cf099
+                                        unique_file_test_teardown),
6cf099
+        cmocka_unit_test(test_sss_unique_file_neg),
6cf099
+        cmocka_unit_test_setup_teardown(test_sss_unique_filename,
6cf099
+                                        unique_file_test_setup,
6cf099
+                                        unique_file_test_teardown),
6cf099
+        cmocka_unit_test_setup_teardown(test_sss_unique_filename_destruct,
6cf099
+                                        unique_file_test_setup,
6cf099
+                                        unique_file_test_teardown),
6cf099
     };
6cf099
 
6cf099
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
6cf099
diff --git a/src/util/util.c b/src/util/util.c
6cf099
index 782cd026b7928e607a8980fb5f333c794feb5b1a..f0925051a08970b191f61a533cd49dd53ae128e0 100644
6cf099
--- a/src/util/util.c
6cf099
+++ b/src/util/util.c
6cf099
@@ -979,3 +979,130 @@ errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *_unix_tim
6cf099
     *_unix_time = ut;
6cf099
     return EOK;
6cf099
 }
6cf099
+
6cf099
+struct tmpfile_watch {
6cf099
+    const char *filename;
6cf099
+};
6cf099
+
6cf099
+static int unlink_dbg(const char *filename)
6cf099
+{
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    ret = unlink(filename);
6cf099
+    if (ret != 0) {
6cf099
+        if (errno == 2) {
6cf099
+            DEBUG(SSSDBG_TRACE_INTERNAL,
6cf099
+                  "File already removed: [%s]\n", filename);
6cf099
+            return 0;
6cf099
+        } else {
6cf099
+            DEBUG(SSSDBG_CRIT_FAILURE,
6cf099
+                  "Cannot remove temporary file [%s]\n", filename);
6cf099
+            return -1;
6cf099
+        }
6cf099
+    }
6cf099
+
6cf099
+    return 0;
6cf099
+}
6cf099
+
6cf099
+static int unique_filename_destructor(void *memptr)
6cf099
+{
6cf099
+    struct tmpfile_watch *tw = talloc_get_type(memptr, struct tmpfile_watch);
6cf099
+
6cf099
+    if (tw == NULL || tw->filename == NULL) {
6cf099
+        DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Wrong private pointer\n");
6cf099
+        return -1;
6cf099
+    }
6cf099
+
6cf099
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Unlinking [%s]\n", tw->filename);
6cf099
+
6cf099
+    return unlink_dbg(tw->filename);
6cf099
+}
6cf099
+
6cf099
+static struct tmpfile_watch *tmpfile_watch_set(TALLOC_CTX *owner,
6cf099
+                                               const char *filename)
6cf099
+{
6cf099
+    struct tmpfile_watch *tw = NULL;
6cf099
+
6cf099
+    tw = talloc_zero(owner, struct tmpfile_watch);
6cf099
+    if (tw == NULL) {
6cf099
+        return NULL;
6cf099
+    }
6cf099
+
6cf099
+    tw->filename = talloc_strdup(tw, filename);
6cf099
+    if (tw->filename == NULL) {
6cf099
+        talloc_free(tw);
6cf099
+        return NULL;
6cf099
+    }
6cf099
+
6cf099
+    talloc_set_destructor((TALLOC_CTX *) tw,
6cf099
+                          unique_filename_destructor);
6cf099
+    return tw;
6cf099
+}
6cf099
+
6cf099
+int sss_unique_file_ex(TALLOC_CTX *owner,
6cf099
+                       char *path_tmpl,
6cf099
+                       mode_t file_umask,
6cf099
+                       errno_t *_err)
6cf099
+{
6cf099
+    size_t tmpl_len;
6cf099
+    errno_t ret;
6cf099
+    int fd = -1;
6cf099
+    mode_t old_umask;
6cf099
+    struct tmpfile_watch *tw = NULL;
6cf099
+
6cf099
+    tmpl_len = strlen(path_tmpl);
6cf099
+    if (tmpl_len < 6 || strcmp(path_tmpl + (tmpl_len - 6), "XXXXXX") != 0) {
6cf099
+        DEBUG(SSSDBG_OP_FAILURE,
6cf099
+              "Template too short or doesn't end with XXXXXX!\n");
6cf099
+        ret = EINVAL;
6cf099
+        goto done;
6cf099
+    }
6cf099
+
6cf099
+    old_umask = umask(file_umask);
6cf099
+    fd = mkstemp(path_tmpl);
6cf099
+    umask(old_umask);
6cf099
+    if (fd == -1) {
6cf099
+        ret = errno;
6cf099
+        DEBUG(SSSDBG_OP_FAILURE,
6cf099
+              "mkstemp(\"%s\") failed [%d]: %s!\n",
6cf099
+              path_tmpl, ret, strerror(ret));
6cf099
+        goto done;
6cf099
+    }
6cf099
+
6cf099
+    if (owner != NULL) {
6cf099
+        tw = tmpfile_watch_set(owner, path_tmpl);
6cf099
+        if (tw == NULL) {
6cf099
+            unlink_dbg(path_tmpl);
6cf099
+            ret = ENOMEM;
6cf099
+            goto done;
6cf099
+        }
6cf099
+    }
6cf099
+
6cf099
+    ret = EOK;
6cf099
+done:
6cf099
+    if (_err) {
6cf099
+        *_err = ret;
6cf099
+    }
6cf099
+    return fd;
6cf099
+}
6cf099
+
6cf099
+int sss_unique_file(TALLOC_CTX *owner,
6cf099
+                    char *path_tmpl,
6cf099
+                    errno_t *_err)
6cf099
+{
6cf099
+    return sss_unique_file_ex(owner, path_tmpl, 077, _err);
6cf099
+}
6cf099
+
6cf099
+errno_t sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl)
6cf099
+{
6cf099
+    int fd;
6cf099
+    errno_t ret;
6cf099
+
6cf099
+    fd = sss_unique_file(owner, path_tmpl, &ret;;
6cf099
+    /* We only care about a unique file name */
6cf099
+    if (fd >= 0) {
6cf099
+        close(fd);
6cf099
+    }
6cf099
+
6cf099
+    return ret;
6cf099
+}
6cf099
diff --git a/src/util/util.h b/src/util/util.h
6cf099
index 94a3ddea839f0998cb7796f1d2fe13f743de3aaf..a20d1d82eb8f10dac515ad25e7e424713bb1c099 100644
6cf099
--- a/src/util/util.h
6cf099
+++ b/src/util/util.h
6cf099
@@ -658,4 +658,25 @@ int get_seuser(TALLOC_CTX *mem_ctx, const char *login_name,
6cf099
 /* convert time from generalized form to unix time */
6cf099
 errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *unix_time);
6cf099
 
6cf099
+/* Creates a unique file using mkstemp with provided umask. The template
6cf099
+ * must end with XXXXXX. Returns the fd, sets _err to an errno value on error.
6cf099
+ *
6cf099
+ * Prefer using sss_unique_file() as it uses a secure umask internally.
6cf099
+ */
6cf099
+int sss_unique_file_ex(TALLOC_CTX *mem_ctx,
6cf099
+                       char *path_tmpl,
6cf099
+                       mode_t file_umask,
6cf099
+                       errno_t *_err);
6cf099
+int sss_unique_file(TALLOC_CTX *owner,
6cf099
+                    char *path_tmpl,
6cf099
+                    errno_t *_err);
6cf099
+
6cf099
+/* Creates a unique filename using mkstemp with secure umask. The template
6cf099
+ * must end with XXXXXX
6cf099
+ *
6cf099
+ * path_tmpl must be a talloc context. Destructor would be set on the filename
6cf099
+ * so that it's guaranteed the file is removed.
6cf099
+ */
6cf099
+int sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl);
6cf099
+
6cf099
 #endif /* __SSSD_UTIL_H__ */
6cf099
-- 
6cf099
2.4.3
6cf099