Files
xen/xen/xsm/flask/hooks.c
T
Jan Beulich 29df230854 XSM: make .hvm_param*() hooks dependent upon HVM=y
They're unreachable / dead otherwise.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
2026-08-03 16:36:13 +02:00

2031 lines
55 KiB
C

/*
* This file contains the Flask hook function implementations for Xen.
*
* Author: George Coker, <gscoker@alpha.ncsc.mil>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*/
#include <xen/init.h>
#include <xen/irq.h>
#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/paging.h>
#include <xen/xmalloc.h>
#include <xsm/xsm.h>
#include <xen/spinlock.h>
#include <xen/cpumask.h>
#include <xen/errno.h>
#include <xen/guest_access.h>
#include <xen/iommu.h>
#ifdef CONFIG_HAS_PCI_MSI
#include <asm/msi.h>
#endif
#include <public/xen.h>
#include <public/physdev.h>
#include <public/platform.h>
#include <public/version.h>
#include <public/hvm/params.h>
#include <public/xenoprof.h>
#include <public/xsm/flask_op.h>
#include <avc.h>
#include <avc_ss.h>
#include <objsec.h>
#include <conditional.h>
#include "private.h"
#ifdef CONFIG_X86
#include <asm/pv/shim.h>
static int flask_shadow_control(struct domain *d, unsigned int op);
#else
#define pv_shim false
#endif
#ifdef CONFIG_HAS_PASSTHROUGH
#ifdef CONFIG_HAS_PCI
static int flask_assign_device(struct domain *d, unsigned int machine_bdf);
static int flask_deassign_device(struct domain *d, unsigned int machine_bdf);
#endif
#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY
static int flask_assign_dtdevice(struct domain *d, const char *dtpath);
static int flask_deassign_dtdevice(struct domain *d, const char *dtpath);
#endif
#endif /* CONFIG_HAS_PASSTHROUGH */
#if defined(CONFIG_SYSCTL) || defined(CONFIG_X86)
static int flask_resource_plug_core(void);
static int flask_resource_unplug_core(void);
#endif
#ifdef CONFIG_SYSCTL
static int flask_resource_use_core(void);
#endif
static uint32_t domain_sid(const struct domain *dom)
{
struct domain_security_struct *dsec = dom->ssid;
return dsec->sid;
}
static uint32_t domain_target_sid(
const struct domain *src, const struct domain *dst)
{
struct domain_security_struct *ssec = src->ssid;
struct domain_security_struct *dsec = dst->ssid;
if ( src == dst )
return ssec->self_sid;
if ( src->target == dst )
return ssec->target_sid;
return dsec->sid;
}
static uint32_t evtchn_sid(const struct evtchn *chn)
{
return chn->ssid.flask_sid;
}
static int domain_has_perm(
const struct domain *dom1, const struct domain *dom2, uint16_t class,
uint32_t perms)
{
uint32_t ssid, tsid;
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad, NONE);
ad.sdom = dom1;
ad.tdom = dom2;
ssid = domain_sid(dom1);
tsid = domain_target_sid(dom1, dom2);
return avc_has_perm(ssid, tsid, class, perms, &ad);
}
static int avc_current_has_perm(
uint32_t tsid, uint16_t class, uint32_t perm, struct avc_audit_data *ad)
{
uint32_t csid = domain_sid(current->domain);
return avc_has_perm(csid, tsid, class, perm, ad);
}
static int current_has_perm(struct domain *d, uint16_t class, uint32_t perms)
{
return domain_has_perm(current->domain, d, class, perms);
}
static int domain_has_evtchn(
struct domain *d, struct evtchn *chn, uint32_t perms)
{
uint32_t dsid = domain_sid(d);
uint32_t esid = evtchn_sid(chn);
return avc_has_perm(dsid, esid, SECCLASS_EVENT, perms, NULL);
}
static int domain_has_xen(struct domain *d, uint32_t perms)
{
uint32_t dsid = domain_sid(d);
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_XEN, perms, NULL);
}
static int get_irq_sid(int irq, uint32_t *sid, struct avc_audit_data *ad)
{
if ( irq >= nr_irqs || irq < 0 )
return -EINVAL;
if ( irq < nr_static_irqs ) {
if (ad) {
AVC_AUDIT_DATA_INIT(ad, IRQ);
ad->irq = irq;
}
return security_irq_sid(irq, sid);
}
#ifdef CONFIG_HAS_PCI_MSI
{
struct irq_desc *desc = irq_to_desc(irq);
if ( desc->msi_desc && desc->msi_desc->dev )
{
struct pci_dev *dev = desc->msi_desc->dev;
uint32_t sbdf = (dev->seg << 16) | (dev->bus << 8) | dev->devfn;
if ( ad )
{
AVC_AUDIT_DATA_INIT(ad, DEV);
ad->device = sbdf;
}
return security_device_sid(sbdf, sid);
}
}
#endif
if ( ad )
{
AVC_AUDIT_DATA_INIT(ad, IRQ);
ad->irq = irq;
}
/* HPET or IOMMU IRQ, should not be seen by domains */
*sid = SECINITSID_UNLABELED;
return 0;
}
static int avc_unknown_permission(const char *name, int id)
{
int rc;
if ( !flask_enforcing || security_get_allow_unknown() )
{
printk(XENLOG_G_WARNING "FLASK: Allowing unknown %s: %d.\n", name, id);
rc = 0;
}
else
{
printk(XENLOG_G_ERR "FLASK: Denying unknown %s: %d.\n", name, id);
rc = -EPERM;
}
return rc;
}
static int cf_check flask_domain_alloc_security(struct domain *d)
{
struct domain_security_struct *dsec;
dsec = xzalloc(struct domain_security_struct);
if ( !dsec )
return -ENOMEM;
/* Set as unlabeled then change as appropriate. */
dsec->sid = SECINITSID_UNLABELED;
switch ( d->domain_id )
{
case DOMID_IDLE:
dsec->sid = SECINITSID_XENBOOT;
break;
case DOMID_XEN:
dsec->sid = SECINITSID_DOMXEN;
break;
case DOMID_IO:
dsec->sid = SECINITSID_DOMIO;
break;
default:
if ( domain_sid(current->domain) == SECINITSID_XENBOOT )
{
if ( d->is_privileged )
dsec->sid = SECINITSID_DOM0;
else if ( pv_shim )
dsec->sid = SECINITSID_DOMU;
}
break;
}
dsec->self_sid = dsec->sid;
d->ssid = dsec;
return 0;
}
static int cf_check flask_set_system_active(void)
{
struct domain_security_struct *dsec;
struct domain *d = current->domain;
dsec = d->ssid;
ASSERT(d->is_privileged);
ASSERT(dsec->sid == SECINITSID_XENBOOT);
ASSERT(dsec->self_sid == SECINITSID_XENBOOT);
if ( d->domain_id != DOMID_IDLE )
{
printk("%s: should only be called by idle domain\n", __func__);
return -EPERM;
}
/*
* While is_privileged has no significant meaning under flask, set to false
* as is_privileged is not only used for a privilege check but also as a
* type of domain check, specifically if the domain is the control domain.
*/
d->is_privileged = false;
dsec->self_sid = dsec->sid = SECINITSID_XEN;
return 0;
}
static void cf_check flask_domain_free_security(struct domain *d)
{
struct domain_security_struct *dsec = d->ssid;
if ( !dsec )
return;
d->ssid = NULL;
xfree(dsec);
}
static int cf_check flask_evtchn_unbound(
struct domain *d1, struct evtchn *chn, domid_t id2)
{
uint32_t sid1, sid2, newsid;
int rc;
struct domain *d2;
d2 = rcu_lock_domain_by_any_id(id2);
if ( d2 == NULL )
return -EPERM;
sid1 = domain_sid(d1);
sid2 = domain_target_sid(d1, d2);
rc = security_transition_sid(sid1, sid2, SECCLASS_EVENT, &newsid);
if ( rc )
goto out;
rc = avc_current_has_perm(newsid, SECCLASS_EVENT, EVENT__CREATE, NULL);
if ( rc )
goto out;
rc = avc_has_perm(newsid, sid2, SECCLASS_EVENT, EVENT__BIND, NULL);
if ( rc )
goto out;
chn->ssid.flask_sid = newsid;
out:
rcu_unlock_domain(d2);
return rc;
}
static int cf_check flask_evtchn_interdomain(
struct domain *d1, struct evtchn *chn1,
struct domain *d2, struct evtchn *chn2)
{
uint32_t sid1, sid2, newsid, reverse_sid;
int rc;
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad, NONE);
ad.sdom = d1;
ad.tdom = d2;
sid1 = domain_sid(d1);
sid2 = domain_target_sid(d1, d2);
rc = security_transition_sid(sid1, sid2, SECCLASS_EVENT, &newsid);
if ( rc )
{
printk("security_transition_sid failed, rc=%d, %pd\n", -rc, d2);
return rc;
}
rc = avc_current_has_perm(newsid, SECCLASS_EVENT, EVENT__CREATE, &ad);
if ( rc )
return rc;
rc = avc_has_perm(newsid, sid2, SECCLASS_EVENT, EVENT__BIND, &ad);
if ( rc )
return rc;
/* It's possible the target domain has changed (relabel or destroy/create)
* since the unbound part was created; re-validate this binding now.
*/
reverse_sid = evtchn_sid(chn2);
sid1 = domain_target_sid(d2, d1);
rc = avc_has_perm(reverse_sid, sid1, SECCLASS_EVENT, EVENT__BIND, &ad);
if ( rc )
return rc;
chn1->ssid.flask_sid = newsid;
return rc;
}
static void cf_check flask_evtchn_close_post(struct evtchn *chn)
{
chn->ssid.flask_sid = SECINITSID_UNLABELED;
}
static int cf_check flask_evtchn_send(struct domain *d, struct evtchn *chn)
{
int rc;
switch ( chn->state )
{
case ECS_INTERDOMAIN:
rc = domain_has_evtchn(d, chn, EVENT__SEND);
break;
case ECS_IPI:
case ECS_UNBOUND:
rc = 0;
break;
default:
rc = avc_unknown_permission("event channel state", chn->state);
break;
}
return rc;
}
static int cf_check flask_evtchn_status(struct domain *d, struct evtchn *chn)
{
return domain_has_evtchn(d, chn, EVENT__STATUS);
}
static int cf_check flask_evtchn_reset(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_EVENT, EVENT__RESET);
}
static int cf_check flask_alloc_security_evtchns(
struct evtchn chn[], unsigned int nr)
{
unsigned int i;
for ( i = 0; i < nr; ++i )
chn[i].ssid.flask_sid = SECINITSID_UNLABELED;
return 0;
}
static void cf_check flask_free_security_evtchns(
struct evtchn chn[], unsigned int nr)
{
unsigned int i;
if ( !chn )
return;
for ( i = 0; i < nr; ++i )
chn[i].ssid.flask_sid = SECINITSID_UNLABELED;
}
static char *cf_check flask_show_security_evtchn(
struct domain *d, const struct evtchn *chn)
{
int irq;
uint32_t sid = 0;
char *ctx;
uint32_t ctx_len;
switch ( chn->state )
{
case ECS_UNBOUND:
case ECS_INTERDOMAIN:
sid = evtchn_sid(chn);
break;
case ECS_PIRQ:
irq = domain_pirq_to_irq(d, chn->u.pirq.irq);
if (irq && get_irq_sid(irq, &sid, NULL))
return NULL;
break;
}
if ( !sid )
return NULL;
if ( security_sid_to_context(sid, &ctx, &ctx_len) )
return NULL;
return ctx;
}
static int cf_check flask_init_hardware_domain(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__CREATE_HARDWARE_DOMAIN);
}
#ifdef CONFIG_GRANT_TABLE
static int cf_check flask_grant_mapref(
struct domain *d1, struct domain *d2, uint32_t flags)
{
uint32_t perms = GRANT__MAP_READ;
if ( !(flags & GNTMAP_readonly) )
perms |= GRANT__MAP_WRITE;
return domain_has_perm(d1, d2, SECCLASS_GRANT, perms);
}
static int cf_check flask_grant_unmapref(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_GRANT, GRANT__UNMAP);
}
static int cf_check flask_grant_setup(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_GRANT, GRANT__SETUP);
}
static int cf_check flask_grant_transfer(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_GRANT, GRANT__TRANSFER);
}
static int cf_check flask_grant_copy(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_GRANT, GRANT__COPY);
}
static int cf_check flask_grant_query_size(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_GRANT, GRANT__QUERY);
}
#endif /* CONFIG_GRANT_TABLE */
static int cf_check flask_get_pod_target(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETPODTARGET);
}
static int cf_check flask_set_pod_target(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETPODTARGET);
}
static int cf_check flask_memory_exchange(struct domain *d)
{
return current_has_perm(d, SECCLASS_MMU, MMU__EXCHANGE);
}
static int cf_check flask_memory_adjust_reservation(
struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_MMU, MMU__ADJUST);
}
static int cf_check flask_memory_stat_reservation(
struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_MMU, MMU__STAT);
}
static int cf_check flask_memory_pin_page(
struct domain *d1, struct domain *d2, struct page_info *page)
{
return domain_has_perm(d1, d2, SECCLASS_MMU, MMU__PINPAGE);
}
static int cf_check flask_claim_pages(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SETCLAIM);
}
static int cf_check flask_get_vnumainfo(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__GET_VNUMAINFO);
}
static int cf_check flask_console_io(struct domain *d, int cmd)
{
uint32_t perm;
switch ( cmd )
{
case CONSOLEIO_read:
perm = XEN__READCONSOLE;
break;
case CONSOLEIO_write:
perm = XEN__WRITECONSOLE;
break;
default:
return avc_unknown_permission("console_io", cmd);
}
return domain_has_xen(d, perm);
}
#ifdef CONFIG_KEXEC
static int cf_check flask_kexec(void)
{
return domain_has_xen(current->domain, XEN__KEXEC);
}
#endif
static int cf_check flask_schedop_shutdown(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_DOMAIN, DOMAIN__SHUTDOWN);
}
static void cf_check flask_security_domaininfo(
struct domain *d, struct xen_domctl_getdomaininfo *info)
{
info->ssidref = domain_sid(d);
}
static int cf_check flask_domain_create(struct domain *d, uint32_t ssidref)
{
int rc;
struct domain_security_struct *dsec = d->ssid;
static int dom0_created = 0;
/*
* If the null label is passed, then use the label from security context
* allocation. NB: if the label from the allocated security context is also
* null, the security server will use unlabeled_t for the domain.
*/
if ( ssidref == 0 )
ssidref = dsec->sid;
/*
* First check if the current domain is allowed to create the target domain
* type before making changes to the current state.
*/
rc = avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL);
if ( rc )
return rc;
/*
* The dom0_t label is expressed as a singleton label in the base policy.
* This cannot be enforced by the security server, therefore it will be
* enforced here.
*/
if ( ssidref == SECINITSID_DOM0 )
{
if ( !dom0_created )
dom0_created = 1;
else
return -EINVAL;
}
dsec->sid = ssidref;
dsec->self_sid = dsec->sid;
rc = security_transition_sid(dsec->sid, dsec->sid, SECCLASS_DOMAIN,
&dsec->self_sid);
return rc;
}
static int cf_check flask_getdomaininfo(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETDOMAININFO);
}
static int flask_domctl_scheduler_op(struct domain *d, int op)
{
switch ( op )
{
case XEN_DOMCTL_SCHEDOP_putinfo:
case XEN_DOMCTL_SCHEDOP_putvcpuinfo:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SETSCHEDULER);
case XEN_DOMCTL_SCHEDOP_getinfo:
case XEN_DOMCTL_SCHEDOP_getvcpuinfo:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETSCHEDULER);
default:
return avc_unknown_permission("domctl_scheduler_op", op);
}
}
#ifdef CONFIG_SYSCTL
static int flask_sysctl_scheduler_op(unsigned int op)
{
switch ( op )
{
case XEN_SYSCTL_SCHEDOP_putinfo:
return domain_has_xen(current->domain, XEN__SETSCHEDULER);
case XEN_SYSCTL_SCHEDOP_getinfo:
return domain_has_xen(current->domain, XEN__GETSCHEDULER);
default:
return avc_unknown_permission("sysctl_scheduler_op", op);
}
}
#endif /* CONFIG_SYSCTL */
static int cf_check flask_set_target(struct domain *d, struct domain *t)
{
int rc;
struct domain_security_struct *dsec, *tsec;
dsec = d->ssid;
tsec = t->ssid;
rc = current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__MAKE_PRIV_FOR);
if ( rc )
return rc;
rc = current_has_perm(t, SECCLASS_DOMAIN2, DOMAIN2__SET_AS_TARGET);
if ( rc )
return rc;
/* Use avc_has_perm to avoid resolving target/current SID */
rc = avc_has_perm(dsec->sid, tsec->sid, SECCLASS_DOMAIN, DOMAIN__SET_TARGET, NULL);
if ( rc )
return rc;
/* (tsec, dsec) defaults the label to tsec, as it should here */
rc = security_transition_sid(tsec->sid, dsec->sid, SECCLASS_DOMAIN,
&dsec->target_sid);
return rc;
}
static int cf_check flask_domctl(struct domain *d, struct xen_domctl *op)
{
switch ( op->cmd )
{
case XEN_DOMCTL_createdomain:
/*
* There is a later hook too, but at this early point simply check
* that the calling domain is privileged enough to create a domain.
*
* Note that d is NULL because we haven't even allocated memory for it
* this early in XEN_DOMCTL_createdomain.
*/
return avc_current_has_perm(op->u.createdomain.ssidref, SECCLASS_DOMAIN,
DOMAIN__CREATE, NULL);
/* These have individual XSM hooks and don't make it here. */
case XEN_DOMCTL_bind_pt_irq:
case XEN_DOMCTL_getdomaininfo:
case XEN_DOMCTL_get_device_group:
case XEN_DOMCTL_get_domain_state:
case XEN_DOMCTL_gsi_permission:
case XEN_DOMCTL_iomem_permission:
case XEN_DOMCTL_ioport_mapping:
case XEN_DOMCTL_ioport_permission:
case XEN_DOMCTL_irq_permission:
case XEN_DOMCTL_memory_mapping:
case XEN_DOMCTL_set_target:
case XEN_DOMCTL_unbind_pt_irq:
ASSERT_UNREACHABLE();
return -EILSEQ;
case XEN_DOMCTL_destroydomain:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__DESTROY);
case XEN_DOMCTL_pausedomain:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__PAUSE);
case XEN_DOMCTL_unpausedomain:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__UNPAUSE);
case XEN_DOMCTL_setvcpuaffinity:
case XEN_DOMCTL_setnodeaffinity:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETAFFINITY);
case XEN_DOMCTL_getvcpuaffinity:
case XEN_DOMCTL_getnodeaffinity:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETAFFINITY);
case XEN_DOMCTL_resumedomain:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__RESUME);
case XEN_DOMCTL_max_vcpus:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__MAX_VCPUS);
case XEN_DOMCTL_max_mem:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETDOMAINMAXMEM);
case XEN_DOMCTL_setdomainhandle:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETDOMAINHANDLE);
case XEN_DOMCTL_scheduler_op:
return flask_domctl_scheduler_op(d, op->u.scheduler_op.cmd);
case XEN_DOMCTL_set_ext_vcpucontext:
case XEN_DOMCTL_set_vcpu_msrs:
case XEN_DOMCTL_setvcpucontext:
case XEN_DOMCTL_setvcpuextstate:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETVCPUCONTEXT);
case XEN_DOMCTL_get_ext_vcpucontext:
case XEN_DOMCTL_get_vcpu_msrs:
case XEN_DOMCTL_getvcpucontext:
case XEN_DOMCTL_getvcpuextstate:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETVCPUCONTEXT);
case XEN_DOMCTL_getvcpuinfo:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETVCPUINFO);
case XEN_DOMCTL_settimeoffset:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETTIME);
case XEN_DOMCTL_setdebugging:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETDEBUGGING);
case XEN_DOMCTL_getpageframeinfo3:
return current_has_perm(d, SECCLASS_MMU, MMU__PAGEINFO);
case XEN_DOMCTL_hypercall_init:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__HYPERCALL);
case XEN_DOMCTL_sethvmcontext:
return current_has_perm(d, SECCLASS_HVM, HVM__SETHVMC);
case XEN_DOMCTL_gethvmcontext:
case XEN_DOMCTL_gethvmcontext_partial:
return current_has_perm(d, SECCLASS_HVM, HVM__GETHVMC);
case XEN_DOMCTL_set_address_size:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETADDRSIZE);
case XEN_DOMCTL_get_address_size:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETADDRSIZE);
#ifdef CONFIG_X86
case XEN_DOMCTL_shadow_op:
return flask_shadow_control(d, op->u.shadow_op.op);
#endif
#ifdef CONFIG_HAS_PASSTHROUGH
case XEN_DOMCTL_test_assign_device:
case XEN_DOMCTL_assign_device:
case XEN_DOMCTL_deassign_device:
switch ( op->u.assign_device.dev )
{
#ifdef CONFIG_HAS_PCI
case XEN_DOMCTL_DEV_PCI:
return op->cmd != XEN_DOMCTL_deassign_device
? flask_assign_device(
d, op->u.assign_device.u.pci.machine_sbdf)
: flask_deassign_device(
d, op->u.assign_device.u.pci.machine_sbdf);
#endif
#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY
case XEN_DOMCTL_DEV_DT:
{
struct dt_device_node *dev;
int ret = dt_find_node_by_gpath(op->u.assign_device.u.dt.path,
op->u.assign_device.u.dt.size,
&dev);
if ( ret )
return ret;
op->u.assign_device.u.dt.dev = dev;
return op->cmd != XEN_DOMCTL_deassign_device
? flask_assign_dtdevice(d, dt_node_full_name(dev))
: flask_deassign_dtdevice(d, dt_node_full_name(dev));
}
#endif
default:
/* Unknown type. */
break;
}
return avc_unknown_permission("assign_device", op->cmd);
#endif /* CONFIG_HAS_PASSTHROUGH */
case XEN_DOMCTL_mem_sharing_op:
return current_has_perm(d, SECCLASS_HVM, HVM__MEM_SHARING);
case XEN_DOMCTL_sendtrigger:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__TRIGGER);
case XEN_DOMCTL_set_access_required:
case XEN_DOMCTL_monitor_op:
case XEN_DOMCTL_vm_event_op:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT);
case XEN_DOMCTL_debug_op:
case XEN_DOMCTL_vmtrace_op:
case XEN_DOMCTL_gdbsx_guestmemio:
case XEN_DOMCTL_gdbsx_pausevcpu:
case XEN_DOMCTL_gdbsx_unpausevcpu:
case XEN_DOMCTL_gdbsx_domstatus:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETDEBUGGING);
case XEN_DOMCTL_subscribe:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SET_MISC_INFO);
case XEN_DOMCTL_set_virq_handler:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SET_VIRQ_HANDLER);
case XEN_DOMCTL_set_cpu_policy:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SET_CPU_POLICY);
case XEN_DOMCTL_gettscinfo:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__GETTSC);
case XEN_DOMCTL_settscinfo:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SETTSC);
case XEN_DOMCTL_audit_p2m:
return current_has_perm(d, SECCLASS_HVM, HVM__AUDIT_P2M);
case XEN_DOMCTL_cacheflush:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__CACHEFLUSH);
case XEN_DOMCTL_setvnumainfo:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN2__SET_VNUMAINFO);
case XEN_DOMCTL_psr_cmt_op:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__PSR_CMT_OP);
case XEN_DOMCTL_psr_alloc:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__PSR_ALLOC);
case XEN_DOMCTL_soft_reset:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SOFT_RESET);
case XEN_DOMCTL_vuart_op:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VUART_OP);
case XEN_DOMCTL_get_cpu_policy:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__GET_CPU_POLICY);
case XEN_DOMCTL_get_paging_mempool_size:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETPAGINGMEMPOOL);
case XEN_DOMCTL_set_paging_mempool_size:
return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETPAGINGMEMPOOL);
case XEN_DOMCTL_dt_overlay:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__DT_OVERLAY);
case XEN_DOMCTL_set_llc_colors:
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SET_LLC_COLORS);
default:
return avc_unknown_permission("domctl", op->cmd);
}
}
#ifdef CONFIG_SYSCTL
static int cf_check flask_sysctl(const struct xen_sysctl *op)
{
switch ( op->cmd )
{
case XEN_SYSCTL_readconsole:
return domain_has_xen(current->domain,
XEN__READCONSOLE |
(op->u.readconsole.clear ? XEN__CLEARCONSOLE
: 0));
case XEN_SYSCTL_tbuf_op:
return domain_has_xen(current->domain, XEN__TBUFCONTROL);
case XEN_SYSCTL_sched_id:
return domain_has_xen(current->domain, XEN__GETSCHEDULER);
case XEN_SYSCTL_perfc_op:
return domain_has_xen(current->domain, XEN__PERFCONTROL);
case XEN_SYSCTL_debug_keys:
return domain_has_xen(current->domain, XEN__DEBUG);
case XEN_SYSCTL_getcpuinfo:
return domain_has_xen(current->domain, XEN__GETCPUINFO);
case XEN_SYSCTL_availheap:
return domain_has_xen(current->domain, XEN__HEAP);
case XEN_SYSCTL_get_pmstat:
return domain_has_xen(current->domain, XEN__PM_OP);
case XEN_SYSCTL_pm_op:
return domain_has_xen(current->domain, XEN__PM_OP);
case XEN_SYSCTL_page_offline_op:
switch ( op->u.page_offline.cmd )
{
case sysctl_page_offline:
return flask_resource_unplug_core();
case sysctl_page_online:
return flask_resource_plug_core();
case sysctl_query_page_offline:
return flask_resource_use_core();
default:
return avc_unknown_permission("page_offline",
op->u.page_offline.cmd);
}
case XEN_SYSCTL_lockprof_op:
return domain_has_xen(current->domain, XEN__LOCKPROF);
case XEN_SYSCTL_cpupool_op:
return domain_has_xen(current->domain, XEN__CPUPOOL_OP);
case XEN_SYSCTL_scheduler_op:
return flask_sysctl_scheduler_op(op->u.scheduler_op.cmd);
case XEN_SYSCTL_physinfo:
case XEN_SYSCTL_cputopoinfo:
case XEN_SYSCTL_numainfo:
case XEN_SYSCTL_pcitopoinfo:
case XEN_SYSCTL_get_cpu_policy:
return domain_has_xen(current->domain, XEN__PHYSINFO);
case XEN_SYSCTL_getdomaininfolist:
return flask_getdomaininfo(dom_xen);
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
switch ( op->u.cpu_hotplug.op )
{
case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
return flask_resource_plug_core();
case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
case XEN_SYSCTL_CPU_HOTPLUG_SMT_DISABLE:
return flask_resource_unplug_core();
default:
return avc_unknown_permission("cpu_hotplug", op->u.cpu_hotplug.op);
}
#endif
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
case XEN_SYSCTL_psr_alloc:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_ALLOC, NULL);
case XEN_SYSCTL_get_cpu_featureset:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__GET_CPU_FEATURESET, NULL);
case XEN_SYSCTL_livepatch_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__LIVEPATCH_OP, NULL);
case XEN_SYSCTL_coverage_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__COVERAGE_OP, NULL);
default:
return avc_unknown_permission("sysctl", op->cmd);
}
}
#endif /* CONFIG_SYSCTL */
static inline uint32_t resource_to_perm(uint8_t access)
{
if ( access )
return RESOURCE__ADD;
else
return RESOURCE__REMOVE;
}
static char *cf_check flask_show_irq_sid(int irq)
{
uint32_t sid, ctx_len;
char *ctx;
int rc = get_irq_sid(irq, &sid, NULL);
if ( rc )
return NULL;
if ( security_sid_to_context(sid, &ctx, &ctx_len) )
return NULL;
return ctx;
}
#ifdef CONFIG_HAS_PIRQ
static int cf_check flask_map_domain_pirq(struct domain *d)
{
return current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
}
static int cf_check flask_unmap_domain_pirq(struct domain *d)
{
return current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__REMOVE);
}
#endif /* CONFIG_HAS_PIRQ */
static int flask_map_domain_msi (
struct domain *d, int irq, const void *data, uint32_t *sid,
struct avc_audit_data *ad)
{
#ifdef CONFIG_HAS_PCI_MSI
const struct msi_info *msi = data;
uint32_t machine_bdf = msi->sbdf.sbdf;
AVC_AUDIT_DATA_INIT(ad, DEV);
ad->device = machine_bdf;
return security_device_sid(machine_bdf, sid);
#else
return -EINVAL;
#endif
}
static uint32_t flask_iommu_resource_use_perm(const struct domain *d)
{
/* 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.
*/
uint32_t perm = RESOURCE__USE_NOIOMMU;
if ( is_iommu_enabled(d) )
perm = ( iommu_intremap ? RESOURCE__USE_IOMMU :
RESOURCE__USE_IOMMU_NOINTREMAP );
return perm;
}
static int cf_check flask_map_domain_irq(
struct domain *d, int irq, const void *data)
{
uint32_t sid, dsid;
int rc = -EPERM;
struct avc_audit_data ad;
uint32_t dperm = flask_iommu_resource_use_perm(d);
if ( irq >= nr_static_irqs && data )
rc = flask_map_domain_msi(d, irq, data, &sid, &ad);
else
rc = get_irq_sid(irq, &sid, &ad);
if ( rc )
return rc;
dsid = domain_sid(d);
rc = avc_current_has_perm(sid, SECCLASS_RESOURCE, RESOURCE__ADD_IRQ, &ad);
if ( rc )
return rc;
rc = avc_has_perm(dsid, sid, SECCLASS_RESOURCE, dperm, &ad);
return rc;
}
static int flask_unmap_domain_msi (
struct domain *d, int irq, const void *data, uint32_t *sid,
struct avc_audit_data *ad)
{
#ifdef CONFIG_HAS_PCI_MSI
const struct pci_dev *pdev = data;
uint32_t machine_bdf = (pdev->seg << 16) | (pdev->bus << 8) | pdev->devfn;
AVC_AUDIT_DATA_INIT(ad, DEV);
ad->device = machine_bdf;
return security_device_sid(machine_bdf, sid);
#else
return -EINVAL;
#endif
}
static int cf_check flask_unmap_domain_irq(
struct domain *d, int irq, const void *data)
{
uint32_t sid;
int rc = -EPERM;
struct avc_audit_data ad;
if ( irq >= nr_static_irqs && data )
rc = flask_unmap_domain_msi(d, irq, data, &sid, &ad);
else
rc = get_irq_sid(irq, &sid, &ad);
if ( rc )
return rc;
rc = avc_current_has_perm(sid, SECCLASS_RESOURCE, RESOURCE__REMOVE_IRQ, &ad);
return rc;
}
static int cf_check flask_bind_pt_irq(
struct domain *d, struct xen_domctl_bind_pt_irq *bind)
{
uint32_t dsid, rsid;
int rc = -EPERM;
int irq;
struct avc_audit_data ad;
uint32_t dperm = flask_iommu_resource_use_perm(d);
rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
if ( rc )
return rc;
irq = domain_pirq_to_irq(d, bind->machine_irq);
rc = get_irq_sid(irq, &rsid, &ad);
if ( rc )
return rc;
rc = avc_current_has_perm(rsid, SECCLASS_HVM, HVM__BIND_IRQ, &ad);
if ( rc )
return rc;
dsid = domain_sid(d);
return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad);
}
static int cf_check flask_unbind_pt_irq(
struct domain *d, struct xen_domctl_bind_pt_irq *bind)
{
return current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__REMOVE);
}
static int cf_check flask_irq_permission(
struct domain *d, int pirq, uint8_t access)
{
/* the PIRQ number is not useful; real IRQ is checked during mapping */
return current_has_perm(d, SECCLASS_RESOURCE, resource_to_perm(access));
}
struct iomem_has_perm_data {
uint32_t ssid;
uint32_t dsid;
uint32_t perm;
uint32_t use_perm;
};
static int cf_check _iomem_has_perm(
void *v, uint32_t sid, unsigned long start, unsigned long end)
{
struct iomem_has_perm_data *data = v;
struct avc_audit_data ad;
int rc = -EPERM;
AVC_AUDIT_DATA_INIT(&ad, RANGE);
ad.range.start = start;
ad.range.end = end;
rc = avc_has_perm(data->ssid, sid, SECCLASS_RESOURCE, data->perm, &ad);
if ( rc )
return rc;
return avc_has_perm(data->dsid, sid, SECCLASS_RESOURCE, data->use_perm, &ad);
}
static int cf_check flask_iomem_permission(
struct domain *d, uint64_t start, uint64_t end, uint8_t access)
{
struct iomem_has_perm_data data;
int rc;
rc = current_has_perm(d, SECCLASS_RESOURCE,
resource_to_perm(access));
if ( rc )
return rc;
if ( access )
data.perm = RESOURCE__ADD_IOMEM;
else
data.perm = RESOURCE__REMOVE_IOMEM;
data.ssid = domain_sid(current->domain);
data.dsid = domain_sid(d);
data.use_perm = flask_iommu_resource_use_perm(d);
return security_iterate_iomem_sids(start, end, _iomem_has_perm, &data);
}
static int cf_check flask_iomem_mapping(struct domain *d, uint64_t start, uint64_t end, uint8_t access)
{
return flask_iomem_permission(d, start, end, access);
}
#define flask_iomem_mapping_vpci flask_iomem_mapping
#ifdef CONFIG_HAS_PCI
static int cf_check flask_pci_config_permission(
struct domain *d, uint32_t machine_bdf, uint16_t start, uint16_t end,
uint8_t access)
{
uint32_t dsid, rsid;
int rc = -EPERM;
struct avc_audit_data ad;
uint32_t perm;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
/* Writes to the BARs count as setup */
if ( access && (end >= 0x10 && start < 0x28) )
perm = RESOURCE__SETUP;
else
perm = flask_iommu_resource_use_perm(d);
AVC_AUDIT_DATA_INIT(&ad, DEV);
ad.device = (unsigned long) machine_bdf;
dsid = domain_sid(d);
return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, perm, &ad);
}
#endif /* CONFIG_HAS_PCI */
#if defined(CONFIG_SYSCTL) || defined(CONFIG_X86)
static int flask_resource_plug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
}
static int flask_resource_unplug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__UNPLUG, NULL);
}
#endif /* CONFIG_SYSCTL || CONFIG_X86 */
#ifdef CONFIG_SYSCTL
static int flask_resource_use_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__USE, NULL);
}
#endif /* CONFIG_SYSCTL */
#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
static int cf_check flask_resource_plug_pci(uint32_t machine_bdf)
{
uint32_t rsid;
int rc = -EPERM;
struct avc_audit_data ad;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
AVC_AUDIT_DATA_INIT(&ad, DEV);
ad.device = (unsigned long) machine_bdf;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__PLUG, &ad);
}
static int cf_check flask_resource_unplug_pci(uint32_t machine_bdf)
{
uint32_t rsid;
int rc = -EPERM;
struct avc_audit_data ad;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
AVC_AUDIT_DATA_INIT(&ad, DEV);
ad.device = (unsigned long) machine_bdf;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__UNPLUG, &ad);
}
#endif /* CONFIG_HAS_PASSTHROUGH && CONFIG_HAS_PCI */
#ifdef CONFIG_HAS_PCI
static int cf_check flask_resource_setup_pci(uint32_t machine_bdf)
{
uint32_t rsid;
int rc = -EPERM;
struct avc_audit_data ad;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
AVC_AUDIT_DATA_INIT(&ad, DEV);
ad.device = (unsigned long) machine_bdf;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__SETUP, &ad);
}
static int cf_check flask_resource_setup_gsi(int gsi)
{
uint32_t rsid;
int rc = -EPERM;
struct avc_audit_data ad;
rc = get_irq_sid(gsi, &rsid, &ad);
if ( rc )
return rc;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__SETUP, &ad);
}
#endif /* CONFIG_HAS_PCI */
static int cf_check flask_resource_setup_misc(void)
{
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_RESOURCE, RESOURCE__SETUP, NULL);
}
#ifdef CONFIG_HYPFS
static inline int cf_check flask_hypfs_op(void)
{
return domain_has_xen(current->domain, XEN__HYPFS_OP);
}
#endif
static int cf_check flask_add_to_physmap(struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_MMU, MMU__PHYSMAP);
}
static int cf_check flask_remove_from_physmap(
struct domain *d1, struct domain *d2)
{
return domain_has_perm(d1, d2, SECCLASS_MMU, MMU__PHYSMAP);
}
static int cf_check flask_map_gmfn_foreign(struct domain *d, struct domain *t)
{
return domain_has_perm(d, t, SECCLASS_MMU, MMU__MAP_READ | MMU__MAP_WRITE);
}
#ifdef CONFIG_HVM
static int cf_check flask_hvm_param(struct domain *d, unsigned long op)
{
uint32_t perm;
switch ( op )
{
case HVMOP_set_param:
perm = HVM__SETPARAM;
break;
case HVMOP_get_param:
perm = HVM__GETPARAM;
break;
default:
perm = HVM__HVMCTL;
break;
}
return current_has_perm(d, SECCLASS_HVM, perm);
}
static int cf_check flask_hvm_param_altp2mhvm(struct domain *d)
{
return current_has_perm(d, SECCLASS_HVM, HVM__ALTP2MHVM);
}
#endif /* CONFIG_HVM */
#ifdef CONFIG_ALTP2M
static int cf_check flask_hvm_altp2mhvm_op(struct domain *d, uint64_t mode, uint32_t op)
{
/*
* Require both mode and XSM to allow the operation. Assume XSM rules
* are written with the XSM_TARGET policy in mind, so add restrictions
* on the domain acting on itself when forbidden by the mode.
*/
switch ( mode )
{
case XEN_ALTP2M_mixed:
break;
case XEN_ALTP2M_limited:
if ( HVMOP_altp2m_vcpu_enable_notify == op )
break;
/* fall-through */
case XEN_ALTP2M_external:
if ( d == current->domain )
return -EPERM;
break;
};
return current_has_perm(d, SECCLASS_HVM, HVM__ALTP2MHVM_OP);
}
#endif /* CONFIG_ALTP2M */
#ifdef CONFIG_VM_EVENT
static int cf_check flask_mem_access(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__MEM_ACCESS);
}
#endif
#ifdef CONFIG_MEM_PAGING
static int cf_check flask_mem_paging(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__MEM_PAGING);
}
#endif
#ifdef CONFIG_MEM_SHARING
static int cf_check flask_mem_sharing(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__MEM_SHARING);
}
#endif
#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
static int cf_check flask_get_device_group(uint32_t machine_bdf)
{
uint32_t rsid;
int rc = -EPERM;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__STAT_DEVICE, NULL);
}
static int flask_test_assign_device(uint32_t machine_bdf)
{
uint32_t rsid;
int rc = -EPERM;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__STAT_DEVICE, NULL);
}
static int flask_assign_device(struct domain *d, uint32_t machine_bdf)
{
uint32_t dsid, rsid;
int rc = -EPERM;
struct avc_audit_data ad;
uint32_t dperm;
if ( !d )
return flask_test_assign_device(machine_bdf);
dperm = flask_iommu_resource_use_perm(d);
rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
if ( rc )
return rc;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
AVC_AUDIT_DATA_INIT(&ad, DEV);
ad.device = (unsigned long) machine_bdf;
rc = avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__ADD_DEVICE, &ad);
if ( rc )
return rc;
dsid = domain_sid(d);
return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad);
}
static int flask_deassign_device(
struct domain *d, uint32_t machine_bdf)
{
uint32_t rsid;
int rc = -EPERM;
rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__REMOVE);
if ( rc )
return rc;
rc = security_device_sid(machine_bdf, &rsid);
if ( rc )
return rc;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__REMOVE_DEVICE, NULL);
}
#endif /* HAS_PASSTHROUGH && HAS_PCI */
#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY)
static int flask_test_assign_dtdevice(const char *dtpath)
{
uint32_t rsid;
int rc = -EPERM;
rc = security_devicetree_sid(dtpath, &rsid);
if ( rc )
return rc;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__STAT_DEVICE,
NULL);
}
static int flask_assign_dtdevice(struct domain *d, const char *dtpath)
{
uint32_t dsid, rsid;
int rc = -EPERM;
struct avc_audit_data ad;
uint32_t dperm;
if ( !d )
return flask_test_assign_dtdevice(dtpath);
dperm = flask_iommu_resource_use_perm(d);
rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
if ( rc )
return rc;
rc = security_devicetree_sid(dtpath, &rsid);
if ( rc )
return rc;
AVC_AUDIT_DATA_INIT(&ad, DTDEV);
ad.dtdev = dtpath;
rc = avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__ADD_DEVICE, &ad);
if ( rc )
return rc;
dsid = domain_sid(d);
return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad);
}
static int flask_deassign_dtdevice(
struct domain *d, const char *dtpath)
{
uint32_t rsid;
int rc = -EPERM;
rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__REMOVE);
if ( rc )
return rc;
rc = security_devicetree_sid(dtpath, &rsid);
if ( rc )
return rc;
return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__REMOVE_DEVICE,
NULL);
}
#endif /* HAS_PASSTHROUGH && HAS_DEVICE_TREE_DISCOVERY */
static int cf_check flask_platform_op(uint32_t op)
{
switch ( op )
{
#ifdef CONFIG_X86
case XENPF_cpu_online:
case XENPF_cpu_hotadd:
case XENPF_mem_hotadd:
return flask_resource_plug_core();
case XENPF_cpu_offline:
return flask_resource_unplug_core();
#endif
case XENPF_settime32:
case XENPF_settime64:
return domain_has_xen(current->domain, XEN__SETTIME);
case XENPF_add_memtype:
return domain_has_xen(current->domain, XEN__MTRR_ADD);
case XENPF_del_memtype:
return domain_has_xen(current->domain, XEN__MTRR_DEL);
case XENPF_read_memtype:
return domain_has_xen(current->domain, XEN__MTRR_READ);
case XENPF_microcode_update:
return domain_has_xen(current->domain, XEN__MICROCODE);
case XENPF_platform_quirk:
return domain_has_xen(current->domain, XEN__QUIRK);
case XENPF_firmware_info:
return domain_has_xen(current->domain, XEN__FIRMWARE);
case XENPF_efi_runtime_call:
return domain_has_xen(current->domain, XEN__FIRMWARE);
case XENPF_enter_acpi_sleep:
return domain_has_xen(current->domain, XEN__SLEEP);
case XENPF_change_freq:
return domain_has_xen(current->domain, XEN__FREQUENCY);
case XENPF_getidletime:
return domain_has_xen(current->domain, XEN__GETIDLE);
case XENPF_set_processor_pminfo:
case XENPF_core_parking:
return domain_has_xen(current->domain, XEN__PM_OP);
case XENPF_get_cpu_version:
case XENPF_get_cpuinfo:
return domain_has_xen(current->domain, XEN__GETCPUINFO);
case XENPF_resource_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__RESOURCE_OP, NULL);
case XENPF_get_symbol:
return avc_has_perm(domain_sid(current->domain), SECINITSID_XEN,
SECCLASS_XEN2, XEN2__GET_SYMBOL, NULL);
case XENPF_get_dom0_console:
return avc_has_perm(domain_sid(current->domain), SECINITSID_XEN,
SECCLASS_XEN2, XEN2__GET_DOM0_CONSOLE, NULL);
default:
return avc_unknown_permission("platform_op", op);
}
}
#ifdef CONFIG_X86
static int cf_check flask_do_mca(void)
{
return domain_has_xen(current->domain, XEN__MCA_OP);
}
static int flask_shadow_control(struct domain *d, unsigned int op)
{
uint32_t perm;
switch ( op )
{
case XEN_DOMCTL_SHADOW_OP_OFF:
perm = SHADOW__DISABLE;
break;
case XEN_DOMCTL_SHADOW_OP_ENABLE:
case XEN_DOMCTL_SHADOW_OP_ENABLE_TEST:
case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION:
case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION:
perm = SHADOW__ENABLE;
break;
case XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY:
case XEN_DOMCTL_SHADOW_OP_PEEK:
case XEN_DOMCTL_SHADOW_OP_CLEAN:
perm = SHADOW__LOGDIRTY;
break;
default:
return avc_unknown_permission("shadow_control", op);
}
return current_has_perm(d, SECCLASS_SHADOW, perm);
}
struct ioport_has_perm_data {
uint32_t ssid;
uint32_t dsid;
uint32_t perm;
uint32_t use_perm;
};
static int cf_check _ioport_has_perm(
void *v, uint32_t sid, unsigned long start, unsigned long end)
{
struct ioport_has_perm_data *data = v;
struct avc_audit_data ad;
int rc;
AVC_AUDIT_DATA_INIT(&ad, RANGE);
ad.range.start = start;
ad.range.end = end;
rc = avc_has_perm(data->ssid, sid, SECCLASS_RESOURCE, data->perm, &ad);
if ( rc )
return rc;
return avc_has_perm(data->dsid, sid, SECCLASS_RESOURCE, data->use_perm, &ad);
}
static int cf_check flask_ioport_permission(
struct domain *d, uint32_t start, uint32_t end, uint8_t access)
{
int rc;
struct ioport_has_perm_data data;
rc = current_has_perm(d, SECCLASS_RESOURCE,
resource_to_perm(access));
if ( rc )
return rc;
if ( access )
data.perm = RESOURCE__ADD_IOPORT;
else
data.perm = RESOURCE__REMOVE_IOPORT;
data.ssid = domain_sid(current->domain);
data.dsid = domain_sid(d);
data.use_perm = flask_iommu_resource_use_perm(d);
return security_iterate_ioport_sids(start, end, _ioport_has_perm, &data);
}
static int cf_check flask_ioport_mapping(
struct domain *d, uint32_t start, uint32_t end, uint8_t access)
{
return flask_ioport_permission(d, start, end, access);
}
#ifdef CONFIG_MEM_SHARING
static int cf_check flask_mem_sharing_op(
struct domain *d, struct domain *cd, int op)
{
int rc = current_has_perm(cd, SECCLASS_HVM, HVM__MEM_SHARING);
if ( rc )
return rc;
return domain_has_perm(d, cd, SECCLASS_HVM, HVM__SHARE_MEM);
}
#endif
static int cf_check flask_apic(struct domain *d, int cmd)
{
uint32_t perm;
switch ( cmd )
{
case PHYSDEVOP_apic_read:
case PHYSDEVOP_alloc_irq_vector:
perm = XEN__READAPIC;
break;
case PHYSDEVOP_apic_write:
perm = XEN__WRITEAPIC;
break;
default:
return avc_unknown_permission("apic", cmd);
}
return domain_has_xen(d, perm);
}
static int cf_check flask_machine_memory_map(void)
{
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_MMU, MMU__MEMORYMAP, NULL);
}
static int cf_check flask_domain_memory_map(struct domain *d)
{
return current_has_perm(d, SECCLASS_MMU, MMU__MEMORYMAP);
}
static int cf_check flask_mmu_update(
struct domain *d, struct domain *t, struct domain *f, uint32_t flags)
{
int rc = 0;
uint32_t map_perms = 0;
if ( t && d != t )
rc = domain_has_perm(d, t, SECCLASS_MMU, MMU__REMOTE_REMAP);
if ( rc )
return rc;
if ( flags & XSM_MMU_UPDATE_READ )
map_perms |= MMU__MAP_READ;
if ( flags & XSM_MMU_UPDATE_WRITE )
map_perms |= MMU__MAP_WRITE;
if ( flags & XSM_MMU_MACHPHYS_UPDATE )
map_perms |= MMU__UPDATEMP;
if ( map_perms )
rc = domain_has_perm(d, f, SECCLASS_MMU, map_perms);
return rc;
}
static int cf_check flask_mmuext_op(struct domain *d, struct domain *f)
{
return domain_has_perm(d, f, SECCLASS_MMU, MMU__MMUEXT_OP);
}
static int cf_check flask_update_va_mapping(
struct domain *d, struct domain *f, l1_pgentry_t pte)
{
uint32_t map_perms = MMU__MAP_READ;
if ( !(l1e_get_flags(pte) & _PAGE_PRESENT) )
return 0;
if ( l1e_get_flags(pte) & _PAGE_RW )
map_perms |= MMU__MAP_WRITE;
return domain_has_perm(d, f, SECCLASS_MMU, map_perms);
}
static int cf_check flask_priv_mapping(struct domain *d, struct domain *t)
{
return domain_has_perm(d, t, SECCLASS_MMU, MMU__TARGET_HACK);
}
static int cf_check flask_pmu_op(struct domain *d, unsigned int op)
{
uint32_t dsid = domain_sid(d);
switch ( op )
{
case XENPMU_mode_set:
case XENPMU_mode_get:
case XENPMU_feature_set:
case XENPMU_feature_get:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PMU_CTRL, NULL);
case XENPMU_init:
case XENPMU_finish:
case XENPMU_lvtpc_set:
case XENPMU_flush:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PMU_USE, NULL);
default:
return -EPERM;
}
}
#endif /* CONFIG_X86 */
static int cf_check flask_dm_op(struct domain *d)
{
return current_has_perm(d, SECCLASS_HVM, HVM__DM);
}
static int cf_check flask_xen_version(uint32_t op)
{
uint32_t dsid = domain_sid(current->domain);
switch ( op )
{
case XENVER_version:
case XENVER_platform_parameters:
case XENVER_get_features:
/* These sub-ops ignore the permission checks and return data. */
return 0;
case XENVER_extraversion:
case XENVER_extraversion2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_EXTRAVERSION, NULL);
case XENVER_compile_info:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_COMPILE_INFO, NULL);
case XENVER_capabilities:
case XENVER_capabilities2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_CAPABILITIES, NULL);
case XENVER_changeset:
case XENVER_changeset2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_CHANGESET, NULL);
case XENVER_pagesize:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_PAGESIZE, NULL);
case XENVER_guest_handle:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_GUEST_HANDLE, NULL);
case XENVER_commandline:
case XENVER_commandline2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_COMMANDLINE, NULL);
case XENVER_build_id:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_BUILD_ID, NULL);
default:
return -EPERM;
}
}
static int cf_check flask_domain_resource_map(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__RESOURCE_MAP);
}
#ifdef CONFIG_ARGO
static int cf_check flask_argo_enable(const struct domain *d)
{
return avc_has_perm(domain_sid(d), SECINITSID_XEN, SECCLASS_ARGO,
ARGO__ENABLE, NULL);
}
static int cf_check flask_argo_register_single_source(
const struct domain *d, const struct domain *t)
{
return domain_has_perm(d, t, SECCLASS_ARGO,
ARGO__REGISTER_SINGLE_SOURCE);
}
static int cf_check flask_argo_register_any_source(const struct domain *d)
{
return avc_has_perm(domain_sid(d), SECINITSID_XEN, SECCLASS_ARGO,
ARGO__REGISTER_ANY_SOURCE, NULL);
}
static int cf_check flask_argo_send(
const struct domain *d, const struct domain *t)
{
return domain_has_perm(d, t, SECCLASS_ARGO, ARGO__SEND);
}
#endif
static int cf_check flask_get_domain_state(struct domain *d)
{
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__GET_DOMAIN_STATE);
}
static const struct xsm_ops __initconst_cf_clobber flask_ops = {
.set_system_active = flask_set_system_active,
.security_domaininfo = flask_security_domaininfo,
#define XSM_HOOK0(rtype, name) .name = flask_ ## name,
#define XSM_HOOK1(rtype, name, ...) XSM_HOOK0(rtype, name)
#define XSM_HOOK2(rtype, name, ...) XSM_HOOK0(rtype, name)
#define XSM_HOOK3(rtype, name, ...) XSM_HOOK0(rtype, name)
#define XSM_HOOK4(rtype, name, ...) XSM_HOOK0(rtype, name)
#define XSM_HOOK5(rtype, name, ...) XSM_HOOK0(rtype, name)
#include <xsm/hooks.h>
.evtchn_close_post = flask_evtchn_close_post,
.alloc_security_domain = flask_domain_alloc_security,
.free_security_domain = flask_domain_free_security,
.alloc_security_evtchns = flask_alloc_security_evtchns,
.free_security_evtchns = flask_free_security_evtchns,
.show_security_evtchn = flask_show_security_evtchn,
.show_irq_sid = flask_show_irq_sid,
.do_xsm_op = do_flask_op,
#ifdef CONFIG_COMPAT
.do_compat_op = compat_flask_op,
#endif
#ifdef CONFIG_ARGO
.argo_enable = flask_argo_enable,
.argo_register_single_source = flask_argo_register_single_source,
.argo_register_any_source = flask_argo_register_any_source,
.argo_send = flask_argo_send,
#endif
};
const struct xsm_ops *__init flask_init(
const void *policy_buffer, size_t policy_size)
{
int ret = -ENOENT;
switch ( flask_bootparam )
{
case FLASK_BOOTPARAM_DISABLED:
printk(XENLOG_INFO "Flask: Disabled at boot.\n");
return NULL;
case FLASK_BOOTPARAM_PERMISSIVE:
flask_enforcing = 0;
break;
case FLASK_BOOTPARAM_ENFORCING:
case FLASK_BOOTPARAM_LATELOAD:
break;
case FLASK_BOOTPARAM_INVALID:
default:
panic("Flask: Invalid value for flask= boot parameter.\n");
break;
}
avc_init();
if ( policy_size && flask_bootparam != FLASK_BOOTPARAM_LATELOAD )
ret = security_load_policy(policy_buffer, policy_size);
if ( ret && flask_bootparam == FLASK_BOOTPARAM_ENFORCING )
panic("Unable to load FLASK policy\n");
if ( ret )
printk(XENLOG_INFO "Flask: Access controls disabled until policy is loaded.\n");
else if ( flask_enforcing )
printk(XENLOG_INFO "Flask: Starting in enforcing mode.\n");
else
printk(XENLOG_INFO "Flask: Starting in permissive mode.\n");
return &flask_ops;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/