]> xenbits.xensource.com Git - xen.git/commitdiff
xsm: policy hooks to require an IOMMU and interrupt remapping
authorChristopher Clark <christopher.clark6@baesystems.com>
Wed, 23 Aug 2017 15:47:04 +0000 (17:47 +0200)
committerJan Beulich <jbeulich@suse.com>
Wed, 23 Aug 2017 15:47:04 +0000 (17:47 +0200)
Isolation of devices passed through to domains usually requires an
active IOMMU. The existing method of requiring an IOMMU is via a Xen
boot parameter ("iommu=force") which will abort boot if an IOMMU is not
available.

More graceful degradation of behaviour when an IOMMU is absent can be
achieved by enabling XSM to perform enforcement of IOMMU requirement.

This patch enables an enforceable XSM policy to specify that an IOMMU is
required for particular domains to access devices and how capable that
IOMMU must be. This allows a Xen system to boot whilst still
ensuring that an IOMMU is active before permitting device use.

Using a XSM policy ensures that the isolation properties remain enforced
even when the large, complex toolstack software changes.

For some hardware platforms interrupt remapping is a strict requirement
for secure isolation. Not all IOMMUs provide interrupt remapping.
The XSM policy can now optionally require interrupt remapping.

The device use hooks now check whether an IOMMU is:
 * Active and securely isolating:
    -- current criteria for this is that interrupt remapping is ok
 * Active but interrupt remapping is not available
 * Not active

This patch also updates the reference XSM policy to use the new
primitives, with policy entries that do not require an active IOMMU.

Signed-off-by: Christopher Clark <christopher.clark6@baesystems.com>
Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Reviewed-by: Ross Philipson <ross.philipson@gmail.com>
tools/flask/policy/modules/nic_dev.te
tools/flask/policy/modules/xen.if
tools/flask/policy/modules/xen.te
xen/xsm/flask/hooks.c
xen/xsm/flask/policy/access_vectors

index e0484af1e8d67b74f29ecb521bb05e17e3f77c0f..5206f1eddd89aa29064d442cc7f6abba35d79409 100644 (file)
@@ -11,4 +11,4 @@
 type nic_dev_t, resource_type;
 
 admin_device(dom0_t, nic_dev_t)
-use_device(domU_t, nic_dev_t)
+use_device_noiommu(domU_t, nic_dev_t)
index ed0df4f01068f6aded617e0dddde41832edecfe3..912640002e733627d333618c8fa262c22d42c5fa 100644 (file)
@@ -167,11 +167,32 @@ define(`make_device_model', `
 #
 ################################################################################
 
-# use_device(domain, device)
+# use_device_iommu(domain, device)
 #   Allow a device to be used by a domain
-define(`use_device', `
+#   only if an IOMMU provides isolation.
+define(`use_device_iommu', `
     allow $1 $1_self:mmu exchange;
-    allow $1 $2:resource use;
+    allow $1 $2:resource use_iommu;
+    allow $1 domio_t:mmu { map_read map_write };
+')
+
+# use_device_iommu_nointremap(domain, device)
+#   Allow a device to be used by a domain
+#   only if an IOMMU is active, even if it does not support
+#   interrupt remapping.
+#   Allows acceptance of (typically older) less isolating hardware.
+define(`use_device_iommu_nointremap', `
+    allow $1 $1_self:mmu exchange;
+    allow $1 $2:resource { use_iommu use_iommu_nointremap };
+    allow $1 domio_t:mmu { map_read map_write };
+')
+
+# use_device_noiommu(domain, device)
+#   Allow a device to be used by a domain
+#   even without an IOMMU available.
+define(`use_device_noiommu', `
+    allow $1 $1_self:mmu exchange;
+    allow $1 $2:resource { use_iommu use_iommu_nointremap use_noiommu };
     allow $1 domio_t:mmu { map_read map_write };
 ')
 
@@ -180,7 +201,7 @@ define(`use_device', `
 define(`admin_device', `
     allow $1 $2:resource { setup stat_device add_device add_irq add_iomem add_ioport remove_device remove_irq remove_iomem remove_ioport plug unplug };
     allow $1 $2:hvm bind_irq;
-    use_device($1, $2)
+    use_device_noiommu($1, $2)
 ')
 
 # delegate_devices(priv-domain, target-domain)
index 0cff2df1ecc75778f7b751c164150e2c953039a6..3dbf93d2b859be5ab8a0ce25f4b14135895fe2b2 100644 (file)
@@ -67,7 +67,8 @@ allow xen_t resource_type : resource { remove_irq remove_ioport remove_iomem };
 neverallow * ~domain_type:domain { create transition };
 
 # Resources must be declared using resource_type
-neverallow * ~resource_type:resource use;
+neverallow * ~resource_type:resource { use use_iommu use_iommu_nointremap
+                                       use_noiommu };
 
 # Events must use event_type (see create_channel for a template)
 neverallow ~event_type *:event bind;
index 91146275bb057ae00a9b32fa94fedd70961a8c95..276ca97608d2e39a4c4ef693ba6ed8e60f522b07 100644 (file)
@@ -20,6 +20,7 @@
 #include <xen/errno.h>
 #include <xen/guest_access.h>
 #include <xen/xenoprof.h>
+#include <xen/iommu.h>
 #ifdef CONFIG_HAS_PCI
 #include <asm/msi.h>
 #endif
@@ -886,11 +887,31 @@ static int flask_map_domain_msi (struct domain *d, int irq, void *data,
 #endif
 }
 
+static u32 flask_iommu_resource_use_perm(void)
+{
+    /* Obtain the permission level required for allowing a domain
+     * to use an assigned device.
+     *
+     * An active IOMMU with interrupt remapping capability is essential
+     * for ensuring strict isolation of devices, so provide a distinct
+     * permission for that case and also enable optional support for
+     * less capable hardware (no IOMMU or IOMMU missing intremap capability)
+     * via other separate permissions.
+     */
+    u32 perm = RESOURCE__USE_NOIOMMU;
+
+    if (iommu_enabled)
+        perm = ( iommu_intremap ? RESOURCE__USE_IOMMU :
+                                  RESOURCE__USE_IOMMU_NOINTREMAP );
+    return perm;
+}
+
 static int flask_map_domain_irq (struct domain *d, int irq, void *data)
 {
     u32 sid, dsid;
     int rc = -EPERM;
     struct avc_audit_data ad;
+    u32 dperm = flask_iommu_resource_use_perm();
 
     if ( irq >= nr_static_irqs && data ) {
         rc = flask_map_domain_msi(d, irq, data, &sid, &ad);
@@ -907,7 +928,7 @@ static int flask_map_domain_irq (struct domain *d, int irq, void *data)
     if ( rc )
         return rc;
 
-    rc = avc_has_perm(dsid, sid, SECCLASS_RESOURCE, RESOURCE__USE, &ad);
+    rc = avc_has_perm(dsid, sid, SECCLASS_RESOURCE, dperm, &ad);
     return rc;
 }
 
@@ -956,6 +977,7 @@ static int flask_bind_pt_irq (struct domain *d, struct xen_domctl_bind_pt_irq *b
     int rc = -EPERM;
     int irq;
     struct avc_audit_data ad;
+    u32 dperm = flask_iommu_resource_use_perm();
 
     rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
     if ( rc )
@@ -972,7 +994,7 @@ static int flask_bind_pt_irq (struct domain *d, struct xen_domctl_bind_pt_irq *b
         return rc;
 
     dsid = domain_sid(d);
-    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, RESOURCE__USE, &ad);
+    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad);
 }
 
 static int flask_unbind_pt_irq (struct domain *d, struct xen_domctl_bind_pt_irq *bind)
@@ -990,6 +1012,7 @@ struct iomem_has_perm_data {
     u32 ssid;
     u32 dsid;
     u32 perm;
+    u32 use_perm;
 };
 
 static int _iomem_has_perm(void *v, u32 sid, unsigned long start, unsigned long end)
@@ -1007,7 +1030,7 @@ static int _iomem_has_perm(void *v, u32 sid, unsigned long start, unsigned long
     if ( rc )
         return rc;
 
-    return avc_has_perm(data->dsid, sid, SECCLASS_RESOURCE, RESOURCE__USE, &ad);
+    return avc_has_perm(data->dsid, sid, SECCLASS_RESOURCE, data->use_perm, &ad);
 }
 
 static int flask_iomem_permission(struct domain *d, uint64_t start, uint64_t end, uint8_t access)
@@ -1027,6 +1050,7 @@ static int flask_iomem_permission(struct domain *d, uint64_t start, uint64_t end
 
     data.ssid = domain_sid(current->domain);
     data.dsid = domain_sid(d);
+    data.use_perm = flask_iommu_resource_use_perm();
 
     return security_iterate_iomem_sids(start, end, _iomem_has_perm, &data);
 }
@@ -1041,7 +1065,7 @@ static int flask_pci_config_permission(struct domain *d, uint32_t machine_bdf, u
     u32 dsid, rsid;
     int rc = -EPERM;
     struct avc_audit_data ad;
-    u32 perm = RESOURCE__USE;
+    u32 perm;
 
     rc = security_device_sid(machine_bdf, &rsid);
     if ( rc )
@@ -1050,6 +1074,8 @@ static int flask_pci_config_permission(struct domain *d, uint32_t machine_bdf, u
     /* Writes to the BARs count as setup */
     if ( access && (end >= 0x10 && start < 0x28) )
         perm = RESOURCE__SETUP;
+    else
+        perm = flask_iommu_resource_use_perm();
 
     AVC_AUDIT_DATA_INIT(&ad, DEV);
     ad.device = (unsigned long) machine_bdf;
@@ -1279,6 +1305,7 @@ static int flask_assign_device(struct domain *d, uint32_t machine_bdf)
     u32 dsid, rsid;
     int rc = -EPERM;
     struct avc_audit_data ad;
+    u32 dperm = flask_iommu_resource_use_perm();
 
     rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
     if ( rc )
@@ -1295,7 +1322,7 @@ static int flask_assign_device(struct domain *d, uint32_t machine_bdf)
         return rc;
 
     dsid = domain_sid(d);
-    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, RESOURCE__USE, &ad);
+    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad);
 }
 
 static int flask_deassign_device(struct domain *d, uint32_t machine_bdf)
@@ -1334,6 +1361,7 @@ static int flask_assign_dtdevice(struct domain *d, const char *dtpath)
     u32 dsid, rsid;
     int rc = -EPERM;
     struct avc_audit_data ad;
+    u32 dperm = flask_iommu_resource_use_perm();
 
     rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
     if ( rc )
@@ -1350,7 +1378,7 @@ static int flask_assign_dtdevice(struct domain *d, const char *dtpath)
         return rc;
 
     dsid = domain_sid(d);
-    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, RESOURCE__USE, &ad);
+    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad);
 }
 
 static int flask_deassign_dtdevice(struct domain *d, const char *dtpath)
@@ -1476,6 +1504,7 @@ struct ioport_has_perm_data {
     u32 ssid;
     u32 dsid;
     u32 perm;
+    u32 use_perm;
 };
 
 static int _ioport_has_perm(void *v, u32 sid, unsigned long start, unsigned long end)
@@ -1493,7 +1522,7 @@ static int _ioport_has_perm(void *v, u32 sid, unsigned long start, unsigned long
     if ( rc )
         return rc;
 
-    return avc_has_perm(data->dsid, sid, SECCLASS_RESOURCE, RESOURCE__USE, &ad);
+    return avc_has_perm(data->dsid, sid, SECCLASS_RESOURCE, data->use_perm, &ad);
 }
 
 static int flask_ioport_permission(struct domain *d, uint32_t start, uint32_t end, uint8_t access)
@@ -1514,6 +1543,7 @@ static int flask_ioport_permission(struct domain *d, uint32_t start, uint32_t en
 
     data.ssid = domain_sid(current->domain);
     data.dsid = domain_sid(d);
+    data.use_perm = flask_iommu_resource_use_perm();
 
     return security_iterate_ioport_sids(start, end, _ioport_has_perm, &data);
 }
index 1f7eb35fc8eff3c7fb19ae2cf969683d4c3efa5c..f276f04e29752ea54473c67e71d2e2fbec78fa89 100644 (file)
@@ -420,11 +420,27 @@ class resource
 #  source = domain making the hypercall
 #  target = domain which will no longer have access to the resource
     remove
+# checked when using some core Xen devices (target xen_t)
+#  source = domain which will have access to the resource
+#  target = xen_t
+    use
 # checked when adding a resource to a domain:
 #  source = domain which will have access to the resource
 #  target = resource's security label
-# also checked when using some core Xen devices (target xen_t)
-    use
+# Requires an active IOMMU capable of interrupt remapping in order to
+# enforce isolation.
+    use_iommu
+# checked when adding a resource to a domain when an IOMMU is available
+# but it is not capable of interrupt mapping:
+#  source = domain which will have access to the resource
+#  target = resource's security label
+# Enable this to allow some less secure systems to still work.
+    use_iommu_nointremap
+# checked when adding a resource to a domain when no IOMMU present:
+#  source = domain which will have access to the resource
+#  target = resource's security label
+# Enable this to allow resource use without an active IOMMU.
+    use_noiommu
 # PHYSDEVOP_map_pirq and ioapic writes for dom0, when acting on real IRQs
 #  For GSI interrupts, the IRQ's label is indexed by the IRQ number
 #  For MSI interrupts, the label of the PCI device is used