]> xenbits.xensource.com Git - libvirt.git/commitdiff
SELinux security driver for sVirt support (James Morris, Dan Walsh & Daniel Berrange)
authorDaniel P. Berrange <berrange@redhat.com>
Tue, 3 Mar 2009 10:06:49 +0000 (10:06 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Tue, 3 Mar 2009 10:06:49 +0000 (10:06 +0000)
ChangeLog
configure.in
po/POTFILES.in
src/Makefile.am
src/security.c
src/security_selinux.c [new file with mode: 0644]
src/security_selinux.h [new file with mode: 0644]

index bb665f505b19ac3af6694d867ca8ec500eee4197..1629b6246783c3a5e2c7fc0ab9806064e5b09b2e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Tue Mar  3 10:01:13 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
+
+       SELinux security driver for sVirt support (James Morris, Dan Walsh & Daniel
+       Berrange)
+       * configure.in: Check for selinux_virtual_domain_context_path() and
+       selinux_virtual_image_context_path() methods in libselinux.so
+       * po/POTFILES.in: add src/security_selinux.c
+       * src/Makefile.am, src/security.c, src/security_selinux.c,
+       src/security_selinux.h: Add SELinux impl of security driver API
+
 Tue Mar  3 09:55:13 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
 
        virsh additions for sVirt support (James Morris & Dan Walsh)
index 30e4b3281ecb489a48ff33710861c2b86f9a32e4..490c3c9676511666dea02d016a911fd605b584ca 100644 (file)
@@ -640,6 +640,45 @@ AM_CONDITIONAL([HAVE_SELINUX], [test "$with_selinux" != "no"])
 AC_SUBST([SELINUX_CFLAGS])
 AC_SUBST([SELINUX_LIBS])
 
+
+AC_ARG_WITH([secdriver-selinux],
+  [  --with-secdriver-selinux         use SELinux security driver],
+  [],
+  [with_secdriver_selinux=check])
+
+if test "$with_selinux" != "yes" ; then
+  if test "$with_secdriver_selinux" = "check" ; then
+    with_secdriver_selinux=no
+  else
+    AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])
+  fi
+else
+  old_cflags="$CFLAGS"
+  old_libs="$LIBS"
+  CFLAGS="$CFLAGS $SELINUX_CFLAGS"
+  LIBS="$CFLAGS $SELINUX_LIBS"
+
+  fail=0
+  AC_CHECK_FUNC([selinux_virtual_domain_context_path], [], [fail=1])
+  AC_CHECK_FUNC([selinux_virtual_image_context_path], [], [fail=1])
+  CFLAGS="$old_cflags"
+  LIBS="$old_libs"
+
+  if test "$fail" = "1" ; then
+    if test "$with_secdriver_selinux" = "check" ; then
+      with_secdriver_selinux=no
+    else
+      AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])
+    fi
+  else
+    with_secdriver_selinux=yes
+    AC_DEFINE_UNQUOTED([WITH_SECDRIVER_SELINUX], 1, [whether SELinux security driver is available])
+  fi
+fi
+AM_CONDITIONAL([WITH_SECDRIVER_SELINUX], [test "$with_secdriver_selinux" != "no"])
+
+
+
 dnl NUMA lib
 AC_ARG_WITH([numactl],
   [  --with-numactl         use numactl for host topology info],
@@ -1320,6 +1359,10 @@ AC_MSG_NOTICE([     LVM: $with_storage_lvm])
 AC_MSG_NOTICE([   iSCSI: $with_storage_iscsi])
 AC_MSG_NOTICE([    Disk: $with_storage_disk])
 AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Security Drivers])
+AC_MSG_NOTICE([])
+AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux])
+AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Driver Loadable Modules])
 AC_MSG_NOTICE([])
 if test "$with_driver_modules" != "no" ; then
index 146d6550a8c7ae3e7ff9eb9ae5b63a5515538308..8b19b7d7597ffd4e71eb85cfe5dc65409120ee17 100644 (file)
@@ -24,6 +24,7 @@ src/qemu_conf.c
 src/qemu_driver.c
 src/remote_internal.c
 src/security.c
+src/security_selinux.c
 src/storage_backend.c
 src/storage_backend_disk.c
 src/storage_backend_fs.c
index 9fd81ec97d6037a31a5a5f930eeeaa142033abdf..d5aac113012fd3c7ef89c076c09c59e5fdcaf87d 100644 (file)
@@ -170,6 +170,9 @@ STORAGE_HELPER_DISK_SOURCES =                                       \
 SECURITY_DRIVER_SOURCES =                                      \
                security.h security.c
 
+SECURITY_DRIVER_SELINUX_SOURCES =                              \
+               security_selinux.h security_selinux.c
+
 
 NODE_DEVICE_DRIVER_SOURCES =                                   \
                node_device.c node_device.h
@@ -387,6 +390,9 @@ endif
 libvirt_driver_security_la_SOURCES = $(SECURITY_DRIVER_SOURCES)
 noinst_LTLIBRARIES += libvirt_driver_security.la
 libvirt_la_LIBADD += libvirt_driver_security.la
+if WITH_SECDRIVER_SELINUX
+libvirt_driver_security_la_SOURCES += $(SECURITY_DRIVER_SELINUX_SOURCES)
+endif
 
 # Add all conditional sources just in case...
 EXTRA_DIST +=                                                  \
index e8294f2640c210179b0f644f7cc017c3f4a7f889..e2bd20aeda1348b6e1ec05dba801b35410c8d693 100644 (file)
 #include "virterror_internal.h"
 
 #include "security.h"
+#ifdef WITH_SECDRIVER_SELINUX
+#include "security_selinux.h"
+#endif
 
 static virSecurityDriverPtr security_drivers[] = {
+#ifdef WITH_SECDRIVER_SELINUX
+    &virSELinuxSecurityDriver,
+#endif
     NULL
 };
 
diff --git a/src/security_selinux.c b/src/security_selinux.c
new file mode 100644 (file)
index 0000000..79478d4
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2008 Red Hat, 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.
+ *
+ * Authors:
+ *     James Morris <jmorris@namei.org>
+ *
+ * SELinux security driver.
+ */
+#include <config.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "security.h"
+#include "security_selinux.h"
+#include "virterror_internal.h"
+#include "util.h"
+#include "memory.h"
+
+static char default_domain_context[1024];
+static char default_image_context[1024];
+#define SECURITY_SELINUX_VOID_DOI       "0"
+#define SECURITY_SELINUX_NAME "selinux"
+
+/* TODO
+   The data struct of used mcs should be replaced with a better data structure in the future
+*/
+
+struct MCS {
+    char *mcs;
+    struct MCS *next;
+};
+static struct MCS *mcsList = NULL;
+
+static int
+mcsAdd(const char *mcs)
+{
+    struct MCS *ptr;
+
+    for (ptr = mcsList; ptr; ptr = ptr->next) {
+        if (STREQ(ptr->mcs, mcs) == 0)
+            return -1;
+    }
+    ptr = malloc(sizeof(struct MCS));
+    ptr->mcs = strdup(mcs);
+    ptr->next = mcsList;
+    mcsList = ptr;
+    return 0;
+}
+
+static int
+mcsRemove(const char *mcs)
+{
+    struct MCS *prevptr = NULL;
+    struct MCS *ptr = NULL;
+
+    for (ptr = mcsList; ptr; ptr = ptr->next) {
+        if (STREQ(ptr->mcs, mcs) == 0) {
+            if (prevptr)
+                prevptr->next = ptr->next;
+            else {
+                mcsList = ptr->next;
+            }
+            free(ptr->mcs);
+            free(ptr);
+            return 0;
+        }
+        prevptr = ptr;
+    }
+    return -1;
+}
+
+static char *
+SELinuxGenNewContext(const char *oldcontext, const char *mcs)
+{
+    char *newcontext = NULL;
+    char *scontext = strdup(oldcontext);
+    if (!scontext) goto err;
+    context_t con = context_new(scontext);
+    if (!con) goto err;
+    context_range_set(con, mcs);
+    newcontext = strdup(context_str(con));
+    context_free(con);
+err:
+    freecon(scontext);
+    return (newcontext);
+}
+
+static int
+SELinuxInitialize(virConnectPtr conn)
+{
+    char *ptr = NULL;
+    int fd = 0;
+    char ebuf[1024];
+
+    virRandomInitialize(time(NULL) ^ getpid());
+
+    fd = open(selinux_virtual_domain_context_path(), O_RDONLY);
+    if (fd < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: cannot open SELinux virtual domain context file %s: %s"),
+                               __func__,selinux_virtual_domain_context_path(),
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    if (saferead(fd, default_domain_context, sizeof(default_domain_context)) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: cannot read SELinux virtual domain context file %s: %s"),
+                               __func__,selinux_virtual_domain_context_path(),
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    ptr = strchrnul(default_domain_context, '\n');
+    *ptr = '\0';
+
+    if ((fd = open(selinux_virtual_image_context_path(), O_RDONLY)) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: cannot open SELinux virtual image context file %s: %s"),
+                               __func__,selinux_virtual_image_context_path(),
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    if (saferead(fd, default_image_context, sizeof(default_image_context)) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: cannot read SELinux virtual image context file %s: %s"),
+                               __func__,selinux_virtual_image_context_path(),
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    ptr = strchrnul(default_image_context, '\n');
+    *ptr = '\0';
+
+    return 0;
+}
+
+static int
+SELinuxGenSecurityLabel(virDomainObjPtr vm)
+{
+    int rc = -1;
+    char mcs[1024];
+    char *scontext = NULL;
+    int c1 = 0;
+    int c2 = 0;
+    if ( ( vm->def->seclabel.label ) ||
+         ( vm->def->seclabel.model ) ||
+         ( vm->def->seclabel.imagelabel ))
+        return rc;
+
+    do {
+        c1 = virRandom(1024);
+        c2 = virRandom(1024);
+
+        if ( c1 == c2 ) {
+            sprintf(mcs, "s0:c%d", c1);
+        } else {
+            if ( c1 == c2 )
+                sprintf(mcs, "s0:c%d,c%d", c1, c2);
+            else
+                sprintf(mcs, "s0:c%d,c%d", c2, c1);
+        }
+    } while(mcsAdd(mcs) == -1);
+
+    vm->def->seclabel.label = SELinuxGenNewContext(default_domain_context, mcs);
+    if (! vm->def->seclabel.label)  goto err;
+    vm->def->seclabel.imagelabel = SELinuxGenNewContext(default_image_context, mcs);
+    if (! vm->def->seclabel.imagelabel)  goto err;
+    vm->def->seclabel.model = strdup(SECURITY_SELINUX_NAME);
+    if (! vm->def->seclabel.model) goto err;
+
+    rc = 0;
+    goto done;
+err:
+    free(vm->def->seclabel.label); vm->def->seclabel.label = NULL;
+    free(vm->def->seclabel.imagelabel); vm->def->seclabel.imagelabel = NULL;
+    free(vm->def->seclabel.model); vm->def->seclabel.model = NULL;
+done:
+    free(scontext);
+    return rc;
+}
+
+static int
+SELinuxSecurityDriverProbe(void)
+{
+    return is_selinux_enabled() ? SECURITY_DRIVER_ENABLE : SECURITY_DRIVER_DISABLE;
+}
+
+static int
+SELinuxSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv)
+{
+    /*
+     * Where will the DOI come from?  SELinux configuration, or qemu
+     * configuration? For the moment, we'll just set it to "0".
+     */
+    virSecurityDriverSetDOI(conn, drv, SECURITY_SELINUX_VOID_DOI);
+    return SELinuxInitialize(conn);
+}
+
+static int
+SELinuxGetSecurityLabel(virConnectPtr conn,
+                        virDomainObjPtr vm,
+                        virSecurityLabelPtr sec)
+{
+    security_context_t ctx;
+
+    if (getpidcon(vm->pid, &ctx) == -1) {
+        char ebuf[1024];
+        virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "getpidcon(): %s"), __func__,
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    if (strlen((char *) ctx) >= VIR_SECURITY_LABEL_BUFLEN) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security label exceeds "
+                               "maximum lenth: %d"), __func__,
+                               VIR_SECURITY_LABEL_BUFLEN - 1);
+        return -1;
+    }
+
+    strcpy(sec->label, (char *) ctx);
+    free(ctx);
+
+    sec->enforcing = security_getenforce();
+    if (sec->enforcing == -1) {
+        char ebuf[1024];
+        virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "security_getenforce(): %s"), __func__,
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+SELinuxSetFilecon(virConnectPtr conn, char *path, char *tcon)
+{
+    char ebuf[1024];
+
+    if(setfilecon(path, tcon) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: unable to set security context "
+                                 "'\%s\' on %s: %s."), __func__,
+                               tcon,
+                               path,
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        if (security_getenforce() == 1)
+            return -1;
+    }
+    return 0;
+}
+
+static int
+SELinuxRestoreSecurityImageLabel(virConnectPtr conn,
+                                 virDomainObjPtr vm,
+                                 virDomainDeviceDefPtr dev)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+
+    if (secdef->imagelabel) {
+        return SELinuxSetFilecon(conn, dev->data.disk->src, default_image_context);
+    }
+    return 0;
+}
+
+static int
+SELinuxSetSecurityImageLabel(virConnectPtr conn,
+                             virDomainObjPtr vm,
+                             virDomainDeviceDefPtr dev)
+
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+
+    if (secdef->imagelabel) {
+        return SELinuxSetFilecon(conn, dev->data.disk->src, secdef->imagelabel);
+    }
+    return 0;
+}
+
+static int
+SELinuxRestoreSecurityLabel(virConnectPtr conn,
+                            virDomainObjPtr vm)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int i;
+    int rc = 0;
+    if (secdef->imagelabel) {
+        for (i = 0 ; i < vm->def->ndisks ; i++) {
+            if (SELinuxSetFilecon(conn, vm->def->disks[i]->src, default_image_context) < 0)
+                rc = -1;
+        }
+        VIR_FREE(secdef->model);
+        VIR_FREE(secdef->label);
+        context_t con = context_new(secdef->imagelabel);
+        if (con) {
+            mcsRemove(context_range_get(con));
+            context_free(con);
+        }
+        VIR_FREE(secdef->imagelabel);
+    }
+    return rc;
+}
+
+static int
+SELinuxSetSecurityLabel(virConnectPtr conn,
+                        virSecurityDriverPtr drv,
+                        virDomainObjPtr vm)
+{
+    /* TODO: verify DOI */
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int i;
+    char ebuf[1024];
+
+    if (!STREQ(drv->name, secdef->model)) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security label driver mismatch: "
+                                 "\'%s\' model configured for domain, but "
+                                 "hypervisor driver is \'%s\'."),
+                                 __func__, secdef->model, drv->name);
+        if (security_getenforce() == 1)
+                return -1;
+    }
+
+    if (setexeccon(secdef->label) == -1) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: unable to set security context "
+                               "'\%s\': %s."), __func__, secdef->label,
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        if (security_getenforce() == 1)
+                return -1;
+    }
+
+    if (secdef->imagelabel) {
+        for (i = 0 ; i < vm->def->ndisks ; i++) {
+            if(setfilecon(vm->def->disks[i]->src, secdef->imagelabel) < 0) {
+                virSecurityReportError(conn, VIR_ERR_ERROR,
+                                       _("%s: unable to set security context "
+                                         "'\%s\' on %s: %s."), __func__,
+                                       secdef->imagelabel,
+                                       vm->def->disks[i]->src,
+                                       virStrerror(errno, ebuf, sizeof ebuf));
+                if (security_getenforce() == 1)
+                    return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+virSecurityDriver virSELinuxSecurityDriver = {
+    .name                       = SECURITY_SELINUX_NAME,
+    .probe                      = SELinuxSecurityDriverProbe,
+    .open                       = SELinuxSecurityDriverOpen,
+    .domainSetSecurityImageLabel = SELinuxSetSecurityImageLabel,
+    .domainRestoreSecurityImageLabel = SELinuxRestoreSecurityImageLabel,
+    .domainGenSecurityLabel     = SELinuxGenSecurityLabel,
+    .domainGetSecurityLabel     = SELinuxGetSecurityLabel,
+    .domainRestoreSecurityLabel = SELinuxRestoreSecurityLabel,
+    .domainSetSecurityLabel     = SELinuxSetSecurityLabel,
+};
diff --git a/src/security_selinux.h b/src/security_selinux.h
new file mode 100644 (file)
index 0000000..1e32209
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2008 Red Hat, 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.
+ *
+ * Authors:
+ *     James Morris <jmorris@namei.org>
+ *
+ */
+#ifndef __VIR_SECURITY_SELINUX_H__
+#define __VIR_SECURITY_SELINUX_H__
+
+extern virSecurityDriver virSELinuxSecurityDriver;
+
+#endif /* __VIR_SECURITY_SELINUX_H__ */