diff --git a/tools/flask/policy/modules/nic_dev.te b/tools/flask/policy/modules/nic_dev.te index e0484af1e8..5206f1eddd 100644 --- a/tools/flask/policy/modules/nic_dev.te +++ b/tools/flask/policy/modules/nic_dev.te @@ -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) diff --git a/tools/flask/policy/modules/xen.if b/tools/flask/policy/modules/xen.if index ed0df4f010..912640002e 100644 --- a/tools/flask/policy/modules/xen.if +++ b/tools/flask/policy/modules/xen.if @@ -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) diff --git a/tools/flask/policy/modules/xen.te b/tools/flask/policy/modules/xen.te index 0cff2df1ec..3dbf93d2b8 100644 --- a/tools/flask/policy/modules/xen.te +++ b/tools/flask/policy/modules/xen.te @@ -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; diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index 91146275bb..276ca97608 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef CONFIG_HAS_PCI #include #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); } diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors index 1f7eb35fc8..f276f04e29 100644 --- a/xen/xsm/flask/policy/access_vectors +++ b/xen/xsm/flask/policy/access_vectors @@ -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