[libvirt] [RFC] sVirt v0.10 - initial prototype

James Morris jmorris at namei.org
Tue Oct 21 02:06:17 UTC 2008


This is a request for comments on the initial prototype release of sVirt, 
a project to add security labeling support to Linux-based virtualization.

Project page: 
  http://www.selinuxproject.org/page/SVirt

Previous libvirt discussions:

  High-level requirements:
    https://www.redhat.com/archives/libvir-list/2008-August/msg00255.html

  XML security labels:
    https://www.redhat.com/archives/libvir-list/2008-August/msg00740.html

A patch for libvirt is attached; and also included in a release tarball at 
http://namei.org/svirt/.  See 'readme.txt' there for more details on 
building and running the code.

The purpose of this release is to establish a proof of concept of applying 
security labels to VMs, and for discussion of the underlying technical 
approach.

With this release, it is possible to define a security label for a 
kvm/qemu domain in its XML configuration ('virsh edit'), launch the domain 
and have it transition to the specified security label ('virsh start'), 
then query the security label of the running domain ('virsh dominfo').

The following changes were made to libvirt:

1. Implementing a pluggable security label driver framework;

2. Implementing an SELinux security label driver for (1);

3. Wiring the security label framework into the Qemu driver;

4. Implementing basic libvirt API calls for initializing the driver, 
   and getting/setting domain security labels;

5. Extending the domain XML configuration to include security labels;

6. Adding domain security label display/edit/dump support to virsh.

One of the design principles I've followed with this is to reduce or 
eliminate configuration wherever possible.  If a variety of security 
labeling drivers are present, libvirtd automatically detects which one to 
enable and enables it.  e.g. if SELinux is enabled on the system, the 
SELinux labeling driver is enabled automatically when livbirtd starts.

Another is to treat security labels as opaque unless they're actually 
being used for security purposes (e.g. to launch the domain).  So, virsh 
and the domain configuration code currently do not need to semantically 
interpet security labels, just understand their format.  This should suit 
the initial simple goal of isolated domains, which only requires security 
labels to be distinct.

The domain security label configuration format is as follows:

# virsh dumpxml sys1
<domain>
    ....
  <seclabel model='selinux'>
    <label>system_u:system_r:virtd_t:s0</label>
    <policytype>targeted</policytype>
  </seclabel>
</domain>

It's possible to query the security label of a running domain via virsh:

# virsh dominfo sys1
Id:             1
Name:           sys1
UUID:           fa3c8e06-0877-2a08-06fd-f2479b7bacb4
OS Type:        hvm
State:          running
CPU(s):         1
CPU time:       11.4s
Max memory:     524288 kB
Used memory:    524288 kB
Autostart:      disable
Security label: system_u:system_r:virtd_t:s0 (selinux/targeted/enforcing)

The security label is deterimed via the new virDomainGetSecLabel() API 
method, which is transported over RPC to the backend driver (qemu in this 
case), and is entirely independent of the local security model, if any.  
e.g. this command could be run remotely from an entirely different 
platform: you just see what's happening on the remote system, as with 
other attributes of the domain.

Feedback on the design thus far is sought before proceeding to more 
comprehensive integration.

In particular, I'd be interested in any thoughts on the placement of the 
security labeling driver within libvirt, as this seems to be the most 
critical architectural issue (I've already refactored this aspect once).  

Currently, the idea is to attach the security labeling driver to the virt 
driver, rather than implement it independently as a top-level component as 
in the case of other types of drivers (e.g. storage).  This is because 
process-based security labeling is highly dependent on the kind of 
virtualization in use, and may not make sense at all in some cases (e.g. 
when using a non-Linux hypervisor, or containers).

In the case of qemu, a security labeling driver is added to qemud:

@@ -63,6 +64,7 @@ struct qemud_driver {
     char *vncListen;
 
     virCapsPtr caps;
+    virSecLabelDriverPtr secLabelDriver;
 };

and then initialized during qemud startup from qemudSecLabelInit().  

During initialization, any available security labeling drivers are probed, 
and the first one which thinks it should be used is installed. Top-level 
libvirt API calls are then dispatched to the active security labeling 
driver via the backend virt driver, as necessary.

Note that the security labeling framework in this release is always 
built-in -- it can be made a compile-time option later if desired.

Requirements not yet addressed include:
- Labeling of resources and generally comprehensive labeling management
- Automatic labeling (e.g. for the simple isolation use-case)
- Integration of labeling support into higher-level management tools such 
  as virt-manager
- Integration with the audit subsystem to help with administration and 
  debugging
- Domain of interpretation (DOI) checking/translation
- Python bindings

As mentioned, the goal at this stage is to get feedback on the underlying 
design: comments welcome!


- James
-- 
James Morris
<jmorris at namei.org>
-------------- next part --------------
diff --git a/Makefile.maint b/Makefile.maint
index d856469..5d03365 100644
--- a/Makefile.maint
+++ b/Makefile.maint
@@ -353,6 +353,7 @@ msg_gen_function += qemudReportError
 msg_gen_function += openvzLog
 msg_gen_function += openvzError
 msg_gen_function += virDomainReportError
+msg_gen_function += virSecLabelReportError
 
 # Uncomment the following and run "make syntax-check" to see diagnostics
 # that are not yet marked for translation, but that need to be rewritten
diff --git a/autobuild.sh b/autobuild.sh
index e62926c..49d4b5d 100755
--- a/autobuild.sh
+++ b/autobuild.sh
@@ -14,10 +14,18 @@ rm -rf coverage
 #mkdir build
 #cd build
 
+SELINUXENABLED=/usr/sbin/selinuxenabled
+
+if [ -x $SELINUXENABLED ] && $SELINUXENABLED ; then
+    WITH_SELINUX="--with-selinux=yes"
+else
+    WITH_SELINUX=""
+fi
+
 ./autogen.sh --prefix="$AUTOBUILD_INSTALL_ROOT" \
   --enable-test-coverage \
   --enable-compile-warnings=error \
-  --with-xen-proxy
+  --with-xen-proxy $WITH_SELINUX
 
 # If the MAKEFLAGS envvar does not yet include a -j option,
 # add -jN where N depends on the number of processors.
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
index 35b80d0..58ded58 100644
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -111,6 +111,53 @@ typedef enum {
 } virDomainCreateFlags;
 
 /**
+ * VIR_SECLABEL_LABEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * label string.  Note that this value is based on that used
+ * by Labeled NFS.
+ */
+#define VIR_SECLABEL_LABEL_BUFLEN (4096 + 1)
+
+/**
+ * VIR_SECLABEL_MODEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * model string.
+ */
+#define VIR_SECLABEL_MODEL_BUFLEN (256 + 1)
+
+/**
+ * VIR_SECLABEL_POLICYTYPE_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * policy string.
+ */
+#define VIR_SECLABEL_POLICYTYPE_BUFLEN (256 + 1)
+
+/**
+ * virDomainSecLabel:
+ *
+ * a virDomainSecLabel is a structure filled by virDomainGetSecLabel(),
+ * providing the security label and associated attributes for the specified
+ * domain.
+ *
+ */
+typedef struct _virDomainSecLabel {
+    char model[VIR_SECLABEL_MODEL_BUFLEN];              /* name of security labeling model */
+    char label[VIR_SECLABEL_LABEL_BUFLEN];              /* security label string */
+    char policytype[VIR_SECLABEL_POLICYTYPE_BUFLEN];    /* policy type */
+    int enforcing;                                      /* 1 if security policy is being enforced for domain */
+} virDomainSecLabel;
+
+/**
+ * virDomainSecLabelPtr:
+ *
+ * a virDomainSecLabelPtr is a pointer to a virDomainSecLabel.
+ */
+typedef virDomainSecLabel *virDomainSecLabelPtr;
+
+/**
  * virNodeInfoPtr:
  *
  * a virNodeInfo is a structure filled by virNodeGetInfo() and providing
@@ -504,6 +551,8 @@ int                     virDomainSetMaxMemory   (virDomainPtr domain,
 int                     virDomainSetMemory      (virDomainPtr domain,
                                                  unsigned long memory);
 int                     virDomainGetMaxVcpus    (virDomainPtr domain);
+int                     virDomainGetSecLabel	(virDomainPtr domain,
+                                                 virDomainSecLabelPtr seclabel);
 
 /*
  * XML domain description
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 3624367..e876c00 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -111,6 +111,53 @@ typedef enum {
 } virDomainCreateFlags;
 
 /**
+ * VIR_SECLABEL_LABEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * label string.  Note that this value is based on that used
+ * by Labeled NFS.
+ */
+#define VIR_SECLABEL_LABEL_BUFLEN (4096 + 1)
+
+/**
+ * VIR_SECLABEL_MODEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * model string.
+ */
+#define VIR_SECLABEL_MODEL_BUFLEN (256 + 1)
+
+/**
+ * VIR_SECLABEL_POLICYTYPE_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * policy string.
+ */
+#define VIR_SECLABEL_POLICYTYPE_BUFLEN (256 + 1)
+
+/**
+ * virDomainSecLabel:
+ *
+ * a virDomainSecLabel is a structure filled by virDomainGetSecLabel(),
+ * providing the security label and associated attributes for the specified
+ * domain.
+ *
+ */
+typedef struct _virDomainSecLabel {
+    char model[VIR_SECLABEL_MODEL_BUFLEN];              /* name of security labeling model */
+    char label[VIR_SECLABEL_LABEL_BUFLEN];              /* security label string */
+    char policytype[VIR_SECLABEL_POLICYTYPE_BUFLEN];    /* policy type */
+    int enforcing;                                      /* 1 if security policy is being enforced for domain */
+} virDomainSecLabel;
+
+/**
+ * virDomainSecLabelPtr:
+ *
+ * a virDomainSecLabelPtr is a pointer to a virDomainSecLabel.
+ */
+typedef virDomainSecLabel *virDomainSecLabelPtr;
+
+/**
  * virNodeInfoPtr:
  *
  * a virNodeInfo is a structure filled by virNodeGetInfo() and providing
@@ -504,6 +551,8 @@ int                     virDomainSetMaxMemory   (virDomainPtr domain,
 int                     virDomainSetMemory      (virDomainPtr domain,
                                                  unsigned long memory);
 int                     virDomainGetMaxVcpus    (virDomainPtr domain);
+int                     virDomainGetSecLabel	(virDomainPtr domain,
+                                                 virDomainSecLabelPtr seclabel);
 
 /*
  * XML domain description
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 8e24708..da955c2 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -58,6 +58,7 @@ typedef enum {
     VIR_FROM_STORAGE,   /* Error from storage driver */
     VIR_FROM_NETWORK,   /* Error from network config */
     VIR_FROM_DOMAIN,    /* Error from domain config */
+    VIR_FROM_SECLABEL,  /* Error from security labeling framework */
 } virErrorDomain;
 
 
@@ -148,6 +149,7 @@ typedef enum {
     VIR_WAR_NO_STORAGE, /* failed to start storage */
     VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */
     VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */
+    VIR_ERR_NO_SECLABEL_MODEL, /* security labeling model not found */
 } virErrorNumber;
 
 /**
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f671155..4a200cb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -18,6 +18,8 @@ src/proxy_internal.c
 src/qemu_conf.c
 src/qemu_driver.c
 src/remote_internal.c
+src/seclabel.c
+src/seclabel_selinux.c
 src/sexpr.c
 src/storage_backend.c
 src/storage_backend_disk.c
diff --git a/python/generator.py b/python/generator.py
index c706b19..b95ac4f 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -332,6 +332,7 @@ skip_function = (
     'virCopyLastError', # Python API is called virGetLastError instead
     'virConnectOpenAuth', # Python C code is manually written
     'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C
+    'virDomainGetSecLabel', # Needs investigation...
 )
 
 
diff --git a/qemud/remote.c b/qemud/remote.c
index 72e064e..c3dc871 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -1248,6 +1248,61 @@ remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED,
 }
 
 static int
+remoteDispatchDomainGetSeclabel (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                 struct qemud_client *client,
+                                 remote_message_header *req,
+                                 remote_domain_get_seclabel_args *args,
+                                 remote_domain_get_seclabel_ret *ret)
+{
+    virDomainPtr dom;
+    virDomainSecLabel seclabel;
+    CHECK_CONN(client);
+
+    dom = get_nonnull_domain (client->conn, args->dom);
+    if (dom == NULL) {
+        remoteDispatchError (client, req, "%s", _("domain not found"));
+        return -2;
+    }
+
+    memset (&seclabel, 0, sizeof seclabel);
+
+    if (virDomainGetSecLabel (dom, &seclabel) == -1) {
+        virDomainFree (dom);
+        remoteDispatchError (client, req, "%s", _("unable to get security label"));
+        return -1;
+    }
+
+    ret->label.label_len = strlen(seclabel.label) + 1;
+    if (VIR_ALLOC_N(ret->label.label_val, ret->label.label_len) < 0) {
+        virDomainFree (dom);
+        remoteDispatchError (client, req, "%s", strerror (errno));
+        return -2;
+    }
+    strcpy(ret->label.label_val, seclabel.label);
+
+    ret->model.model_len = strlen(seclabel.model) + 1;
+    if (VIR_ALLOC_N(ret->model.model_val, ret->model.model_len) < 0) {
+        virDomainFree (dom);
+        remoteDispatchError (client, req, "%s", strerror (errno));
+        return -2;
+    }
+    strcpy(ret->model.model_val, seclabel.model);
+
+    ret->policytype.policytype_len = strlen(seclabel.policytype) + 1;
+    if (VIR_ALLOC_N(ret->policytype.policytype_val, ret->policytype.policytype_len) < 0) {
+        virDomainFree (dom);
+        remoteDispatchError (client, req, "%s", strerror (errno));
+        return -2;
+    }
+    strcpy(ret->policytype.policytype_val, seclabel.policytype);
+
+    ret->enforcing = seclabel.enforcing;
+
+    virDomainFree(dom);
+    return 0;
+}
+
+static int
 remoteDispatchDomainGetOsType (struct qemud_server *server ATTRIBUTE_UNUSED,
                                struct qemud_client *client,
                                remote_message_header *req,
diff --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h
index f46b493..fe55109 100644
--- a/qemud/remote_dispatch_localvars.h
+++ b/qemud/remote_dispatch_localvars.h
@@ -89,6 +89,8 @@ remote_domain_get_vcpus_ret lv_remote_domain_get_vcpus_ret;
 remote_domain_get_scheduler_parameters_args lv_remote_domain_get_scheduler_parameters_args;
 remote_domain_get_scheduler_parameters_ret lv_remote_domain_get_scheduler_parameters_ret;
 remote_node_get_info_ret lv_remote_node_get_info_ret;
+remote_domain_get_seclabel_args lv_remote_domain_get_seclabel_args;
+remote_domain_get_seclabel_ret lv_remote_domain_get_seclabel_ret;
 remote_network_lookup_by_name_args lv_remote_network_lookup_by_name_args;
 remote_network_lookup_by_name_ret lv_remote_network_lookup_by_name_ret;
 remote_domain_memory_peek_args lv_remote_domain_memory_peek_args;
diff --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h
index 89296d7..52d1ce1 100644
--- a/qemud/remote_dispatch_proc_switch.h
+++ b/qemud/remote_dispatch_proc_switch.h
@@ -179,6 +179,15 @@ case REMOTE_PROC_DOMAIN_GET_SCHEDULER_TYPE:
         ret = (char *) &lv_remote_domain_get_scheduler_type_ret;
         memset (&lv_remote_domain_get_scheduler_type_ret, 0, sizeof lv_remote_domain_get_scheduler_type_ret);
         break;
+case REMOTE_PROC_DOMAIN_GET_SECLABEL:
+        fn = (dispatch_fn) remoteDispatchDomainGetSeclabel;
+        args_filter = (xdrproc_t) xdr_remote_domain_get_seclabel_args;
+        args = (char *) &lv_remote_domain_get_seclabel_args;
+        memset (&lv_remote_domain_get_seclabel_args, 0, sizeof lv_remote_domain_get_seclabel_args);
+        ret_filter = (xdrproc_t) xdr_remote_domain_get_seclabel_ret;
+        ret = (char *) &lv_remote_domain_get_seclabel_ret;
+        memset (&lv_remote_domain_get_seclabel_ret, 0, sizeof lv_remote_domain_get_seclabel_ret);
+        break;
 case REMOTE_PROC_DOMAIN_GET_VCPUS:
         fn = (dispatch_fn) remoteDispatchDomainGetVcpus;
         args_filter = (xdrproc_t) xdr_remote_domain_get_vcpus_args;
diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h
index 3f4eb9f..2f38611 100644
--- a/qemud/remote_dispatch_prototypes.h
+++ b/qemud/remote_dispatch_prototypes.h
@@ -25,6 +25,7 @@ static int remoteDispatchDomainGetMaxVcpus (struct qemud_server *server, struct
 static int remoteDispatchDomainGetOsType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret);
 static int remoteDispatchDomainGetSchedulerParameters (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret);
 static int remoteDispatchDomainGetSchedulerType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret);
+static int remoteDispatchDomainGetSeclabel (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_seclabel_args *args, remote_domain_get_seclabel_ret *ret);
 static int remoteDispatchDomainGetVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret);
 static int remoteDispatchDomainInterfaceStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret);
 static int remoteDispatchDomainLookupById (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret);
diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c
index 33a25e1..f8ccf71 100644
--- a/qemud/remote_protocol.c
+++ b/qemud/remote_protocol.c
@@ -1083,6 +1083,36 @@ xdr_remote_domain_get_max_vcpus_ret (XDR *xdrs, remote_domain_get_max_vcpus_ret
 }
 
 bool_t
+xdr_remote_domain_get_seclabel_args (XDR *xdrs, remote_domain_get_seclabel_args *objp)
+{
+
+         if (!xdr_remote_nonnull_domain (xdrs, &objp->dom))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_domain_get_seclabel_ret (XDR *xdrs, remote_domain_get_seclabel_ret *objp)
+{
+        char **objp_cpp1 = (char **) (void *) &objp->label.label_val;
+        char **objp_cpp2 = (char **) (void *) &objp->policytype.policytype_val;
+        char **objp_cpp0 = (char **) (void *) &objp->model.model_val;
+
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->model.model_len, REMOTE_SECLABEL_MODEL_MAX,
+                sizeof (char), (xdrproc_t) xdr_char))
+                 return FALSE;
+         if (!xdr_array (xdrs, objp_cpp1, (u_int *) &objp->label.label_len, REMOTE_SECLABEL_LABEL_MAX,
+                sizeof (char), (xdrproc_t) xdr_char))
+                 return FALSE;
+         if (!xdr_array (xdrs, objp_cpp2, (u_int *) &objp->policytype.policytype_len, REMOTE_SECLABEL_POLICYTYPE_MAX,
+                sizeof (char), (xdrproc_t) xdr_char))
+                 return FALSE;
+         if (!xdr_int (xdrs, &objp->enforcing))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_domain_attach_device_args (XDR *xdrs, remote_domain_attach_device_args *objp)
 {
 
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h
index 0bf8b79..9a6b3ac 100644
--- a/qemud/remote_protocol.h
+++ b/qemud/remote_protocol.h
@@ -37,6 +37,9 @@ typedef remote_nonnull_string *remote_string;
 #define REMOTE_AUTH_TYPE_LIST_MAX 20
 #define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536
 #define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536
+#define REMOTE_SECLABEL_MODEL_MAX VIR_SECLABEL_MODEL_BUFLEN
+#define REMOTE_SECLABEL_LABEL_MAX VIR_SECLABEL_LABEL_BUFLEN
+#define REMOTE_SECLABEL_POLICYTYPE_MAX VIR_SECLABEL_POLICYTYPE_BUFLEN
 
 typedef char remote_uuid[VIR_UUID_BUFLEN];
 
@@ -589,6 +592,28 @@ struct remote_domain_get_max_vcpus_ret {
 };
 typedef struct remote_domain_get_max_vcpus_ret remote_domain_get_max_vcpus_ret;
 
+struct remote_domain_get_seclabel_args {
+        remote_nonnull_domain dom;
+};
+typedef struct remote_domain_get_seclabel_args remote_domain_get_seclabel_args;
+
+struct remote_domain_get_seclabel_ret {
+        struct {
+                u_int model_len;
+                char *model_val;
+        } model;
+        struct {
+                u_int label_len;
+                char *label_val;
+        } label;
+        struct {
+                u_int policytype_len;
+                char *policytype_val;
+        } policytype;
+        int enforcing;
+};
+typedef struct remote_domain_get_seclabel_ret remote_domain_get_seclabel_ret;
+
 struct remote_domain_attach_device_args {
         remote_nonnull_domain dom;
         remote_nonnull_string xml;
@@ -1189,6 +1214,7 @@ enum remote_procedure {
         REMOTE_PROC_NODE_GET_FREE_MEMORY = 102,
         REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103,
         REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104,
+        REMOTE_PROC_DOMAIN_GET_SECLABEL = 105,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1308,6 +1334,8 @@ extern  bool_t xdr_remote_domain_get_vcpus_args (XDR *, remote_domain_get_vcpus_
 extern  bool_t xdr_remote_domain_get_vcpus_ret (XDR *, remote_domain_get_vcpus_ret*);
 extern  bool_t xdr_remote_domain_get_max_vcpus_args (XDR *, remote_domain_get_max_vcpus_args*);
 extern  bool_t xdr_remote_domain_get_max_vcpus_ret (XDR *, remote_domain_get_max_vcpus_ret*);
+extern  bool_t xdr_remote_domain_get_seclabel_args (XDR *, remote_domain_get_seclabel_args*);
+extern  bool_t xdr_remote_domain_get_seclabel_ret (XDR *, remote_domain_get_seclabel_ret*);
 extern  bool_t xdr_remote_domain_attach_device_args (XDR *, remote_domain_attach_device_args*);
 extern  bool_t xdr_remote_domain_detach_device_args (XDR *, remote_domain_detach_device_args*);
 extern  bool_t xdr_remote_domain_get_autostart_args (XDR *, remote_domain_get_autostart_args*);
@@ -1489,6 +1517,8 @@ extern bool_t xdr_remote_domain_get_vcpus_args ();
 extern bool_t xdr_remote_domain_get_vcpus_ret ();
 extern bool_t xdr_remote_domain_get_max_vcpus_args ();
 extern bool_t xdr_remote_domain_get_max_vcpus_ret ();
+extern bool_t xdr_remote_domain_get_seclabel_args ();
+extern bool_t xdr_remote_domain_get_seclabel_ret ();
 extern bool_t xdr_remote_domain_attach_device_args ();
 extern bool_t xdr_remote_domain_detach_device_args ();
 extern bool_t xdr_remote_domain_get_autostart_args ();
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
index f1bd9ff..6170cd8 100644
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -110,6 +110,21 @@ const REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX = 65536;
  */
 const REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX = 65536;
 
+/*
+ * Maximum length of a security label model field.
+ */
+const REMOTE_SECLABEL_MODEL_MAX = VIR_SECLABEL_MODEL_BUFLEN;
+
+/*
+ * Maximum length of a security label field.
+ */
+const REMOTE_SECLABEL_LABEL_MAX = VIR_SECLABEL_LABEL_BUFLEN;
+
+/*
+ * Maximum length of a security label policy type field.
+ */
+const REMOTE_SECLABEL_POLICYTYPE_MAX = VIR_SECLABEL_POLICYTYPE_BUFLEN;
+
 /* UUID.  VIR_UUID_BUFLEN definition comes from libvirt.h */
 typedef opaque remote_uuid[VIR_UUID_BUFLEN];
 
@@ -577,6 +592,17 @@ struct remote_domain_get_max_vcpus_ret {
     int num;
 };
 
+struct remote_domain_get_seclabel_args {
+    remote_nonnull_domain dom;
+};
+
+struct remote_domain_get_seclabel_ret {
+    char model<REMOTE_SECLABEL_MODEL_MAX>;
+    char label<REMOTE_SECLABEL_LABEL_MAX>;
+    char policytype<REMOTE_SECLABEL_POLICYTYPE_MAX>;
+    int enforcing;
+};
+
 struct remote_domain_attach_device_args {
     remote_nonnull_domain dom;
     remote_nonnull_string xml;
@@ -1086,7 +1112,9 @@ enum remote_procedure {
     REMOTE_PROC_NODE_GET_FREE_MEMORY = 102,
 
     REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103,
-    REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104
+    REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104,
+
+    REMOTE_PROC_DOMAIN_GET_SECLABEL = 105
 };
 
 /* Custom RPC structure. */
diff --git a/src/Makefile.am b/src/Makefile.am
index 5a769f8..758463c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,7 +111,7 @@ QEMU_DRIVER_SOURCES =						\
 NETWORK_DRIVER_SOURCES =					\
 		network_driver.h network_driver.c
 
-# And finally storage backend specific impls
+# Storage backend specific impls
 STORAGE_DRIVER_SOURCES =					\
 		storage_driver.h storage_driver.c
 
@@ -132,6 +132,12 @@ STORAGE_HELPER_DISK_SOURCES =					\
 		parthelper.c
 
 
+# Security framework and drivers for various models
+SECURITY_DRIVER_SOURCES =					\
+		seclabel.h seclabel.c
+
+SECURITY_DRIVER_SELINUX_SOURCES =				\
+		seclabel_selinux.h seclabel_selinux.c
 
 #########################
 #
@@ -173,6 +179,7 @@ libvirt_la_SOURCES += $(NETWORK_DRIVER_SOURCES)
 endif
 libvirt_la_SOURCES += $(STORAGE_DRIVER_SOURCES)
 libvirt_la_SOURCES += $(STORAGE_DRIVER_FS_SOURCES)
+libvirt_la_SOURCES += $(SECURITY_DRIVER_SOURCES)
 
 if WITH_QEMU
 libvirt_la_SOURCES += $(QEMU_DRIVER_SOURCES)
@@ -193,6 +200,10 @@ endif
 if WITH_STORAGE_DISK
 libvirt_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
 endif
+
+if HAVE_SELINUX
+libvirt_la_SOURCES += $(SECURITY_DRIVER_SELINUX_SOURCES)
+endif
 endif
 
 # Add all conditional sources just in case...
@@ -208,7 +219,9 @@ EXTRA_DIST +=							\
 		$(STORAGE_DRIVER_FS_SOURCES)			\
 		$(STORAGE_DRIVER_LVM_SOURCES)			\
 		$(STORAGE_DRIVER_ISCSI_SOURCES)			\
-		$(STORAGE_DRIVER_DISK_SOURCES)
+		$(STORAGE_DRIVER_DISK_SOURCES)			\
+		$(SECURITY_DRIVER_SOURCES)			\
+		$(SECURITY_DRIVER_SELINUX_SOURCES)
 
 
 libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \
diff --git a/src/domain_conf.c b/src/domain_conf.c
index 2bade8d..5154fac 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -345,6 +345,18 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
     VIR_FREE(def);
 }
 
+void virDomainSecLabelDefFree(virDomainDefPtr def);
+
+void virDomainSecLabelDefFree(virDomainDefPtr def)
+{
+    if (def->seclabel.model)
+        VIR_FREE(def->seclabel.model);
+    if (def->seclabel.label)
+        VIR_FREE(def->seclabel.label);
+    if (def->seclabel.policytype)
+        VIR_FREE(def->seclabel.policytype);
+}
+
 void virDomainDefFree(virDomainDefPtr def)
 {
     unsigned int i;
@@ -403,6 +415,8 @@ void virDomainDefFree(virDomainDefPtr def)
     VIR_FREE(def->cpumask);
     VIR_FREE(def->emulator);
 
+    virDomainSecLabelDefFree(def);
+
     VIR_FREE(def);
 }
 
@@ -1628,6 +1642,61 @@ static int virDomainLifecycleParseXML(virConnectPtr conn,
     return 0;
 }
 
+static int
+virDomainSecLabelDefParseXMLString(virConnectPtr conn,
+                                   const char *str,
+                                   int maxlen,
+                                   const char *name,
+                                   char **to,
+                                   xmlXPathContextPtr ctxt)
+{
+    char *tmp = virXPathString(conn, str, ctxt);
+
+    if (tmp != NULL) {
+        if (strlen(tmp) >= maxlen) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("\'%s\' longer than %d characters"),
+                                 name, maxlen);
+            return -1;
+        }
+        *to = tmp;
+    } else {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("\'%s\' missing"), name);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+virDomainSecLabelDefParseXML(virConnectPtr conn,
+                             const virDomainDefPtr def,
+                             xmlXPathContextPtr ctxt)
+{
+    if (virXPathNode(conn, "./seclabel", ctxt) == NULL)
+        return 0;
+
+    if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/@model)",
+                                          VIR_SECLABEL_MODEL_BUFLEN-1, "model",
+                                          &def->seclabel.model, ctxt) == -1)
+        goto error;
+
+    if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/label[1])",
+                                          VIR_SECLABEL_LABEL_BUFLEN-1, "label",
+                                          &def->seclabel.label, ctxt) == -1)
+        goto error;
+
+    if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/policytype[1])",
+                                          VIR_SECLABEL_POLICYTYPE_BUFLEN-1, "policytype",
+                                          &def->seclabel.policytype, ctxt) == -1)
+        goto error;
+
+    return 0;
+
+error:
+    virDomainSecLabelDefFree(def);
+    return -1;
+}
 
 virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn,
                                               const virDomainDefPtr def,
@@ -2187,6 +2256,10 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
     }
     VIR_FREE(nodes);
 
+    /* analysis of security label */
+    if (virDomainSecLabelDefParseXML(conn, def, ctxt) == -1)
+        goto error;
+
     return def;
 
 no_memory:
@@ -3179,6 +3252,14 @@ char *virDomainDefFormat(virConnectPtr conn,
             goto cleanup;
 
     virBufferAddLit(&buf, "  </devices>\n");
+
+    if (def->seclabel.model) {
+        virBufferEscapeString(&buf, "  <seclabel model='%s'>\n", def->seclabel.model);
+        virBufferEscapeString(&buf, "    <label>%s</label>\n", def->seclabel.label);
+        virBufferEscapeString(&buf, "    <policytype>%s</policytype>\n", def->seclabel.policytype);
+        virBufferAddLit(&buf, "  </seclabel>\n");
+    }
+
     virBufferAddLit(&buf, "</domain>\n");
 
     if (virBufferError(&buf))
diff --git a/src/domain_conf.h b/src/domain_conf.h
index 4d193f4..a8b5e01 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -389,6 +389,15 @@ struct _virDomainOSDef {
     char *bootloaderArgs;
 };
 
+/* Security label configuration for domain */
+typedef struct _virDomainSecLabelDef virDomainSecLabelDef;
+typedef virDomainSecLabelDef *virDomainSecLabelDefPtr;
+struct _virDomainSecLabelDef {
+    char *model;        /* name of security labeling model */
+    char *label;        /* security label string */
+    char *policytype;   /* policy type */
+};
+
 #define VIR_DOMAIN_CPUMASK_LEN 1024
 
 /* Guest VM main configuration */
@@ -446,6 +455,7 @@ struct _virDomainDef {
 
     /* Only 1 */
     virDomainChrDefPtr console;
+    virDomainSecLabelDef seclabel;
 };
 
 /* Guest VM runtime state */
diff --git a/src/driver.h b/src/driver.h
index 0540f80..fd7ebdc 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -182,6 +182,9 @@ typedef int
 typedef int
         (*virDrvDomainGetMaxVcpus)	(virDomainPtr domain);
 typedef int
+        (*virDrvDomainGetSecLabel)	(virDomainPtr domain,
+                                         virDomainSecLabelPtr seclabel);
+typedef int
         (*virDrvDomainAttachDevice)	(virDomainPtr domain,
                                          const char *xml);
 typedef int
@@ -330,6 +333,7 @@ struct _virDriver {
     virDrvDomainPinVcpu		domainPinVcpu;
     virDrvDomainGetVcpus		domainGetVcpus;
     virDrvDomainGetMaxVcpus		domainGetMaxVcpus;
+    virDrvDomainGetSecLabel     domainGetSecLabel;
     virDrvDomainDumpXML		domainDumpXML;
     virDrvListDefinedDomains	listDefinedDomains;
     virDrvNumOfDefinedDomains	numOfDefinedDomains;
diff --git a/src/libvirt.c b/src/libvirt.c
index ca2675a..3b90c37 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -3228,6 +3228,41 @@ virDomainGetMaxVcpus(virDomainPtr domain)
     return -1;
 }
 
+/**
+ * virDomainGetSecLabel:
+ * @domain: a domain object
+ * @vm: VM object (looked up by caller)
+ * @seclabel: pointer to a virDomainSecLabel structure
+ *
+ * Extract security label of an actice domain.
+ *
+ * Returns 0 in case of success, -1 in case of failure, and -2
+ * if the operation is not supported (caller decides if that's
+ * an error).
+ */
+int
+virDomainGetSecLabel(virDomainPtr domain, virDomainSecLabelPtr seclabel)
+{
+    virConnectPtr conn;
+
+    if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+        virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        return -1;
+    }
+
+    if (seclabel == NULL) {
+        virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+
+    conn = domain->conn;
+
+    if (conn->driver->domainGetSecLabel)
+        return conn->driver->domainGetSecLabel(domain, seclabel);
+
+    virLibConnWarning(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -2;
+}
 
 /**
  * virDomainAttachDevice:
diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version
index 3cc4505..190abda 100644
--- a/src/libvirt_sym.version
+++ b/src/libvirt_sym.version
@@ -81,6 +81,8 @@
 
 	virDomainMigrate;
 
+	virDomainGetSecLabel;
+
 	virNetworkGetConnect;
 	virConnectNumOfNetworks;
 	virConnectListNetworks;
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
index c598d1d..dd76a8a 100644
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -1256,6 +1256,7 @@ static virDriver lxcDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     NULL, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
     lxcDomainDumpXML, /* domainDumpXML */
     lxcListDefinedDomains, /* listDefinedDomains */
     lxcNumDefinedDomains, /* numOfDefinedDomains */
diff --git a/src/openvz_driver.c b/src/openvz_driver.c
index b82d0df..4e3fa73 100644
--- a/src/openvz_driver.c
+++ b/src/openvz_driver.c
@@ -990,6 +990,7 @@ static virDriver openvzDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     openvzDomainGetMaxVcpus, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
     openvzDomainDumpXML, /* domainDumpXML */
     openvzListDefinedDomains, /* listDomains */
     openvzNumDefinedDomains, /* numOfDomains */
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index cfd7d35..db4dbc2 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -31,6 +31,7 @@
 #include "capabilities.h"
 #include "network_conf.h"
 #include "domain_conf.h"
+#include "seclabel.h"
 
 #define qemudDebug(fmt, ...) do {} while(0)
 
@@ -63,6 +64,7 @@ struct qemud_driver {
     char *vncListen;
 
     virCapsPtr caps;
+    virSecLabelDriverPtr secLabelDriver;
 };
 
 
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 904fe00..2a27b93 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -64,6 +64,7 @@
 #include "memory.h"
 #include "uuid.h"
 #include "domain_conf.h"
+#include "seclabel.h"
 
 /* For storing short-lived temporary files. */
 #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt"
@@ -141,6 +142,30 @@ qemudAutostartConfigs(struct qemud_driver *driver) {
     }
 }
 
+static int
+qemudSecLabelInit(struct qemud_driver *qemud_drv)
+{
+    int ret;
+    virSecLabelDriverPtr seclabel_drv;
+
+    ret = virSecLabelDriverStartup(&seclabel_drv);
+    if (ret == -1) {
+        qemudLog(QEMUD_ERR, _("Failed to start security labeling driver"));
+        return -1;
+    }
+    if (ret == -2)
+        return 0;
+
+    qemud_drv->secLabelDriver = seclabel_drv;
+
+    qemudLog(QEMUD_DEBUG, "Initialized security labeling driver \'%s\' with "
+             "policy type \'%s\' in %s mode.\n", seclabel_drv->name,
+             virSecLabelDriverGetPolicyType(seclabel_drv),
+             virSecLabelDriverGetEnforcing(seclabel_drv) ? "enforcing" : "permissive");
+
+    return 0;
+}
+
 /**
  * qemudStartup:
  *
@@ -199,6 +224,11 @@ qemudStartup(void) {
     if ((qemu_driver->caps = qemudCapsInit()) == NULL)
         goto out_of_memory;
 
+    if (qemudSecLabelInit(qemu_driver) < 0) {
+        qemudShutdown();
+        return -1;
+    }
+
     if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
         qemudShutdown();
         return -1;
@@ -742,6 +772,15 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
     return -1;
 }
 
+static int qemudDomainSetSecLabel(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm)
+{
+    if (vm->def->seclabel.model != NULL)
+        if (driver->secLabelDriver && driver->secLabelDriver->domainSetLabel)
+            return driver->secLabelDriver->domainSetLabel(conn, driver->secLabelDriver,
+                                                          &vm->def->seclabel);
+    return 0;
+}
+
 static int qemudStartVMDaemon(virConnectPtr conn,
                               struct qemud_driver *driver,
                               virDomainObjPtr vm,
@@ -833,6 +872,16 @@ static int qemudStartVMDaemon(virConnectPtr conn,
         return -1;
     }
 
+    /*
+     * Set up the security label for the domain here, before doing
+     * too much else.
+     */
+    if (qemudDomainSetSecLabel(conn, driver, vm) < 0) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("Failed to set security label"));
+        return -1;
+    }
+
     if (qemudExtractVersionInfo(emulator,
                                 NULL,
                                 &qemuCmdFlags) < 0) {
@@ -2098,7 +2147,47 @@ static int qemudDomainGetMaxVcpus(virDomainPtr dom) {
     return ret;
 }
 
+static int qemudDomainGetSecLabel(virDomainPtr dom, virDomainSecLabelPtr seclabel)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    const char *type;
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(dom->uuid, uuidstr);
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching uuid '%s'"), uuidstr);
+        return -1;
+    }
+
+    if (!(type = virDomainVirtTypeToString(vm->def->virtType))) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("unknown virt type in domain definition '%d'"),
+                         vm->def->virtType);
+        return -1;
+    }
+
+    /*
+     * Theoretically, the pid can be replaced during this operation and
+     * return the label of a different process.  If atomicity is needed,
+     * further validation will be required.
+     */
+    if (virDomainIsActive(vm)) {
+        if (driver->secLabelDriver && driver->secLabelDriver->domainGetLabel) {
+            if (driver->secLabelDriver->domainGetLabel(dom->conn, driver->secLabelDriver,
+                                                       vm, seclabel) == -1) {
+                qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                                 _("Failed to get security label"));
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
 
+/* TODO: check seclabel restore */
 static int qemudDomainRestore(virConnectPtr conn,
                        const char *path) {
     struct qemud_driver *driver = (struct qemud_driver *)conn->privateData;
@@ -3174,6 +3263,7 @@ static virDriver qemuDriver = {
     NULL, /* domainGetVcpus */
 #endif
     qemudDomainGetMaxVcpus, /* domainGetMaxVcpus */
+    qemudDomainGetSecLabel, /* domainGetSecLabel */
     qemudDomainDumpXML, /* domainDumpXML */
     qemudListDefinedDomains, /* listDomains */
     qemudNumDefinedDomains, /* numOfDomains */
diff --git a/src/remote_internal.c b/src/remote_internal.c
index 35b7b4b..bfb71b2 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -1838,6 +1838,58 @@ remoteDomainGetMaxVcpus (virDomainPtr domain)
     return ret.num;
 }
 
+static int
+remoteDomainGetSecLabel (virDomainPtr domain, virDomainSecLabelPtr seclabel)
+{
+    remote_domain_get_seclabel_args args;
+    remote_domain_get_seclabel_ret ret;
+    int rv = 0;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECLABEL,
+              (xdrproc_t) xdr_remote_domain_get_seclabel_args, (char *)&args,
+              (xdrproc_t) xdr_remote_domain_get_seclabel_ret, (char *)&ret) == -1) {
+        return -1;
+    }
+
+    if (ret.policytype.policytype_val != NULL) {
+        if (strlen (ret.label.label_val) >= sizeof seclabel->label) {
+            errorf (domain->conn, VIR_ERR_RPC, _("security label exceeds maximum: %zd"),
+                    sizeof seclabel->label - 1);
+            rv = -1;
+            goto cleanup;
+        }
+
+        if (strlen (ret.model.model_val) >= sizeof seclabel->model) {
+            errorf (domain->conn, VIR_ERR_RPC, _("security model exceeds maximum: %zd"),
+                    sizeof seclabel->model - 1);
+            rv = -1;
+            goto cleanup;
+        }
+
+        if (strlen (ret.policytype.policytype_val) >= sizeof seclabel->policytype) {
+            errorf (domain->conn, VIR_ERR_RPC, _("security policytype exceeds maximum: %zd"),
+                    sizeof seclabel->policytype - 1);
+            rv = -1;
+            goto cleanup;
+        }
+
+        strcpy (seclabel->label, ret.label.label_val);
+        strcpy (seclabel->model, ret.model.model_val);
+        strcpy (seclabel->policytype, ret.policytype.policytype_val);
+        seclabel->enforcing = ret.enforcing;
+
+    cleanup:
+        free (ret.label.label_val);
+        free (ret.model.model_val);
+        free (ret.policytype.policytype_val);
+    }
+
+    return rv;
+}
+
 static char *
 remoteDomainDumpXML (virDomainPtr domain, int flags)
 {
@@ -4850,6 +4902,7 @@ static virDriver driver = {
     .domainPinVcpu = remoteDomainPinVcpu,
     .domainGetVcpus = remoteDomainGetVcpus,
     .domainGetMaxVcpus = remoteDomainGetMaxVcpus,
+    .domainGetSecLabel = remoteDomainGetSecLabel,
     .domainDumpXML = remoteDomainDumpXML,
     .listDefinedDomains = remoteListDefinedDomains,
     .numOfDefinedDomains = remoteNumOfDefinedDomains,
diff --git a/src/seclabel.c b/src/seclabel.c
new file mode 100644
index 0000000..ffe0c90
--- /dev/null
+++ b/src/seclabel.c
@@ -0,0 +1,130 @@
+/*
+ * 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 at namei.org>
+ *
+ */
+#include <config.h>
+#include <string.h>
+
+#include "seclabel.h"
+
+#if HAVE_SELINUX
+#include "seclabel_selinux.h"
+#endif
+
+static virSecLabelDriverPtr seclabel_drivers[] = {
+#ifdef HAVE_SELINUX
+    &virSELinuxSecLabelDriver,
+#endif
+};
+
+/*
+ * Probe each security labeling driver: each should perform a test to see if
+ * it should be loaded, e.g. if the currently active host security mechanism
+ * matches.  If the probe succeeds, initialize the driver and return it.
+ *
+ * Returns 0 on success, -1 on error, and -2 on no driver found (caller
+ * to determine if that's an error).
+ */
+int
+virSecLabelDriverStartup(virSecLabelDriverPtr * drv)
+{
+    unsigned int i;
+
+    for (i = 0; i < (sizeof(seclabel_drivers) / sizeof(seclabel_drivers[0])); i++) {
+        virSecLabelDriverPtr tmp = seclabel_drivers[i];
+
+        if (tmp->probe()) {
+            virSecLabelDriverInit(tmp);
+            if (tmp->open(NULL, tmp) == -1) {
+                return -1;
+            } else {
+                *drv = tmp;
+                break;
+            }
+        } else
+            return -2;
+    }
+    return 0;
+}
+
+void
+virSecLabelReportError(virConnectPtr conn, int code, const char *fmt, ...)
+{
+    va_list args;
+    char errorMessage[1024];
+
+    if (fmt) {
+        va_start(args, fmt);
+        vsnprintf(errorMessage, sizeof(errorMessage) - 1, fmt, args);
+        va_end(args);
+    } else
+        errorMessage[0] = '\0';
+
+    __virRaiseError(conn, NULL, NULL, VIR_FROM_SECLABEL, code,
+                    VIR_ERR_ERROR, NULL, NULL, NULL, -1, -1, "%s",
+                    errorMessage);
+}
+
+/*
+ * Helpers
+ */
+void
+virSecLabelDriverSetEnforcing(virSecLabelDriverPtr drv, int enforcing)
+{
+    drv->_private.enforcing = enforcing;
+}
+
+int
+virSecLabelDriverGetEnforcing(virSecLabelDriverPtr drv)
+{
+    return drv->_private.enforcing;
+}
+
+void
+virSecLabelDriverInit(virSecLabelDriverPtr drv)
+{
+    memset(&drv->_private, 0, sizeof drv->_private);
+}
+
+int
+virSecLabelDriverSetPolicyType(virConnectPtr conn,
+                               virSecLabelDriverPtr drv,
+                               const char *policytype)
+{
+    if (strlen(policytype) >= VIR_SECLABEL_POLICYTYPE_BUFLEN) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: policy type \'%s\' is "
+                               "longer than the maximum allowed length of %d"),
+                               __func__, policytype,
+                               VIR_SECLABEL_POLICYTYPE_BUFLEN - 1);
+        return -1;
+    }
+    strcpy(drv->_private.policytype, policytype);
+    return 0;
+}
+
+const char *
+virSecLabelDriverGetPolicyType(virSecLabelDriverPtr drv)
+{
+    return drv->_private.policytype;
+}
+
+void
+virSecLabelDriverSetInitialized(virSecLabelDriverPtr drv)
+{
+    drv->_private.initialized = 1;
+}
+
+const char *
+virSecLabelDriverGetModel(virSecLabelDriverPtr drv)
+{
+    return drv->name;
+}
diff --git a/src/seclabel.h b/src/seclabel.h
new file mode 100644
index 0000000..4dc602f
--- /dev/null
+++ b/src/seclabel.h
@@ -0,0 +1,68 @@
+/*
+ * 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 at namei.org>
+ *
+ */
+#ifndef __VIR_SECLABEL_H__
+#define __VIR_SECLABEL_H__
+
+#include "internal.h"
+#include "domain_conf.h"
+
+typedef struct _virSecLabelDriver virSecLabelDriver;
+typedef virSecLabelDriver *virSecLabelDriverPtr;
+typedef int (*virSecLabelDriverProbe) (void);
+typedef int (*virSecLabelDriverOpen) (virConnectPtr conn,
+                                      virSecLabelDriverPtr drv);
+typedef int (*virSecLabelDomainGetLabel) (virConnectPtr conn,
+                                          virSecLabelDriverPtr drv,
+                                          virDomainObjPtr vm,
+                                          virDomainSecLabelPtr sec);
+typedef int (*virSecLabelDomainSetLabel) (virConnectPtr conn,
+                                          virSecLabelDriverPtr drv,
+                                          virDomainSecLabelDefPtr secdef);
+
+struct _virSecLabelDriver {
+    const char *name;
+    virSecLabelDriverProbe probe;
+    virSecLabelDriverOpen open;
+    virSecLabelDomainGetLabel domainGetLabel;
+    virSecLabelDomainSetLabel domainSetLabel;
+
+    /*
+     * This is internally managed driver state and should only be accessed
+     * via helpers below.
+     */
+    struct {
+        int enforcing:1, initialized:1;
+        char policytype[VIR_SECLABEL_POLICYTYPE_BUFLEN];
+    } _private;
+};
+
+/* Global methods */
+int virSecLabelDriverStartup(virSecLabelDriverPtr * drv);
+
+void
+virSecLabelReportError(virConnectPtr conn, int code, const char *fmt, ...)
+    ATTRIBUTE_FORMAT(printf, 3, 4);
+
+/* Helpers */
+void virSecLabelDriverSetEnforcing(virSecLabelDriverPtr drv,
+                                   int enforcing);
+int virSecLabelDriverGetEnforcing(virSecLabelDriverPtr drv);
+void virSecLabelDriverInit(virSecLabelDriverPtr drv);
+int virSecLabelDriverSetPolicyType(virConnectPtr conn,
+                                   virSecLabelDriverPtr drv,
+                                   const char *policytype);
+const char *virSecLabelDriverGetPolicyType(virSecLabelDriverPtr drv);
+void virSecLabelDriverSetInitialized(virSecLabelDriverPtr drv);
+const char *virSecLabelDriverGetModel(virSecLabelDriverPtr drv);
+
+#endif /* __VIR_SECLABEL_H__ */
diff --git a/src/seclabel_selinux.c b/src/seclabel_selinux.c
new file mode 100644
index 0000000..4c67a7f
--- /dev/null
+++ b/src/seclabel_selinux.c
@@ -0,0 +1,132 @@
+/*
+ * 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 at namei.org>
+ *
+ * SELinux seclabel driver.
+ */
+#include <config.h>
+#include <selinux/selinux.h>
+
+#include "seclabel.h"
+#include "seclabel_selinux.h"
+
+static int
+SELinuxSecLabelDriverProbe(void)
+{
+    return is_selinux_enabled();
+}
+
+static int
+SELinuxSecLabelDriverOpen(virConnectPtr conn, virSecLabelDriverPtr drv)
+{
+    char *policytype;
+    int enforcing, ret;
+
+    if (selinux_getpolicytype(&policytype) == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "selinux_getpolicytype(): %s"), __func__,
+                               strerror(errno));
+        return -1;
+    }
+
+    virSecLabelDriverSetPolicyType(conn, drv, policytype);
+
+    enforcing = security_getenforce();
+    if (enforcing == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "security_getenforce(): %s"), __func__,
+                               strerror(errno));
+        ret = -1;
+        goto out_free;
+    }
+
+    virSecLabelDriverSetEnforcing(drv, enforcing);
+    ret = 0;
+
+  out_free:
+    free(policytype);
+    return ret;
+}
+
+static int
+SELinuxSecLabelDomainGetLabel(virConnectPtr conn, virSecLabelDriverPtr drv,
+                              virDomainObjPtr vm, virDomainSecLabelPtr sec)
+{
+    security_context_t ctx;
+    const char *p;
+
+    if (getpidcon(vm->pid, &ctx) == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "getpidcon(): %s"), __func__,
+                               strerror(errno));
+        return -1;
+    }
+
+    if (strlen((char *) ctx) >= VIR_SECLABEL_LABEL_BUFLEN) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security label exceeds "
+                               "maximum length: %d"), __func__,
+                               VIR_SECLABEL_LABEL_BUFLEN - 1);
+        return -1;
+    }
+
+    strcpy(sec->label, (char *) ctx);
+    free(ctx);
+
+    p = virSecLabelDriverGetModel(drv);
+    if (strlen(p) >= VIR_SECLABEL_MODEL_BUFLEN) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security model exceeds "
+                               "maximum length: %d"), __func__,
+                               VIR_SECLABEL_MODEL_BUFLEN - 1);
+        return -1;
+    }
+    strcpy(sec->model, p);
+
+    p = virSecLabelDriverGetPolicyType(drv);
+    if (strlen(p) >= VIR_SECLABEL_POLICYTYPE_BUFLEN) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security polictype exceeds "
+                               "maximum length: %d"), __func__,
+                               VIR_SECLABEL_POLICYTYPE_BUFLEN - 1);
+        return -1;
+    }
+    strcpy(sec->policytype, p);
+
+    sec->enforcing = virSecLabelDriverGetEnforcing(drv);
+
+    return 0;
+}
+
+static int
+SELinuxSecLabelDomainSetLabel(virConnectPtr conn, virSecLabelDriverPtr drv,
+                              const virDomainSecLabelDefPtr secdef)
+{
+    /* TODO: verify DOI */
+
+    if (drv) { /* Shut up gcc for now... */ };
+
+    if (setexeccon(secdef->label) == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: unable to set security context "
+                               "'\%s\': %s"), __func__, secdef->label,
+                               strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+virSecLabelDriver virSELinuxSecLabelDriver = {
+    .name               = "selinux",
+    .probe              = SELinuxSecLabelDriverProbe,
+    .open               = SELinuxSecLabelDriverOpen,
+    .domainGetLabel     = SELinuxSecLabelDomainGetLabel,
+    .domainSetLabel     = SELinuxSecLabelDomainSetLabel,
+};
diff --git a/src/seclabel_selinux.h b/src/seclabel_selinux.h
new file mode 100644
index 0000000..20e575d
--- /dev/null
+++ b/src/seclabel_selinux.h
@@ -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 at namei.org>
+ *
+ */
+#ifndef __VIR_SECLABEL_SELINUX_H__
+#define __VIR_SECLABEL_SELINUX_H__
+
+extern virSecLabelDriver virSELinuxSecLabelDriver;
+
+#endif /* __VIR_SECLABEL_SELINUX_H__ */
diff --git a/src/storage_backend.c b/src/storage_backend.c
index de4efa7..df021d7 100644
--- a/src/storage_backend.c
+++ b/src/storage_backend.c
@@ -316,6 +316,7 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
     VIR_FREE(vol->target.perms.label);
 
 #if HAVE_SELINUX
+/* XXX: make this a security driver call */
     if (fgetfilecon(fd, &filecon) == -1) {
         if (errno != ENODATA && errno != ENOTSUP) {
             virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
diff --git a/src/test.c b/src/test.c
index aab74e4..d6cfd3c 100644
--- a/src/test.c
+++ b/src/test.c
@@ -1530,6 +1530,7 @@ static virDriver testDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     NULL, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
     testDomainDumpXML, /* domainDumpXML */
     testListDefinedDomains, /* listDefinedDomains */
     testNumOfDefinedDomains, /* numOfDefinedDomains */
diff --git a/src/virsh.c b/src/virsh.c
index 89aa4fa..4c1208c 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -971,6 +971,7 @@ static const vshCmdOptDef opts_undefine[] = {
     {NULL, 0, 0, NULL}
 };
 
+/* XXX MAC policy for defining & undefining domains ?? */
 static int
 cmdUndefine(vshControl *ctl, const vshCmd *cmd)
 {
@@ -1542,6 +1543,7 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd)
 {
     virDomainInfo info;
     virDomainPtr dom;
+    virDomainSecLabel seclabel;
     int ret = TRUE, autostart;
     unsigned int id;
     char *str, uuid[VIR_UUID_STRING_BUFLEN];
@@ -1600,6 +1602,20 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd)
                  autostart ? _("enable") : _("disable") );
     }
 
+    memset(&seclabel, 0, sizeof seclabel);
+
+    if (virDomainGetSecLabel(dom, &seclabel) == -1)
+        ret = FALSE;
+    else {
+        /*
+         * Security labels are only valid for active
+         * domains.
+         */
+        if (seclabel.model[0] != '\0')
+            vshPrint(ctl, "%-15s %s (%s/%s/%s)\n", _("Security label:"),
+                     seclabel.label, seclabel.model, seclabel.policytype,
+                     seclabel.enforcing ? "enforcing" : "permissive");
+    }
     virDomainFree(dom);
     return ret;
 }
diff --git a/src/virterror.c b/src/virterror.c
index 21c7339..3b4ce2c 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -310,6 +310,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_DOMAIN:
             dom = "Domain Config ";
             break;
+        case VIR_FROM_SECLABEL:
+            dom = "Security Labeling ";
+            break;
 
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
@@ -719,6 +722,12 @@ __virErrorMsg(virErrorNumber error, const char *info)
                 else
                         errmsg = _("Failed to find a storage driver: %s");
                 break;
+        case VIR_ERR_NO_SECLABEL_MODEL:
+            if (info == NULL)
+                errmsg = _("Security labeling model not found");
+            else
+                errmsg = _("Security labeling model not found: %s");
+            break;
     }
     return (errmsg);
 }
diff --git a/tests/daemon-conf b/tests/daemon-conf
index db1f0d3..5c09315 100755
--- a/tests/daemon-conf
+++ b/tests/daemon-conf
@@ -41,6 +41,9 @@ while :; do
   # Filter out this diagnostic.
   sed '/^Cannot set group when not running as root$/d' err > k && mv k err
 
+  # Filter out this diagnostic, too.
+  sed '/^Initialized security labeling driver/d' err > k && mv k err
+
   printf '%s\n\n' "remoteReadConfigFile: $f: $param_name: $msg" > expected-err
   diff -u expected-err err || fail=1
 


More information about the libvir-list mailing list