]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
portable: copy SELinux label when extracting units from images 20767/head
authorLuca Boccassi <luca.boccassi@microsoft.com>
Thu, 16 Sep 2021 11:47:42 +0000 (12:47 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Mon, 20 Sep 2021 13:23:55 +0000 (14:23 +0100)
Units are copied out via sendmsg datafd from images, but that means
the SELinux labels get lost in transit. Extract them and copy them over.

Given recvmsg cannot use multiple IOV transparently when the sizes are
variable, use a '\0' as a separator between the filename and the label.

meson.build
src/portable/portable.c
src/portable/portable.h

index 8d5a1055febf811d049e6154309b020a09e7d84f..4c4e9512eaa7d4274f35eb994cb68128fc86289b 100644 (file)
@@ -2291,7 +2291,7 @@ if conf.get('ENABLE_PORTABLED') == 1
                 systemd_portabled_sources,
                 include_directories : includes,
                 link_with : [libshared],
-                dependencies : [threads],
+                dependencies : [threads, libselinux],
                 install_rpath : rootlibexecdir,
                 install : true,
                 install_dir : rootlibexecdir)
index 8af7fe2aefb2e4cdb546e77b51657f8acc8ce588..4daafea3d391bd38cd051762a1b53305cd2c2878 100644 (file)
@@ -28,6 +28,7 @@
 #include "path-lookup.h"
 #include "portable.h"
 #include "process-util.h"
+#include "selinux-util.h"
 #include "set.h"
 #include "signal-util.h"
 #include "socket-util.h"
@@ -78,7 +79,7 @@ static bool unit_match(const char *unit, char **matches) {
         return false;
 }
 
-static PortableMetadata *portable_metadata_new(const char *name, const char *path, int fd) {
+static PortableMetadata *portable_metadata_new(const char *name, const char *path, const char *selinux_label, int fd) {
         PortableMetadata *m;
 
         m = malloc0(offsetof(PortableMetadata, name) + strlen(name) + 1);
@@ -92,6 +93,15 @@ static PortableMetadata *portable_metadata_new(const char *name, const char *pat
                         return mfree(m);
         }
 
+        /* The metadata file might have SELinux labels, we need to carry them and reapply them */
+        if (!isempty(selinux_label)) {
+                m->selinux_label = strdup(selinux_label);
+                if (!m->selinux_label) {
+                        free(m->image_path);
+                        return mfree(m);
+                }
+        }
+
         strcpy(m->name, name);
         m->fd = fd;
 
@@ -105,6 +115,7 @@ PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
         safe_close(i->fd);
         free(i->source);
         free(i->image_path);
+        free(i->selinux_label);
 
         return mfree(i);
 }
@@ -201,6 +212,7 @@ static int extract_now(
                 if (socket_fd >= 0) {
                         struct iovec iov[] = {
                                 IOVEC_MAKE_STRING(os_release_id),
+                                IOVEC_MAKE((char *)"\0", sizeof(char)),
                         };
 
                         r = send_one_fd_iov_with_data_fd(socket_fd, iov, ELEMENTSOF(iov), os_release_fd);
@@ -209,7 +221,7 @@ static int extract_now(
                 }
 
                 if (ret_os_release) {
-                        os_release = portable_metadata_new(os_release_id, NULL, os_release_fd);
+                        os_release = portable_metadata_new(os_release_id, NULL, NULL, os_release_fd);
                         if (!os_release)
                                 return -ENOMEM;
 
@@ -264,8 +276,19 @@ static int extract_now(
                         }
 
                         if (socket_fd >= 0) {
+                                _cleanup_(mac_selinux_freep) char *con = NULL;
+#if HAVE_SELINUX
+                                /* The units will be copied on the host's filesystem, so if they had a SELinux label
+                                 * we have to preserve it. Copy it out so that it can be applied later. */
+
+                                r = fgetfilecon_raw(fd, &con);
+                                if (r < 0 && errno != ENODATA)
+                                        log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name);
+#endif
                                 struct iovec iov[] = {
                                         IOVEC_MAKE_STRING(de->d_name),
+                                        IOVEC_MAKE((char *)"\0", sizeof(char)),
+                                        IOVEC_MAKE_STRING(strempty(con)),
                                 };
 
                                 r = send_one_fd_iov_with_data_fd(socket_fd, iov, ELEMENTSOF(iov), fd);
@@ -273,7 +296,7 @@ static int extract_now(
                                         return log_debug_errno(r, "Failed to send unit metadata to parent: %m");
                         }
 
-                        m = portable_metadata_new(de->d_name, NULL, fd);
+                        m = portable_metadata_new(de->d_name, NULL, NULL, fd);
                         if (!m)
                                 return -ENOMEM;
                         fd = -1;
@@ -401,7 +424,11 @@ static int portable_extract_by_path(
                 for (;;) {
                         _cleanup_(portable_metadata_unrefp) PortableMetadata *add = NULL;
                         _cleanup_close_ int fd = -1;
-                        char iov_buffer[PATH_MAX + 2];
+                        /* We use NAME_MAX space for the SELinux label here. The kernel currently enforces no limit, but
+                         * according to suggestions from the SELinux people this will change and it will probably be
+                         * identical to NAME_MAX. For now we use that, but this should be updated one day when the final
+                         * limit is known. */
+                        char iov_buffer[PATH_MAX + NAME_MAX + 2];
                         struct iovec iov = IOVEC_INIT(iov_buffer, sizeof(iov_buffer));
 
                         ssize_t n = receive_one_fd_iov(seq[0], &iov, 1, 0, &fd);
@@ -420,7 +447,13 @@ static int portable_extract_by_path(
                                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Invalid item sent from child.");
 
-                        add = portable_metadata_new(iov_buffer, path, fd);
+                        /* Given recvmsg cannot be used with multiple io vectors if you don't know the size in advance,
+                         * use a marker to separate the name and the optional SELinux context. */
+                        char *selinux_label = memchr(iov_buffer, 0, n);
+                        assert(selinux_label);
+                        selinux_label++;
+
+                        add = portable_metadata_new(iov_buffer, path, selinux_label, fd);
                         if (!add)
                                 return -ENOMEM;
                         fd = -1;
@@ -1065,7 +1098,10 @@ static int attach_unit_file(
                 _cleanup_(unlink_and_freep) char *tmp = NULL;
                 _cleanup_close_ int fd = -1;
 
+                (void) mac_selinux_create_file_prepare_label(path, m->selinux_label);
+
                 fd = open_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &tmp);
+                mac_selinux_create_file_clear(); /* Clear immediately in case of errors */
                 if (fd < 0)
                         return log_debug_errno(fd, "Failed to create unit file '%s': %m", path);
 
index 94144287aec2016af4cd08775d8bcdd4e40a1ea2..077ab3333f18f9fc0b8c6ef5eea1666eeeefe829 100644 (file)
@@ -12,6 +12,7 @@ typedef struct PortableMetadata {
         int fd;
         char *source;
         char *image_path;
+        char *selinux_label;
         char name[];
 } PortableMetadata;