ia64/xen-unstable

view tools/python/xen/util/xsm/acm/acm.py @ 19848:5839491bbf20

[IA64] replace MAX_VCPUS with d->max_vcpus where necessary.

don't use MAX_VCPUS, and use vcpu::max_vcpus.
The changeset of 2f9e1348aa98 introduced max_vcpus to allow more vcpus
per guest. This patch is ia64 counter part.

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