# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Date 1183989104 -3600
# Node ID aa640601575fb4b509befd9f032f0f3d577a46fc
# Parent 83fd4ad219cd321024c6b5f01b85464cd64faf2d
[Xen-API] Extension of the Xen-API for managing Xen Security Policies
This patch implements extensions for managing security policies in
xend. The XSPolicy and ACMPolicy classes provide the interface for the
Xen-API and implement functionality for setting, updating and
activating of a Xen security policy as well as labeling of virtual
machines and resources such as block devices. Labeling of network
devices will follow.
The acmpolicy class implements a compiler for translating an XML
policy into their binary format and provides functionality for
comparing a current policy against a new one when changing/updating a
policy.
The xspolicyadmin class administers the policy of the system.
Some of the xend-internal code deals with transforming the labeling
information from the S-Expression format into the new Xen-API
format. This is similar to much of the other code that is already
there.
Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxx>
---
tools/python/xen/util/acmpolicy.py | 1199 ++++++++++++++++++++++++++++
tools/python/xen/util/bootloader.py | 521 ++++++++++++
tools/python/xen/util/security.py | 791 ++++++++++++++++--
tools/python/xen/util/xsconstants.py | 104 ++
tools/python/xen/util/xspolicy.py | 66 +
tools/python/xen/xend/XendAPI.py | 66 +
tools/python/xen/xend/XendConfig.py | 36
tools/python/xen/xend/XendDomain.py | 3
tools/python/xen/xend/XendDomainInfo.py | 180 +++-
tools/python/xen/xend/XendError.py | 18
tools/python/xen/xend/XendVDI.py | 12
tools/python/xen/xend/XendXSPolicy.py | 222 +++++
tools/python/xen/xend/XendXSPolicyAdmin.py | 313 +++++++
tools/python/xen/xend/server/blkif.py | 15
tools/security/policies/security_policy.xsd | 29
15 files changed, 3414 insertions(+), 161 deletions(-)
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/util/acmpolicy.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/util/acmpolicy.py Mon Jul 09 14:51:44 2007 +0100
@@ -0,0 +1,1199 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2006,2007 International Business Machines Corp.
+# Author: Stefan Berger <stefanb@xxxxxxxxxx>
+#============================================================================
+
+import os
+import commands
+import struct
+import stat
+import array
+from xml.dom import minidom, Node
+from xen.xend.XendLogging import log
+from xen.util import security, xsconstants, bootloader, mkdir
+from xen.util.xspolicy import XSPolicy
+from xen.util.security import ACMError
+from xen.xend.XendError import SecurityError
+
+ACM_POLICIES_DIR = security.policy_dir_prefix + "/"
+
+# Constants needed for generating a binary policy from its XML
+# representation
+ACM_POLICY_VERSION = 3 # Latest one
+ACM_CHWALL_VERSION = 1
+
+ACM_STE_VERSION = 1
+
+ACM_MAGIC = 0x001debc;
+
+ACM_NULL_POLICY = 0
+ACM_CHINESE_WALL_POLICY = 1
+ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY = 2
+ACM_POLICY_UNDEFINED = 15
+
+
+ACM_SCHEMA_FILE = "/etc/xen/acm-security/policies/security_policy.xsd"
+
+class ACMPolicy(XSPolicy):
+ """
+ ACMPolicy class. Implements methods for getting information from
+ the XML representation of the policy as well as compilation and
+ loading of a policy into the HV.
+ """
+
+ def __init__(self, name=None, dom=None, ref=None, xml=None):
+ if name:
+ self.name = name
+ self.dom = minidom.parse(self.path_from_policy_name(name))
+ elif dom:
+ self.dom = dom
+ self.name = self.get_name()
+ elif xml:
+ self.dom = minidom.parseString(xml)
+ self.name = self.get_name()
+ rc = self.validate()
+ if rc != xsconstants.XSERR_SUCCESS:
+ raise SecurityError(rc)
+ mkdir.parents(ACM_POLICIES_DIR, stat.S_IRWXU)
+ if ref:
+ from xen.xend.XendXSPolicy import XendACMPolicy
+ self.xendacmpolicy = XendACMPolicy(self, {}, ref)
+ else:
+ self.xendacmpolicy = None
+ XSPolicy.__init__(self, name=self.name, ref=ref)
+
+ def get_dom(self):
+ return self.dom
+
+ def get_name(self):
+ return self.policy_dom_get_hdr_item("PolicyName")
+
+ def get_type(self):
+ return xsconstants.XS_POLICY_ACM
+
+ def get_type_name(self):
+ return xsconstants.ACM_POLICY_ID
+
+ def __str__(self):
+ return self.get_name()
+
+
+ def validate(self):
+ """
+ validate against the policy's schema Does not fail if the
+ libxml2 python lib is not installed
+ """
+ rc = xsconstants.XSERR_SUCCESS
+ try:
+ import libxml2
+ except Exception, e:
+ log.warn("Libxml2 python-wrapper is not installed on the system.")
+ return xsconstants.XSERR_SUCCESS
+ try:
+ parserctxt = libxml2.schemaNewParserCtxt(ACM_SCHEMA_FILE)
+ schemaparser = parserctxt.schemaParse()
+ valid = schemaparser.schemaNewValidCtxt()
+ doc = libxml2.parseDoc(self.toxml())
+ if doc.schemaValidateDoc(valid) != 0:
+ rc = -xsconstants.XSERR_BAD_XML
+ except Exception, e:
+ log.warn("Problem with the schema: %s" % str(e))
+ rc = -xsconstants.XSERR_GENERAL_FAILURE
+ if rc != xsconstants.XSERR_SUCCESS:
+ log.warn("XML did not validate against schema")
+ rc = self.__validate_name_and_labels()
+ return rc
+
+ def __validate_name_and_labels(self):
+ """ no ':' allowed in the policy name and the labels """
+ if ':' in self.get_name():
+ return -xsconstants.XSERR_BAD_POLICY_NAME
+ for s in self.policy_get_resourcelabel_names():
+ if ':' in s:
+ return -xsconstants.XSERR_BAD_LABEL
+ for s in self.policy_get_virtualmachinelabel_names():
+ if ':' in s:
+ return -xsconstants.XSERR_BAD_LABEL
+ return xsconstants.XSERR_SUCCESS
+
+
+ def update(self, xml_new):
+ """
+ Update the policy with the new XML. The hypervisor decides
+ whether the new policy can be applied.
+ """
+ rc = -xsconstants.XSERR_XML_PROCESSING
+ errors = ""
+ acmpol_old = self
+ try:
+ acmpol_new = ACMPolicy(xml=xml_new)
+ except Exception:
+ return -xsconstants.XSERR_XML_PROCESSING, errors
+
+ vmlabel_map = acmpol_new.policy_get_vmlabel_translation_map()
+ # An update requires version information in the current
+ # and new policy. The version number of the current policy
+ # must be the same as what is in the FromPolicy/Version node
+ # in the new one and the current policy's name must be the
+ # same as in FromPolicy/PolicyName
+
+ now_vers = acmpol_old.policy_dom_get_hdr_item("Version")
+ now_name = acmpol_old.policy_dom_get_hdr_item("PolicyName")
+ req_oldvers = acmpol_new.policy_dom_get_frompol_item("Version")
+ req_oldname = acmpol_new.policy_dom_get_frompol_item("PolicyName")
+
+ if now_vers == "" or \
+ now_vers != req_oldvers or \
+ now_name != req_oldname:
+ log.info("Policy rejected: %s != %s or %s != %s" % \
+ (now_vers,req_oldvers,now_name,req_oldname))
+ return -xsconstants.XSERR_VERSION_PREVENTS_UPDATE, errors
+
+ if not self.isVersionUpdate(acmpol_new):
+ log.info("Policy rejected since new version is not an update.")
+ return -xsconstants.XSERR_VERSION_PREVENTS_UPDATE, errors
+
+ if self.isloaded():
+ newvmnames = \
+ acmpol_new.policy_get_virtualmachinelabel_names_sorted()
+ oldvmnames = \
+ acmpol_old.policy_get_virtualmachinelabel_names_sorted()
+ del_array = ""
+ chg_array = ""
+ for o in oldvmnames:
+ if o not in newvmnames:
+ old_idx = oldvmnames.index(o) + 1 # for _NULL_LABEL_
+ if vmlabel_map.has_key(o):
+ #not a deletion, but a renaming
+ new = vmlabel_map[o]
+ new_idx = newvmnames.index(new) + 1 # for _NULL_LABEL_
+ chg_array += struct.pack("ii", old_idx, new_idx)
+ else:
+ del_array += struct.pack("i", old_idx)
+ for v in newvmnames:
+ if v in oldvmnames:
+ old_idx = oldvmnames.index(v) + 1 # for _NULL_LABEL_
+ new_idx = newvmnames.index(v) + 1 # for _NULL_LABEL_
+ if old_idx != new_idx:
+ chg_array += struct.pack("ii", old_idx, new_idx)
+
+ # VM labels indicated in the 'from' attribute of a VM or
+ # resource node but that did not exist in the old policy
+ # are considered bad labels.
+ bad_renamings = set(vmlabel_map.keys()) - set(oldvmnames)
+ if len(bad_renamings) > 0:
+ log.error("Bad VM label renamings: %s" %
+ list(bad_renamings))
+ return -xsconstants.XSERR_BAD_LABEL, errors
+
+ reslabel_map = acmpol_new.policy_get_reslabel_translation_map()
+ oldresnames = acmpol_old.policy_get_resourcelabel_names()
+ bad_renamings = set(reslabel_map.keys()) - set(oldresnames)
+ if len(bad_renamings) > 0:
+ log.error("Bad resource label renamings: %s" %
+ list(bad_renamings))
+ return -xsconstants.XSERR_BAD_LABEL, errors
+
+ #Get binary and map from the new policy
+ rc, map, bin_pol = acmpol_new.policy_create_map_and_bin()
+ if rc != xsconstants.XSERR_SUCCESS:
+ log.error("Could not build the map and binary policy.")
+ return rc, errors
+
+ #Need to do / check the following:
+ # - relabel all resources where there is a 'from' field in
+ # the policy and mark those as unlabeled where the label
+ # does not appear in the new policy anymore
+ # - relabel all VMs where there is a 'from' field in the
+ # policy and mark those as unlabeled where the label
+ # does not appear in the new policy anymore; no running
+ # or paused VM may be unlabeled through this
+ # - check that under the new labeling conditions the VMs
+ # still have access to their resources as before. Unlabeled
+ # resources are inaccessible. If this check fails, the
+ # update failed.
+ # - Attempt changes in the hypervisor; if this step fails,
+ # roll back the relabeling of resources and VMs
+ # - Commit the relabeling of resources
+
+
+ rc, errors = security.change_acm_policy(bin_pol,
+ del_array, chg_array,
+ vmlabel_map, reslabel_map,
+ self, acmpol_new)
+
+ if rc == 0:
+ # Replace the old DOM with the new one and save it
+ self.dom = acmpol_new.dom
+ self.compile()
+ log.info("ACM policy update was successful")
+ else:
+ #Not loaded in HV
+ self.dom = acmpol_new.dom
+ self.compile()
+ return rc, errors
+
+ def compareVersions(self, v1, v2):
+ """
+ Compare two policy versions given their tuples of major and
+ minor.
+ Return '0' if versions are equal, '>0' if v1 > v2 and
+ '<' if v1 < v2
+ """
+ rc = v1[0] - v2[0]
+ if rc == 0:
+ rc = v1[1] - v2[1]
+ return rc
+
+ def getVersionTuple(self, item="Version"):
+ v_str = self.policy_dom_get_hdr_item(item)
+ return self.__convVersionToTuple(v_str)
+
+ def get_version(self):
+ return self.policy_dom_get_hdr_item("Version")
+
+ def isVersionUpdate(self, polnew):
+ if self.compareVersions(polnew.getVersionTuple(),
+ self.getVersionTuple()) > 0:
+ return True
+ return False
+
+ def __convVersionToTuple(self, v_str):
+ """ Convert a version string, formatted according to the scheme
+ "%d.%d" into a tuple of (major, minor). Return (0,0) if the
+ string is empty.
+ """
+ major = 0
+ minor = 0
+ if v_str != "":
+ tmp = v_str.split(".")
+ major = int(tmp[0])
+ if len(tmp) > 1:
+ minor = int(tmp[1])
+ return (major, minor)
+
+
+ def policy_path(self, name, prefix = ACM_POLICIES_DIR ):
+ path = prefix + name.replace('.','/')
+ _path = path.split("/")
+ del _path[-1]
+ mkdir.parents("/".join(_path), stat.S_IRWXU)
+ return path
+
+ def path_from_policy_name(self, name):
+ return self.policy_path(name) + "-security_policy.xml"
+
+ #
+ # Functions interacting with the bootloader
+ #
+ def vmlabel_to_ssidref(self, vm_label):
+ """ Convert a VMlabel into an ssidref given the current
+ policy
+ Return xsconstants.INVALID_SSIDREF if conversion failed.
+ """
+ ssidref = xsconstants.INVALID_SSIDREF
+ names = self.policy_get_virtualmachinelabel_names_sorted()
+ try:
+ vmidx = names.index(vm_label) + 1 # for _NULL_LABEL_
+ ssidref = (vmidx << 16) | vmidx
+ except:
+ pass
+ return ssidref
+
+ def set_vm_bootlabel(self, vm_label):
+ parms="<>"
+ if vm_label != "":
+ ssidref = self.vmlabel_to_ssidref(vm_label)
+ if ssidref == xsconstants.INVALID_SSIDREF:
+ return -xsconstants.XSERR_BAD_LABEL
+ parms = "0x%08x:%s:%s:%s" % \
+ (ssidref, xsconstants.ACM_POLICY_ID, \
+ self.get_name(),vm_label)
+ else:
+ ssidref = 0 #Identifier for removal
+ try:
+ def_title = bootloader.get_default_title()
+ bootloader.set_kernel_attval(def_title, "ssidref", parms)
+ except:
+ return -xsconstants.XSERR_GENERAL_FAILURE
+ return ssidref
+
+ #
+ # Utility functions related to the policy's files
+ #
+ def get_filename(self, postfix, prefix = ACM_POLICIES_DIR, dotted=False):
+ """
+ Create the filename for the policy. The prefix is prepended
+ to the path. If dotted is True, then a policy name like
+ 'a.b.c' will remain as is, otherwise it will become 'a/b/c'
+ """
+ name = self.get_name()
+ if name:
+ p = name.split(".")
+ path = ""
+ if dotted == True:
+ sep = "."
+ else:
+ sep = "/"
+ if len(p) > 1:
+ path = sep.join(p[0:len(p)-1])
+ if prefix != "" or path != "":
+ allpath = prefix + path + sep + p[-1] + postfix
+ else:
+ allpath = p[-1] + postfix
+ return allpath
+ return None
+
+ def __readfile(self, name):
+ cont = ""
+ filename = self.get_filename(name)
+ f = open(filename, "r")
+ if f:
+ cont = f.read()
+ f.close()
+ return cont
+
+ def get_map(self):
+ return self.__readfile(".map")
+
+ def get_bin(self):
+ return self.__readfile(".bin")
+
+ #
+ # DOM-related functions
+ #
+
+ def policy_dom_get(self, parent, key, createit=False):
+ for node in parent.childNodes:
+ if node.nodeType == Node.ELEMENT_NODE:
+ if node.nodeName == key:
+ return node
+ if createit:
+ self.dom_create_node(parent, key)
+ return self.policy_dom_get(parent, key)
+
+ def dom_create_node(self, parent, newname, value=" "):
+ xml = "<a><"+newname+">"+ value +"</"+newname+"></a>"
+ frag = minidom.parseString(xml)
+ frag.childNodes[0].nodeType = Node.DOCUMENT_FRAGMENT_NODE
+ parent.appendChild(frag.childNodes[0])
+ return frag.childNodes[0]
+
+ def dom_get_node(self, path, createit=False):
+ node = None
+ parts = path.split("/")
+ doc = self.get_dom()
+ if len(parts) > 0:
+ node = self.policy_dom_get(doc.documentElement, parts[0])
+ if node:
+ i = 1
+ while i < len(parts):
+ _node = self.policy_dom_get(node, parts[i], createit)
+ if not _node:
+ if not createit:
+ break
+ else:
+ self.dom_create_node(node, parts[i])
+ _node = self.policy_dom_get(node, parts[i])
+ node = _node
+ i += 1
+ return node
+
+ #
+ # Header-related functions
+ #
+ def policy_dom_get_header_subnode(self, nodename):
+ node = self.dom_get_node("PolicyHeader/%s" % nodename)
+ return node
+
+ def policy_dom_get_hdr_item(self, name, default=""):
+ node = self.policy_dom_get_header_subnode(name)
+ if node and len(node.childNodes) > 0:
+ return node.childNodes[0].nodeValue
+ return default
+
+ def policy_dom_get_frompol_item(self, name, default="", createit=False):
+ node = self.dom_get_node("PolicyHeader/FromPolicy",createit)
+ if node:
+ node = self.policy_dom_get(node, name, createit)
+ if node and len(node.childNodes) > 0:
+ return node.childNodes[0].nodeValue
+ return default
+
+ def get_header_fields_map(self):
+ header = {
+ 'policyname' : self.policy_dom_get_hdr_item("PolicyName"),
+ 'policyurl' : self.policy_dom_get_hdr_item("PolicyUrl"),
+ 'reference' : self.policy_dom_get_hdr_item("Reference"),
+ 'date' : self.policy_dom_get_hdr_item("Date"),
+ 'namespaceurl' : self.policy_dom_get_hdr_item("NameSpaceUrl"),
+ 'version' : self.policy_dom_get_hdr_item("Version")
+ }
+ return header
+
+ def set_frompolicy_name(self, name):
+ """ For tools to adapt the header of the policy """
+ node = self.dom_get_node("PolicyHeader/FromPolicy/PolicyName",
+ createit=True)
+ node.childNodes[0].nodeValue = name
+
+ def set_frompolicy_version(self, version):
+ """ For tools to adapt the header of the policy """
+ node = self.dom_get_node("PolicyHeader/FromPolicy/Version",
+ createit=True)
+ node.childNodes[0].nodeValue = version
+
+ def set_policy_name(self, name):
+ """ For tools to adapt the header of the policy """
+ node = self.dom_get_node("PolicyHeader/PolicyName")
+ node.childNodes[0].nodeValue = name
+
+ def set_policy_version(self, version):
+ """ For tools to adapt the header of the policy """
+ node = self.dom_get_node("PolicyHeader/Version")
+ node.childNodes[0].nodeValue = version
+
+ def update_frompolicy(self, curpol):
+ self.set_frompolicy_name(curpol.policy_dom_get_hdr_item("PolicyName"))
+ version = curpol.policy_dom_get_hdr_item("Version")
+ self.set_frompolicy_version(version)
+ (maj, min) = self.__convVersionToTuple(version)
+ self.set_policy_version("%s.%s" % (maj, min+1))
+
+ #
+ # Get all types that are part of a node
+ #
+
+ def policy_get_types(self, node):
+ strings = []
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == "Type":
+ strings.append(node.childNodes[i].childNodes[0].nodeValue)
+ i += 1
+ return strings
+
+ #
+ # Simple Type Enforcement-related functions
+ #
+
+ def policy_get_stetypes_node(self):
+ node =
self.dom_get_node("SimpleTypeEnforcement/SimpleTypeEnforcementTypes")
+ return node
+
+ def policy_get_stetypes_types(self):
+ strings = []
+ node = self.policy_get_stetypes_node()
+ if node:
+ strings = self.policy_get_types(node)
+ return strings
+
+ #
+ # Chinese Wall Type-related functions
+ #
+
+ def policy_get_chwall_types(self):
+ strings = []
+ node = self.dom_get_node("ChineseWall/ChineseWallTypes")
+ if node:
+ strings = self.policy_get_types(node)
+ return strings
+
+ def policy_get_chwall_cfses(self):
+ cfs = []
+ node = self.dom_get_node("ChineseWall/ConflictSets")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ _cfs = {}
+ if node.childNodes[i].nodeName == "Conflict":
+ _cfs['name'] = node.childNodes[i].getAttribute('name')
+ _cfs['chws'] = self.policy_get_types(node.childNodes[i])
+ cfs.append(_cfs)
+ i += 1
+ return cfs
+
+ def policy_get_chwall_cfses_names_sorted(self):
+ """
+ Return the list of all conflict set names in alphabetical
+ order.
+ """
+ cfs_names = []
+ node = self.dom_get_node("ChineseWall/ConflictSets")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == "Conflict":
+ n = node.childNodes[i].getAttribute('name')
+ #it better have a name!
+ if n:
+ cfs_names.append(n)
+ i += 1
+ cfs_names.sort()
+ return cfs_names
+
+ #
+ # Subject Label-related functions
+ #
+
+ def policy_get_bootstrap_vmlabel(self):
+ node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels")
+ if node:
+ vmlabel = node.getAttribute("bootstrap")
+ return vmlabel
+
+ # Get the names of all virtual machine labels; returns an array
+ def policy_get_virtualmachinelabel_names(self):
+ strings = []
+ node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == "VirtualMachineLabel":
+ name = self.policy_dom_get(node.childNodes[i], "Name")
+ strings.append(name.childNodes[0].nodeValue)
+ i += 1
+ return strings
+
+ def policy_sort_virtualmachinelabel_names(self, vmnames):
+ bootstrap = self.policy_get_bootstrap_vmlabel()
+ if bootstrap not in vmnames:
+ raise SecurityError(-xsconstants.XSERR_POLICY_INCONSISTENT)
+ vmnames.remove(bootstrap)
+ vmnames.sort()
+ vmnames.insert(0, bootstrap)
+ return vmnames
+
+ def policy_get_virtualmachinelabel_names_sorted(self):
+ """ Get a sorted list of VMlabel names. The bootstrap VM's
+ label will be the first one in that list, followed
+ by an alphabetically sorted list of VM label names """
+ vmnames = self.policy_get_virtualmachinelabel_names()
+ return self.policy_sort_virtualmachinelabel_names(vmnames)
+
+ def policy_get_virtualmachinelabels(self):
+ """ Get a list of all virtual machine labels in this policy """
+ res = []
+ node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == "VirtualMachineLabel":
+ _res = {}
+ _res['type'] = xsconstants.ACM_LABEL_VM
+ name = self.policy_dom_get(node.childNodes[i], "Name")
+ _res['name'] = name.childNodes[0].nodeValue
+ stes = self.policy_dom_get(node.childNodes[i],
+ "SimpleTypeEnforcementTypes")
+ if stes:
+ _res['stes'] = self.policy_get_types(stes)
+ else:
+ _res['stes'] = []
+ chws = self.policy_dom_get(node.childNodes[i],
+ "ChineseWallTypes")
+ if chws:
+ _res['chws'] = self.policy_get_types(chws)
+ else:
+ _res['chws'] = []
+ res.append(_res)
+ i += 1
+ return res
+
+ def policy_get_stes_of_vmlabel(self, vmlabel):
+ """ Get a list of all STEs of a given VMlabel """
+ return self.__policy_get_stes_of_labeltype(vmlabel,
+ "VirtualMachineLabel")
+
+ def policy_get_stes_of_resource(self, reslabel):
+ """ Get a list of all resources of a given VMlabel """
+ return self.__policy_get_stes_of_labeltype(reslabel, "ResourceLabel")
+
+ def __policy_get_stes_of_labeltype(self, label, labeltype):
+ node = self.dom_get_node("SecurityLabelTemplate/SubjectLabels")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == labeltype:
+ name = self.policy_dom_get(node.childNodes[i], "Name")
+ if name.childNodes[0].nodeValue == label:
+ stes = self.policy_dom_get(node.childNodes[i],
+ "SimpleTypeEnforcementTypes")
+ if not stes:
+ return []
+ return self.policy_get_types(stes)
+ i += 1
+ return []
+
+ def policy_check_vmlabel_against_reslabels(self, vmlabel, resources):
+ """
+ Check whether the given vmlabel is compatible with the given
+ resource labels. Do this by getting all the STEs of the
+ vmlabel and the STEs of the resources. Any STE type of the
+ VM label must match an STE type of the resource.
+ """
+ vm_stes = self.policy_get_stes_of_vmlabel(vmlabel)
+ if len(vm_stes) == 0:
+ return False
+ for res in resources:
+ res_stes = self.policy_get_stes_of_resource(res)
+ if len( set(res_stes).union( set(vm_stes) ) ) == 0:
+ return False
+ return True
+
+ def __policy_get_label_translation_map(self, path, labeltype):
+ res = {}
+ node = self.dom_get_node("SecurityLabelTemplate/" + path)
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == labeltype:
+ name = self.policy_dom_get(node.childNodes[i], "Name")
+ from_name = name.getAttribute("from")
+ if from_name:
+ res.update({from_name : name.childNodes[0].nodeValue})
+ i += 1
+ return res
+
+ def policy_get_vmlabel_translation_map(self):
+ """
+ Get a dictionary of virtual machine mappings from their
+ old VMlabel name to the new VMlabel name.
+ """
+ return self.__policy_get_label_translation_map("SubjectLabels",
+ "VirtualMachineLabel")
+
+ def policy_get_reslabel_translation_map(self):
+ """
+ Get a dictionary of resource mappings from their
+ old resource label name to the new resource label name.
+ """
+ return self.__policy_get_label_translation_map("ObjectLabels",
+ "ResourceLabel")
+
+ #
+ # Object Label-related functions
+ #
+ def policy_get_resourcelabel_names(self):
+ """
+ Get the names of all resource labels in an array but
+ only those that actually have types
+ """
+ strings = []
+ node = self.dom_get_node("SecurityLabelTemplate/ObjectLabels")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == "ResourceLabel":
+ name = self.policy_dom_get(node.childNodes[i], "Name")
+ stes = self.policy_dom_get(node.childNodes[i],
+ "SimpleTypeEnforcementTypes")
+ if stes:
+ strings.append(name.childNodes[0].nodeValue)
+ i += 1
+ return strings
+
+ def policy_get_resourcelabels(self):
+ """
+ Get all information about all resource labels of this policy.
+ """
+ res = []
+ node = self.dom_get_node("SecurityLabelTemplate/ObjectLabels")
+ if node:
+ i = 0
+ while i < len(node.childNodes):
+ if node.childNodes[i].nodeName == "ResourceLabel":
+ _res = {}
+ _res['type'] = xsconstants.ACM_LABEL_RES
+ name = self.policy_dom_get(node.childNodes[i], "Name")
+ _res['name'] = name.childNodes[0].nodeValue
+ stes = self.policy_dom_get(node.childNodes[i],
+ "SimpleTypeEnforcementTypes")
+ if stes:
+ _res['stes'] = self.policy_get_types(stes)
+ else:
+ _res['stes'] = []
+ _res['chws'] = []
+ res.append(_res)
+ i += 1
+ return res
+
+
+ def policy_find_reslabels_with_stetype(self, stetype):
+ """
+ Find those resource labels that hold a given STE type.
+ """
+ res = []
+ reslabels = self.policy_get_resourcelabels()
+ for resl in reslabels:
+ if stetype in resl['stes']:
+ res.append(resl['name'])
+ return res
+
+
+ def toxml(self):
+ dom = self.get_dom()
+ if dom:
+ return dom.toxml()
+ return None
+
+ def save(self):
+ ### Save the XML policy into a file ###
+ rc = -xsconstants.XSERR_FILE_ERROR
+ name = self.get_name()
+ if name:
+ path = self.path_from_policy_name(name)
+ if path:
+ f = open(path, 'w')
+ if f:
+ f.write(self.toxml())
+ f.close()
+ rc = 0
+ return rc
+
+ def __write_to_file(self, suffix, data):
+ #write the data into a file with the given suffix
+ f = open(self.get_filename(suffix),"w")
+ if f:
+ try:
+ try:
+ f.write(data)
+ except Exception, e:
+ log.error("Error writing file: %s" % str(e))
+ return -xsconstants.XSERR_FILE_ERROR
+ finally:
+ f.close()
+ else:
+ return -xsconstants.XSERR_FILE_ERROR
+ return xsconstants.XSERR_SUCCESS
+
+
+ def compile(self):
+ rc = self.save()
+ if rc == 0:
+ rc, mapfile, bin_pol = self.policy_create_map_and_bin()
+
+ if rc == 0:
+ rc = self.__write_to_file(".map", mapfile)
+ if rc != 0:
+ log.error("Error writing map file")
+
+ if rc == 0:
+ rc = self.__write_to_file(".bin", bin_pol)
+ if rc != 0:
+ log.error("Error writing binary policy file")
+ return rc
+
+ def loadintohv(self):
+ """
+ load this policy into the hypervisor
+ if successful,the policy's flags will indicate that the
+ policy is the one loaded into the hypervisor
+ """
+ (ret, output) = commands.getstatusoutput(
+ security.xensec_tool +
+ " loadpolicy " +
+ self.get_filename(".bin"))
+ if ret != 0:
+ return -xsconstants.XSERR_POLICY_LOAD_FAILED
+ return xsconstants.XSERR_SUCCESS
+
+ def isloaded(self):
+ """
+ Determine whether this policy is the active one.
+ """
+ security.refresh_security_policy()
+ if self.get_name() == security.active_policy:
+ return True
+ return False
+
+ def destroy(self):
+ """
+ Destroy the policy including its binary, mapping and
+ XML files.
+ This only works if the policy is not the one that's loaded
+ """
+ if self.isloaded():
+ return -xsconstants.XSERR_POLICY_LOADED
+ files = [ self.get_filename(".map",""),
+ self.get_filename(".bin",""),
+ self.path_from_policy_name(self.get_name())]
+ for f in files:
+ try:
+ os.unlink(f)
+ except:
+ pass
+ if self.xendacmpolicy:
+ self.xendacmpolicy.destroy()
+ XSPolicy.destroy(self)
+ return xsconstants.XSERR_SUCCESS
+
+ def policy_get_domain_label(self, domid):
+ """
+ Given a domain's ID, retrieve the label it has using
+ its ssidref for reverse calculation.
+ """
+ try:
+ mgmt_dom = security.get_ssid(domid)
+ except:
+ return ""
+ return self.policy_get_domain_label_by_ssidref(int(mgmt_dom[3]))
+
+ def policy_get_domain_label_by_ssidref(self, ssidref):
+ """ Given an ssidref, find the corresponding VM label """
+ chwall_ref = ssidref & 0xffff
+ try:
+ allvmtypes = self.policy_get_virtualmachinelabel_names_sorted()
+ except:
+ return None
+ return allvmtypes[chwall_ref-1] # skip _NULL_LABEL_
+
+ def policy_get_domain_label_formatted(self, domid):
+ label = self.policy_get_domain_label(domid)
+ if label == "":
+ return ""
+ return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, self.get_name(), label)
+
+ def policy_get_domain_label_by_ssidref_formatted(self, ssidref):
+ label = self.policy_get_domain_label_by_ssidref(ssidref)
+ if label == "":
+ return ""
+ return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, self.get_name(), label)
+
+ def policy_create_map_and_bin(self):
+ """
+ Create the policy's map and binary files -- compile the policy.
+ """
+ def roundup8(len):
+ return ((len + 7) & ~7)
+
+ rc = xsconstants.XSERR_SUCCESS
+ mapfile = ""
+ primpolcode = ACM_POLICY_UNDEFINED
+ secpolcode = ACM_POLICY_UNDEFINED
+ unknown_ste = set()
+ unknown_chw = set()
+
+ rc = self.validate()
+ if rc:
+ return rc, "", ""
+
+ stes = self.policy_get_stetypes_types()
+ if stes:
+ stes.sort()
+
+ chws = self.policy_get_chwall_types()
+ if chws:
+ chws.sort()
+
+ vms = self.policy_get_virtualmachinelabels()
+ bootstrap = self.policy_get_bootstrap_vmlabel()
+
+ vmlabels = self.policy_get_virtualmachinelabel_names_sorted()
+ if bootstrap not in vmlabels:
+ log.error("Bootstrap label '%s' not found among VM labels '%s'." \
+ % (bootstrap, vmlabels))
+ return -xsconstants.XSERR_POLICY_INCONSISTENT, "", ""
+
+ vms_with_chws = []
+ chws_by_vm = {}
+ for v in vms:
+ if v.has_key("chws"):
+ vms_with_chws.append(v["name"])
+ chws_by_vm[v["name"]] = v["chws"]
+ if bootstrap in vms_with_chws:
+ vms_with_chws.remove(bootstrap)
+ vms_with_chws.sort()
+ vms_with_chws.insert(0, bootstrap)
+ else:
+ vms_with_chws.sort()
+
+ vms_with_stes = []
+ stes_by_vm = {}
+ for v in vms:
+ if v.has_key("stes"):
+ vms_with_stes.append(v["name"])
+ stes_by_vm[v["name"]] = v["stes"]
+ if bootstrap in vms_with_stes:
+ vms_with_stes.remove(bootstrap)
+ vms_with_stes.sort()
+ vms_with_stes.insert(0, bootstrap)
+ else:
+ vms_with_stes.sort()
+
+ resnames = self.policy_get_resourcelabel_names()
+ resnames.sort()
+ stes_by_res = {}
+ res = self.policy_get_resourcelabels()
+ for r in res:
+ if r.has_key("stes"):
+ stes_by_res[r["name"]] = r["stes"]
+
+ max_chw_ssids = 1 + len(vms_with_chws)
+ max_chw_types = 1 + len(vms_with_chws)
+ max_ste_ssids = 1 + len(vms_with_stes) + len(resnames)
+ max_ste_types = 1 + len(vms_with_stes) + len(resnames)
+
+ mapfile = "POLICYREFERENCENAME %s\n" % self.get_name()
+ mapfile += "MAGIC %08x\n" % ACM_MAGIC
+ mapfile += "POLICFILE %s\n" % \
+ self.path_from_policy_name(self.get_name())
+ mapfile += "BINARYFILE %s\n" % self.get_filename(".bin")
+ mapfile += "MAX-CHWALL-TYPES %08x\n" % len(chws)
+ mapfile += "MAX-CHWALL-SSIDS %08x\n" % max_chw_ssids
+ mapfile += "MAX-CHWALL-LABELS %08x\n" % max_chw_ssids
+ mapfile += "MAX-STE-TYPES %08x\n" % len(stes)
+ mapfile += "MAX-STE-SSIDS %08x\n" % max_ste_ssids
+ mapfile += "MAX-STE-LABELS %08x\n" % max_ste_ssids
+ mapfile += "\n"
+
+ if chws:
+ mapfile += \
+ "PRIMARY CHWALL\n"
+ primpolcode = ACM_CHINESE_WALL_POLICY
+ if stes:
+ mapfile += \
+ "SECONDARY STE\n"
+ else:
+ mapfile += \
+ "SECONDARY NULL\n"
+ secpolcode = ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY
+ else:
+ if stes:
+ mapfile += \
+ "PRIMARY STE\n"
+ primpolcode = ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY
+ mapfile += \
+ "SECONDARY NULL\n"
+
+ mapfile += "\n"
+
+ if len(vms_with_chws) > 0:
+ mapfile += \
+ "LABEL->SSID ANY CHWALL __NULL_LABEL__ %x\n" % 0
+ i = 0
+ for v in vms_with_chws:
+ mapfile += \
+ "LABEL->SSID VM CHWALL %-20s %x\n" % \
+ (v, i+1)
+ i += 1
+ mapfile += "\n"
+
+ if len(vms_with_stes) > 0 or len(resnames) > 0:
+ mapfile += \
+ "LABEL->SSID ANY STE __NULL_LABEL__ %08x\n" % 0
+ i = 0
+ for v in vms_with_stes:
+ mapfile += \
+ "LABEL->SSID VM STE %-20s %x\n" % (v, i+1)
+ i += 1
+ j = 0
+ for r in resnames:
+ mapfile += \
+ "LABEL->SSID RES STE %-20s %x\n" % (r, j+i+1)
+ j += 1
+ mapfile += "\n"
+
+ if vms_with_chws:
+ mapfile += \
+ "SSID->TYPE CHWALL %08x\n" % 0
+ i = 1
+ for v in vms_with_chws:
+ mapfile += \
+ "SSID->TYPE CHWALL %08x" % i
+ for c in chws_by_vm[v]:
+ mapfile += " %s" % c
+ mapfile += "\n"
+ i += 1
+ mapfile += "\n"
+
+ if len(vms_with_stes) > 0 or len(resnames) > 0:
+ mapfile += \
+ "SSID->TYPE STE %08x\n" % 0
+ i = 1
+ for v in vms_with_stes:
+ mapfile += \
+ "SSID->TYPE STE %08x" % i
+ for s in stes_by_vm[v]:
+ mapfile += " %s" % s
+ mapfile += "\n"
+ i += 1
+
+ for r in resnames:
+ mapfile += \
+ "SSID->TYPE STE %08x" % i
+ for s in stes_by_res[r]:
+ mapfile += " %s" % s
+ mapfile += "\n"
+ i += 1
+ mapfile += "\n"
+
+ if chws:
+ i = 0
+ while i < len(chws):
+ mapfile += \
+ "TYPE CHWALL %-20s %d\n" % (chws[i], i)
+ i += 1
+ mapfile += "\n"
+ if stes:
+ i = 0
+ while i < len(stes):
+ mapfile += \
+ "TYPE STE %-20s %d\n" % (stes[i], i)
+ i += 1
+ mapfile += "\n"
+
+ mapfile += "\n"
+
+ # Build header with policy name
+ length = roundup8(4 + len(self.get_name()) + 1)
+ polname = self.get_name();
+ pr_bin = struct.pack("!i", len(polname)+1)
+ pr_bin += polname;
+ while len(pr_bin) < length:
+ pr_bin += "\x00"
+
+ # Build chinese wall part
+ cfses_names = self.policy_get_chwall_cfses_names_sorted()
+ cfses = self.policy_get_chwall_cfses()
+
+ chwformat = "!iiiiiiiii"
+ max_chw_cfs = len(cfses)
+ chw_ssid_offset = struct.calcsize(chwformat)
+ chw_confset_offset = chw_ssid_offset + \
+ 2 * len(chws) * max_chw_types
+ chw_running_types_offset = 0
+ chw_conf_agg_offset = 0
+
+ chw_bin = struct.pack(chwformat,
+ ACM_CHWALL_VERSION,
+ ACM_CHINESE_WALL_POLICY,
+ len(chws),
+ max_chw_ssids,
+ max_chw_cfs,
+ chw_ssid_offset,
+ chw_confset_offset,
+ chw_running_types_offset,
+ chw_conf_agg_offset)
+ chw_bin_body = ""
+ # simulate __NULL_LABEL__
+ for c in chws:
+ chw_bin_body += struct.pack("!h",0)
+ # VMs that are listed and their chinese walls
+ for v in vms_with_chws:
+ for c in chws:
+ unknown_chw |= (set(chws_by_vm[v]) - set(chws))
+ if c in chws_by_vm[v]:
+ chw_bin_body += struct.pack("!h",1)
+ else:
+ chw_bin_body += struct.pack("!h",0)
+
+ # Conflict sets -- they need to be processed in alphabetical order
+ for cn in cfses_names:
+ if cn == "" or cn is None:
+ return -xsconstants.XSERR_BAD_CONFLICTSET, "", ""
+ i = 0
+ while i < len(cfses):
+ if cfses[i]['name'] == cn:
+ conf = cfses[i]['chws']
+ break
+ i += 1
+ for c in chws:
+ if c in conf:
+ chw_bin_body += struct.pack("!h",1)
+ else:
+ chw_bin_body += struct.pack("!h",0)
+ del cfses[i]
+
+ if len(cfses) != 0:
+ return -xsconstants.XSERR_BAD_CONFLICTSET, "", ""
+
+ chw_bin += chw_bin_body
+
+ while len(chw_bin) < roundup8(len(chw_bin)):
+ chw_bin += "\x00"
+
+ # Build STE part
+ steformat="!iiiii"
+ ste_bin = struct.pack(steformat,
+ ACM_STE_VERSION,
+ ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY,
+ len(stes),
+ max_ste_types,
+ struct.calcsize(steformat))
+ ste_bin_body = ""
+ if stes:
+ # Simulate __NULL_LABEL__
+ for s in stes:
+ ste_bin_body += struct.pack("!h",0)
+ # VMs that are listed and their chinese walls
+ for v in vms_with_stes:
+ unknown_ste |= (set(stes_by_vm[v]) - set(stes))
+ for s in stes:
+ if s in stes_by_vm[v]:
+ ste_bin_body += struct.pack("!h",1)
+ else:
+ ste_bin_body += struct.pack("!h",0)
+ for r in resnames:
+ unknown_ste |= (set(stes_by_res[r]) - set(stes))
+ for s in stes:
+ if s in stes_by_res[r]:
+ ste_bin_body += struct.pack("!h",1)
+ else:
+ ste_bin_body += struct.pack("!h",0)
+
+ ste_bin += ste_bin_body;
+
+ while len(ste_bin) < roundup8(len(ste_bin)):
+ ste_bin += "\x00"
+
+ #Write binary header:
+ headerformat="!iiiiiiiiii"
+ totallen_bin = struct.calcsize(headerformat) + \
+ len(pr_bin) + len(chw_bin) + len(ste_bin)
+ polref_offset = struct.calcsize(headerformat)
+ primpoloffset = polref_offset + len(pr_bin)
+ if primpolcode == ACM_CHINESE_WALL_POLICY:
+ secpoloffset = primpoloffset + len(chw_bin)
+ elif primpolcode == ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY:
+ secpoloffset = primpoloffset + len(ste_bin)
+ else:
+ secpoloffset = primpoloffset
+
+ (major, minor) = self.getVersionTuple()
+ hdr_bin = struct.pack(headerformat,
+ ACM_POLICY_VERSION,
+ ACM_MAGIC,
+ totallen_bin,
+ polref_offset,
+ primpolcode,
+ primpoloffset,
+ secpolcode,
+ secpoloffset,
+ major, minor)
+
+ all_bin = array.array('B')
+ for s in [ hdr_bin, pr_bin, chw_bin, ste_bin ]:
+ for c in s:
+ all_bin.append(ord(c))
+
+ log.info("Compiled policy: rc = %s" % hex(rc))
+ if len(unknown_ste) > 0:
+ log.info("The following STEs in VM/res labels were unknown:" \
+ " %s" % list(unknown_ste))
+ if len(unknown_chw) > 0:
+ log.info("The following Ch. Wall types in labels were unknown:" \
+ " %s" % list(unknown_chw))
+ return rc, mapfile, all_bin.tostring()
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/util/bootloader.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/util/bootloader.py Mon Jul 09 14:51:44 2007 +0100
@@ -0,0 +1,521 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2006,2007 International Business Machines Corp.
+# Author: Stefan Berger <stefanb@xxxxxxxxxx>
+#============================================================================
+
+import re
+import os, stat
+import tempfile
+import shutil
+import threading
+from xen.xend.XendLogging import log
+
+__bootloader = None
+
+#
+# Functions for modifying entries in the bootloader, i.e. adding
+# a module to boot the system with a policy.
+#
+
+def get_default_title():
+ """ See description in Bootloader class below """
+ return __bootloader.get_default_title()
+
+
+def get_boot_policies():
+ """ See description in Bootloader class below """
+ return __bootloader.get_boot_policies()
+
+
+def add_boot_policy(index, binpolname):
+ """ See description in Bootloader class below """
+ return __bootloader.add_boot_policy(index, binpolname)
+
+
+def rm_policy_from_boottitle(index, unamelist):
+ """ See description in Bootloader class below """
+ return __bootloader.rm_policy_from_boottitle(index, unamelist)
+
+
+def set_kernel_attval(index, att, val):
+ """ See description in Bootloader class below """
+ return __bootloader.set_kernel_attval(index, att, val)
+
+
+def get_kernel_val(index, att):
+ """ See description in Bootloader class below """
+ return __bootloader.get_kernel_val(index, att)
+
+
+def set_boot_policy(title_idx, filename):
+ boottitles = get_boot_policies()
+ if boottitles.has_key(title_idx):
+ rm_policy_from_boottitle(title_idx, [ boottitles[title_idx] ])
+ rc = add_boot_policy(title_idx, filename)
+ return rc
+
+
+def loads_default_policy(filename):
+ """ Determine whether the given policy is loaded by the default boot title
"""
+ polfile = get_default_policy()
+ if polfile != None:
+ if polfile == filename or \
+ "/"+polfile == filename:
+ return True
+ return False
+
+
+def get_default_policy():
+ """ Get the name of the policy loaded by the default boot title """
+ title = get_default_title()
+ policies = get_boot_policies()
+ return policies.get(title)
+
+
+def set_default_boot_policy(filename):
+ """ Set the boot policy in the default title to the given name. """
+ title = get_default_title()
+ return set_boot_policy(title, filename)
+
+
+def __is_bootdir_mounted():
+ """
+ Determine whether the boot partition /boot is mounted or not
+ """
+ rc = False
+ file = open("/proc/mounts")
+ for line in file:
+ tmp = line.split(" ")
+ if tmp[1] == "/boot":
+ rc = True
+ break
+ return rc
+
+def get_prefix():
+ if __is_bootdir_mounted():
+ return "/"
+ else:
+ return "/boot/"
+
+
+
+class Bootloader:
+ """ Bootloader class that real bootloader implementations must overwrite
"""
+ def __init__(self):
+ pass
+
+ def probe(self):
+ """ Test whether this implementation of a bootloader is supported on
the
+ local system """
+ return True
+
+ def get_default_title(self):
+ """ Get the index (starting with 0) of the default boot title
+ This number is read from the grub configuration file.
+ In case of an error '-1' is returned
+ @rtype: int
+ @return: the index of the default boot title
+ """
+ return None
+
+ def get_boot_policies(self):
+ """ Get a dictionary of policies that the system is booting with.
+ @rtype: dict
+ @return: dictionary of boot titles where the keys are the
+ indices of the boot titles
+ """
+ return {}
+
+ def add_boot_policy(self, index, binpolname):
+ """ Add the binary policy for automatic loading when
+ booting the system. Add it to the boot title at index
+ 'index'.
+ """
+ return False
+
+ def rm_policy_from_boottitle(self, index, unamelist):
+ """ Remove a policy from the given title. A list of possible policies
+ must be given to detect what module to remove
+ """
+ return False
+
+ def set_kernel_attval(self, index, att, val):
+ """
+ Append an attribut/value pair to the kernel line.
+ @param index : The index of the title to modify
+ @param att : The attribute to add
+ @param val : The value to add. If no value or the special value
+ '<>' is given, then the attribute will be removed.
+ If an empty value is given, then only the attribute
+ is added in the format "att", otherwise "att=val"
+ is added.
+ """
+ return False
+
+ def get_kernel_val(self, index, att):
+ """
+ Get an attribute's value from the kernel line.
+ @param index : The index of the title to get the attribute/value
from
+ @param att : The attribute to read the value of
+ """
+ return None
+
+
+class Grub(Bootloader):
+ """ Implementation for manipulating bootloader entries in grub according
+ to the 'Bootloader' class interface """
+
+ def __init__(self):
+ self.__bootfile_lock = threading.RLock()
+ self.title_re = re.compile("\s*title\s", re.IGNORECASE)
+ self.module_re = re.compile("\s+module\s", re.IGNORECASE)
+ self.policy_re = re.compile(".*\.bin", re.IGNORECASE)
+ self.kernel_re = re.compile("\s*kernel\s", re.IGNORECASE)
+ Bootloader.__init__(self)
+
+ def probe(self):
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return False
+ return True
+
+
+ def __get_bootfile(self):
+ """ Get the name of the bootfile """
+ boot_file = "/boot/grub/grub.conf"
+ alt_boot_file = "/boot/grub/menu.lst"
+
+ if not os.path.isfile(boot_file):
+ #take alternate boot file instead
+ boot_file = alt_boot_file
+
+ #follow symlink since menue.lst might be linked to grub.conf
+ if not os.path.exists(boot_file):
+ raise IOError("Boot file \'%s\' not found." % boot_file)
+
+ if stat.S_ISLNK(os.lstat(boot_file)[stat.ST_MODE]):
+ new_name = os.readlink(boot_file)
+ if new_name[0] == "/":
+ boot_file = new_name
+ else:
+ path = boot_file.split('/')
+ path[len(path)-1] = new_name
+ boot_file = '/'.join(path)
+ if not os.path.exists(boot_file):
+ raise IOError("Boot file \'%s\' not found." % boot_file)
+ return boot_file
+
+
+ def __get_titles(self):
+ """ Get the names of all boot titles in the grub config file
+ @rtype: list
+ @return: list of names of available boot titles
+ """
+ titles = []
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return []
+ try:
+ self.__bootfile_lock.acquire()
+ grub_fd = open(boot_file)
+ for line in grub_fd:
+ if self.title_re.match(line):
+ line = line.rstrip().lstrip()
+ titles.append(line.lstrip('title').lstrip())
+ finally:
+ self.__bootfile_lock.release()
+ return titles
+
+
+ def get_default_title(self):
+ """ Get the index (starting with 0) of the default boot title
+ This number is read from the grub configuration file.
+ In case of an error '-1' is returned
+ @rtype: int
+ @return: the index of the default boot title
+ """
+ def_re = re.compile("default", re.IGNORECASE)
+ default = None
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return default
+ try:
+ self.__bootfile_lock.acquire()
+ grub_fd = open(boot_file)
+ for line in grub_fd:
+ line = line.rstrip()
+ if def_re.match(line):
+ line = line.rstrip()
+ line = line.lstrip("default=")
+ default = int(line)
+ break
+ finally:
+ self.__bootfile_lock.release()
+ return default
+
+
+ def get_boot_policies(self):
+ """ Get a dictionary of policies that the system is booting with.
+ @rtype: dict
+ @return: dictionary of boot titles where the keys are the
+ indices of the boot titles
+ """
+ policies = {}
+ within_title = 0
+ idx = -1
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return policies
+ try:
+ self.__bootfile_lock.acquire()
+
+ grub_fd = open(boot_file)
+ for line in grub_fd:
+ if self.title_re.match(line):
+ within_title = 1
+ idx = idx + 1
+ if within_title and self.module_re.match(line):
+ if self.policy_re.match(line):
+ start = line.find("module")
+ pol = line[start+6:]
+ pol = pol.lstrip().rstrip()
+ if pol[0] == '/':
+ pol = pol[1:]
+ if pol[0:5] == "boot/":
+ pol = pol[5:]
+ policies[idx] = pol
+ finally:
+ self.__bootfile_lock.release()
+ return policies
+
+
+ def add_boot_policy(self, index, binpolname):
+ """ Add the binary policy for automatic loading when
+ booting the system. Add it to the boot title at index
+ 'index'.
+ """
+ ctr = 0
+ module_line = ""
+ within_title = 0
+ found = False
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return False
+ try:
+ self.__bootfile_lock.acquire()
+ grub_fd = open(boot_file)
+ (tmp_fd, tmp_grub) = tempfile.mkstemp()
+ for line in grub_fd:
+ if self.title_re.match(line):
+ if module_line != "" and not found:
+ os.write(tmp_fd, module_line)
+ found = True
+
+ if ctr == index:
+ within_title = 1
+ else:
+ within_title = 0
+ ctr = ctr + 1
+ elif within_title and self.module_re.match(line):
+ start = line.find("module")
+ l = line[start+6:len(line)]
+ l = l.lstrip()
+ if l[0] == '/':
+ prefix = "/"
+ else:
+ prefix = ""
+ prefix = get_prefix()
+ module_line = "\tmodule %s%s\n" % (prefix,binpolname)
+ else:
+ if module_line != "" and not found:
+ os.write(tmp_fd, module_line)
+ found = True
+
+ os.write(tmp_fd, line)
+
+ if module_line != "" and not found:
+ os.write(tmp_fd, module_line)
+ found = True
+
+ shutil.move(boot_file, boot_file+"_save")
+ shutil.copyfile(tmp_grub, boot_file)
+ os.close(tmp_fd)
+ try:
+ os.remove(tmp_grub)
+ except:
+ pass
+ finally:
+ self.__bootfile_lock.release()
+ return found
+
+
+ def rm_policy_from_boottitle(self, index, unamelist):
+ """ Remove a policy from the given title. A list of possible policies
+ must be given to detect what module to remove
+ """
+ found = False
+ ctr = 0
+ within_title = 0
+
+ prefix = get_prefix()
+ namelist = [prefix+name for name in unamelist]
+
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return False
+ try:
+ self.__bootfile_lock.acquire()
+
+ grub_fd = open(boot_file)
+ (tmp_fd, tmp_grub) = tempfile.mkstemp()
+ for line in grub_fd:
+ omit_line = False
+ if self.title_re.match(line):
+ if ctr == index:
+ within_title = 1
+ else:
+ within_title = 0
+ ctr = ctr + 1
+ if within_title and self.module_re.match(line):
+ if self.policy_re.match(line):
+ start = line.find("module")
+ pol = line[start+6:len(line)]
+ pol = pol.lstrip().rstrip()
+ if pol in namelist:
+ omit_line = True
+ found = True
+ if not omit_line:
+ os.write(tmp_fd, line)
+ if found:
+ shutil.move(boot_file, boot_file+"_save")
+ shutil.copyfile(tmp_grub, boot_file)
+ os.close(tmp_fd)
+ try:
+ os.remove(tmp_grub)
+ except:
+ pass
+ finally:
+ self.__bootfile_lock.release()
+ return found
+
+
+ def set_kernel_attval(self, index, att, val):
+ """
+ Append an attribut/value pair to the kernel line.
+ @param index : The index of the title to modify
+ @param att : The attribute to add
+ @param val : The value to add. If no value or the special value
+ '<>' is given, then the attribute will be removed.
+ If an empty value is given, then only the attribute
+ is added in the format "att", otherwise "att=val"
+ is added.
+ """
+ found = False
+ ctr = 0
+ within_title = 0
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ False
+ try:
+ self.__bootfile_lock.acquire()
+
+ grub_fd = open(boot_file)
+ (tmp_fd, tmp_grub) = tempfile.mkstemp()
+ for line in grub_fd:
+ if self.title_re.match(line):
+ if ctr == index:
+ within_title = 1
+ else:
+ within_title = 0
+ ctr = ctr + 1
+ if within_title and self.kernel_re.match(line):
+ nitems = []
+ items = line.split(" ")
+ i = 0
+ while i < len(items):
+ el = items[i].split("=",1)
+ if el[0] != att:
+ nitems.append(items[i].rstrip("\n"))
+ i += 1
+ if val == "":
+ nitems.append("%s" % (att))
+ elif val != None and val != "<>":
+ nitems.append("%s=%s" % (att,val))
+ line = " ".join(nitems) + "\n"
+ os.write(tmp_fd, line)
+ shutil.move(boot_file, boot_file+"_save")
+ shutil.copyfile(tmp_grub, boot_file)
+ os.close(tmp_fd)
+ try:
+ os.remove(tmp_grub)
+ except:
+ pass
+ finally:
+ self.__bootfile_lock.release()
+ return found
+
+
+ def get_kernel_val(self, index, att):
+ """
+ Get an attribute's value from the kernel line.
+ @param index : The index of the title to get the attribute/value
from
+ @param att : The attribute to read the value of
+ """
+ ctr = 0
+ within_title = 0
+ try:
+ boot_file = self.__get_bootfile()
+ except:
+ return None
+ try:
+ self.__bootfile_lock.acquire()
+
+ grub_fd = open(boot_file)
+ for line in grub_fd:
+ if self.title_re.match(line):
+ if ctr == index:
+ within_title = 1
+ else:
+ within_title = 0
+ ctr = ctr + 1
+ if within_title and self.kernel_re.match(line):
+ line = line.rstrip().lstrip()
+ items = line.split(" ")
+ i = 0
+ while i < len(items):
+ el = items[i].split("=",1)
+ if el[0] == att:
+ if len(el) == 1:
+ return "<>"
+ return el[1]
+ i += 1
+ finally:
+ self.__bootfile_lock.release()
+ return None # Not found
+
+
+__bootloader = Bootloader()
+
+grub = Grub()
+if grub.probe() == True:
+ __bootloader = grub
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/util/security.py
--- a/tools/python/xen/util/security.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/util/security.py Mon Jul 09 14:51:44 2007 +0100
@@ -15,17 +15,22 @@
# Copyright (C) 2006 International Business Machines Corp.
# Author: Reiner Sailer
# Author: Bryan D. Payne <bdpayne@xxxxxxxxxx>
+# Author: Stefan Berger <stefanb@xxxxxxxxxx>
#============================================================================
import commands
import logging
-import sys, os, string, re
-import traceback
-import shutil
+import os, string, re
+import threading
+import struct
+import stat
from xen.lowlevel import acm
from xen.xend import sxp
+from xen.xend import XendConstants
from xen.xend.XendLogging import log
-from xen.util import dictio
+from xen.xend.XendError import VmError
+from xen.util import dictio, xsconstants
+from xen.xend.XendConstants import *
#global directories and tools for security management
policy_dir_prefix = "/etc/xen/acm-security/policies"
@@ -60,6 +65,10 @@ policy_name_re = re.compile(".*[chwall|s
#other global variables
NULL_SSIDREF = 0
+#general Rlock for map files; only one lock for all mapfiles
+__mapfile_lock = threading.RLock()
+__resfile_lock = threading.RLock()
+
log = logging.getLogger("xend.util.security")
# Our own exception definition. It is masked (pass) if raised and
@@ -75,12 +84,18 @@ def err(msg):
def err(msg):
"""Raise ACM exception.
"""
- sys.stderr.write("ACMError: " + msg + "\n")
raise ACMError(msg)
active_policy = None
+
+
+def mapfile_lock():
+ __mapfile_lock.acquire()
+
+def mapfile_unlock():
+ __mapfile_lock.release()
def refresh_security_policy():
@@ -106,6 +121,39 @@ def on():
return (active_policy not in ['INACTIVE', 'NULL'])
+def calc_dom_ssidref_from_info(info):
+ """
+ Calculate a domain's ssidref from the security_label in its
+ info.
+ This function is called before the domain is started and
+ makes sure that:
+ - the type of the policy is the same as indicated in the label
+ - the name of the policy is the same as indicated in the label
+ - calculates an up-to-date ssidref for the domain
+ The latter is necessary since the domain's ssidref could have
+ changed due to changes to the policy.
+ """
+ import xen.xend.XendConfig
+ if isinstance(info, xen.xend.XendConfig.XendConfig):
+ if info.has_key('security_label'):
+ seclab = info['security_label']
+ tmp = seclab.split(":")
+ if len(tmp) != 3:
+ raise VmError("VM label '%s' in wrong format." % seclab)
+ typ, policyname, vmlabel = seclab.split(":")
+ if typ != xsconstants.ACM_POLICY_ID:
+ raise VmError("Policy type '%s' not supported." % typ)
+ refresh_security_policy()
+ if active_policy != policyname:
+ raise VmError("Active policy '%s' different than "
+ "what in VM's label ('%s')." %
+ (active_policy, policyname))
+ ssidref = label2ssidref(vmlabel, policyname, "dom")
+ return ssidref
+ else:
+ return 0
+ raise VmError("security.calc_dom_ssidref_from_info: info of type '%s'"
+ "not supported." % type(info))
# Assumes a 'security' info [security access_control ...] [ssidref ...]
def get_security_info(info, field):
@@ -144,7 +192,6 @@ def get_security_info(info, field):
return 0
else:
return None
-
def get_security_printlabel(info):
@@ -250,32 +297,37 @@ def ssidref2label(ssidref_var):
else:
err("Instance type of ssidref not supported (must be of type 'str' or
'int')")
- (primary, secondary, f, pol_exists) = getmapfile(None)
- if not f:
- if (pol_exists):
- err("Mapping file for policy \'" + policyname + "\' not found.\n" +
- "Please use makepolicy command to create mapping file!")
- else:
- err("Policy file for \'" + active_policy + "\' not found.")
-
- #2. get labelnames for both ssidref parts
- pri_ssid = ssidref & 0xffff
- sec_ssid = ssidref >> 16
- pri_null_ssid = NULL_SSIDREF & 0xffff
- sec_null_ssid = NULL_SSIDREF >> 16
- pri_labels = []
- sec_labels = []
- labels = []
-
- for line in f:
- l = line.split()
- if (len(l) < 5) or (l[0] != "LABEL->SSID"):
- continue
- if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid):
- pri_labels.append(l[3])
- if secondary and (l[2] == secondary) and (int(l[4], 16) == sec_ssid):
- sec_labels.append(l[3])
- f.close()
+ try:
+ mapfile_lock()
+
+ (primary, secondary, f, pol_exists) = getmapfile(None)
+ if not f:
+ if (pol_exists):
+ err("Mapping file for policy not found.\n" +
+ "Please use makepolicy command to create mapping file!")
+ else:
+ err("Policy file for \'" + active_policy + "\' not found.")
+
+ #2. get labelnames for both ssidref parts
+ pri_ssid = ssidref & 0xffff
+ sec_ssid = ssidref >> 16
+ pri_null_ssid = NULL_SSIDREF & 0xffff
+ sec_null_ssid = NULL_SSIDREF >> 16
+ pri_labels = []
+ sec_labels = []
+ labels = []
+
+ for line in f:
+ l = line.split()
+ if (len(l) < 5) or (l[0] != "LABEL->SSID"):
+ continue
+ if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid):
+ pri_labels.append(l[3])
+ if secondary and (l[2] == secondary) and (int(l[4], 16) ==
sec_ssid):
+ sec_labels.append(l[3])
+ f.close()
+ finally:
+ mapfile_unlock()
#3. get the label that is in both lists (combination must be a single
label)
if (primary == "CHWALL") and (pri_ssid == pri_null_ssid) and (sec_ssid !=
sec_null_ssid):
@@ -297,7 +349,7 @@ def ssidref2label(ssidref_var):
-def label2ssidref(labelname, policyname, type):
+def label2ssidref(labelname, policyname, typ):
"""
returns ssidref corresponding to labelname;
maps current policy to default directory
@@ -307,42 +359,51 @@ def label2ssidref(labelname, policyname,
err("Cannot translate labels for \'" + policyname + "\' policy.")
allowed_types = ['ANY']
- if type == 'dom':
+ if typ == 'dom':
allowed_types.append('VM')
- elif type == 'res':
+ elif typ == 'res':
allowed_types.append('RES')
else:
err("Invalid type. Must specify 'dom' or 'res'.")
- (primary, secondary, f, pol_exists) = getmapfile(policyname)
-
- #2. get labelnames for ssidref parts and find a common label
- pri_ssid = []
- sec_ssid = []
- for line in f:
- l = line.split()
- if (len(l) < 5) or (l[0] != "LABEL->SSID"):
- continue
- if primary and (l[1] in allowed_types) and (l[2] == primary) and (l[3]
== labelname):
- pri_ssid.append(int(l[4], 16))
- if secondary and (l[1] in allowed_types) and (l[2] == secondary) and
(l[3] == labelname):
- sec_ssid.append(int(l[4], 16))
- f.close()
- if (type == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0):
- pri_ssid.append(NULL_SSIDREF)
- elif (type == 'res') and (secondary == "CHWALL") and (len(sec_ssid) == 0):
- sec_ssid.append(NULL_SSIDREF)
-
- #3. sanity check and composition of ssidref
- if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and (secondary !=
"NULL")):
- err("Label \'" + labelname + "\' not found.")
- elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1):
- err("Label \'" + labelname + "\' not unique in policy (policy error)")
- if secondary == "NULL":
- return pri_ssid[0]
- else:
- return (sec_ssid[0] << 16) | pri_ssid[0]
-
+ try:
+ mapfile_lock()
+ (primary, secondary, f, pol_exists) = getmapfile(policyname)
+
+ #2. get labelnames for ssidref parts and find a common label
+ pri_ssid = []
+ sec_ssid = []
+ for line in f:
+ l = line.split()
+ if (len(l) < 5) or (l[0] != "LABEL->SSID"):
+ continue
+ if primary and (l[1] in allowed_types) and \
+ (l[2] == primary) and \
+ (l[3] == labelname):
+ pri_ssid.append(int(l[4], 16))
+ if secondary and (l[1] in allowed_types) and \
+ (l[2] == secondary) and \
+ (l[3] == labelname):
+ sec_ssid.append(int(l[4], 16))
+ f.close()
+ if (typ == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0):
+ pri_ssid.append(NULL_SSIDREF)
+ elif (typ == 'res') and (secondary == "CHWALL") and \
+ (len(sec_ssid) == 0):
+ sec_ssid.append(NULL_SSIDREF)
+
+ #3. sanity check and composition of ssidref
+ if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and \
+ (secondary != "NULL")):
+ err("Label \'" + labelname + "\' not found.")
+ elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1):
+ err("Label \'" + labelname + "\' not unique in policy (policy
error)")
+ if secondary == "NULL":
+ return pri_ssid[0]
+ else:
+ return (sec_ssid[0] << 16) | pri_ssid[0]
+ finally:
+ mapfile_unlock()
def refresh_ssidref(config):
@@ -381,8 +442,9 @@ def refresh_ssidref(config):
err("Illegal field in access_control")
#verify policy is correct
if active_policy != policyname:
- err("Policy \'" + policyname + "\' in label does not match active
policy \'"
- + active_policy +"\'!")
+ err("Policy \'" + str(policyname) +
+ "\' in label does not match active policy \'"
+ + str(active_policy) +"\'!")
new_ssidref = label2ssidref(labelname, policyname, 'dom')
if not new_ssidref:
@@ -470,6 +532,25 @@ def get_decision(arg1, arg2):
err("Cannot determine decision (Invalid parameter).")
+def hv_chg_policy(bin_pol, del_array, chg_array):
+ """
+ Change the binary policy in the hypervisor
+ The 'del_array' and 'chg_array' give hints about deleted ssidrefs
+ and changed ssidrefs which can be due to deleted VM labels
+ or reordered VM labels
+ """
+ rc = -xsconstants.XSERR_GENERAL_FAILURE
+ errors = ""
+ if not on():
+ err("No policy active.")
+ try:
+ rc, errors = acm.chgpolicy(bin_pol, del_array, chg_array)
+ except Exception, e:
+ pass
+ if (len(errors) > 0):
+ rc = -xsconstants.XSERR_HV_OP_FAILED
+ return rc, errors
+
def make_policy(policy_name):
policy_file = string.join(string.split(policy_name, "."), "/")
@@ -479,8 +560,6 @@ def make_policy(policy_name):
(ret, output) = commands.getstatusoutput(xensec_xml2bin + " -d " +
policy_dir_prefix + " " + policy_file)
if ret:
err("Creating policy failed:\n" + output)
-
-
def load_policy(policy_name):
global active_policy
@@ -538,8 +617,8 @@ def list_labels(policy_name, condition):
def get_res_label(resource):
- """Returns resource label information (label, policy) if it exists.
- Otherwise returns null label and policy.
+ """Returns resource label information (policytype, label, policy) if
+ it exists. Otherwise returns null label and policy.
"""
def default_res_label():
ssidref = NULL_SSIDREF
@@ -547,23 +626,19 @@ def get_res_label(resource):
label = ssidref2label(ssidref)
else:
label = None
- return (label, 'NULL')
-
- (label, policy) = default_res_label()
-
- # load the resource label file
- res_label_cache = {}
- try:
- res_label_cache = dictio.dict_read("resources", res_label_filename)
- except:
- log.info("Resource label file not found.")
- return default_res_label()
-
- # find the resource information
- if res_label_cache.has_key(resource):
- (policy, label) = res_label_cache[resource]
-
- return (label, policy)
+ return (xsconstants.ACM_POLICY_ID, 'NULL', label)
+
+
+ tmp = get_resource_label(resource)
+ if len(tmp) == 2:
+ policytype = xsconstants.ACM_POLICY_ID
+ policy, label = tmp
+ elif len(tmp) == 3:
+ policytype, policy, label = tmp
+ else:
+ policytype, policy, label = default_res_label()
+
+ return (policytype, label, policy)
def get_res_security_details(resource):
@@ -582,7 +657,7 @@ def get_res_security_details(resource):
(label, ssidref, policy) = default_security_details()
# find the entry associated with this resource
- (label, policy) = get_res_label(resource)
+ (policytype, label, policy) = get_res_label(resource)
if policy == 'NULL':
log.info("Resource label for "+resource+" not in file, using DEFAULT.")
return default_security_details()
@@ -596,8 +671,29 @@ def get_res_security_details(resource):
return (label, ssidref, policy)
-
-def unify_resname(resource):
+def security_label_to_details(seclab):
+ """ Convert a Xen-API type of security label into details """
+ def default_security_details():
+ ssidref = NULL_SSIDREF
+ if on():
+ label = ssidref2label(ssidref)
+ else:
+ label = None
+ policy = active_policy
+ return (label, ssidref, policy)
+
+ (policytype, policy, label) = seclab.split(":")
+
+ # is this resource label for the running policy?
+ if policy == active_policy:
+ ssidref = label2ssidref(label, policy, 'res')
+ else:
+ log.info("Resource label not for active policy, using DEFAULT.")
+ return default_security_details()
+
+ return (label, ssidref, policy)
+
+def unify_resname(resource, mustexist=True):
"""Makes all resource locations absolute. In case of physical
resources, '/dev/' is added to local file names"""
@@ -606,28 +702,53 @@ def unify_resname(resource):
# sanity check on resource name
try:
- (type, resfile) = resource.split(":", 1)
+ (typ, resfile) = resource.split(":", 1)
except:
err("Resource spec '%s' contains no ':' delimiter" % resource)
- if type == "tap":
+ if typ == "tap":
try:
(subtype, resfile) = resfile.split(":")
except:
err("Resource spec '%s' contains no tap subtype" % resource)
- if type in ["phy", "tap"]:
+ import os
+ if typ in ["phy", "tap"]:
if not resfile.startswith("/"):
resfile = "/dev/" + resfile
+ if mustexist:
+ stats = os.lstat(resfile)
+ if stat.S_ISLNK(stats[stat.ST_MODE]):
+ resolved = os.readlink(resfile)
+ if resolved[0] != "/":
+ resfile = os.path.join(os.path.dirname(resfile), resolved)
+ resfile = os.path.abspath(resfile)
+ else:
+ resfile = resolved
+ stats = os.lstat(resfile)
+ if not (stat.S_ISBLK(stats[stat.ST_MODE])):
+ err("Invalid resource")
+
+ if typ in [ "file", "tap" ]:
+ if mustexist:
+ stats = os.lstat(resfile)
+ if stat.S_ISLNK(stats[stat.ST_MODE]):
+ resfile = os.readlink(resfile)
+ stats = os.lstat(resfile)
+ if not stat.S_ISREG(stats[stat.ST_MODE]):
+ err("Invalid resource")
#file: resources must specified with absolute path
- if (not resfile.startswith("/")) or (not os.path.exists(resfile)):
- err("Invalid resource.")
+ #vlan resources don't start with '/'
+ if typ != "vlan":
+ if (not resfile.startswith("/")) or \
+ (mustexist and not os.path.exists(resfile)):
+ err("Invalid resource.")
# from here on absolute file names with resources
- if type == "tap":
- type = type + ":" + subtype
- resource = type + ":" + resfile
+ if typ == "tap":
+ typ = typ + ":" + subtype
+ resource = typ + ":" + resfile
return resource
@@ -662,9 +783,481 @@ def res_security_check(resource, domain_
else:
# Note, we can't canonicalise the resource here, because people using
# xm without ACM are free to use relative paths.
- (label, policy) = get_res_label(resource)
+ (policytype, label, policy) = get_res_label(resource)
if policy != 'NULL':
raise ACMError("Security is off, but '"+resource+"' is labeled")
rtnval = 0
return rtnval
+
+def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label):
+ """Checks if the given resource can be used by the given domain
+ label. Returns 1 if the resource can be used, otherwise 0.
+ """
+ rtnval = 1
+ # if security is on, ask the hypervisor for a decision
+ if on():
+ typ, dpolicy, domain_label = xapi_dom_label.split(":")
+ if not dpolicy or not domain_label:
+ raise VmError("VM security label in wrong format.")
+ if active_policy != rpolicy:
+ raise VmError("Resource's policy '%s' != active policy '%s'" %
+ (rpolicy, active_policy))
+ domac = ['access_control']
+ domac.append(['policy', active_policy])
+ domac.append(['label', domain_label])
+ domac.append(['type', 'dom'])
+ decision = get_decision(domac, ['ssidref', str(rssidref)])
+
+ log.info("Access Control Decision : %s" % decision)
+ # provide descriptive error messages
+ if decision == 'DENIED':
+ if rlabel == ssidref2label(NULL_SSIDREF):
+ #raise ACMError("Resource is not labeled")
+ rtnval = 0
+ else:
+ #raise ACMError("Permission denied for resource because label
'"+rlabel+"' is not allowed")
+ rtnval = 0
+
+ # security is off, make sure resource isn't labeled
+ else:
+ # Note, we can't canonicalise the resource here, because people using
+ # xm without ACM are free to use relative paths.
+ if rpolicy != 'NULL':
+ #raise ACMError("Security is off, but resource is labeled")
+ rtnval = 0
+
+ return rtnval
+
+
+def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi):
+ """Assign a resource label to a resource
+ @param resource: The name of a resource, i.e., "phy:/dev/hda", or
+ "tap:qcow:/path/to/file.qcow"
+
+ @param reslabel_xapi: A resource label foramtted as in all other parts of
+ the Xen-API, i.e., ACM:xm-test:blue"
+ @rtype: int
+ @return Success (0) or failure value (< 0)
+ """
+ olabel = ""
+ if reslabel_xapi == "":
+ return rm_resource_label(resource, oldlabel_xapi)
+ typ, policyref, label = reslabel_xapi.split(":")
+ if typ != xsconstants.ACM_POLICY_ID:
+ return -xsconstants.XSERR_WRONG_POLICY_TYPE
+ if not policyref or not label:
+ return -xsconstants.XSERR_BAD_LABEL_FORMAT
+ if oldlabel_xapi not in [ "" ]:
+ tmp = oldlabel_xapi.split(":")
+ if len(tmp) != 3:
+ return -xsconstants.XSERR_BAD_LABEL_FORMAT
+ otyp, opolicyref, olabel = tmp
+ # Only ACM is supported
+ if otyp != xsconstants.ACM_POLICY_ID:
+ return -xsconstants.XSERR_WRONG_POLICY_TYPE
+ return set_resource_label(resource, typ, policyref, label, olabel)
+
+def is_resource_in_use(resource):
+ """ Investigate all running domains whether they use this device """
+ from xen.xend import XendDomain
+ dominfos = XendDomain.instance().list('all')
+ lst = []
+ for dominfo in dominfos:
+ if is_resource_in_use_by_dom(dominfo, resource):
+ lst.append(dominfo)
+ return lst
+
+def devices_equal(res1, res2):
+ """ Determine whether two devices are equal """
+ return (unify_resname(res1) == unify_resname(res2))
+
+def is_resource_in_use_by_dom(dominfo, resource):
+ """ Determine whether a resources is in use by a given domain
+ @return True or False
+ """
+ if not dominfo.domid:
+ return False
+ if dominfo._stateGet() not in [ DOM_STATE_RUNNING ]:
+ return False
+ devs = dominfo.info['devices']
+ uuids = devs.keys()
+ for uuid in uuids:
+ dev = devs[uuid]
+ if len(dev) >= 2 and dev[1].has_key('uname'):
+ # dev[0] is type, i.e. 'vbd'
+ if devices_equal(dev[1]['uname'], resource):
+ log.info("RESOURCE IN USE: Domain %d uses %s." %
+ (dominfo.domid, resource))
+ return True
+ return False
+
+
+def get_domain_resources(dominfo):
+ """ Collect all resources of a domain in a map where each entry of
+ the map is a list.
+ Entries are strored in the following formats:
+ tap:qcow:/path/xyz.qcow
+ """
+ resources = { 'vbd' : [], 'tap' : []}
+ devs = dominfo.info['devices']
+ uuids = devs.keys()
+ for uuid in uuids:
+ dev = devs[uuid]
+ typ = dev[0]
+ if typ in [ 'vbd', 'tap' ]:
+ resources[typ].append(dev[1]['uname'])
+
+ return resources
+
+
+def resources_compatible_with_vmlabel(xspol, dominfo, vmlabel):
+ """
+ Check whether the resources' labels are compatible with the
+ given VM label. This is a function to be used when for example
+ a running domain is to get the new label 'vmlabel'
+ """
+ if not xspol:
+ return False
+
+ try:
+ __resfile_lock.acquire()
+ try:
+ access_control = dictio.dict_read("resources",
+ res_label_filename)
+ except:
+ return False
+ return __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
+ access_control)
+ finally:
+ __resfile_lock.release()
+ return False
+
+
+def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
+ access_control):
+ """
+ Check whether the resources' labels are compatible with the
+ given VM label. The access_control parameter provides a
+ dictionary of the resource name to resource label mappings
+ under which the evaluation should be done.
+ """
+ resources = get_domain_resources(dominfo)
+ reslabels = [] # all resource labels
+ polname = xspol.get_name()
+ for key in resources.keys():
+ for res in resources[key]:
+ try:
+ tmp = access_control[res]
+ if len(tmp) != 3:
+ return False
+
+ if polname != tmp[1]:
+ return False
+ label = tmp[2]
+ if not label in reslabels:
+ reslabels.append(label)
+ except:
+ return False
+ # Check that all resource labes have a common STE type with the
+ # vmlabel
+ rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels)
+ return rc;
+
+def set_resource_label(resource, policytype, policyref, reslabel, \
+ oreslabel = None):
+ """Assign a label to a resource
+ If the old label (oreslabel) is given, then the resource must have
+ that old label.
+ A resource label may be changed if
+ - the resource is not in use
+ @param resource : The name of a resource, i.e., "phy:/dev/hda"
+ @param policyref : The name of the policy
+ @param reslabel : the resource label within the policy
+ @param oreslabel : optional current resource label
+
+ @rtype: int
+ @return Success (0) or failure value (< 0)
+ """
+ try:
+ resource = unify_resname(resource, mustexist=False)
+ except Exception:
+ return -xsconstants.XSERR_BAD_RESOURCE_FORMAT
+
+ domains = is_resource_in_use(resource)
+ if len(domains) > 0:
+ return -xsconstants.XSERR_RESOURCE_IN_USE
+
+ try:
+ __resfile_lock.acquire()
+ access_control = {}
+ try:
+ access_control = dictio.dict_read("resources", res_label_filename)
+ except:
+ pass
+ if oreslabel:
+ if not access_control.has_key(resource):
+ return -xsconstants.XSERR_BAD_LABEL
+ tmp = access_control[resource]
+ if len(tmp) != 3:
+ return -xsconstants.XSERR_BAD_LABEL
+ if tmp[2] != oreslabel:
+ return -xsconstants.XSERR_BAD_LABEL
+ if reslabel != "":
+ new_entry = { resource : tuple([policytype, policyref, reslabel])}
+ access_control.update(new_entry)
+ else:
+ if access_control.has_key(resource):
+ del access_control[resource]
+ dictio.dict_write(access_control, "resources", res_label_filename)
+ finally:
+ __resfile_lock.release()
+ return xsconstants.XSERR_SUCCESS
+
+def rm_resource_label(resource, oldlabel_xapi):
+ """Remove a resource label from a physical resource
+ @param resource: The name of a resource, i.e., "phy:/dev/hda"
+
+ @rtype: int
+ @return Success (0) or failure value (< 0)
+ """
+ tmp = oldlabel_xapi.split(":")
+ if len(tmp) != 3:
+ return -xsconstants.XSERR_BAD_LABEL_FORMAT
+ otyp, opolicyref, olabel = tmp
+ # Only ACM is supported
+ if otyp != xsconstants.ACM_POLICY_ID and \
+ otyp != xsconstants.INVALID_POLICY_PREFIX + xsconstants.ACM_POLICY_ID:
+ return -xsconstants.XSERR_WRONG_POLICY_TYPE
+ return set_resource_label(resource, "", "", "", olabel)
+
+def get_resource_label_xapi(resource):
+ """Get the assigned resource label of a physical resource
+ in the format used by then Xen-API, i.e., "ACM:xm-test:blue"
+
+ @rtype: string
+ @return the string representing policy type, policy name and label of
+ the resource
+ """
+ res = get_resource_label(resource)
+ return format_resource_label(res)
+
+def format_resource_label(res):
+ if res:
+ if len(res) == 2:
+ return xsconstants.ACM_POLICY_ID + ":" + res[0] + ":" + res[1]
+ if len(res) == 3:
+ return ":".join(res)
+ return ""
+
+def get_resource_label(resource):
+ """Get the assigned resource label of a given resource
+ @param resource: The name of a resource, i.e., "phy:/dev/hda"
+
+ @rtype: list
+ @return tuple of (policy name, resource label), i.e., (xm-test, blue)
+ """
+ try:
+ resource = unify_resname(resource, mustexist=False)
+ except Exception:
+ return []
+
+ reslabel_map = get_labeled_resources()
+
+ if reslabel_map.has_key(resource):
+ return list(reslabel_map[resource])
+ else:
+ #Try to resolve each label entry
+ for key, value in reslabel_map.items():
+ try:
+ if resource == unify_resname(key):
+ return list(value)
+ except:
+ pass
+
+ return []
+
+
+def get_labeled_resources_xapi():
+ """ Get a map of all labeled resource with the labels formatted in the
+ xen-api resource label format.
+ """
+ reslabel_map = get_labeled_resources()
+ for key, labeldata in reslabel_map.items():
+ reslabel_map[key] = format_resource_label(labeldata)
+ return reslabel_map
+
+
+def get_labeled_resources():
+ """Get a map of all labeled resources
+ @rtype: list
+ @return list of labeled resources
+ """
+ try:
+ __resfile_lock.acquire()
+ try:
+ access_control = dictio.dict_read("resources", res_label_filename)
+ except:
+ return {}
+ finally:
+ __resfile_lock.release()
+ return access_control
+
+
+def relabel_domains(relabel_list):
+ """
+ Relabel the given domains to have a new ssidref.
+ @param relabel_list: a list containing tuples of domid, ssidref
+ example: [ [0, 0x00020002] ]
+ """
+ rel_rules = ""
+ for r in relabel_list:
+ log.info("Relabeling domain with domid %d to new ssidref 0x%08x",
+ r[0], r[1])
+ rel_rules += struct.pack("ii", r[0], r[1])
+ try:
+ rc, errors = acm.relabel_domains(rel_rules)
+ except Exception, e:
+ log.info("Error after relabel_domains: %s" % str(e))
+ rc = -xsconstants.XSERR_GENERAL_FAILURE
+ errors = ""
+ if (len(errors) > 0):
+ rc = -xsconstants.XSERR_HV_OP_FAILED
+ return rc, errors
+
+
+def change_acm_policy(bin_pol, del_array, chg_array,
+ vmlabel_map, reslabel_map, cur_acmpol, new_acmpol):
+ """
+ Change the ACM policy of the system by relabeling
+ domains and resources first and doing some access checks.
+ Then update the policy in the hypervisor. If this is all successful,
+ relabel the domains permanently and commit the relabed resources.
+
+ Need to do / check the following:
+ - relabel all resources where there is a 'from' field in
+ the policy. [ NOT DOING THIS: and mark those as unlabeled where the
label
+ does not appear in the new policy anymore (deletion) ]
+ - relabel all VMs where there is a 'from' field in the
+ policy and mark those as unlabeled where the label
+ does not appear in the new policy anymore; no running
+ or paused VM may be unlabeled through this
+ - check that under the new labeling conditions the VMs
+ still have access to their resources as before. Unlabeled
+ resources are inaccessible. If this check fails, the
+ update failed.
+ - Attempt changes in the hypervisor; if this step fails,
+ roll back the relabeling of resources and VMs
+ - Make the relabeling of resources and VMs permanent
+ """
+ rc = xsconstants.XSERR_SUCCESS
+
+ domain_label_map = {}
+ new_policyname = new_acmpol.get_name()
+ new_policytype = new_acmpol.get_type_name()
+ cur_policyname = cur_acmpol.get_name()
+ cur_policytype = cur_acmpol.get_type_name()
+ polnew_reslabels = new_acmpol.policy_get_resourcelabel_names()
+ errors=""
+
+ try:
+ __resfile_lock.acquire()
+ mapfile_lock()
+
+ # Get all domains' dominfo.
+ from xen.xend import XendDomain
+ dominfos = XendDomain.instance().list('all')
+
+ log.info("----------------------------------------------")
+ # relabel resources
+
+ access_control = {}
+ try:
+ access_control = dictio.dict_read("resources", res_label_filename)
+ finally:
+ pass
+ for key, labeldata in access_control.items():
+ if len(labeldata) == 2:
+ policy, label = labeldata
+ policytype = xsconstants.ACM_POLICY_ID
+ elif len(labeldata) == 3:
+ policytype, policy, label = labeldata
+ else:
+ return -xsconstants.XSERR_BAD_LABEL_FORMAT, ""
+
+ if policytype != cur_policytype or \
+ policy != cur_policyname:
+ continue
+
+ # label been renamed or deleted?
+ if reslabel_map.has_key(label) and cur_policyname == policy:
+ label = reslabel_map[label]
+ elif label not in polnew_reslabels:
+ policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
+ # Update entry
+ access_control[key] = \
+ tuple([ policytype, new_policyname, label ])
+
+ # All resources have new labels in the access_control map
+ # There may still be labels in there that are invalid now.
+
+ # Do this in memory without writing to disk:
+ # - Relabel all domains independent of whether they are running
+ # or not
+ # - later write back to config files
+ polnew_vmlabels = new_acmpol.policy_get_virtualmachinelabel_names()
+
+ for dominfo in dominfos:
+ sec_lab = dominfo.get_security_label()
+ if not sec_lab:
+ continue
+ policytype, policy, vmlabel = sec_lab.split(":")
+ name = dominfo.getName()
+
+ if policytype != cur_policytype or \
+ policy != cur_policyname:
+ continue
+
+ new_vmlabel = vmlabel
+ if vmlabel_map.has_key(vmlabel):
+ new_vmlabel = vmlabel_map[vmlabel]
+ if new_vmlabel not in polnew_vmlabels:
+ policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
+ new_seclab = "%s:%s:%s" % \
+ (policytype, new_policyname, new_vmlabel)
+
+ domain_label_map[dominfo] = [ sec_lab, new_seclab ]
+
+ if dominfo._stateGet() in (DOM_STATE_PAUSED, DOM_STATE_RUNNING):
+ compatible = __resources_compatible_with_vmlabel(new_acmpol,
+ dominfo,
+ new_vmlabel,
+ access_control)
+ log.info("Domain %s with new label '%s' can access its "
+ "resources? : %s" %
+ (name, new_vmlabel, str(compatible)))
+ log.info("VM labels in new domain: %s" %
+ new_acmpol.policy_get_virtualmachinelabel_names())
+ if not compatible:
+ return (-xsconstants.XSERR_RESOURCE_ACCESS, "")
+
+ rc, errors = hv_chg_policy(bin_pol, del_array, chg_array)
+ if rc == 0:
+ # Write the relabeled resources back into the file
+ dictio.dict_write(access_control, "resources", res_label_filename)
+ # Properly update all VMs to their new labels
+ for dominfo, labels in domain_label_map.items():
+ sec_lab, new_seclab = labels
+ if sec_lab != new_seclab:
+ log.info("Updating domain %s to new label '%s'." % \
+ (new_seclab, sec_lab))
+ # This better be working!
+ dominfo.set_security_label(new_seclab,
+ sec_lab,
+ new_acmpol)
+ finally:
+ log.info("----------------------------------------------")
+ mapfile_unlock()
+ __resfile_lock.release()
+
+ return rc, errors
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/util/xsconstants.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/util/xsconstants.py Mon Jul 09 14:51:44 2007 +0100
@@ -0,0 +1,104 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2007 International Business Machines Corp.
+# Author: Stefan Berger <stefanb@xxxxxxxxxx>
+#============================================================================
+
+XS_INST_NONE = 0
+XS_INST_BOOT = (1 << 0)
+XS_INST_LOAD = (1 << 1)
+
+XS_POLICY_NONE = 0
+XS_POLICY_ACM = (1 << 0)
+
+# Some internal variables used by the Xen-API
+ACM_LABEL_VM = (1 << 0)
+ACM_LABEL_RES = (1 << 1)
+
+# Base for XS error codes for collision avoidance with other error codes
+XSERR_BASE = 0x1000
+
+# XS error codes as used by the Xen-API
+XSERR_SUCCESS = 0
+XSERR_GENERAL_FAILURE = 1 + XSERR_BASE
+XSERR_BAD_XML = 2 + XSERR_BASE # XML is wrong (not
according to schema)
+XSERR_XML_PROCESSING = 3 + XSERR_BASE
+XSERR_POLICY_INCONSISTENT = 4 + XSERR_BASE # i.e., bootstrap name not a
VM label
+XSERR_FILE_ERROR = 5 + XSERR_BASE
+XSERR_BAD_RESOURCE_FORMAT = 6 + XSERR_BASE # badly formatted resource
+XSERR_BAD_LABEL_FORMAT = 7 + XSERR_BASE
+XSERR_RESOURCE_NOT_LABELED = 8 + XSERR_BASE
+XSERR_RESOURCE_ALREADY_LABELED = 9 + XSERR_BASE
+XSERR_WRONG_POLICY_TYPE = 10 + XSERR_BASE
+XSERR_BOOTPOLICY_INSTALLED = 11 + XSERR_BASE
+XSERR_NO_DEFAULT_BOOT_TITLE = 12 + XSERR_BASE
+XSERR_POLICY_LOAD_FAILED = 13 + XSERR_BASE
+XSERR_POLICY_LOADED = 14 + XSERR_BASE
+XSERR_POLICY_TYPE_UNSUPPORTED = 15 + XSERR_BASE
+XSERR_BAD_CONFLICTSET = 16 + XSERR_BASE
+XSERR_RESOURCE_IN_USE = 17 + XSERR_BASE
+XSERR_BAD_POLICY_NAME = 18 + XSERR_BASE
+XSERR_VERSION_PREVENTS_UPDATE = 19 + XSERR_BASE
+XSERR_BAD_LABEL = 20 + XSERR_BASE
+XSERR_VM_WRONG_STATE = 21 + XSERR_BASE
+XSERR_POLICY_NOT_LOADED = 22 + XSERR_BASE
+XSERR_RESOURCE_ACCESS = 23 + XSERR_BASE
+XSERR_HV_OP_FAILED = 24 + XSERR_BASE
+XSERR_BOOTPOLICY_INSTALL_ERROR = 25 + XSERR_BASE
+XSERR_LAST = 25 + XSERR_BASE ## KEEP LAST
+
+XSERR_MESSAGES = [
+ '',
+ 'General Failure',
+ 'XML is malformed',
+ 'Error while processing XML',
+ 'Policy has inconsistencies',
+ 'A file access error occurred',
+ 'The resource format is not valid',
+ 'The label format is not valid',
+ 'The resource is not labeld',
+ 'The resource is already labeld',
+ 'The policy type is wrong',
+ 'The system boot policy is installed',
+ 'Could not find the default boot title',
+ 'Loading of the policy failed',
+ 'The policy is loaded',
+ 'The policy type is unsupported',
+ 'There is a bad conflict set',
+ 'The resource is in use',
+ 'The policy has an invalid name',
+ 'The version of the policy prevents an update',
+ 'The label is bad',
+ 'Operation not premittend - the VM is in the wrong state',
+ 'The policy is not loaded',
+ 'Error accessing resource',
+ 'Operation failed in hypervisor',
+ 'Boot policy installation error'
+]
+
+def xserr2string(err):
+ if err == XSERR_SUCCESS:
+ return "Success"
+ if err >= XSERR_GENERAL_FAILURE and \
+ err <= XSERR_LAST:
+ return XSERR_MESSAGES[err - XSERR_BASE]
+ return "Unknown XSERR code '%s'." % (hex(err))
+
+# Policy identifiers used in labels
+ACM_POLICY_ID = "ACM"
+
+INVALID_POLICY_PREFIX = "INV_"
+
+INVALID_SSIDREF = 0xFFFFFFFF
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/util/xspolicy.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/util/xspolicy.py Mon Jul 09 14:51:44 2007 +0100
@@ -0,0 +1,66 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2006,2007 International Business Machines Corp.
+# Author: Stefan Berger <stefanb@xxxxxxxxxx>
+#============================================================================
+
+import threading
+import xsconstants
+
+class XSPolicy:
+ """
+ The base policy class for all policies administered through
+ XSPolicyAdmin.
+ """
+
+ def __init__(self, name=None, ref=None):
+ self.lock = threading.Lock()
+ self.ref = ref
+ self.name = name
+ if ref:
+ from xen.xend.XendXSPolicy import XendXSPolicy
+ self.xendxspolicy = XendXSPolicy(self, {}, ref)
+ else:
+ self.xendxspolicy = None
+
+ def grab_lock(self):
+ self.lock.acquire()
+
+ def unlock(self):
+ self.lock.release()
+
+ def get_ref(self):
+ return self.ref
+
+ def destroy(self):
+ if self.xendxspolicy:
+ self.xendxspolicy.destroy()
+
+ # All methods below should be overwritten by the inheriting class
+
+ def isloaded(self):
+ return False
+
+ def loadintohv(self):
+ return xsconstants.XSERR_POLICY_LOAD_FAILED
+
+ def get_type(self):
+ return xsconstants.XS_POLICY_NONE
+
+ def get_type_name(self):
+ return ""
+
+ def update(self, repr_new):
+ return -xsconstants.XSERR_GENERAL_FAILURE, ""
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendAPI.py
--- a/tools/python/xen/xend/XendAPI.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/XendAPI.py Mon Jul 09 14:51:44 2007 +0100
@@ -40,11 +40,13 @@ from XendVMMetrics import XendVMMetrics
from XendVMMetrics import XendVMMetrics
from XendPIF import XendPIF
from XendPBD import XendPBD
+from XendXSPolicy import XendXSPolicy, XendACMPolicy
from XendAPIConstants import *
from xen.util.xmlrpclib2 import stringify
from xen.util.blkif import blkdev_name_to_number
+from xen.util import xsconstants
AUTH_NONE = 'none'
@@ -467,6 +469,8 @@ classes = {
'console' : valid_console,
'SR' : valid_sr,
'task' : valid_task,
+ 'XSPolicy' : valid_object("XSPolicy"),
+ 'ACMPolicy' : valid_object("ACMPolicy"),
'debug' : valid_debug,
'network' : valid_object("network"),
'PIF' : valid_object("PIF"),
@@ -481,6 +485,8 @@ autoplug_classes = {
'VM_metrics' : XendVMMetrics,
'PBD' : XendPBD,
'PIF_metrics' : XendPIFMetrics,
+ 'XSPolicy' : XendXSPolicy,
+ 'ACMPolicy' : XendACMPolicy,
}
class XendAPI(object):
@@ -1170,7 +1176,8 @@ class XendAPI(object):
'HVM_boot_params',
'platform',
'PCI_bus',
- 'other_config']
+ 'other_config',
+ 'security_label']
VM_methods = [('clone', 'VM'),
('start', None),
@@ -1230,7 +1237,8 @@ class XendAPI(object):
'HVM_boot_params',
'platform',
'PCI_bus',
- 'other_config']
+ 'other_config',
+ 'security_label']
def VM_get(self, name, session, vm_ref):
return xen_api_success(
@@ -1601,7 +1609,22 @@ class XendAPI(object):
if dom:
return xen_api_success([dom.get_uuid()])
return xen_api_success([])
-
+
+ def VM_get_security_label(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ label = dom.get_security_label()
+ return xen_api_success(label)
+
+ def VM_set_security_label(self, session, vm_ref, sec_label, old_label):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ (rc, errors, oldlabel, new_ssidref) = \
+ dom.set_security_label(sec_label, old_label)
+ if rc != xsconstants.XSERR_SUCCESS:
+ return xen_api_error(['SECURITY_ERROR', rc])
+ if rc == 0:
+ rc = new_ssidref
+ return xen_api_success(rc)
+
def VM_create(self, session, vm_struct):
xendom = XendDomain.instance()
domuuid = XendTask.log_progress(0, 100,
@@ -1655,6 +1678,7 @@ class XendAPI(object):
'domid': domid is None and -1 or domid,
'is_control_domain': xeninfo.info['is_control_domain'],
'metrics': xeninfo.get_metrics(),
+ 'security_label': xeninfo.get_security_label(),
'crash_dumps': []
}
return xen_api_success(record)
@@ -1952,7 +1976,8 @@ class XendAPI(object):
'runtime_properties']
VIF_attr_rw = ['device',
'MAC',
- 'MTU']
+ 'MTU',
+ 'security_label']
VIF_attr_inst = VIF_attr_rw
@@ -2054,7 +2079,10 @@ class XendAPI(object):
except Exception, exn:
log.exception(exn)
return xen_api_success({})
-
+
+ def VIF_get_security_label(self, session, vif_ref):
+ return self._VIF_get(vif_ref, 'security_label')
+
# Xen API: Class VIF_metrics
# ----------------------------------------------------------------
@@ -2098,7 +2126,8 @@ class XendAPI(object):
'virtual_size',
'sharable',
'read_only',
- 'other_config']
+ 'other_config',
+ 'security_label']
VDI_attr_inst = VDI_attr_ro + VDI_attr_rw
VDI_methods = [('destroy', None)]
@@ -2206,13 +2235,24 @@ class XendAPI(object):
xennode = XendNode.instance()
return xen_api_success(xennode.get_vdi_by_name_label(name))
+ def VDI_set_security_label(self, session, vdi_ref, sec_lab, old_lab):
+ vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref)
+ rc = vdi.set_security_label(sec_lab, old_lab)
+ if rc < 0:
+ return xen_api_error(['SECURITY_ERROR', rc])
+ return xen_api_success(rc)
+
+ def VDI_get_security_label(self, session, vdi_ref):
+ vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref)
+ return xen_api_success(vdi.get_security_label())
# Xen API: Class VTPM
# ----------------------------------------------------------------
VTPM_attr_rw = [ ]
VTPM_attr_ro = ['VM',
- 'backend']
+ 'backend',
+ 'runtime_properties' ]
VTPM_attr_inst = VTPM_attr_rw
@@ -2289,6 +2329,18 @@ class XendAPI(object):
vtpms = [d.get_vtpms() for d in XendDomain.instance().list('all')]
vtpms = reduce(lambda x, y: x + y, vtpms)
return xen_api_success(vtpms)
+
+ def VTPM_get_runtime_properties(self, _, vtpm_ref):
+ xendom = XendDomain.instance()
+ dominfo = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
+ device = dominfo.get_dev_config_by_uuid('vtpm', vtpm_ref)
+
+ try:
+ device_sxps = dominfo.getDeviceSxprs('vtpm')
+ device_dict = dict(device_sxps[0][1])
+ return xen_api_success(device_dict)
+ except:
+ return xen_api_success({})
# Xen API: Class console
# ----------------------------------------------------------------
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendConfig.py
--- a/tools/python/xen/xend/XendConfig.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/XendConfig.py Mon Jul 09 14:51:44 2007 +0100
@@ -30,6 +30,8 @@ from xen.xend.XendConstants import DOM_S
from xen.xend.XendConstants import DOM_STATE_HALTED
from xen.xend.server.netif import randomMAC
from xen.util.blkif import blkdev_name_to_number
+from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
+from xen.util import xsconstants
log = logging.getLogger("xend.XendConfig")
log.setLevel(logging.WARN)
@@ -160,6 +162,7 @@ XENAPI_CFG_TYPES = {
'platform': dict,
'tools_version': dict,
'other_config': dict,
+ 'security_label': str,
}
# List of legacy configuration keys that have no equivalent in the
@@ -168,7 +171,6 @@ LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
# roundtripped (dynamic, unmodified)
'shadow_memory',
- 'security',
'vcpu_avail',
'cpu_weight',
'cpu_cap',
@@ -319,7 +321,6 @@ class XendConfig(dict):
'memory_static_max': 0,
'memory_dynamic_max': 0,
'devices': {},
- 'security': None,
'on_xend_start': 'ignore',
'on_xend_stop': 'ignore',
'cpus': [],
@@ -425,9 +426,10 @@ class XendConfig(dict):
self._memory_sanity_check()
self['cpu_time'] = dominfo['cpu_time']/1e9
- # TODO: i don't know what the security stuff expects here
if dominfo.get('ssidref'):
- self['security'] = [['ssidref', dominfo['ssidref']]]
+ ssidref = int(dominfo.get('ssidref'))
+ self['security_label'] =
XSPolicyAdminInstance().ssidref_to_vmlabel(ssidref)
+
self['shutdown_reason'] = dominfo['shutdown_reason']
# parse state into Xen API states
@@ -634,8 +636,26 @@ class XendConfig(dict):
except ValueError, e:
raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
- if 'security' in cfg and isinstance(cfg['security'], str):
- cfg['security'] = sxp.from_string(cfg['security'])
+ if 'security' in cfg and not cfg.get('security_label'):
+ secinfo = cfg['security']
+ if isinstance(secinfo, list):
+ # The xm command sends a list formatted like this:
+ # [['access_control', ['policy', 'xm-test'],['label', 'red']],
+ # ['ssidref', 196611]]
+ policy = ""
+ label = ""
+ policytype = xsconstants.ACM_POLICY_ID
+ for idx in range(0, len(secinfo)):
+ if secinfo[idx][0] == "access_control":
+ for aidx in range(1, len(secinfo[idx])):
+ if secinfo[idx][aidx][0] == "policy":
+ policy = secinfo[idx][aidx][1]
+ if secinfo[idx][aidx][0] == "label":
+ label = secinfo[idx][aidx][1]
+ if label != "" and policy != "":
+ cfg['security_label'] = "%s:%s:%s" % \
+ (policytype, policy, label)
+ del cfg['security']
old_state = sxp.child_value(sxp_cfg, 'state')
if old_state:
@@ -778,7 +798,6 @@ class XendConfig(dict):
self[sxp_arg] = val
_set_cfg_if_exists('shadow_memory')
- _set_cfg_if_exists('security')
_set_cfg_if_exists('features')
_set_cfg_if_exists('on_xend_stop')
_set_cfg_if_exists('on_xend_start')
@@ -890,6 +909,9 @@ class XendConfig(dict):
continue
if self.has_key(legacy) and self[legacy] not in (None, []):
sxpr.append([legacy, self[legacy]])
+
+ if self.has_key('security_label'):
+ sxpr.append(['security_label', self['security_label']])
sxpr.append(['image', self.image_sxpr()])
sxpr.append(['status', domain._stateGet()])
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendDomain.py
--- a/tools/python/xen/xend/XendDomain.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/XendDomain.py Mon Jul 09 14:51:44 2007 +0100
@@ -49,7 +49,7 @@ from xen.xend.XendAPIConstants import *
from xen.xend.xenstore.xstransact import xstransact
from xen.xend.xenstore.xswatch import xswatch
-from xen.util import mkdir, security
+from xen.util import mkdir
from xen.xend import uuid
xc = xen.lowlevel.xc.xc()
@@ -486,7 +486,6 @@ class XendDomain:
"""
self.domains_lock.acquire()
try:
- security.refresh_ssidref(config)
dominfo = XendDomainInfo.restore(config)
return dominfo
finally:
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/XendDomainInfo.py Mon Jul 09 14:51:44 2007 +0100
@@ -830,6 +830,9 @@ class XendDomainInfo:
else:
f('image/%s' % n, v)
+ if self.info.has_key('security_label'):
+ f('security_label', self.info['security_label'])
+
to_store.update(self._vcpuDomDetails())
log.debug("Storing domain details: %s", scrub_password(to_store))
@@ -999,9 +1002,6 @@ class XendDomainInfo:
xen.xend.XendDomain.instance().managed_config_save(self)
log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
vcpus)
-
- def getLabel(self):
- return security.get_security_info(self.info, 'label')
def getMemoryTarget(self):
"""Get this domain's target memory size, in KB."""
@@ -1446,11 +1446,20 @@ class XendDomainInfo:
# allocation of 1MB. We free up 2MB here to be on the safe side.
balloon.free(2*1024) # 2MB should be plenty
- self.domid = xc.domain_create(
- domid = 0,
- ssidref = security.get_security_info(self.info, 'ssidref'),
- handle = uuid.fromString(self.info['uuid']),
- hvm = int(hvm))
+ ssidref = security.calc_dom_ssidref_from_info(self.info)
+ if ssidref == 0 and security.on():
+ raise VmError('VM is not properly labeled.')
+
+ try:
+ self.domid = xc.domain_create(
+ domid = 0,
+ ssidref = ssidref,
+ handle = uuid.fromString(self.info['uuid']),
+ hvm = int(hvm))
+ except Exception, e:
+ # may get here if due to ACM the operation is not permitted
+ if security.on():
+ raise VmError('Domain in conflict set with running domain?')
if self.domid < 0:
raise VmError('Creating domain failed: name=%s' %
@@ -1965,24 +1974,6 @@ class XendDomainInfo:
image_sxpr = self.info.image_sxpr()
if image_sxpr:
to_store['image'] = sxp.to_string(image_sxpr)
-
- if self._infoIsSet('security'):
- secinfo = self.info['security']
- to_store['security'] = sxp.to_string(secinfo)
- for idx in range(0, len(secinfo)):
- if secinfo[idx][0] == 'access_control':
- to_store['security/access_control'] = sxp.to_string(
- [secinfo[idx][1], secinfo[idx][2]])
- for aidx in range(1, len(secinfo[idx])):
- if secinfo[idx][aidx][0] == 'label':
- to_store['security/access_control/label'] = \
- secinfo[idx][aidx][1]
- if secinfo[idx][aidx][0] == 'policy':
- to_store['security/access_control/policy'] = \
- secinfo[idx][aidx][1]
- if secinfo[idx][0] == 'ssidref':
- to_store['security/ssidref'] = str(secinfo[idx][1])
-
if not self._readVm('xend/restart_count'):
to_store['xend/restart_count'] = str(0)
@@ -2101,15 +2092,6 @@ class XendDomainInfo:
info["maxmem_kb"] = XendNode.instance() \
.physinfo_dict()['total_memory'] * 1024
- #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
if 'ssidref' in info:
info.pop('ssidref')
@@ -2193,7 +2175,133 @@ class XendDomainInfo:
return self.info.get('tools_version', {})
def get_metrics(self):
return self.metrics.get_uuid();
-
+
+
+ def get_security_label(self):
+ domid = self.getDomid()
+
+ from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
+ xspol = XSPolicyAdminInstance().get_loaded_policy()
+
+ if domid == 0:
+ if xspol:
+ label = xspol.policy_get_domain_label_formatted(domid)
+ else:
+ label = ""
+ else:
+ label = self.info.get('security_label', '')
+ return label
+
+ def set_security_label(self, seclab, old_seclab, xspol=None):
+ """
+ Set the security label of a domain from its old to
+ a new value.
+ @param seclab New security label formatted in the form
+ <policy type>:<policy name>:<vm label>
+ @param old_seclab The current security label that the
+ VM must have.
+ @param xspol An optional policy under which this
+ update should be done. If not given,
+ then the current active policy is used.
+ @return Returns return code, a string with errors from
+ the hypervisor's operation, old label of the
+ domain
+ """
+ rc = 0
+ errors = ""
+ old_label = ""
+ new_ssidref = 0
+ domid = self.getDomid()
+ res_labels = None
+
+ from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
+ from xen.util import xsconstants
+
+ state = self._stateGet()
+ # Relabel only HALTED or RUNNING or PAUSED domains
+ if domid != 0 and \
+ state not in \
+ [ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
+ DOM_STATE_SUSPENDED ]:
+ log.warn("Relabeling domain not possible in state '%s'" %
+ DOM_STATES[state])
+ return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
+
+ # Remove security label. Works only for halted domains
+ if not seclab or seclab == "":
+ if state not in [ DOM_STATE_HALTED ]:
+ return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
+
+ if self.info.has_key('security_label'):
+ old_label = self.info['security_label']
+ # Check label against expected one.
+ if old_label != old_seclab:
+ return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
+ del self.info['security_label']
+ xen.xend.XendDomain.instance().managed_config_save(self)
+ return (xsconstants.XSERR_SUCCESS, "", "", 0)
+
+ tmp = seclab.split(":")
+ if len(tmp) != 3:
+ return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
+ typ, policy, label = tmp
+
+ poladmin = XSPolicyAdminInstance()
+ if not xspol:
+ xspol = poladmin.get_policy_by_name(policy)
+
+ if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
+ #if domain is running or paused try to relabel in hypervisor
+ if not xspol:
+ return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
+
+ if typ != xspol.get_type_name() or \
+ policy != xspol.get_name():
+ return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
+
+ if typ == xsconstants.ACM_POLICY_ID:
+ new_ssidref = xspol.vmlabel_to_ssidref(label)
+ if new_ssidref == xsconstants.INVALID_SSIDREF:
+ return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
+
+ # Check that all used resources are accessible under the
+ # new label
+ if not security.resources_compatible_with_vmlabel(xspol,
+ self, label):
+ return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
+
+ #Check label against expected one.
+ old_label = self.get_security_label()
+ if old_label != old_seclab:
+ return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
+
+ # relabel domain in the hypervisor
+ rc, errors = security.relabel_domains([[domid, new_ssidref]])
+ log.info("rc from relabeling in HV: %d" % rc)
+ else:
+ return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
+
+ if rc == 0:
+ # HALTED, RUNNING or PAUSED
+ if domid == 0:
+ if xspol:
+ ssidref = poladmin.set_domain0_bootlabel(xspol, label)
+ else:
+ return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
+ else:
+ if self.info.has_key('security_label'):
+ old_label = self.info['security_label']
+ # Check label against expected one, unless wildcard
+ if old_label != old_seclab:
+ return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
+
+ self.info['security_label'] = seclab
+ try:
+ xen.xend.XendDomain.instance().managed_config_save(self)
+ except:
+ pass
+ return (rc, errors, old_label, new_ssidref)
+
def get_on_shutdown(self):
after_shutdown = self.info.get('actions_after_shutdown')
if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendError.py
--- a/tools/python/xen/xend/XendError.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/XendError.py Mon Jul 09 14:51:44 2007 +0100
@@ -174,6 +174,23 @@ class NetworkError(XendAPIError):
def __str__(self):
return 'NETWORK_ERROR: %s %s' % (self.error, self.network)
+
+from xen.util.xsconstants import xserr2string
+
+class SecurityError(XendAPIError):
+ def __init__(self, error, message=None):
+ XendAPIError.__init__(self)
+ self.error = error
+ if not message:
+ self.message = xserr2string(-error)
+ else:
+ self.message = message
+
+ def get_api_error(self):
+ return ['SECURITY_ERROR', self.error, self.message]
+
+ def __str__(self):
+ return 'SECURITY_ERROR: %s:%s' % (self.error, self.message)
XEND_ERROR_AUTHENTICATION_FAILED = ('ELUSER', 'Authentication Failed')
XEND_ERROR_SESSION_INVALID = ('EPERMDENIED', 'Session Invalid')
@@ -188,4 +205,5 @@ XEND_ERROR_VTPM_INVALID = ('EVT
XEND_ERROR_VTPM_INVALID = ('EVTPMINVALID', 'VTPM Invalid')
XEND_ERROR_VDI_INVALID = ('EVDIINVALID', 'VDI Invalid')
XEND_ERROR_SR_INVALID = ('ESRINVALID', 'SR Invalid')
+XEND_ERROR_XSPOLICY_INVALID = ('EXSPOLICYINVALID', 'XS Invalid')
XEND_ERROR_TODO = ('ETODO', 'Lazy Programmer Error')
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendVDI.py
--- a/tools/python/xen/xend/XendVDI.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/XendVDI.py Mon Jul 09 14:51:44 2007 +0100
@@ -23,6 +23,7 @@ import os
from xen.util.xmlrpclib2 import stringify
from xmlrpclib import dumps, loads
+from xen.util import security, xsconstants
KB = 1024
MB = 1024 * 1024
@@ -160,6 +161,17 @@ class XendVDI(AutoSaveObject):
def get_location(self):
raise NotImplementedError()
+
+ def set_security_label(self, sec_lab, old_lab):
+ image = self.get_location()
+ rc = security.set_resource_label_xapi(image, sec_lab, old_lab)
+ if rc != xsconstants.XSERR_SUCCESS:
+ raise SecurityError(rc)
+ return rc
+
+ def get_security_label(self):
+ image = self.get_location()
+ return security.get_resource_label_xapi(image)
class XendQCoWVDI(XendVDI):
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendXSPolicy.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/xend/XendXSPolicy.py Mon Jul 09 14:51:44 2007 +0100
@@ -0,0 +1,222 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (c) 2007 IBM Corporation
+# Copyright (c) 2006 Xensource
+#============================================================================
+
+import logging
+from xen.xend.XendBase import XendBase
+from xen.xend.XendError import *
+from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
+from xen.util import xsconstants, security
+import base64
+
+log = logging.getLogger("xend.XendXSPolicy")
+log.setLevel(logging.TRACE)
+
+
+class XendXSPolicy(XendBase):
+ """ Administration class for an XSPolicy. """
+
+ def getClass(self):
+ return "XSPolicy"
+
+ def getMethods(self):
+ methods = ['activate_xspolicy']
+ return XendBase.getMethods() + methods
+
+ def getFuncs(self):
+ funcs = [ 'get_xstype',
+ 'set_xspolicy',
+ 'get_xspolicy',
+ 'rm_xsbootpolicy',
+ 'get_resource_label',
+ 'set_resource_label',
+ 'get_labeled_resources' ]
+ return XendBase.getFuncs() + funcs
+
+ getClass = classmethod(getClass)
+ getMethods = classmethod(getMethods)
+ getFuncs = classmethod(getFuncs)
+
+ def __init__(self, xspol, record, uuid):
+ """ xspol = actual XSPolicy object """
+ self.xspol = xspol
+ XendBase.__init__(self, uuid, record)
+
+ def get_record(self):
+ xspol_record = {
+ 'uuid' : self.get_uuid(),
+ 'flags' : XSPolicyAdminInstance().get_policy_flags(self.xspol),
+ 'repr' : self.xspol.toxml(),
+ 'type' : self.xspol.get_type(),
+ }
+ return xspol_record
+
+ def get_xstype(self):
+ return XSPolicyAdminInstance().isXSEnabled()
+
+ def set_xspolicy(self, xstype, xml, flags, overwrite):
+ ref = ""
+ xstype = int(xstype)
+ flags = int(flags)
+
+ polstate = { 'xs_ref': "", 'repr' : "", 'type' : 0,
+ 'flags' : 0 , 'version': 0 , 'errors' : "", 'xserr' : 0 }
+ if xstype == xsconstants.XS_POLICY_ACM:
+ poladmin = XSPolicyAdminInstance()
+ try:
+ (xspol, rc, errors) = poladmin.add_acmpolicy_to_system(
+ xml, flags,
+ overwrite)
+ if rc != 0:
+ polstate.update( { 'xserr' : rc,
+ 'errors': base64.b64encode(errors) } )
+ else:
+ ref = xspol.get_ref()
+ polstate = {
+ 'xs_ref' : ref,
+ 'flags' : poladmin.get_policy_flags(xspol),
+ 'type' : xstype,
+ 'repr' : "",
+ 'version': xspol.get_version(),
+ 'errors' : base64.b64encode(errors),
+ 'xserr' : rc,
+ }
+ except Exception, e:
+ raise
+ else:
+ raise SecurityError(-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED)
+ return polstate
+
+ def activate_xspolicy(self, flags):
+ flags = int(flags)
+ rc = -xsconstants.XSERR_GENERAL_FAILURE
+ poladmin = XSPolicyAdminInstance()
+ try:
+ rc = poladmin.activate_xspolicy(self.xspol, flags)
+ except Exception, e:
+ log.info("Activate_policy: %s" % str(e))
+ if rc != flags:
+ raise SecurityError(rc)
+ return flags
+
+ def get_xspolicy(self):
+ polstate = { 'xs_ref' : "",
+ 'repr' : "",
+ 'type' : 0,
+ 'flags' : 0,
+ 'version': "",
+ 'errors' : "",
+ 'xserr' : 0 }
+ poladmin = XSPolicyAdminInstance()
+ refs = poladmin.get_policies_refs()
+ # Will return one or no policy
+ if refs and len(refs) > 0:
+ ref = refs[0]
+ xspol = XSPolicyAdminInstance().policy_from_ref(ref)
+ try:
+ xspol.grab_lock()
+
+ polstate = {
+ 'xs_ref' : ref,
+ 'repr' : xspol.toxml(),
+ 'type' : xspol.get_type(),
+ 'flags' : poladmin.get_policy_flags(xspol),
+ 'version': xspol.get_version(),
+ 'errors' : "",
+ 'xserr' : 0,
+ }
+ finally:
+ if xspol:
+ xspol.unlock()
+ return polstate
+
+ def rm_xsbootpolicy(self):
+ rc = XSPolicyAdminInstance().rm_bootpolicy()
+ if rc != xsconstants.XSERR_SUCCESS:
+ raise SecurityError(rc)
+
+ def get_labeled_resources(self):
+ return security.get_labeled_resources_xapi()
+
+ def set_resource_label(self, resource, sec_lab, old_lab):
+ rc = security.set_resource_label_xapi(resource, sec_lab, old_lab)
+ if rc != xsconstants.XSERR_SUCCESS:
+ raise SecurityError(rc)
+
+ def get_resource_label(self, resource):
+ res = security.get_resource_label_xapi(resource)
+ return res
+
+ get_xstype = classmethod(get_xstype)
+ get_xspolicy = classmethod(get_xspolicy)
+ set_xspolicy = classmethod(set_xspolicy)
+ rm_xsbootpolicy = classmethod(rm_xsbootpolicy)
+ set_resource_label = classmethod(set_resource_label)
+ get_resource_label = classmethod(get_resource_label)
+ get_labeled_resources = classmethod(get_labeled_resources)
+
+
+class XendACMPolicy(XendXSPolicy):
+ """ Administration class of an ACMPolicy """
+
+ def getClass(self):
+ return "ACMPolicy"
+
+ def getAttrRO(self):
+ attrRO = [ 'xml',
+ 'map',
+ 'binary',
+ 'header' ]
+ return XendXSPolicy.getAttrRO() + attrRO
+
+ getClass = classmethod(getClass)
+ getAttrRO = classmethod(getAttrRO)
+
+ def __init__(self, acmpol, record, uuid):
+ """ acmpol = actual ACMPolicy object """
+ self.acmpol = acmpol
+ XendXSPolicy.__init__(self, acmpol, record, uuid)
+
+ def get_record(self):
+ polstate = {
+ 'uuid' : self.get_uuid(),
+ 'flags' : XSPolicyAdminInstance().get_policy_flags(self.acmpol),
+ 'repr' : self.acmpol.toxml(),
+ 'type' : self.acmpol.get_type(),
+ }
+ return polstate
+
+ def get_header(self):
+ header = {
+ 'policyname' : "", 'policyurl' : "", 'reference' : "",
+ 'date' : "", 'namespaceurl' : "", 'version' : "",
+ }
+ try:
+ header = self.acmpol.get_header_fields_map()
+ except:
+ pass
+ return header
+
+ def get_xml(self):
+ return self.acmpol.toxml()
+
+ def get_map(self):
+ return self.acmpol.get_map()
+
+ def get_binary(self):
+ polbin = self.acmpol.get_bin()
+ return base64.b64encode(polbin)
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/XendXSPolicyAdmin.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/xend/XendXSPolicyAdmin.py Mon Jul 09 14:51:44
2007 +0100
@@ -0,0 +1,313 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2006,2007 International Business Machines Corp.
+# Author: Stefan Berger <stefanb@xxxxxxxxxx>
+#============================================================================
+import os
+import shutil
+
+from xml.dom import minidom, Node
+
+from xen.xend.XendLogging import log
+from xen.xend import uuid
+from xen.util import security, xsconstants, dictio, bootloader
+from xen.util.xspolicy import XSPolicy
+from xen.util.acmpolicy import ACMPolicy
+from xen.xend.XendError import SecurityError
+
+XS_MANAGED_POLICIES_FILE = "/etc/xen/acm-security/policies/managed_policies"
+
+class XSPolicyAdmin:
+ """ The class that handles the managed policies in the system.
+ Handles adding and removing managed policies. All managed
+ policies are handled using a reference (UUID) which is
+ assigned to the policy by this class.
+ """
+
+ def __init__(self, maxpolicies):
+ """ Create a management class for managing the system's
+ policies.
+
+ @param maxpolicies: The max. number of policies allowed
+ on the system (currently '1')
+ """
+ self.maxpolicies = maxpolicies
+ try:
+ self.policies = dictio.dict_read("managed_policies",
+ XS_MANAGED_POLICIES_FILE)
+ except Exception, e:
+ self.policies = {}
+
+ self.xsobjs = {}
+ for ref, data in self.policies.items():
+ name = data[0]
+ typ = data[1]
+ try:
+ if typ == xsconstants.ACM_POLICY_ID:
+ self.xsobjs[ref] = ACMPolicy(name=name, ref=ref)
+ else:
+ del self.policies[ref]
+ except Exception, e:
+ log.error("XSPolicyAdmin: Could not find policy '%s': %s" %
+ (name, str(e)))
+ del self.policies[ref]
+ log.debug("XSPolicyAdmin: Known policies: %s" % self.policies)
+
+ def isXSEnabled(self):
+ """ Check whether 'security' is enabled on this system.
+ This currently only checks for ACM-enablement.
+ """
+ rc = 0
+ if security.on():
+ rc |= xsconstants.XS_POLICY_ACM
+ return rc
+
+ def add_acmpolicy_to_system(self, xmltext, flags, overwrite):
+ """ Add an ACM policy's xml representation to the system. The
+ policy will automatically be compiled
+ flags:
+ XS_INST_BOOT : make policy the one to boot the system with
+ by default; if there's a policy already installed,
+ refuse to install this policy unless its one with
+ the same name
+ XS_INST_LOAD : load the policy immediately; if this does not work
+ refuse to install this policy
+ overwrite:
+ If any policy is installed and this is False, refuse to install
+ this policy
+ If flags is True, then any existing policy will be removed from
+ the system and the new one will be installed
+ """
+ errors = ""
+ loadedpol = self.get_loaded_policy()
+ if loadedpol:
+ # This is meant as an update to a currently loaded policy
+ if flags & xsconstants.XS_INST_LOAD == 0:
+ raise SecurityError(-xsconstants.XSERR_POLICY_LOADED)
+ rc, errors = loadedpol.update(xmltext)
+ if rc == 0:
+ self.rm_bootpolicy()
+ irc = self.activate_xspolicy(loadedpol, flags)
+ return (loadedpol, rc, errors)
+
+ try:
+ dom = minidom.parseString(xmltext.encode("utf-8"))
+ except:
+ raise SecurityError(-xsconstants.XSERR_BAD_XML)
+
+ ref = uuid.createString()
+
+ acmpol = ACMPolicy(dom=dom, ref=ref)
+
+ #First some basic tests that do not modify anything:
+
+ if flags & xsconstants.XS_INST_BOOT and not overwrite:
+ filename = acmpol.get_filename(".bin","",dotted=True)
+ if bootloader.get_default_policy != None and \
+ not bootloader.loads_default_policy(filename):
+ raise SecurityError(-xsconstants.XSERR_BOOTPOLICY_INSTALLED)
+
+ if not overwrite and len(self.policies) >= self.maxpolicies:
+ raise SecurityError(-xsconstants.XSERR_BOOTPOLICY_INSTALLED)
+
+ if overwrite:
+ #This should only give one key since only one policy is
+ #allowed.
+ keys = self.policies.keys()
+ for k in keys:
+ self.rm_bootpolicy()
+ rc = self.rm_policy_from_system(k, force=overwrite)
+ if rc != xsconstants.XSERR_SUCCESS:
+ raise SecurityError(rc)
+
+ rc = acmpol.compile()
+ if rc != 0:
+ raise SecurityError(rc)
+
+ if flags & xsconstants.XS_INST_LOAD:
+ rc = acmpol.loadintohv()
+ if rc != 0:
+ raise SecurityError(rc)
+
+ if flags & xsconstants.XS_INST_BOOT:
+ rc = self.make_boot_policy(acmpol)
+ if rc != 0:
+ # If it cannot be installed due to unsupported
+ # bootloader, let it be ok.
+ pass
+
+ if dom:
+ new_entry = { ref : tuple([acmpol.get_name(),
+ xsconstants.ACM_POLICY_ID]) }
+ self.policies.update(new_entry)
+ self.xsobjs[ref] = acmpol
+ dictio.dict_write(self.policies,
+ "managed_policies",
+ XS_MANAGED_POLICIES_FILE)
+ return (acmpol, xsconstants.XSERR_SUCCESS, errors)
+
+ def make_boot_policy(self, acmpol):
+ spolfile = acmpol.get_filename(".bin")
+ dpolfile = "/boot/" + acmpol.get_filename(".bin","",dotted=True)
+ if not os.path.isfile(spolfile):
+ log.error("binary policy file does not exist.")
+ return -xsconstants.XSERR_FILE_ERROR
+ try:
+ shutil.copyfile(spolfile, dpolfile)
+ except:
+ return -xsconstants.XSERR_FILE_ERROR
+
+ try:
+ filename = acmpol.get_filename(".bin","",dotted=True)
+ if bootloader.set_default_boot_policy(filename) != True:
+ return xsconstants.XSERR_BOOTPOLICY_INSTALL_ERROR
+ except:
+ return xsconstants.XSERR_FILE_ERROR
+ return xsconstants.XSERR_SUCCESS
+
+ def activate_xspolicy(self, xspol, flags):
+ rc = xsconstants.XSERR_SUCCESS
+ if flags & xsconstants.XS_INST_LOAD:
+ rc = xspol.loadintohv()
+ if rc == xsconstants.XSERR_SUCCESS and \
+ flags & xsconstants.XS_INST_BOOT:
+ rc = self.make_boot_policy(xspol)
+ if rc == xsconstants.XSERR_SUCCESS:
+ rc = flags
+ return rc
+
+ def rm_policy_from_system(self, ref, force=False):
+ if self.policies.has_key(ref):
+ acmpol = self.xsobjs[ref]
+ rc = acmpol.destroy()
+ if rc == xsconstants.XSERR_SUCCESS or force:
+ del self.policies[ref]
+ del self.xsobjs[ref]
+ dictio.dict_write(self.policies,
+ "managed_policies",
+ XS_MANAGED_POLICIES_FILE)
+ rc = xsconstants.XSERR_SUCCESS
+ return rc
+
+ def rm_bootpolicy(self):
+ """ Remove any (ACM) boot policy from the grub configuration file
+ """
+ rc = 0
+ title = bootloader.get_default_title()
+ if title != None:
+ polnames = []
+ for (k, v) in self.xsobjs.items():
+ polnames.append(v.get_filename(".bin","",dotted=True))
+ bootloader.rm_policy_from_boottitle(title, polnames)
+ else:
+ rc = -xsconstants.XSERR_NO_DEFAULT_BOOT_TITLE
+ return rc
+
+ def get_policy_flags(self, acmpol):
+ """ Get the currently active flags of a policy, i.e., whether the
+ system is using this policy as its boot policy for the default
+ boot title.
+ """
+ flags = 0
+
+ filename = acmpol.get_filename(".bin","", dotted=True)
+ if bootloader.loads_default_policy(filename):
+ flags |= xsconstants.XS_INST_BOOT
+
+ if acmpol.isloaded():
+ flags |= xsconstants.XS_INST_LOAD
+ return flags
+
+ def get_policies(self):
+ """ Get all managed policies. """
+ return self.xsobjs.values()
+
+ def get_policies_refs(self):
+ """ Get all managed policies' references. """
+ return self.xsobjs.keys()
+
+ def has_ref(self, ref):
+ """ Check whether there is a policy with the given reference """
+ return self.xsobjs.has_key(ref)
+
+ def policy_from_ref(self, ref):
+ """ Get the policy's object given its reference """
+ if ref in self.xsobjs.keys():
+ return self.xsobjs[ref]
+ return None
+
+ def ref_from_polname(self, polname):
+ """ Get the reference of the policy given its name """
+ ref = None
+ for (k, v) in self.xsobjs.items():
+ if v.get_name() == polname:
+ ref = k
+ break
+ return ref
+
+ def lock_policy(self, ref):
+ """ get exclusive access to a policy """
+ self.xsobjs[ref].grab_lock()
+
+ def unlock_policy(self, ref):
+ """ release exclusive access to a policy """
+ self.xsobjs[ref].unlock()
+
+ def get_loaded_policy(self):
+ for pol in self.xsobjs.values():
+ if pol.isloaded():
+ return pol
+ return None
+
+ def get_policy_by_name(self, name):
+ for pol in self.xsobjs.values():
+ if pol.get_name() == name:
+ return pol
+ return None
+
+ def get_domain0_bootlabel(self):
+ """ Get the domain0 bootlabel from the default boot title """
+ title = ""
+ def_title = bootloader.get_default_title()
+ line = bootloader.get_kernel_val(def_title, "ssidref")
+ if line:
+ parms = line.split(":",1)
+ if len(parms) > 1:
+ title = parms[1]
+ return title
+
+ def set_domain0_bootlabel(self, xspol, label):
+ """ Set the domain-0 bootlabel under the given policy """
+ return xspol.set_vm_bootlabel(label)
+
+ def rm_domain0_bootlabel(self):
+ """ Remove the domain-0 bootlabel from the default boot title """
+ def_title = bootloader.get_default_title()
+ return bootloader.set_kernel_attval(def_title, "ssidref", None)
+
+ def ssidref_to_vmlabel(self, ssidref):
+ """ Given an ssidref, return the vmlabel under the current policy """
+ vmlabel = ""
+ pol = self.get_loaded_policy()
+ if pol:
+ vmlabel = pol.policy_get_domain_label_by_ssidref_formatted(ssidref)
+ return vmlabel
+
+poladmin = None
+
+def XSPolicyAdminInstance(maxpolicies=1):
+ global poladmin
+ if poladmin == None:
+ poladmin = XSPolicyAdmin(maxpolicies)
diff -r 83fd4ad219cd -r aa640601575f tools/python/xen/xend/server/blkif.py
--- a/tools/python/xen/xend/server/blkif.py Mon Jul 09 14:30:46 2007 +0100
+++ b/tools/python/xen/xend/server/blkif.py Mon Jul 09 14:51:44 2007 +0100
@@ -73,10 +73,17 @@ class BlkifController(DevController):
back['uuid'] = uuid
if security.on():
- (label, ssidref, policy) = security.get_res_security_details(uname)
- back.update({'acm_label' : label,
- 'acm_ssidref': str(ssidref),
- 'acm_policy' : policy})
+ (label, ssidref, policy) = \
+ security.get_res_security_details(uname)
+ domain_label = self.vm.get_security_label()
+ if domain_label:
+ rc = security.res_security_check_xapi(label, ssidref, policy,
+ domain_label)
+ if rc == 0:
+ raise VmError("VM's access to block device '%s' denied." %
+ uname)
+ else:
+ raise VmError("VM must have a security label.")
devid = blkif.blkdev_name_to_number(dev)
if devid is None:
diff -r 83fd4ad219cd -r aa640601575f tools/security/policies/security_policy.xsd
--- a/tools/security/policies/security_policy.xsd Mon Jul 09 14:30:46
2007 +0100
+++ b/tools/security/policies/security_policy.xsd Mon Jul 09 14:51:44
2007 +0100
@@ -22,7 +22,7 @@
<xsd:element name="Reference" type="xsd:string"
minOccurs="0" maxOccurs="1" />
<xsd:element name="Date" minOccurs="0"
maxOccurs="1" type="xsd:string"></xsd:element>
<xsd:element name="NameSpaceUrl" minOccurs="0"
maxOccurs="1" type="xsd:string"></xsd:element>
- <xsd:element name="Version" minOccurs="0"
maxOccurs="1" type="VersionFormat"/>
+ <xsd:element name="Version" minOccurs="1"
maxOccurs="1" type="VersionFormat"/>
<xsd:element ref="FromPolicy" minOccurs="0"
maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
@@ -91,23 +91,23 @@
<xsd:sequence>
<xsd:element maxOccurs="unbounded"
minOccurs="1" ref="Type" />
</xsd:sequence>
- <xsd:attribute name="name" type="xsd:string"
use="optional"></xsd:attribute>
+ <xsd:attribute name="name" type="xsd:string"
use="required"></xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="VirtualMachineLabel">
<xsd:complexType>
<xsd:sequence>
- <xsd:element ref="Name"></xsd:element>
+ <xsd:element name="Name"
type="NameWithFrom"></xsd:element>
<xsd:element ref="SimpleTypeEnforcementTypes"
minOccurs="0" maxOccurs="unbounded" />
- <xsd:element ref="ChineseWallTypes"
minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element name="ChineseWallTypes"
type="SingleChineseWallType" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ResourceLabel">
<xsd:complexType>
<xsd:sequence>
- <xsd:element ref="Name"></xsd:element>
- <xsd:element ref="SimpleTypeEnforcementTypes"
minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element name="Name"
type="NameWithFrom"></xsd:element>
+ <xsd:element name="SimpleTypeEnforcementTypes"
type="SingleSimpleTypeEnforcementType" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
@@ -131,4 +131,21 @@
<xsd:pattern
value="[0-9]{1,8}.[0-9]{1,8}"></xsd:pattern>
</xsd:restriction>
</xsd:simpleType>
+ <xsd:complexType name="NameWithFrom">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="from" type="xsd:string"
use="optional"></xsd:attribute>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ <xsd:complexType name="SingleSimpleTypeEnforcementType">
+ <xsd:sequence>
+ <xsd:element maxOccurs="1" minOccurs="1" ref="Type" />
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="SingleChineseWallType">
+ <xsd:sequence>
+ <xsd:element maxOccurs="1" minOccurs="1" ref="Type" />
+ </xsd:sequence>
+ </xsd:complexType>
</xsd:schema>
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|