ia64/xen-unstable

view tools/python/xen/util/xsm/acm/acm.py @ 17578:94c6501c4ffe

xend: Refactor security.on() call

I am refactoring the security.on() call to return the actual type of
the security module that is found to be enabled rather than just
returning True or False.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 06 10:05:52 2008 +0100 (2008-05-06)
parents 592e3ab73459
children 65eec0554f39
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
35 from xen.xend.XendConstants import *
37 #global directories and tools for security management
38 install_policy_dir_prefix = "/etc/xen/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 (primary, secondary, f, pol_exists) = getmapfile(policy_name)
724 if not f:
725 if pol_exists:
726 err("Cannot find mapfile for policy \'" + policy_name + "\'.\n")
727 else:
728 err("Unknown policy \'" + policy_name + "\'")
730 labels = []
731 for line in f:
732 if condition.match(line):
733 label = line.split()[3]
734 if label not in labels:
735 labels.append(label)
737 if '__NULL_LABEL__' in labels:
738 labels.remove('__NULL_LABEL__')
740 return labels
743 def get_res_label(resource):
744 """Returns resource label information (policytype, label, policy) if
745 it exists. Otherwise returns null label and policy.
746 """
747 def default_res_label():
748 ssidref = NULL_SSIDREF
749 if on():
750 label = ssidref2label(ssidref)
751 else:
752 label = None
753 return (xsconstants.ACM_POLICY_ID, 'NULL', label)
756 tmp = get_resource_label(resource)
757 if len(tmp) == 2:
758 policytype = xsconstants.ACM_POLICY_ID
759 policy, label = tmp
760 elif len(tmp) == 3:
761 policytype, policy, label = tmp
762 else:
763 policytype, policy, label = default_res_label()
765 return (policytype, label, policy)
768 def get_res_security_details(resource):
769 """Returns the (label, ssidref, policy) associated with a given
770 resource from the global resource label file.
771 """
772 def default_security_details():
773 ssidref = NULL_SSIDREF
774 if on():
775 label = ssidref2label(ssidref)
776 else:
777 label = None
778 policy = active_policy
779 return (label, ssidref, policy)
781 (label, ssidref, policy) = default_security_details()
783 # find the entry associated with this resource
784 (policytype, label, policy) = get_res_label(resource)
785 if policy == 'NULL':
786 log.info("Resource label for "+resource+" not in file, using DEFAULT.")
787 return default_security_details()
789 if policytype != xsconstants.ACM_POLICY_ID:
790 raise VmError("Unknown policy type '%s in label for resource '%s'" %
791 (policytype, resource))
793 # is this resource label for the running policy?
794 if policy == active_policy:
795 ssidref = label2ssidref(label, policy, 'res')
796 else:
797 log.info("Resource label not for active policy, using DEFAULT.")
798 return default_security_details()
800 return (label, ssidref, policy)
802 def security_label_to_details(seclab):
803 """ Convert a Xen-API type of security label into details """
804 def default_security_details():
805 ssidref = NULL_SSIDREF
806 if on():
807 label = ssidref2label(ssidref)
808 else:
809 label = None
810 policy = active_policy
811 return (label, ssidref, policy)
813 (policytype, policy, label) = seclab.split(":")
815 # is this resource label for the running policy?
816 if policy == active_policy:
817 ssidref = label2ssidref(label, policy, 'res')
818 else:
819 log.info("Resource label not for active policy, using DEFAULT.")
820 return default_security_details()
822 return (label, ssidref, policy)
824 def unify_resname(resource, mustexist=True):
825 """Makes all resource locations absolute. In case of physical
826 resources, '/dev/' is added to local file names"""
828 if not resource:
829 return resource
831 # sanity check on resource name
832 try:
833 (typ, resfile) = resource.split(":", 1)
834 except:
835 err("Resource spec '%s' contains no ':' delimiter" % resource)
837 if typ == "tap":
838 try:
839 (subtype, resfile) = resfile.split(":")
840 except:
841 err("Resource spec '%s' contains no tap subtype" % resource)
843 if typ in ["phy"]:
844 if not resfile.startswith("/"):
845 resfile = "/dev/" + resfile
846 if mustexist:
847 resfile = os.path.realpath(resfile)
848 try:
849 stats = os.lstat(resfile)
850 if not (stat.S_ISBLK(stats[stat.ST_MODE])):
851 err("Invalid resource")
852 except:
853 err("Invalid resource")
855 if typ in [ "file", "tap" ]:
856 resfile = os.path.realpath(resfile)
857 if mustexist and not os.path.isfile(resfile):
858 err("Invalid resource")
860 #file: resources must be specified with absolute path
861 #vlan resources don't start with '/'
862 if typ != "vlan":
863 if (not resfile.startswith("/")) or \
864 (mustexist and not os.path.exists(resfile)):
865 err("Invalid resource.")
867 # from here on absolute file names with resources
868 if typ == "tap":
869 typ = typ + ":" + subtype
870 resource = typ + ":" + resfile
871 return resource
874 def res_security_check(resource, domain_label):
875 """Checks if the given resource can be used by the given domain
876 label. Returns 1 if the resource can be used, otherwise 0.
877 """
878 rtnval = 1
880 # if security is on, ask the hypervisor for a decision
881 if on():
882 #build canonical resource name
883 resource = unify_resname(resource)
885 (label, ssidref, policy) = get_res_security_details(resource)
886 domac = ['access_control']
887 domac.append(['policy', active_policy])
888 domac.append(['label', domain_label])
889 domac.append(['type', 'dom'])
890 decision = get_decision(domac, ['ssidref', str(ssidref)])
892 # provide descriptive error messages
893 if decision == 'DENIED':
894 if label == ssidref2label(NULL_SSIDREF):
895 raise XSMError("Resource '"+resource+"' is not labeled")
896 rtnval = 0
897 else:
898 raise XSMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed")
899 rtnval = 0
901 # security is off, make sure resource isn't labeled
902 else:
903 # Note, we can't canonicalise the resource here, because people using
904 # xm without ACM are free to use relative paths.
905 (policytype, label, policy) = get_res_label(resource)
906 if policy != 'NULL':
907 raise XSMError("Security is off, but '"+resource+"' is labeled")
908 rtnval = 0
910 return rtnval
912 def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label):
913 """Checks if the given resource can be used by the given domain
914 label. Returns 1 if the resource can be used, otherwise 0.
915 """
916 rtnval = 1
917 # if security is on, ask the hypervisor for a decision
918 if on():
919 typ, dpolicy, domain_label = xapi_dom_label.split(":")
920 if not dpolicy or not domain_label:
921 raise VmError("VM security label in wrong format.")
922 if active_policy != rpolicy:
923 raise VmError("Resource's policy '%s' != active policy '%s'" %
924 (rpolicy, active_policy))
925 domac = ['access_control']
926 domac.append(['policy', active_policy])
927 domac.append(['label', domain_label])
928 domac.append(['type', 'dom'])
929 decision = get_decision(domac, ['ssidref', str(rssidref)])
931 log.info("Access Control Decision : %s" % decision)
932 # provide descriptive error messages
933 if decision == 'DENIED':
934 if rlabel == ssidref2label(NULL_SSIDREF):
935 #raise XSMError("Resource is not labeled")
936 rtnval = 0
937 else:
938 #raise XSMError("Permission denied for resource because label '"+rlabel+"' is not allowed")
939 rtnval = 0
941 # security is off, make sure resource isn't labeled
942 else:
943 # Note, we can't canonicalise the resource here, because people using
944 # xm without ACM are free to use relative paths.
945 if rpolicy != 'NULL':
946 #raise XSMError("Security is off, but resource is labeled")
947 rtnval = 0
949 return rtnval
952 def validate_label_xapi(xapi_label, dom_or_res):
953 """
954 Make sure that this label is part of the currently enforced policy
955 and that it references the current policy.
956 dom_or_res defines whether this is a VM ('res') or resource label
957 ('res')
958 """
959 tmp = xapi_label.split(":")
960 if len(tmp) != 3:
961 return -xsconstants.XSERR_BAD_LABEL_FORMAT
962 policytyp, policyref, label = tmp
963 return validate_label(policytyp, policyref, label, dom_or_res)
966 def validate_label(policytype, policyref, label, dom_or_res):
967 """
968 Make sure that this label is part of the currently enforced policy
969 and that it reference the current policy.
970 """
971 if policytype != xsconstants.ACM_POLICY_ID:
972 return -xsconstants.XSERR_WRONG_POLICY_TYPE
973 if not policytype or not label:
974 return -xsconstants.XSERR_BAD_LABEL_FORMAT
975 rc = xsconstants.XSERR_SUCCESS
976 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
977 curpol = XSPolicyAdminInstance().get_loaded_policy()
978 if not curpol or curpol.get_name() != policyref:
979 rc = -xsconstants.XSERR_BAD_LABEL
980 else:
981 try:
982 label2ssidref(label, curpol.get_name() , dom_or_res)
983 except:
984 rc = -xsconstants.XSERR_BAD_LABEL
985 return rc
988 def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi):
989 """Assign a resource label to a resource
990 @param resource: The name of a resource, i.e., "phy:/dev/hda", or
991 "tap:qcow:/path/to/file.qcow"
993 @param reslabel_xapi: A resource label foramtted as in all other parts of
994 the Xen-API, i.e., ACM:xm-test:blue"
995 @rtype: int
996 @return Success (0) or failure value (< 0)
997 """
998 olabel = ""
999 if reslabel_xapi == "":
1000 return rm_resource_label(resource, oldlabel_xapi)
1002 rc = validate_label_xapi(reslabel_xapi, 'res')
1003 if rc != xsconstants.XSERR_SUCCESS:
1004 return rc
1006 if oldlabel_xapi not in [ "" ]:
1007 tmp = oldlabel_xapi.split(":")
1008 if len(tmp) != 3:
1009 return -xsconstants.XSERR_BAD_LABEL_FORMAT
1010 otyp, opolicyref, olabel = tmp
1011 # Only ACM is supported
1012 if otyp != xsconstants.ACM_POLICY_ID and \
1013 otyp != xsconstants.INVALID_POLICY_PREFIX + \
1014 xsconstants.ACM_POLICY_ID:
1015 return -xsconstants.XSERR_WRONG_POLICY_TYPE
1016 typ, policyref, label = reslabel_xapi.split(":")
1017 return set_resource_label(resource, typ, policyref, label, olabel)
1020 def is_resource_in_use(resource):
1021 """
1022 Domain-0 'owns' resources of type 'VLAN', the rest are owned by
1023 the guests.
1024 """
1025 from xen.xend import XendDomain
1026 lst = []
1027 if resource.startswith('vlan'):
1028 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
1029 curpol = XSPolicyAdminInstance().get_loaded_policy()
1030 policytype, label, policy = get_res_label(resource)
1031 if curpol and \
1032 policytype == xsconstants.ACM_POLICY_ID and \
1033 policy == curpol.get_name() and \
1034 label in curpol.policy_get_resourcelabel_names():
1035 # VLAN is in use.
1036 lst.append(XendDomain.instance().
1037 get_vm_by_uuid(XendDomain.DOM0_UUID))
1038 else:
1039 dominfos = XendDomain.instance().list('all')
1040 for dominfo in dominfos:
1041 if is_resource_in_use_by_dom(dominfo, resource):
1042 lst.append(dominfo)
1043 return lst
1045 def devices_equal(res1, res2, mustexist=True):
1046 """ Determine whether two devices are equal """
1047 return (unify_resname(res1, mustexist) ==
1048 unify_resname(res2, mustexist))
1050 def is_resource_in_use_by_dom(dominfo, resource):
1051 """ Determine whether a resources is in use by a given domain
1052 @return True or False
1053 """
1054 if not dominfo.domid:
1055 return False
1056 if dominfo._stateGet() not in [ DOM_STATE_RUNNING ]:
1057 return False
1058 devs = dominfo.info['devices']
1059 uuids = devs.keys()
1060 for uuid in uuids:
1061 dev = devs[uuid]
1062 if len(dev) >= 2 and dev[1].has_key('uname'):
1063 # dev[0] is type, i.e. 'vbd'
1064 if devices_equal(dev[1]['uname'], resource, mustexist=False):
1065 log.info("RESOURCE IN USE: Domain %d uses %s." %
1066 (dominfo.domid, resource))
1067 return True
1068 return False
1071 def get_domain_resources(dominfo):
1072 """ Collect all resources of a domain in a map where each entry of
1073 the map is a list.
1074 Entries are strored in the following formats:
1075 tap:qcow:/path/xyz.qcow
1076 """
1077 resources = { 'vbd' : [], 'tap' : [], 'vif' : []}
1078 devs = dominfo.info['devices']
1079 uuids = devs.keys()
1080 for uuid in uuids:
1081 dev = devs[uuid]
1082 typ = dev[0]
1083 if typ in [ 'vbd', 'tap' ]:
1084 resources[typ].append(dev[1]['uname'])
1085 if typ in [ 'vif' ]:
1086 sec_lab = dev[1].get('security_label')
1087 if sec_lab:
1088 resources[typ].append(sec_lab)
1089 else:
1090 # !!! This should really get the label of the domain
1091 # or at least a resource label that has the same STE type
1092 # as the domain has
1093 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
1094 resources[typ].append("%s:%s:%s" %
1095 (xsconstants.ACM_POLICY_ID,
1096 active_policy,
1097 ACM_LABEL_UNLABELED))
1099 return resources
1102 def resources_compatible_with_vmlabel(xspol, dominfo, vmlabel):
1103 """
1104 Check whether the resources' labels are compatible with the
1105 given VM label. This is a function to be used when for example
1106 a running domain is to get the new label 'vmlabel'
1107 """
1108 if not xspol:
1109 return False
1111 try:
1112 resfile_lock()
1113 try:
1114 access_control = dictio.dict_read("resources",
1115 res_label_filename)
1116 except:
1117 # No labeled resources -> must be compatible
1118 return True
1119 return __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
1120 access_control)
1121 finally:
1122 resfile_unlock()
1123 return False
1126 def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
1127 access_control,
1128 is_policy_update=False):
1129 """
1130 Check whether the resources' labels are compatible with the
1131 given VM label. The access_control parameter provides a
1132 dictionary of the resource name to resource label mappings
1133 under which the evaluation should be done.
1134 Call this only for a paused or running domain.
1135 """
1136 def collect_labels(reslabels, s_label, polname):
1137 if len(s_label) != 3 or polname != s_label[1]:
1138 return False
1139 label = s_label[2]
1140 if not label in reslabels:
1141 reslabels.append(label)
1142 return True
1144 resources = get_domain_resources(dominfo)
1145 reslabels = [] # all resource labels
1147 polname = xspol.get_name()
1148 for key, value in resources.items():
1149 if key in [ 'vbd', 'tap' ]:
1150 for res in resources[key]:
1151 if not res in access_control:
1152 label = [xsconstants.ACM_POLICY_ID,
1153 xspol.get_name(),
1154 ACM_LABEL_UNLABELED]
1155 else:
1156 label = access_control[res]
1157 if not collect_labels(reslabels, label, polname):
1158 return False
1159 elif key in [ 'vif' ]:
1160 for xapi_label in value:
1161 label = xapi_label.split(":")
1162 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
1163 if not (is_policy_update and \
1164 label[2] == ACM_LABEL_UNLABELED):
1165 if not collect_labels(reslabels, label, polname):
1166 return False
1167 else:
1168 log.error("Unhandled device type: %s" % key)
1169 return False
1171 # Check that all resource labes have a common STE type with the
1172 # vmlabel
1173 if len(reslabels) > 0:
1174 rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels)
1175 else:
1176 rc = True
1177 log.info("vmlabel=%s, reslabels=%s, rc=%s" %
1178 (vmlabel, reslabels, str(rc)))
1179 return rc;
1181 def set_resource_label(resource, policytype, policyref, reslabel, \
1182 oreslabel = None):
1183 """
1184 Xend exports this function via XML-RPC.
1186 Assign a label to a resource
1187 If the old label (oreslabel) is given, then the resource must have
1188 that old label.
1189 A resource label may be changed if
1190 - the resource is not in use
1191 @param resource : The name of a resource, i.e., "phy:/dev/hda"
1192 @param policyref : The name of the policy
1193 @param reslabel : the resource label within the policy
1194 @param oreslabel : optional current resource label
1196 @rtype: int
1197 @return Success (0) or failure value (< 0)
1198 """
1200 if reslabel != "":
1201 ssidref = label2ssidref(reslabel, policyref, 'res')
1203 try:
1204 resource = unify_resname(resource, mustexist=False)
1205 except Exception:
1206 return -xsconstants.XSERR_BAD_RESOURCE_FORMAT
1208 domains = is_resource_in_use(resource)
1209 if len(domains) > 0:
1210 return -xsconstants.XSERR_RESOURCE_IN_USE
1212 try:
1213 resfile_lock()
1214 access_control = {}
1215 try:
1216 access_control = dictio.dict_read("resources", res_label_filename)
1217 except:
1218 pass
1219 if oreslabel:
1220 if not access_control.has_key(resource):
1221 return -xsconstants.XSERR_BAD_LABEL
1222 tmp = access_control[resource]
1223 if len(tmp) != 3:
1224 return -xsconstants.XSERR_BAD_LABEL
1225 if tmp[2] != oreslabel:
1226 return -xsconstants.XSERR_BAD_LABEL
1227 if resource.startswith('vlan:'):
1228 for key, value in access_control.items():
1229 if value == tuple([policytype, policyref, reslabel]) and \
1230 key.startswith('vlan:'):
1231 return -xsconstants.XSERR_BAD_LABEL
1232 if reslabel != "":
1233 new_entry = { resource : tuple([policytype, policyref, reslabel])}
1234 access_control.update(new_entry)
1235 command = "add"
1236 reslbl = ":".join([policytype, policyref, reslabel])
1237 else:
1238 if access_control.has_key(resource):
1239 del access_control[resource]
1240 command = "remove"
1241 reslbl = ""
1242 run_resource_label_change_script(resource, reslbl, command)
1243 dictio.dict_write(access_control, "resources", res_label_filename)
1244 finally:
1245 resfile_unlock()
1246 return xsconstants.XSERR_SUCCESS
1248 def rm_resource_label(resource, oldlabel_xapi):
1249 """Remove a resource label from a physical resource
1250 @param resource: The name of a resource, i.e., "phy:/dev/hda"
1252 @rtype: int
1253 @return Success (0) or failure value (< 0)
1254 """
1255 tmp = oldlabel_xapi.split(":")
1256 if len(tmp) != 3:
1257 return -xsconstants.XSERR_BAD_LABEL_FORMAT
1258 otyp, opolicyref, olabel = tmp
1259 # Only ACM is supported
1260 if otyp != xsconstants.ACM_POLICY_ID and \
1261 otyp != xsconstants.INVALID_POLICY_PREFIX + xsconstants.ACM_POLICY_ID:
1262 return -xsconstants.XSERR_WRONG_POLICY_TYPE
1263 return set_resource_label(resource, "", "", "", olabel)
1265 def get_resource_label_xapi(resource):
1266 """Get the assigned resource label of a physical resource
1267 in the format used by then Xen-API, i.e., "ACM:xm-test:blue"
1269 @rtype: string
1270 @return the string representing policy type, policy name and label of
1271 the resource
1272 """
1273 res = get_resource_label(resource)
1274 return format_resource_label(res)
1276 def format_resource_label(res):
1277 if res:
1278 if len(res) == 2:
1279 return xsconstants.ACM_POLICY_ID + ":" + res[0] + ":" + res[1]
1280 if len(res) == 3:
1281 return ":".join(res)
1282 return ""
1284 def get_resource_label(resource):
1285 """
1286 Xend exports this function via XML-RPC.
1288 Get the assigned resource label of a given resource
1289 @param resource: The name of a resource, i.e., "phy:/dev/hda"
1291 @rtype: list
1292 @return tuple of (policy name, resource label), i.e., (xm-test, blue)
1293 """
1294 try:
1295 resource = unify_resname(resource, mustexist=False)
1296 except Exception:
1297 return []
1299 reslabel_map = get_labeled_resources()
1301 if reslabel_map.has_key(resource):
1302 return list(reslabel_map[resource])
1303 else:
1304 #Try to resolve each label entry
1305 for key, value in reslabel_map.items():
1306 try:
1307 if resource == unify_resname(key):
1308 return list(value)
1309 except:
1310 pass
1312 return []
1315 def get_labeled_resources_xapi():
1316 """ Get a map of all labeled resource with the labels formatted in the
1317 xen-api resource label format.
1318 """
1319 reslabel_map = get_labeled_resources()
1320 for key, labeldata in reslabel_map.items():
1321 reslabel_map[key] = format_resource_label(labeldata)
1322 return reslabel_map
1325 def get_labeled_resources():
1326 """
1327 Xend exports this function via XML-RPC
1329 Get a map of all labeled resources.
1330 @rtype: list
1331 @return list of labeled resources
1332 """
1333 try:
1334 resfile_lock()
1335 try:
1336 access_control = dictio.dict_read("resources", res_label_filename)
1337 except:
1338 return {}
1339 finally:
1340 resfile_unlock()
1341 return access_control
1344 def relabel_domains(relabel_list):
1345 """
1346 Relabel the given domains to have a new ssidref.
1347 @param relabel_list: a list containing tuples of domid, ssidref
1348 example: [ [0, 0x00020002] ]
1349 """
1350 rel_rules = ""
1351 for r in relabel_list:
1352 log.info("Relabeling domain with domid %d to new ssidref 0x%08x",
1353 r[0], r[1])
1354 rel_rules += struct.pack("ii", r[0], r[1])
1355 try:
1356 rc, errors = acm.relabel_domains(rel_rules)
1357 except Exception, e:
1358 log.info("Error after relabel_domains: %s" % str(e))
1359 rc = -xsconstants.XSERR_GENERAL_FAILURE
1360 errors = ""
1361 if (len(errors) > 0):
1362 rc = -xsconstants.XSERR_HV_OP_FAILED
1363 return rc, errors
1366 def change_acm_policy(bin_pol, del_array, chg_array,
1367 vmlabel_map, reslabel_map, cur_acmpol, new_acmpol,
1368 is_reset):
1369 """
1370 Change the ACM policy of the system by relabeling
1371 domains and resources first and doing some access checks.
1372 Then update the policy in the hypervisor. If this is all successful,
1373 relabel the domains permanently and commit the relabed resources.
1375 Need to do / check the following:
1376 - relabel all resources where there is a 'from' field in
1377 the policy. [ NOT DOING THIS: and mark those as unlabeled where the label
1378 does not appear in the new policy anymore (deletion) ]
1379 - relabel all VMs where there is a 'from' field in the
1380 policy and mark those as unlabeled where the label
1381 does not appear in the new policy anymore; no running
1382 or paused VM may be unlabeled through this
1383 - check that under the new labeling conditions the VMs
1384 still have access to their resources as before. Unlabeled
1385 resources are inaccessible. If this check fails, the
1386 update failed.
1387 - Attempt changes in the hypervisor; if this step fails,
1388 roll back the relabeling of resources and VMs
1389 - Make the relabeling of resources and VMs permanent
1391 This function should be called with the lock to the domains
1392 held (XendDomain.instance().domains_lock)
1393 """
1394 from xen.util.acmpolicy import ACM_LABEL_UNLABELED
1395 rc = xsconstants.XSERR_SUCCESS
1397 domain_label_map = {}
1398 new_policyname = new_acmpol.get_name()
1399 new_policytype = new_acmpol.get_type_name()
1400 cur_policyname = cur_acmpol.get_name()
1401 cur_policytype = cur_acmpol.get_type_name()
1402 polnew_reslabels = new_acmpol.policy_get_resourcelabel_names()
1403 errors=""
1405 try:
1406 resfile_lock()
1407 mapfile_lock()
1409 # Get all domains' dominfo.
1410 from xen.xend import XendDomain
1411 dominfos = XendDomain.instance().list('all')
1413 log.info("----------------------------------------------")
1415 label_changes = []
1416 # relabel resources
1418 access_control = {}
1419 try:
1420 access_control = dictio.dict_read("resources", res_label_filename)
1421 except:
1422 pass
1424 for key, labeldata in access_control.items():
1425 if len(labeldata) == 2:
1426 policy, label = labeldata
1427 policytype = xsconstants.ACM_POLICY_ID
1428 elif len(labeldata) == 3:
1429 policytype, policy, label = labeldata
1430 else:
1431 return -xsconstants.XSERR_BAD_LABEL_FORMAT, ""
1433 if policytype != cur_policytype or \
1434 policy != cur_policyname:
1435 continue
1437 # label been renamed or deleted?
1438 if policytype != xsconstants.ACM_POLICY_ID:
1439 continue
1440 elif reslabel_map.has_key(label) and cur_policyname == policy:
1441 # renaming of an active label; policy may have been renamed
1442 label = reslabel_map[label]
1443 polname = new_policyname
1444 elif label not in polnew_reslabels:
1445 # label been removed
1446 policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
1447 label_changes.append(key)
1448 polname = policy
1449 else:
1450 # no change to label
1451 policytype = xsconstants.ACM_POLICY_ID
1452 polname = new_policyname
1454 # Update entry
1455 access_control[key] = \
1456 tuple([ policytype, polname, label ])
1458 # All resources have new labels in the access_control map
1459 # There may still be labels in there that are invalid now.
1461 # Do this in memory without writing to disk:
1462 # - Relabel all domains independent of whether they are running
1463 # or not
1464 # - later write back to config files
1465 polnew_vmlabels = new_acmpol.policy_get_virtualmachinelabel_names()
1467 for dominfo in dominfos:
1468 sec_lab = dominfo.get_security_label()
1469 if not sec_lab:
1470 continue
1471 policytype, policy, vmlabel = sec_lab.split(":")
1472 name = dominfo.getName()
1474 if policytype != cur_policytype or \
1475 policy != cur_policyname:
1476 continue
1478 new_vmlabel = vmlabel
1479 if vmlabel_map.has_key(vmlabel) and \
1480 (not is_reset or name == "Domain-0") :
1481 # renaming of the label; this is only allowed if it's
1482 # not a reset of the policy or if it is a reset, then
1483 # only for Domain-0
1484 new_vmlabel = vmlabel_map[vmlabel]
1485 polname = new_policyname
1486 elif new_vmlabel not in polnew_vmlabels and \
1487 vmlabel != ACM_LABEL_UNLABELED:
1488 # removal of VM label and not the 'unlabeled' label
1489 policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
1490 polname = policy
1491 else:
1492 polname = new_policyname
1494 new_seclab = "%s:%s:%s" % \
1495 (policytype, polname, new_vmlabel)
1497 domain_label_map[dominfo] = [ sec_lab, new_seclab ]
1499 if dominfo._stateGet() in (DOM_STATE_PAUSED, DOM_STATE_RUNNING):
1500 compatible = __resources_compatible_with_vmlabel(new_acmpol,
1501 dominfo,
1502 new_vmlabel,
1503 access_control,
1504 is_policy_update=True)
1505 log.info("Domain %s with new label '%s' can access its "
1506 "resources? : %s" %
1507 (name, new_vmlabel, str(compatible)))
1508 log.info("VM labels in new policy: %s" %
1509 new_acmpol.policy_get_virtualmachinelabel_names())
1510 if not compatible:
1511 return (-xsconstants.XSERR_RESOURCE_ACCESS, "")
1513 rc, errors = hv_chg_policy(bin_pol, del_array, chg_array)
1514 if rc == 0:
1515 for key in label_changes:
1516 run_resource_label_change_script(key, "", "remove")
1517 # Write the relabeled resources back into the file
1518 dictio.dict_write(access_control, "resources", res_label_filename)
1519 # Properly update all VMs to their new labels
1520 for dominfo, labels in domain_label_map.items():
1521 sec_lab, new_seclab = labels
1522 if sec_lab != new_seclab:
1523 log.info("Updating domain %s to new label '%s'." % \
1524 (dominfo.getName(), new_seclab))
1525 # This better be working!
1526 res = dominfo.set_security_label(new_seclab,
1527 sec_lab,
1528 new_acmpol,
1529 cur_acmpol)
1530 if res[0] != xsconstants.XSERR_SUCCESS:
1531 log.info("ERROR: Could not chg label on domain %s: %s" %
1532 (dominfo.getName(),
1533 xsconstants.xserr2string(-int(res[0]))))
1534 finally:
1535 log.info("----------------------------------------------")
1536 mapfile_unlock()
1537 resfile_unlock()
1539 return rc, errors
1541 def parse_security_label(security_label):
1542 tmp = security_label.split(":")
1543 if len(tmp) != 3:
1544 return ""
1545 else:
1546 return security_label
1548 def set_security_label(policy, label):
1549 if label and policy and label != "" and policy != "":
1550 return "%s:%s:%s" % (xsconstants.ACM_POLICY_ID, policy, label)
1551 else:
1552 return ""
1554 def ssidref2security_label(ssidref):
1555 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
1556 return XSPolicyAdminInstance().ssidref_to_vmlabel(ssidref)
1558 def get_security_label(self, xspol=None):
1559 """
1560 Get the security label of a domain
1561 @param xspol The policy to use when converting the ssid into
1562 a label; only to be passed during the updating
1563 of the policy
1564 """
1565 domid = self.getDomid()
1567 if not xspol:
1568 from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
1569 xspol = XSPolicyAdminInstance().get_loaded_policy()
1571 label = ""
1572 if xspol:
1573 label = xspol.policy_get_domain_label_formatted(domid)
1574 if domid != 0:
1575 label = self.info.get('security_label', label)
1576 return label
1579 def check_can_run(sec_label):
1580 """ Check whether a VM could run, given its vm label. A VM can run if
1581 - it is authorized
1582 - is not in conflict with any running domain
1583 """
1584 try:
1585 mapfile_lock()
1587 if sec_label == None or sec_label == "":
1588 vm_label = ACM_LABEL_UNLABELED
1589 else:
1590 poltype, policy, vm_label = sec_label.split(':')
1591 if policy != get_active_policy_name():
1592 return -xsconstants.XSERR_BAD_POLICY_NAME
1593 ssidref = label2ssidref(vm_label, policy, 'dom')
1594 if ssidref != xsconstants.INVALID_SSIDREF:
1595 if not has_authorization(ssidref):
1596 return -xsconstants.XSERR_VM_NOT_AUTHORIZED
1597 if is_in_conflict(ssidref):
1598 return -xsconstants.XSERR_VM_IN_CONFLICT
1599 return -xsconstants.XSERR_SUCCESS
1600 else:
1601 return -xsconstants.XSERR_BAD_LABEL
1602 finally:
1603 mapfile_unlock()
1606 __cond = threading.Condition()
1607 __script_runner = None
1608 __orders = []
1610 def run_resource_label_change_script(resource, label, command):
1611 global __cond, __orders, __script_runner
1613 def __run_resource_label_change_script():
1614 global __cond, __orders
1615 script = XendOptions.instance().get_resource_label_change_script()
1616 if script:
1617 parms = {}
1618 while True:
1619 __cond.acquire()
1620 if len(__orders) == 0:
1621 __cond.wait()
1623 parms['label'], \
1624 parms['command'], \
1625 parms['resource'] = __orders[0]
1627 __orders = __orders[1:]
1628 __cond.release()
1630 log.info("Running resource label change script %s: %s" %
1631 (script, parms))
1632 parms.update(os.environ)
1633 os.spawnve(os.P_WAIT, script[0], script, parms)
1634 else:
1635 log.info("No script given for relabeling of resources.")
1636 if not __script_runner:
1637 __script_runner = \
1638 threading.Thread(target=__run_resource_label_change_script,
1639 args=())
1640 __script_runner.start()
1642 __cond.acquire()
1643 __orders.append((label,command,resource))
1644 __cond.notify()
1645 __cond.release()