[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [Xen-API] Extension of the Xen-API for managing Xen Security Policies
# 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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |