This patch integrates the new access control management tools into 'xm'
and 'xend' and supports label/ssid translation support for
migration/life-migration/resume.
Signed-off by: Reiner Sailer <sailer@xxxxxxxxxx>
---
tools/python/xen/xend/XendDomain.py | 4 +
tools/python/xen/xend/XendDomainInfo.py | 56 ++++++++++++++++------
tools/python/xen/xm/create.py | 81 +++++++++++++++++++++++++++++---
tools/python/xen/xm/main.py | 69 +++++++++++++++++++++++----
4 files changed, 178 insertions(+), 32 deletions(-)
Index: xen-unstable.hg-shype/tools/python/xen/xend/XendDomain.py
===================================================================
--- xen-unstable.hg-shype.orig/tools/python/xen/xend/XendDomain.py
+++ xen-unstable.hg-shype/tools/python/xen/xend/XendDomain.py
@@ -38,6 +38,7 @@ from xen.xend.XendError import XendError
from xen.xend.XendLogging import log
from xen.xend.xenstore.xstransact import xstransact
from xen.xend.xenstore.xswatch import xswatch
+from xen.util import security
xc = xen.lowlevel.xc.xc()
@@ -265,7 +266,7 @@ class XendDomain:
# handling in the relocation-socket handling code (relocate.py) is
# poor, so we need to log this for debugging.
log.exception("Restore failed")
- raise
+ raise XendError("Restore failed")
def restore_(self, config):
@@ -283,6 +284,7 @@ class XendDomain:
"""
self.domains_lock.acquire()
try:
+ security.refresh_ssidref(config)
dominfo = XendDomainInfo.restore(config)
self._add_domain(dominfo)
return dominfo
Index: xen-unstable.hg-shype/tools/python/xen/xend/XendDomainInfo.py
===================================================================
--- xen-unstable.hg-shype.orig/tools/python/xen/xend/XendDomainInfo.py
+++ xen-unstable.hg-shype/tools/python/xen/xend/XendDomainInfo.py
@@ -33,7 +33,7 @@ import threading
import xen.lowlevel.xc
from xen.util import asserts
from xen.util.blkif import blkdev_uname_to_file
-
+from xen.util import security
import balloon
import image
import sxp
@@ -120,7 +120,6 @@ VM_CONFIG_PARAMS = [
# file, so those are handled separately.
ROUNDTRIPPING_CONFIG_ENTRIES = [
('uuid', str),
- ('ssidref', int),
('vcpus', int),
('vcpu_avail', int),
('cpu_weight', float),
@@ -138,7 +137,6 @@ ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFI
#
VM_STORE_ENTRIES = [
('uuid', str),
- ('ssidref', int),
('vcpus', int),
('vcpu_avail', int),
('memory', int),
@@ -291,6 +289,9 @@ def parseConfig(config):
result['cpu'] = get_cfg('cpu', int)
result['cpus'] = get_cfg('cpus', str)
result['image'] = get_cfg('image')
+ tmp_security = get_cfg('security')
+ if tmp_security:
+ result['security'] = tmp_security
try:
if result['image']:
@@ -437,7 +438,7 @@ class XendDomainInfo:
self.validateInfo()
self.image = None
-
+ self.security = None
self.store_port = None
self.store_mfn = None
self.console_port = None
@@ -515,6 +516,7 @@ class XendDomainInfo:
else:
entries = VM_STORE_ENTRIES
entries.append(('image', str))
+ entries.append(('security', str))
map(lambda x, y: useIfNeeded(x[0], y), entries,
self.readVMDetails(entries))
@@ -538,7 +540,6 @@ class XendDomainInfo:
try:
defaultInfo('name', lambda: "Domain-%d" % self.domid)
- defaultInfo('ssidref', lambda: 0)
defaultInfo('on_poweroff', lambda: "destroy")
defaultInfo('on_reboot', lambda: "restart")
defaultInfo('on_crash', lambda: "restart")
@@ -565,12 +566,16 @@ class XendDomainInfo:
defaultInfo('backend', lambda: [])
defaultInfo('device', lambda: [])
defaultInfo('image', lambda: None)
+ defaultInfo('security', lambda: None)
self.check_name(self.info['name'])
if isinstance(self.info['image'], str):
self.info['image'] = sxp.from_string(self.info['image'])
+ if isinstance(self.info['security'], str):
+ self.info['security'] = sxp.from_string(self.info['security'])
+
if self.info['memory'] == 0:
if self.infoIsSet('mem_kb'):
self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
@@ -668,6 +673,20 @@ class XendDomainInfo:
if self.infoIsSet('image'):
to_store['image'] = sxp.to_string(self.info['image'])
+ if self.infoIsSet('security'):
+ security = self.info['security']
+ to_store['security'] = sxp.to_string(security)
+ for idx in range(0, len(security)):
+ if security[idx][0] == 'access_control':
+ to_store['security/access_control'] = sxp.to_string([
security[idx][1] , security[idx][2] ])
+ for aidx in range(1, len(security[idx])):
+ if security[idx][aidx][0] == 'label':
+ to_store['security/access_control/label'] =
security[idx][aidx][1]
+ if security[idx][aidx][0] == 'policy':
+ to_store['security/access_control/policy'] =
security[idx][aidx][1]
+ if security[idx][0] == 'ssidref':
+ to_store['security/ssidref'] = str(security[idx][1])
+
log.debug("Storing VM details: %s", to_store)
self.writeVm(to_store)
@@ -760,9 +779,8 @@ class XendDomainInfo:
self.storeVm('vcpu_avail', self.info['vcpu_avail'])
self.writeDom(self.vcpuDomDetails())
-
- def getSsidref(self):
- return self.info['ssidref']
+ def getLabel(self):
+ return security.get_security_info(self.info, 'label')
def getMemoryTarget(self):
"""Get this domain's target memory size, in KB."""
@@ -954,12 +972,21 @@ class XendDomainInfo:
"""
log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
-
if not info:
info = dom_get(self.domid)
if not info:
return
+ #manually update ssidref / security fields
+ if security.on() and info.has_key('ssidref'):
+ if (info['ssidref'] != 0) and self.info.has_key('security'):
+ security_field = self.info['security']
+ if not security_field:
+ #create new security element
+ self.info.update({'security': [['ssidref',
str(info['ssidref'])]]})
+ #ssidref field not used any longer
+ info.pop('ssidref')
+
self.info.update(info)
self.validateInfo()
self.refreshShutdown(info)
@@ -996,7 +1023,6 @@ class XendDomainInfo:
s += " id=" + str(self.domid)
s += " name=" + self.info['name']
s += " memory=" + str(self.info['memory'])
- s += " ssidref=" + str(self.info['ssidref'])
s += ">"
return s
@@ -1058,6 +1084,9 @@ class XendDomainInfo:
if self.infoIsSet('image'):
sxpr.append(['image', self.info['image']])
+ if self.infoIsSet('security'):
+ sxpr.append(['security', self.info['security']])
+
for cls in controllerClasses:
for config in self.getDeviceConfigurations(cls):
sxpr.append(['device', config])
@@ -1159,12 +1188,11 @@ class XendDomainInfo:
@raise: VmError on error
"""
- log.debug('XendDomainInfo.construct: %s %s',
- self.domid,
- self.info['ssidref'])
+ log.debug('XendDomainInfo.construct: %s',
+ self.domid)
self.domid = xc.domain_create(
- dom = 0, ssidref = self.info['ssidref'],
+ dom = 0, ssidref = security.get_security_info(self.info,
'ssidref'),
handle = uuid.fromString(self.info['uuid']))
if self.domid < 0:
Index: xen-unstable.hg-shype/tools/python/xen/xm/create.py
===================================================================
--- xen-unstable.hg-shype.orig/tools/python/xen/xm/create.py
+++ xen-unstable.hg-shype/tools/python/xen/xm/create.py
@@ -35,6 +35,7 @@ import xen.xend.XendClient
from xen.xend.XendClient import server
from xen.xend.XendBootloader import bootloader
from xen.util import blkif
+from xen.util import security
from xen.xm.opts import *
@@ -145,10 +146,6 @@ gopts.var('memory', val='MEMORY',
fn=set_int, default=128,
use="Domain memory in MB.")
-gopts.var('ssidref', val='SSIDREF',
- fn=set_u32, default=0,
- use="Security Identifier.")
-
gopts.var('maxmem', val='MEMORY',
fn=set_int, default=None,
use="Maximum domain memory in MB.")
@@ -293,6 +290,14 @@ gopts.var('vtpm', val="instance=INSTANCE
number can be found in /etc/xen/vtpm.db. Use the backend in the
given domain.""")
+gopts.var('access_control', val="policy=POLICY,label=LABEL",
+ fn=append_value, default=[],
+ use="""Add a security label and the security policy reference that
defines it.
+ The local ssid reference is calculated when starting/resuming the
domain. At
+ this time, the policy is checked against the active policy as well.
This way,
+ migrating through save/restore is covered and local labels are
automatically
+ created correctly on the system where a domain is started /
resumed.""")
+
gopts.var('nics', val="NUM",
fn=set_int, default=-1,
use="""DEPRECATED. Use empty vif entries instead.
@@ -502,6 +507,43 @@ def configure_usb(config_devs, vals):
config_usb = ['usb', ['path', path]]
config_devs.append(['device', config_usb])
+
+def configure_security(config, vals):
+ """Create the config for ACM security labels.
+ """
+ access_control = vals.access_control
+ num = len(access_control)
+ if num == 1:
+ d = access_control[0]
+ policy = d.get('policy')
+ label = d.get('label')
+ if policy != security.active_policy:
+ err("Security policy (" + policy + ") incompatible with enforced
policy ("
+ + security.active_policy + ")." )
+ config_access_control = ['access_control',
+ ['policy', policy],
+ ['label', label] ]
+
+ #ssidref cannot be specified together with access_control
+ if sxp.child_value(config, 'ssidref'):
+ err("ERROR: SSIDREF and access_control are mutually exclusive but
both specified!")
+ #else calculate ssidre from label
+ ssidref = security.label2ssidref(label, policy)
+ if not ssidref :
+ err("ERROR calculating ssidref from access_control.")
+ security_label = ['security', [ config_access_control, ['ssidref' ,
ssidref ] ] ]
+ config.append(security_label)
+ elif num == 0:
+ if hasattr(vals, 'ssidref'):
+ if not security.on():
+ err("ERROR: Security ssidref specified but no policy active.")
+ ssidref = getattr(vals, 'ssidref')
+ security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
+ config.append(security_label)
+ elif num > 1:
+ err("VM config error: Multiple access_control definitions!")
+
+
def configure_vtpm(config_devs, vals):
"""Create the config for virtual TPM interfaces.
"""
@@ -595,9 +637,9 @@ def make_config(vals):
if v:
config.append([n, v])
- map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
- 'on_poweroff', 'on_reboot', 'on_crash', 'vcpus'])
-
+ map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff',
+ 'on_reboot', 'on_crash', 'vcpus'])
+
if vals.uuid is not None:
config.append(['uuid', vals.uuid])
if vals.cpu is not None:
@@ -628,6 +670,7 @@ def make_config(vals):
configure_vifs(config_devs, vals)
configure_usb(config_devs, vals)
configure_vtpm(config_devs, vals)
+ configure_security(config, vals)
config += config_devs
return config
@@ -696,6 +739,29 @@ def preprocess_vtpm(vals):
vtpms.append(d)
vals.vtpm = vtpms
+def preprocess_access_control(vals):
+ if not vals.access_control:
+ return
+ access_controls = []
+ num = len(vals.access_control)
+ if num == 1:
+ access_control = (vals.access_control)[0]
+ d = {}
+ a = access_control.split(',')
+ if len(a) > 2:
+ err('Too many elements in access_control specifier: ' +
access_control)
+ for b in a:
+ (k, v) = b.strip().split('=', 1)
+ k = k.strip()
+ v = v.strip()
+ if k not in ['policy','label']:
+ err('Invalid access_control specifier: ' + access_control)
+ d[k] = v
+ access_controls.append(d)
+ vals.access_control = access_controls
+ elif num > 1:
+ err('Multiple access_control definitions.')
+
def preprocess_ip(vals):
if vals.ip or vals.dhcp != 'off':
dummy_nfs_server = '1.2.3.4'
@@ -785,6 +851,7 @@ def preprocess(vals):
preprocess_nfs(vals)
preprocess_vnc(vals)
preprocess_vtpm(vals)
+ preprocess_access_control(vals)
def comma_sep_kv_to_dict(c):
Index: xen-unstable.hg-shype/tools/python/xen/xm/main.py
===================================================================
--- xen-unstable.hg-shype.orig/tools/python/xen/xm/main.py
+++ xen-unstable.hg-shype/tools/python/xen/xm/main.py
@@ -40,6 +40,7 @@ from xen.xm.opts import *
import console
import xen.xend.XendClient
from xen.xend.XendClient import server
+from xen.util import security
# getopt.gnu_getopt is better, but only exists in Python 2.3+. Use
# getopt.getopt if gnu_getopt is not available. This will mean that options
@@ -55,6 +56,8 @@ create_help = """create [-c] <ConfigFil
destroy_help = "destroy <DomId> Terminate a domain
immediately"
help_help = "help Display this message"
list_help = "list [--long] [DomId, ...] List information about
domains"
+list_label_help = "list [--label] [DomId, ...] List information about
domains including their labels"
+
mem_max_help = "mem-max <DomId> <Mem> Set maximum memory
reservation for a domain"
mem_set_help = "mem-set <DomId> <Mem> Adjust the current memory
usage for a domain"
migrate_help = "migrate <DomId> <Host> Migrate a domain to another
machine"
@@ -114,6 +117,12 @@ vnet_list_help = "vnet-list [-l|--long]
vnet_create_help = "vnet-create <config> create a vnet from a
config file"
vnet_delete_help = "vnet-delete <vnetid> delete a vnet"
vtpm_list_help = "vtpm-list <DomId> [--long] list virtual TPM devices"
+addlabel_help = "addlabel <ConfigFile> <label> Add security label to
ConfigFile"
+cfgbootpolicy_help = "cfgbootpolicy <policy> Add policy to boot
configuration "
+dumppolicy_help = "dumppolicy Print hypervisor ACM state
information"
+loadpolicy_help = "loadpolicy <policy> Load binary policy into
hypervisor"
+makepolicy_help = "makepolicy <policy> Build policy and create
.bin/.map files"
+labels_help = "labels [policy] [type=DOM|..] List <type> labels for
(active) policy."
short_command_list = [
"console",
@@ -140,6 +149,7 @@ domain_commands = [
"domid",
"domname",
"list",
+ "list_label",
"mem-max",
"mem-set",
"migrate",
@@ -185,8 +195,17 @@ vnet_commands = [
"vnet-delete",
]
+acm_commands = [
+ "labels",
+ "addlabel",
+ "makepolicy",
+ "loadpolicy",
+ "cfgbootpolicy",
+ "dumppolicy"
+ ]
+
all_commands = (domain_commands + host_commands + scheduler_commands +
- device_commands + vnet_commands)
+ device_commands + vnet_commands + acm_commands)
def commandToHelp(cmd):
@@ -225,6 +244,9 @@ xm full list of subcommands:
Vnet commands:
""" + help_spacer.join(map(commandToHelp, vnet_commands)) + """
+ Access Control commands:
+ """ + help_spacer.join(map(commandToHelp, acm_commands)) + """
+
<DomName> can be substituted for <DomId> in xm subcommands.
For a short list of subcommands run 'xm help'
@@ -332,8 +354,9 @@ def getDomains(domain_names):
def xm_list(args):
use_long = 0
show_vcpus = 0
+ show_labels = 0
try:
- (options, params) = getopt.gnu_getopt(args, 'lv', ['long','vcpus'])
+ (options, params) = getopt.gnu_getopt(args, 'lv',
['long','vcpus','label'])
except getopt.GetoptError, opterr:
err(opterr)
sys.exit(1)
@@ -343,6 +366,8 @@ def xm_list(args):
use_long = 1
if k in ['-v', '--vcpus']:
show_vcpus = 1
+ if k in ['--label']:
+ show_labels = 1
if show_vcpus:
print >>sys.stderr, (
@@ -354,6 +379,8 @@ def xm_list(args):
if use_long:
map(PrettyPrint.prettyprint, doms)
+ elif show_labels:
+ xm_label_list(doms)
else:
xm_brief_list(doms)
@@ -369,7 +396,7 @@ def parse_doms_info(info):
'vcpus' : get_info('online_vcpus', int, 0),
'state' : get_info('state', str, '??'),
'cpu_time' : get_info('cpu_time', float, 0),
- 'ssidref' : get_info('ssidref', int, 0),
+ 'seclabel' : security.get_security_printlabel(info),
}
@@ -391,13 +418,29 @@ def xm_brief_list(doms):
print 'Name ID Mem(MiB) VCPUs State Time(s)'
for dom in doms:
d = parse_doms_info(dom)
- if (d['ssidref'] != 0):
- d['ssidstr'] = (" s:%04x/p:%04x" %
- ((d['ssidref'] >> 16) & 0xffff,
- d['ssidref'] & 0xffff))
+ print ("%(name)-32s %(dom)3d %(mem)8d %(vcpus)5d %(state)5s
%(cpu_time)7.1f" % d)
+
+
+def xm_label_list(doms):
+ output = []
+ print 'Name ID Mem(MiB) VCPUs State Time(s)
Label'
+ for dom in doms:
+ d = parse_doms_info(dom)
+ l = "%(name)-32s %(dom)3d %(mem)8d %(vcpus)5d %(state)5s
%(cpu_time)7.1f " % d
+ if security.active_policy not in ['INACTIVE', 'NULL', 'DEFAULT']:
+ if d['seclabel']:
+ line = (l, d['seclabel'])
+ else:
+ line = (l, "ERROR")
+ elif security.active_policy in ['DEFAULT']:
+ line = (l, "DEFAULT")
else:
- d['ssidstr'] = ""
- print ("%(name)-32s %(dom)3d %(mem)8d %(vcpus)5d %(state)5s
%(cpu_time)7.1f%(ssidstr)s" % d)
+ line = (l, "INACTIVE")
+ output.append(line)
+ #sort by labels
+ output.sort(lambda x,y: cmp( x[1].lower(), y[1].lower()))
+ for l in output:
+ print l[0] + l[1]
def xm_vcpu_list(args):
@@ -1010,7 +1053,13 @@ subcommands = [
'create',
'migrate',
'sysrq',
- 'shutdown'
+ 'shutdown',
+ 'labels',
+ 'addlabel',
+ 'cfgbootpolicy',
+ 'makepolicy',
+ 'loadpolicy',
+ 'dumppolicy'
]
for c in subcommands:
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|