nbd/server: Add --selinux-label option

Under SELinux, Unix domain sockets have two labels.  One is on the
disk and can be set with commands such as chcon(1).  There is a
different label stored in memory (called the process label).  This can
only be set by the process creating the socket.  When using SELinux +
SVirt and wanting qemu to be able to connect to a qemu-nbd instance,
you must set both labels correctly first.

For qemu-nbd the options to set the second label are awkward.  You can
create the socket in a wrapper program and then exec into qemu-nbd.
Or you could try something with LD_PRELOAD.

This commit adds the ability to set the label straightforwardly on the
command line, via the new --selinux-label flag.  (The name of the flag
is the same as the equivalent nbdkit option.)

A worked example showing how to use the new option can be found in
this bug: https://bugzilla.redhat.com/show_bug.cgi?id=1984938

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1984938
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

[eblake: rebase to configure changes, reject --selinux-label if it is
not compiled in or not used on a Unix socket]
Note that we may relax some of these restrictions at a later date,
such as making it possible to label a TCP socket, although it may be
smarter to do so as a generic QMP action rather than more one-off
command lines in qemu-nbd.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20211115202944.615966-1-eblake@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
[eblake: adjust meson output as suggested by thuth]
Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Richard W.M. Jones 2021-11-15 14:29:43 -06:00 committed by Eric Blake
parent 76df2b8d69
commit 3d212b41e9
10 changed files with 67 additions and 1 deletions

View File

@ -1201,6 +1201,11 @@ keyutils = dependency('libkeyutils', required: false,
has_gettid = cc.has_function('gettid') has_gettid = cc.has_function('gettid')
# libselinux
selinux = dependency('libselinux',
required: get_option('selinux'),
method: 'pkg-config', kwargs: static_kwargs)
# Malloc tests # Malloc tests
malloc = [] malloc = []
@ -1479,6 +1484,7 @@ config_host_data.set('CONFIG_SPICE_PROTOCOL', spice_protocol.found())
config_host_data.set('CONFIG_SPICE', spice.found()) config_host_data.set('CONFIG_SPICE', spice.found())
config_host_data.set('CONFIG_X11', x11.found()) config_host_data.set('CONFIG_X11', x11.found())
config_host_data.set('CONFIG_CFI', get_option('cfi')) config_host_data.set('CONFIG_CFI', get_option('cfi'))
config_host_data.set('CONFIG_SELINUX', selinux.found())
config_host_data.set('QEMU_VERSION', '"@0@"'.format(meson.project_version())) config_host_data.set('QEMU_VERSION', '"@0@"'.format(meson.project_version()))
config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0]) config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0])
config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1]) config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1])
@ -3054,7 +3060,8 @@ if have_tools
qemu_io = executable('qemu-io', files('qemu-io.c'), qemu_io = executable('qemu-io', files('qemu-io.c'),
dependencies: [block, qemuutil], install: true) dependencies: [block, qemuutil], install: true)
qemu_nbd = executable('qemu-nbd', files('qemu-nbd.c'), qemu_nbd = executable('qemu-nbd', files('qemu-nbd.c'),
dependencies: [blockdev, qemuutil, gnutls], install: true) dependencies: [blockdev, qemuutil, gnutls, selinux],
install: true)
subdir('storage-daemon') subdir('storage-daemon')
subdir('contrib/rdmacm-mux') subdir('contrib/rdmacm-mux')
@ -3430,6 +3437,7 @@ summary_info += {'libdaxctl support': libdaxctl}
summary_info += {'libudev': libudev} summary_info += {'libudev': libudev}
# Dummy dependency, keep .found() # Dummy dependency, keep .found()
summary_info += {'FUSE lseek': fuse_lseek.found()} summary_info += {'FUSE lseek': fuse_lseek.found()}
summary_info += {'selinux': selinux}
summary(summary_info, bool_yn: true, section: 'Dependencies') summary(summary_info, bool_yn: true, section: 'Dependencies')
if not supported_cpus.contains(cpu) if not supported_cpus.contains(cpu)

View File

@ -201,3 +201,6 @@ option('slirp', type: 'combo', value: 'auto',
option('fdt', type: 'combo', value: 'auto', option('fdt', type: 'combo', value: 'auto',
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'], choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
description: 'Whether and how to find the libfdt library') description: 'Whether and how to find the libfdt library')
option('selinux', type: 'feature', value: 'auto',
description: 'SELinux support in qemu-nbd')

View File

@ -47,6 +47,10 @@
#include "trace/control.h" #include "trace/control.h"
#include "qemu-version.h" #include "qemu-version.h"
#ifdef CONFIG_SELINUX
#include <selinux/selinux.h>
#endif
#ifdef __linux__ #ifdef __linux__
#define HAVE_NBD_DEVICE 1 #define HAVE_NBD_DEVICE 1
#else #else
@ -64,6 +68,7 @@
#define QEMU_NBD_OPT_FORK 263 #define QEMU_NBD_OPT_FORK 263
#define QEMU_NBD_OPT_TLSAUTHZ 264 #define QEMU_NBD_OPT_TLSAUTHZ 264
#define QEMU_NBD_OPT_PID_FILE 265 #define QEMU_NBD_OPT_PID_FILE 265
#define QEMU_NBD_OPT_SELINUX_LABEL 266
#define MBR_SIZE 512 #define MBR_SIZE 512
@ -116,6 +121,9 @@ static void usage(const char *name)
" --fork fork off the server process and exit the parent\n" " --fork fork off the server process and exit the parent\n"
" once the server is running\n" " once the server is running\n"
" --pid-file=PATH store the server's process ID in the given file\n" " --pid-file=PATH store the server's process ID in the given file\n"
#ifdef CONFIG_SELINUX
" --selinux-label=LABEL set SELinux process label on listening socket\n"
#endif
#if HAVE_NBD_DEVICE #if HAVE_NBD_DEVICE
"\n" "\n"
"Kernel NBD client support:\n" "Kernel NBD client support:\n"
@ -454,6 +462,7 @@ static const char *socket_activation_validate_opts(const char *device,
const char *sockpath, const char *sockpath,
const char *address, const char *address,
const char *port, const char *port,
const char *selinux,
bool list) bool list)
{ {
if (device != NULL) { if (device != NULL) {
@ -472,6 +481,10 @@ static const char *socket_activation_validate_opts(const char *device,
return "TCP port number can't be set when using socket activation"; return "TCP port number can't be set when using socket activation";
} }
if (selinux != NULL) {
return "SELinux label can't be set when using socket activation";
}
if (list) { if (list) {
return "List mode is incompatible with socket activation"; return "List mode is incompatible with socket activation";
} }
@ -534,6 +547,8 @@ int main(int argc, char **argv)
{ "trace", required_argument, NULL, 'T' }, { "trace", required_argument, NULL, 'T' },
{ "fork", no_argument, NULL, QEMU_NBD_OPT_FORK }, { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK },
{ "pid-file", required_argument, NULL, QEMU_NBD_OPT_PID_FILE }, { "pid-file", required_argument, NULL, QEMU_NBD_OPT_PID_FILE },
{ "selinux-label", required_argument, NULL,
QEMU_NBD_OPT_SELINUX_LABEL },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
int ch; int ch;
@ -560,6 +575,7 @@ int main(int argc, char **argv)
int old_stderr = -1; int old_stderr = -1;
unsigned socket_activation; unsigned socket_activation;
const char *pid_file_name = NULL; const char *pid_file_name = NULL;
const char *selinux_label = NULL;
BlockExportOptions *export_opts; BlockExportOptions *export_opts;
#ifdef CONFIG_POSIX #ifdef CONFIG_POSIX
@ -749,6 +765,9 @@ int main(int argc, char **argv)
case QEMU_NBD_OPT_PID_FILE: case QEMU_NBD_OPT_PID_FILE:
pid_file_name = optarg; pid_file_name = optarg;
break; break;
case QEMU_NBD_OPT_SELINUX_LABEL:
selinux_label = optarg;
break;
} }
} }
@ -788,6 +807,7 @@ int main(int argc, char **argv)
/* Using socket activation - check user didn't use -p etc. */ /* Using socket activation - check user didn't use -p etc. */
const char *err_msg = socket_activation_validate_opts(device, sockpath, const char *err_msg = socket_activation_validate_opts(device, sockpath,
bindto, port, bindto, port,
selinux_label,
list); list);
if (err_msg != NULL) { if (err_msg != NULL) {
error_report("%s", err_msg); error_report("%s", err_msg);
@ -827,6 +847,18 @@ int main(int argc, char **argv)
} }
} }
if (selinux_label) {
#ifdef CONFIG_SELINUX
if (sockpath == NULL && device == NULL) {
error_report("--selinux-label is not permitted without --socket");
exit(EXIT_FAILURE);
}
#else
error_report("SELinux support not enabled in this binary");
exit(EXIT_FAILURE);
#endif
}
if (list) { if (list) {
saddr = nbd_build_socket_address(sockpath, bindto, port); saddr = nbd_build_socket_address(sockpath, bindto, port);
return qemu_nbd_client_list(saddr, tlscreds, bindto); return qemu_nbd_client_list(saddr, tlscreds, bindto);
@ -940,6 +972,13 @@ int main(int argc, char **argv)
} else { } else {
backlog = MIN(shared, SOMAXCONN); backlog = MIN(shared, SOMAXCONN);
} }
#ifdef CONFIG_SELINUX
if (selinux_label && setsockcreatecon_raw(selinux_label) == -1) {
error_report("Cannot set SELinux socket create context to %s: %s",
selinux_label, strerror(errno));
exit(EXIT_FAILURE);
}
#endif
saddr = nbd_build_socket_address(sockpath, bindto, port); saddr = nbd_build_socket_address(sockpath, bindto, port);
if (qio_net_listener_open_sync(server, saddr, backlog, if (qio_net_listener_open_sync(server, saddr, backlog,
&local_err) < 0) { &local_err) < 0) {
@ -947,6 +986,13 @@ int main(int argc, char **argv)
error_report_err(local_err); error_report_err(local_err);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#ifdef CONFIG_SELINUX
if (selinux_label && setsockcreatecon_raw(NULL) == -1) {
error_report("Cannot clear SELinux socket create context: %s",
strerror(errno));
exit(EXIT_FAILURE);
}
#endif
} else { } else {
size_t i; size_t i;
/* See comment in check_socket_activation above. */ /* See comment in check_socket_activation above. */

View File

@ -72,6 +72,7 @@ meson_options_help() {
printf "%s\n" ' sdl SDL user interface' printf "%s\n" ' sdl SDL user interface'
printf "%s\n" ' sdl-image SDL Image support for icons' printf "%s\n" ' sdl-image SDL Image support for icons'
printf "%s\n" ' seccomp seccomp support' printf "%s\n" ' seccomp seccomp support'
printf "%s\n" ' selinux SELinux support in qemu-nbd'
printf "%s\n" ' smartcard CA smartcard emulation support' printf "%s\n" ' smartcard CA smartcard emulation support'
printf "%s\n" ' snappy snappy compression support' printf "%s\n" ' snappy snappy compression support'
printf "%s\n" ' sparse sparse checker' printf "%s\n" ' sparse sparse checker'
@ -215,6 +216,8 @@ _meson_option_parse() {
--disable-sdl-image) printf "%s" -Dsdl_image=disabled ;; --disable-sdl-image) printf "%s" -Dsdl_image=disabled ;;
--enable-seccomp) printf "%s" -Dseccomp=enabled ;; --enable-seccomp) printf "%s" -Dseccomp=enabled ;;
--disable-seccomp) printf "%s" -Dseccomp=disabled ;; --disable-seccomp) printf "%s" -Dseccomp=disabled ;;
--enable-selinux) printf "%s" -Dselinux=enabled ;;
--disable-selinux) printf "%s" -Dselinux=disabled ;;
--enable-slirp) printf "%s" -Dslirp=enabled ;; --enable-slirp) printf "%s" -Dslirp=enabled ;;
--disable-slirp) printf "%s" -Dslirp=disabled ;; --disable-slirp) printf "%s" -Dslirp=disabled ;;
--enable-slirp=*) quote_sh "-Dslirp=$2" ;; --enable-slirp=*) quote_sh "-Dslirp=$2" ;;

View File

@ -51,6 +51,7 @@ ENV PACKAGES \
libpng-devel \ libpng-devel \
librbd-devel \ librbd-devel \
libseccomp-devel \ libseccomp-devel \
libselinux-devel \
libslirp-devel \ libslirp-devel \
libssh-devel \ libssh-devel \
libtasn1-devel \ libtasn1-devel \

View File

@ -8,6 +8,7 @@ ENV PACKAGES \
gcc \ gcc \
git \ git \
libffi-devel.i686 \ libffi-devel.i686 \
libselinux-devel.i686 \
libtasn1-devel.i686 \ libtasn1-devel.i686 \
libzstd-devel.i686 \ libzstd-devel.i686 \
make \ make \

View File

@ -53,6 +53,7 @@ ENV PACKAGES \
libpng-devel \ libpng-devel \
librbd-devel \ librbd-devel \
libseccomp-devel \ libseccomp-devel \
libselinux-devel \
libslirp-devel \ libslirp-devel \
libssh-devel \ libssh-devel \
libtasn1-devel \ libtasn1-devel \

View File

@ -55,6 +55,7 @@ ENV PACKAGES \
libpulse-devel \ libpulse-devel \
librbd-devel \ librbd-devel \
libseccomp-devel \ libseccomp-devel \
libselinux-devel \
libspice-server-devel \ libspice-server-devel \
libssh-devel \ libssh-devel \
libtasn1-devel \ libtasn1-devel \

View File

@ -60,6 +60,7 @@ ENV PACKAGES \
libsdl2-dev \ libsdl2-dev \
libsdl2-image-dev \ libsdl2-image-dev \
libseccomp-dev \ libseccomp-dev \
libselinux-dev \
libsnappy-dev \ libsnappy-dev \
libspice-protocol-dev \ libspice-protocol-dev \
libspice-server-dev \ libspice-server-dev \

View File

@ -60,6 +60,7 @@ ENV PACKAGES \
libsdl2-dev \ libsdl2-dev \
libsdl2-image-dev \ libsdl2-image-dev \
libseccomp-dev \ libseccomp-dev \
libselinux-dev \
libslirp-dev \ libslirp-dev \
libsnappy-dev \ libsnappy-dev \
libspice-protocol-dev \ libspice-protocol-dev \