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 | #============================================================================ |
---|
19 | |
---|
20 | import commands |
---|
21 | import logging |
---|
22 | import sys, os, string, re |
---|
23 | import traceback |
---|
24 | import shutil |
---|
25 | from xen.lowlevel import acm |
---|
26 | from xen.xend import sxp |
---|
27 | from xen.xend.XendLogging import log |
---|
28 | from xen.util import dictio |
---|
29 | |
---|
30 | #global directories and tools for security management |
---|
31 | policy_dir_prefix = "/etc/xen/acm-security/policies" |
---|
32 | res_label_filename = policy_dir_prefix + "/resource_labels" |
---|
33 | boot_filename = "/boot/grub/menu.lst" |
---|
34 | altboot_filename = "/boot/grub/grub.conf" |
---|
35 | xensec_xml2bin = "/usr/sbin/xensec_xml2bin" |
---|
36 | xensec_tool = "/usr/sbin/xensec_tool" |
---|
37 | |
---|
38 | #global patterns for map file |
---|
39 | #police_reference_tagname = "POLICYREFERENCENAME" |
---|
40 | primary_entry_re = re.compile("\s*PRIMARY\s+.*", re.IGNORECASE) |
---|
41 | secondary_entry_re = re.compile("\s*SECONDARY\s+.*", re.IGNORECASE) |
---|
42 | label_template_re = re.compile(".*security_label_template.xml", re.IGNORECASE) |
---|
43 | mapping_filename_re = re.compile(".*\.map", re.IGNORECASE) |
---|
44 | policy_reference_entry_re = re.compile("\s*POLICYREFERENCENAME\s+.*", re.IGNORECASE) |
---|
45 | vm_label_re = re.compile("\s*LABEL->SSID\s+VM\s+.*", re.IGNORECASE) |
---|
46 | res_label_re = re.compile("\s*LABEL->SSID\s+RES\s+.*", re.IGNORECASE) |
---|
47 | all_label_re = re.compile("\s*LABEL->SSID\s+.*", re.IGNORECASE) |
---|
48 | access_control_re = re.compile("\s*access_control\s*=", re.IGNORECASE) |
---|
49 | |
---|
50 | #global patterns for boot configuration file |
---|
51 | xen_title_re = re.compile("\s*title\s+XEN", re.IGNORECASE) |
---|
52 | any_title_re = re.compile("\s*title\s", re.IGNORECASE) |
---|
53 | xen_kernel_re = re.compile("\s*kernel.*xen.*\.gz", re.IGNORECASE) |
---|
54 | kernel_ver_re = re.compile("\s*module.*vmlinuz", re.IGNORECASE) |
---|
55 | any_module_re = re.compile("\s*module\s", re.IGNORECASE) |
---|
56 | empty_line_re = re.compile("^\s*$") |
---|
57 | binary_name_re = re.compile(".*[chwall|ste|chwall_ste].*\.bin", re.IGNORECASE) |
---|
58 | policy_name_re = re.compile(".*[chwall|ste|chwall_ste].*", re.IGNORECASE) |
---|
59 | |
---|
60 | #other global variables |
---|
61 | NULL_SSIDREF = 0 |
---|
62 | |
---|
63 | log = logging.getLogger("xend.util.security") |
---|
64 | |
---|
65 | # Our own exception definition. It is masked (pass) if raised and |
---|
66 | # whoever raises this exception must provide error information. |
---|
67 | class ACMError(Exception): |
---|
68 | def __init__(self,value): |
---|
69 | self.value = value |
---|
70 | def __str__(self): |
---|
71 | return repr(self.value) |
---|
72 | |
---|
73 | |
---|
74 | |
---|
75 | def err(msg): |
---|
76 | """Raise ACM exception. |
---|
77 | """ |
---|
78 | sys.stderr.write("ACMError: " + msg + "\n") |
---|
79 | raise ACMError(msg) |
---|
80 | |
---|
81 | |
---|
82 | |
---|
83 | active_policy = None |
---|
84 | |
---|
85 | |
---|
86 | def refresh_security_policy(): |
---|
87 | """ |
---|
88 | retrieves security policy |
---|
89 | """ |
---|
90 | global active_policy |
---|
91 | |
---|
92 | try: |
---|
93 | active_policy = acm.policy() |
---|
94 | except: |
---|
95 | active_policy = "INACTIVE" |
---|
96 | |
---|
97 | # now set active_policy |
---|
98 | refresh_security_policy() |
---|
99 | |
---|
100 | def on(): |
---|
101 | """ |
---|
102 | returns none if security policy is off (not compiled), |
---|
103 | any string otherwise, use it: if not security.on() ... |
---|
104 | """ |
---|
105 | refresh_security_policy() |
---|
106 | return (active_policy not in ['INACTIVE', 'NULL']) |
---|
107 | |
---|
108 | |
---|
109 | |
---|
110 | # Assumes a 'security' info [security access_control ...] [ssidref ...] |
---|
111 | def get_security_info(info, field): |
---|
112 | """retrieves security field from self.info['security']) |
---|
113 | allowed search fields: ssidref, label, policy |
---|
114 | """ |
---|
115 | if isinstance(info, dict): |
---|
116 | security = info['security'] |
---|
117 | elif isinstance(info, list): |
---|
118 | security = sxp.child_value(info, 'security') |
---|
119 | if not security: |
---|
120 | if field == 'ssidref': |
---|
121 | #return default ssid |
---|
122 | return 0 |
---|
123 | else: |
---|
124 | err("Security information not found in info struct.") |
---|
125 | |
---|
126 | if field == 'ssidref': |
---|
127 | search = 'ssidref' |
---|
128 | elif field in ['policy', 'label']: |
---|
129 | search = 'access_control' |
---|
130 | else: |
---|
131 | err("Illegal field in get_security_info.") |
---|
132 | |
---|
133 | for idx in range(0, len(security)): |
---|
134 | if search != security[idx][0]: |
---|
135 | continue |
---|
136 | if search == 'ssidref': |
---|
137 | return int(security[idx][1]) |
---|
138 | else: |
---|
139 | for aidx in range(0, len(security[idx])): |
---|
140 | if security[idx][aidx][0] == field: |
---|
141 | return str(security[idx][aidx][1]) |
---|
142 | |
---|
143 | if search == 'ssidref': |
---|
144 | return 0 |
---|
145 | else: |
---|
146 | return None |
---|
147 | |
---|
148 | |
---|
149 | |
---|
150 | def get_security_printlabel(info): |
---|
151 | """retrieves printable security label from self.info['security']), |
---|
152 | preferably the label name and otherwise (if label is not specified |
---|
153 | in config and cannot be found in mapping file) a hex string of the |
---|
154 | ssidref or none if both not available |
---|
155 | """ |
---|
156 | try: |
---|
157 | if not on(): |
---|
158 | return "INACTIVE" |
---|
159 | if active_policy in ["DEFAULT"]: |
---|
160 | return "DEFAULT" |
---|
161 | |
---|
162 | printlabel = get_security_info(info, 'label') |
---|
163 | if printlabel: |
---|
164 | return printlabel |
---|
165 | ssidref = get_security_info(info, 'ssidref') |
---|
166 | if not ssidref: |
---|
167 | return None |
---|
168 | #try to translate ssidref to a label |
---|
169 | result = ssidref2label(ssidref) |
---|
170 | if not result: |
---|
171 | printlabel = "0x%08x" % ssidref |
---|
172 | else: |
---|
173 | printlabel = result |
---|
174 | return printlabel |
---|
175 | except ACMError: |
---|
176 | #don't throw an exception in xm list |
---|
177 | return "ERROR" |
---|
178 | |
---|
179 | |
---|
180 | |
---|
181 | def getmapfile(policyname): |
---|
182 | """ |
---|
183 | in: if policyname is None then the currently |
---|
184 | active hypervisor policy is used |
---|
185 | out: 1. primary policy, 2. secondary policy, |
---|
186 | 3. open file descriptor for mapping file, and |
---|
187 | 4. True if policy file is available, False otherwise |
---|
188 | """ |
---|
189 | if not policyname: |
---|
190 | policyname = active_policy |
---|
191 | map_file_ok = False |
---|
192 | primary = None |
---|
193 | secondary = None |
---|
194 | #strip last part of policy as file name part |
---|
195 | policy_dir_list = string.split(policyname, ".") |
---|
196 | policy_file = policy_dir_list.pop() |
---|
197 | if len(policy_dir_list) > 0: |
---|
198 | policy_dir = string.join(policy_dir_list, "/") + "/" |
---|
199 | else: |
---|
200 | policy_dir = "" |
---|
201 | |
---|
202 | map_filename = policy_dir_prefix + "/" + policy_dir + policy_file + ".map" |
---|
203 | # check if it is there, if not check if policy file is there |
---|
204 | if not os.path.isfile(map_filename): |
---|
205 | policy_filename = policy_dir_prefix + "/" + policy_dir + policy_file + "-security_policy.xml" |
---|
206 | if not os.path.isfile(policy_filename): |
---|
207 | err("Policy file \'" + policy_filename + "\' not found.") |
---|
208 | else: |
---|
209 | err("Mapping file \'" + map_filename + "\' not found." + |
---|
210 | " Use xm makepolicy to create it.") |
---|
211 | |
---|
212 | f = open(map_filename) |
---|
213 | for line in f: |
---|
214 | if policy_reference_entry_re.match(line): |
---|
215 | l = line.split() |
---|
216 | if (len(l) == 2) and (l[1] == policyname): |
---|
217 | map_file_ok = True |
---|
218 | elif primary_entry_re.match(line): |
---|
219 | l = line.split() |
---|
220 | if len(l) == 2: |
---|
221 | primary = l[1] |
---|
222 | elif secondary_entry_re.match(line): |
---|
223 | l = line.split() |
---|
224 | if len(l) == 2: |
---|
225 | secondary = l[1] |
---|
226 | f.close() |
---|
227 | f = open(map_filename) |
---|
228 | if map_file_ok and primary and secondary: |
---|
229 | return (primary, secondary, f, True) |
---|
230 | else: |
---|
231 | err("Mapping file inconsistencies found. Try makepolicy to create a new one.") |
---|
232 | |
---|
233 | |
---|
234 | |
---|
235 | def ssidref2label(ssidref_var): |
---|
236 | """ |
---|
237 | returns labelname corresponding to ssidref; |
---|
238 | maps current policy to default directory |
---|
239 | to find mapping file |
---|
240 | """ |
---|
241 | #1. translated permitted input formats |
---|
242 | if isinstance(ssidref_var, str): |
---|
243 | ssidref_var.strip() |
---|
244 | if ssidref_var[0:2] == "0x": |
---|
245 | ssidref = int(ssidref_var[2:], 16) |
---|
246 | else: |
---|
247 | ssidref = int(ssidref_var) |
---|
248 | elif isinstance(ssidref_var, int): |
---|
249 | ssidref = ssidref_var |
---|
250 | else: |
---|
251 | err("Instance type of ssidref not supported (must be of type 'str' or 'int')") |
---|
252 | |
---|
253 | (primary, secondary, f, pol_exists) = getmapfile(None) |
---|
254 | if not f: |
---|
255 | if (pol_exists): |
---|
256 | err("Mapping file for policy \'" + policyname + "\' not found.\n" + |
---|
257 | "Please use makepolicy command to create mapping file!") |
---|
258 | else: |
---|
259 | err("Policy file for \'" + active_policy + "\' not found.") |
---|
260 | |
---|
261 | #2. get labelnames for both ssidref parts |
---|
262 | pri_ssid = ssidref & 0xffff |
---|
263 | sec_ssid = ssidref >> 16 |
---|
264 | pri_null_ssid = NULL_SSIDREF & 0xffff |
---|
265 | sec_null_ssid = NULL_SSIDREF >> 16 |
---|
266 | pri_labels = [] |
---|
267 | sec_labels = [] |
---|
268 | labels = [] |
---|
269 | |
---|
270 | for line in f: |
---|
271 | l = line.split() |
---|
272 | if (len(l) < 5) or (l[0] != "LABEL->SSID"): |
---|
273 | continue |
---|
274 | if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid): |
---|
275 | pri_labels.append(l[3]) |
---|
276 | if secondary and (l[2] == secondary) and (int(l[4], 16) == sec_ssid): |
---|
277 | sec_labels.append(l[3]) |
---|
278 | f.close() |
---|
279 | |
---|
280 | #3. get the label that is in both lists (combination must be a single label) |
---|
281 | if (primary == "CHWALL") and (pri_ssid == pri_null_ssid) and (sec_ssid != sec_null_ssid): |
---|
282 | labels = sec_labels |
---|
283 | elif (secondary == "CHWALL") and (pri_ssid != pri_null_ssid) and (sec_ssid == sec_null_ssid): |
---|
284 | labels = pri_labels |
---|
285 | elif secondary == "NULL": |
---|
286 | labels = pri_labels |
---|
287 | else: |
---|
288 | for i in pri_labels: |
---|
289 | for j in sec_labels: |
---|
290 | if (i==j): |
---|
291 | labels.append(i) |
---|
292 | if len(labels) != 1: |
---|
293 | err("Label for ssidref \'" + str(ssidref) + |
---|
294 | "\' unknown or not unique in policy \'" + active_policy + "\'") |
---|
295 | |
---|
296 | return labels[0] |
---|
297 | |
---|
298 | |
---|
299 | |
---|
300 | def label2ssidref(labelname, policyname, type): |
---|
301 | """ |
---|
302 | returns ssidref corresponding to labelname; |
---|
303 | maps current policy to default directory |
---|
304 | to find mapping file """ |
---|
305 | |
---|
306 | if policyname in ['NULL', 'INACTIVE', 'DEFAULT']: |
---|
307 | err("Cannot translate labels for \'" + policyname + "\' policy.") |
---|
308 | |
---|
309 | allowed_types = ['ANY'] |
---|
310 | if type == 'dom': |
---|
311 | allowed_types.append('VM') |
---|
312 | elif type == 'res': |
---|
313 | allowed_types.append('RES') |
---|
314 | else: |
---|
315 | err("Invalid type. Must specify 'dom' or 'res'.") |
---|
316 | |
---|
317 | (primary, secondary, f, pol_exists) = getmapfile(policyname) |
---|
318 | |
---|
319 | #2. get labelnames for ssidref parts and find a common label |
---|
320 | pri_ssid = [] |
---|
321 | sec_ssid = [] |
---|
322 | for line in f: |
---|
323 | l = line.split() |
---|
324 | if (len(l) < 5) or (l[0] != "LABEL->SSID"): |
---|
325 | continue |
---|
326 | if primary and (l[1] in allowed_types) and (l[2] == primary) and (l[3] == labelname): |
---|
327 | pri_ssid.append(int(l[4], 16)) |
---|
328 | if secondary and (l[1] in allowed_types) and (l[2] == secondary) and (l[3] == labelname): |
---|
329 | sec_ssid.append(int(l[4], 16)) |
---|
330 | f.close() |
---|
331 | if (type == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0): |
---|
332 | pri_ssid.append(NULL_SSIDREF) |
---|
333 | elif (type == 'res') and (secondary == "CHWALL") and (len(sec_ssid) == 0): |
---|
334 | sec_ssid.append(NULL_SSIDREF) |
---|
335 | |
---|
336 | #3. sanity check and composition of ssidref |
---|
337 | if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and (secondary != "NULL")): |
---|
338 | err("Label \'" + labelname + "\' not found.") |
---|
339 | elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1): |
---|
340 | err("Label \'" + labelname + "\' not unique in policy (policy error)") |
---|
341 | if secondary == "NULL": |
---|
342 | return pri_ssid[0] |
---|
343 | else: |
---|
344 | return (sec_ssid[0] << 16) | pri_ssid[0] |
---|
345 | |
---|
346 | |
---|
347 | |
---|
348 | def refresh_ssidref(config): |
---|
349 | """ |
---|
350 | looks up ssidref from security field |
---|
351 | and refreshes the value if label exists |
---|
352 | """ |
---|
353 | #called by dom0, policy could have changed after xen.utils.security was initialized |
---|
354 | refresh_security_policy() |
---|
355 | |
---|
356 | security = None |
---|
357 | if isinstance(config, dict): |
---|
358 | security = config['security'] |
---|
359 | elif isinstance(config, list): |
---|
360 | security = sxp.child_value(config, 'security') |
---|
361 | else: |
---|
362 | err("Instance type of config parameter not supported.") |
---|
363 | if not security: |
---|
364 | #nothing to do (no security label attached) |
---|
365 | return config |
---|
366 | |
---|
367 | policyname = None |
---|
368 | labelname = None |
---|
369 | # compose new security field |
---|
370 | for idx in range(0, len(security)): |
---|
371 | if security[idx][0] == 'ssidref': |
---|
372 | security.pop(idx) |
---|
373 | break |
---|
374 | elif security[idx][0] == 'access_control': |
---|
375 | for jdx in [1, 2]: |
---|
376 | if security[idx][jdx][0] == 'label': |
---|
377 | labelname = security[idx][jdx][1] |
---|
378 | elif security[idx][jdx][0] == 'policy': |
---|
379 | policyname = security[idx][jdx][1] |
---|
380 | else: |
---|
381 | err("Illegal field in access_control") |
---|
382 | #verify policy is correct |
---|
383 | if active_policy != policyname: |
---|
384 | err("Policy \'" + policyname + "\' in label does not match active policy \'" |
---|
385 | + active_policy +"\'!") |
---|
386 | |
---|
387 | new_ssidref = label2ssidref(labelname, policyname, 'dom') |
---|
388 | if not new_ssidref: |
---|
389 | err("SSIDREF refresh failed!") |
---|
390 | |
---|
391 | security.append([ 'ssidref',str(new_ssidref)]) |
---|
392 | security = ['security', security ] |
---|
393 | |
---|
394 | for idx in range(0,len(config)): |
---|
395 | if config[idx][0] == 'security': |
---|
396 | config.pop(idx) |
---|
397 | break |
---|
398 | config.append(security) |
---|
399 | |
---|
400 | |
---|
401 | |
---|
402 | def get_ssid(domain): |
---|
403 | """ |
---|
404 | enables domains to retrieve the label / ssidref of a running domain |
---|
405 | """ |
---|
406 | if not on(): |
---|
407 | err("No policy active.") |
---|
408 | |
---|
409 | if isinstance(domain, str): |
---|
410 | domain_int = int(domain) |
---|
411 | elif isinstance(domain, int): |
---|
412 | domain_int = domain |
---|
413 | else: |
---|
414 | err("Illegal parameter type.") |
---|
415 | try: |
---|
416 | ssid_info = acm.getssid(int(domain_int)) |
---|
417 | except: |
---|
418 | err("Cannot determine security information.") |
---|
419 | |
---|
420 | if active_policy in ["DEFAULT"]: |
---|
421 | label = "DEFAULT" |
---|
422 | else: |
---|
423 | label = ssidref2label(ssid_info["ssidref"]) |
---|
424 | return(ssid_info["policyreference"], |
---|
425 | label, |
---|
426 | ssid_info["policytype"], |
---|
427 | ssid_info["ssidref"]) |
---|
428 | |
---|
429 | |
---|
430 | |
---|
431 | def get_decision(arg1, arg2): |
---|
432 | """ |
---|
433 | enables domains to retrieve access control decisions from |
---|
434 | the hypervisor Access Control Module. |
---|
435 | IN: args format = ['domid', id] or ['ssidref', ssidref] |
---|
436 | or ['access_control', ['policy', policy], ['label', label], ['type', type]] |
---|
437 | """ |
---|
438 | |
---|
439 | if not on(): |
---|
440 | err("No policy active.") |
---|
441 | |
---|
442 | #translate labels before calling low-level function |
---|
443 | if arg1[0] == 'access_control': |
---|
444 | if (arg1[1][0] != 'policy') or (arg1[2][0] != 'label') or (arg1[3][0] != 'type'): |
---|
445 | err("Argument type not supported.") |
---|
446 | ssidref = label2ssidref(arg1[2][1], arg1[1][1], arg1[3][1]) |
---|
447 | arg1 = ['ssidref', str(ssidref)] |
---|
448 | if arg2[0] == 'access_control': |
---|
449 | if (arg2[1][0] != 'policy') or (arg2[2][0] != 'label') or (arg2[3][0] != 'type'): |
---|
450 | err("Argument type not supported.") |
---|
451 | ssidref = label2ssidref(arg2[2][1], arg2[1][1], arg2[3][1]) |
---|
452 | arg2 = ['ssidref', str(ssidref)] |
---|
453 | |
---|
454 | # accept only int or string types for domid and ssidref |
---|
455 | if isinstance(arg1[1], int): |
---|
456 | arg1[1] = str(arg1[1]) |
---|
457 | if isinstance(arg2[1], int): |
---|
458 | arg2[1] = str(arg2[1]) |
---|
459 | if not isinstance(arg1[1], str) or not isinstance(arg2[1], str): |
---|
460 | err("Invalid id or ssidref type, string or int required") |
---|
461 | |
---|
462 | try: |
---|
463 | decision = acm.getdecision(arg1[0], arg1[1], arg2[0], arg2[1]) |
---|
464 | except: |
---|
465 | err("Cannot determine decision.") |
---|
466 | |
---|
467 | if decision: |
---|
468 | return decision |
---|
469 | else: |
---|
470 | err("Cannot determine decision (Invalid parameter).") |
---|
471 | |
---|
472 | |
---|
473 | |
---|
474 | def make_policy(policy_name): |
---|
475 | policy_file = string.join(string.split(policy_name, "."), "/") |
---|
476 | if not os.path.isfile(policy_dir_prefix + "/" + policy_file + "-security_policy.xml"): |
---|
477 | err("Unknown policy \'" + policy_name + "\'") |
---|
478 | |
---|
479 | (ret, output) = commands.getstatusoutput(xensec_xml2bin + " -d " + policy_dir_prefix + " " + policy_file) |
---|
480 | if ret: |
---|
481 | err("Creating policy failed:\n" + output) |
---|
482 | |
---|
483 | |
---|
484 | |
---|
485 | def load_policy(policy_name): |
---|
486 | global active_policy |
---|
487 | policy_file = policy_dir_prefix + "/" + string.join(string.split(policy_name, "."), "/") |
---|
488 | if not os.path.isfile(policy_file + ".bin"): |
---|
489 | if os.path.isfile(policy_file + "-security_policy.xml"): |
---|
490 | err("Binary file does not exist." + |
---|
491 | "Please use makepolicy to build the policy binary.") |
---|
492 | else: |
---|
493 | err("Unknown Policy " + policy_name) |
---|
494 | |
---|
495 | #require this policy to be the first or the same as installed |
---|
496 | if active_policy not in ['DEFAULT', policy_name]: |
---|
497 | err("Active policy \'" + active_policy + |
---|
498 | "\' incompatible with new policy \'" + policy_name + "\'") |
---|
499 | (ret, output) = commands.getstatusoutput(xensec_tool + " loadpolicy " + policy_file + ".bin") |
---|
500 | if ret: |
---|
501 | err("Loading policy failed:\n" + output) |
---|
502 | else: |
---|
503 | # refresh active policy |
---|
504 | refresh_security_policy() |
---|
505 | |
---|
506 | |
---|
507 | |
---|
508 | def dump_policy(): |
---|
509 | if active_policy in ['NULL', 'INACTIVE']: |
---|
510 | err("\'" + active_policy + "\' policy. Nothing to dump.") |
---|
511 | |
---|
512 | (ret, output) = commands.getstatusoutput(xensec_tool + " getpolicy") |
---|
513 | if ret: |
---|
514 | err("Dumping hypervisor policy failed:\n" + output) |
---|
515 | print output |
---|
516 | |
---|
517 | |
---|
518 | |
---|
519 | def list_labels(policy_name, condition): |
---|
520 | if (not policy_name) and (active_policy) in ["NULL", "INACTIVE", "DEFAULT"]: |
---|
521 | err("Current policy \'" + active_policy + "\' has no labels defined.\n") |
---|
522 | |
---|
523 | (primary, secondary, f, pol_exists) = getmapfile(policy_name) |
---|
524 | if not f: |
---|
525 | if pol_exists: |
---|
526 | err("Cannot find mapfile for policy \'" + policy_name + |
---|
527 | "\'.\nPlease use makepolicy to create mapping file.") |
---|
528 | else: |
---|
529 | err("Unknown policy \'" + policy_name + "\'") |
---|
530 | |
---|
531 | labels = [] |
---|
532 | for line in f: |
---|
533 | if condition.match(line): |
---|
534 | label = line.split()[3] |
---|
535 | if label not in labels: |
---|
536 | labels.append(label) |
---|
537 | return labels |
---|
538 | |
---|
539 | |
---|
540 | def get_res_label(resource): |
---|
541 | """Returns resource label information (label, policy) if it exists. |
---|
542 | Otherwise returns null label and policy. |
---|
543 | """ |
---|
544 | def default_res_label(): |
---|
545 | ssidref = NULL_SSIDREF |
---|
546 | if on(): |
---|
547 | label = ssidref2label(ssidref) |
---|
548 | else: |
---|
549 | label = None |
---|
550 | return (label, 'NULL') |
---|
551 | |
---|
552 | (label, policy) = default_res_label() |
---|
553 | |
---|
554 | # load the resource label file |
---|
555 | res_label_cache = {} |
---|
556 | try: |
---|
557 | res_label_cache = dictio.dict_read("resources", res_label_filename) |
---|
558 | except: |
---|
559 | log.info("Resource label file not found.") |
---|
560 | return default_res_label() |
---|
561 | |
---|
562 | # find the resource information |
---|
563 | if res_label_cache.has_key(resource): |
---|
564 | (policy, label) = res_label_cache[resource] |
---|
565 | |
---|
566 | return (label, policy) |
---|
567 | |
---|
568 | |
---|
569 | def get_res_security_details(resource): |
---|
570 | """Returns the (label, ssidref, policy) associated with a given |
---|
571 | resource from the global resource label file. |
---|
572 | """ |
---|
573 | def default_security_details(): |
---|
574 | ssidref = NULL_SSIDREF |
---|
575 | if on(): |
---|
576 | label = ssidref2label(ssidref) |
---|
577 | else: |
---|
578 | label = None |
---|
579 | policy = active_policy |
---|
580 | return (label, ssidref, policy) |
---|
581 | |
---|
582 | (label, ssidref, policy) = default_security_details() |
---|
583 | |
---|
584 | # find the entry associated with this resource |
---|
585 | (label, policy) = get_res_label(resource) |
---|
586 | if policy == 'NULL': |
---|
587 | log.info("Resource label for "+resource+" not in file, using DEFAULT.") |
---|
588 | return default_security_details() |
---|
589 | |
---|
590 | # is this resource label for the running policy? |
---|
591 | if policy == active_policy: |
---|
592 | ssidref = label2ssidref(label, policy, 'res') |
---|
593 | else: |
---|
594 | log.info("Resource label not for active policy, using DEFAULT.") |
---|
595 | return default_security_details() |
---|
596 | |
---|
597 | return (label, ssidref, policy) |
---|
598 | |
---|
599 | |
---|
600 | def unify_resname(resource): |
---|
601 | """Makes all resource locations absolute. In case of physical |
---|
602 | resources, '/dev/' is added to local file names""" |
---|
603 | |
---|
604 | if not resource: |
---|
605 | return resource |
---|
606 | |
---|
607 | # sanity check on resource name |
---|
608 | try: |
---|
609 | (type, resfile) = resource.split(":", 1) |
---|
610 | except: |
---|
611 | err("Resource spec '%s' contains no ':' delimiter" % resource) |
---|
612 | |
---|
613 | if type == "tap": |
---|
614 | try: |
---|
615 | (subtype, resfile) = resfile.split(":") |
---|
616 | except: |
---|
617 | err("Resource spec '%s' contains no tap subtype" % resource) |
---|
618 | |
---|
619 | if type in ["phy", "tap"]: |
---|
620 | if not resfile.startswith("/"): |
---|
621 | resfile = "/dev/" + resfile |
---|
622 | |
---|
623 | #file: resources must specified with absolute path |
---|
624 | if (not resfile.startswith("/")) or (not os.path.exists(resfile)): |
---|
625 | err("Invalid resource.") |
---|
626 | |
---|
627 | # from here on absolute file names with resources |
---|
628 | if type == "tap": |
---|
629 | type = type + ":" + subtype |
---|
630 | resource = type + ":" + resfile |
---|
631 | return resource |
---|
632 | |
---|
633 | |
---|
634 | def res_security_check(resource, domain_label): |
---|
635 | """Checks if the given resource can be used by the given domain |
---|
636 | label. Returns 1 if the resource can be used, otherwise 0. |
---|
637 | """ |
---|
638 | rtnval = 1 |
---|
639 | |
---|
640 | # if security is on, ask the hypervisor for a decision |
---|
641 | if on(): |
---|
642 | #build canonical resource name |
---|
643 | resource = unify_resname(resource) |
---|
644 | |
---|
645 | (label, ssidref, policy) = get_res_security_details(resource) |
---|
646 | domac = ['access_control'] |
---|
647 | domac.append(['policy', active_policy]) |
---|
648 | domac.append(['label', domain_label]) |
---|
649 | domac.append(['type', 'dom']) |
---|
650 | decision = get_decision(domac, ['ssidref', str(ssidref)]) |
---|
651 | |
---|
652 | # provide descriptive error messages |
---|
653 | if decision == 'DENIED': |
---|
654 | if label == ssidref2label(NULL_SSIDREF): |
---|
655 | raise ACMError("Resource '"+resource+"' is not labeled") |
---|
656 | rtnval = 0 |
---|
657 | else: |
---|
658 | raise ACMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed") |
---|
659 | rtnval = 0 |
---|
660 | |
---|
661 | # security is off, make sure resource isn't labeled |
---|
662 | else: |
---|
663 | # Note, we can't canonicalise the resource here, because people using |
---|
664 | # xm without ACM are free to use relative paths. |
---|
665 | (label, policy) = get_res_label(resource) |
---|
666 | if policy != 'NULL': |
---|
667 | raise ACMError("Security is off, but '"+resource+"' is labeled") |
---|
668 | rtnval = 0 |
---|
669 | |
---|
670 | return rtnval |
---|