ia64/xen-unstable

view tools/python/xen/util/xsm/acm/acm.py @ 16521:5255eac35270

Implement legacy XML-RPC interface for ACM commands.

This patch implements a (non Xen-API) legacy XML-RPC interface for the
ACM commands and funnels the calls into code introduced by the Xen-API
support for ACM security management. Since some of the functionality
has changed, also the xm applications have changed. In particular the
following old commands have been removed along with some tools the
have become obsolete now:

- loadpolicy (included in: setpolicy)
- makepolicy (included in: setpolicy)
- cfgbootpolicy (included in: setpolicy)

and the following commands been introduced:

- setpolicy
- getpolicy
- resetpolicy

All tools have been adapted to work in Xen-API and legacy XML-RPC
mode. Both modes support the same functionality.

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