Blob Blame History Raw
From 6f466e0a3d950d21bd750ef53cb93b75dc023f9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 2 Aug 2017 14:00:03 +0200
Subject: [PATCH 52/93] UTIL: Add sss_create_dir()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The newly added function helps us to create a new dir avoiding a
possible TUCTOU issue.

It's going to be used by the new session provider code.

A simple test for this new function has also been provided.

Related:
https://pagure.io/SSSD/sssd/issue/2995

Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>

Reviewed-by: Pavel Březina <pbrezina@redhat.com>
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
---
 src/tests/files-tests.c | 37 ++++++++++++++++++++++++
 src/util/files.c        | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/util.h         |  5 ++++
 3 files changed, 119 insertions(+)

diff --git a/src/tests/files-tests.c b/src/tests/files-tests.c
index 9feb9274ace02dd977950b8de220ee1f1aa18e65..1ccf404b94dc5518308c54380117c1162dc85f22 100644
--- a/src/tests/files-tests.c
+++ b/src/tests/files-tests.c
@@ -378,6 +378,42 @@ START_TEST(test_copy_node)
 }
 END_TEST
 
+START_TEST(test_create_dir)
+{
+    int ret;
+    char origpath[PATH_MAX+1];
+    char *new_dir;
+    struct stat info;
+
+    errno = 0;
+
+    fail_unless(getcwd(origpath, PATH_MAX) == origpath, "Cannot getcwd\n");
+    fail_unless(errno == 0, "Cannot getcwd\n");
+
+    /* create a dir */
+    ret = sss_create_dir(dir_path, "testdir", S_IRUSR | S_IXUSR, uid, gid);
+    fail_unless(ret == EOK, "cannot create dir: %s", strerror(ret));
+
+    new_dir = talloc_asprintf(NULL, "%s/testdir", dir_path);
+    ret = stat(new_dir, &info);
+    fail_unless(ret == EOK, "failed to stat '%s'\n", new_dir);
+
+    /* check the dir has been created */
+    fail_unless(S_ISDIR(info.st_mode) != 0, "'%s' is not a dir.\n", new_dir);
+
+    /* check the permissions are okay */
+    fail_unless((info.st_mode & S_IRUSR) != 0, "Read permission is not set\n");
+    fail_unless((info.st_mode & S_IWUSR) == 0, "Write permission is set\n");
+    fail_unless((info.st_mode & S_IXUSR) != 0, "Exec permission is not set\n");
+
+    /* check the owner is okay */
+    fail_unless(info.st_uid == uid, "Dir created with the wrong uid\n");
+    fail_unless(info.st_gid == gid, "Dir created with the wrong gid\n");
+
+    talloc_free(new_dir);
+}
+END_TEST
+
 static Suite *files_suite(void)
 {
     Suite *s = suite_create("files_suite");
@@ -393,6 +429,7 @@ static Suite *files_suite(void)
     tcase_add_test(tc_files, test_copy_file);
     tcase_add_test(tc_files, test_copy_symlink);
     tcase_add_test(tc_files, test_copy_node);
+    tcase_add_test(tc_files, test_create_dir);
     suite_add_tcase(s, tc_files);
 
     return s;
diff --git a/src/util/files.c b/src/util/files.c
index 5827b29d8b5cf13248514f693e859d42335069d9..33b21e2ea3bad854d5a8e831a84ad4d768b7f9c0 100644
--- a/src/util/files.c
+++ b/src/util/files.c
@@ -807,3 +807,80 @@ fail:
     talloc_free(cctx);
     return ret;
 }
+
+int sss_create_dir(const char *parent_dir_path,
+                   const char *dir_name,
+                   mode_t mode,
+                   uid_t uid, gid_t gid)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *dir_path;
+    int ret = EOK;
+    int parent_dir_fd = -1;
+    int dir_fd = -1;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    parent_dir_fd = sss_open_cloexec(parent_dir_path, O_RDONLY | O_DIRECTORY,
+                                     &ret);
+    if (parent_dir_fd == -1) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Cannot open() directory '%s' [%d]: %s\n",
+              parent_dir_path, ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    dir_path = talloc_asprintf(tmp_ctx, "%s/%s", parent_dir_path, dir_name);
+    if (dir_path == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    errno = 0;
+    ret = mkdirat(parent_dir_fd, dir_name, mode);
+    if (ret == -1) {
+        if (errno == EEXIST) {
+            ret = EOK;
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Directory '%s' already created!\n", dir_path);
+        } else {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Error reading '%s': %s\n", parent_dir_path, strerror(ret));
+            goto fail;
+        }
+    }
+
+    dir_fd = sss_open_cloexec(dir_path, O_RDONLY | O_DIRECTORY, &ret);
+    if (dir_fd == -1) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Cannot open() directory '%s' [%d]: %s\n",
+              dir_path, ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    errno = 0;
+    ret = fchown(dir_fd, uid, gid);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to own the newly created directory '%s' [%d]: %s\n",
+              dir_path, ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    ret = EOK;
+
+fail:
+    if (parent_dir_fd != -1) {
+        close(parent_dir_fd);
+    }
+    if (dir_fd != -1) {
+        close(dir_fd);
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/util/util.h b/src/util/util.h
index 80411ec91046b7dc7993b8d175fedebd2b70a79a..3d8bfe4795e976294b565c0869e3b842cf318efd 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -696,6 +696,11 @@ int sss_copy_file_secure(const char *src,
                          uid_t uid, gid_t gid,
                          bool force);
 
+int sss_create_dir(const char *parent_dir_path,
+                   const char *dir_name,
+                   mode_t mode,
+                   uid_t uid, gid_t gid);
+
 /* from selinux.c */
 int selinux_file_context(const char *dst_name);
 int reset_selinux_file_context(void);
-- 
2.14.1