Thanks Daniel and Laine for the feedback so far. Below is a brief list of changes since v1 as well as the patch which should be complete this time. For others, introduction and background can be found here along with latest response: https://www.redhat.com/archives/libvir-list/2016-May/msg00628.html Changes since v1: Security requirements involving kernel patching to bring in a new Smack interface are now documented in "docs/drvlxc.html.in” Fixed the syntax and spacing as requested and recommended by the “make check-syntax” script Moved Smack related block in configure.ac to the dedicated m4 file (m4/virt-smack.m4) Removed the check for Smack-specific driver before calling SetChildProcessLabel in lxc_container.c and moved the function call outside of the namespaces conditional. Replaced calloc/malloc and open/read/close calls with Libvirt specific macros in the Smack driver. Modified src/Makefile to include security driver libraries when compiling libvirt_nss and dependencies configure.ac | 4 + docs/drvlxc.html.in | 14 +- m4/virt-smack.m4 | 69 ++ po/POTFILES.in | 1 + src/Makefile.am | 14 +- src/lxc/lxc_container.c | 24 +- src/lxc/lxc_controller.c | 21 + src/security/security_apparmor.c | 9 + src/security/security_dac.c | 9 + src/security/security_driver.c | 7 + src/security/security_driver.h | 4 + src/security/security_manager.c | 17 + src/security/security_manager.h | 3 + src/security/security_nop.c | 8 + src/security/security_selinux.c | 9 + src/security/security_smack.c | 1484 ++++++++++++++++++++++++++++++++++++++ src/security/security_smack.h | 37 + src/security/security_stack.c | 9 + src/util/vircommand.c | 63 ++ src/util/vircommand.h | 3 + 20 files changed, 1798 insertions(+), 11 deletions(-) create mode 100644 m4/virt-smack.m4 create mode 100644 src/security/security_smack.c create mode 100644 src/security/security_smack.h Signed-off-by: Randy Aybar <raybar@xxxxxxxxx> --- diff --git a/configure.ac b/configure.ac index 378069d..c785914 100644 --- a/configure.ac +++ b/configure.ac @@ -255,6 +255,7 @@ LIBVIRT_CHECK_READLINE LIBVIRT_CHECK_SANLOCK LIBVIRT_CHECK_SASL LIBVIRT_CHECK_SELINUX +LIBVIRT_CHECK_SMACK LIBVIRT_CHECK_SSH2 LIBVIRT_CHECK_SYSTEMD_DAEMON LIBVIRT_CHECK_UDEV @@ -1459,6 +1460,7 @@ if test "$with_apparmor" = "no"; then fi AM_CONDITIONAL([WITH_APPARMOR_PROFILES], [test "$with_apparmor_profiles" != "no"]) + dnl DTrace static probes AC_ARG_WITH([dtrace], [AS_HELP_STRING([--with-dtrace], @@ -2754,6 +2756,7 @@ AC_MSG_NOTICE([Security Drivers]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux ($SELINUX_MOUNT)]) AC_MSG_NOTICE([AppArmor: $with_secdriver_apparmor (install profiles: $with_apparmor_profiles)]) +AC_MSG_NOTICE([Smack: $with_secdriver_smack]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Driver Loadable Modules]) AC_MSG_NOTICE([]) @@ -2784,6 +2787,7 @@ LIBVIRT_RESULT_READLINE LIBVIRT_RESULT_SANLOCK LIBVIRT_RESULT_SASL LIBVIRT_RESULT_SELINUX +LIBVIRT_RESULT_SMACK LIBVIRT_RESULT_SSH2 LIBVIRT_RESULT_SYSTEMD_DAEMON LIBVIRT_RESULT_UDEV diff --git a/docs/drvlxc.html.in b/docs/drvlxc.html.in index 3dc9d59..f96c9ba 100644 --- a/docs/drvlxc.html.in +++ b/docs/drvlxc.html.in @@ -157,8 +157,7 @@ to all containers are <li><code>/dev/console</code> symlinked to <code>/dev/pts/0</code></li> </ul> -<p> -In addition, for every console defined in the guest configuration, +<p> In addition, for every console defined in the guest configuration, a symlink will be created from <code>/dev/ttyN</code> symlinked to the corresponding <code>/dev/pts/M</code> pseudo TTY device. The first console will be <code>/dev/tty1</code>, with further consoles @@ -190,6 +189,17 @@ isolation between a container and the host must ensure that they are writing a suitable configuration. </p> +<p> +NOTE: The SMACK security driver depends on a security interface provided +by the SMACK LSM to fully enforced with namespaces. This interface is +brought in by the Linux kernel version 4.3. It is recommended to use the +appropriate kernel version or backport the below changes to ensure proper +opertaion of the SMACK driver with namespaces. + +Link that references the change in the kernel: +<a href="https://lwn.net/Articles/660675/">https://lwn.net/Articles/660675/</a> +</p> + <h3><a name="securenetworking">Network isolation</a></h3> <p> diff --git a/m4/virt-smack.m4 b/m4/virt-smack.m4 new file mode 100644 index 0000000..b555733 --- /dev/null +++ b/m4/virt-smack.m4 @@ -0,0 +1,69 @@ +dnl The libsmack.so library +dnl +dnl Copyright (C) 2013 changyaoh. +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License, or (at your option) any later version. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library. If not, see +dnl <http://www.gnu.org/licenses/>. +dnl + +AC_DEFUN([LIBVIRT_CHECK_SMACK],[ + LIBVIRT_CHECK_LIB([SMACK], [smack], + [smack_set_label_for_self], [sys/smack.h]) + + AC_ARG_WITH([secdriver-smack], + [AS_HELP_STRING([--with-secdriver-smack], + [use Smack security driver @<:@default=check@:>@])], + [], + [with_secdriver_smack=check]) + + if test "$with_smack" != "yes" ; then + if test "$with_secdriver_smack" = "check" ; then + with_secdriver_smack=no + fi + if test "$with_secdriver_smack" != "no" ; then + AC_MSG_ERROR([You must install the Smack development package in order to compile libvirt]) + fi + elif test "with_secdriver_smack" != "no" ; then + with_secdriver_smack=yes + AC_DEFINE_UNQUOTED([WITH_SECDRIVER_SMACK], 1, [whether Smack security driver is available]) + fi + AM_CONDITIONAL([WITH_SECDRIVER_SMACK], [test "$with_secdriver_smack" != "no"]) + + + + AC_ARG_WITH([smack_mount], + [AS_HELP_STRING([--with-smack-mount], + [set Smack mount point @<:@default=check@:>@])], + [], + [with_smack_mount=check]) + + if test "$with_smack" = "yes"; then + AC_MSG_CHECKING([Smack mount point]) + if test "$with_smack_mount" = "check" || test -z "$with_smack_mount"; then + if test -d /sys/fs/smackfs ; then + SMACK_MOUNT=/sys/fs/smackfs + else + SMACK_MOUNT=/smack + fi + else + SMACK_MOUNT=$with_smack_mount + fi + AC_MSG_RESULT([$SMACK_MOUNT]) + AC_DEFINE_UNQUOTED([SMACK_MOUNT], ["$SMACK_MOUNT"], [Smack mount point]) + fi +]) + +AC_DEFUN([LIBVIRT_RESULT_SMACK],[ + LIBVIRT_RESULT_LIB([SMACK]) +]) diff --git a/po/POTFILES.in b/po/POTFILES.in index 506d535..99e6d6e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -159,6 +159,7 @@ src/security/security_dac.c src/security/security_driver.c src/security/security_manager.c src/security/security_selinux.c +src/security/security_smack.c src/security/virt-aa-helper.c src/storage/parthelper.c src/storage/storage_backend.c diff --git a/src/Makefile.am b/src/Makefile.am index 314f6df..9e53ed9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,6 +68,10 @@ if WITH_SECDRIVER_APPARMOR SECDRIVER_CFLAGS += $(APPARMOR_CFLAGS) SECDRIVER_LIBS += $(APPARMOR_LIBS) endif WITH_SECDRIVER_APPARMOR +if WITH_SECDRIVER_SMACK +SECDRIVER_CFLAGS += $(SMACK_CFLAGS) +SECDRIVER_LIBS += $(SMACK_LIBS) +endif WITH_SECDRIVER_SMACK if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) @@ -1017,6 +1021,9 @@ SECURITY_DRIVER_SELINUX_SOURCES = \ SECURITY_DRIVER_APPARMOR_SOURCES = \ security/security_apparmor.h security/security_apparmor.c +SECURITY_DRIVER_SMACK_SOURCES = \ + security/security_smack.h security/security_smack.c + ACCESS_DRIVER_GENERATED = \ access/viraccessapicheck.h \ access/viraccessapicheck.c \ @@ -1765,6 +1772,10 @@ if WITH_SECDRIVER_APPARMOR libvirt_security_manager_la_SOURCES += $(SECURITY_DRIVER_APPARMOR_SOURCES) libvirt_security_manager_la_CFLAGS += $(APPARMOR_CFLAGS) endif WITH_SECDRIVER_APPARMOR +if WITH_SECDRIVER_SMACK +libvirt_security_manager_la_SOURCES += $(SECURITY_DRIVER_SMACK_SOURCES) +libvirt_security_manager_la_CFLAGS += $(SMACK_CFLAGS) +endif WITH_SECDRIVER_SMACK libvirt_driver_access_la_SOURCES = \ $(ACCESS_DRIVER_SOURCES) $(ACCESS_DRIVER_GENERATED) @@ -1896,6 +1907,7 @@ EXTRA_DIST += \ $(NWFILTER_DRIVER_SOURCES) \ $(SECURITY_DRIVER_SELINUX_SOURCES) \ $(SECURITY_DRIVER_APPARMOR_SOURCES) \ + $(SECURITY_DRIVER_SMACK_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ $(SECRET_UTIL_SOURCES) \ $(VBOX_DRIVER_EXTRA_DIST) \ @@ -3030,12 +3042,12 @@ libvirt_nss_la_SOURCES = \ libvirt_nss_la_CFLAGS = \ -DLIBVIRT_NSS \ $(AM_CFLAGS) \ + $(SECDRIVER_LIBS) \ $(YAJL_CFLAGS) \ $(NULL) libvirt_nss_la_LDFLAGS = \ $(AM_LDFLAGS) \ $(NULL) - libvirt_nss_la_LIBADD = \ $(YAJL_LIBS) \ $(NULL) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index a909b66..ff0e461 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -2206,6 +2206,10 @@ static int lxcContainerChild(void *data) if (lxcContainerSetID(vmDef) < 0) goto cleanup; + VIR_DEBUG("Setting up security labeling"); + if (virSecurityManagerSetProcessLabel(argv->securityDriver, vmDef) < 0) + goto cleanup; + root = virDomainGetFilesystemForTarget(vmDef, "/"); if (argv->nttyPaths) { @@ -2254,20 +2258,12 @@ static int lxcContainerChild(void *data) goto cleanup; } - /* drop a set of root capabilities */ - if (lxcContainerDropCapabilities(vmDef, !!hasReboot) < 0) - goto cleanup; - if (lxcContainerSendContinue(argv->handshakefd) < 0) { virReportSystemError(errno, "%s", _("Failed to send continue signal to controller")); goto cleanup; } - VIR_DEBUG("Setting up security labeling"); - if (virSecurityManagerSetProcessLabel(argv->securityDriver, vmDef) < 0) - goto cleanup; - VIR_DEBUG("Setting up inherited FDs"); VIR_FORCE_CLOSE(argv->handshakefd); VIR_FORCE_CLOSE(argv->monitor); @@ -2275,6 +2271,10 @@ static int lxcContainerChild(void *data) argv->npassFDs, argv->passFDs) < 0) goto cleanup; + /* drop a set of root capabilities */ + if (lxcContainerDropCapabilities(vmDef, !!hasReboot) < 0) + goto cleanup; + ret = 0; cleanup: VIR_FREE(ttyPath); @@ -2396,6 +2396,14 @@ int lxcContainerStart(virDomainDefPtr def, return -1; } } + + VIR_DEBUG("Setting up security labeling"); + if (virSecurityManagerSetChildProcessLabel(securityDriver, def, NULL) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to send label to relabel interface.")); + return -1; + } + if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHARENET] == -1) { if (lxcNeedNetworkNamespace(def)) { VIR_DEBUG("Enable network namespaces"); diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index 0304354..026762e 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -1484,6 +1484,9 @@ static int virLXCControllerSetupDev(virLXCControllerPtr ctrl) if (lxcContainerChown(ctrl->def, dev) < 0) goto cleanup; + if (virSecurityManagerSetImagePathLabel(ctrl->securityManager, ctrl->def, dev) < 0) + goto cleanup; + ret = 0; cleanup: VIR_FREE(opts); @@ -1532,6 +1535,11 @@ static int virLXCControllerPopulateDevices(virLXCControllerPtr ctrl) if (lxcContainerChown(ctrl->def, path) < 0) goto cleanup; + if (virSecurityManagerSetImagePathLabel(ctrl->securityManager, + ctrl->def, + path) < 0) + goto cleanup; + VIR_FREE(path); } @@ -2190,6 +2198,14 @@ virLXCControllerSetupDevPTS(virLXCControllerPtr ctrl) (lxcContainerChown(ctrl->def, devpts) < 0)) goto cleanup; + if ((virSecurityManagerSetImagePathLabel(ctrl->securityManager, + ctrl->def, + ctrl->devptmx)) < 0 || + (virSecurityManagerSetImagePathLabel(ctrl->securityManager, + ctrl->def, + devpts) < 0)) + goto cleanup; + ret = 0; cleanup: @@ -2234,6 +2250,11 @@ virLXCControllerSetupConsoles(virLXCControllerPtr ctrl, if (lxcContainerChown(ctrl->def, ttyHostPath) < 0) goto cleanup; + if (virSecurityManagerSetImagePathLabel(ctrl->securityManager, + ctrl->def, + ttyHostPath) < 0) + goto cleanup; + VIR_FREE(ttyHostPath); } diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index af2b639..bffcf83 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -986,6 +986,14 @@ AppArmorSetFDLabel(virSecurityManagerPtr mgr, return reload_profile(mgr, def, fd_path, true); } +static int +AppArmorSetPathLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + const char *path ATTRIBUTE_UNUSED) +{ + return 0; +} + static char * AppArmorGetMountOptions(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr vm ATTRIBUTE_UNUSED) @@ -1043,6 +1051,7 @@ virSecurityDriver virAppArmorSecurityDriver = { .domainRestoreSavedStateLabel = AppArmorRestoreSavedStateLabel, .domainSetSecurityImageFDLabel = AppArmorSetFDLabel, + .domainSetSecurityImagePathLabel = AppArmorSetPathLabel, .domainSetSecurityTapFDLabel = AppArmorSetFDLabel, .domainGetSecurityMountOptions = AppArmorGetMountOptions, diff --git a/src/security/security_dac.c b/src/security/security_dac.c index df3ed47..0ada728 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -1519,6 +1519,14 @@ virSecurityDACSetImageFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, } static int +virSecurityDACSetImagePathLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + const char *path ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDACSetTapFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainDefPtr def ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED) @@ -1601,6 +1609,7 @@ virSecurityDriver virSecurityDriverDAC = { .domainRestoreSavedStateLabel = virSecurityDACRestoreSavedStateLabel, .domainSetSecurityImageFDLabel = virSecurityDACSetImageFDLabel, + .domainSetSecurityImagePathLabel = virSecurityDACSetImagePathLabel, .domainSetSecurityTapFDLabel = virSecurityDACSetTapFDLabel, .domainGetSecurityMountOptions = virSecurityDACGetMountOptions, diff --git a/src/security/security_driver.c b/src/security/security_driver.c index 4800d52..3ca3766 100644 --- a/src/security/security_driver.c +++ b/src/security/security_driver.c @@ -35,6 +35,10 @@ # include "security_apparmor.h" #endif +#ifdef WITH_SECDRIVER_SMACK +# include "security_smack.h" +#endif + #include "security_nop.h" #define VIR_FROM_THIS VIR_FROM_SECURITY @@ -48,6 +52,9 @@ static virSecurityDriverPtr security_drivers[] = { #ifdef WITH_SECDRIVER_APPARMOR &virAppArmorSecurityDriver, #endif +#ifdef WITH_SECDRIVER_SMACK + &virSecurityDriverSmack, +#endif &virSecurityDriverNop, /* Must always be last, since it will always probe */ }; diff --git a/src/security/security_driver.h b/src/security/security_driver.h index 7cb62f0..97c0c30 100644 --- a/src/security/security_driver.h +++ b/src/security/security_driver.h @@ -104,6 +104,9 @@ typedef int (*virSecurityDomainSecurityVerify) (virSecurityManagerPtr mgr, typedef int (*virSecurityDomainSetImageFDLabel) (virSecurityManagerPtr mgr, virDomainDefPtr def, int fd); +typedef int (*virSecurityDomainSetImagePathLabel) (virSecurityManagerPtr mgr, + virDomainDefPtr def, + const char *path); typedef int (*virSecurityDomainSetTapFDLabel) (virSecurityManagerPtr mgr, virDomainDefPtr def, int fd); @@ -165,6 +168,7 @@ struct _virSecurityDriver { virSecurityDomainRestoreSavedStateLabel domainRestoreSavedStateLabel; virSecurityDomainSetImageFDLabel domainSetSecurityImageFDLabel; + virSecurityDomainSetImagePathLabel domainSetSecurityImagePathLabel; virSecurityDomainSetTapFDLabel domainSetSecurityTapFDLabel; virSecurityDomainGetMountOptions domainGetSecurityMountOptions; diff --git a/src/security/security_manager.c b/src/security/security_manager.c index ecb4a40..507c41e 100644 --- a/src/security/security_manager.c +++ b/src/security/security_manager.c @@ -916,6 +916,23 @@ virSecurityManagerSetImageFDLabel(virSecurityManagerPtr mgr, return -1; } +int +virSecurityManagerSetImagePathLabel(virSecurityManagerPtr mgr, + virDomainDefPtr vm, + const char* path) +{ + if (mgr->drv->domainSetSecurityImagePathLabel) { + int ret; + virObjectLock(mgr); + ret = mgr->drv->domainSetSecurityImagePathLabel(mgr, vm, path); + virObjectUnlock(mgr); + return ret; + } + + virReportUnsupportedError(); + return -1; +} + int virSecurityManagerSetTapFDLabel(virSecurityManagerPtr mgr, diff --git a/src/security/security_manager.h b/src/security/security_manager.h index 4cbc2d8..886d00a 100644 --- a/src/security/security_manager.h +++ b/src/security/security_manager.h @@ -143,6 +143,9 @@ int virSecurityManagerVerify(virSecurityManagerPtr mgr, int virSecurityManagerSetImageFDLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, int fd); +int virSecurityManagerSetImagePathLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + const char *path); int virSecurityManagerSetTapFDLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, int fd); diff --git a/src/security/security_nop.c b/src/security/security_nop.c index 951125d..3d4d47a 100644 --- a/src/security/security_nop.c +++ b/src/security/security_nop.c @@ -236,6 +236,13 @@ virSecurityDomainSetImageLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, return 0; } +static int +virSecurityDomainSetPathLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + const char* path ATTRIBUTE_UNUSED) +{ + return 0; +} virSecurityDriver virSecurityDriverNop = { .privateDataLen = 0, @@ -277,6 +284,7 @@ virSecurityDriver virSecurityDriverNop = { .domainRestoreSavedStateLabel = virSecurityDomainRestoreSavedStateLabelNop, .domainSetSecurityImageFDLabel = virSecurityDomainSetFDLabelNop, + .domainSetSecurityImagePathLabel = virSecurityDomainSetPathLabelNop, .domainSetSecurityTapFDLabel = virSecurityDomainSetFDLabelNop, .domainGetSecurityMountOptions = virSecurityDomainGetMountOptionsNop, diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index b33d54a..56e07ca 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -2467,6 +2467,14 @@ virSecuritySELinuxSetImageFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, } static int +virSecuritySELinuxSetImagePathLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + const char *path ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecuritySELinuxSetTapFDLabel(virSecurityManagerPtr mgr, virDomainDefPtr def, int fd) @@ -2647,6 +2655,7 @@ virSecurityDriver virSecurityDriverSELinux = { .domainRestoreSavedStateLabel = virSecuritySELinuxRestoreSavedStateLabel, .domainSetSecurityImageFDLabel = virSecuritySELinuxSetImageFDLabel, + .domainSetSecurityImagePathLabel = virSecuritySELinuxSetImagePathLabel, .domainSetSecurityTapFDLabel = virSecuritySELinuxSetTapFDLabel, .domainGetSecurityMountOptions = virSecuritySELinuxGetSecurityMountOptions, diff --git a/src/security/security_smack.c b/src/security/security_smack.c new file mode 100644 index 0000000..6f43db4 --- /dev/null +++ b/src/security/security_smack.c @@ -0,0 +1,1484 @@ +/* + * Copyright (C) 2015 Cisco Systems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: + * Hongliang Liang <hliang@xxxxxxxxxxx> + * Changyao Han <changyao@xxxxxxxxxxx> + * + * Updated to libvirt v1.2.15: (Original was written for libvirt v1.1.4) + * Raghuram S. Sudhaakar <rsudhaak@xxxxxxxxx> + * Randy Aybar <raybar@xxxxxxxxx> + * + * Based on security_selinux.c by James Morris <jmorris@xxxxxxxxx> + * and security_apparmor.c by Jamie Strandboge <jamie@xxxxxxxxxxxxx> + * + * Smack scurity driver. + * + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/xattr.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <fcntl.h> +#include <sys/smack.h> +#include <errno.h> +#include <unistd.h> +#include <wait.h> +#include <stdlib.h> + +#include "security_smack.h" +#include "virerror.h" +#include "viralloc.h" +#include "datatypes.h" +#include "viruuid.h" +#include "virlog.h" +#include "virpci.h" +#include "virusb.h" +#include "virscsi.h" +#include "virstoragefile.h" +#include "virfile.h" +#include "configmake.h" +#include "vircommand.h" +#include "virhash.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_SECURITY +VIR_LOG_INIT("security.security_smack"); + +#define SECURITY_SMACK_VOID_DOI "0" +#define SECURITY_SMACK_NAME "smack" + +typedef struct _SmackCallbackData SmackCallbackData; +typedef SmackCallbackData *SmackCallbackDataPtr; + +struct _SmackCallbackData { + virSecurityManagerPtr manager; + virSecurityLabelDefPtr secdef; +}; + +static char * +virSecuritySmackGetLabelName(virDomainDefPtr def) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *name = NULL; + + virUUIDFormat(def->uuid, uuidstr); + if (virAsprintf(&name, "%s%s", SMACK_PREFIX, uuidstr) < 0) + return NULL; + + return name; +} + +static int +virSecuritySmackGetPIDLabel(pid_t pid, char **label) +{ + char *result, *path; + int ret; + + ret = VIR_ALLOC_N(result, SMACK_LABEL_LEN + 1); + if (ret < 0 || result == NULL) + return -1; + ret = virAsprintf(&path, "/proc/%d/attr/current", pid); + if (ret < 0) + return -1; + + ret = virFileReadAll(path, SMACK_LABEL_LEN, label); + + VIR_FREE(path); + return ret; +} + +int +virSecuritySmackSockCreate(const char *label, const char *attr) +{ + int ret = -1; + long int tid; + char *path; + + tid = syscall(SYS_gettid); + + VIR_DEBUG("/proc/self/task/%ld/attr/%s", tid, attr); + + if (virAsprintf(&path, "/proc/self/task/%ld/attr/%s", tid, attr) < 0) + return -1; + + VIR_DEBUG("setSockCreate pid is in %d", getpid()); + VIR_DEBUG("real user ID is in %d", getuid()); + VIR_DEBUG("effective user ID is in %d", geteuid()); + VIR_DEBUG("label from self %s", label); + VIR_DEBUG("location /proc/self/attr/%s", attr); + + ret = virFileWriteStr(path, label != NULL ? label : "", 0); + + VIR_FREE(path); + return ret; +} + +static int +virSecuritySmackSetPathLabel(const char *path, const char *tlabel) +{ + char * elabel = NULL; + + VIR_INFO("Setting Smack label on '%s' to '%s'", path, tlabel); + + if (smack_set_label_for_path(path, "security.SMACK64", 0, tlabel) < 0) { + int setfilelabel_errno = errno; + + if (smack_new_label_from_path(path, "security.SMACK64", 0, &elabel) >= 0) { + if (STREQ(tlabel, elabel)) { + VIR_FREE(elabel); + /* It's alright, there's nothing to change anyway. */ + return 0; + } + VIR_FREE(elabel); + } + + /* if the error complaint is related to an image hosted on + * an nfs mount, or a usbfs/sysfs filesystem not supporting + * labelling, then just ignore it & hope for the best. + */ + + if (setfilelabel_errno != EOPNOTSUPP && setfilelabel_errno != ENOTSUP) { + virReportSystemError(setfilelabel_errno, + _("unable to set security context '%s' on '%s'"), + tlabel, path); + return -1; + + } else { + const char *msg; + if ((virFileIsSharedFS(path) == 1)) { + msg = _("Setting security context '%s' on '%s' not supported. "); + VIR_WARN(msg, tlabel, path); + } else { + VIR_INFO("Setting security context '%s' on '%s' not supported", + tlabel, path); + + } + + } + + } + return 0; + +} + +static int +virSecuritySmackSetHostdevLabelHelper(const char *file, void *opaque) +{ + virSecurityLabelDefPtr seclabel; + virDomainDefPtr def = opaque; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + return virSecuritySmackSetPathLabel(file, seclabel->imagelabel); +} + +static int +virSecuritySmackSetUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return virSecuritySmackSetHostdevLabelHelper(file, opaque); +} + + +static int +virSecuritySmackSetPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return virSecuritySmackSetHostdevLabelHelper(file, opaque); +} + +static int +virSecuritySmackSetSCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, void *opaque) +{ + return virSecuritySmackSetHostdevLabelHelper(file, opaque); +} + + +static int +virSecuritySmackRestoreFileLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + const char *path) +{ + struct stat buf; + int ret = -1; + char *newpath = NULL; + char ebuf[1024]; + + VIR_INFO("Restoring Smack label on '%s'", path); + + if (virFileResolveLink(path, &newpath) < 0) { + VIR_WARN("cannot resolve symlink %s: %s", path, + virStrerror(errno, ebuf, sizeof(ebuf))); + goto err; + } + + if (stat(newpath, &buf) != 0) { + VIR_WARN("cannot stat %s: %s", newpath, + virStrerror(errno, ebuf, sizeof(ebuf))); + goto err; + } + + ret = virSecuritySmackSetPathLabel(newpath, "smack-unused"); + + err: + VIR_FREE(newpath); + return ret; +} + + +static int +virSecuritySmackRestoreUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + + return virSecuritySmackRestoreFileLabel(mgr, file); +} + +static int +virSecuritySmackRestorePCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + + return virSecuritySmackRestoreFileLabel(mgr, file); +} + +static int +virSecuritySmackRestoreSCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + + return virSecuritySmackRestoreFileLabel(mgr, file); +} + +static int +virSecuritySmackRestoreImageLabelInt(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virStorageSourcePtr src, + bool migrated) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + if (src->readonly || src->shared) + return 0; + + if (!src || src->type == VIR_STORAGE_TYPE_NETWORK) + return 0; + + if (migrated) { + int ret = virFileIsSharedFS(src->path); + if (ret < 0) + return -1; + if (ret == 1) { + VIR_DEBUG("Skipping image label restore on %s because FS is shared", src->path); + return 0; + } + + } + + return virSecuritySmackRestoreFileLabel(mgr, src->path); + +} + +static int +virSecuritySmackSetFileLabel(int fd, char *tlabel) +{ + char *elabel = NULL; + + VIR_INFO("Setting Smack label on fd %d to '%s'", fd, tlabel); + + if (smack_set_label_for_file(fd, "security.SMACK64", tlabel) < 0) { + int fsetfilelabel_errno = errno; + + if (smack_new_label_from_file(fd, "security.SMACK64", &elabel) >= 0) { + if (STREQ(tlabel, elabel)) { + VIR_FREE(elabel); + /* It's alright, there's nothing to change anyway. */ + + return 0; + } + + VIR_FREE(elabel); + } + /* if the error complaint is related to an image hosted on + * an nfs mount, or a usbfs/sysfs filesystem not supporting + * labelling, then just ignore it & hope for the best. + */ + if (fsetfilelabel_errno != EOPNOTSUPP) { + virReportSystemError(fsetfilelabel_errno, + _("unable to set security context '%s' on fd %d"), tlabel, fd); + return -1; + } else { + VIR_INFO("Setting security label '%s' on fd %d not supported", + tlabel, fd); + } + } + return 0; +} + + +static int +virSecuritySmackSetHostdevSubsysLabel(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + + switch (dev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + { + virUSBDevicePtr usb; + + if (dev->missing) + return 0; + + usb = virUSBDeviceNew(dev->source.subsys.u.usb.bus, + dev->source.subsys.u.usb.device, + vroot); + if (!usb) + goto done; + + ret = virUSBDeviceFileIterate(usb, virSecuritySmackSetUSBLabel, def); + virUSBDeviceFree(usb); + + break; + } + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + { + virPCIDevicePtr pci = + virPCIDeviceNew(dev->source.subsys.u.pci.addr.domain, + dev->source.subsys.u.pci.addr.bus, + dev->source.subsys.u.pci.addr.slot, + dev->source.subsys.u.pci.addr.function); + + if (!pci) + goto done; + + if (dev->source.subsys.u.pci.backend + == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) { + char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci); + + if (!vfioGroupDev) { + virPCIDeviceFree(pci); + goto done; + } + ret = virSecuritySmackRestorePCILabel(pci, vfioGroupDev, def); + VIR_FREE(vfioGroupDev); + } else { + ret = virPCIDeviceFileIterate(pci, virSecuritySmackSetPCILabel, def); + } + virPCIDeviceFree(pci); + break; + } + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: + { + virDomainHostdevSubsysSCSIHostPtr scsihostsrc = + &(dev->source.subsys.u.scsi.u.host); + virSCSIDevicePtr scsi = + virSCSIDeviceNew(NULL, + scsihostsrc->adapter, scsihostsrc->bus, + scsihostsrc->target, scsihostsrc->unit, + dev->readonly, dev->shareable); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, virSecuritySmackSetSCSILabel, def); + virSCSIDeviceFree(scsi); + + break; + } + + default: + ret = 0; + break; + } + + done: + return ret; +} + +static int +virSecuritySmackSetHostdevCapsLabel(virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + virSecurityLabelDefPtr seclabel; + char *path; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + switch (dev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: + { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.storage.block) < 0) + return -1; + } else { + if (VIR_STRDUP(path, dev->source.caps.u.storage.block) < 0) + return -1; + } + ret = virSecuritySmackSetPathLabel(path, seclabel->imagelabel); + VIR_FREE(path); + break; + } + + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.misc.chardev) < 0) + return -1; + } else { + if (VIR_STRDUP(path, dev->source.caps.u.misc.chardev) < 0) + return -1; + } + ret = virSecuritySmackSetPathLabel(path, seclabel->imagelabel); + VIR_FREE(path); + break; + } + + default: + { + ret = 0; + break; + } + } + + return ret; + +} + +static int +virSecuritySmackSetHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + virSecurityLabelDefPtr seclabel; + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virSecuritySmackSetHostdevSubsysLabel(def, dev, vroot); + + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return virSecuritySmackSetHostdevCapsLabel(def, dev, vroot); + + default: + return 0; + + } +} + +static int +virSecuritySmackRestoreHostdevSubsysLabel(virSecurityManagerPtr mgr, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + + switch (dev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + { + virUSBDevicePtr usb; + + if (dev->missing) + return 0; + + usb = virUSBDeviceNew(dev->source.subsys.u.usb.bus, + dev->source.subsys.u.usb.device, + vroot); + if (!usb) + goto done; + + ret = virUSBDeviceFileIterate(usb, virSecuritySmackRestoreUSBLabel, mgr); + virUSBDeviceFree(usb); + + break; + } + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + { + virPCIDevicePtr pci = + virPCIDeviceNew(dev->source.subsys.u.pci.addr.domain, + dev->source.subsys.u.pci.addr.bus, + dev->source.subsys.u.pci.addr.slot, + dev->source.subsys.u.pci.addr.function); + + if (!pci) + goto done; + + if (dev->source.subsys.u.pci.backend + == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) { + char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci); + + if (!vfioGroupDev) { + virPCIDeviceFree(pci); + goto done; + } + ret = virSecuritySmackRestorePCILabel(pci, vfioGroupDev, mgr); + VIR_FREE(vfioGroupDev); + } else { + ret = virPCIDeviceFileIterate(pci, virSecuritySmackRestorePCILabel, mgr); + } + virPCIDeviceFree(pci); + break; + } + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: + { + virDomainHostdevSubsysSCSIHostPtr scsihostsrc = + &(dev->source.subsys.u.scsi.u.host); + virSCSIDevicePtr scsi = + virSCSIDeviceNew(NULL, + scsihostsrc->adapter, scsihostsrc->bus, + scsihostsrc->target, scsihostsrc->unit, + dev->readonly, dev->shareable); + + if (!scsi) + goto done; + + ret = virSCSIDeviceFileIterate(scsi, virSecuritySmackRestoreSCSILabel, mgr); + virSCSIDeviceFree(scsi); + + break; + } + + default: + { + ret = 0; + break; + } + } + + done: + return ret; + +} + +static int +virSecuritySmackRestoreHostdevCapsLabel(virSecurityManagerPtr mgr, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + int ret = -1; + char *path; + + switch (dev->source.caps.type) { + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: + { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.storage.block) < 0) + return -1; + } else { + if (VIR_STRDUP(path, dev->source.caps.u.storage.block) < 0) + return -1; + } + ret = virSecuritySmackRestoreFileLabel(mgr, path); + VIR_FREE(path); + break; + } + + case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: + { + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, + dev->source.caps.u.misc.chardev) < 0) + return -1; + } else { + if (VIR_STRDUP(path, dev->source.caps.u.misc.chardev) < 0) + return -1; + } + ret = virSecuritySmackRestoreFileLabel(mgr, path); + VIR_FREE(path); + break; + } + + default: + ret = 0; + break; + } + + return ret; +} + +static int +virSecuritySmackRestoreHostdevLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + const char *vroot) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + switch (dev->mode) { + case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: + return virSecuritySmackRestoreHostdevSubsysLabel(mgr, dev, vroot); + + case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: + return virSecuritySmackRestoreHostdevCapsLabel(mgr, dev, vroot); + + default: + return 0; + } +} + +/*Called on libvirtd startup to see if Smack is available*/ +static int +virSecuritySmackSecurityDriverProbe(const char *virtDriver) +{ + if (!smack_smackfs_path() || NULL == virtDriver) + return SECURITY_DRIVER_DISABLE; + + return SECURITY_DRIVER_ENABLE; + +} + +/*Security dirver initialization .*/ +static int +virSecuritySmackSecurityDriverOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecuritySmackSecurityDriverClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char * +virSecuritySmackSecurityDriverGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return SECURITY_SMACK_NAME; +} + +static const char * +virSecuritySmackSecurityDriverGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return SECURITY_SMACK_VOID_DOI; +} + +static int +virSecuritySmackSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + virSecurityLabelDefPtr seclabel; + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (STREQ(SECURITY_SMACK_NAME, seclabel->model) != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "'%s' model configured for domain, but " + "hypervisor driver is '%s'."), + seclabel->model, SECURITY_SMACK_NAME); + return -1; + } + + if (seclabel->type == VIR_DOMAIN_SECLABEL_STATIC) { + if (smack_label_length(seclabel->label) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid security label %s"), seclabel->label); + return -1; + } + } + + return 0; + +} + +static int +virSecuritySmackSetDiskLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virDomainDiskDefPtr disk) +{ + virSecurityLabelDefPtr seclabel; + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) + return 0; + + VIR_DEBUG("set disk image security label before"); + + if (setxattr(disk->src->path, "security.SMACK64", seclabel->imagelabel, + strlen(seclabel->imagelabel) + 1, 0) < 0) + return -1; + + VIR_DEBUG("disk image %s", disk->src->path); + VIR_DEBUG("set disk image security label after"); + + return 0; + +} + +static int +virSecuritySmackRestoreDiskLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virDomainDiskDefPtr disk) +{ + return virSecuritySmackRestoreImageLabelInt(mgr, def, disk->src, false); +} + +static int +virSecuritySmackSetImageLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virStorageSourcePtr src) +{ + virSecurityLabelDefPtr seclabel; + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + if (src->type == VIR_STORAGE_TYPE_NETWORK) + return 0; + + VIR_DEBUG("set disk image security label before"); + + if (setxattr(src->path, "security.SMACK64", seclabel->imagelabel, + strlen(seclabel->imagelabel) + 1, 0) < 0) + return -1; + + VIR_DEBUG("disk image %s", src->path); + VIR_DEBUG("set disk image security label after"); + + return 0; + +} + +static int +virSecuritySmackRestoreImageLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + virStorageSourcePtr src) +{ + return virSecuritySmackRestoreImageLabelInt(mgr, def, src, false); + +} + +static int +virSecuritySmackSetDaemonSocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr vm) +{ + + return 0; + virSecurityLabelDefPtr seclabel; + char *label = NULL; + int ret = -1; + + seclabel = virDomainDefGetSecurityLabelDef(vm, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (seclabel->label == NULL) + return 0; + + if (STREQ(SECURITY_SMACK_NAME, seclabel->model) != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "'%s' model configured for domain, but " + "hypervisor driver is '%s'."), + seclabel->model, SECURITY_SMACK_NAME); + return -1; + } + + if (smack_new_label_from_self(&label) == -1) { + virReportSystemError(errno, + _("unable to get current process context '%s'"), seclabel->label); + goto done; + } + + VIR_DEBUG("SmackSetSecurityDaemonSocketLabel is in %d", getpid()); + VIR_DEBUG("label from self %s", label); + + + if (virSecuritySmackSockCreate(label, "sockincreate") == -1) { + virReportSystemError(errno, + _("unable to set socket smack label '%s'"), seclabel->label); + goto done; + } + + ret = 0; + done: + + VIR_FREE(label); + return ret; + +} + +static int +virSecuritySmackSetSocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr vm) +{ + + virSecurityLabelDefPtr seclabel; + + return 0; + seclabel = virDomainDefGetSecurityLabelDef(vm, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (seclabel->label == NULL) + return 0; + + if (STREQ(SECURITY_SMACK_NAME, seclabel->model) != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "'%s' model configured for domain, but " + "hypervisor driver is '%s'."), + seclabel->model, SECURITY_SMACK_NAME); + return -1; + } + + VIR_DEBUG("Setting VM %s socket label %s", vm->name, seclabel->label); + + if (virSecuritySmackSockCreate(seclabel->label, "sockoutcreate") == -1) { + virReportSystemError(errno, + _("unable to set socket smack label '%s'"), + seclabel->label); + return -1; + } + + + return 0; + +} + +static int +virSecuritySmackClearSocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + + virSecurityLabelDefPtr seclabel; + + return 0; + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (seclabel->label == NULL) + return 0; + + if (STREQ(SECURITY_SMACK_NAME, seclabel->model) != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "'%s' model configured for domain, but " + "hypervisor driver is '%s'."), + seclabel->model, SECURITY_SMACK_NAME); + return -1; + } + + VIR_DEBUG("clear sock label"); + + if (virSecuritySmackSockCreate(NULL, "sockincreate") == -1 || + virSecuritySmackSockCreate(NULL, "sockoutcreate") == -1) { + virReportSystemError(errno, + _("unable to clear socket smack label '%s'"), + seclabel->label); + + return -1; + } + + return 0; +} + +/* +*Current called in qemuStartVMDaemon to setup a 'label'. We make the +*label based on UUID. +*this is called on 'start'with RestoreSecurityLabel being called on +*shutdown + */ +static int +virSecuritySmackGenLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def) +{ + int ret = -1; + char *label_name = NULL; + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return ret; + + VIR_DEBUG("label=%s", virSecurityManagerGetDriver(mgr)); + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + seclabel->label) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("security label already defined for VM")); + return ret; + } + + if (seclabel->imagelabel) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("security image label already defined for VM")); + return ret; + } + + if (seclabel->model && + STRNEQ(seclabel->model, SECURITY_SMACK_NAME)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label model %s is not supported with smack"), + seclabel->model); + return ret; + } + + VIR_DEBUG("type=%d", seclabel->type); + + if ((label_name = virSecuritySmackGetLabelName(def)) == NULL) + return ret; + + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { + + /*set process label*/ + if (VIR_STRDUP(seclabel->label, label_name) < 0) + goto cleanup; + } + + /*set imagelabel the same as label*/ + if (VIR_STRDUP(seclabel->imagelabel, label_name) < 0) + goto cleanup; + + if (!seclabel->model && + VIR_STRDUP(seclabel->model, SECURITY_SMACK_NAME) < 0) + goto cleanup; + + ret = 0; + + cleanup: + + if (ret != 0) { + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC) + VIR_FREE(seclabel->label); + VIR_FREE(seclabel->imagelabel); + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + !seclabel->baselabel) + VIR_FREE(seclabel->model); + } + + VIR_FREE(label_name); + + VIR_DEBUG("model=%s label=%s imagelabel=%s", + NULLSTR(seclabel->model), + NULLSTR(seclabel->label), + NULLSTR(seclabel->imagelabel)); + + return ret; + +} + +static int +virSecuritySmackReserveLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + pid_t pid ATTRIBUTE_UNUSED) +{ + /*Security label is based UUID,*/ + return 0; +} + +/* +*Called on VM shutdown and destroy. +*/ +static int +virSecuritySmackReleaseLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { + VIR_FREE(seclabel->label); + VIR_FREE(seclabel->model); + } + VIR_FREE(seclabel->imagelabel); + + return 0; + +} + +/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is +* running. +*/ +static int +virSecuritySmackGetProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + pid_t pid, + virSecurityLabelPtr sec) +{ + + char *label_name = NULL; + + if (virSecuritySmackGetPIDLabel(pid, &label_name) == -1) { + virReportSystemError(errno, + _("unable to get PID %d security label"), + pid); + return -1; + } + + if (strlen(label_name) >= VIR_SECURITY_LABEL_BUFLEN) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label exceeds " + "maximum length: %d"), + VIR_SECURITY_LABEL_BUFLEN - 1); + VIR_FREE(label_name); + return -1; + } + + label_name = virStrcpy(sec->label, label_name, VIR_SECURITY_LABEL_BUFLEN); + VIR_FREE(label_name); + /*Smack default enforced*/ + sec->enforcing = 1; + + return label_name == NULL ? -1 : 0; +} + +static int +virSecuritySmackSetProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (seclabel->label == NULL) + return 0; + + if (STRNEQ(SECURITY_SMACK_NAME, seclabel->model)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "\'%s\' model configured for domain, but " + "hypervisor driver is \'%s\'."), + seclabel->model, SECURITY_SMACK_NAME); + + return -1; + } + + if (smack_set_label_for_self(seclabel->label) < 0) { + virReportError(errno, + _("unable to set security label '%s'"), + seclabel->label); + + return -1; + } + + return 0; + +} + +static int +virSecuritySmackSetChildProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + virCommandPtr cmd) +{ + virSecurityLabelDefPtr seclabel; + int rlbl; + char *smackfs_path = NULL; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (seclabel->label == NULL) + return 0; + + if (STRNEQ(SECURITY_SMACK_NAME, seclabel->model)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "\'%s\' model configured for domain, but " + "hypervisor driver is \'%s\'."), + seclabel->model, SECURITY_SMACK_NAME); + + return -1; + } + + /* + * Send label to relabel-self interface to allow child to label + * its self once it finishes setting up. Apply only if interface is + * available and user namespace is enabled. + */ + + if (STREQ(virSecurityManagerGetDriver(mgr), "LXC")) { + + if (!def->idmap.nuidmap) + return 0; + + VIR_DEBUG("Applying label %s to relabel-self interface.", seclabel->label); + + if (virAsprintf(&smackfs_path, "%s/relabel-self", smack_smackfs_path()) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to obtain path for smackfs. Is smack enabled? ")); + return -1; + } + + rlbl = open(smackfs_path, O_WRONLY); + + if (rlbl < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not open relabel interface \'%s\' for writing. Is it " + "enabled in the kernel?"), + smackfs_path); + return -1; + } + + if (safewrite(rlbl, seclabel->label, strlen(seclabel->label)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not write to relabel interface \'%s\'."), + smackfs_path); + return -1; + } + + VIR_FORCE_CLOSE(rlbl); + } + + /* save in cmd to be set after fork/before child process is exec'ed */ + virCommandSetSmackLabel(cmd, seclabel->label); + VIR_DEBUG("save smack label in cmd %s", seclabel->label); + + return 0; + +} + +static int +virSecuritySmackSetAllLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + const char *stdin_path) +{ + + size_t i; + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + VIR_DEBUG("set image security label before"); + + for (i = 0; i < def->ndisks; i++) { + if (def->disks[i]->src->type == VIR_STORAGE_TYPE_DIR) { + VIR_WARN("Unable to relabel directory tree %s for disk %s", + def->disks[i]->src->path, def->disks[i]->dst); + continue; + } + + VIR_DEBUG("set image security label"); + + if (virSecuritySmackSetImageLabel(mgr, + def, def->disks[i]->src) < 0) + return -1; + } + + VIR_DEBUG("set image security label after"); + + for (i = 0; i< def->nhostdevs; i++) { + if (virSecuritySmackSetHostdevLabel(mgr, + def, + def->hostdevs[i], + NULL) < 0) + return -1; + + } + + if (stdin_path) { + if (setxattr(stdin_path, "security.SMACK64", seclabel->imagelabel, + strlen(seclabel->imagelabel) + 1, 0)< 0 && + virFileIsSharedFS(stdin_path) != 1) + return -1; + } + + return 0; + +} + +static int +virSecuritySmackRestoreAllLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + bool migrated ATTRIBUTE_UNUSED) +{ + size_t i; + virSecurityLabelDefPtr seclabel; + + VIR_DEBUG("Restoring security label on %s", def->name); + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + for (i = 0; i < def->ndisks; i++) { + + if (virSecuritySmackRestoreImageLabelInt(mgr, + def, + def->disks[i]->src, + migrated) < 0) + + return -1; + + } + + return 0; + +} + + +static int +virSecuritySmackSetSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + const char *savefile) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + return virSecuritySmackSetPathLabel(savefile, seclabel->imagelabel); +} + +static int +virSecuritySmackRestoreSavedStateLabel(virSecurityManagerPtr mgr, + virDomainDefPtr def, + const char *savefile) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (!seclabel->relabel) + return 0; + + return virSecuritySmackRestoreFileLabel(mgr, savefile); +} + +static int +virSecuritySmackSetImageFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + int fd) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (seclabel->imagelabel == NULL) + return 0; + + return virSecuritySmackSetFileLabel(fd, seclabel->imagelabel); + +} + +static int +virSecuritySmackSetImagePathLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + const char *path) +{ + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + + if (seclabel == NULL) + return -1; + + if (seclabel->imagelabel == NULL) + return 0; + + if (virSecuritySmackSetPathLabel(path, seclabel->imagelabel) < 0) + return -1; + + return 0; +} + +static int +virSecuritySmackSetTapFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def, + int fd) +{ + struct stat buf; + virSecurityLabelDefPtr seclabel; + + seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME); + if (seclabel == NULL) + return -1; + + if (seclabel->label == NULL) + return 0; + + + if (fstat(fd, &buf) < 0) { + virReportSystemError(errno, _("cannot stat tap fd %d"), fd); + return -1; + } + + if ((buf.st_mode & S_IFMT) != S_IFCHR) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("tap fd %d is not character device"), fd); + return -1; + } + + return virSecuritySmackSetFileLabel(fd, seclabel->label); + +} + +static char * +virSecuritySmackGetSecurityMountOptions(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + char *opts = NULL; + virSecurityLabelDefPtr seclabel; + + if ((seclabel = virDomainDefGetSecurityLabelDef(def, SECURITY_SMACK_NAME))) { + if (!seclabel->imagelabel) { + if (!seclabel->label) + seclabel->imagelabel = virSecuritySmackGetLabelName(def); + else + seclabel->imagelabel = seclabel->label; + } + if (seclabel->imagelabel && + virAsprintf(&opts, + ",smackfsdef=\"%s\"", + (const char*) seclabel->imagelabel) < 0) + return NULL; + } + + if (!opts && VIR_STRDUP(opts, "") < 0) + return NULL; + + return opts; + +} + +static const char * +virSecuritySmackGetBaseLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + int virtType ATTRIBUTE_UNUSED) +{ + return NULL; +} + +virSecurityDriver virSecurityDriverSmack = { + .privateDataLen = 0, + .name = SECURITY_SMACK_NAME, + .probe = virSecuritySmackSecurityDriverProbe, + .open = virSecuritySmackSecurityDriverOpen, + .close = virSecuritySmackSecurityDriverClose, + + .getModel = virSecuritySmackSecurityDriverGetModel, + .getDOI = virSecuritySmackSecurityDriverGetDOI, + + .domainSecurityVerify = virSecuritySmackSecurityVerify, + + .domainSetSecurityDiskLabel = virSecuritySmackSetDiskLabel, + .domainRestoreSecurityDiskLabel = virSecuritySmackRestoreDiskLabel, + + .domainSetSecurityImageLabel = virSecuritySmackSetImageLabel, + .domainRestoreSecurityImageLabel = virSecuritySmackRestoreImageLabel, + + .domainSetSecurityDaemonSocketLabel = virSecuritySmackSetDaemonSocketLabel, + .domainSetSecuritySocketLabel = virSecuritySmackSetSocketLabel, + .domainClearSecuritySocketLabel = virSecuritySmackClearSocketLabel, + + .domainGenSecurityLabel = virSecuritySmackGenLabel, + .domainReserveSecurityLabel = virSecuritySmackReserveLabel, + .domainReleaseSecurityLabel = virSecuritySmackReleaseLabel, + + .domainGetSecurityProcessLabel = virSecuritySmackGetProcessLabel, + .domainSetSecurityProcessLabel = virSecuritySmackSetProcessLabel, + .domainSetSecurityChildProcessLabel = virSecuritySmackSetChildProcessLabel, + + .domainSetSecurityAllLabel = virSecuritySmackSetAllLabel, + .domainRestoreSecurityAllLabel = virSecuritySmackRestoreAllLabel, + + .domainSetSecurityHostdevLabel = virSecuritySmackSetHostdevLabel, + .domainRestoreSecurityHostdevLabel = virSecuritySmackRestoreHostdevLabel, + + .domainSetSavedStateLabel = virSecuritySmackSetSavedStateLabel, + .domainRestoreSavedStateLabel = virSecuritySmackRestoreSavedStateLabel, + + .domainSetSecurityImageFDLabel = virSecuritySmackSetImageFDLabel, + .domainSetSecurityImagePathLabel = virSecuritySmackSetImagePathLabel, + .domainSetSecurityTapFDLabel = virSecuritySmackSetTapFDLabel, + + .domainGetSecurityMountOptions = virSecuritySmackGetSecurityMountOptions, + + .getBaseLabel = virSecuritySmackGetBaseLabel, + +}; diff --git a/src/security/security_smack.h b/src/security/security_smack.h new file mode 100644 index 0000000..3d9fad9 --- /dev/null +++ b/src/security/security_smack.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Cisco Systems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: + * Hongliang Liang <hliang@xxxxxxxx,cn> + * Changyao Han <changyao@xxxxxxxxxxx> + * Raghuram S. Sudhaakar <rssudhaakar@xxxxxxxxx> + * Randy Aybar <raybar@xxxxxxxxx> + */ + +#ifndef __VIR_SECURITY_SMACK_H__ +# define __VIR_SECURITY_SMACK_H__ + +# include "security_driver.h" + +int virSecuritySmackSockCreate(const char *label, const char *attr); + + +extern virSecurityDriver virSecurityDriverSmack; + +# define SMACK_PREFIX "smack-" + +#endif /* __VIR_SECURITY_SMACK_H__ */ diff --git a/src/security/security_stack.c b/src/security/security_stack.c index 3ea2751..e30f003 100644 --- a/src/security/security_stack.c +++ b/src/security/security_stack.c @@ -495,6 +495,14 @@ virSecurityStackSetImageFDLabel(virSecurityManagerPtr mgr, } static int +virSecurityStackSetImagePathLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr vm ATTRIBUTE_UNUSED, + const char *path ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityStackSetTapFDLabel(virSecurityManagerPtr mgr, virDomainDefPtr vm, int fd) @@ -659,6 +667,7 @@ virSecurityDriver virSecurityDriverStack = { .domainRestoreSavedStateLabel = virSecurityStackRestoreSavedStateLabel, .domainSetSecurityImageFDLabel = virSecurityStackSetImageFDLabel, + .domainSetSecurityImagePathLabel = virSecurityStackSetImagePathLabel, .domainSetSecurityTapFDLabel = virSecurityStackSetTapFDLabel, .domainGetSecurityMountOptions = virSecurityStackGetMountOptions, diff --git a/src/util/vircommand.c b/src/util/vircommand.c index 027cb64..cdcb3a2 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -41,6 +41,9 @@ #if defined(WITH_SECDRIVER_APPARMOR) # include <sys/apparmor.h> #endif +#if defined(WITH_SECDRIVER_SMACK) +# include <sys/smack.h> +#endif #define __VIR_COMMAND_PRIV_H_ALLOW__ #include "vircommandpriv.h" @@ -134,6 +137,10 @@ struct _virCommand { #if defined(WITH_SECDRIVER_APPARMOR) char *appArmorProfile; #endif +#if defined(WITH_SECDRIVER_SMACK) + char *smackLabel; +#endif + int mask; }; @@ -722,6 +729,30 @@ virExec(virCommandPtr cmd) } # endif +# if defined(WITH_SECDRIVER_SMACK) + if (cmd->smackLabel) { + VIR_DEBUG("Setting child security label to %s", cmd->smackLabel); + + if (smack_set_label_for_self(cmd->smackLabel) < 0) { + virReportSystemError(errno, + _("unable to set Smack label '%s' " + "for '%s'"), + cmd->smackLabel, cmd->args[0]); + goto fork_error; + } + } +# endif + +/* + * if (smack_new_label_from_self(&label) == -1) + * { + * goto fork_error; + * } + * VIR_DEBUG("smack label is %s",label); + * free(label); + * + * + */ /* The steps above may need to do something privileged, so we delay * setuid and clearing capabilities until the last minute. */ @@ -1197,6 +1228,35 @@ virCommandSetAppArmorProfile(virCommandPtr cmd, } + +/** + * virCommandSetSmackLabel: + * @cmd: the command to modify + * @label: the Smack label to use for the child process + * + * Saves a copy of @label to use when setting the Smack context + * label (write to /proc/self/attr/current ) after the child process has + * been started. If Smack isn't compiled into libvirt, or if label is + * NULL, nothing will be done. + */ +void +virCommandSetSmackLabel(virCommandPtr cmd, + const char *label ATTRIBUTE_UNUSED) + +{ + if (!cmd || cmd->has_error) + return; + +#if defined(WITH_SECDRIVER_SMACK) + VIR_FREE(cmd->smackLabel); + if (VIR_STRDUP_QUIET(cmd->smackLabel, label) < 0) + cmd->has_error = ENOMEM; +#endif + return; + +} + + /** * virCommandDaemonize: * @cmd: the command to modify @@ -2796,6 +2856,9 @@ virCommandFree(virCommandPtr cmd) #if defined(WITH_SECDRIVER_APPARMOR) VIR_FREE(cmd->appArmorProfile); #endif +#if defined(WITH_SECDRIVER_SMACK) + VIR_FREE(cmd->smackLabel); +#endif VIR_FREE(cmd); } diff --git a/src/util/vircommand.h b/src/util/vircommand.h index 198da2f..dfc8a65 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -88,6 +88,9 @@ void virCommandSetSELinuxLabel(virCommandPtr cmd, void virCommandSetAppArmorProfile(virCommandPtr cmd, const char *profile); +void virCommandSetSmackLabel(virCommandPtr cmd, + const char *label); + void virCommandDaemonize(virCommandPtr cmd); void virCommandNonblockingFDs(virCommandPtr cmd); -- 1.9.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list