ia64/xen-unstable

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

Implement legacy XML-RPC interface for ACM commands.

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

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