]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
machined: when copying files from/to userns containers chown to root
authorLennart Poettering <lennart@poettering.net>
Mon, 13 Feb 2017 18:24:01 +0000 (19:24 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 17 Feb 2017 09:22:28 +0000 (10:22 +0100)
This changes the file copy logic of machined to set the UID/GID of all
copied files to 0 if the host and container do not share the same user
namespace.

Fixes: #4078
man/machinectl.xml
src/basic/copy.c
src/basic/copy.h
src/machine/machine-dbus.c
src/test/test-copy.c
src/tmpfiles/tmpfiles.c

index 38cf919a78f4542d0242966118add5b4cc716eb9..b96aea1a48a9311980c0f9cc3212787e99a32a61 100644 (file)
         system into a running container. Takes a container name,
         followed by the source path on the host and the destination
         path in the container. If the destination path is omitted, the
-        same as the source path is used.</para></listitem>
-      </varlistentry>
+        same as the source path is used.</para>
 
+        <para>If host and container share the same user and group namespace, file ownership by numeric user ID and
+        group ID is preserved for the copy, otherwise all files and directories in the copy will be owned by the root
+        user and group (UID/GID 0).</para></listitem>
+      </varlistentry>
 
       <varlistentry>
         <term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
         into the host system. Takes a container name, followed by the
         source path in the container the destination path on the host.
         If the destination path is omitted, the same as the source path
-        is used.</para></listitem>
+        is used.</para>
+
+        <para>If host and container share the same user and group namespace, file ownership by numeric user ID and
+        group ID is preserved for the copy, otherwise all files and directories in the copy will be owned by the root
+        user and group (UID/GID 0).</para></listitem>
       </varlistentry>
     </variablelist></refsect2>
 
index f0a975d46102108c173707d7bd9805f1b2a21b0e..6273ac9b4768c65aaf82dcc7314a490630531c51 100644 (file)
@@ -45,6 +45,7 @@
 #include "strv.h"
 #include "time-util.h"
 #include "umask-util.h"
+#include "user-util.h"
 #include "xattr-util.h"
 
 #define COPY_BUFFER_SIZE (16*1024u)
@@ -176,7 +177,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
         return 0; /* return 0 if we hit EOF earlier than the size limit */
 }
 
-static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_symlink(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags) {
+
         _cleanup_free_ char *target = NULL;
         int r;
 
@@ -191,13 +201,25 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int
         if (symlinkat(target, dt, to) < 0)
                 return -errno;
 
-        if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+        if (fchownat(dt, to,
+                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
+                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
+                     AT_SYMLINK_NOFOLLOW) < 0)
                 return -errno;
 
         return 0;
 }
 
-static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to, CopyFlags copy_flags) {
+static int fd_copy_regular(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags) {
+
         _cleanup_close_ int fdf = -1, fdt = -1;
         struct timespec ts[2];
         int r, q;
@@ -220,7 +242,9 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
                 return r;
         }
 
-        if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+        if (fchown(fdt,
+                   uid_is_valid(override_uid) ? override_uid : st->st_uid,
+                   gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
                 r = -errno;
 
         if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -229,7 +253,6 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
         ts[0] = st->st_atim;
         ts[1] = st->st_mtim;
         (void) futimens(fdt, ts);
-
         (void) copy_xattr(fdf, fdt);
 
         q = close(fdt);
@@ -243,7 +266,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
         return r;
 }
 
-static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_fifo(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags) {
         int r;
 
         assert(from);
@@ -254,7 +285,10 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
         if (r < 0)
                 return -errno;
 
-        if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+        if (fchownat(dt, to,
+                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
+                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
+                     AT_SYMLINK_NOFOLLOW) < 0)
                 r = -errno;
 
         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -263,7 +297,15 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
         return r;
 }
 
-static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_node(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags) {
         int r;
 
         assert(from);
@@ -274,7 +316,10 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt,
         if (r < 0)
                 return -errno;
 
-        if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+        if (fchownat(dt, to,
+                     uid_is_valid(override_uid) ? override_uid : st->st_uid,
+                     gid_is_valid(override_gid) ? override_gid : st->st_gid,
+                     AT_SYMLINK_NOFOLLOW) < 0)
                 r = -errno;
 
         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -290,6 +335,8 @@ static int fd_copy_directory(
                 int dt,
                 const char *to,
                 dev_t original_device,
+                uid_t override_uid,
+                gid_t override_gid,
                 CopyFlags copy_flags) {
 
         _cleanup_close_ int fdf = -1, fdt = -1;
@@ -343,15 +390,15 @@ static int fd_copy_directory(
                         continue;
 
                 if (S_ISREG(buf.st_mode))
-                        q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, copy_flags);
+                        q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
                 else if (S_ISDIR(buf.st_mode))
-                        q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, copy_flags);
+                        q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
                 else if (S_ISLNK(buf.st_mode))
-                        q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+                        q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
                 else if (S_ISFIFO(buf.st_mode))
-                        q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+                        q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
-                        q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+                        q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
                 else
                         q = -EOPNOTSUPP;
 
@@ -368,7 +415,9 @@ static int fd_copy_directory(
                         st->st_mtim
                 };
 
-                if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+                if (fchown(fdt,
+                           uid_is_valid(override_uid) ? override_uid : st->st_uid,
+                           gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
                         r = -errno;
 
                 if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -381,7 +430,7 @@ static int fd_copy_directory(
         return r;
 }
 
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, CopyFlags copy_flags) {
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
         struct stat st;
 
         assert(from);
@@ -391,21 +440,21 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, CopyFlags c
                 return -errno;
 
         if (S_ISREG(st.st_mode))
-                return fd_copy_regular(fdf, from, &st, fdt, to, copy_flags);
+                return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
         else if (S_ISDIR(st.st_mode))
-                return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, copy_flags);
+                return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags);
         else if (S_ISLNK(st.st_mode))
-                return fd_copy_symlink(fdf, from, &st, fdt, to);
+                return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
         else if (S_ISFIFO(st.st_mode))
-                return fd_copy_fifo(fdf, from, &st, fdt, to);
+                return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
-                return fd_copy_node(fdf, from, &st, fdt, to);
+                return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
         else
                 return -EOPNOTSUPP;
 }
 
-int copy_tree(const char *from, const char *to, CopyFlags copy_flags) {
-        return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, copy_flags);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+        return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
 }
 
 int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
@@ -420,7 +469,7 @@ int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
         if (!S_ISDIR(st.st_mode))
                 return -ENOTDIR;
 
-        return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, copy_flags);
+        return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
 }
 
 int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
@@ -435,7 +484,7 @@ int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
         if (!S_ISDIR(st.st_mode))
                 return -ENOTDIR;
 
-        return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, copy_flags);
+        return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
 }
 
 int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
index c2f02c455e94ac530856ae7f544c7ced0c15f10a..4f3e11423ef3196392150b980052de2cad801032 100644 (file)
@@ -33,8 +33,8 @@ typedef enum CopyFlags {
 int copy_file_fd(const char *from, int to, CopyFlags copy_flags);
 int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
 int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
-int copy_tree(const char *from, const char *to, CopyFlags copy_flags);
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, CopyFlags copy_flags);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
 int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags);
 int copy_directory(const char *from, const char *to, CopyFlags copy_flags);
 int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags);
index 13d68553dfed10d778c0a0de49e5ec4f42268d33..29fc68b90f0bf9c361df9c8607eaddb2c2d5de32 100644 (file)
@@ -1055,10 +1055,12 @@ finish:
 int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
         _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
+        CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE;
         _cleanup_close_ int hostfd = -1;
         Machine *m = userdata;
         bool copy_from;
         pid_t child;
+        uid_t uid_shift;
         char *t;
         int r;
 
@@ -1097,6 +1099,10 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
         if (r == 0)
                 return 1; /* Will call us back */
 
+        r = machine_get_uid_shift(m, &uid_shift);
+        if (r < 0)
+                return r;
+
         copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
 
         if (copy_from) {
@@ -1151,10 +1157,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
                         goto child_fail;
                 }
 
+                /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
+                 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
+                 * the UID/GIDs as they are. */
                 if (copy_from)
-                        r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, COPY_REFLINK|COPY_MERGE);
+                        r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
                 else
-                        r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, COPY_REFLINK|COPY_MERGE);
+                        r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
 
                 hostfd = safe_close(hostfd);
                 containerfd = safe_close(containerfd);
index 7ccefe9396188edc39bb851a26962c9473c207d3..ed6725611de4958a533c576c42c85fe5d036966f 100644 (file)
@@ -31,6 +31,7 @@
 #include "rm-rf.h"
 #include "string-util.h"
 #include "strv.h"
+#include "user-util.h"
 #include "util.h"
 
 static void test_copy_file(void) {
@@ -125,7 +126,7 @@ static void test_copy_tree(void) {
         unixsockp = strjoina(original_dir, "unixsock");
         assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
 
-        assert_se(copy_tree(original_dir, copy_dir, COPY_REFLINK|COPY_MERGE) == 0);
+        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0);
 
         STRV_FOREACH(p, files) {
                 _cleanup_free_ char *buf = NULL, *f;
@@ -152,8 +153,8 @@ static void test_copy_tree(void) {
         assert_se(stat(unixsockp, &st) >= 0);
         assert_se(S_ISSOCK(st.st_mode));
 
-        assert_se(copy_tree(original_dir, copy_dir, COPY_REFLINK) < 0);
-        assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, COPY_REFLINK) < 0);
+        assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
+        assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
 
         (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
         (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
index 382853a2a5410fbc8aab9b07403b9456703f96de..08a138b1b27158826441f3665b7e9c6142e9921c 100644 (file)
@@ -1170,7 +1170,7 @@ static int create_item(Item *i) {
                         return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
 
                 log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
-                r = copy_tree(resolved, i->path, COPY_REFLINK);
+                r = copy_tree(resolved, i->path, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, COPY_REFLINK);
 
                 if (r == -EROFS && stat(i->path, &st) == 0)
                         r = -EEXIST;