Skip to contentSkip to conversation

qemu: add SELinux hook for loading virtual hard disk

RFC PATCH New David Windsor 2 participants 2 messages Jul 20, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 qemu: add SELinux hook for loading virtual hard disk

Add infrastructure to qemu to make it an SELinux object manager. Currently, the AVC is not being used, since only one permission is currently being checked.

kvm-userspace/qemu/vl.c +14 -0
Index: src/kvm-userspace/qemu/vl.c
===================================================================
@@ -92,6 +92,8 @@
 #include "qemu-kvm.h"
 #endif
 
+#include "selinux.h"
+
 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
 #ifdef __sun__
 #define SMBD_COMMAND "/usr/sfw/sbin/smbd"
@@ -7522,6 +7524,14 @@ int main(int argc, char **argv)
     }
 #endif
 
+#ifdef CONFIG_VM_SECURITY
+    /* Initialize SELinux */
+    if (selinux_init()) {
+       fprintf(stderr, "Could not initialize SELinux\n");
+       exit(1);
+    }
+#endif
+
     /* we always create the cdrom drive, even if no disk is there */
     bdrv_init();
     if (cdrom_index >= 0) {
@@ -7537,6 +7547,14 @@ int main(int argc, char **argv)
                 snprintf(buf, sizeof(buf), "hd%c", i + 'a');
                 bs_table[i] = bdrv_new(buf);
             }
+
+#ifdef CONFIG_VM_SECURITY
+           if (selinux_vm_use_block_device(hd_filename[i])) {
+               fprintf(stderr, "qemu: SELinux denied access to hard disk

image '%s'\n", hd_filename[i]);
+ exit(1);
+ }
+#endif
+
if (bdrv_open(bs_table[i], hd_filename[i], snapshot ?
BDRV_O_SNAPSHOT : 0) < 0) {
fprintf(stderr, "qemu: could not open hard disk image
'%s'\n",

                         hd_filename[i]);
kvm-userspace/qemu/configure +10 -1
Index: src/kvm-userspace/qemu/configure
===================================================================
@@ -90,6 +90,7 @@ bsd="no"
 linux="no"
 kqemu="no"
 kvm="no"
+vm_security="no"
 profiler="no"
 kernel_path=""
 cocoa="no"
@@ -238,6 +239,8 @@ for opt do
   ;;
   --enable-kvm) kvm="yes"
   ;;
+  --enable-vm-security) vm_security="yes"
+  ;;
   --enable-profiler) profiler="yes"
   ;;
   --kernel-path=*) kernel_path="$optarg"
@@ -287,6 +290,7 @@ echo "kqemu kernel acceleration support:
 echo "  --disable-kqemu          disable kqemu support"
 echo "  --kernel-path=PATH       set the kernel path (configure probes it)"
 echo "  --enable-kvm             enable kernel virtual machine support"
+echo "  --enable-vm-security     enable SELinux enforcement hooks"
 echo ""
 echo "Advanced options (experts only):"
 echo "  --source-path=PATH       path of source code [$source_path]"
@@ -868,13 +872,18 @@ bflt="no"
 interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
 echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
 
+if test $vm_security = "yes" ; then
+       echo "#define CONFIG_VM_SECURITY 1" >> $config_h
+       echo "CONFIG_VM_SECURITY=yes" >> $config_mak
+fi
+
 configure_kvm() {
   if test $kvm = "yes" -a "$target_softmmu" = "yes" -a \
           \( "$cpu" = "i386" -o "$cpu" = "x86_64" \); then
     echo "#define USE_KVM 1" >> $config_h
     echo "CONFIG_KVM_KERNEL_INC=$kernel_path/include" >> $config_mak
   fi
-}
+  }
 
 if test "$target_cpu" = "i386" ; then
   echo "TARGET_ARCH=i386" >> $config_mak
kvm-userspace/configure +1 -0
Index: src/kvm-userspace/configure
===================================================================
@@ -5,6 +5,7 @@ kerneldir=/lib/modules/$(uname -r)/build
 want_module=1
 qemu_cc=$(ls /usr/bin/gcc3* /usr/bin/gcc-3* 2>/dev/null | tail -n1)
 disable_gcc_check=
+enable_vm_security=
 
 usage() {
     cat <<-EOF
@@ -18,6 +19,7 @@ usage() {
            --qemu-cc="$qemu_cc"   compiler for qemu (needs gcc3.x)

($qemu_cc)
--disable-gcc-check don't insist on gcc-3.x
- this will break running without kvm
+ --enable-vm-security enable SELinux security enforcement
EOF
exit 1
}
@@ -53,6 +55,9 @@ while [[ "$1" = -* ]]; do

        --disable-gcc-check)
            disable_gcc_check=1
            ;;
+       --enable-vm-security)
+           enable_vm_security=1
+           ;;
        --help)
            usage
            ;;
@@ -87,6 +92,7 @@ target_cpu() {
     --enable-kvm --kernel-path="$libkvm_kerneldir" \
     --enable-alsa \
     ${disable_gcc_check:+"--disable-gcc-check"} \
+    ${enable_vm_security:+"--enable-vm-security"}\
     --prefix="$prefix"

)

kvm-userspace/qemu/Makefile.target +0 -0
Index: src/kvm-userspace/qemu/Makefile.target
===================================================================
@@ -232,7 +232,7 @@ OBJS+= libqemu.a
 
 # cpu emulator library
 LIBOBJS=exec.o kqemu.o qemu-kvm.o translate-op.o translate-all.o

cpu-exec.o\
- translate.o op.o
+ translate.o op.o selinux.o
ifdef CONFIG_SOFTFLOAT
LIBOBJS+=fpu/softfloat.o
else
@@ -364,6 +364,9 @@ CFLAGS += -I $(CONFIG_KVM_KERNEL_INC)
LIBS += -lkvm
DEPLIBS += ../user/libkvm.a
endif
+ifdef CONFIG_VM_SECURITY
+LIBS += -lselinux
+endif

# SCSI layer
VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o

kvm-userspace/qemu/selinux.h new file +18 -0
Index: src/kvm-userspace/qemu/selinux.h
===================================================================
@@ -0,0 +1,24 @@
+#ifndef _QEMU_SELINUX_H_
+#define _QEMU_SELINUX_H_
+
+/*
+ * Initialize qemu's SELinux subsystem.
+ *
+ * Returns 0 on success, non-zero on error.
+ * This function will only return non-zero when SELinux is
+ * enabled in the running kernel and is in enforcing mode.
+ */
+int selinux_init(void);
+
+/*
+ * Check the SELinux policy as to whether the current process is allowed
+ * to load a virtual disk into a virtual machine.
+ * This function should only be called after calling selinux_init.
+ *
+ * Returns 0 if the operation is allowed, non-zero if the operation is

denied.
+ * This function will only return non-zero if SELinux is enabled
+ * in the running kernel and is in enforcing mode.
+ */
+int selinux_vm_use_block_device(const char *hd_filename); +
+#endif

kvm-userspace/qemu/selinux.c new file +39 -0
Index: src/kvm-userspace/qemu/selinux.c
===================================================================
@@ -0,0 +1,114 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+#include <selinux/selinux.h>
+
+#include "selinux.h"
+
+static int qemu_selinux_enabled = 0;
+
+/*
+ * Initialize qemu's SELinux subsystem.
+ *
+ * Returns 0 on success, non-zero on error.
+ * This function will return non-zero only when SELinux is
+ * enabled in the running kernel and is in enforcing mode.
+ */
+int selinux_init(void)
+{
+       int retval = -1;
+       struct security_class_mapping map[] = {
+                { "vm",
+                        { "entrypoint", NULL }},
+                { NULL }
+        };
+
+       if (qemu_selinux_enabled) {
+               retval = 0;
+               goto out;
+       }
+
+       retval = is_selinux_enabled();
+       if (!retval)
+               goto out;
+
+       /* Create userspace object class and permission mappings */
+       retval = selinux_set_mapping(map);
+        if (retval < 0) {
+                printf("SELinux: failed to set up security class

mapping\n");
+ goto out;
+ }
+
+ qemu_selinux_enabled = 1;
+out:
+ if (retval != 0 && !security_getenforce())
+ retval = 0;
+ return retval;
+}
+
+/*
+ * Check the SELinux policy as to whether the current process is
+ * allowed to load a virtual disk into a virtual machine.
+ * This function should only be called after calling selinux_init.
+ *
+ * Returns 0 if the operation is allowed, non-zero if the operation is denied.
+ * This function will only return non-zero if SELinux is enabled
+ * in the running kernel and is in enforcing mode.
+ */
+int selinux_vm_use_block_device(const char *hd_filename)
+{

+        security_context_t scon;
+        security_context_t tcon;
+        security_class_t vm_class;
+        access_vector_t vm_perms;
+        struct av_decision avd;
+        int retval = -1;

+
+ retval = is_selinux_enabled();
+ if (!retval) {
+ retval = 0;
+ goto out;
+ }
+
+ vm_class = string_to_security_class("vm");
+ if (!vm_class) {
+ retval = -1;
+ goto out;
+ }
+
+ vm_perms = string_to_av_perm(vm_class, "entrypoint");
+ if (!vm_perms) {
+ retval = -1;
+ goto out;
+ }
+

+        /* The context of the VM is the context of the current process */
+        retval = getcon(&scon);
+        if (retval)
+                goto out;
+
+        /* Get the context of the virtual disk */
+        retval = getfilecon(hd_filename, &tcon);
+        if (retval < 0) {

+ freecon(scon);
+ goto out;
+ }
+
+ retval = security_compute_av(scon, tcon, vm_class,
+ vm_perms, &avd);

+        freecon(scon);
+        freecon(tcon);
+        if (!retval && ((vm_perms & avd.allowed) == vm_perms)) {
+                goto out;
+        } else {
+                /* Access is denied */
+                retval = 1;
+                goto out;

+ }
+
+out:
+ if (retval != 0 && !security_getenforce())
+ retval = 0;
+ return retval;
+}

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

#1 lore raw

RFC PATCH 1/1 qemu: add SELinux hook for loading virtual hard disk

Add infrastructure to qemu to make it an SELinux object manager. Currently, the AVC is not being used, since only one permission is currently being checked.

kvm-userspace/qemu/vl.c +14 -0
Index: src/kvm-userspace/qemu/vl.c
===================================================================
@@ -92,6 +92,8 @@
 #include "qemu-kvm.h"
 #endif
 
+#include "selinux.h"
+
 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
 #ifdef __sun__
 #define SMBD_COMMAND "/usr/sfw/sbin/smbd"
@@ -7522,6 +7524,14 @@ int main(int argc, char **argv)
     }
 #endif
 
+#ifdef CONFIG_VM_SECURITY
+    /* Initialize SELinux */
+    if (selinux_init()) {
+       fprintf(stderr, "Could not initialize SELinux\n");
+       exit(1);
+    }
+#endif
+
     /* we always create the cdrom drive, even if no disk is there */
     bdrv_init();
     if (cdrom_index >= 0) {
@@ -7537,6 +7547,14 @@ int main(int argc, char **argv)
                 snprintf(buf, sizeof(buf), "hd%c", i + 'a');
                 bs_table[i] = bdrv_new(buf);
             }
+
+#ifdef CONFIG_VM_SECURITY
+           if (selinux_vm_use_block_device(hd_filename[i])) {
+               fprintf(stderr, "qemu: SELinux denied access to hard disk

image '%s'\n", hd_filename[i]);
+ exit(1);
+ }
+#endif
+
if (bdrv_open(bs_table[i], hd_filename[i], snapshot ?
BDRV_O_SNAPSHOT : 0) < 0) {
fprintf(stderr, "qemu: could not open hard disk image
'%s'\n",

                         hd_filename[i]);
kvm-userspace/qemu/configure +10 -1
Index: src/kvm-userspace/qemu/configure
===================================================================
@@ -90,6 +90,7 @@ bsd="no"
 linux="no"
 kqemu="no"
 kvm="no"
+vm_security="no"
 profiler="no"
 kernel_path=""
 cocoa="no"
@@ -238,6 +239,8 @@ for opt do
   ;;
   --enable-kvm) kvm="yes"
   ;;
+  --enable-vm-security) vm_security="yes"
+  ;;
   --enable-profiler) profiler="yes"
   ;;
   --kernel-path=*) kernel_path="$optarg"
@@ -287,6 +290,7 @@ echo "kqemu kernel acceleration support:
 echo "  --disable-kqemu          disable kqemu support"
 echo "  --kernel-path=PATH       set the kernel path (configure probes it)"
 echo "  --enable-kvm             enable kernel virtual machine support"
+echo "  --enable-vm-security     enable SELinux enforcement hooks"
 echo ""
 echo "Advanced options (experts only):"
 echo "  --source-path=PATH       path of source code [$source_path]"
@@ -868,13 +872,18 @@ bflt="no"
 interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
 echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
 
+if test $vm_security = "yes" ; then
+       echo "#define CONFIG_VM_SECURITY 1" >> $config_h
+       echo "CONFIG_VM_SECURITY=yes" >> $config_mak
+fi
+
 configure_kvm() {
   if test $kvm = "yes" -a "$target_softmmu" = "yes" -a \
           \( "$cpu" = "i386" -o "$cpu" = "x86_64" \); then
     echo "#define USE_KVM 1" >> $config_h
     echo "CONFIG_KVM_KERNEL_INC=$kernel_path/include" >> $config_mak
   fi
-}
+  }
 
 if test "$target_cpu" = "i386" ; then
   echo "TARGET_ARCH=i386" >> $config_mak
kvm-userspace/configure +1 -0
Index: src/kvm-userspace/configure
===================================================================
@@ -5,6 +5,7 @@ kerneldir=/lib/modules/$(uname -r)/build
 want_module=1
 qemu_cc=$(ls /usr/bin/gcc3* /usr/bin/gcc-3* 2>/dev/null | tail -n1)
 disable_gcc_check=
+enable_vm_security=
 
 usage() {
     cat <<-EOF
@@ -18,6 +19,7 @@ usage() {
            --qemu-cc="$qemu_cc"   compiler for qemu (needs gcc3.x)

($qemu_cc)
--disable-gcc-check don't insist on gcc-3.x
- this will break running without kvm
+ --enable-vm-security enable SELinux security enforcement
EOF
exit 1
}
@@ -53,6 +55,9 @@ while [[ "$1" = -* ]]; do

        --disable-gcc-check)
            disable_gcc_check=1
            ;;
+       --enable-vm-security)
+           enable_vm_security=1
+           ;;
        --help)
            usage
            ;;
@@ -87,6 +92,7 @@ target_cpu() {
     --enable-kvm --kernel-path="$libkvm_kerneldir" \
     --enable-alsa \
     ${disable_gcc_check:+"--disable-gcc-check"} \
+    ${enable_vm_security:+"--enable-vm-security"}\
     --prefix="$prefix"

)

kvm-userspace/qemu/Makefile.target +0 -0
Index: src/kvm-userspace/qemu/Makefile.target
===================================================================
@@ -232,7 +232,7 @@ OBJS+= libqemu.a
 
 # cpu emulator library
 LIBOBJS=exec.o kqemu.o qemu-kvm.o translate-op.o translate-all.o

cpu-exec.o\
- translate.o op.o
+ translate.o op.o selinux.o
ifdef CONFIG_SOFTFLOAT
LIBOBJS+=fpu/softfloat.o
else
@@ -364,6 +364,9 @@ CFLAGS += -I $(CONFIG_KVM_KERNEL_INC)
LIBS += -lkvm
DEPLIBS += ../user/libkvm.a
endif
+ifdef CONFIG_VM_SECURITY
+LIBS += -lselinux
+endif

# SCSI layer
VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o

kvm-userspace/qemu/selinux.h new file +18 -0
Index: src/kvm-userspace/qemu/selinux.h
===================================================================
@@ -0,0 +1,24 @@
+#ifndef _QEMU_SELINUX_H_
+#define _QEMU_SELINUX_H_
+
+/*
+ * Initialize qemu's SELinux subsystem.
+ *
+ * Returns 0 on success, non-zero on error.
+ * This function will only return non-zero when SELinux is
+ * enabled in the running kernel and is in enforcing mode.
+ */
+int selinux_init(void);
+
+/*
+ * Check the SELinux policy as to whether the current process is allowed
+ * to load a virtual disk into a virtual machine.
+ * This function should only be called after calling selinux_init.
+ *
+ * Returns 0 if the operation is allowed, non-zero if the operation is

denied.
+ * This function will only return non-zero if SELinux is enabled
+ * in the running kernel and is in enforcing mode.
+ */
+int selinux_vm_use_block_device(const char *hd_filename); +
+#endif

kvm-userspace/qemu/selinux.c new file +39 -0
Index: src/kvm-userspace/qemu/selinux.c
===================================================================
@@ -0,0 +1,114 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+#include <selinux/selinux.h>
+
+#include "selinux.h"
+
+static int qemu_selinux_enabled = 0;
+
+/*
+ * Initialize qemu's SELinux subsystem.
+ *
+ * Returns 0 on success, non-zero on error.
+ * This function will return non-zero only when SELinux is
+ * enabled in the running kernel and is in enforcing mode.
+ */
+int selinux_init(void)
+{
+       int retval = -1;
+       struct security_class_mapping map[] = {
+                { "vm",
+                        { "entrypoint", NULL }},
+                { NULL }
+        };
+
+       if (qemu_selinux_enabled) {
+               retval = 0;
+               goto out;
+       }
+
+       retval = is_selinux_enabled();
+       if (!retval)
+               goto out;
+
+       /* Create userspace object class and permission mappings */
+       retval = selinux_set_mapping(map);
+        if (retval < 0) {
+                printf("SELinux: failed to set up security class

mapping\n");
+ goto out;
+ }
+
+ qemu_selinux_enabled = 1;
+out:
+ if (retval != 0 && !security_getenforce())
+ retval = 0;
+ return retval;
+}
+
+/*
+ * Check the SELinux policy as to whether the current process is
+ * allowed to load a virtual disk into a virtual machine.
+ * This function should only be called after calling selinux_init.
+ *
+ * Returns 0 if the operation is allowed, non-zero if the operation is denied.
+ * This function will only return non-zero if SELinux is enabled
+ * in the running kernel and is in enforcing mode.
+ */
+int selinux_vm_use_block_device(const char *hd_filename)
+{

+        security_context_t scon;
+        security_context_t tcon;
+        security_class_t vm_class;
+        access_vector_t vm_perms;
+        struct av_decision avd;
+        int retval = -1;

+
+ retval = is_selinux_enabled();
+ if (!retval) {
+ retval = 0;
+ goto out;
+ }
+
+ vm_class = string_to_security_class("vm");
+ if (!vm_class) {
+ retval = -1;
+ goto out;
+ }
+
+ vm_perms = string_to_av_perm(vm_class, "entrypoint");
+ if (!vm_perms) {
+ retval = -1;
+ goto out;
+ }
+

+        /* The context of the VM is the context of the current process */
+        retval = getcon(&scon);
+        if (retval)
+                goto out;
+
+        /* Get the context of the virtual disk */
+        retval = getfilecon(hd_filename, &tcon);
+        if (retval < 0) {

+ freecon(scon);
+ goto out;
+ }
+
+ retval = security_compute_av(scon, tcon, vm_class,
+ vm_perms, &avd);

+        freecon(scon);
+        freecon(tcon);
+        if (!retval && ((vm_perms & avd.allowed) == vm_perms)) {
+                goto out;
+        } else {
+                /* Access is denied */
+                retval = 1;
+                goto out;

+ }
+
+out:
+ if (retval != 0 && !security_getenforce())
+ retval = 0;
+ return retval;
+}