Xen Security Modules : XSM-FLASK: Difference between revisions
(Update Xen command line options for flask) |
|||
(15 intermediate revisions by 3 users not shown) | |||
Line 16: | Line 16: | ||
Via XSM, Xen implements a type of mandatory access control via a security architecture called FLASK using a module of the same name. An abbreviation for '''Fl'''ux '''A'''dvanced '''S'''ecurity '''K'''ernel, FLASK has its origins in several trusted operating system research projects, managed jointly between the United States’ National Security Agency, the Secure Computing Corporation, and the University of Utah. The specific intent of FLASK is to separate security enforcement from security policy, better isolating logical components of security systems. |
Via XSM, Xen implements a type of mandatory access control via a security architecture called FLASK using a module of the same name. An abbreviation for '''Fl'''ux '''A'''dvanced '''S'''ecurity '''K'''ernel, FLASK has its origins in several trusted operating system research projects, managed jointly between the United States’ National Security Agency, the Secure Computing Corporation, and the University of Utah. The specific intent of FLASK is to separate security enforcement from security policy, better isolating logical components of security systems. |
||
As applied to operating system design, FLASK brings about security enhancements that improve auditing of actions and more granular control thereof. Today, it is perhaps best known through its expression in Security Enhanced Linux (SELinux). |
As applied to operating system design, FLASK brings about security enhancements that improve auditing of actions and more granular control thereof. Today, it is perhaps best known through its expression in [http://selinuxproject.org/ Security Enhanced Linux (SELinux)]. |
||
In the same sense that SELinux is a collection of Linux security enhancements, auditing and policy mechanisms, so also is the FLASK Xen Security Module to Xen itself. Similarly, each rely upon a set of administration tools to craft security policies and guide their enforcement. |
In the same sense that SELinux is a collection of Linux security enhancements, auditing and policy mechanisms, so also is the FLASK Xen Security Module to Xen itself. Similarly, each rely upon a set of administration tools to craft security policies and guide their enforcement. |
||
Line 63: | Line 63: | ||
* make -C tools/flask/policy |
* make -C tools/flask/policy |
||
The demonstration policy is generated from statements defined in files within the tools/flask/policy path.. After compilation, the resultant policy will be named xenpolicy.24 and reside within the directory tools/flask/policy. |
The demonstration policy is generated from statements defined in files within the tools/flask/policy path.. After compilation, the resultant policy will be named xenpolicy-$XEN_FULLVERSION (before 4.5 release, it's named xenpolicy.24) and reside within the directory tools/flask/policy. |
||
Quoting from its description, this policy illustrates "most of the features of FLASK that can be used without dom0 disaggregation. The main types for domUs are: |
Quoting from its description, this policy illustrates "most of the features of FLASK that can be used without dom0 disaggregation. The main types for domUs are: |
||
Line 69: | Line 69: | ||
- isolated_domU_t can only communicate with dom0 |
- isolated_domU_t can only communicate with dom0 |
||
- prot_domU_t is a domain type whose creation can be disabled with a boolean |
- prot_domU_t is a domain type whose creation can be disabled with a boolean |
||
- nomigrate_t is a domain that must be created via the nomigrate_t_building type, |
- nomigrate_t is a domain that must be created via the nomigrate_t_building type, |
||
and whose memory cannot be read by dom0 once created |
|||
HVM domains with stubdomain device models use two types (one per domain): |
HVM domains with stubdomain device models use two types (one per domain): |
||
Line 79: | Line 80: | ||
When Xen is compiled with XSM, the following grub command-line options can be used to set FLASK properties. Please consult distribution documentation for the recommended method to update the grub configuration. Using Debian and Ubuntu as examples, these options can be placed in /etc/default/grub within the variable GRUB_CMDLINE_XEN_DEFAULT. |
When Xen is compiled with XSM, the following grub command-line options can be used to set FLASK properties. Please consult distribution documentation for the recommended method to update the grub configuration. Using Debian and Ubuntu as examples, these options can be placed in /etc/default/grub within the variable GRUB_CMDLINE_XEN_DEFAULT. |
||
Specifically: |
Specifically: |
||
* GRUB_CMDLINE_XEN_DEFAULT=" |
* GRUB_CMDLINE_XEN_DEFAULT="flask=<OPTION>” |
||
When package management scripts regenerate the grub configuration (e.g., during a kernel upgrade or at the request of a sysadmin), a system configured in this manner will be guaranteed to properly embed XSM parameters within the grub configuration. For testing purposes, manually editing the grub command line at boot will also suffice. |
When package management scripts regenerate the grub configuration (e.g., during a kernel upgrade or at the request of a sysadmin), a system configured in this manner will be guaranteed to properly embed XSM parameters within the grub configuration. For testing purposes, manually editing the grub command line at boot will also suffice. |
||
The options for "flask=" are: |
|||
XSM-specific command line parameters: |
|||
* flask_enforcing |
|||
* `permissive`: This is intended for development and is not suitable for use with untrusted guests. If a policy is provided by the bootloader, it will be loaded; errors will be reported to the ring buffer but will not prevent booting. The policy can be changed to enforcing mode using "xl setenforce". |
|||
** The default value for flask_enforcing is '0'. This parameter causes the platform to boot in permissive mode which means that the policy is loaded but not enforced. This mode is often helpful for developing new systems and policies as the policy violations are reported on the xen console and may be viewed in dom0 through 'xl dmesg'. |
|||
* `enforcing`: This requires a security policy to be provided by the bootloader and will enter enforcing mode prior to the creation of domain 0. If a valid policy is not provided, the hypervisor will not continue booting. |
|||
**To boot the platform into enforcing mode, which means that the policy is loaded and enforced, append 'flask_enforcing=1' on the grub line. |
|||
* `late`: This disables loading of the security policy from the bootloader. FLASK will be enabled but will not enforce access controls until a policy is loaded by a domain using "xl loadpolicy". Once a policy is loaded, FLASK will run in enforcing mode unless "xl setenforce" has changed that setting. |
|||
** '''NOTE''': This parameter may also be changed through the flask hypercall. |
|||
* `disabled`: This causes the XSM framework to revert to the dummy module. The dummy module provides the same security policy as is used when compiling the hypervisor without support for XSM. The xsm_op hypercall can also be used to switch to this mode after boot, but there is no way to re-enable FLASK once the dummy module is loaded. |
|||
** '''NOTE''': This parameter may also be changed through the ‘xl setenforce’ command |
|||
* flask_enabled |
|||
** The default value for flask_enabled is '1'. This parameter causes the platform to enable the FLASK security module under the XSM framework. The parameter may be enabled/disabled only once per boot. If the parameter is set to '0', only a reboot can re-enable flask. When flask_enabled is '0' the DUMMY module is enforced; when this module name is encountered in XSM-related documentation and mailing list discussions, it should be interpreted as a “pass through” default XSM policy which matches behavior of a system without XSM enabled at compile-time. |
|||
** This parameter may also be changed through the flask hypercall. |
|||
** This parameter may also be queried through the ‘xl getenforce’ command |
|||
** This parameter may be altered only once per boot. |
|||
The compiled policy must be installed into a grub-accessible location (such as /boot where kernels are also stored), as it is loaded during the Xen boot process. Otherwise, FLASK will not be enabled at runtime. It is recommended by the authors of XSM to add an extra subdirectory, such as /boot/flask, to use as a repository for XSM policies. |
The compiled policy must be installed into a grub-accessible location (such as /boot where kernels are also stored), as it is loaded during the Xen boot process. Otherwise, FLASK will not be enabled at runtime. It is recommended by the authors of XSM to add an extra subdirectory, such as /boot/flask, to use as a repository for XSM policies. |
||
The policy has to be added to grub as a module after the Xen kernel multiboot line, for example as: |
The policy has to be added to grub as a module after the Xen kernel multiboot line, for example as: |
||
multiboot /boot/ |
multiboot /boot/xen-VERSION.gz dom0_mem=1024M,max:1024M flask=enforcing |
||
module /boot/vmlinuz- |
module /boot/vmlinuz-X.Y-amd64 root=/dev/mapper/vg_system-root ro quiet |
||
module /boot/initrd.img- |
module /boot/initrd.img-X.Y |
||
module /boot/flask/xenpolicy |
module /boot/flask/xenpolicy-VERSION |
||
'''NOTE''': it is critical that the ordering of the modules above be retained, as Xen expects implicitly that the kernel is the first referenced module. |
'''NOTE''': it is critical that the ordering of the modules above be retained, as Xen expects implicitly that the kernel is the first referenced module. For Xen 4.4 and below, the policy file is always xenpolicy.24. |
||
To automatically generate this line when running grub-update, the Xen grub update helper script can be modified (e.g., in Ubuntu/Debian by copying /etc/grub.d/20_linux_xen to 21_linux_xsm and adding the "module" line for the policy.) This helper script will differ greatly depending on installed grub version (and linux distribution). |
To automatically generate this line when running grub-update, the Xen grub update helper script can be modified (e.g., in Ubuntu/Debian by copying /etc/grub.d/20_linux_xen to 21_linux_xsm and adding the "module" line for the policy.) This helper script will differ greatly depending on installed grub version (and linux distribution). |
||
Line 111: | Line 107: | ||
=Creating domains with XSM security labels= |
=Creating domains with XSM security labels= |
||
==DomU Configuration== |
|||
Once FLASK is loaded, domains need to be created using a security label in their configuration. Without this line, the domains will be classified “unlabeled”. |
|||
Once FLASK is loaded, domains need to be created using a security label in their configuration. Without this line, the domains will be classified “unlabeled”. |
|||
Example line from a domU configuration: |
Example line from a domU configuration: |
||
seclabel='system_u:system_r:domU_t' |
seclabel='system_u:system_r:domU_t' |
||
The label’s three sections are: user, role and type. These will be discussed further in this document. |
The security label’s three sections are: user, role and type (denoted by convention via _u, _r, _t suffixes, though strictly speaking, such suffixes these are not required). These will be discussed further in this document. Each component must be defined within the loaded policy to ensure proper function. |
||
'''NOTE''': an unlabeled domU may or may not cause a problem. If Flask is enabled, but not enforcing, the unlabelled domain is likely to work without interruption. If Flask is enabled and enforcing via a policy that explicitly permits an unlabelled domain to perform some operations, the same is also true. However, it is similarly likely that such a domain will be prevented from performing some operations (which can be problematic if the code invoking the hypercall does not tolerate failure). |
|||
Monitoring XSM logs |
|||
==Listing domUs and applied security labels== |
|||
XSM:Flask will emit avc: denied messages when a permission is denied by the |
|||
Both xl and xm utilities possess the ability to display running virtual machines with their assigned security labels. |
|||
*For xl, use : xl list -Z |
|||
*For xm, use : xm list --label |
|||
Example output: |
|||
policy, just like SELinux, and can be viewed with xl dmesg: |
|||
steve@xen1:~$ sudo xl list -Z |
|||
Name ID Mem VCPUs State Time(s) Security Label |
|||
Domain-0 0 1023 24 r----- 745223.8 system_u:system_r:dom0_t |
|||
windows-7-sp1-x86 185 1024 1 -b---- 29089.3 tamas:vm_r:domU_t |
|||
windows-xp-sp2 193 128 1 -b---- 51247.2 steve:vm_r:domU_t |
|||
centos-6-x86_64 330 2051 1 -b---- 1821.8 system_u:system_r:unlabeled_t |
|||
centos-6-i386 733 259 1 -b---- 448.8 system_u:system_r:unlabeled_t |
|||
==Monitoring XSM logs== |
|||
XSM:Flask will emit log events "avc: denied" when a permission is denied by the policy, just like SELinux (avc being an acronym for Access Vector Cache). These log events can be viewed with xl dmesg: |
|||
xl dmesg | grep avc |
xl dmesg | grep avc |
||
Example output for a denied attempt by a HVM domU to invoke HVMOP_set_param: |
|||
An example output looks like: |
|||
(XEN) avc: denied { setparam } for domid=0 scontext=system_u:system_r:dom0_t |
(XEN) avc: denied { setparam } for domid=0 scontext=system_u:system_r:dom0_t |
||
tcontext=system_u:system_r:domU_t tclass=hvm |
tcontext=system_u:system_r:domU_t tclass=hvm |
||
Within the braces is the operation that was denied. The parameters domid and scontext (source context) reference the domU that requested the operation, and tcontext (target context) describes the VM that would have been operated upon. |
|||
Familiar SELinux utilities like audit2allow can be used as a policy creation aid by processing these denied events: |
|||
xl dmesg | audit2allow |
|||
The output is a list of generated allow rules, which can be added to the policy via the *.te file. |
|||
=FLASK Policies= |
=FLASK Policies= |
||
The sample FLASK policy can be found in tools/flask/policy/policy/modules/xen. The two files found here are <nowiki>xen.te</nowiki>, the policy description, and <nowiki>xen.if</nowiki>, a collection of macros to be used in the policy description. |
The sample FLASK policy can be found in tools/flask/policy/policy/modules/xen. The two files found here are <nowiki>xen.te</nowiki>, the policy description, and <nowiki>xen.if</nowiki>, a collection of macros to be used in the policy description. |
||
==Adding new security modules== |
|||
New security modules can be easily added by editing tools/flask/policy/policy<nowiki>/modules.co</nowiki>nf and adding the new modules name as such: |
|||
<module_name> = on |
|||
The module has to defined in the folder tools/flask/policy/policy/modules/<module_name> with two files: |
|||
<module_name>.te |
|||
<module_name>.if |
|||
When multiple security modules are present the modules can’t contain duplicate role or type declarations. |
|||
The sample policy is recommended to be left turned on as it contains several definitions required to boot Xen. |
|||
When a new policy has been created, compile it as in the earlier section 'Compiling the sample FLASK policy.' Install the policy into its destination directory (e.g., /boot/flask), and modify the bootloader configuration as necessary. |
|||
==Types, roles, users and attributes== |
==Types, roles, users and attributes== |
||
Line 177: | Line 208: | ||
===Policy Users=== |
===Policy Users=== |
||
On the highest level we can define '''users'''. Users are not defined in the .te (like roles and types), rather they are defined in tools/flask/policy/policy/users. This allows us to define a generic set of users across multiple security modules. |
On the highest level we can define '''users'''. Users are not defined in the .te (like roles and types), rather they are defined in tools/flask/policy/policy/users. This allows us to define a generic set of users across multiple security modules. As an example, the sample FLASK policy creates three users: system_u, customer_1 and customer_2. |
||
A user can be assigned multiple roles but by default the system_u user is assigned the system_r role, and the customer_* users are assigned the vm_r role. |
A user can be assigned multiple roles but by default the system_u user is assigned the system_r role, and the customer_* users are assigned the vm_r role. |
||
Line 184: | Line 215: | ||
user <user name> roles { <role1> <role2> }; |
user <user name> roles { <role1> <role2> }; |
||
==Policy Constraints== |
|||
Constraints are defined in the file tools/flask/policy/policy/constraints. Constraints are a safety net to specifically disallow undesired operations that the policy may allow otherwise. The sample Flask policy defines two constraints to prevent event channels and grants between different customers. A constraints syntax is as follows: |
Constraints, or conditional filters, are defined in the file tools/flask/policy/policy/constraints. Constraints are a safety net to specifically disallow undesired operations that the policy may allow otherwise. The sample Flask policy defines two constraints to prevent event channels and grants between different customers. A constraints syntax is as follows: |
||
constrain <security class> { <hypercall> } ( expression ); |
constrain <security class> { <hypercall> } ( expression ); |
||
Line 198: | Line 229: | ||
The constraint defines that the hypercalls belonging to the security class are only allowed when the expression is true. The expression can contain any user, role and type defined in the policy in the following format: |
The constraint defines that the hypercalls belonging to the security class are only allowed when the expression is true. The expression can contain any user, role and type defined in the policy in the following format: |
||
expression : ( expression ) |
|||
| not expression |
|||
| expression and expression |
|||
| expression or expression |
|||
| u1 op u2 |
|||
| r1 role_op r2 |
|||
| t1 op t2 |
|||
| u1 op names |
|||
| u2 op names |
|||
| r1 op names |
|||
| r2 op names |
|||
| t1 op names |
|||
| t2 op names |
|||
<nowiki>#</nowiki> |
|||
<nowiki># op : == | !=</nowiki> |
|||
<nowiki># role_op : == | != | eq | dom | domby | incomp</nowiki> |
|||
<nowiki>#</nowiki> |
|||
<nowiki># names : name | { name_list }</nowiki> |
|||
<nowiki># name_list : name | name_list name</nowiki> |
|||
op : == | != |
|||
===Macros=== |
|||
role_op : == | != | eq | dom | domby | incomp |
|||
names : name | { name_list } |
|||
Macros are used heavily across the sample Flask policy. For each security module a macro file can be defined as a .if file. The sample macro file (tools/flask/policy/policy/modules/xen<nowiki>/xen.if</nowiki>) contains several commonly used macros, for example for declaring a new domain type and it’s associated sub-types and to setup it’s common allow rules. Macros can call other macros. The macro .if file is required to exist for the policy to compile, even if no macros are defined for a module. |
|||
name_list : name | name_list name |
|||
==Macros== |
|||
===Adding new security modules=== |
|||
Macros are used heavily across the sample Flask policy. For each security module a macro file can be defined as a .if file. The sample macro file (tools/flask/policy/policy/modules/xen<nowiki>/xen.if</nowiki>) contains several commonly used macros, for example for declaring a new domain type and it’s associated sub-types and to setup it’s common allow rules. Macros can call other macros. The macro .if file is required to exist for the policy to compile, even if no macros are defined for a module. |
|||
New security modules can be easily added by editing tools/flask/policy/policy<nowiki>/modules.co</nowiki>nf and adding the new modules name as such: |
|||
<module_name> = on |
|||
The module has to defined in the folder tools/flask/policy/policy/modules/<module_name> with two files: |
|||
<module_name>.te |
|||
<module_name>.if |
|||
==Security classes== |
|||
When multiple security modules are present the modules can’t contain duplicate role or type declarations. |
|||
The sample policy is recommended to be left turned on as it contains several definitions required to boot Xen. |
|||
===Security classes=== |
|||
Security classes are defined in xen/xsm/flask/policy/access_vectors and categorize each hypercall into one of the classes. Each class can hold a maximum of 32 members. |
Security classes are defined in xen/xsm/flask/policy/access_vectors and categorize each hypercall into one of the classes. Each class can hold a maximum of 32 members. |
||
class xen |
===class xen=== |
||
Class xen consists of operations dealing with the hypervisor itself. Unless otherwise specified, the source is the domain executing the hypercall, and the target is the xen initial sid (type xen_t). |
Class xen consists of operations dealing with the hypervisor itself. Unless otherwise specified, the source is the domain executing the hypercall, and the target is the xen initial sid (type xen_t). |
||
{| class="zebra sortable fullwidth" |
|||
{| class="zebra" |
|||
|- |
|- |
||
! Description |
|||
! |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
Description |
|||
! |
|||
Hypercall |
|||
|- |
|- |
||
| |
| |
||
Line 480: | Line 492: | ||
|} |
|} |
||
===class domain and class domain2=== |
|||
Classes domain and domain2 consist of operations that a domain performs on another domain or on itself. Unless otherwise specified, the source is the domain executing the hypercall, and the target is the domain being operated upon(which may result in a _self or _target type). Transitions in class domain are used to produce the _self and _target types. |
|||
====class domain==== |
|||
{| class="zebra sortable fullwidth" |
|||
class domain and class domain2: |
|||
!Description |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
Classes domain and domain2 consist of operations that a domain performs on |
|||
another domain or on itself. Unless otherwise specified, the source is the |
|||
domain executing the hypercall, and the target is the domain being operated on |
|||
(which may result in a _self or _target type). Transitions in class domain are used to produce the _self and _target types. |
|||
class domain: |
|||
{| class="zebra" |
|||
|- |
|||
! |
|||
Description |
|||
! |
|||
Hypercall |
|||
|- |
|- |
||
| |
| |
||
Line 545: | Line 539: | ||
| |
| |
||
checked in FLASK_RELABEL_DOMAIN for any relabel operation: |
checked in FLASK_RELABEL_DOMAIN for any relabel operation: |
||
source = the old label of the domain |
|||
source = the old label of the domain |
|||
target = the new label of the domain |
target = the new label of the domain |
||
see also the domain2 relabel{from,to,self} permissions |
see also the domain2 relabel{from,to,self} permissions |
||
| |
| |
||
transition |
transition |
||
Line 649: | Line 639: | ||
| |
| |
||
checked in XEN_DOMCTL_set_target: |
checked in XEN_DOMCTL_set_target: |
||
source = the new device model domain |
source = the new device model domain |
||
target = the new target domain |
target = the new target domain |
||
see also the domain2 make_priv_for and set_as_target checks |
see also the domain2 make_priv_for and set_as_target checks |
||
| |
| |
||
set_target |
set_target |
||
Line 746: | Line 732: | ||
class domain2 |
====class domain2 ==== |
||
Class domain2 is simply a continuation of class domain. |
|||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
{| class="zebra" |
|||
|- |
|- |
||
| |
| |
||
checked in FLASK_RELABEL_DOMAIN with non-DOMID_SELF: |
checked in FLASK_RELABEL_DOMAIN with non-DOMID_SELF: |
||
source = the domain making the hypercall |
source = the domain making the hypercall |
||
target = the old label of the domain being relabeled |
target = the old label of the domain being relabeled |
||
| |
| |
||
relabelfrom |
relabelfrom |
||
Line 765: | Line 749: | ||
| |
| |
||
checked in FLASK_RELABEL_DOMAIN with non-DOMID_SELF: |
checked in FLASK_RELABEL_DOMAIN with non-DOMID_SELF: |
||
source = the domain making the hypercall |
source = the domain making the hypercall |
||
target = the new label of the domain being relabeled |
target = the new label of the domain being relabeled |
||
Line 776: | Line 758: | ||
| |
| |
||
checked in FLASK_RELABEL_DOMAIN, only with DOMID_SELF: |
checked in FLASK_RELABEL_DOMAIN, only with DOMID_SELF: |
||
source = the old label of the domain |
source = the old label of the domain |
||
target = the new label of the domain |
target = the new label of the domain |
||
see also domain__transition |
see also domain__transition |
||
Line 789: | Line 768: | ||
| |
| |
||
checked in XEN_DOMCTL_set_target: |
checked in XEN_DOMCTL_set_target: |
||
source = the domain making the hypercall |
source = the domain making the hypercall |
||
target = the new device model domain |
target = the new device model domain |
||
| |
| |
||
make_priv_for |
make_priv_for |
||
Line 802: | Line 776: | ||
| |
| |
||
checked in XEN_DOMCTL_set_target: |
checked in XEN_DOMCTL_set_target: |
||
source = the domain making the hypercall |
source = the domain making the hypercall |
||
target = the new target domain |
target = the new target domain |
||
| |
| |
||
set_as_target |
set_as_target |
||
Line 841: | Line 812: | ||
class hvm |
===class hvm=== |
||
Similar to class domain, but primarily contains domctls related to HVM domains. |
Similar to class domain, but primarily contains domctls related to HVM domains. |
||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
{| class="zebra" |
|||
|- |
|- |
||
| |
| |
||
Line 914: | Line 885: | ||
| |
| |
||
HVMOP_modified_memory, HVMOP_get_mem_type, HVMOP_set_mem_type, |
HVMOP_modified_memory, HVMOP_get_mem_type, HVMOP_set_mem_type, |
||
HVMOP_set_mem_access, HVMOP_get_mem_access, HVMOP_pagetable_dying, |
HVMOP_set_mem_access, HVMOP_get_mem_access, HVMOP_pagetable_dying, |
||
HVMOP_inject_trap |
HVMOP_inject_trap |
||
| |
| |
||
hvmctl |
hvmctl |
||
Line 932: | Line 900: | ||
| |
| |
||
XEN_DOMCTL_mem_sharing_op and XENMEM_sharing_op_{share,add_physmap} with: |
XEN_DOMCTL_mem_sharing_op and XENMEM_sharing_op_{share,add_physmap} with: |
||
source = the domain making the hypercall |
source = the domain making the hypercall |
||
target = domain whose memory is being shared |
target = domain whose memory is being shared |
||
| |
| |
||
mem_sharing |
mem_sharing |
||
Line 957: | Line 922: | ||
| |
| |
||
checked in XENMEM_sharing_op_{share,add_physmap} with: |
checked in XENMEM_sharing_op_{share,add_physmap} with: |
||
source = domain whose memory is being shared |
source = domain whose memory is being shared |
||
target = client domain |
target = client domain |
||
| |
| |
||
share_mem |
share_mem |
||
Line 968: | Line 930: | ||
class event |
===class event=== |
||
Class event describes event channels. Interdomain event channels have their own security label which is computed using a type transition between the source and target domains. Each endpoint has its own label, and the permission checks must pass on both endpoints for an event channel to be established. |
Class event describes event channels. Interdomain event channels have their own security label which is computed using a type transition between the source and target domains. Each endpoint has its own label, and the permission checks must pass on both endpoints for an event channel to be established. |
||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
{| class="zebra" |
|||
|- |
|- |
||
| |
| |
||
when creating an interdomain event channel endpoint: |
when creating an interdomain event channel endpoint: |
||
source = event channel label |
source = event channel label |
||
target = remote domain the event channel binds to. This may be a |
|||
_self or _target label if the endpoints are related as such. |
|||
This permission is checked when creating an unbound event channel and when the interdomain event channel is established. |
This permission is checked when creating an unbound event channel and when the interdomain event channel is established. |
||
| |
| |
||
bind |
bind |
||
Line 991: | Line 950: | ||
| |
| |
||
EVTCHNOP_send: |
EVTCHNOP_send: |
||
source = domain sending the event |
source = domain sending the event |
||
target = event channel label |
target = event channel label |
||
| |
| |
||
send |
send |
||
Line 1,009: | Line 965: | ||
| |
| |
||
when creating an interdomain event channel endpoint: |
when creating an interdomain event channel endpoint: |
||
source = the domain creating the channel (which might not be an endpoint) |
source = the domain creating the channel (which might not be an endpoint) |
||
target = event channel label |
target = event channel label |
||
| |
| |
||
create |
create |
||
Line 1,020: | Line 973: | ||
| |
| |
||
EVTCHNOP_reset: |
EVTCHNOP_reset: |
||
source = domain making the hypercall |
source = domain making the hypercall |
||
target = domain whose event channels are being reset |
target = domain whose event channels are being reset |
||
| |
| |
||
reset |
reset |
||
Line 1,030: | Line 980: | ||
|} |
|} |
||
===class grant=== |
|||
class grant: |
|||
Class grant describes pages shared by grant mappings. Pages use the security label of their owning domain. |
Class grant describes pages shared by grant mappings. Pages use the security label of their owning domain. |
||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
{| class="zebra" |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
|- |
|- |
||
| |
| |
||
Line 1,092: | Line 1,040: | ||
class mmu |
===class mmu=== |
||
Class mmu describes pages of memory not accessed using grants. Permissions are checked using the domain ID used to access the page - the most common case is a domain's own ID (the _self label). Using DOMID_IO in the map command to restrict the mapping to IO memory will result in the target being domio_t, and migration uses read-only mappings with a target of DOMID_XEN (domxen_t). |
Class mmu describes pages of memory not accessed using grants. Permissions are checked using the domain ID used to access the page - the most common case is a domain's own ID (the _self label). Using DOMID_IO in the map command to restrict the mapping to IO memory will result in the target being domio_t, and migration uses read-only mappings with a target of DOMID_XEN (domxen_t). |
||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
! style="min-width=150px; max-width=250px;"| Hypercall |
|||
{| class="zebra" |
|||
|- |
|- |
||
| |
| |
||
checked when using mmu_update to map a page readably |
checked when using mmu_update to map a page readably |
||
source = domain making the hypercall (which might not own the page table) |
source = domain making the hypercall (which might not own the page table) |
||
target = domain whose pages are being mapped |
target = domain whose pages are being mapped |
||
| |
| |
||
map_read |
map_read |
||
Line 1,115: | Line 1,058: | ||
| |
| |
||
checked when using mmu_update to map a page writably |
checked when using mmu_update to map a page writably |
||
source = domain making the hypercall |
|||
target = domain whose pages are being mapped |
|||
<nowiki># target = domain whose pages are being mapped</nowiki> |
|||
| |
| |
||
map_write |
map_write |
||
Line 1,184: | Line 1,122: | ||
| |
| |
||
checked when using mmu_update to update the page tables of another domain |
checked when using mmu_update to update the page tables of another domain |
||
source = domain making the hypercall |
source = domain making the hypercall |
||
target = domain whose page tables are being modified |
target = domain whose page tables are being modified |
||
| |
| |
||
remote_remap |
remote_remap |
||
Line 1,204: | Line 1,137: | ||
| |
| |
||
XENMEM_exchange: |
XENMEM_exchange: |
||
source = domain making the hypercall |
source = domain making the hypercall |
||
target = domain whose pages are being exchanged |
target = domain whose pages are being exchanged |
||
| |
| |
||
exchange |
exchange |
||
Line 1,217: | Line 1,145: | ||
class shadow |
===class shadow=== |
||
Control of the paging_domctl split by subop |
Control of the paging_domctl split by subop |
||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
{| class="zebra" |
|||
! style="min-width=150px; max-width=250px;"| Operation |
|||
|- |
|||
! |
|||
Description |
|||
! |
|||
Operation |
|||
|- |
|- |
||
| |
| |
||
Line 1,255: | Line 1,177: | ||
class resource |
===class resource=== |
||
Class resource is used to describe the resources used in hardware device passthrough. Resources include: hardware IRQs, MMIO regions, x86 I/O ports, and PCI devices; see docs/misc<nowiki>/xsm-flask.tx</nowiki>t for how to label them. |
Class resource is used to describe the resources used in hardware device passthrough. Resources include: hardware IRQs, MMIO regions, x86 I/O ports, and PCI devices; see docs/misc<nowiki>/xsm-flask.tx</nowiki>t for how to label them. |
||
Access to the legacy PCI configuration space on x86 via port 0xCF8/CFC requires IS_PRIV, even with FLASK. Writes to the BARs are checked as "setup", while other reads/writes are "use"; the target is the PCI device whose configuration space is being modified. Accesses to the MMIO-based PCI express configuration space described by the ACPI MCFG table are controlled as MMIO accesses, and cannot special-case BAR writes. |
Access to the legacy PCI configuration space on x86 via port 0xCF8/CFC requires IS_PRIV, even with FLASK. Writes to the BARs are checked as "setup", while other reads/writes are "use"; the target is the PCI device whose configuration space is being modified. Accesses to the MMIO-based PCI express configuration space described by the ACPI MCFG table are controlled as MMIO accesses, and cannot special-case BAR writes. |
||
The {add,remove}_{irq,ioport,iomem,device} permissions use: |
The {add,remove}_{irq,ioport,iomem,device} permissions use: |
||
source = domain making the hypercall |
source = domain making the hypercall |
||
target = resource's security label |
target = resource's security label |
||
{| class="zebra sortable fullwidth" |
|||
!Description |
|||
! style="min-width=150px; max-width=250px;"| Operation |
|||
{| class="zebra" |
|||
|- |
|- |
||
! |
|||
Description |
|||
! |
|||
Operation |
|||
|- |
|- |
||
Line 1,312: | Line 1,221: | ||
| |
| |
||
PHYSDEVOP_map_pirq and ioapic writes for dom0, when acting on real IRQs |
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 GSI interrupts, the IRQ's label is indexed by the IRQ number |
||
For MSI interrupts, the label of the PCI device is used |
For MSI interrupts, the label of the PCI device is used |
||
Line 1,344: | Line 1,251: | ||
| |
| |
||
XEN_DOMCTL_get_device_group, XEN_DOMCTL_test_assign_device: |
XEN_DOMCTL_get_device_group, XEN_DOMCTL_test_assign_device: |
||
source = domain making the hypercall |
source = domain making the hypercall |
||
target = PCI device being queried |
target = PCI device being queried |
||
| |
| |
||
stat_device |
stat_device |
||
Line 1,371: | Line 1,273: | ||
| |
| |
||
checked for PCI hot and cold-plug hypercalls, with target as the PCI device |
checked for PCI hot and cold-plug hypercalls, with target as the PCI device |
||
checked for CPU and memory hotplug with xen_t as the target |
checked for CPU and memory hotplug with xen_t as the target |
||
Line 1,380: | Line 1,281: | ||
| |
| |
||
checked for PCI hot-unplug hypercalls, with target as the PCI device |
checked for PCI hot-unplug hypercalls, with target as the PCI device |
||
checked for CPU offlining with xen_t as the target |
checked for CPU offlining with xen_t as the target |
||
| |
| |
||
Line 1,397: | Line 1,295: | ||
class security |
===class security=== |
||
Class security describes the FLASK security server itself; these operations are accessed using the xsm_op hypercall. The source is the domain invoking the hypercall, and the target is security_t. |
Class security describes the FLASK security server itself; these operations are accessed using the xsm_op hypercall. The source is the domain invoking the hypercall, and the target is security_t. |
||
Line 1,403: | Line 1,301: | ||
Any domain with access to load_policy or setenforce must be trusted, since it can bypass the rest of the security policy. |
Any domain with access to load_policy or setenforce must be trusted, since it can bypass the rest of the security policy. |
||
{| class="zebra" |
{| class="zebra sortable fullwidth" |
||
!Description |
|||
|- |
|||
! style="min-width=150px; max-width=250px;"| Operation |
|||
|Description |
|||
|Operation |
|||
|- |
|- |
||
|use the security server to compute an access check |
|use the security server to compute an access check |
||
Line 1,444: | Line 1,341: | ||
|del_ocontext |
|del_ocontext |
||
|} |
|} |
||
=XSM Limitations= |
|||
==XSM control over Xenstore operations== |
|||
FLASK currently provides locally defined access vectors for controlling xenstore operations; however, xenstored and oxenstored do not currently provide support for managing domU access to xenstore as managed by such access vectors. |
|||
Future development is required to complete this feature. |
|||
Until this feature is available, only the much coarser xenstore permission model can be used to control domU access to xenstore data. |
|||
==Xen versions prior to 4.3== |
|||
Xen versions 4.2 and earlier lack full XSM support in hypercall handlers, setting a -EPERM return code for many hypercalls. This is due to XSM being considered a preview feature in development at that time. For these versions, only sysctl hypercalls and domctl hypercall XEN_DOMCTL_getdomaininfo were truly usable by non-privileged domains. The following commit was the one to remedy this situation [[http://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=996cf47b8081fc6b2efd4fa9a69f6b77028719e6;hp=d018d6b4fb36f04086783d6883b25641ae166034]] |
|||
==Linux versions prior to 3.8== |
|||
Linux versions prior to 3.8 carried a hard-coded check for xen_initial_domain() in the privcmd device handlers for dispatching hypercalls on the behalf of userspace. This caused -EPERM to be returned for all domUs other than dom0 when attempting to use a hypercall, even if that hypercall was explicitly enabled and permitted by the security label assigned to the calling domU. The relevant patch is a simple one ( [[https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=30d4b180e20c081f435143f8bc211c66a930608a]] ) and is known to apply to earlier versions like 3.7.1 without modification. Back-porting should be trivial in most cases. |
|||
Hypercalls invoked directly within the kernel should not be subject to the consideration above, as it only applies to the privcmd device handler code for userspace. |
|||
[[Category:Xen]] |
[[Category:Xen]] |
Latest revision as of 09:02, 27 May 2015
Overview
This is intended to be a hands-on description of Xen Security Modules (XSM) as well as their structure and operation. This document has been compiled based on docs/misc/xsm-flask.txt, mailing list discussions, Xen source code, and other support material contained within the Xen source-tree.
A reader of this document should be able to:
1. Build and install Xen with XSM enabled 2. Create and load a security policy 3. Tag virtual machines with security labels managed by the security policy 4. Utilize a simple application making use of privileges permitted by the policy
What is XSM
Xen offers a security framework called Xen Security Modules, or XSM, to enable an administrator or developer to exert fine-grained control over a Xen domain and its capabilities. Specifically, XSM makes it possible to define permissible interactions between domains, the hypervisor itself, and related resources such as memory and devices.
Via XSM, Xen implements a type of mandatory access control via a security architecture called FLASK using a module of the same name. An abbreviation for Flux Advanced Security Kernel, FLASK has its origins in several trusted operating system research projects, managed jointly between the United States’ National Security Agency, the Secure Computing Corporation, and the University of Utah. The specific intent of FLASK is to separate security enforcement from security policy, better isolating logical components of security systems.
As applied to operating system design, FLASK brings about security enhancements that improve auditing of actions and more granular control thereof. Today, it is perhaps best known through its expression in Security Enhanced Linux (SELinux).
In the same sense that SELinux is a collection of Linux security enhancements, auditing and policy mechanisms, so also is the FLASK Xen Security Module to Xen itself. Similarly, each rely upon a set of administration tools to craft security policies and guide their enforcement.
Below are some illustrative examples of what the FLASK Xen Security Module can make possible:
- Prevent two domains from communicating via event channels or grants - Grant a set of privileged capabilities to otherwise unprivileged domains - Control which domains can use device passthrough (and which devices) - Restrict or audit operations performed by privileged domains - Prevent a privileged domain from arbitrarily mapping pages from other domains - Isolate hypervisor support components (such as qemu or xenstore) from each other, and provide assurance that each may only perform its designated actions
Linux and Xen version requirements for use of XSM
Several important considerations exist when intending to deploy XSM. The versions of Xen and Linux are of critical importance. Below are the specific requirements:
- Xen version 4.3 and newer: Earlier versions lack full XSM support (but may still be usable in a restricted sense, depending upon the use case). Prior to the 4.3 release, only the sysctl hypercalls and one domctl hypercall XEN_DOMCTL_getdomaininfo were truly usable by non-privileged domains. Specific details: commit http://xenbits.xen.org/gitweb/?p=xen.git;a=commit;h=996cf47b8081fc6b2efd4fa9a69f6b77028719e6 and later.
- Linux version 3.8 or newer to allow hypercalls from userspace applications in HVM domUs as exposed via /dev/privcmd. Alternatively, the adaptation of the following patch to an earlier version (known to apply cleanly to 3.7.1): https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=30d4b180e20c081f435143f8bc211c66a930608a
Enabling XSM in Xen
Several steps must be performed to use XSM with Xen. First, Xen itself must be compiled with XSM. Second, a XSM FLASK policy must be created. Third and finally, the dom0 bootloader configuration must be updated to enable XSM at boot and load the policy. NOTE: these steps require a make clean, rebuild, reinstall and reboot.
Compile Xen with XSM
XSM is a compile-time option for Xen; by default, it is a disabled feature.
To use the framework, Xen must be compiled with XSM and FLASK explicitly enabled.
To do so, edit Config.mk or the .config file and set XSM_ENABLE and FLASK_ENABLE to "y"
Then, follow the standard build procedure described at Compiling_Xen_From_Source. NOTE: if a previously compiled Xen exists, be sure to 'make clean' after updating Config.mk and prior to compiling.
Compiling the sample FLASK policy
During typical Xen compilation, compilation of the FLASK sample policy is not performed due to the necessary dependency upon the SElinux compiler, otherwise known as ‘checkpolicy.’ This tool must be installed before proceeding:
Installing checkpolicy
- apt-get install checkpolicy
- yum install checkpolicy
- or similar
Next, to build the sample policy itself:
- cd xen-source-tree/
- make -C tools/flask/policy
The demonstration policy is generated from statements defined in files within the tools/flask/policy path.. After compilation, the resultant policy will be named xenpolicy-$XEN_FULLVERSION (before 4.5 release, it's named xenpolicy.24) and reside within the directory tools/flask/policy.
Quoting from its description, this policy illustrates "most of the features of FLASK that can be used without dom0 disaggregation. The main types for domUs are:
- domU_t is a domain that can communicate with any other domU_t - isolated_domU_t can only communicate with dom0 - prot_domU_t is a domain type whose creation can be disabled with a boolean - nomigrate_t is a domain that must be created via the nomigrate_t_building type, and whose memory cannot be read by dom0 once created
HVM domains with stubdomain device models use two types (one per domain):
- domHVM_t is an HVM domain that uses a stubdomain device model - dm_dom_t is the device model for a domain with type domHVM_t"
Booting Xen with FLASK
When Xen is compiled with XSM, the following grub command-line options can be used to set FLASK properties. Please consult distribution documentation for the recommended method to update the grub configuration. Using Debian and Ubuntu as examples, these options can be placed in /etc/default/grub within the variable GRUB_CMDLINE_XEN_DEFAULT. Specifically:
- GRUB_CMDLINE_XEN_DEFAULT="flask=<OPTION>”
When package management scripts regenerate the grub configuration (e.g., during a kernel upgrade or at the request of a sysadmin), a system configured in this manner will be guaranteed to properly embed XSM parameters within the grub configuration. For testing purposes, manually editing the grub command line at boot will also suffice.
The options for "flask=" are:
- `permissive`: This is intended for development and is not suitable for use with untrusted guests. If a policy is provided by the bootloader, it will be loaded; errors will be reported to the ring buffer but will not prevent booting. The policy can be changed to enforcing mode using "xl setenforce".
- `enforcing`: This requires a security policy to be provided by the bootloader and will enter enforcing mode prior to the creation of domain 0. If a valid policy is not provided, the hypervisor will not continue booting.
- `late`: This disables loading of the security policy from the bootloader. FLASK will be enabled but will not enforce access controls until a policy is loaded by a domain using "xl loadpolicy". Once a policy is loaded, FLASK will run in enforcing mode unless "xl setenforce" has changed that setting.
- `disabled`: This causes the XSM framework to revert to the dummy module. The dummy module provides the same security policy as is used when compiling the hypervisor without support for XSM. The xsm_op hypercall can also be used to switch to this mode after boot, but there is no way to re-enable FLASK once the dummy module is loaded.
The compiled policy must be installed into a grub-accessible location (such as /boot where kernels are also stored), as it is loaded during the Xen boot process. Otherwise, FLASK will not be enabled at runtime. It is recommended by the authors of XSM to add an extra subdirectory, such as /boot/flask, to use as a repository for XSM policies.
The policy has to be added to grub as a module after the Xen kernel multiboot line, for example as:
multiboot /boot/xen-VERSION.gz dom0_mem=1024M,max:1024M flask=enforcing module /boot/vmlinuz-X.Y-amd64 root=/dev/mapper/vg_system-root ro quiet module /boot/initrd.img-X.Y module /boot/flask/xenpolicy-VERSION
NOTE: it is critical that the ordering of the modules above be retained, as Xen expects implicitly that the kernel is the first referenced module. For Xen 4.4 and below, the policy file is always xenpolicy.24.
To automatically generate this line when running grub-update, the Xen grub update helper script can be modified (e.g., in Ubuntu/Debian by copying /etc/grub.d/20_linux_xen to 21_linux_xsm and adding the "module" line for the policy.) This helper script will differ greatly depending on installed grub version (and linux distribution).
Once Xen is booted with the FLASK policy, the policy can be reloaded using the “xl loadpolicy” command.
Creating domains with XSM security labels
DomU Configuration
Once FLASK is loaded, domains need to be created using a security label in their configuration. Without this line, the domains will be classified “unlabeled”.
Example line from a domU configuration:
seclabel='system_u:system_r:domU_t'
The security label’s three sections are: user, role and type (denoted by convention via _u, _r, _t suffixes, though strictly speaking, such suffixes these are not required). These will be discussed further in this document. Each component must be defined within the loaded policy to ensure proper function.
NOTE: an unlabeled domU may or may not cause a problem. If Flask is enabled, but not enforcing, the unlabelled domain is likely to work without interruption. If Flask is enabled and enforcing via a policy that explicitly permits an unlabelled domain to perform some operations, the same is also true. However, it is similarly likely that such a domain will be prevented from performing some operations (which can be problematic if the code invoking the hypercall does not tolerate failure).
Listing domUs and applied security labels
Both xl and xm utilities possess the ability to display running virtual machines with their assigned security labels.
- For xl, use : xl list -Z
- For xm, use : xm list --label
Example output:
steve@xen1:~$ sudo xl list -Z Name ID Mem VCPUs State Time(s) Security Label Domain-0 0 1023 24 r----- 745223.8 system_u:system_r:dom0_t windows-7-sp1-x86 185 1024 1 -b---- 29089.3 tamas:vm_r:domU_t windows-xp-sp2 193 128 1 -b---- 51247.2 steve:vm_r:domU_t centos-6-x86_64 330 2051 1 -b---- 1821.8 system_u:system_r:unlabeled_t centos-6-i386 733 259 1 -b---- 448.8 system_u:system_r:unlabeled_t
Monitoring XSM logs
XSM:Flask will emit log events "avc: denied" when a permission is denied by the policy, just like SELinux (avc being an acronym for Access Vector Cache). These log events can be viewed with xl dmesg:
xl dmesg | grep avc
Example output for a denied attempt by a HVM domU to invoke HVMOP_set_param:
(XEN) avc: denied { setparam } for domid=0 scontext=system_u:system_r:dom0_t tcontext=system_u:system_r:domU_t tclass=hvm
Within the braces is the operation that was denied. The parameters domid and scontext (source context) reference the domU that requested the operation, and tcontext (target context) describes the VM that would have been operated upon.
Familiar SELinux utilities like audit2allow can be used as a policy creation aid by processing these denied events:
xl dmesg | audit2allow
The output is a list of generated allow rules, which can be added to the policy via the *.te file.
FLASK Policies
The sample FLASK policy can be found in tools/flask/policy/policy/modules/xen. The two files found here are xen.te, the policy description, and xen.if, a collection of macros to be used in the policy description.
Adding new security modules
New security modules can be easily added by editing tools/flask/policy/policy/modules.conf and adding the new modules name as such:
<module_name> = on
The module has to defined in the folder tools/flask/policy/policy/modules/<module_name> with two files:
<module_name>.te <module_name>.if
When multiple security modules are present the modules can’t contain duplicate role or type declarations.
The sample policy is recommended to be left turned on as it contains several definitions required to boot Xen.
When a new policy has been created, compile it as in the earlier section 'Compiling the sample FLASK policy.' Install the policy into its destination directory (e.g., /boot/flask), and modify the bootloader configuration as necessary.
Types, roles, users and attributes
Flask provides multiple levels of categories to abstract away from hypercalls, including types, roles, users, and attributes.
Policy Types
On the most basic level, a “type” can be defined to specify the hypercalls that type is allowed to execute and how it is allowed to execute them. When XSM is enforcing the policy, only those hypercalls will be executable that are explicitly allowed in the policy.
The sample XSM policy defines several types, for example: xen_t, dom0_t and domU_t. A new type can be defined with the “type” keyword:
type new_type_t;
Each type can be assigned into an attribute category, to easily reference each type having the attribute. The sample policy defines several attributes, for example: xen_type, domain_type, resource_type, etc.
A type can be assigned one or multiple attributes, for example
type xen_t, xen_type, mls_priv;
assigns the xen_t type the attributes xen_type and mls_priv.
Defining which hypercalls are allowed for each type is done using the “allow” keyword. Hypercalls are grouped into security classes (defined in xen/xsm/flask/policy/access_vectors) which will be discussed later. For example the following
allow dom0_t security_t:security check_context;
enables the dom0_t type to execute the check_context hypercall in the security class targeting a security_t type. The allow line can be generalized as follows:
allow <source type> <target type>:<security class> <hypercall>;
Multiple hypercalls belonging to the same security class can be defined by wrapping the hypercalls into {}, for example:
allow dom0_t dom0_t:resource { add remove };
Using the attributes we can quickly allow each type with that attribute access to hypercalls:
# Allow all domains to use (unprivileged parts of) the tmem hypercall allow domain_type xen_t:xen tmem_op;
Policy Roles
Roles are the second level above types, where we can define sets of types belonging to a role. While attributes are only accessible internally inside the policy (as a shortcut), a role is part of the security label. A role defines which types belong to that role, therefore it can prevent us from erroneously assigning a type to a VM when it’s role doesn’t have that type.
The sample FLASK policy defines two roles: system_r and vm_r. A role definition is as follows:
role system_r; role system_r types { xen_type domain_type };
The first line declares a new role while the second line assigns the types belonging to that role (using attributes). When we want to include all types with an attribute except some, we can exclude those types by placing “-” in front of them, like
role vm_r; role vm_r types { domain_type -dom0_t };
In the above, the vm_r role is defined as all types with the domain_type attribute, except the dom0_t type.
Policy Users
On the highest level we can define users. Users are not defined in the .te (like roles and types), rather they are defined in tools/flask/policy/policy/users. This allows us to define a generic set of users across multiple security modules. As an example, the sample FLASK policy creates three users: system_u, customer_1 and customer_2.
A user can be assigned multiple roles but by default the system_u user is assigned the system_r role, and the customer_* users are assigned the vm_r role.
A new user can be defined in the users file as
user <user name> roles { <role1> <role2> };
Policy Constraints
Constraints, or conditional filters, are defined in the file tools/flask/policy/policy/constraints. Constraints are a safety net to specifically disallow undesired operations that the policy may allow otherwise. The sample Flask policy defines two constraints to prevent event channels and grants between different customers. A constraints syntax is as follows:
constrain <security class> { <hypercall> } ( expression );
For example:
constrain grant { map_read map_write copy } ( u1 == system_u or u2 == system_u or u1 == u2 );
The constraint defines that the hypercalls belonging to the security class are only allowed when the expression is true. The expression can contain any user, role and type defined in the policy in the following format:
expression : ( expression ) | not expression | expression and expression | expression or expression | u1 op u2 | r1 role_op r2 | t1 op t2 | u1 op names | u2 op names | r1 op names | r2 op names | t1 op names | t2 op names
op : == | != role_op : == | != | eq | dom | domby | incomp
names : name | { name_list } name_list : name | name_list name
Macros
Macros are used heavily across the sample Flask policy. For each security module a macro file can be defined as a .if file. The sample macro file (tools/flask/policy/policy/modules/xen/xen.if) contains several commonly used macros, for example for declaring a new domain type and it’s associated sub-types and to setup it’s common allow rules. Macros can call other macros. The macro .if file is required to exist for the policy to compile, even if no macros are defined for a module.
Security classes
Security classes are defined in xen/xsm/flask/policy/access_vectors and categorize each hypercall into one of the classes. Each class can hold a maximum of 32 members.
class xen
Class xen consists of operations dealing with the hypervisor itself. Unless otherwise specified, the source is the domain executing the hypercall, and the target is the xen initial sid (type xen_t).
Description | Hypercall |
---|---|
XENPF_settime |
settime |
XEN_SYSCTL_tbuf_op |
tbufcontrol |
CONSOLEIO_read, XEN_SYSCTL_readconsole |
readconsole |
XEN_SYSCTL_readconsole with clear=1 |
clearconsole |
XEN_SYSCTL_perfc_op |
perfcontrol |
XENPF_add_memtype |
mtrr_add |
XENPF_del_memtype |
mtrr_del |
XENPF_read_memtype |
mtrr_read |
XENPF_microcode_update |
microcode |
XEN_SYSCTL_physinfo, XEN_SYSCTL_topologyinfo, XEN_SYSCTL_numainfo |
physinfo |
XENPF_platform_quirk |
quirk |
CONSOLEIO_write |
writeconsole |
PHYSDEVOP_apic_read, PHYSDEVOP_alloc_irq_vector |
readapic |
PHYSDEVOP_apic_write |
writeapic |
Most XENOPROF_* |
privprofile |
XENOPROF_{init,enable_virq,disable_virq,get_buffer} |
nonprivprofile |
kexec hypercall |
kexec |
XENPF_firmware_info, XENPF_efi_runtime_call |
firmware |
XENPF_enter_acpi_sleep |
sleep |
XENPF_change_freq |
frequency |
XENPF_getidletime |
getidle |
XEN_SYSCTL_debug_keys |
debug |
XEN_SYSCTL_getcpuinfo, XENPF_get_cpu_version, XENPF_get_cpuinfo |
getcpuinfo |
XEN_SYSCTL_availheap |
heap |
XEN_SYSCTL_get_pmstat, XEN_SYSCTL_pm_op, XENPF_set_processor_pminfo, XENPF_core_parking |
pm_op |
mca hypercall |
mca_op |
XEN_SYSCTL_lockprof_op |
lockprof |
XEN_SYSCTL_cpupool_op |
cpupool_op |
tmem hypercall (any access) |
tmem_op |
TMEM_CONTROL command of tmem hypercall |
tmem_control |
XEN_SYSCTL_scheduler_op with XEN_DOMCTL_SCHEDOP_getinfo, XEN_SYSCTL_sched_id |
getscheduler |
XEN_SYSCTL_scheduler_op with XEN_DOMCTL_SCHEDOP_putinfo |
setscheduler |
class domain and class domain2
Classes domain and domain2 consist of operations that a domain performs on another domain or on itself. Unless otherwise specified, the source is the domain executing the hypercall, and the target is the domain being operated upon(which may result in a _self or _target type). Transitions in class domain are used to produce the _self and _target types.
class domain
Description | Hypercall |
---|---|
XEN_DOMCTL_setvcpucontext |
setvcpucontext |
XEN_DOMCTL_pausedomain |
pause |
XEN_DOMCTL_unpausedomain |
unpause |
XEN_DOMCTL_resumedomain |
resume |
XEN_DOMCTL_createdomain |
create |
checked in FLASK_RELABEL_DOMAIN for any relabel operation: source = the old label of the domain target = the new label of the domain see also the domain2 relabel{from,to,self} permissions |
transition |
XEN_DOMCTL_max_vcpus |
max_vcpus |
XEN_DOMCTL_destroydomain |
destroy |
XEN_DOMCTL_setvcpuaffinity |
setvcpuaffinity |
XEN_DOMCTL_getvcpuaffinity |
getvcpuaffinity |
XEN_DOMCTL_scheduler_op with XEN_DOMCTL_SCHEDOP_getinfo |
getscheduler |
XEN_DOMCTL_getdomaininfo, XEN_SYSCTL_getdomaininfolist |
getdomaininfo |
XEN_DOMCTL_getvcpuinfo |
getvcpuinfo |
XEN_DOMCTL_getvcpucontext |
getvcpucontext |
XEN_DOMCTL_max_mem |
setdomainmaxmem |
XEN_DOMCTL_setdomainhandle |
setdomainhandle |
XEN_DOMCTL_setdebugging |
setdebugging |
XEN_DOMCTL_hypercall_init |
hypercall |
XEN_DOMCTL_settimeoffset |
settime |
checked in XEN_DOMCTL_set_target: source = the new device model domain target = the new target domain see also the domain2 make_priv_for and set_as_target checks |
set_target |
SCHEDOP_remote_shutdown |
shutdown |
XEN_DOMCTL_set{,_machine}_address_size |
setaddrsize |
XEN_DOMCTL_get{,_machine}_address_size |
getaddrsize |
XEN_DOMCTL_sendtrigger |
trigger |
XEN_DOMCTL_get_ext_vcpucontext |
getextvcpucontext |
XEN_DOMCTL_set_ext_vcpucontext |
setextvcpucontext |
XEN_DOMCTL_getvcpuextstate |
getvcpuextstate |
XEN_DOMCTL_setvcpuextstate |
setvcpuextstate |
XENMEM_get_pod_target |
getpodtarget |
XENMEM_set_pod_target |
setpodtarget |
XEN_DOMCTL_subscribe, XEN_DOMCTL_disable_migrate, XEN_DOMCTL_suppress_spurious_page_faults |
set_misc_info |
XEN_DOMCTL_set_virq_handler |
set_virq_handler |
class domain2
Class domain2 is simply a continuation of class domain.
Description | Hypercall |
---|---|
checked in FLASK_RELABEL_DOMAIN with non-DOMID_SELF: source = the domain making the hypercall target = the old label of the domain being relabeled |
relabelfrom |
checked in FLASK_RELABEL_DOMAIN with non-DOMID_SELF: source = the domain making the hypercall target = the new label of the domain being relabeled |
relabelto |
checked in FLASK_RELABEL_DOMAIN, only with DOMID_SELF: source = the old label of the domain target = the new label of the domain see also domain__transition |
relabelself |
checked in XEN_DOMCTL_set_target: source = the domain making the hypercall target = the new device model domain |
make_priv_for |
checked in XEN_DOMCTL_set_target: source = the domain making the hypercall target = the new target domain |
set_as_target |
XEN_DOMCTL_set_cpuid |
set_cpuid |
XEN_DOMCTL_gettscinfo |
gettsc |
XEN_DOMCTL_settscinfo |
settsc |
XEN_DOMCTL_scheduler_op with XEN_DOMCTL_SCHEDOP_putinfo |
setscheduler |
class hvm
Similar to class domain, but primarily contains domctls related to HVM domains.
Description | Hypercall |
---|---|
XEN_DOMCTL_sethvmcontext |
sethvmc |
XEN_DOMCTL_gethvmcontext, XEN_DOMCTL_gethvmcontext_partial |
gethvmc |
HVMOP_set_param |
setparam |
HVMOP_get_param |
getparam |
HVMOP_set_pci_intx_level (also needs hvmctl) |
pcilevel |
HVMOP_set_isa_irq_level |
irqlevel |
HVMOP_set_pci_link_route |
pciroute, bind_irq |
XEN_DOMCTL_pin_mem_cacheattr |
cacheattr |
HVMOP_track_dirty_vram |
trackdirtyvram |
HVMOP_modified_memory, HVMOP_get_mem_type, HVMOP_set_mem_type, HVMOP_set_mem_access, HVMOP_get_mem_access, HVMOP_pagetable_dying, HVMOP_inject_trap |
hvmctl |
XEN_DOMCTL_set_access_required |
mem_event |
XEN_DOMCTL_mem_sharing_op and XENMEM_sharing_op_{share,add_physmap} with: source = the domain making the hypercall target = domain whose memory is being shared |
mem_sharing |
XEN_DOMCTL_audit_p2m |
audit_p2m |
HVMOP_inject_msi |
send_irq |
checked in XENMEM_sharing_op_{share,add_physmap} with: source = domain whose memory is being shared target = client domain |
share_mem |
class event
Class event describes event channels. Interdomain event channels have their own security label which is computed using a type transition between the source and target domains. Each endpoint has its own label, and the permission checks must pass on both endpoints for an event channel to be established.
Description | Hypercall |
---|---|
when creating an interdomain event channel endpoint: source = event channel label target = remote domain the event channel binds to. This may be a _self or _target label if the endpoints are related as such. This permission is checked when creating an unbound event channel and when the interdomain event channel is established. |
bind |
EVTCHNOP_send: source = domain sending the event target = event channel label |
send |
EVTCHNOP_status; same as _send |
status |
when creating an interdomain event channel endpoint: source = the domain creating the channel (which might not be an endpoint) target = event channel label |
create |
EVTCHNOP_reset: source = domain making the hypercall target = domain whose event channels are being reset |
reset |
class grant
Class grant describes pages shared by grant mappings. Pages use the security label of their owning domain.
Description | Hypercall |
---|---|
GNTTABOP_map_grant_ref with any access |
map_read |
GNTTABOP_map_grant_ref with write access |
map_write |
GNTTABOP_unmap_grant_ref |
unmap |
GNTTABOP_transfer |
transfer |
GNTTABOP_setup_table, GNTTABOP_get_status_frames (target is commonly _self) |
setup |
GNTTABOP_copy |
copy |
GNTTABOP_query_size, GNTTABOP_get_version |
query |
class mmu
Class mmu describes pages of memory not accessed using grants. Permissions are checked using the domain ID used to access the page - the most common case is a domain's own ID (the _self label). Using DOMID_IO in the map command to restrict the mapping to IO memory will result in the target being domio_t, and migration uses read-only mappings with a target of DOMID_XEN (domxen_t).
Description | Hypercall |
---|---|
checked when using mmu_update to map a page readably source = domain making the hypercall (which might not own the page table) target = domain whose pages are being mapped |
map_read |
checked when using mmu_update to map a page writably source = domain making the hypercall target = domain whose pages are being mapped |
map_write |
XEN_DOMCTL_getpageframeinfo* |
pageinfo |
XEN_DOMCTL_getmemlist |
pagelist |
XENMEM_{increase,decrease}_reservation, XENMEM_populate_physmap |
adjust |
XENMEM_{current,maximum}_reservation, XENMEM_maximum_gpfn |
stat |
mmu_update MMU_MACHPHYS_UPDATE |
updatemp |
XENMEM_add_to_physmap, XENMEM_remove_from_physmap |
physmap |
MMUEXT_PIN_L*_TABLE |
pinpage |
XENMEM_machine_memory_map (with target xen_t), XENMEM_set_memory_map (with domain target) |
memorymap |
checked when using mmu_update to update the page tables of another domain source = domain making the hypercall target = domain whose page tables are being modified |
remote_remap |
the mmuext_op hypercall acting on the target domain |
mmuext_op |
XENMEM_exchange: source = domain making the hypercall target = domain whose pages are being exchanged |
exchange |
class shadow
Control of the paging_domctl split by subop
Description | Operation |
---|---|
XEN_DOMCTL_SHADOW_OP_OFF |
disable |
enable, get/set allocation |
enable |
enable, read, and clean log |
logdirty |
class resource
Class resource is used to describe the resources used in hardware device passthrough. Resources include: hardware IRQs, MMIO regions, x86 I/O ports, and PCI devices; see docs/misc/xsm-flask.txt for how to label them.
Access to the legacy PCI configuration space on x86 via port 0xCF8/CFC requires IS_PRIV, even with FLASK. Writes to the BARs are checked as "setup", while other reads/writes are "use"; the target is the PCI device whose configuration space is being modified. Accesses to the MMIO-based PCI express configuration space described by the ACPI MCFG table are controlled as MMIO accesses, and cannot special-case BAR writes.
The {add,remove}_{irq,ioport,iomem,device} permissions use:
source = domain making the hypercall target = resource's security label
Description | Operation |
---|---|
checked when adding a resource to a domain: source = domain making the hypercall target = domain which will have access to the resource |
add |
checked when removing a resource from a domain: source = domain making the hypercall target = domain which will no longer have access to the resource |
remove |
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 |
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 |
add_irq |
PHYSDEVOP_unmap_pirq (same as map, and only for real IRQs) |
remove_irq |
XEN_DOMCTL_ioport_permission, XEN_DOMCTL_ioport_mapping |
add_ioport, remove_ioport |
XEN_DOMCTL_iomem_permission, XEN_DOMCTL_memory_mapping |
add_iomem, remove_iomem |
XEN_DOMCTL_get_device_group, XEN_DOMCTL_test_assign_device: source = domain making the hypercall target = PCI device being queried |
stat_device |
XEN_DOMCTL_assign_device |
add_device |
XEN_DOMCTL_deassign_device |
remove_device |
checked for PCI hot and cold-plug hypercalls, with target as the PCI device checked for CPU and memory hotplug with xen_t as the target |
plug |
checked for PCI hot-unplug hypercalls, with target as the PCI device checked for CPU offlining with xen_t as the target |
unplug |
checked for PHYSDEVOP_restore_msi* (target PCI device) checked for PHYSDEVOP_setup_gsi (target IRQ) checked for PHYSDEVOP_pci_mmcfg_reserved (target xen_t) |
setup |
class security
Class security describes the FLASK security server itself; these operations are accessed using the xsm_op hypercall. The source is the domain invoking the hypercall, and the target is security_t.
Any domain with access to load_policy or setenforce must be trusted, since it can bypass the rest of the security policy.
Description | Operation |
---|---|
use the security server to compute an access check | compute_av |
use the security server to compute a type transition | compute_create |
use the security server to compute member selection | compute_member |
sid <-> context string conversions | check_context |
allow loading a new XSM/FLASK policy | load_policy |
use the security server to compute an object relabel | compute_relabel |
use the security server to list the SIDs reachable by a given user | compute_user |
allow switching between enforcing and permissive mode | setenforce |
allow changing policy booleans | setbool |
allow changing security server configuration parameters | setsecparam |
add ocontext label definitions for resources | add_ocontext |
remove ocontext label definitions for resources | del_ocontext |
XSM Limitations
XSM control over Xenstore operations
FLASK currently provides locally defined access vectors for controlling xenstore operations; however, xenstored and oxenstored do not currently provide support for managing domU access to xenstore as managed by such access vectors.
Future development is required to complete this feature.
Until this feature is available, only the much coarser xenstore permission model can be used to control domU access to xenstore data.
Xen versions prior to 4.3
Xen versions 4.2 and earlier lack full XSM support in hypercall handlers, setting a -EPERM return code for many hypercalls. This is due to XSM being considered a preview feature in development at that time. For these versions, only sysctl hypercalls and domctl hypercall XEN_DOMCTL_getdomaininfo were truly usable by non-privileged domains. The following commit was the one to remedy this situation [[1]]
Linux versions prior to 3.8
Linux versions prior to 3.8 carried a hard-coded check for xen_initial_domain() in the privcmd device handlers for dispatching hypercalls on the behalf of userspace. This caused -EPERM to be returned for all domUs other than dom0 when attempting to use a hypercall, even if that hypercall was explicitly enabled and permitted by the security label assigned to the calling domU. The relevant patch is a simple one ( [[2]] ) and is known to apply to earlier versions like 3.7.1 without modification. Back-porting should be trivial in most cases.
Hypercalls invoked directly within the kernel should not be subject to the consideration above, as it only applies to the privcmd device handler code for userspace.