[Pkg-sssd-devel] sssd: Changes to 'debian-experimental'

Timo Aaltonen tjaalton-guest at alioth.debian.org
Mon Apr 8 14:44:43 UTC 2013


 debian/changelog                        |   12 
 debian/patches/fix-cve-2013-0219-1.diff |  172 +++++
 debian/patches/fix-cve-2013-0219-2.diff | 1028 ++++++++++++++++++++++++++++++++
 debian/patches/fix-cve-2013-0220.diff   |   67 ++
 debian/patches/series                   |    4 
 5 files changed, 1275 insertions(+), 8 deletions(-)

New commits:
commit 6495fdb3f342e8ffefaf28d133e62ebfdd55a4e0
Author: Timo Aaltonen <tjaalton at ubuntu.com>
Date:   Wed Mar 6 11:34:14 2013 +0200

    release to unstable

diff --git a/debian/changelog b/debian/changelog
index 20d9dad..01497d3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-sssd (1.8.4-2) UNRELEASED; urgency=low
+sssd (1.8.4-2) unstable; urgency=low
 
   * fix-cve-2013-0219-1.diff, fix-cve-2013-0219-2.diff,
     fix-cve-2013-0220.diff: Upstream commits from the stable tree to fix

commit bc2aa74cace988286728516d89690c0f01b2b601
Author: Timo Aaltonen <tjaalton at ubuntu.com>
Date:   Wed Feb 27 23:40:14 2013 +0200

    fix-cve-2013-0219-1.diff, fix-cve-2013-0219-2.diff, fix-cve-2013-0220.diff: Upstream commits from the stable tree to fix recent CVE reports. (Closes: #698871)

diff --git a/debian/changelog b/debian/changelog
index 3183a80..20d9dad 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+sssd (1.8.4-2) UNRELEASED; urgency=low
+
+  * fix-cve-2013-0219-1.diff, fix-cve-2013-0219-2.diff,
+    fix-cve-2013-0220.diff: Upstream commits from the stable tree to fix
+    recent CVE reports. (Closes: #698871)
+
+ -- Timo Aaltonen <tjaalton at ubuntu.com>  Wed, 27 Feb 2013 23:38:28 +0200
+
 sssd (1.8.4-1) unstable; urgency=low
 
   * New upstream bugfix release 1.8.2.
diff --git a/debian/patches/fix-cve-2013-0219-1.diff b/debian/patches/fix-cve-2013-0219-1.diff
new file mode 100644
index 0000000..29702a6
--- /dev/null
+++ b/debian/patches/fix-cve-2013-0219-1.diff
@@ -0,0 +1,172 @@
+commit 2b0c414b919ec75be2bcfcca850ef309760816d3
+Author: Jakub Hrozek <jhrozek at redhat.com>
+Date:   Wed Dec 12 19:02:33 2012 +0100
+
+    TOOLS: Use openat/unlinkat when removing the homedir
+    
+    The removal of a home directory is sensitive to concurrent modification
+    of the directory tree being removed and can unlink files outside the
+    directory tree.
+    
+    This security issue was assigned CVE-2013-0219
+    
+    https://fedorahosted.org/sssd/ticket/1782
+
+diff --git a/src/tools/files.c b/src/tools/files.c
+index 6947535..ad6f1c0 100644
+--- a/src/tools/files.c
++++ b/src/tools/files.c
+@@ -78,8 +78,9 @@ struct copy_ctx {
+ /* wrapper in order not to create a temporary context in
+  * every iteration */
+ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
+-                                dev_t parent_dev,
+-                                const char *root);
++                                int parent_fd,
++                                const char *dir_name,
++                                dev_t parent_dev);
+ 
+ int remove_tree(const char *root)
+ {
+@@ -91,7 +92,7 @@ int remove_tree(const char *root)
+         return ENOMEM;
+     }
+ 
+-    ret = remove_tree_with_ctx(tmp_ctx, 0, root);
++    ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0);
+     talloc_free(tmp_ctx);
+     return ret;
+ }
+@@ -102,75 +103,75 @@ int remove_tree(const char *root)
+  * reach the top level remove_tree() again
+  */
+ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
+-                                dev_t parent_dev,
+-                                const char *root)
++                                int parent_fd,
++                                const char *dir_name,
++                                dev_t parent_dev)
+ {
+-    char *fullpath = NULL;
+     struct dirent *result;
+-    struct dirent direntp;
+     struct stat statres;
+     DIR *rootdir = NULL;
+     int ret, err;
++    int dir_fd;
++
++    dir_fd = openat(parent_fd, dir_name,
++                    O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
++    if (dir_fd == -1) {
++        ret = errno;
++        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot open %s: [%d]: %s\n",
++              dir_name, ret, strerror(ret)));
++        return ret;
++    }
+ 
+-    rootdir = opendir(root);
++    rootdir = fdopendir(dir_fd);
+     if (rootdir == NULL) {
+         ret = errno;
+-        DEBUG(1, ("Cannot open directory %s [%d][%s]\n",
+-                  root, ret, strerror(ret)));
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("Cannot open directory: [%d][%s]\n", ret, strerror(ret)));
++        close(dir_fd);
+         goto fail;
+     }
+ 
+-    while (readdir_r(rootdir, &direntp, &result) == 0) {
+-        if (result == NULL) {
+-            /* End of directory */
+-            break;
+-        }
+-
+-        if (strcmp (direntp.d_name, ".") == 0 ||
+-            strcmp (direntp.d_name, "..") == 0) {
++    while ((result = readdir(rootdir)) != NULL) {
++        if (strcmp(result->d_name, ".") == 0 ||
++            strcmp(result->d_name, "..") == 0) {
+             continue;
+         }
+ 
+-        fullpath = talloc_asprintf(mem_ctx, "%s/%s", root, direntp.d_name);
+-        if (fullpath == NULL) {
+-            ret = ENOMEM;
+-            goto fail;
+-        }
+-
+-        ret = lstat(fullpath, &statres);
++        ret = fstatat(dir_fd, result->d_name,
++                      &statres, AT_SYMLINK_NOFOLLOW);
+         if (ret != 0) {
+             ret = errno;
+-            DEBUG(1, ("Cannot stat %s: [%d][%s]\n",
+-                      fullpath, ret, strerror(ret)));
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("stat failed: [%d][%s]\n", ret, strerror(ret)));
+             goto fail;
+         }
+ 
+         if (S_ISDIR(statres.st_mode)) {
+             /* if directory, recursively descend, but check if on the same FS */
+             if (parent_dev && parent_dev != statres.st_dev) {
+-                DEBUG(1, ("Directory %s is on different filesystem, "
+-                          "will not follow\n", fullpath));
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      ("Directory %s is on different filesystem, "
++                       "will not follow\n"));
+                 ret = EFAULT;
+                 goto fail;
+             }
+ 
+-            ret = remove_tree_with_ctx(mem_ctx, statres.st_dev, fullpath);
++            ret = remove_tree_with_ctx(mem_ctx, dir_fd, result->d_name, statres.st_dev);
+             if (ret != EOK) {
+-                DEBUG(1, ("Removing subdirectory %s failed: [%d][%s]\n",
+-                            fullpath, ret, strerror(ret)));
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                      ("Removing subdirectory failed: [%d][%s]\n",
++                       ret, strerror(ret)));
+                 goto fail;
+             }
+         } else {
+-            ret = unlink(fullpath);
++            ret = unlinkat(dir_fd, result->d_name, 0);
+             if (ret != 0) {
+                 ret = errno;
+-                DEBUG(1, ("Removing file %s failed: [%d][%s]\n",
+-                          fullpath, ret, strerror(ret)));
++                DEBUG(SSSDBG_CRIT_FAILURE,
++                        ("Removing file failed: [%d][%s]\n", ret, strerror(ret)));
+                 goto fail;
+             }
+         }
+-
+-        talloc_free(fullpath);
+     }
+ 
+     ret = closedir(rootdir);
+@@ -180,19 +181,17 @@ static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
+         goto fail;
+     }
+ 
+-    ret = rmdir(root);
+-    if (ret != 0) {
++    ret = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
++    if (ret == -1) {
+         ret = errno;
+-        goto fail;
+     }
+ 
+     ret = EOK;
+-
+ fail:
+     if (rootdir) {  /* clean up on abnormal exit but retain return code */
+         err = closedir(rootdir);
+         if (err) {
+-            DEBUG(1, ("closedir failed, bad dirp?\n"));
++            DEBUG(SSSDBG_CRIT_FAILURE, ("closedir failed, bad dirp?\n"));
+         }
+     }
+     return ret;
diff --git a/debian/patches/fix-cve-2013-0219-2.diff b/debian/patches/fix-cve-2013-0219-2.diff
new file mode 100644
index 0000000..f973607
--- /dev/null
+++ b/debian/patches/fix-cve-2013-0219-2.diff
@@ -0,0 +1,1028 @@
+commit cfcfa9bd014a7717821c98262a11772c8e79c13e
+Author: Ondrej Kos <okos at redhat.com>
+Date:   Tue Jan 29 14:15:48 2013 +0100
+
+    TOOLS: Use file descriptor to avoid races when creating a home directory
+    
+    When creating a home directory, the destination tree can be modified in
+    various ways while it is being constructed because directory
+    permissions
+    are set before populating the directory. This can lead to file creation
+    and permission changes outside the target directory tree, using hard
+    links.
+    
+    This security problem was assigned CVE-2013-0219
+    
+    https://fedorahosted.org/sssd/ticket/1782
+
+diff --git a/Makefile.am b/Makefile.am
+index e64cc09..01a1abb 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -755,6 +755,7 @@ FILES_TESTS_LIBS = \
+     $(CHECK_LIBS) \
+     $(POPT_LIBS) \
+     $(TALLOC_LIBS) \
++	$(DHASH_LIBS) \
+     libsss_test_common.la
+ if BUILD_SELINUX
+     FILES_TESTS_LIBS += $(SELINUX_LIBS)
+@@ -767,7 +768,8 @@ files_tests_SOURCES = \
+     src/tests/files-tests.c \
+     src/util/check_and_open.c \
+     src/tools/selinux.c \
+-    src/tools/files.c
++    src/tools/files.c \
++	src/util/util.c
+ files_tests_CFLAGS = \
+     $(AM_CFLAGS) \
+     $(CHECK_CFLAGS)
+diff --git a/src/tests/files-tests.c b/src/tests/files-tests.c
+index cb20e1a..06aa596 100644
+--- a/src/tests/files-tests.c
++++ b/src/tests/files-tests.c
+@@ -183,7 +183,7 @@ START_TEST(test_simple_copy)
+ 
+     /* and finally copy.. */
+     DEBUG(5, ("Will copy from '%s' to '%s'\n", dir_path, dst_path));
+-    ret = copy_tree(dir_path, dst_path, uid, gid);
++    ret = copy_tree(dir_path, dst_path, 0700, uid, gid);
+     fail_unless(ret == EOK, "copy_tree failed\n");
+ 
+     /* check if really copied */
+@@ -225,7 +225,7 @@ START_TEST(test_copy_symlink)
+ 
+     /* and finally copy.. */
+     DEBUG(5, ("Will copy from '%s' to '%s'\n", dir_path, dst_path));
+-    ret = copy_tree(dir_path, dst_path, uid, gid);
++    ret = copy_tree(dir_path, dst_path, 0700, uid, gid);
+     fail_unless(ret == EOK, "copy_tree failed\n");
+ 
+     /* check if really copied */
+@@ -264,7 +264,7 @@ START_TEST(test_copy_node)
+ 
+     /* and finally copy.. */
+     DEBUG(5, ("Will copy from '%s' to '%s'\n", dir_path, dst_path));
+-    ret = copy_tree(dir_path, dst_path, uid, gid);
++    ret = copy_tree(dir_path, dst_path, 0700, uid, gid);
+     fail_unless(ret == EOK, "copy_tree failed\n");
+ 
+     /* check if really copied */
+diff --git a/src/tools/files.c b/src/tools/files.c
+index ad6f1c0..3d2273f 100644
+--- a/src/tools/files.c
++++ b/src/tools/files.c
+@@ -66,13 +66,12 @@
+ #include "util/util.h"
+ #include "tools/tools_util.h"
+ 
+-int copy_tree(const char *src_root, const char *dst_root,
+-              uid_t uid, gid_t gid);
+-
+ struct copy_ctx {
+     const char *src_orig;
+     const char *dst_orig;
+     dev_t       src_dev;
++    uid_t       uid;
++    gid_t       gid;
+ };
+ 
+ /* wrapper in order not to create a temporary context in
+@@ -197,66 +196,13 @@ fail:
+     return ret;
+ }
+ 
+-static int copy_dir(const char *src, const char *dst,
+-                    const struct stat *statp, const struct timeval mt[2],
+-                    uid_t uid, gid_t gid)
+-{
+-    int ret = 0;
+-
+-    /*
+-     * Create a new target directory, make it owned by
+-     * the user and then recursively copy that directory.
+-     */
+-    selinux_file_context(dst);
+-
+-    ret = mkdir(dst, statp->st_mode);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot mkdir directory '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        return ret;
+-    }
+-
+-    ret = chown(dst, uid, gid);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot chown directory '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        return ret;
+-    }
+-
+-    ret = chmod(dst, statp->st_mode);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot chmod directory '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        return ret;
+-    }
+-
+-    ret = copy_tree(src, dst, uid, gid);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot copy directory from '%s' to '%s': [%d][%s].\n",
+-                  src, dst, ret, strerror(ret)));
+-        return ret;
+-    }
+-
+-    ret = utimes(dst, mt);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot set utimes on a directory '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        return ret;
+-    }
+-
+-    return EOK;
+-}
+-
+-static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
++static char *talloc_readlinkat(TALLOC_CTX *mem_ctx, int dir_fd,
++                               const char *filename)
+ {
+     size_t size = 1024;
+     ssize_t nchars;
+     char *buffer;
++    char *new_buffer;
+ 
+     buffer = talloc_array(mem_ctx, char, size);
+     if (!buffer) {
+@@ -264,8 +210,9 @@ static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
+     }
+ 
+     while (1) {
+-        nchars = readlink(filename, buffer, size);
++        nchars = readlinkat(dir_fd, filename, buffer, size);
+         if (nchars < 0) {
++            talloc_free(buffer);
+             return NULL;
+         }
+ 
+@@ -276,10 +223,12 @@ static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
+ 
+         /* Try again with a bigger buffer */
+         size *= 2;
+-        buffer = talloc_realloc(mem_ctx, buffer, char, size);
+-        if (!buffer) {
++        new_buffer = talloc_realloc(mem_ctx, buffer, char, size);
++        if (!new_buffer) {
++            talloc_free(buffer);
+             return NULL;
+         }
++        buffer = new_buffer;
+     }
+ 
+     /* readlink does not nul-terminate */
+@@ -287,424 +236,500 @@ static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
+     return buffer;
+ }
+ 
+-static int copy_symlink(struct copy_ctx *cctx,
+-                        const char *src,
+-                        const char *dst,
+-                        const struct stat *statp,
+-                        const struct timeval mt[],
+-                        uid_t uid, gid_t gid)
++static int
++copy_symlink(int src_dir_fd,
++             int dst_dir_fd,
++             const char *file_name,
++             const char *full_path,
++             const struct stat *statp,
++             uid_t uid, gid_t gid)
+ {
+-    int ret;
+-    char *oldlink;
+-    char *tmp;
+-    TALLOC_CTX *tmp_ctx = NULL;
++    char *buf;
++    errno_t ret;
++    struct timespec timebuf[2];
+ 
+-    tmp_ctx = talloc_new(cctx);
+-    if (!tmp_ctx) {
++    buf = talloc_readlinkat(NULL, src_dir_fd, file_name);
++    if (!buf) {
+         return ENOMEM;
+     }
+ 
+-    /*
+-     * Get the name of the file which the link points
+-     * to.  If that name begins with the original
+-     * source directory name, that part of the link
+-     * name will be replaced with the original
+-     * destination directory name.
+-     */
+-    oldlink = talloc_readlink(tmp_ctx, src);
+-    if (oldlink == NULL) {
+-        ret = ENOMEM;
+-        goto done;
++    ret = selinux_file_context(full_path);
++    if (ret != 0) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("Failed to set SELinux context for [%s]\n", full_path));
++        /* Not fatal */
+     }
+ 
+-    /* If src was a link to an entry of the src_orig directory itself,
+-     * create a link to the corresponding entry in the dst_orig
+-     * directory.
+-     * FIXME: This may change a relative link to an absolute link
+-     */
+-    if (strncmp(oldlink, cctx->src_orig, strlen(cctx->src_orig)) == 0) {
+-        tmp = talloc_asprintf(tmp_ctx, "%s%s", cctx->dst_orig, oldlink + strlen(cctx->src_orig));
+-        if (tmp == NULL) {
+-            ret = ENOMEM;
+-            goto done;
++    ret = symlinkat(buf, dst_dir_fd, file_name);
++    talloc_free(buf);
++    if (ret == -1) {
++        ret = errno;
++        if (ret == EEXIST) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  ("symlink pointing to already exists at '%s'\n", full_path));
++            return EOK;
+         }
+ 
+-        talloc_free(oldlink);
+-        oldlink = tmp;
++        DEBUG(SSSDBG_CRIT_FAILURE, ("symlinkat failed: %s\n", strerror(ret)));
++        return ret;
+     }
+ 
+-    selinux_file_context(dst);
+-
+-    ret = symlink(oldlink, dst);
+-    if (ret != 0) {
++    ret = fchownat(dst_dir_fd, file_name,
++                   uid, gid, AT_SYMLINK_NOFOLLOW);
++    if (ret == -1) {
+         ret = errno;
+-        DEBUG(1, ("symlink() failed on file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto done;
++        DEBUG(SSSDBG_CRIT_FAILURE,
++             ("fchownat failed: %s\n", strerror(ret)));
++        return ret;
+     }
+ 
+-    ret = lchown(dst, uid, gid);
+-    if (ret != 0) {
++    timebuf[0] = statp->st_atim;
++    timebuf[1] = statp->st_mtim;
++    ret = utimensat(dst_dir_fd, file_name, timebuf,
++                    AT_SYMLINK_NOFOLLOW);
++    if (ret == -1) {
+         ret = errno;
+-        DEBUG(1, ("lchown() failed on file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto done;
++        DEBUG(SSSDBG_MINOR_FAILURE, ("utimensat failed [%d]: %s\n",
++              ret, strerror(ret)));
++        /* Do not fail */
+     }
+ 
+-done:
+-    talloc_free(tmp_ctx);
+-    return ret;
++    return EOK;
+ }
+ 
+-static int copy_special(const char *dst,
++/* Create a special file named file_name under a directory with file
++ * descriptor dst_dir_fd. full_path is used for both setting SELinux
++ * context and logging. The node is owned by uid/gid and its mode
++ * and device number is read from statp.
++ */
++static int copy_special(int dst_dir_fd,
++                        const char *file_name,
++                        const char *full_path,
+                         const struct stat *statp,
+-                        const struct timeval mt[],
+                         uid_t uid, gid_t gid)
+ {
+-    int ret = 0;
++    int ret;
++    struct timespec timebuf[2];
+ 
+-    selinux_file_context(dst);
++    ret = selinux_file_context(full_path);
++    if (ret != 0) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("Failed to set SELinux context for [%s]\n", full_path));
++        /* Not fatal */
++    }
+ 
+-    ret = mknod(dst, statp->st_mode & ~07777, statp->st_rdev);
++    ret = mknodat(dst_dir_fd, file_name, statp->st_mode & ~07777,
++                  statp->st_rdev);
+     if (ret != 0) {
+         ret = errno;
+-        DEBUG(1, ("Cannot mknod special file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Cannot mknod special file '%s': [%d][%s].\n",
++              full_path, ret, strerror(ret)));
+         return ret;
+     }
+ 
+-    ret = chown(dst, uid, gid);
++    ret = fchownat(dst_dir_fd, file_name, uid, gid, 0);
+     if (ret != 0) {
+         ret = errno;
+-        DEBUG(1, ("Cannot chown special file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("fchownat failed for '%s': [%d][%s]\n",
++              full_path, ret, strerror(ret)));
+         return ret;
+     }
+ 
+-    ret = chmod(dst, statp->st_mode & 07777);
++    ret = fchmodat(dst_dir_fd, file_name, statp->st_mode & 07777, 0);
+     if (ret != 0) {
+         ret = errno;
+-        DEBUG(1, ("Cannot chmod special file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              ("fchmodat failed for '%s': [%d][%s]\n",
++              full_path, ret, strerror(ret)));
+         return ret;
+     }
+ 
+-    ret = utimes(dst, mt);
+-    if (ret != 0) {
++    timebuf[0] = statp->st_atim;
++    timebuf[1] = statp->st_mtim;
++    ret = utimensat(dst_dir_fd, file_name, timebuf, 0);
++    if (ret == -1) {
+         ret = errno;
+-        DEBUG(1, ("Cannot call utimes on special file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        return ret;
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("utimensat failed for '%s': [%d][%s]\n",
++              full_path, ret, strerror(ret)));
++        /* Do not fail, this shouldn't be fatal */
+     }
+ 
+     return EOK;
+ }
+ 
+-static int copy_file(const char *src,
+-                     const char *dst,
+-                     const struct stat *statp,
+-                     const struct timeval mt[],
+-                     uid_t uid, gid_t gid)
++/* Copy bytes from input file descriptor ifd into file named
++ * dst_named under directory with dest_dir_fd. Own the new file
++ * by uid/gid
++ */
++static int
++copy_file(int ifd,
++          int dest_dir_fd,
++          const char *file_name,
++          const char *full_path,
++          const struct stat *statp,
++          uid_t uid, gid_t gid)
+ {
+-    int ret;
+-    int ifd = -1;
+     int ofd = -1;
++    errno_t ret;
+     char buf[1024];
+-    ssize_t cnt, written, res;
+-    struct stat fstatbuf;
+-
+-    ifd = open(src, O_RDONLY);
+-    if (ifd < 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot open() source file '%s': [%d][%s].\n",
+-                  src, ret, strerror(ret)));
+-        goto fail;
+-    }
++    ssize_t cnt, written;
++    struct timespec timebuf[2];
+ 
+-    ret = fstat(ifd, &fstatbuf);
++    ret = selinux_file_context(full_path);
+     if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot fstat() source file '%s': [%d][%s].\n",
+-                  src, ret, strerror(ret)));
+-        goto fail;
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              ("Failed to set SELinux context for [%s]\n", full_path));
++        /* Not fatal */
+     }
+ 
+-    if (statp->st_dev != fstatbuf.st_dev ||
+-        statp->st_ino != fstatbuf.st_ino) {
+-        DEBUG(1, ("File %s was modified between lstat and open.\n", src));
+-        ret = EIO;
+-        goto fail;
+-    }
+-
+-    selinux_file_context(dst);
+-
+-    ofd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
+-    if (ofd < 0) {
++    /* Start with absolutely restrictive permissions */
++    ofd = openat(dest_dir_fd, file_name,
++                 O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW,
++                 0);
++    if (ofd < 0 && errno != EEXIST) {
+         ret = errno;
+-        DEBUG(1, ("Cannot open() destination file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto fail;
+-    }
+-
+-    ret = fchown(ofd, uid, gid);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot fchown() destination file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto fail;
+-    }
+-
+-    ret = fchmod(ofd, statp->st_mode & 07777);
+-    if (ret != 0) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot fchmod() destination file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto fail;
++        DEBUG(SSSDBG_OP_FAILURE,
++               ("Cannot open() destination file '%s': [%d][%s].\n",
++               full_path, ret, strerror(ret)));
++        goto done;
+     }
+ 
+-    while ((cnt = read(ifd, buf, sizeof(buf))) != 0) {
++    while ((cnt = sss_atomic_read(ifd, buf, sizeof(buf))) != 0) {
+         if (cnt == -1) {
+-            if (errno == EINTR || errno == EAGAIN) {
+-                continue;
+-            }
+-
+-            DEBUG(1, ("Cannot read() from source file '%s': [%d][%s].\n",
+-                        src, ret, strerror(ret)));
+-            goto fail;
++            ret = errno;
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("Cannot read() from source file: [%d][%s].\n",
++                   ret, strerror(ret)));
++            goto done;
+         }
+-        else if (cnt > 0) {
+-            /* Copy the buffer to the new file */
+-            written = 0;
+-            while (written < cnt) {
+-                res = write(ofd, buf+written, (size_t)cnt-written);
+-                if (res == -1) {
+-                    ret = errno;
+-                    if (ret == EINTR || ret == EAGAIN) {
+-                        /* retry the write */
+-                        continue;
+-                    }
+-                    DEBUG(1, ("Cannot write() to destination file '%s': [%d][%s].\n",
+-                                dst, ret, strerror(ret)));
+-                    goto fail;
+-                }
+-                else if (res <= 0) {
+-                    DEBUG(1, ("Unexpected result from write(): [%d]\n", res));
+-                    goto fail;
+-                }
+-
+-                written += res;
+-            }
++
++        errno = 0;
++        written = sss_atomic_write(ofd, buf, cnt);
++        if (written == -1) {
++            ret = errno;
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("Cannot write() to destination file: [%d][%s].\n",
++                   ret, strerror(ret)));
++            goto done;
+         }
+-        else {
+-            DEBUG(1, ("Unexpected return code of read [%d]\n", cnt));
+-            goto fail;
++
++        if (written != cnt) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  ("Wrote %d bytes, expected %d\n", written, cnt));
++            goto done;
+         }
+     }
+ 
+-    ret = close(ifd);
+-    ifd = -1;
+-    if (ret != 0) {
++    /* Set the ownership; permissions are still
++     * restrictive. */
++    ret = fchown(ofd, uid, gid);
++    if (ret == -1 && errno != EPERM) {
+         ret = errno;
+-        DEBUG(1, ("Cannot close() source file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto fail;
++        DEBUG(SSSDBG_OP_FAILURE,
++              ("Error changing owner of '%s': %s\n",
++              full_path, strerror(ret)));
++        goto done;
+     }
+ 
+-    ret = close(ofd);
+-    ifd = -1;
+-    if (ret != 0) {
++    /* Set the desired mode. */
++    ret = fchmod(ofd, statp->st_mode);
++    if (ret == -1) {
+         ret = errno;
+-        DEBUG(1, ("Cannot close() destination file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto fail;
++        DEBUG(SSSDBG_OP_FAILURE, ("Error changing owner of '%s': %s\n",
++              full_path, strerror(ret)));
++              goto done;
+     }
+ 
+-    ret = utimes(dst, mt);
+-    if (ret != 0) {
++    timebuf[0] = statp->st_atim;
++    timebuf[1] = statp->st_mtim;
++    ret = futimens(ofd, timebuf);
++    if (ret == -1) {
+         ret = errno;
+-        DEBUG(1, ("Cannot call utimes() on destination file '%s': [%d][%s].\n",
+-                  dst, ret, strerror(ret)));
+-        goto fail;
++        DEBUG(SSSDBG_MINOR_FAILURE, ("futimens failed [%d]: %s\n",
++              ret, strerror(ret)));
++        /* Do not fail */
+     }
+ 
+-    return EOK;
++    close(ofd);
++    ofd = -1;
++    ret = EOK;
+ 
+-    /* Reachable by jump only */
+-fail:
+-    if (ifd != -1) close(ifd);
++done:
+     if (ofd != -1) close(ofd);
+     return ret;
+ }
+ 
+-/*
+- * The context is not freed in case of error
+- * because this is a recursive function, will be freed when we
+- * reach the top level copy_tree() again
+- */
+-static int copy_entry(struct copy_ctx *cctx,
+-                      const char *src,
+-                      const char *dst,
+-                      uid_t uid,
+-                      gid_t gid)
++static errno_t
++copy_dir(struct copy_ctx *cctx,
++         int src_dir_fd, const char *src_dir_path,
++         int dest_parent_fd, const char *dest_dir_name,
++         const char *dest_dir_path,
++         mode_t mode,
++         const struct stat *src_dir_stat);
++
++static errno_t
++copy_entry(struct copy_ctx *cctx,
++           int src_dir_fd,
++           const char *src_dir_path,
++           int dest_dir_fd,
++           const char *dest_dir_path,
++           const char *ent_name)
+ {
+-    int ret = EOK;
+-    struct stat sb;
+-    struct timeval mt[2];
+-
+-    ret = lstat(src, &sb);
+-    if (ret == -1) {
+-        ret = errno;
+-        DEBUG(1, ("Cannot lstat() the source file '%s': [%d][%s].\n",
+-                  src, ret, strerror(ret)));
+-        return ret;
++    char *src_ent_path = NULL;
++    char *dest_ent_path = NULL;
++    int ifd = -1;
++    errno_t ret;
++    struct stat st;
++
++    /* Build the path of the source file or directory and its
++     * corresponding member in the new tree. */
++    src_ent_path = talloc_asprintf(cctx, "%s/%s", src_dir_path, ent_name);
++    dest_ent_path = talloc_asprintf(cctx, "%s/%s", dest_dir_path, ent_name);
++    if (!src_ent_path || !dest_ent_path) {
++        ret = ENOMEM;
++        goto done;
+     }
+ 
+-    mt[0].tv_sec  = sb.st_atime;
+-    mt[0].tv_usec = 0;
+-
+-    mt[1].tv_sec  = sb.st_mtime;
+-    mt[1].tv_usec = 0;
++    /* Open the input entry first, then we can fstat() it and be
++     * certain that it is still the same file.  O_NONBLOCK protects
++     * us against FIFOs and perhaps side-effects of the open() of a
++     * device file if there ever was one here, and doesn't matter
++     * for regular files or directories. */
++    ifd = openat(src_dir_fd, ent_name,
++                 O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK);
++    if (ifd == -1 && errno != ELOOP) {
++        /* openat error */
++        ret = errno;
++        DEBUG(SSSDBG_CRIT_FAILURE, ("openat failed on '%s': %s\n",
++              src_ent_path, strerror(ret)));
++        goto done;
++    } else if (ifd == -1 && errno == ELOOP) {
++        /* Should be a symlink.. */
++        ret = fstatat(src_dir_fd, ent_name, &st, AT_SYMLINK_NOFOLLOW);
++        if (ret == -1) {
++            ret = errno;
++            DEBUG(SSSDBG_CRIT_FAILURE, ("fstatat failed on '%s': %s\n",
++                  src_ent_path, strerror(ret)));
++            goto done;
++        }
+ 
+-    if (S_ISLNK (sb.st_mode)) {
+-        ret = copy_symlink(cctx, src, dst, &sb, mt, uid, gid);
++        /* Handle symlinks */
++        ret = copy_symlink(src_dir_fd, dest_dir_fd, ent_name,
++                           dest_ent_path, &st, cctx->uid, cctx->gid);
+         if (ret != EOK) {
+-            DEBUG(1, ("Cannot copy symlink '%s' to '%s': [%d][%s]\n",
+-                      src, dst, ret, strerror(ret)));
++            DEBUG(SSSDBG_OP_FAILURE, ("Cannot copy '%s' to '%s'\n",
++                  src_ent_path, dest_ent_path));
+         }
+-        return ret;
++        goto done;
+     }
+ 
+-    if (S_ISDIR(sb.st_mode)) {
+-        /* Check if we're still on the same FS */
+-        if (sb.st_dev != cctx->src_dev) {
+-            DEBUG(2, ("Will not descend to other FS\n"));
+-            /* Skip this without error */
+-            return EOK;
++    ret = fstat(ifd, &st);
++    if (ret != 0) {
++        ret = errno;
++        DEBUG(SSSDBG_CRIT_FAILURE,
++                ("couldn't stat '%s': %s", src_ent_path, strerror(ret)));
++        goto done;
++    }
++
++    if (S_ISDIR(st.st_mode)) {
++        /* If it's a directory, descend into it. */
++        ret = copy_dir(cctx, ifd, src_ent_path,
++                       dest_dir_fd, ent_name,
++                       dest_ent_path, st.st_mode & 07777,
++                       &st);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                    ("Could recursively copy '%s' to '%s': %s\n",
++                    src_ent_path, dest_dir_fd, strerror(ret)));
++            goto done;
++        }
++    } else if (S_ISREG(st.st_mode)) {
++        /* Copy a regular file */
++        ret = copy_file(ifd, dest_dir_fd, ent_name, dest_ent_path,
++                        &st, cctx->uid, cctx->gid);
++        if (ret) {
++            DEBUG(SSSDBG_OP_FAILURE, ("Cannot copy '%s' to '%s'\n",
++                    src_ent_path, dest_ent_path));
++            goto done;
+         }
+-        return copy_dir(src, dst, &sb, mt, uid, gid);
+-    } else if (!S_ISREG(sb.st_mode)) {
+-        /*
+-         * Deal with FIFOs and special files.  The user really
+-         * shouldn't have any of these, but it seems like it
+-         * would be nice to copy everything ...
+-         */
+-        return copy_special(dst, &sb, mt, uid, gid);
+     } else {
+-        /*
+-         * Create the new file and copy the contents.  The new
+-         * file will be owned by the provided UID and GID values.
+-         */
+-        return copy_file(src, dst, &sb, mt, uid, gid);
++        /* Copy a special file */
++        ret = copy_special(dest_dir_fd, ent_name, dest_ent_path,
++                           &st, cctx->uid, cctx->gid);
++        if (ret) {
++            DEBUG(SSSDBG_OP_FAILURE, ("Cannot copy '%s' to '%s'\n",
++                  src_ent_path, dest_ent_path));
++            goto done;
++        }
+     }
+ 
++    ret = EOK;
++done:
++    talloc_free(src_ent_path);
++    talloc_free(dest_ent_path);
++    if (ifd != -1) close(ifd);
+     return ret;
+ }
+ 
+-/*
+- * The context is not freed in case of error
+- * because this is a recursive function, will be freed when we
+- * reach the top level copy_tree() again
+- */
+-static int copy_tree_ctx(struct copy_ctx *cctx,
+-                         const char *src_root,
+-                         const char *dst_root,
+-                         uid_t uid,
+-                         gid_t gid)
++static errno_t
++copy_dir(struct copy_ctx *cctx,
++         int src_dir_fd, const char *src_dir_path,
++         int dest_parent_fd, const char *dest_dir_name,
++         const char *dest_dir_path,
++         mode_t mode,
++         const struct stat *src_dir_stat)
+ {



More information about the Pkg-sssd-devel mailing list