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@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@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@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@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
 
