summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
authorLuca Boccassi <luca.boccassi@microsoft.com>2021-09-16 12:47:42 +0100
committerLuca Boccassi <luca.boccassi@microsoft.com>2021-09-20 14:23:55 +0100
commit409f4cf1f2bc797bb6a73eab310b707308a8352e (patch)
tree2a48a5abad0f602dce7d6da4696f518abb613a25 /src/portable
parent447d46879ca2b7c001a3d27d6a89ef05fb77c297 (diff)
downloadsystemd-409f4cf1f2bc797bb6a73eab310b707308a8352e.tar.gz
portable: copy SELinux label when extracting units from images
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.
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/portable.c46
-rw-r--r--src/portable/portable.h1
2 files changed, 42 insertions, 5 deletions
diff --git a/src/portable/portable.c b/src/portable/portable.c
index 8af7fe2aef..4daafea3d3 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -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);
diff --git a/src/portable/portable.h b/src/portable/portable.h
index 94144287ae..077ab3333f 100644
--- a/src/portable/portable.h
+++ b/src/portable/portable.h
@@ -12,6 +12,7 @@ typedef struct PortableMetadata {
int fd;
char *source;
char *image_path;
+ char *selinux_label;
char name[];
} PortableMetadata;