ia64/xen-unstable

annotate tools/python/xen/util/xsm/acm/acm.py @ 16522:54482c56e435

Implement legacy XML-RPC interface for ACM commands.

This patch moves the directory of files where xend is writing policies
and resource labels into to /var/lib/xend/security/policies.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 05 09:45:13 2007 +0000 (2007-12-05)
parents 5255eac35270
children 433f6a6a862a
rev   line source
kfraser@15817 1 #===========================================================================
kfraser@15817 2 # This library is free software; you can redistribute it and/or
kfraser@15817 3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
kfraser@15817 4 # License as published by the Free Software Foundation.
kfraser@15817 5 #
kfraser@15817 6 # This library is distributed in the hope that it will be useful,
kfraser@15817 7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
kfraser@15817 8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
kfraser@15817 9 # Lesser General Public License for more details.
kfraser@15817 10 #
kfraser@15817 11 # You should have received a copy of the GNU Lesser General Public
kfraser@15817 12 # License along with this library; if not, write to the Free Software
kfraser@15817 13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
kfraser@15817 14 #============================================================================
kfraser@15817 15 # Copyright (C) 2006 International Business Machines Corp.
kfraser@15817 16 # Author: Reiner Sailer
kfraser@15817 17 # Author: Bryan D. Payne <bdpayne@us.ibm.com>
kfraser@15817 18 # Author: Stefan Berger <stefanb@us.ibm.com>
kfraser@15817 19 #============================================================================
kfraser@15817 20
kfraser@15817 21 import commands
kfraser@15817 22 import logging
kfraser@15817 23 import os, string, re
kfraser@15817 24 import threading
kfraser@15817 25 import struct
kfraser@15817 26 import stat
keir@16521 27 import base64
kfraser@15817 28 from xen.lowlevel import acm
kfraser@15817 29 from xen.xend import sxp
kfraser@15817 30 from xen.xend import XendConstants
keir@16260 31 from xen.xend import XendOptions
kfraser@15817 32 from xen.xend.XendLogging import log
kfraser@15817 33 from xen.xend.XendError import VmError
kfraser@15817 34 from xen.util import dictio, xsconstants
kfraser@15817 35 from xen.xend.XendConstants import *
kfraser@15817 36
kfraser@15817 37 #global directories and tools for security management
keir@16522 38 install_policy_dir_prefix = "/etc/xen/acm-security/policies"
keir@16522 39 security_dir_prefix = XendOptions.instance().get_xend_security_path()
keir@15975 40 policy_dir_prefix = security_dir_prefix + "/policies"
kfraser@15817 41 res_label_filename = policy_dir_prefix + "/resource_labels"
kfraser@15817 42 boot_filename = "/boot/grub/menu.lst"
kfraser@15817 43 altboot_filename = "/boot/grub/grub.conf"
kfraser@15817 44 xensec_tool = "/usr/sbin/xensec_tool"
kfraser@15817 45
kfraser@15817 46 #global patterns for map file
kfraser@15817 47 #police_reference_tagname = "POLICYREFERENCENAME"
kfraser@15817 48 primary_entry_re = re.compile("\s*PRIMARY\s+.*", re.IGNORECASE)
kfraser@15817 49 secondary_entry_re = re.compile("\s*SECONDARY\s+.*", re.IGNORECASE)
kfraser@15817 50 label_template_re = re.compile(".*security_label_template.xml", re.IGNORECASE)
kfraser@15817 51 mapping_filename_re = re.compile(".*\.map", re.IGNORECASE)
kfraser@15817 52 policy_reference_entry_re = re.compile("\s*POLICYREFERENCENAME\s+.*", re.IGNORECASE)
keir@16521 53 vm_label_re = re.compile("\s*LABEL->SSID\s.+[VM|ANY]\s+.*", re.IGNORECASE)
kfraser@15817 54 res_label_re = re.compile("\s*LABEL->SSID\s+RES\s+.*", re.IGNORECASE)
kfraser@15817 55 all_label_re = re.compile("\s*LABEL->SSID\s+.*", re.IGNORECASE)
kfraser@15817 56 access_control_re = re.compile("\s*access_control\s*=", re.IGNORECASE)
kfraser@15817 57
kfraser@15817 58 #global patterns for boot configuration file
kfraser@15817 59 xen_title_re = re.compile("\s*title\s+XEN", re.IGNORECASE)
kfraser@15817 60 any_title_re = re.compile("\s*title\s", re.IGNORECASE)
kfraser@15817 61 xen_kernel_re = re.compile("\s*kernel.*xen.*\.gz", re.IGNORECASE)
kfraser@15817 62 kernel_ver_re = re.compile("\s*module.*vmlinuz", re.IGNORECASE)
kfraser@15817 63 any_module_re = re.compile("\s*module\s", re.IGNORECASE)
kfraser@15817 64 empty_line_re = re.compile("^\s*$")
kfraser@15817 65 binary_name_re = re.compile(".*[chwall|ste|chwall_ste].*\.bin", re.IGNORECASE)
kfraser@15817 66 policy_name_re = re.compile(".*[chwall|ste|chwall_ste].*", re.IGNORECASE)
kfraser@15817 67
kfraser@15817 68 #decision hooks known to the hypervisor
kfraser@15817 69 ACMHOOK_sharing = 1
kfraser@15817 70 ACMHOOK_authorization = 2
kfraser@15817 71
kfraser@15817 72 #other global variables
kfraser@15817 73 NULL_SSIDREF = 0
kfraser@15817 74
kfraser@15817 75 #general Rlock for map files; only one lock for all mapfiles
kfraser@15817 76 __mapfile_lock = threading.RLock()
kfraser@15817 77 __resfile_lock = threading.RLock()
kfraser@15817 78
kfraser@15817 79 log = logging.getLogger("xend.util.security")
kfraser@15817 80
keir@16521 81
keir@16521 82 #Functions exported through XML-RPC
keir@16521 83 xmlrpc_exports = [
keir@16521 84 'set_resource_label',
keir@16521 85 'get_resource_label',
keir@16521 86 'list_labels',
keir@16521 87 'get_labeled_resources',
keir@16521 88 'set_policy',
keir@16521 89 'get_policy',
keir@16521 90 'activate_policy',
keir@16521 91 'rm_bootpolicy',
keir@16521 92 'get_xstype',
keir@16521 93 'get_domain_label',
keir@16521 94 'set_domain_label'
keir@16521 95 ]
keir@16521 96
kfraser@15817 97 # Our own exception definition. It is masked (pass) if raised and
kfraser@15817 98 # whoever raises this exception must provide error information.
keir@16521 99 class XSMError(Exception):
kfraser@15817 100 def __init__(self,value):
kfraser@15817 101 self.value = value
kfraser@15817 102 def __str__(self):
kfraser@15817 103 return repr(self.value)
kfraser@15817 104
kfraser@15817 105
kfraser@15817 106
kfraser@15817 107 def err(msg):
kfraser@15817 108 """Raise ACM exception.
kfraser@15817 109 """
keir@16521 110 raise XSMError(msg)
kfraser@15817 111
kfraser@15817 112
kfraser@15817 113
kfraser@15817 114 active_policy = None
kfraser@15817 115
kfraser@15817 116
kfraser@15817 117 def mapfile_lock():
kfraser@15817 118 __mapfile_lock.acquire()
kfraser@15817 119
kfraser@15817 120 def mapfile_unlock():
kfraser@15817 121 __mapfile_lock.release()
kfraser@15817 122
kfraser@15817 123
keir@16081 124 def resfile_lock():
keir@16081 125 __resfile_lock.acquire()
keir@16081 126
keir@16081 127 def resfile_unlock():
keir@16081 128 __resfile_lock.release()
keir@16081 129
keir@16081 130
kfraser@15817 131 def refresh_security_policy():
kfraser@15817 132 """
kfraser@15817 133 retrieves security policy
kfraser@15817 134 """
kfraser@15817 135 global active_policy
kfraser@15817 136
keir@16052 137 active_policy = 'INACCESSIBLE'
keir@16521 138
keir@16052 139 if os.access("/proc/xen/privcmd", os.R_OK|os.W_OK):
keir@16052 140 try:
keir@16052 141 active_policy = acm.policy()
keir@16052 142 except:
keir@16052 143 active_policy = "INACTIVE"
kfraser@15817 144
keir@16521 145 def get_active_policy_name():
keir@16521 146 refresh_security_policy()
keir@16521 147 return active_policy
keir@16521 148
kfraser@15817 149 # now set active_policy
kfraser@15817 150 refresh_security_policy()
kfraser@15817 151
kfraser@15817 152 def on():
kfraser@15817 153 """
kfraser@15817 154 returns none if security policy is off (not compiled),
kfraser@15817 155 any string otherwise, use it: if not security.on() ...
kfraser@15817 156 """
keir@16521 157 return (get_active_policy_name() not in ['INACTIVE', 'NULL'])
kfraser@15817 158
kfraser@15817 159
kfraser@15817 160 def calc_dom_ssidref_from_info(info):
kfraser@15817 161 """
kfraser@15817 162 Calculate a domain's ssidref from the security_label in its
kfraser@15817 163 info.
kfraser@15817 164 This function is called before the domain is started and
kfraser@15817 165 makes sure that:
kfraser@15817 166 - the type of the policy is the same as indicated in the label
kfraser@15817 167 - the name of the policy is the same as indicated in the label
kfraser@15817 168 - calculates an up-to-date ssidref for the domain
kfraser@15817 169 The latter is necessary since the domain's ssidref could have
kfraser@15817 170 changed due to changes to the policy.
kfraser@15817 171 """
kfraser@15817 172 import xen.xend.XendConfig
kfraser@15817 173 if isinstance(info, xen.xend.XendConfig.XendConfig):
kfraser@15817 174 if info.has_key('security_label'):
kfraser@15817 175 seclab = info['security_label']
kfraser@15817 176 tmp = seclab.split(":")
kfraser@15817 177 if len(tmp) != 3:
kfraser@15817 178 raise VmError("VM label '%s' in wrong format." % seclab)
kfraser@15817 179 typ, policyname, vmlabel = seclab.split(":")
kfraser@15817 180 if typ != xsconstants.ACM_POLICY_ID:
kfraser@15817 181 raise VmError("Policy type '%s' must be changed." % typ)
keir@16521 182 if get_active_policy_name() != policyname:
kfraser@15817 183 raise VmError("Active policy '%s' different than "
kfraser@15817 184 "what in VM's label ('%s')." %
keir@16521 185 (get_active_policy_name(), policyname))
kfraser@15817 186 ssidref = label2ssidref(vmlabel, policyname, "dom")
kfraser@15817 187 return ssidref
kfraser@15817 188 else:
kfraser@15817 189 return 0x0
kfraser@15817 190 raise VmError("security.calc_dom_ssidref_from_info: info of type '%s'"
kfraser@15817 191 "not supported." % type(info))
kfraser@15817 192
kfraser@15817 193
kfraser@15817 194 def getmapfile(policyname):
kfraser@15817 195 """
kfraser@15817 196 in: if policyname is None then the currently
kfraser@15817 197 active hypervisor policy is used
kfraser@15817 198 out: 1. primary policy, 2. secondary policy,
kfraser@15817 199 3. open file descriptor for mapping file, and
kfraser@15817 200 4. True if policy file is available, False otherwise
kfraser@15817 201 """
kfraser@15817 202 if not policyname:
keir@16521 203 policyname = get_active_policy_name()
kfraser@15817 204 map_file_ok = False
kfraser@15817 205 primary = None
kfraser@15817 206 secondary = None
kfraser@15817 207 #strip last part of policy as file name part
kfraser@15817 208 policy_dir_list = string.split(policyname, ".")
kfraser@15817 209 policy_file = policy_dir_list.pop()
kfraser@15817 210 if len(policy_dir_list) > 0:
kfraser@15817 211 policy_dir = string.join(policy_dir_list, "/") + "/"
kfraser@15817 212 else:
kfraser@15817 213 policy_dir = ""
kfraser@15817 214
kfraser@15817 215 map_filename = policy_dir_prefix + "/" + policy_dir + policy_file + ".map"
kfraser@15817 216 # check if it is there, if not check if policy file is there
kfraser@15817 217 if not os.path.isfile(map_filename):
kfraser@15817 218 policy_filename = policy_dir_prefix + "/" + policy_dir + policy_file + "-security_policy.xml"
kfraser@15817 219 if not os.path.isfile(policy_filename):
kfraser@15817 220 err("Policy file \'" + policy_filename + "\' not found.")
kfraser@15817 221 else:
keir@16521 222 err("Mapping file \'" + map_filename + "\' not found.")
kfraser@15817 223
kfraser@15817 224 f = open(map_filename)
kfraser@15817 225 for line in f:
kfraser@15817 226 if policy_reference_entry_re.match(line):
kfraser@15817 227 l = line.split()
kfraser@15817 228 if (len(l) == 2) and (l[1] == policyname):
kfraser@15817 229 map_file_ok = True
kfraser@15817 230 elif primary_entry_re.match(line):
kfraser@15817 231 l = line.split()
kfraser@15817 232 if len(l) == 2:
kfraser@15817 233 primary = l[1]
kfraser@15817 234 elif secondary_entry_re.match(line):
kfraser@15817 235 l = line.split()
kfraser@15817 236 if len(l) == 2:
kfraser@15817 237 secondary = l[1]
kfraser@15817 238 f.close()
kfraser@15817 239 f = open(map_filename)
kfraser@15817 240 if map_file_ok and primary and secondary:
kfraser@15817 241 return (primary, secondary, f, True)
kfraser@15817 242 else:
keir@16521 243 err("Mapping file inconsistencies found.")
kfraser@15817 244
kfraser@15817 245
kfraser@15817 246
kfraser@15817 247 def ssidref2label(ssidref_var):
kfraser@15817 248 """
kfraser@15817 249 returns labelname corresponding to ssidref;
kfraser@15817 250 maps current policy to default directory
kfraser@15817 251 to find mapping file
kfraser@15817 252 """
kfraser@15817 253 #1. translated permitted input formats
kfraser@15817 254 if isinstance(ssidref_var, str):
kfraser@15817 255 ssidref_var.strip()
kfraser@15817 256 if ssidref_var[0:2] == "0x":
kfraser@15817 257 ssidref = int(ssidref_var[2:], 16)
kfraser@15817 258 else:
kfraser@15817 259 ssidref = int(ssidref_var)
kfraser@15817 260 elif isinstance(ssidref_var, int):
kfraser@15817 261 ssidref = ssidref_var
kfraser@15817 262 else:
kfraser@15817 263 err("Instance type of ssidref not supported (must be of type 'str' or 'int')")
kfraser@15817 264
kfraser@15817 265 if ssidref == 0:
kfraser@15817 266 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
kfraser@15817 267 return ACM_LABEL_UNLABELED
kfraser@15817 268
kfraser@15817 269 try:
kfraser@15817 270 mapfile_lock()
kfraser@15817 271
kfraser@15817 272 (primary, secondary, f, pol_exists) = getmapfile(None)
kfraser@15817 273 if not f:
kfraser@15817 274 if (pol_exists):
keir@16521 275 err("Mapping file for policy not found.")
kfraser@15817 276 else:
keir@16521 277 err("Policy file for \'" + get_active_policy_name() +
keir@16521 278 "\' not found.")
kfraser@15817 279
kfraser@15817 280 #2. get labelnames for both ssidref parts
kfraser@15817 281 pri_ssid = ssidref & 0xffff
kfraser@15817 282 sec_ssid = ssidref >> 16
kfraser@15817 283 pri_null_ssid = NULL_SSIDREF & 0xffff
kfraser@15817 284 sec_null_ssid = NULL_SSIDREF >> 16
kfraser@15817 285 pri_labels = []
kfraser@15817 286 sec_labels = []
kfraser@15817 287 labels = []
kfraser@15817 288
kfraser@15817 289 for line in f:
kfraser@15817 290 l = line.split()
kfraser@15817 291 if (len(l) < 5) or (l[0] != "LABEL->SSID"):
kfraser@15817 292 continue
kfraser@15817 293 if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid):
kfraser@15817 294 pri_labels.append(l[3])
kfraser@15817 295 if secondary and (l[2] == secondary) and (int(l[4], 16) == sec_ssid):
kfraser@15817 296 sec_labels.append(l[3])
kfraser@15817 297 f.close()
kfraser@15817 298 finally:
kfraser@15817 299 mapfile_unlock()
kfraser@15817 300
kfraser@15817 301 #3. get the label that is in both lists (combination must be a single label)
kfraser@15817 302 if (primary == "CHWALL") and (pri_ssid == pri_null_ssid) and (sec_ssid != sec_null_ssid):
kfraser@15817 303 labels = sec_labels
kfraser@15817 304 elif (secondary == "CHWALL") and (pri_ssid != pri_null_ssid) and (sec_ssid == sec_null_ssid):
kfraser@15817 305 labels = pri_labels
kfraser@15817 306 elif secondary == "NULL":
kfraser@15817 307 labels = pri_labels
kfraser@15817 308 else:
kfraser@15817 309 for i in pri_labels:
kfraser@15817 310 for j in sec_labels:
kfraser@15817 311 if (i==j):
kfraser@15817 312 labels.append(i)
kfraser@15817 313 if len(labels) != 1:
kfraser@15817 314 err("Label for ssidref \'" + str(ssidref) +
kfraser@15817 315 "\' unknown or not unique in policy \'" + active_policy + "\'")
kfraser@15817 316
kfraser@15817 317 return labels[0]
kfraser@15817 318
kfraser@15817 319
kfraser@15817 320
kfraser@15817 321 def label2ssidref(labelname, policyname, typ):
kfraser@15817 322 """
kfraser@15817 323 returns ssidref corresponding to labelname;
kfraser@15817 324 maps current policy to default directory
kfraser@15817 325 to find mapping file """
kfraser@15817 326
keir@16522 327 if policyname in ['NULL', 'INACTIVE', 'INACCESSIBLE' ]:
kfraser@15817 328 err("Cannot translate labels for \'" + policyname + "\' policy.")
kfraser@15817 329
kfraser@15817 330 allowed_types = ['ANY']
kfraser@15817 331 if typ == 'dom':
kfraser@15817 332 allowed_types.append('VM')
kfraser@15817 333 elif typ == 'res':
kfraser@15817 334 allowed_types.append('RES')
kfraser@15817 335 else:
kfraser@15817 336 err("Invalid type. Must specify 'dom' or 'res'.")
kfraser@15817 337
kfraser@15817 338 try:
kfraser@15817 339 mapfile_lock()
kfraser@15817 340 (primary, secondary, f, pol_exists) = getmapfile(policyname)
kfraser@15817 341
kfraser@15817 342 #2. get labelnames for ssidref parts and find a common label
kfraser@15817 343 pri_ssid = []
kfraser@15817 344 sec_ssid = []
kfraser@15817 345 for line in f:
kfraser@15817 346 l = line.split()
kfraser@15817 347 if (len(l) < 5) or (l[0] != "LABEL->SSID"):
kfraser@15817 348 continue
kfraser@15817 349 if primary and (l[1] in allowed_types) and \
kfraser@15817 350 (l[2] == primary) and \
kfraser@15817 351 (l[3] == labelname):
kfraser@15817 352 pri_ssid.append(int(l[4], 16))
kfraser@15817 353 if secondary and (l[1] in allowed_types) and \
kfraser@15817 354 (l[2] == secondary) and \
kfraser@15817 355 (l[3] == labelname):
kfraser@15817 356 sec_ssid.append(int(l[4], 16))
kfraser@15817 357 f.close()
kfraser@15817 358 if (typ == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0):
kfraser@15817 359 pri_ssid.append(NULL_SSIDREF)
kfraser@15817 360 elif (typ == 'res') and (secondary == "CHWALL") and \
kfraser@15817 361 (len(sec_ssid) == 0):
kfraser@15817 362 sec_ssid.append(NULL_SSIDREF)
kfraser@15817 363
kfraser@15817 364 #3. sanity check and composition of ssidref
kfraser@15817 365 if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and \
kfraser@15817 366 (secondary != "NULL")):
kfraser@15817 367 err("Label \'" + labelname + "\' not found.")
kfraser@15817 368 elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1):
kfraser@15817 369 err("Label \'" + labelname + "\' not unique in policy (policy error)")
kfraser@15817 370 if secondary == "NULL":
kfraser@15817 371 return pri_ssid[0]
kfraser@15817 372 else:
kfraser@15817 373 return (sec_ssid[0] << 16) | pri_ssid[0]
kfraser@15817 374 finally:
kfraser@15817 375 mapfile_unlock()
kfraser@15817 376
kfraser@15817 377
kfraser@15817 378 def refresh_ssidref(config):
kfraser@15817 379 """
kfraser@15817 380 looks up ssidref from security field
kfraser@15817 381 and refreshes the value if label exists
kfraser@15817 382 """
kfraser@15817 383 #called by dom0, policy could have changed after xen.utils.security was initialized
kfraser@15817 384 refresh_security_policy()
kfraser@15817 385
kfraser@15817 386 security = None
kfraser@15817 387 if isinstance(config, dict):
kfraser@15817 388 security = config['security']
kfraser@15817 389 elif isinstance(config, list):
kfraser@15817 390 security = sxp.child_value(config, 'security')
kfraser@15817 391 else:
kfraser@15817 392 err("Instance type of config parameter not supported.")
kfraser@15817 393 if not security:
kfraser@15817 394 #nothing to do (no security label attached)
kfraser@15817 395 return config
kfraser@15817 396
kfraser@15817 397 policyname = None
kfraser@15817 398 labelname = None
kfraser@15817 399 # compose new security field
kfraser@15817 400 for idx in range(0, len(security)):
kfraser@15817 401 if security[idx][0] == 'ssidref':
kfraser@15817 402 security.pop(idx)
kfraser@15817 403 break
kfraser@15817 404 elif security[idx][0] == 'access_control':
kfraser@15817 405 for jdx in [1, 2]:
kfraser@15817 406 if security[idx][jdx][0] == 'label':
kfraser@15817 407 labelname = security[idx][jdx][1]
kfraser@15817 408 elif security[idx][jdx][0] == 'policy':
kfraser@15817 409 policyname = security[idx][jdx][1]
kfraser@15817 410 else:
kfraser@15817 411 err("Illegal field in access_control")
kfraser@15817 412 #verify policy is correct
kfraser@15817 413 if active_policy != policyname:
kfraser@15817 414 err("Policy \'" + str(policyname) +
kfraser@15817 415 "\' in label does not match active policy \'"
kfraser@15817 416 + str(active_policy) +"\'!")
kfraser@15817 417
kfraser@15817 418 new_ssidref = label2ssidref(labelname, policyname, 'dom')
kfraser@15817 419 if not new_ssidref:
kfraser@15817 420 err("SSIDREF refresh failed!")
kfraser@15817 421
kfraser@15817 422 security.append([ 'ssidref',str(new_ssidref)])
kfraser@15817 423 security = ['security', security ]
kfraser@15817 424
kfraser@15817 425 for idx in range(0,len(config)):
kfraser@15817 426 if config[idx][0] == 'security':
kfraser@15817 427 config.pop(idx)
kfraser@15817 428 break
kfraser@15817 429 config.append(security)
kfraser@15817 430
kfraser@15817 431
kfraser@15817 432
kfraser@15817 433 def get_ssid(domain):
kfraser@15817 434 """
kfraser@15817 435 enables domains to retrieve the label / ssidref of a running domain
kfraser@15817 436 """
kfraser@15817 437 if not on():
kfraser@15817 438 err("No policy active.")
kfraser@15817 439
kfraser@15817 440 if isinstance(domain, str):
kfraser@15817 441 domain_int = int(domain)
kfraser@15817 442 elif isinstance(domain, int):
kfraser@15817 443 domain_int = domain
kfraser@15817 444 else:
kfraser@15817 445 err("Illegal parameter type.")
kfraser@15817 446 try:
kfraser@15817 447 ssid_info = acm.getssid(int(domain_int))
kfraser@15817 448 except:
kfraser@15817 449 err("Cannot determine security information.")
kfraser@15817 450
keir@16522 451 label = ssidref2label(ssid_info["ssidref"])
keir@16522 452
kfraser@15817 453 return(ssid_info["policyreference"],
kfraser@15817 454 label,
kfraser@15817 455 ssid_info["policytype"],
kfraser@15817 456 ssid_info["ssidref"])
kfraser@15817 457
kfraser@15817 458
kfraser@15817 459
kfraser@15817 460 def get_decision(arg1, arg2):
kfraser@15817 461 """
kfraser@15817 462 enables domains to retrieve access control decisions from
kfraser@15817 463 the hypervisor Access Control Module.
kfraser@15817 464 IN: args format = ['domid', id] or ['ssidref', ssidref]
kfraser@15817 465 or ['access_control', ['policy', policy], ['label', label], ['type', type]]
kfraser@15817 466 """
kfraser@15817 467
kfraser@15817 468 if not on():
kfraser@15817 469 err("No policy active.")
kfraser@15817 470
kfraser@15817 471 #translate labels before calling low-level function
kfraser@15817 472 if arg1[0] == 'access_control':
kfraser@15817 473 if (arg1[1][0] != 'policy') or (arg1[2][0] != 'label') or (arg1[3][0] != 'type'):
kfraser@15817 474 err("Argument type not supported.")
kfraser@15817 475 ssidref = label2ssidref(arg1[2][1], arg1[1][1], arg1[3][1])
kfraser@15817 476 arg1 = ['ssidref', str(ssidref)]
kfraser@15817 477 if arg2[0] == 'access_control':
kfraser@15817 478 if (arg2[1][0] != 'policy') or (arg2[2][0] != 'label') or (arg2[3][0] != 'type'):
kfraser@15817 479 err("Argument type not supported.")
kfraser@15817 480 ssidref = label2ssidref(arg2[2][1], arg2[1][1], arg2[3][1])
kfraser@15817 481 arg2 = ['ssidref', str(ssidref)]
kfraser@15817 482
kfraser@15817 483 # accept only int or string types for domid and ssidref
kfraser@15817 484 if isinstance(arg1[1], int):
kfraser@15817 485 arg1[1] = str(arg1[1])
kfraser@15817 486 if isinstance(arg2[1], int):
kfraser@15817 487 arg2[1] = str(arg2[1])
kfraser@15817 488 if not isinstance(arg1[1], str) or not isinstance(arg2[1], str):
kfraser@15817 489 err("Invalid id or ssidref type, string or int required")
kfraser@15817 490
kfraser@15817 491 try:
kfraser@15817 492 decision = acm.getdecision(arg1[0], arg1[1], arg2[0], arg2[1],
kfraser@15817 493 ACMHOOK_sharing)
kfraser@15817 494 except:
kfraser@15817 495 err("Cannot determine decision.")
kfraser@15817 496
kfraser@15817 497 if decision:
kfraser@15817 498 return decision
kfraser@15817 499 else:
kfraser@15817 500 err("Cannot determine decision (Invalid parameter).")
kfraser@15817 501
kfraser@15817 502
kfraser@15817 503 def has_authorization(ssidref):
kfraser@15817 504 """ Check if the domain with the given ssidref has authorization to
kfraser@15817 505 run on this system. To have authoriztion dom0's STE types must
kfraser@15817 506 be a superset of that of the domain's given through its ssidref.
kfraser@15817 507 """
kfraser@15817 508 rc = True
kfraser@15817 509 dom0_ssidref = int(acm.getssid(0)['ssidref'])
kfraser@15817 510 decision = acm.getdecision('ssidref', str(dom0_ssidref),
kfraser@15817 511 'ssidref', str(ssidref),
kfraser@15817 512 ACMHOOK_authorization)
kfraser@15817 513 if decision == "DENIED":
kfraser@15817 514 rc = False
kfraser@15817 515 return rc
kfraser@15817 516
kfraser@15817 517
kfraser@15817 518 def hv_chg_policy(bin_pol, del_array, chg_array):
kfraser@15817 519 """
kfraser@15817 520 Change the binary policy in the hypervisor
kfraser@15817 521 The 'del_array' and 'chg_array' give hints about deleted ssidrefs
kfraser@15817 522 and changed ssidrefs which can be due to deleted VM labels
kfraser@15817 523 or reordered VM labels
kfraser@15817 524 """
kfraser@15817 525 rc = -xsconstants.XSERR_GENERAL_FAILURE
kfraser@15817 526 errors = ""
kfraser@15817 527 if not on():
kfraser@15817 528 err("No policy active.")
kfraser@15817 529 try:
kfraser@15817 530 rc, errors = acm.chgpolicy(bin_pol, del_array, chg_array)
kfraser@15817 531 except Exception, e:
kfraser@15817 532 pass
kfraser@15817 533 if len(errors) > 0:
kfraser@15817 534 rc = -xsconstants.XSERR_HV_OP_FAILED
kfraser@15817 535 return rc, errors
kfraser@15817 536
keir@16051 537 def hv_get_policy():
keir@16051 538 """
keir@16051 539 Gte the binary policy enforced in the hypervisor
keir@16051 540 """
keir@16051 541 rc = -xsconstants.XSERR_GENERAL_FAILURE
keir@16051 542 bin_pol = ""
keir@16051 543 if not on():
keir@16051 544 err("No policy active.")
keir@16051 545 try:
keir@16051 546 rc, bin_pol = acm.getpolicy()
keir@16051 547 except Exception, e:
keir@16051 548 pass
keir@16051 549 if len(bin_pol) == 0:
keir@16051 550 bin_pol = None
keir@16051 551 return rc, bin_pol
keir@16051 552
kfraser@15817 553
keir@16521 554 def set_policy(xs_type, xml, flags, overwrite):
keir@16521 555 """
keir@16521 556 Xend exports this function via XML-RPC
keir@16521 557 """
keir@16521 558 from xen.xend import XendXSPolicyAdmin
keir@16521 559 xspoladmin = XendXSPolicyAdmin.XSPolicyAdminInstance()
keir@16521 560 try:
keir@16521 561 acmpol, rc, errors = \
keir@16521 562 xspoladmin.add_acmpolicy_to_system(xml,
keir@16521 563 int(flags),
keir@16521 564 True)
keir@16521 565 return rc, base64.b64encode(errors)
keir@16521 566 except Exception, e:
keir@16521 567 err(str(e))
kfraser@15817 568
kfraser@15817 569
keir@16521 570 def get_policy():
keir@16521 571 """
keir@16521 572 Xend exports this function via XML-RPC
keir@16521 573 """
keir@16521 574 from xen.xend import XendXSPolicyAdmin
keir@16521 575 poladmin = XendXSPolicyAdmin.XSPolicyAdminInstance()
keir@16521 576 try:
keir@16521 577 policy = poladmin.get_loaded_policy()
keir@16521 578 if policy != None:
keir@16521 579 return policy.toxml(), poladmin.get_policy_flags(policy)
keir@16521 580 except Exception, e:
keir@16521 581 err(str(e))
keir@16521 582 return "", 0
keir@16521 583
keir@16521 584 def activate_policy(flags):
keir@16521 585 """
keir@16521 586 Xend exports this function via XML-RPC
keir@16521 587 """
keir@16521 588 from xen.xend import XendXSPolicyAdmin
keir@16521 589 poladmin = XendXSPolicyAdmin.XSPolicyAdminInstance()
keir@16521 590 try:
keir@16521 591 policies = poladmin.get_policies()
keir@16521 592 if len(policies) > 0:
keir@16521 593 flags = int(flags)
keir@16521 594 irc = poladmin.activate_xspolicy(policies[0], flags)
keir@16521 595 return irc
keir@16521 596 except Exception, e:
keir@16521 597 err("Error while activating the policy: " % str(e))
keir@16521 598 return 0
keir@16521 599
keir@16521 600
keir@16521 601 def rm_bootpolicy():
keir@16521 602 """
keir@16521 603 Xend exports this function via XML-RPC
keir@16521 604 """
keir@16521 605 from xen.xend import XendXSPolicyAdmin
keir@16521 606 rc = XendXSPolicyAdmin.XSPolicyAdminInstance().rm_bootpolicy()
keir@16521 607 if rc != xsconstants.XSERR_SUCCESS:
keir@16521 608 err("Error while removing boot policy: %s" % \
keir@16521 609 str(xsconstants.xserr2string(-rc)))
keir@16521 610 return rc
keir@16521 611
keir@16521 612
keir@16521 613 def get_xstype():
keir@16521 614 """
keir@16521 615 Xend exports this function via XML-RPC
keir@16521 616 """
keir@16521 617 from xen.xend import XendXSPolicyAdmin
keir@16521 618 return XendXSPolicyAdmin.XSPolicyAdminInstance().isXSEnabled()
keir@16521 619
keir@16521 620
keir@16521 621 def get_domain_label(domain):
keir@16521 622 """
keir@16521 623 Xend exports this function via XML-RPC
keir@16521 624 """
keir@16521 625 from xen.xend import XendDomain
keir@16521 626 dom = XendDomain.instance().domain_lookup_nr(domain)
keir@16521 627 if dom:
keir@16521 628 seclab = dom.get_security_label()
keir@16521 629 return seclab
kfraser@15817 630 else:
keir@16521 631 err("Domain not found.")
kfraser@15817 632
kfraser@15817 633
keir@16521 634 def set_domain_label(domain, seclab, old_seclab):
keir@16521 635 """
keir@16521 636 Xend exports this function via XML-RPC
keir@16521 637 """
keir@16521 638 from xen.xend import XendDomain
keir@16521 639 dom = XendDomain.instance().domain_lookup_nr(domain)
keir@16521 640 if dom:
keir@16521 641 results = dom.set_security_label(seclab, old_seclab)
keir@16521 642 rc, errors, old_label, new_ssidref = results
keir@16521 643 return rc, new_ssidref
keir@16521 644 else:
keir@16521 645 err("Domain not found.")
keir@16521 646
kfraser@15817 647
kfraser@15817 648 def dump_policy():
keir@16052 649 if active_policy in ['NULL', 'INACTIVE', 'INACCESSIBLE' ]:
kfraser@15817 650 err("\'" + active_policy + "\' policy. Nothing to dump.")
kfraser@15817 651
kfraser@15817 652 (ret, output) = commands.getstatusoutput(xensec_tool + " getpolicy")
kfraser@15817 653 if ret:
keir@16051 654 err("Dumping hypervisor policy failed:\n" + output)
keir@16051 655
kfraser@15817 656 print output
kfraser@15817 657
kfraser@15817 658
keir@16051 659 def dump_policy_file(filename, ssidref=None):
keir@16051 660 ssid = ""
keir@16051 661 if ssidref:
keir@16051 662 ssid = " " + str(ssidref)
keir@16051 663 (ret, output) = commands.getstatusoutput(xensec_tool + " dumppolicy " +
keir@16051 664 filename + ssid)
keir@16051 665 if ret:
keir@16051 666 err("Dumping policy failed:\n" + output)
keir@16051 667
keir@16051 668 print output
keir@16051 669
kfraser@15817 670
keir@16521 671 def list_labels(policy_name, ltype):
keir@16521 672 """
keir@16521 673 Xend exports this function via XML-RPC
keir@16521 674
keir@16521 675 List the VM,resource or any kind of labels contained in the
keir@16521 676 given policy. If no policy name is given, the currently
keir@16521 677 active policy's label will be returned if they exist.
keir@16521 678 """
keir@16521 679 if not policy_name:
keir@16521 680 if active_policy in [ 'NULL', 'INACTIVE', "" ]:
keir@16521 681 err("Current policy \'" + active_policy + "\' "
keir@16521 682 "has no labels defined.\n")
keir@16521 683
keir@16521 684 if not ltype or ltype == 'dom':
keir@16521 685 condition = vm_label_re
keir@16521 686 elif ltype == 'res':
keir@16521 687 condition = res_label_re
keir@16521 688 elif ltype == 'any':
keir@16521 689 condition = all_label_re
keir@16521 690 else:
keir@16521 691 err("Unknown label type \'" + ltype + "\'")
kfraser@15817 692
kfraser@15817 693 (primary, secondary, f, pol_exists) = getmapfile(policy_name)
kfraser@15817 694 if not f:
kfraser@15817 695 if pol_exists:
keir@16521 696 err("Cannot find mapfile for policy \'" + policy_name + "\'.\n")
kfraser@15817 697 else:
kfraser@15817 698 err("Unknown policy \'" + policy_name + "\'")
kfraser@15817 699
kfraser@15817 700 labels = []
kfraser@15817 701 for line in f:
kfraser@15817 702 if condition.match(line):
kfraser@15817 703 label = line.split()[3]
kfraser@15817 704 if label not in labels:
kfraser@15817 705 labels.append(label)
keir@16521 706
keir@16521 707 if '__NULL_LABEL__' in labels:
keir@16521 708 labels.remove('__NULL_LABEL__')
keir@16521 709
kfraser@15817 710 return labels
kfraser@15817 711
kfraser@15817 712
kfraser@15817 713 def get_res_label(resource):
kfraser@15817 714 """Returns resource label information (policytype, label, policy) if
kfraser@15817 715 it exists. Otherwise returns null label and policy.
kfraser@15817 716 """
kfraser@15817 717 def default_res_label():
kfraser@15817 718 ssidref = NULL_SSIDREF
kfraser@15817 719 if on():
kfraser@15817 720 label = ssidref2label(ssidref)
kfraser@15817 721 else:
kfraser@15817 722 label = None
kfraser@15817 723 return (xsconstants.ACM_POLICY_ID, 'NULL', label)
kfraser@15817 724
kfraser@15817 725
kfraser@15817 726 tmp = get_resource_label(resource)
kfraser@15817 727 if len(tmp) == 2:
kfraser@15817 728 policytype = xsconstants.ACM_POLICY_ID
kfraser@15817 729 policy, label = tmp
kfraser@15817 730 elif len(tmp) == 3:
kfraser@15817 731 policytype, policy, label = tmp
kfraser@15817 732 else:
kfraser@15817 733 policytype, policy, label = default_res_label()
kfraser@15817 734
kfraser@15817 735 return (policytype, label, policy)
kfraser@15817 736
kfraser@15817 737
kfraser@15817 738 def get_res_security_details(resource):
kfraser@15817 739 """Returns the (label, ssidref, policy) associated with a given
kfraser@15817 740 resource from the global resource label file.
kfraser@15817 741 """
kfraser@15817 742 def default_security_details():
kfraser@15817 743 ssidref = NULL_SSIDREF
kfraser@15817 744 if on():
kfraser@15817 745 label = ssidref2label(ssidref)
kfraser@15817 746 else:
kfraser@15817 747 label = None
kfraser@15817 748 policy = active_policy
kfraser@15817 749 return (label, ssidref, policy)
kfraser@15817 750
kfraser@15817 751 (label, ssidref, policy) = default_security_details()
kfraser@15817 752
kfraser@15817 753 # find the entry associated with this resource
kfraser@15817 754 (policytype, label, policy) = get_res_label(resource)
kfraser@15817 755 if policy == 'NULL':
kfraser@15817 756 log.info("Resource label for "+resource+" not in file, using DEFAULT.")
kfraser@15817 757 return default_security_details()
kfraser@15817 758
keir@16221 759 if policytype != xsconstants.ACM_POLICY_ID:
keir@16221 760 raise VmError("Unknown policy type '%s in label for resource '%s'" %
keir@16221 761 (policytype, resource))
keir@16221 762
kfraser@15817 763 # is this resource label for the running policy?
kfraser@15817 764 if policy == active_policy:
kfraser@15817 765 ssidref = label2ssidref(label, policy, 'res')
kfraser@15817 766 else:
kfraser@15817 767 log.info("Resource label not for active policy, using DEFAULT.")
kfraser@15817 768 return default_security_details()
kfraser@15817 769
kfraser@15817 770 return (label, ssidref, policy)
kfraser@15817 771
kfraser@15817 772 def security_label_to_details(seclab):
kfraser@15817 773 """ Convert a Xen-API type of security label into details """
kfraser@15817 774 def default_security_details():
kfraser@15817 775 ssidref = NULL_SSIDREF
kfraser@15817 776 if on():
kfraser@15817 777 label = ssidref2label(ssidref)
kfraser@15817 778 else:
kfraser@15817 779 label = None
kfraser@15817 780 policy = active_policy
kfraser@15817 781 return (label, ssidref, policy)
kfraser@15817 782
kfraser@15817 783 (policytype, policy, label) = seclab.split(":")
kfraser@15817 784
kfraser@15817 785 # is this resource label for the running policy?
kfraser@15817 786 if policy == active_policy:
kfraser@15817 787 ssidref = label2ssidref(label, policy, 'res')
kfraser@15817 788 else:
kfraser@15817 789 log.info("Resource label not for active policy, using DEFAULT.")
kfraser@15817 790 return default_security_details()
kfraser@15817 791
kfraser@15817 792 return (label, ssidref, policy)
kfraser@15817 793
kfraser@15817 794 def unify_resname(resource, mustexist=True):
kfraser@15817 795 """Makes all resource locations absolute. In case of physical
kfraser@15817 796 resources, '/dev/' is added to local file names"""
kfraser@15817 797
kfraser@15817 798 if not resource:
kfraser@15817 799 return resource
kfraser@15817 800
kfraser@15817 801 # sanity check on resource name
kfraser@15817 802 try:
kfraser@15817 803 (typ, resfile) = resource.split(":", 1)
kfraser@15817 804 except:
kfraser@15817 805 err("Resource spec '%s' contains no ':' delimiter" % resource)
kfraser@15817 806
kfraser@15817 807 if typ == "tap":
kfraser@15817 808 try:
kfraser@15817 809 (subtype, resfile) = resfile.split(":")
kfraser@15817 810 except:
kfraser@15817 811 err("Resource spec '%s' contains no tap subtype" % resource)
kfraser@15817 812
keir@16296 813 if typ in ["phy"]:
kfraser@15817 814 if not resfile.startswith("/"):
kfraser@15817 815 resfile = "/dev/" + resfile
kfraser@15817 816 if mustexist:
keir@16296 817 resfile = os.path.realpath(resfile)
keir@16296 818 try:
kfraser@15817 819 stats = os.lstat(resfile)
keir@16296 820 if not (stat.S_ISBLK(stats[stat.ST_MODE])):
keir@16296 821 err("Invalid resource")
keir@16296 822 except:
kfraser@15817 823 err("Invalid resource")
kfraser@15817 824
kfraser@15817 825 if typ in [ "file", "tap" ]:
keir@16296 826 resfile = os.path.realpath(resfile)
keir@16296 827 if mustexist and not os.path.isfile(resfile):
keir@16296 828 err("Invalid resource")
kfraser@15817 829
keir@16296 830 #file: resources must be specified with absolute path
kfraser@15817 831 #vlan resources don't start with '/'
kfraser@15817 832 if typ != "vlan":
kfraser@15817 833 if (not resfile.startswith("/")) or \
kfraser@15817 834 (mustexist and not os.path.exists(resfile)):
kfraser@15817 835 err("Invalid resource.")
kfraser@15817 836
kfraser@15817 837 # from here on absolute file names with resources
kfraser@15817 838 if typ == "tap":
kfraser@15817 839 typ = typ + ":" + subtype
kfraser@15817 840 resource = typ + ":" + resfile
kfraser@15817 841 return resource
kfraser@15817 842
kfraser@15817 843
kfraser@15817 844 def res_security_check(resource, domain_label):
kfraser@15817 845 """Checks if the given resource can be used by the given domain
kfraser@15817 846 label. Returns 1 if the resource can be used, otherwise 0.
kfraser@15817 847 """
kfraser@15817 848 rtnval = 1
kfraser@15817 849
kfraser@15817 850 # if security is on, ask the hypervisor for a decision
kfraser@15817 851 if on():
kfraser@15817 852 #build canonical resource name
kfraser@15817 853 resource = unify_resname(resource)
kfraser@15817 854
kfraser@15817 855 (label, ssidref, policy) = get_res_security_details(resource)
kfraser@15817 856 domac = ['access_control']
kfraser@15817 857 domac.append(['policy', active_policy])
kfraser@15817 858 domac.append(['label', domain_label])
kfraser@15817 859 domac.append(['type', 'dom'])
kfraser@15817 860 decision = get_decision(domac, ['ssidref', str(ssidref)])
kfraser@15817 861
kfraser@15817 862 # provide descriptive error messages
kfraser@15817 863 if decision == 'DENIED':
kfraser@15817 864 if label == ssidref2label(NULL_SSIDREF):
keir@16521 865 raise XSMError("Resource '"+resource+"' is not labeled")
kfraser@15817 866 rtnval = 0
kfraser@15817 867 else:
keir@16521 868 raise XSMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed")
kfraser@15817 869 rtnval = 0
kfraser@15817 870
kfraser@15817 871 # security is off, make sure resource isn't labeled
kfraser@15817 872 else:
kfraser@15817 873 # Note, we can't canonicalise the resource here, because people using
kfraser@15817 874 # xm without ACM are free to use relative paths.
kfraser@15817 875 (policytype, label, policy) = get_res_label(resource)
kfraser@15817 876 if policy != 'NULL':
keir@16521 877 raise XSMError("Security is off, but '"+resource+"' is labeled")
kfraser@15817 878 rtnval = 0
kfraser@15817 879
kfraser@15817 880 return rtnval
kfraser@15817 881
kfraser@15817 882 def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label):
kfraser@15817 883 """Checks if the given resource can be used by the given domain
kfraser@15817 884 label. Returns 1 if the resource can be used, otherwise 0.
kfraser@15817 885 """
kfraser@15817 886 rtnval = 1
kfraser@15817 887 # if security is on, ask the hypervisor for a decision
kfraser@15817 888 if on():
kfraser@15817 889 typ, dpolicy, domain_label = xapi_dom_label.split(":")
kfraser@15817 890 if not dpolicy or not domain_label:
kfraser@15817 891 raise VmError("VM security label in wrong format.")
kfraser@15817 892 if active_policy != rpolicy:
kfraser@15817 893 raise VmError("Resource's policy '%s' != active policy '%s'" %
kfraser@15817 894 (rpolicy, active_policy))
kfraser@15817 895 domac = ['access_control']
kfraser@15817 896 domac.append(['policy', active_policy])
kfraser@15817 897 domac.append(['label', domain_label])
kfraser@15817 898 domac.append(['type', 'dom'])
kfraser@15817 899 decision = get_decision(domac, ['ssidref', str(rssidref)])
kfraser@15817 900
kfraser@15817 901 log.info("Access Control Decision : %s" % decision)
kfraser@15817 902 # provide descriptive error messages
kfraser@15817 903 if decision == 'DENIED':
kfraser@15817 904 if rlabel == ssidref2label(NULL_SSIDREF):
keir@16521 905 #raise XSMError("Resource is not labeled")
kfraser@15817 906 rtnval = 0
kfraser@15817 907 else:
keir@16521 908 #raise XSMError("Permission denied for resource because label '"+rlabel+"' is not allowed")
kfraser@15817 909 rtnval = 0
kfraser@15817 910
kfraser@15817 911 # security is off, make sure resource isn't labeled
kfraser@15817 912 else:
kfraser@15817 913 # Note, we can't canonicalise the resource here, because people using
kfraser@15817 914 # xm without ACM are free to use relative paths.
kfraser@15817 915 if rpolicy != 'NULL':
keir@16521 916 #raise XSMError("Security is off, but resource is labeled")
kfraser@15817 917 rtnval = 0
kfraser@15817 918
kfraser@15817 919 return rtnval
kfraser@15817 920
kfraser@15817 921
keir@16521 922 def validate_label_xapi(xapi_label, dom_or_res):
keir@16521 923 """
keir@16521 924 Make sure that this label is part of the currently enforced policy
keir@16521 925 and that it references the current policy.
keir@16521 926 dom_or_res defines whether this is a VM ('res') or resource label
keir@16521 927 ('res')
keir@16521 928 """
keir@16521 929 tmp = xapi_label.split(":")
keir@16521 930 if len(tmp) != 3:
keir@16521 931 return -xsconstants.XSERR_BAD_LABEL_FORMAT
keir@16521 932 policytyp, policyref, label = tmp
keir@16521 933 return validate_label(policytyp, policyref, label, dom_or_res)
keir@16521 934
keir@16521 935
keir@16521 936 def validate_label(policytype, policyref, label, dom_or_res):
kfraser@15817 937 """
kfraser@15817 938 Make sure that this label is part of the currently enforced policy
kfraser@15817 939 and that it reference the current policy.
kfraser@15817 940 """
keir@16521 941 if policytype != xsconstants.ACM_POLICY_ID:
keir@16521 942 return -xsconstants.XSERR_WRONG_POLICY_TYPE
keir@16521 943 if not policytype or not label:
keir@16521 944 return -xsconstants.XSERR_BAD_LABEL_FORMAT
kfraser@15817 945 rc = xsconstants.XSERR_SUCCESS
kfraser@15817 946 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
kfraser@15817 947 curpol = XSPolicyAdminInstance().get_loaded_policy()
kfraser@15817 948 if not curpol or curpol.get_name() != policyref:
kfraser@15817 949 rc = -xsconstants.XSERR_BAD_LABEL
kfraser@15817 950 else:
kfraser@15817 951 try:
keir@16521 952 label2ssidref(label, curpol.get_name() , dom_or_res)
kfraser@15817 953 except:
kfraser@15817 954 rc = -xsconstants.XSERR_BAD_LABEL
kfraser@15817 955 return rc
kfraser@15817 956
kfraser@15817 957
kfraser@15817 958 def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi):
kfraser@15817 959 """Assign a resource label to a resource
kfraser@15817 960 @param resource: The name of a resource, i.e., "phy:/dev/hda", or
kfraser@15817 961 "tap:qcow:/path/to/file.qcow"
kfraser@15817 962
kfraser@15817 963 @param reslabel_xapi: A resource label foramtted as in all other parts of
kfraser@15817 964 the Xen-API, i.e., ACM:xm-test:blue"
kfraser@15817 965 @rtype: int
kfraser@15817 966 @return Success (0) or failure value (< 0)
kfraser@15817 967 """
kfraser@15817 968 olabel = ""
kfraser@15817 969 if reslabel_xapi == "":
kfraser@15817 970 return rm_resource_label(resource, oldlabel_xapi)
keir@16521 971
keir@16521 972 rc = validate_label_xapi(reslabel_xapi, 'res')
keir@16521 973 if rc != xsconstants.XSERR_SUCCESS:
keir@16521 974 return rc
keir@16521 975
kfraser@15817 976 if oldlabel_xapi not in [ "" ]:
kfraser@15817 977 tmp = oldlabel_xapi.split(":")
kfraser@15817 978 if len(tmp) != 3:
kfraser@15817 979 return -xsconstants.XSERR_BAD_LABEL_FORMAT
kfraser@15817 980 otyp, opolicyref, olabel = tmp
kfraser@15817 981 # Only ACM is supported
kfraser@15817 982 if otyp != xsconstants.ACM_POLICY_ID and \
kfraser@15817 983 otyp != xsconstants.INVALID_POLICY_PREFIX + \
kfraser@15817 984 xsconstants.ACM_POLICY_ID:
kfraser@15817 985 return -xsconstants.XSERR_WRONG_POLICY_TYPE
keir@16521 986 typ, policyref, label = reslabel_xapi.split(":")
kfraser@15817 987 return set_resource_label(resource, typ, policyref, label, olabel)
kfraser@15817 988
kfraser@15817 989
kfraser@15817 990 def is_resource_in_use(resource):
kfraser@15835 991 """
kfraser@15835 992 Domain-0 'owns' resources of type 'VLAN', the rest are owned by
kfraser@15835 993 the guests.
kfraser@15835 994 """
kfraser@15817 995 from xen.xend import XendDomain
kfraser@15817 996 lst = []
kfraser@15835 997 if resource.startswith('vlan'):
kfraser@15835 998 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
kfraser@15835 999 curpol = XSPolicyAdminInstance().get_loaded_policy()
kfraser@15835 1000 policytype, label, policy = get_res_label(resource)
kfraser@15835 1001 if curpol and \
kfraser@15835 1002 policytype == xsconstants.ACM_POLICY_ID and \
kfraser@15835 1003 policy == curpol.get_name() and \
kfraser@15835 1004 label in curpol.policy_get_resourcelabel_names():
kfraser@15835 1005 # VLAN is in use.
kfraser@15835 1006 lst.append(XendDomain.instance().
kfraser@15835 1007 get_vm_by_uuid(XendDomain.DOM0_UUID))
kfraser@15835 1008 else:
kfraser@15835 1009 dominfos = XendDomain.instance().list('all')
kfraser@15835 1010 for dominfo in dominfos:
kfraser@15835 1011 if is_resource_in_use_by_dom(dominfo, resource):
kfraser@15835 1012 lst.append(dominfo)
kfraser@15817 1013 return lst
kfraser@15817 1014
kfraser@15817 1015 def devices_equal(res1, res2, mustexist=True):
kfraser@15817 1016 """ Determine whether two devices are equal """
kfraser@15817 1017 return (unify_resname(res1, mustexist) ==
kfraser@15817 1018 unify_resname(res2, mustexist))
kfraser@15817 1019
kfraser@15817 1020 def is_resource_in_use_by_dom(dominfo, resource):
kfraser@15817 1021 """ Determine whether a resources is in use by a given domain
kfraser@15817 1022 @return True or False
kfraser@15817 1023 """
kfraser@15817 1024 if not dominfo.domid:
kfraser@15817 1025 return False
kfraser@15817 1026 if dominfo._stateGet() not in [ DOM_STATE_RUNNING ]:
kfraser@15817 1027 return False
kfraser@15817 1028 devs = dominfo.info['devices']
kfraser@15817 1029 uuids = devs.keys()
kfraser@15817 1030 for uuid in uuids:
kfraser@15817 1031 dev = devs[uuid]
kfraser@15817 1032 if len(dev) >= 2 and dev[1].has_key('uname'):
kfraser@15817 1033 # dev[0] is type, i.e. 'vbd'
kfraser@15817 1034 if devices_equal(dev[1]['uname'], resource, mustexist=False):
kfraser@15817 1035 log.info("RESOURCE IN USE: Domain %d uses %s." %
kfraser@15817 1036 (dominfo.domid, resource))
kfraser@15817 1037 return True
kfraser@15817 1038 return False
kfraser@15817 1039
kfraser@15817 1040
kfraser@15817 1041 def get_domain_resources(dominfo):
kfraser@15817 1042 """ Collect all resources of a domain in a map where each entry of
kfraser@15817 1043 the map is a list.
kfraser@15817 1044 Entries are strored in the following formats:
kfraser@15817 1045 tap:qcow:/path/xyz.qcow
kfraser@15817 1046 """
kfraser@15817 1047 resources = { 'vbd' : [], 'tap' : [], 'vif' : []}
kfraser@15817 1048 devs = dominfo.info['devices']
kfraser@15817 1049 uuids = devs.keys()
kfraser@15817 1050 for uuid in uuids:
kfraser@15817 1051 dev = devs[uuid]
kfraser@15817 1052 typ = dev[0]
kfraser@15817 1053 if typ in [ 'vbd', 'tap' ]:
kfraser@15817 1054 resources[typ].append(dev[1]['uname'])
kfraser@15817 1055 if typ in [ 'vif' ]:
kfraser@15817 1056 sec_lab = dev[1].get('security_label')
kfraser@15817 1057 if sec_lab:
kfraser@15817 1058 resources[typ].append(sec_lab)
kfraser@15817 1059 else:
kfraser@15835 1060 # !!! This should really get the label of the domain
kfraser@15835 1061 # or at least a resource label that has the same STE type
kfraser@15835 1062 # as the domain has
kfraser@15835 1063 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
kfraser@15817 1064 resources[typ].append("%s:%s:%s" %
kfraser@15817 1065 (xsconstants.ACM_POLICY_ID,
kfraser@15817 1066 active_policy,
kfraser@15817 1067 ACM_LABEL_UNLABELED))
kfraser@15817 1068
kfraser@15817 1069 return resources
kfraser@15817 1070
kfraser@15817 1071
kfraser@15817 1072 def resources_compatible_with_vmlabel(xspol, dominfo, vmlabel):
kfraser@15817 1073 """
kfraser@15817 1074 Check whether the resources' labels are compatible with the
kfraser@15817 1075 given VM label. This is a function to be used when for example
kfraser@15817 1076 a running domain is to get the new label 'vmlabel'
kfraser@15817 1077 """
kfraser@15817 1078 if not xspol:
kfraser@15817 1079 return False
kfraser@15817 1080
kfraser@15817 1081 try:
keir@16081 1082 resfile_lock()
kfraser@15817 1083 try:
kfraser@15817 1084 access_control = dictio.dict_read("resources",
kfraser@15817 1085 res_label_filename)
kfraser@15817 1086 except:
kfraser@15835 1087 # No labeled resources -> must be compatible
kfraser@15835 1088 return True
kfraser@15817 1089 return __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
kfraser@15817 1090 access_control)
kfraser@15817 1091 finally:
keir@16081 1092 resfile_unlock()
kfraser@15817 1093 return False
kfraser@15817 1094
kfraser@15817 1095
kfraser@15817 1096 def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
kfraser@15835 1097 access_control,
kfraser@15835 1098 is_policy_update=False):
kfraser@15817 1099 """
kfraser@15817 1100 Check whether the resources' labels are compatible with the
kfraser@15817 1101 given VM label. The access_control parameter provides a
kfraser@15817 1102 dictionary of the resource name to resource label mappings
kfraser@15817 1103 under which the evaluation should be done.
kfraser@15835 1104 Call this only for a paused or running domain.
kfraser@15817 1105 """
kfraser@15817 1106 def collect_labels(reslabels, s_label, polname):
kfraser@15817 1107 if len(s_label) != 3 or polname != s_label[1]:
kfraser@15817 1108 return False
kfraser@15817 1109 label = s_label[2]
kfraser@15817 1110 if not label in reslabels:
kfraser@15817 1111 reslabels.append(label)
kfraser@15817 1112 return True
kfraser@15817 1113
kfraser@15817 1114 resources = get_domain_resources(dominfo)
kfraser@15817 1115 reslabels = [] # all resource labels
kfraser@15817 1116
kfraser@15817 1117 polname = xspol.get_name()
kfraser@15817 1118 for key, value in resources.items():
kfraser@15817 1119 if key in [ 'vbd', 'tap' ]:
kfraser@15817 1120 for res in resources[key]:
kfraser@15817 1121 try:
kfraser@15817 1122 label = access_control[res]
kfraser@15817 1123 if not collect_labels(reslabels, label, polname):
kfraser@15817 1124 return False
kfraser@15817 1125 except:
kfraser@15817 1126 return False
kfraser@15817 1127 elif key in [ 'vif' ]:
kfraser@15817 1128 for xapi_label in value:
kfraser@15817 1129 label = xapi_label.split(":")
kfraser@15835 1130 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
kfraser@15835 1131 if not (is_policy_update and \
kfraser@15835 1132 label[2] == ACM_LABEL_UNLABELED):
kfraser@15835 1133 if not collect_labels(reslabels, label, polname):
kfraser@15835 1134 return False
kfraser@15817 1135 else:
kfraser@15817 1136 log.error("Unhandled device type: %s" % key)
kfraser@15817 1137 return False
kfraser@15817 1138
kfraser@15817 1139 # Check that all resource labes have a common STE type with the
kfraser@15817 1140 # vmlabel
kfraser@15835 1141 if len(reslabels) > 0:
kfraser@15835 1142 rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels)
kfraser@15835 1143 else:
kfraser@15835 1144 rc = True
kfraser@15835 1145 log.info("vmlabel=%s, reslabels=%s, rc=%s" %
kfraser@15835 1146 (vmlabel, reslabels, str(rc)))
kfraser@15817 1147 return rc;
kfraser@15817 1148
kfraser@15817 1149 def set_resource_label(resource, policytype, policyref, reslabel, \
kfraser@15817 1150 oreslabel = None):
keir@16521 1151 """
keir@16521 1152 Xend exports this function via XML-RPC.
keir@16521 1153
keir@16521 1154 Assign a label to a resource
kfraser@15817 1155 If the old label (oreslabel) is given, then the resource must have
kfraser@15817 1156 that old label.
kfraser@15817 1157 A resource label may be changed if
kfraser@15817 1158 - the resource is not in use
kfraser@15817 1159 @param resource : The name of a resource, i.e., "phy:/dev/hda"
kfraser@15817 1160 @param policyref : The name of the policy
kfraser@15817 1161 @param reslabel : the resource label within the policy
kfraser@15817 1162 @param oreslabel : optional current resource label
kfraser@15817 1163
kfraser@15817 1164 @rtype: int
kfraser@15817 1165 @return Success (0) or failure value (< 0)
kfraser@15817 1166 """
keir@16521 1167
keir@16521 1168 if reslabel != "":
keir@16521 1169 ssidref = label2ssidref(reslabel, policyref, 'res')
keir@16521 1170
kfraser@15817 1171 try:
kfraser@15817 1172 resource = unify_resname(resource, mustexist=False)
kfraser@15817 1173 except Exception:
kfraser@15817 1174 return -xsconstants.XSERR_BAD_RESOURCE_FORMAT
kfraser@15817 1175
kfraser@15817 1176 domains = is_resource_in_use(resource)
kfraser@15817 1177 if len(domains) > 0:
kfraser@15817 1178 return -xsconstants.XSERR_RESOURCE_IN_USE
kfraser@15817 1179
kfraser@15817 1180 try:
keir@16081 1181 resfile_lock()
kfraser@15817 1182 access_control = {}
kfraser@15817 1183 try:
kfraser@15817 1184 access_control = dictio.dict_read("resources", res_label_filename)
kfraser@15817 1185 except:
kfraser@15817 1186 pass
kfraser@15817 1187 if oreslabel:
kfraser@15817 1188 if not access_control.has_key(resource):
kfraser@15817 1189 return -xsconstants.XSERR_BAD_LABEL
kfraser@15817 1190 tmp = access_control[resource]
kfraser@15817 1191 if len(tmp) != 3:
kfraser@15817 1192 return -xsconstants.XSERR_BAD_LABEL
kfraser@15817 1193 if tmp[2] != oreslabel:
kfraser@15817 1194 return -xsconstants.XSERR_BAD_LABEL
kfraser@15817 1195 if reslabel != "":
kfraser@15817 1196 new_entry = { resource : tuple([policytype, policyref, reslabel])}
kfraser@15817 1197 access_control.update(new_entry)
keir@16260 1198 command = "add"
keir@16260 1199 reslbl = ":".join([policytype, policyref, reslabel])
kfraser@15817 1200 else:
kfraser@15817 1201 if access_control.has_key(resource):
kfraser@15817 1202 del access_control[resource]
keir@16260 1203 command = "remove"
keir@16260 1204 reslbl = ""
keir@16260 1205 run_resource_label_change_script(resource, reslbl, command)
kfraser@15817 1206 dictio.dict_write(access_control, "resources", res_label_filename)
kfraser@15817 1207 finally:
keir@16081 1208 resfile_unlock()
kfraser@15817 1209 return xsconstants.XSERR_SUCCESS
kfraser@15817 1210
kfraser@15817 1211 def rm_resource_label(resource, oldlabel_xapi):
kfraser@15817 1212 """Remove a resource label from a physical resource
kfraser@15817 1213 @param resource: The name of a resource, i.e., "phy:/dev/hda"
kfraser@15817 1214
kfraser@15817 1215 @rtype: int
kfraser@15817 1216 @return Success (0) or failure value (< 0)
kfraser@15817 1217 """
kfraser@15817 1218 tmp = oldlabel_xapi.split(":")
kfraser@15817 1219 if len(tmp) != 3:
kfraser@15817 1220 return -xsconstants.XSERR_BAD_LABEL_FORMAT
kfraser@15817 1221 otyp, opolicyref, olabel = tmp
kfraser@15817 1222 # Only ACM is supported
kfraser@15817 1223 if otyp != xsconstants.ACM_POLICY_ID and \
kfraser@15817 1224 otyp != xsconstants.INVALID_POLICY_PREFIX + xsconstants.ACM_POLICY_ID:
kfraser@15817 1225 return -xsconstants.XSERR_WRONG_POLICY_TYPE
kfraser@15817 1226 return set_resource_label(resource, "", "", "", olabel)
kfraser@15817 1227
kfraser@15817 1228 def get_resource_label_xapi(resource):
kfraser@15817 1229 """Get the assigned resource label of a physical resource
kfraser@15817 1230 in the format used by then Xen-API, i.e., "ACM:xm-test:blue"
kfraser@15817 1231
kfraser@15817 1232 @rtype: string
kfraser@15817 1233 @return the string representing policy type, policy name and label of
kfraser@15817 1234 the resource
kfraser@15817 1235 """
kfraser@15817 1236 res = get_resource_label(resource)
kfraser@15817 1237 return format_resource_label(res)
kfraser@15817 1238
kfraser@15817 1239 def format_resource_label(res):
kfraser@15817 1240 if res:
kfraser@15817 1241 if len(res) == 2:
kfraser@15817 1242 return xsconstants.ACM_POLICY_ID + ":" + res[0] + ":" + res[1]
kfraser@15817 1243 if len(res) == 3:
kfraser@15817 1244 return ":".join(res)
kfraser@15817 1245 return ""
kfraser@15817 1246
kfraser@15817 1247 def get_resource_label(resource):
keir@16521 1248 """
keir@16521 1249 Xend exports this function via XML-RPC.
keir@16521 1250
keir@16521 1251 Get the assigned resource label of a given resource
kfraser@15817 1252 @param resource: The name of a resource, i.e., "phy:/dev/hda"
kfraser@15817 1253
kfraser@15817 1254 @rtype: list
kfraser@15817 1255 @return tuple of (policy name, resource label), i.e., (xm-test, blue)
kfraser@15817 1256 """
kfraser@15817 1257 try:
kfraser@15817 1258 resource = unify_resname(resource, mustexist=False)
kfraser@15817 1259 except Exception:
kfraser@15817 1260 return []
kfraser@15817 1261
kfraser@15817 1262 reslabel_map = get_labeled_resources()
kfraser@15817 1263
kfraser@15817 1264 if reslabel_map.has_key(resource):
kfraser@15817 1265 return list(reslabel_map[resource])
kfraser@15817 1266 else:
kfraser@15817 1267 #Try to resolve each label entry
kfraser@15817 1268 for key, value in reslabel_map.items():
kfraser@15817 1269 try:
kfraser@15817 1270 if resource == unify_resname(key):
kfraser@15817 1271 return list(value)
kfraser@15817 1272 except:
kfraser@15817 1273 pass
kfraser@15817 1274
kfraser@15817 1275 return []
kfraser@15817 1276
kfraser@15817 1277
kfraser@15817 1278 def get_labeled_resources_xapi():
kfraser@15817 1279 """ Get a map of all labeled resource with the labels formatted in the
kfraser@15817 1280 xen-api resource label format.
kfraser@15817 1281 """
kfraser@15817 1282 reslabel_map = get_labeled_resources()
kfraser@15817 1283 for key, labeldata in reslabel_map.items():
kfraser@15817 1284 reslabel_map[key] = format_resource_label(labeldata)
kfraser@15817 1285 return reslabel_map
kfraser@15817 1286
kfraser@15817 1287
kfraser@15817 1288 def get_labeled_resources():
keir@16521 1289 """
keir@16521 1290 Xend exports this function via XML-RPC
keir@16521 1291
keir@16521 1292 Get a map of all labeled resources.
kfraser@15817 1293 @rtype: list
kfraser@15817 1294 @return list of labeled resources
kfraser@15817 1295 """
kfraser@15817 1296 try:
keir@16081 1297 resfile_lock()
kfraser@15817 1298 try:
kfraser@15817 1299 access_control = dictio.dict_read("resources", res_label_filename)
kfraser@15817 1300 except:
kfraser@15817 1301 return {}
kfraser@15817 1302 finally:
keir@16081 1303 resfile_unlock()
kfraser@15817 1304 return access_control
kfraser@15817 1305
kfraser@15817 1306
kfraser@15817 1307 def relabel_domains(relabel_list):
kfraser@15817 1308 """
kfraser@15817 1309 Relabel the given domains to have a new ssidref.
kfraser@15817 1310 @param relabel_list: a list containing tuples of domid, ssidref
kfraser@15817 1311 example: [ [0, 0x00020002] ]
kfraser@15817 1312 """
kfraser@15817 1313 rel_rules = ""
kfraser@15817 1314 for r in relabel_list:
kfraser@15817 1315 log.info("Relabeling domain with domid %d to new ssidref 0x%08x",
kfraser@15817 1316 r[0], r[1])
kfraser@15817 1317 rel_rules += struct.pack("ii", r[0], r[1])
kfraser@15817 1318 try:
kfraser@15817 1319 rc, errors = acm.relabel_domains(rel_rules)
kfraser@15817 1320 except Exception, e:
kfraser@15817 1321 log.info("Error after relabel_domains: %s" % str(e))
kfraser@15817 1322 rc = -xsconstants.XSERR_GENERAL_FAILURE
kfraser@15817 1323 errors = ""
kfraser@15817 1324 if (len(errors) > 0):
kfraser@15817 1325 rc = -xsconstants.XSERR_HV_OP_FAILED
kfraser@15817 1326 return rc, errors
kfraser@15817 1327
kfraser@15817 1328
kfraser@15817 1329 def change_acm_policy(bin_pol, del_array, chg_array,
kfraser@15817 1330 vmlabel_map, reslabel_map, cur_acmpol, new_acmpol):
kfraser@15817 1331 """
kfraser@15817 1332 Change the ACM policy of the system by relabeling
kfraser@15817 1333 domains and resources first and doing some access checks.
kfraser@15817 1334 Then update the policy in the hypervisor. If this is all successful,
kfraser@15817 1335 relabel the domains permanently and commit the relabed resources.
kfraser@15817 1336
kfraser@15817 1337 Need to do / check the following:
kfraser@15817 1338 - relabel all resources where there is a 'from' field in
kfraser@15817 1339 the policy. [ NOT DOING THIS: and mark those as unlabeled where the label
kfraser@15817 1340 does not appear in the new policy anymore (deletion) ]
kfraser@15817 1341 - relabel all VMs where there is a 'from' field in the
kfraser@15817 1342 policy and mark those as unlabeled where the label
kfraser@15817 1343 does not appear in the new policy anymore; no running
kfraser@15817 1344 or paused VM may be unlabeled through this
kfraser@15817 1345 - check that under the new labeling conditions the VMs
kfraser@15817 1346 still have access to their resources as before. Unlabeled
kfraser@15817 1347 resources are inaccessible. If this check fails, the
kfraser@15817 1348 update failed.
kfraser@15817 1349 - Attempt changes in the hypervisor; if this step fails,
kfraser@15817 1350 roll back the relabeling of resources and VMs
kfraser@15817 1351 - Make the relabeling of resources and VMs permanent
keir@16081 1352
keir@16081 1353 This function should be called with the lock to the domains
keir@16081 1354 held (XendDomain.instance().domains_lock)
kfraser@15817 1355 """
keir@16521 1356 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
kfraser@15817 1357 rc = xsconstants.XSERR_SUCCESS
kfraser@15817 1358
kfraser@15817 1359 domain_label_map = {}
kfraser@15817 1360 new_policyname = new_acmpol.get_name()
kfraser@15817 1361 new_policytype = new_acmpol.get_type_name()
kfraser@15817 1362 cur_policyname = cur_acmpol.get_name()
kfraser@15817 1363 cur_policytype = cur_acmpol.get_type_name()
kfraser@15817 1364 polnew_reslabels = new_acmpol.policy_get_resourcelabel_names()
kfraser@15817 1365 errors=""
kfraser@15817 1366
kfraser@15817 1367 try:
keir@16081 1368 resfile_lock()
kfraser@15817 1369 mapfile_lock()
kfraser@15817 1370
kfraser@15817 1371 # Get all domains' dominfo.
kfraser@15817 1372 from xen.xend import XendDomain
kfraser@15817 1373 dominfos = XendDomain.instance().list('all')
kfraser@15817 1374
kfraser@15817 1375 log.info("----------------------------------------------")
kfraser@15817 1376 # relabel resources
kfraser@15817 1377
kfraser@15817 1378 access_control = {}
kfraser@15817 1379 try:
kfraser@15817 1380 access_control = dictio.dict_read("resources", res_label_filename)
kfraser@15835 1381 except:
kfraser@15817 1382 pass
keir@16081 1383
kfraser@15817 1384 for key, labeldata in access_control.items():
kfraser@15817 1385 if len(labeldata) == 2:
kfraser@15817 1386 policy, label = labeldata
kfraser@15817 1387 policytype = xsconstants.ACM_POLICY_ID
kfraser@15817 1388 elif len(labeldata) == 3:
kfraser@15817 1389 policytype, policy, label = labeldata
kfraser@15817 1390 else:
kfraser@15817 1391 return -xsconstants.XSERR_BAD_LABEL_FORMAT, ""
kfraser@15817 1392
kfraser@15817 1393 if policytype != cur_policytype or \
kfraser@15817 1394 policy != cur_policyname:
kfraser@15817 1395 continue
kfraser@15817 1396
kfraser@15817 1397 # label been renamed or deleted?
keir@16521 1398 if policytype != xsconstants.ACM_POLICY_ID:
keir@16521 1399 continue
keir@16521 1400 elif reslabel_map.has_key(label) and cur_policyname == policy:
keir@16521 1401 # renaming of an active label; policy may have been renamed
kfraser@15817 1402 label = reslabel_map[label]
keir@16521 1403 polname = new_policyname
kfraser@15817 1404 elif label not in polnew_reslabels:
keir@16521 1405 # label been removed
kfraser@15817 1406 policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
keir@16260 1407 run_resource_label_change_script(key, "", "remove")
keir@16521 1408 polname = policy
keir@16521 1409 else:
keir@16521 1410 # no change to label
keir@16521 1411 policytype = xsconstants.ACM_POLICY_ID
keir@16521 1412 polname = new_policyname
keir@16521 1413
kfraser@15817 1414 # Update entry
kfraser@15817 1415 access_control[key] = \
keir@16521 1416 tuple([ policytype, polname, label ])
kfraser@15817 1417
kfraser@15817 1418 # All resources have new labels in the access_control map
kfraser@15817 1419 # There may still be labels in there that are invalid now.
kfraser@15817 1420
kfraser@15817 1421 # Do this in memory without writing to disk:
kfraser@15817 1422 # - Relabel all domains independent of whether they are running
kfraser@15817 1423 # or not
kfraser@15817 1424 # - later write back to config files
kfraser@15817 1425 polnew_vmlabels = new_acmpol.policy_get_virtualmachinelabel_names()
kfraser@15817 1426
kfraser@15817 1427 for dominfo in dominfos:
kfraser@15817 1428 sec_lab = dominfo.get_security_label()
kfraser@15817 1429 if not sec_lab:
kfraser@15817 1430 continue
kfraser@15817 1431 policytype, policy, vmlabel = sec_lab.split(":")
kfraser@15817 1432 name = dominfo.getName()
kfraser@15817 1433
kfraser@15817 1434 if policytype != cur_policytype or \
kfraser@15817 1435 policy != cur_policyname:
kfraser@15817 1436 continue
kfraser@15817 1437
kfraser@15817 1438 new_vmlabel = vmlabel
kfraser@15817 1439 if vmlabel_map.has_key(vmlabel):
keir@16521 1440 # renaming of the label
kfraser@15817 1441 new_vmlabel = vmlabel_map[vmlabel]
keir@16521 1442 polname = new_policyname
keir@16521 1443 elif new_vmlabel not in polnew_vmlabels and \
keir@16521 1444 vmlabel != ACM_LABEL_UNLABELED:
keir@16521 1445 # removal of VM label and not the 'unlabeled' label
kfraser@15817 1446 policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
keir@16521 1447 polname = policy
keir@16521 1448 else:
keir@16521 1449 polname = new_policyname
keir@16521 1450
kfraser@15817 1451 new_seclab = "%s:%s:%s" % \
keir@16521 1452 (policytype, polname, new_vmlabel)
kfraser@15817 1453
kfraser@15817 1454 domain_label_map[dominfo] = [ sec_lab, new_seclab ]
kfraser@15817 1455
kfraser@15817 1456 if dominfo._stateGet() in (DOM_STATE_PAUSED, DOM_STATE_RUNNING):
kfraser@15817 1457 compatible = __resources_compatible_with_vmlabel(new_acmpol,
kfraser@15817 1458 dominfo,
kfraser@15817 1459 new_vmlabel,
kfraser@15835 1460 access_control,
kfraser@15835 1461 is_policy_update=True)
kfraser@15817 1462 log.info("Domain %s with new label '%s' can access its "
kfraser@15817 1463 "resources? : %s" %
kfraser@15817 1464 (name, new_vmlabel, str(compatible)))
kfraser@15835 1465 log.info("VM labels in new policy: %s" %
kfraser@15817 1466 new_acmpol.policy_get_virtualmachinelabel_names())
kfraser@15817 1467 if not compatible:
kfraser@15817 1468 return (-xsconstants.XSERR_RESOURCE_ACCESS, "")
kfraser@15817 1469
kfraser@15817 1470 rc, errors = hv_chg_policy(bin_pol, del_array, chg_array)
kfraser@15817 1471 if rc == 0:
kfraser@15817 1472 # Write the relabeled resources back into the file
kfraser@15817 1473 dictio.dict_write(access_control, "resources", res_label_filename)
kfraser@15817 1474 # Properly update all VMs to their new labels
kfraser@15817 1475 for dominfo, labels in domain_label_map.items():
kfraser@15817 1476 sec_lab, new_seclab = labels
kfraser@15817 1477 if sec_lab != new_seclab:
kfraser@15817 1478 log.info("Updating domain %s to new label '%s'." % \
kfraser@15835 1479 (dominfo.getName(), new_seclab))
kfraser@15817 1480 # This better be working!
kfraser@15835 1481 res = dominfo.set_security_label(new_seclab,
kfraser@15835 1482 sec_lab,
kfraser@15835 1483 new_acmpol,
kfraser@15835 1484 cur_acmpol)
kfraser@15835 1485 if res[0] != xsconstants.XSERR_SUCCESS:
kfraser@15835 1486 log.info("ERROR: Could not chg label on domain %s: %s" %
kfraser@15835 1487 (dominfo.getName(),
kfraser@15835 1488 xsconstants.xserr2string(-int(res[0]))))
kfraser@15817 1489 finally:
kfraser@15817 1490 log.info("----------------------------------------------")
kfraser@15817 1491 mapfile_unlock()
keir@16081 1492 resfile_unlock()
kfraser@15817 1493
kfraser@15817 1494 return rc, errors
kfraser@15817 1495
kfraser@15817 1496 def parse_security_label(security_label):
kfraser@15817 1497 tmp = security_label.split(":")
kfraser@15817 1498 if len(tmp) != 3:
kfraser@15817 1499 return ""
kfraser@15817 1500 else:
kfraser@15817 1501 return security_label
kfraser@15817 1502
kfraser@15817 1503 def set_security_label(policy, label):
keir@16033 1504 if label and policy and label != "" and policy != "":
keir@15962 1505 return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, policy, label)
kfraser@15817 1506 else:
kfraser@15817 1507 return ""
kfraser@15817 1508
kfraser@15817 1509 def ssidref2security_label(ssidref):
kfraser@15817 1510 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
kfraser@15817 1511 return XSPolicyAdminInstance().ssidref_to_vmlabel(ssidref)
keir@15962 1512
keir@15962 1513 def get_security_label(self, xspol=None):
keir@15962 1514 """
keir@15962 1515 Get the security label of a domain
keir@15962 1516 @param xspol The policy to use when converting the ssid into
keir@15962 1517 a label; only to be passed during the updating
keir@15962 1518 of the policy
keir@15962 1519 """
keir@15962 1520 domid = self.getDomid()
keir@15962 1521
keir@15962 1522 if not xspol:
keir@15962 1523 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
keir@15962 1524 xspol = XSPolicyAdminInstance().get_loaded_policy()
keir@15962 1525
keir@16259 1526 label = ""
keir@16259 1527 if xspol:
keir@16259 1528 label = xspol.policy_get_domain_label_formatted(domid)
keir@16259 1529 if domid != 0:
keir@16259 1530 label = self.info.get('security_label', label)
keir@15962 1531 return label
keir@16260 1532
keir@16260 1533 def run_resource_label_change_script(resource, label, command):
keir@16521 1534 def __run_resource_label_change_script(label, command):
keir@16521 1535 script = XendOptions.instance().get_resource_label_change_script()
keir@16521 1536 if script:
keir@16521 1537 parms = {
keir@16521 1538 'resource' : resource,
keir@16521 1539 'label' : label,
keir@16521 1540 'command' : command,
keir@16521 1541 }
keir@16521 1542 log.info("Running resource label change script %s: %s" %
keir@16521 1543 (script, parms))
keir@16521 1544 parms.update(os.environ)
keir@16521 1545 os.spawnve(os.P_WAIT, script[0], script, parms)
keir@16521 1546 else:
keir@16521 1547 log.info("No script given for relabeling of resources.")
keir@16521 1548 thread = threading.Thread(target=__run_resource_label_change_script,
keir@16521 1549 args=(label,command))
keir@16521 1550 thread.start()