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.
This commit is contained in:
Luca Boccassi 2021-09-16 12:47:42 +01:00
parent 447d46879c
commit 409f4cf1f2
3 changed files with 43 additions and 6 deletions

View 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)

View 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);

View file

@ -12,6 +12,7 @@ typedef struct PortableMetadata {
int fd;
char *source;
char *image_path;
char *selinux_label;
char name[];
} PortableMetadata;