[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [XSM] correctly rename, relocate and patch the security.py file
# HG changeset patch # User kfraser@xxxxxxxxxxxxxxxxxxxxx # Date 1189098318 -3600 # Node ID 49b67fdefcd6e1ffb70be155579dfbd7b8a38767 # Parent 453f87d6e7f085d9fb1d90e40e12a829c69876f9 [XSM] correctly rename, relocate and patch the security.py file This patch corrects an error in the original XSM tools patch. The original patch did not rename, relocate and patch the security.py file from changeset 15730:256160ff19b7. This patch addresses this issue as well as any updates made to security.py in xen-staging during the merge of XSM. Signed-off-by: George Coker <gscoker@xxxxxxxxxxxxxx> --- tools/python/xen/util/security.py | 1301 ----------------------------------- tools/python/xen/util/xsm/acm/acm.py | 70 + 2 files changed, 53 insertions(+), 1318 deletions(-) diff -r 453f87d6e7f0 -r 49b67fdefcd6 tools/python/xen/util/security.py --- a/tools/python/xen/util/security.py Thu Sep 06 18:04:42 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1301 +0,0 @@ -#=========================================================================== -# 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 International Business Machines Corp. -# Author: Reiner Sailer -# Author: Bryan D. Payne <bdpayne@xxxxxxxxxx> -# Author: Stefan Berger <stefanb@xxxxxxxxxx> -#============================================================================ - -import commands -import logging -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.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" -res_label_filename = policy_dir_prefix + "/resource_labels" -boot_filename = "/boot/grub/menu.lst" -altboot_filename = "/boot/grub/grub.conf" -xensec_xml2bin = "/usr/sbin/xensec_xml2bin" -xensec_tool = "/usr/sbin/xensec_tool" - -#global patterns for map file -#police_reference_tagname = "POLICYREFERENCENAME" -primary_entry_re = re.compile("\s*PRIMARY\s+.*", re.IGNORECASE) -secondary_entry_re = re.compile("\s*SECONDARY\s+.*", re.IGNORECASE) -label_template_re = re.compile(".*security_label_template.xml", re.IGNORECASE) -mapping_filename_re = re.compile(".*\.map", re.IGNORECASE) -policy_reference_entry_re = re.compile("\s*POLICYREFERENCENAME\s+.*", re.IGNORECASE) -vm_label_re = re.compile("\s*LABEL->SSID\s+VM\s+.*", re.IGNORECASE) -res_label_re = re.compile("\s*LABEL->SSID\s+RES\s+.*", re.IGNORECASE) -all_label_re = re.compile("\s*LABEL->SSID\s+.*", re.IGNORECASE) -access_control_re = re.compile("\s*access_control\s*=", re.IGNORECASE) - -#global patterns for boot configuration file -xen_title_re = re.compile("\s*title\s+XEN", re.IGNORECASE) -any_title_re = re.compile("\s*title\s", re.IGNORECASE) -xen_kernel_re = re.compile("\s*kernel.*xen.*\.gz", re.IGNORECASE) -kernel_ver_re = re.compile("\s*module.*vmlinuz", re.IGNORECASE) -any_module_re = re.compile("\s*module\s", re.IGNORECASE) -empty_line_re = re.compile("^\s*$") -binary_name_re = re.compile(".*[chwall|ste|chwall_ste].*\.bin", re.IGNORECASE) -policy_name_re = re.compile(".*[chwall|ste|chwall_ste].*", re.IGNORECASE) - -#decision hooks known to the hypervisor -ACMHOOK_sharing = 1 -ACMHOOK_authorization = 2 - -#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 -# whoever raises this exception must provide error information. -class ACMError(Exception): - def __init__(self,value): - self.value = value - def __str__(self): - return repr(self.value) - - - -def err(msg): - """Raise ACM exception. - """ - raise ACMError(msg) - - - -active_policy = None - - -def mapfile_lock(): - __mapfile_lock.acquire() - -def mapfile_unlock(): - __mapfile_lock.release() - - -def refresh_security_policy(): - """ - retrieves security policy - """ - global active_policy - - try: - active_policy = acm.policy() - except: - active_policy = "INACTIVE" - -# now set active_policy -refresh_security_policy() - -def on(): - """ - returns none if security policy is off (not compiled), - any string otherwise, use it: if not security.on() ... - """ - refresh_security_policy() - 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' must be changed." % 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 0x0 - raise VmError("security.calc_dom_ssidref_from_info: info of type '%s'" - "not supported." % type(info)) - - -def getmapfile(policyname): - """ - in: if policyname is None then the currently - active hypervisor policy is used - out: 1. primary policy, 2. secondary policy, - 3. open file descriptor for mapping file, and - 4. True if policy file is available, False otherwise - """ - if not policyname: - policyname = active_policy - map_file_ok = False - primary = None - secondary = None - #strip last part of policy as file name part - policy_dir_list = string.split(policyname, ".") - policy_file = policy_dir_list.pop() - if len(policy_dir_list) > 0: - policy_dir = string.join(policy_dir_list, "/") + "/" - else: - policy_dir = "" - - map_filename = policy_dir_prefix + "/" + policy_dir + policy_file + ".map" - # check if it is there, if not check if policy file is there - if not os.path.isfile(map_filename): - policy_filename = policy_dir_prefix + "/" + policy_dir + policy_file + "-security_policy.xml" - if not os.path.isfile(policy_filename): - err("Policy file \'" + policy_filename + "\' not found.") - else: - err("Mapping file \'" + map_filename + "\' not found." + - " Use xm makepolicy to create it.") - - f = open(map_filename) - for line in f: - if policy_reference_entry_re.match(line): - l = line.split() - if (len(l) == 2) and (l[1] == policyname): - map_file_ok = True - elif primary_entry_re.match(line): - l = line.split() - if len(l) == 2: - primary = l[1] - elif secondary_entry_re.match(line): - l = line.split() - if len(l) == 2: - secondary = l[1] - f.close() - f = open(map_filename) - if map_file_ok and primary and secondary: - return (primary, secondary, f, True) - else: - err("Mapping file inconsistencies found. Try makepolicy to create a new one.") - - - -def ssidref2label(ssidref_var): - """ - returns labelname corresponding to ssidref; - maps current policy to default directory - to find mapping file - """ - #1. translated permitted input formats - if isinstance(ssidref_var, str): - ssidref_var.strip() - if ssidref_var[0:2] == "0x": - ssidref = int(ssidref_var[2:], 16) - else: - ssidref = int(ssidref_var) - elif isinstance(ssidref_var, int): - ssidref = ssidref_var - else: - err("Instance type of ssidref not supported (must be of type 'str' or 'int')") - - if ssidref == 0: - from xen.util.acmpolicy import ACM_LABEL_UNLABELED - return ACM_LABEL_UNLABELED - - 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): - labels = sec_labels - elif (secondary == "CHWALL") and (pri_ssid != pri_null_ssid) and (sec_ssid == sec_null_ssid): - labels = pri_labels - elif secondary == "NULL": - labels = pri_labels - else: - for i in pri_labels: - for j in sec_labels: - if (i==j): - labels.append(i) - if len(labels) != 1: - err("Label for ssidref \'" + str(ssidref) + - "\' unknown or not unique in policy \'" + active_policy + "\'") - - return labels[0] - - - -def label2ssidref(labelname, policyname, typ): - """ - returns ssidref corresponding to labelname; - maps current policy to default directory - to find mapping file """ - - if policyname in ['NULL', 'INACTIVE', 'DEFAULT']: - err("Cannot translate labels for \'" + policyname + "\' policy.") - - allowed_types = ['ANY'] - if typ == 'dom': - allowed_types.append('VM') - elif typ == 'res': - allowed_types.append('RES') - else: - err("Invalid type. Must specify 'dom' or 'res'.") - - 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): - """ - looks up ssidref from security field - and refreshes the value if label exists - """ - #called by dom0, policy could have changed after xen.utils.security was initialized - refresh_security_policy() - - security = None - if isinstance(config, dict): - security = config['security'] - elif isinstance(config, list): - security = sxp.child_value(config, 'security') - else: - err("Instance type of config parameter not supported.") - if not security: - #nothing to do (no security label attached) - return config - - policyname = None - labelname = None - # compose new security field - for idx in range(0, len(security)): - if security[idx][0] == 'ssidref': - security.pop(idx) - break - elif security[idx][0] == 'access_control': - for jdx in [1, 2]: - if security[idx][jdx][0] == 'label': - labelname = security[idx][jdx][1] - elif security[idx][jdx][0] == 'policy': - policyname = security[idx][jdx][1] - else: - err("Illegal field in access_control") - #verify policy is correct - if active_policy != policyname: - err("Policy \'" + str(policyname) + - "\' in label does not match active policy \'" - + str(active_policy) +"\'!") - - new_ssidref = label2ssidref(labelname, policyname, 'dom') - if not new_ssidref: - err("SSIDREF refresh failed!") - - security.append([ 'ssidref',str(new_ssidref)]) - security = ['security', security ] - - for idx in range(0,len(config)): - if config[idx][0] == 'security': - config.pop(idx) - break - config.append(security) - - - -def get_ssid(domain): - """ - enables domains to retrieve the label / ssidref of a running domain - """ - if not on(): - err("No policy active.") - - if isinstance(domain, str): - domain_int = int(domain) - elif isinstance(domain, int): - domain_int = domain - else: - err("Illegal parameter type.") - try: - ssid_info = acm.getssid(int(domain_int)) - except: - err("Cannot determine security information.") - - if active_policy in ["DEFAULT"]: - label = "DEFAULT" - else: - label = ssidref2label(ssid_info["ssidref"]) - return(ssid_info["policyreference"], - label, - ssid_info["policytype"], - ssid_info["ssidref"]) - - - -def get_decision(arg1, arg2): - """ - enables domains to retrieve access control decisions from - the hypervisor Access Control Module. - IN: args format = ['domid', id] or ['ssidref', ssidref] - or ['access_control', ['policy', policy], ['label', label], ['type', type]] - """ - - if not on(): - err("No policy active.") - - #translate labels before calling low-level function - if arg1[0] == 'access_control': - if (arg1[1][0] != 'policy') or (arg1[2][0] != 'label') or (arg1[3][0] != 'type'): - err("Argument type not supported.") - ssidref = label2ssidref(arg1[2][1], arg1[1][1], arg1[3][1]) - arg1 = ['ssidref', str(ssidref)] - if arg2[0] == 'access_control': - if (arg2[1][0] != 'policy') or (arg2[2][0] != 'label') or (arg2[3][0] != 'type'): - err("Argument type not supported.") - ssidref = label2ssidref(arg2[2][1], arg2[1][1], arg2[3][1]) - arg2 = ['ssidref', str(ssidref)] - - # accept only int or string types for domid and ssidref - if isinstance(arg1[1], int): - arg1[1] = str(arg1[1]) - if isinstance(arg2[1], int): - arg2[1] = str(arg2[1]) - if not isinstance(arg1[1], str) or not isinstance(arg2[1], str): - err("Invalid id or ssidref type, string or int required") - - try: - decision = acm.getdecision(arg1[0], arg1[1], arg2[0], arg2[1], - ACMHOOK_sharing) - except: - err("Cannot determine decision.") - - if decision: - return decision - else: - err("Cannot determine decision (Invalid parameter).") - - -def has_authorization(ssidref): - """ Check if the domain with the given ssidref has authorization to - run on this system. To have authoriztion dom0's STE types must - be a superset of that of the domain's given through its ssidref. - """ - rc = True - dom0_ssidref = int(acm.getssid(0)['ssidref']) - decision = acm.getdecision('ssidref', str(dom0_ssidref), - 'ssidref', str(ssidref), - ACMHOOK_authorization) - if decision == "DENIED": - rc = False - return rc - - -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, "."), "/") - if not os.path.isfile(policy_dir_prefix + "/" + policy_file + "-security_policy.xml"): - err("Unknown 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 - policy_file = policy_dir_prefix + "/" + string.join(string.split(policy_name, "."), "/") - if not os.path.isfile(policy_file + ".bin"): - if os.path.isfile(policy_file + "-security_policy.xml"): - err("Binary file does not exist." + - "Please use makepolicy to build the policy binary.") - else: - err("Unknown Policy " + policy_name) - - #require this policy to be the first or the same as installed - if active_policy not in ['DEFAULT', policy_name]: - err("Active policy \'" + active_policy + - "\' incompatible with new policy \'" + policy_name + "\'") - (ret, output) = commands.getstatusoutput(xensec_tool + " loadpolicy " + policy_file + ".bin") - if ret: - err("Loading policy failed:\n" + output) - else: - # refresh active policy - refresh_security_policy() - - - -def dump_policy(): - if active_policy in ['NULL', 'INACTIVE']: - err("\'" + active_policy + "\' policy. Nothing to dump.") - - (ret, output) = commands.getstatusoutput(xensec_tool + " getpolicy") - if ret: - err("Dumping hypervisor policy failed:\n" + output) - print output - - - -def list_labels(policy_name, condition): - if (not policy_name) and (active_policy) in ["NULL", "INACTIVE", "DEFAULT"]: - err("Current policy \'" + active_policy + "\' has no labels defined.\n") - - (primary, secondary, f, pol_exists) = getmapfile(policy_name) - if not f: - if pol_exists: - err("Cannot find mapfile for policy \'" + policy_name + - "\'.\nPlease use makepolicy to create mapping file.") - else: - err("Unknown policy \'" + policy_name + "\'") - - labels = [] - for line in f: - if condition.match(line): - label = line.split()[3] - if label not in labels: - labels.append(label) - return labels - - -def get_res_label(resource): - """Returns resource label information (policytype, label, policy) if - it exists. Otherwise returns null label and policy. - """ - def default_res_label(): - ssidref = NULL_SSIDREF - if on(): - label = ssidref2label(ssidref) - else: - label = None - 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): - """Returns the (label, ssidref, policy) associated with a given - resource from the global resource label file. - """ - def default_security_details(): - ssidref = NULL_SSIDREF - if on(): - label = ssidref2label(ssidref) - else: - label = None - policy = active_policy - return (label, ssidref, policy) - - (label, ssidref, policy) = default_security_details() - - # find the entry associated with this 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() - - # 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 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""" - - if not resource: - return resource - - # sanity check on resource name - try: - (typ, resfile) = resource.split(":", 1) - except: - err("Resource spec '%s' contains no ':' delimiter" % resource) - - if typ == "tap": - try: - (subtype, resfile) = resfile.split(":") - except: - err("Resource spec '%s' contains no tap subtype" % resource) - - 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 - #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 typ == "tap": - typ = typ + ":" + subtype - resource = typ + ":" + resfile - return resource - - -def res_security_check(resource, domain_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(): - #build canonical resource name - resource = unify_resname(resource) - - (label, ssidref, policy) = get_res_security_details(resource) - domac = ['access_control'] - domac.append(['policy', active_policy]) - domac.append(['label', domain_label]) - domac.append(['type', 'dom']) - decision = get_decision(domac, ['ssidref', str(ssidref)]) - - # provide descriptive error messages - if decision == 'DENIED': - if label == ssidref2label(NULL_SSIDREF): - raise ACMError("Resource '"+resource+"' is not labeled") - rtnval = 0 - else: - raise ACMError("Permission denied for resource '"+resource+"' because label '"+label+"' 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. - (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 validate_label(label, policyref): - """ - Make sure that this label is part of the currently enforced policy - and that it reference the current policy. - """ - rc = xsconstants.XSERR_SUCCESS - from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance - curpol = XSPolicyAdminInstance().get_loaded_policy() - if not curpol or curpol.get_name() != policyref: - rc = -xsconstants.XSERR_BAD_LABEL - else: - try: - label2ssidref(label, curpol.get_name() , 'res') - except: - rc = -xsconstants.XSERR_BAD_LABEL - return rc - - -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 and \ - otyp != xsconstants.INVALID_POLICY_PREFIX + \ - xsconstants.ACM_POLICY_ID: - return -xsconstants.XSERR_WRONG_POLICY_TYPE - rc = validate_label(label, policyref) - if rc != xsconstants.XSERR_SUCCESS: - return rc - return set_resource_label(resource, typ, policyref, label, olabel) - - -def is_resource_in_use(resource): - """ - Domain-0 'owns' resources of type 'VLAN', the rest are owned by - the guests. - """ - from xen.xend import XendDomain - lst = [] - if resource.startswith('vlan'): - from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance - curpol = XSPolicyAdminInstance().get_loaded_policy() - policytype, label, policy = get_res_label(resource) - if curpol and \ - policytype == xsconstants.ACM_POLICY_ID and \ - policy == curpol.get_name() and \ - label in curpol.policy_get_resourcelabel_names(): - # VLAN is in use. - lst.append(XendDomain.instance(). - get_vm_by_uuid(XendDomain.DOM0_UUID)) - else: - dominfos = XendDomain.instance().list('all') - for dominfo in dominfos: - if is_resource_in_use_by_dom(dominfo, resource): - lst.append(dominfo) - return lst - -def devices_equal(res1, res2, mustexist=True): - """ Determine whether two devices are equal """ - return (unify_resname(res1, mustexist) == - unify_resname(res2, mustexist)) - -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, mustexist=False): - 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' : [], 'vif' : []} - 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']) - if typ in [ 'vif' ]: - sec_lab = dev[1].get('security_label') - if sec_lab: - resources[typ].append(sec_lab) - else: - # !!! This should really get the label of the domain - # or at least a resource label that has the same STE type - # as the domain has - from xen.util.acmpolicy import ACM_LABEL_UNLABELED - resources[typ].append("%s:%s:%s" % - (xsconstants.ACM_POLICY_ID, - active_policy, - ACM_LABEL_UNLABELED)) - - 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: - # No labeled resources -> must be compatible - return True - 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, - is_policy_update=False): - """ - 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. - Call this only for a paused or running domain. - """ - def collect_labels(reslabels, s_label, polname): - if len(s_label) != 3 or polname != s_label[1]: - return False - label = s_label[2] - if not label in reslabels: - reslabels.append(label) - return True - - resources = get_domain_resources(dominfo) - reslabels = [] # all resource labels - - polname = xspol.get_name() - for key, value in resources.items(): - if key in [ 'vbd', 'tap' ]: - for res in resources[key]: - try: - label = access_control[res] - if not collect_labels(reslabels, label, polname): - return False - except: - return False - elif key in [ 'vif' ]: - for xapi_label in value: - label = xapi_label.split(":") - from xen.util.acmpolicy import ACM_LABEL_UNLABELED - if not (is_policy_update and \ - label[2] == ACM_LABEL_UNLABELED): - if not collect_labels(reslabels, label, polname): - return False - else: - log.error("Unhandled device type: %s" % key) - return False - - # Check that all resource labes have a common STE type with the - # vmlabel - if len(reslabels) > 0: - rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels) - else: - rc = True - log.info("vmlabel=%s, reslabels=%s, rc=%s" % - (vmlabel, reslabels, str(rc))) - 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) - except: - 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, - is_policy_update=True) - log.info("Domain %s with new label '%s' can access its " - "resources? : %s" % - (name, new_vmlabel, str(compatible))) - log.info("VM labels in new policy: %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'." % \ - (dominfo.getName(), new_seclab)) - # This better be working! - res = dominfo.set_security_label(new_seclab, - sec_lab, - new_acmpol, - cur_acmpol) - if res[0] != xsconstants.XSERR_SUCCESS: - log.info("ERROR: Could not chg label on domain %s: %s" % - (dominfo.getName(), - xsconstants.xserr2string(-int(res[0])))) - finally: - log.info("----------------------------------------------") - mapfile_unlock() - __resfile_lock.release() - - return rc, errors diff -r 453f87d6e7f0 -r 49b67fdefcd6 tools/python/xen/util/xsm/acm/acm.py --- a/tools/python/xen/util/xsm/acm/acm.py Thu Sep 06 18:04:42 2007 +0100 +++ b/tools/python/xen/util/xsm/acm/acm.py Thu Sep 06 18:05:18 2007 +0100 @@ -838,13 +838,28 @@ def set_resource_label_xapi(resource, re def is_resource_in_use(resource): - """ Investigate all running domains whether they use this device """ + """ + Domain-0 'owns' resources of type 'VLAN', the rest are owned by + the guests. + """ 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) + if resource.startswith('vlan'): + from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance + curpol = XSPolicyAdminInstance().get_loaded_policy() + policytype, label, policy = get_res_label(resource) + if curpol and \ + policytype == xsconstants.ACM_POLICY_ID and \ + policy == curpol.get_name() and \ + label in curpol.policy_get_resourcelabel_names(): + # VLAN is in use. + lst.append(XendDomain.instance(). + get_vm_by_uuid(XendDomain.DOM0_UUID)) + else: + dominfos = XendDomain.instance().list('all') + for dominfo in dominfos: + if is_resource_in_use_by_dom(dominfo, resource): + lst.append(dominfo) return lst def devices_equal(res1, res2, mustexist=True): @@ -892,6 +907,10 @@ def get_domain_resources(dominfo): if sec_lab: resources[typ].append(sec_lab) else: + # !!! This should really get the label of the domain + # or at least a resource label that has the same STE type + # as the domain has + from xen.util.acmpolicy import ACM_LABEL_UNLABELED resources[typ].append("%s:%s:%s" % (xsconstants.ACM_POLICY_ID, active_policy, @@ -915,7 +934,8 @@ def resources_compatible_with_vmlabel(xs access_control = dictio.dict_read("resources", res_label_filename) except: - return False + # No labeled resources -> must be compatible + return True return __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel, access_control) finally: @@ -924,12 +944,14 @@ def resources_compatible_with_vmlabel(xs def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel, - access_control): + access_control, + is_policy_update=False): """ 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. + Call this only for a paused or running domain. """ def collect_labels(reslabels, s_label, polname): if len(s_label) != 3 or polname != s_label[1]: @@ -955,15 +977,23 @@ def __resources_compatible_with_vmlabel( elif key in [ 'vif' ]: for xapi_label in value: label = xapi_label.split(":") - if not collect_labels(reslabels, label, polname): - return False + from xen.util.acmpolicy import ACM_LABEL_UNLABELED + if not (is_policy_update and \ + label[2] == ACM_LABEL_UNLABELED): + if not collect_labels(reslabels, label, polname): + return False else: log.error("Unhandled device type: %s" % key) return False # Check that all resource labes have a common STE type with the # vmlabel - rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels) + if len(reslabels) > 0: + rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels) + else: + rc = True + log.info("vmlabel=%s, reslabels=%s, rc=%s" % + (vmlabel, reslabels, str(rc))) return rc; def set_resource_label(resource, policytype, policyref, reslabel, \ @@ -1176,7 +1206,7 @@ def change_acm_policy(bin_pol, del_array access_control = {} try: access_control = dictio.dict_read("resources", res_label_filename) - finally: + except: pass for key, labeldata in access_control.items(): if len(labeldata) == 2: @@ -1234,11 +1264,12 @@ def change_acm_policy(bin_pol, del_array compatible = __resources_compatible_with_vmlabel(new_acmpol, dominfo, new_vmlabel, - access_control) + access_control, + is_policy_update=True) 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" % + log.info("VM labels in new policy: %s" % new_acmpol.policy_get_virtualmachinelabel_names()) if not compatible: return (-xsconstants.XSERR_RESOURCE_ACCESS, "") @@ -1252,11 +1283,16 @@ def change_acm_policy(bin_pol, del_array sec_lab, new_seclab = labels if sec_lab != new_seclab: log.info("Updating domain %s to new label '%s'." % \ - (sec_lab, new_seclab)) + (dominfo.getName(), new_seclab)) # This better be working! - dominfo.set_security_label(new_seclab, - sec_lab, - new_acmpol) + res = dominfo.set_security_label(new_seclab, + sec_lab, + new_acmpol, + cur_acmpol) + if res[0] != xsconstants.XSERR_SUCCESS: + log.info("ERROR: Could not chg label on domain %s: %s" % + (dominfo.getName(), + xsconstants.xserr2string(-int(res[0])))) finally: log.info("----------------------------------------------") mapfile_unlock() _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |