Stephen Smalley
2011-Mar-22 15:11 UTC
[Xen-devel] [RFC][PATCH] xen/xsm/flask: Policy based setting of is_privileged
This is an RFC on one approach to supporting more than one "privileged" domain in Xen in order to enable decomposition of dom0. This approach allows the definition of privilege to be assigned through policy rather than hardcoded. The expectation is that one would then use the other policy-based controls to restrict precisely what can be done by the privileged domain. This will likely require expanding the set of XSM hooks to ensure full coverage of all privileged operations. I know that others have implemented similar extensions or hacks to IS_PRIV and/or IS_PRIV_FOR in order to provide similar functionality, so hopefully this RFC will bring out all relevant parties and open up a dialogue on how to do this in a uniform way. This patch provides a simple policy-based mechanism for setting the privilege state of a domain so that we can allow domains other than dom0 to invoke privileged hypercalls. The implementation approach in this patch avoids any changes outside of the flask code. Upon domain creation, within the existing xsm hook, we check a new ''ispriv'' permission on the domain ssid and if allowed by the policy, we set the is_privileged flag for the domain. An alternative implementation would be to replace the IS_PRIV() definition with an XSM hook call similar to what we do for capable() in Linux, but that would require a change to core xen and would turn IS_PRIV() from a bit test into a function call, adding overhead on each check. To avoid granting privilege to all domains when in permissive mode, the ''ispriv'' check is performed with the AVC_STRICT flag (ported from SELinux) to ignore permissive mode. We also use the _noaudit interface since the check is applied on every domain creation rather than when there is an actual attempt to use privilege. The class validation logic in the policy loader was changed to be more forgiving of unknown classes/permissions so that if you boot with an older policy that does not define the new permission, it will still load the policy and just deny the unknown class/perms. --- tools/flask/policy/policy/flask/Makefile | 4 ++-- tools/flask/policy/policy/flask/access_vectors | 1 + tools/flask/policy/policy/modules/xen/xen.if | 1 + xen/xsm/flask/avc.c | 7 +++++-- xen/xsm/flask/hooks.c | 4 ++++ xen/xsm/flask/include/av_perm_to_string.h | 1 + xen/xsm/flask/include/av_permissions.h | 1 + xen/xsm/flask/include/avc.h | 4 +++- xen/xsm/flask/ss/services.c | 6 +++--- 9 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tools/flask/policy/policy/flask/Makefile b/tools/flask/policy/policy/flask/Makefile --- a/tools/flask/policy/policy/flask/Makefile +++ b/tools/flask/policy/policy/flask/Makefile @@ -2,7 +2,7 @@ LIBSEL ?= ../../libselinux # flask needs to know where to export the kernel headers. -LINUXDIR ?= ../../../linux-2.6 +XENDIR ?= ../../../../.. AWK = awk @@ -30,7 +30,7 @@ install -m 644 class_to_string.h av_inherit.h common_perm_to_string.h av_perm_to_string.h $(LIBSEL)/src tokern: all - install -m 644 $(ALL_H_FILES) $(LINUXDIR)/security/selinux/include + install -m 644 $(ALL_H_FILES) $(XENDIR)/xen/xsm/flask/include install: all diff --git a/tools/flask/policy/policy/flask/access_vectors b/tools/flask/policy/policy/flask/access_vectors --- a/tools/flask/policy/policy/flask/access_vectors +++ b/tools/flask/policy/policy/flask/access_vectors @@ -77,6 +77,7 @@ setextvcpucontext getvcpuextstate setvcpuextstate + ispriv } class hvm diff --git a/tools/flask/policy/policy/modules/xen/xen.if b/tools/flask/policy/policy/modules/xen/xen.if --- a/tools/flask/policy/policy/modules/xen/xen.if +++ b/tools/flask/policy/policy/modules/xen/xen.if @@ -5,6 +5,7 @@ ################################################################################ define(`create_domain'', ` type $2, domain_type; + allow $1 self:domain ispriv; allow $1 $2:domain {create max_vcpus setdomainmaxmem setaddrsize getdomaininfo hypercall setvcpucontext scheduler unpause diff --git a/xen/xsm/flask/avc.c b/xen/xsm/flask/avc.c --- a/xen/xsm/flask/avc.c +++ b/xen/xsm/flask/avc.c @@ -773,6 +773,7 @@ * should be released for the auditing. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested, + unsigned flags, struct av_decision *in_avd) { struct avc_node *node; @@ -809,7 +810,9 @@ if ( denied ) { - if ( !flask_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE) ) + if ( flags & AVC_STRICT ) + rc = -EACCES; + else if ( !flask_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE) ) avc_update_node(AVC_CALLBACK_GRANT,requested, ssid,tsid,tclass,avd->seqno); else @@ -843,7 +846,7 @@ struct av_decision avd; int rc; - rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd); + rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); return rc; } diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -507,6 +507,10 @@ dsec1->create_sid = SECSID_NULL; dsec2->create_sid = SECSID_NULL; + if (avc_has_perm_noaudit(dsec2->sid, dsec2->sid, SECCLASS_DOMAIN, + DOMAIN__ISPRIV, AVC_STRICT, NULL) == 0) + d->is_privileged = 1; + return rc; } diff --git a/xen/xsm/flask/include/av_perm_to_string.h b/xen/xsm/flask/include/av_perm_to_string.h --- a/xen/xsm/flask/include/av_perm_to_string.h +++ b/xen/xsm/flask/include/av_perm_to_string.h @@ -52,6 +52,7 @@ S_(SECCLASS_DOMAIN, DOMAIN__SETEXTVCPUCONTEXT, "setextvcpucontext") S_(SECCLASS_DOMAIN, DOMAIN__GETVCPUEXTSTATE, "getvcpuextstate") S_(SECCLASS_DOMAIN, DOMAIN__SETVCPUEXTSTATE, "setvcpuextstate") + S_(SECCLASS_DOMAIN, DOMAIN__ISPRIV, "ispriv") S_(SECCLASS_HVM, HVM__SETHVMC, "sethvmc") S_(SECCLASS_HVM, HVM__GETHVMC, "gethvmc") S_(SECCLASS_HVM, HVM__SETPARAM, "setparam") diff --git a/xen/xsm/flask/include/av_permissions.h b/xen/xsm/flask/include/av_permissions.h --- a/xen/xsm/flask/include/av_permissions.h +++ b/xen/xsm/flask/include/av_permissions.h @@ -53,6 +53,7 @@ #define DOMAIN__SETEXTVCPUCONTEXT 0x02000000UL #define DOMAIN__GETVCPUEXTSTATE 0x04000000UL #define DOMAIN__SETVCPUEXTSTATE 0x08000000UL +#define DOMAIN__ISPRIV 0x10000000UL #define HVM__SETHVMC 0x00000001UL #define HVM__GETHVMC 0x00000002UL diff --git a/xen/xsm/flask/include/avc.h b/xen/xsm/flask/include/avc.h --- a/xen/xsm/flask/include/avc.h +++ b/xen/xsm/flask/include/avc.h @@ -73,8 +73,10 @@ void avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct av_decision *avd, int result, struct avc_audit_data *auditdata); +#define AVC_STRICT 1 /* Ignore permissive mode. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested, - struct av_decision *avd); + unsigned flags, + struct av_decision *avd); int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct avc_audit_data *auditdata); diff --git a/xen/xsm/flask/ss/services.c b/xen/xsm/flask/ss/services.c --- a/xen/xsm/flask/ss/services.c +++ b/xen/xsm/flask/ss/services.c @@ -1186,7 +1186,7 @@ printk(KERN_INFO "Flask: class %s not defined in policy\n", def_class); - return -EINVAL; + continue; } pol_class = p->p_class_val_to_name[i-1]; if ( strcmp(pol_class, def_class) ) @@ -1214,7 +1214,7 @@ printk(KERN_INFO "Flask: permission %s in class %s not defined in policy\n", def_perm, pol_class); - return -EINVAL; + continue; } perdatum = hashtab_search(perms->table, def_perm); if ( perdatum == NULL ) @@ -1264,7 +1264,7 @@ printk(KERN_INFO "Flask: permission %s in class %s not defined in policy\n", def_perm, pol_class); - return -EINVAL; + continue; } perdatum = hashtab_search(perms->table, def_perm); if ( perdatum == NULL ) -- Stephen Smalley National Security Agency _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel