ia64/xen-unstable

view tools/python/xen/util/xsm/acm/acm.py @ 16584:433f6a6a862a

xend, acm: Extend Xen-API with function to reset the policy
This patch extends the Xen-API and the legacy XML-RPC interface with a
function to reset the policy on the system (through an update with the
default policy). I adapted the code in 'xm resetpolicy' to use this
now.

This patch also extends libxen and the documentation to reflect the
new function.

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