1# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com> 2# 3# Copyright (C) 2006 Red Hat 4# see file 'COPYING' for use and warranty information 5# 6# This program is free software; you can redistribute it and/or 7# modify it under the terms of the GNU General Public License as 8# published by the Free Software Foundation; version 2 only 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, write to the Free Software 17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18# 19 20""" 21Classes for representing and manipulating interfaces. 22""" 23 24import copy 25import itertools 26 27from . import access 28from . import refpolicy 29from . import objectmodel 30from . import matching 31from .sepolgeni18n import _ 32 33 34class Param: 35 """ 36 Object representing a paramater for an interface. 37 """ 38 def __init__(self): 39 self.__name = "" 40 self.type = refpolicy.SRC_TYPE 41 self.obj_classes = refpolicy.IdSet() 42 self.required = True 43 44 def set_name(self, name): 45 if not access.is_idparam(name): 46 raise ValueError("Name [%s] is not a param" % name) 47 self.__name = name 48 49 def get_name(self): 50 return self.__name 51 52 name = property(get_name, set_name) 53 54 num = property(fget=lambda self: int(self.name[1:])) 55 56 def __repr__(self): 57 return "<sepolgen.policygen.Param instance [%s, %s, %s]>" % \ 58 (self.name, refpolicy.field_to_str[self.type], " ".join(self.obj_classes)) 59 60 61# Helper for extract perms 62def __param_insert(name, type, av, params): 63 ret = 0 64 if name in params: 65 p = params[name] 66 # The entries are identical - we're done 67 if type == p.type: 68 return 69 # Hanldle implicitly typed objects (like process) 70 if (type == refpolicy.SRC_TYPE or type == refpolicy.TGT_TYPE) and \ 71 (p.type == refpolicy.TGT_TYPE or p.type == refpolicy.SRC_TYPE): 72 #print name, refpolicy.field_to_str[p.type] 73 # If the object is not implicitly typed, tell the 74 # caller there is a likely conflict. 75 ret = 1 76 if av: 77 avobjs = [av.obj_class] 78 else: 79 avobjs = [] 80 for obj in itertools.chain(p.obj_classes, avobjs): 81 if obj in objectmodel.implicitly_typed_objects: 82 ret = 0 83 break 84 # "Promote" to a SRC_TYPE as this is the likely usage. 85 # We do this even if the above test fails on purpose 86 # as there is really no sane way to resolve the conflict 87 # here. The caller can take other actions if needed. 88 p.type = refpolicy.SRC_TYPE 89 else: 90 # There is some conflict - no way to resolve it really 91 # so we just leave the first entry and tell the caller 92 # there was a conflict. 93 ret = 1 94 else: 95 p = Param() 96 p.name = name 97 p.type = type 98 params[p.name] = p 99 100 if av: 101 p.obj_classes.add(av.obj_class) 102 return ret 103 104 105 106def av_extract_params(av, params): 107 """Extract the paramaters from an access vector. 108 109 Extract the paramaters (in the form $N) from an access 110 vector, storing them as Param objects in a dictionary. 111 Some attempt is made at resolving conflicts with other 112 entries in the dict, but if an unresolvable conflict is 113 found it is reported to the caller. 114 115 The goal here is to figure out how interface paramaters are 116 actually used in the interface - e.g., that $1 is a domain used as 117 a SRC_TYPE. In general an interface will look like this: 118 119 interface(`foo', ` 120 allow $1 foo : file read; 121 ') 122 123 This is simple to figure out - $1 is a SRC_TYPE. A few interfaces 124 are more complex, for example: 125 126 interface(`foo_trans',` 127 domain_auto_trans($1,fingerd_exec_t,fingerd_t) 128 129 allow $1 fingerd_t:fd use; 130 allow fingerd_t $1:fd use; 131 allow fingerd_t $1:fifo_file rw_file_perms; 132 allow fingerd_t $1:process sigchld; 133 ') 134 135 Here the usage seems ambigious, but it is not. $1 is still domain 136 and therefore should be returned as a SRC_TYPE. 137 138 Returns: 139 0 - success 140 1 - conflict found 141 """ 142 ret = 0 143 found_src = False 144 if access.is_idparam(av.src_type): 145 if __param_insert(av.src_type, refpolicy.SRC_TYPE, av, params) == 1: 146 ret = 1 147 148 if access.is_idparam(av.tgt_type): 149 if __param_insert(av.tgt_type, refpolicy.TGT_TYPE, av, params) == 1: 150 ret = 1 151 152 if access.is_idparam(av.obj_class): 153 if __param_insert(av.obj_class, refpolicy.OBJ_CLASS, av, params) == 1: 154 ret = 1 155 156 for perm in av.perms: 157 if access.is_idparam(perm): 158 if __param_insert(perm, PERM) == 1: 159 ret = 1 160 161 return ret 162 163def role_extract_params(role, params): 164 if access.is_idparam(role.role): 165 return __param_insert(role.role, refpolicy.ROLE, None, params) 166 167def type_rule_extract_params(rule, params): 168 def extract_from_set(set, type): 169 ret = 0 170 for x in set: 171 if access.is_idparam(x): 172 if __param_insert(x, type, None, params): 173 ret = 1 174 return ret 175 176 ret = 0 177 if extract_from_set(rule.src_types, refpolicy.SRC_TYPE): 178 ret = 1 179 180 if extract_from_set(rule.tgt_types, refpolicy.TGT_TYPE): 181 ret = 1 182 183 if extract_from_set(rule.obj_classes, refpolicy.OBJ_CLASS): 184 ret = 1 185 186 if access.is_idparam(rule.dest_type): 187 if __param_insert(rule.dest_type, refpolicy.DEST_TYPE, None, params): 188 ret = 1 189 190 return ret 191 192def ifcall_extract_params(ifcall, params): 193 ret = 0 194 for arg in ifcall.args: 195 if access.is_idparam(arg): 196 # Assume interface arguments are source types. Fairly safe 197 # assumption for most interfaces 198 if __param_insert(arg, refpolicy.SRC_TYPE, None, params): 199 ret = 1 200 201 return ret 202 203class AttributeVector: 204 def __init__(self): 205 self.name = "" 206 self.access = access.AccessVectorSet() 207 208 def add_av(self, av): 209 self.access.add_av(av) 210 211class AttributeSet: 212 def __init__(self): 213 self.attributes = { } 214 215 def add_attr(self, attr): 216 self.attributes[attr.name] = attr 217 218 def from_file(self, fd): 219 def parse_attr(line): 220 fields = line[1:-1].split() 221 if len(fields) != 2 or fields[0] != "Attribute": 222 raise SyntaxError("Syntax error Attribute statement %s" % line) 223 a = AttributeVector() 224 a.name = fields[1] 225 226 return a 227 228 a = None 229 for line in fd: 230 line = line[:-1] 231 if line[0] == "[": 232 if a: 233 self.add_attr(a) 234 a = parse_attr(line) 235 elif a: 236 l = line.split(",") 237 av = access.AccessVector(l) 238 a.add_av(av) 239 if a: 240 self.add_attr(a) 241 242class InterfaceVector: 243 def __init__(self, interface=None, attributes={}): 244 # Enabled is a loose concept currently - we are essentially 245 # not enabling interfaces that we can't handle currently. 246 # See InterfaceVector.add_ifv for more information. 247 self.enabled = True 248 self.name = "" 249 # The access that is enabled by this interface - eventually 250 # this will include indirect access from typeattribute 251 # statements. 252 self.access = access.AccessVectorSet() 253 # Paramaters are stored in a dictionary (key: param name 254 # value: Param object). 255 self.params = { } 256 if interface: 257 self.from_interface(interface, attributes) 258 self.expanded = False 259 260 def from_interface(self, interface, attributes={}): 261 self.name = interface.name 262 263 # Add allow rules 264 for avrule in interface.avrules(): 265 if avrule.rule_type != refpolicy.AVRule.ALLOW: 266 continue 267 # Handle some policy bugs 268 if "dontaudit" in interface.name: 269 #print "allow rule in interface: %s" % interface 270 continue 271 avs = access.avrule_to_access_vectors(avrule) 272 for av in avs: 273 self.add_av(av) 274 275 # Add typeattribute access 276 if attributes: 277 for typeattribute in interface.typeattributes(): 278 for attr in typeattribute.attributes: 279 if attr not in attributes.attributes: 280 # print "missing attribute " + attr 281 continue 282 attr_vec = attributes.attributes[attr] 283 for a in attr_vec.access: 284 av = copy.copy(a) 285 if av.src_type == attr_vec.name: 286 av.src_type = typeattribute.type 287 if av.tgt_type == attr_vec.name: 288 av.tgt_type = typeattribute.type 289 self.add_av(av) 290 291 292 # Extract paramaters from roles 293 for role in interface.roles(): 294 if role_extract_params(role, self.params): 295 pass 296 #print "found conflicting role param %s for interface %s" % \ 297 # (role.name, interface.name) 298 # Extract paramaters from type rules 299 for rule in interface.typerules(): 300 if type_rule_extract_params(rule, self.params): 301 pass 302 #print "found conflicting params in rule %s in interface %s" % \ 303 # (str(rule), interface.name) 304 305 for ifcall in interface.interface_calls(): 306 if ifcall_extract_params(ifcall, self.params): 307 pass 308 #print "found conflicting params in ifcall %s in interface %s" % \ 309 # (str(ifcall), interface.name) 310 311 312 def add_av(self, av): 313 if av_extract_params(av, self.params) == 1: 314 pass 315 #print "found conflicting perms [%s]" % str(av) 316 self.access.add_av(av) 317 318 def to_string(self): 319 s = [] 320 s.append("[InterfaceVector %s]" % self.name) 321 for av in self.access: 322 s.append(str(av)) 323 return "\n".join(s) 324 325 def __str__(self): 326 return self.__repr__() 327 328 def __repr__(self): 329 return "<InterfaceVector %s:%s>" % (self.name, self.enabled) 330 331 332class InterfaceSet: 333 def __init__(self, output=None): 334 self.interfaces = { } 335 self.tgt_type_map = { } 336 self.tgt_type_all = [] 337 self.output = output 338 339 def o(self, str): 340 if self.output: 341 self.output.write(str + "\n") 342 343 def to_file(self, fd): 344 for iv in self.interfaces.values(): 345 fd.write("[InterfaceVector %s " % iv.name) 346 for param in iv.params.values(): 347 fd.write("%s:%s " % (param.name, refpolicy.field_to_str[param.type])) 348 fd.write("]\n") 349 avl = iv.access.to_list() 350 for av in avl: 351 fd.write(",".join(av)) 352 fd.write("\n") 353 354 def from_file(self, fd): 355 def parse_ifv(line): 356 fields = line[1:-1].split() 357 if len(fields) < 2 or fields[0] != "InterfaceVector": 358 raise SyntaxError("Syntax error InterfaceVector statement %s" % line) 359 ifv = InterfaceVector() 360 ifv.name = fields[1] 361 if len(fields) == 2: 362 return 363 for field in fields[2:]: 364 p = field.split(":") 365 if len(p) != 2: 366 raise SyntaxError("Invalid param in InterfaceVector statement %s" % line) 367 param = Param() 368 param.name = p[0] 369 param.type = refpolicy.str_to_field[p[1]] 370 ifv.params[param.name] = param 371 return ifv 372 373 ifv = None 374 for line in fd: 375 line = line[:-1] 376 if line[0] == "[": 377 if ifv: 378 self.add_ifv(ifv) 379 ifv = parse_ifv(line) 380 elif ifv: 381 l = line.split(",") 382 av = access.AccessVector(l) 383 ifv.add_av(av) 384 if ifv: 385 self.add_ifv(ifv) 386 387 self.index() 388 389 def add_ifv(self, ifv): 390 self.interfaces[ifv.name] = ifv 391 392 def index(self): 393 for ifv in self.interfaces.values(): 394 tgt_types = set() 395 for av in ifv.access: 396 if access.is_idparam(av.tgt_type): 397 self.tgt_type_all.append(ifv) 398 tgt_types = set() 399 break 400 tgt_types.add(av.tgt_type) 401 402 for type in tgt_types: 403 l = self.tgt_type_map.setdefault(type, []) 404 l.append(ifv) 405 406 def add(self, interface, attributes={}): 407 ifv = InterfaceVector(interface, attributes) 408 self.add_ifv(ifv) 409 410 def add_headers(self, headers, output=None, attributes={}): 411 for i in itertools.chain(headers.interfaces(), headers.templates()): 412 self.add(i, attributes) 413 414 self.expand_ifcalls(headers) 415 self.index() 416 417 def map_param(self, id, ifcall): 418 if access.is_idparam(id): 419 num = int(id[1:]) 420 if num > len(ifcall.args): 421 # Tell caller to drop this because it must have 422 # been generated from an optional param. 423 return None 424 else: 425 arg = ifcall.args[num - 1] 426 if isinstance(arg, list): 427 return arg 428 else: 429 return [arg] 430 else: 431 return [id] 432 433 def map_add_av(self, ifv, av, ifcall): 434 src_types = self.map_param(av.src_type, ifcall) 435 if src_types is None: 436 return 437 438 tgt_types = self.map_param(av.tgt_type, ifcall) 439 if tgt_types is None: 440 return 441 442 obj_classes = self.map_param(av.obj_class, ifcall) 443 if obj_classes is None: 444 return 445 446 new_perms = refpolicy.IdSet() 447 for perm in av.perms: 448 p = self.map_param(perm, ifcall) 449 if p is None: 450 continue 451 else: 452 new_perms.update(p) 453 if len(new_perms) == 0: 454 return 455 456 for src_type in src_types: 457 for tgt_type in tgt_types: 458 for obj_class in obj_classes: 459 ifv.access.add(src_type, tgt_type, obj_class, new_perms) 460 461 def do_expand_ifcalls(self, interface, if_by_name): 462 # Descend an interface call tree adding the access 463 # from each interface. This is a depth first walk 464 # of the tree. 465 466 stack = [(interface, None)] 467 ifv = self.interfaces[interface.name] 468 ifv.expanded = True 469 470 while len(stack) > 0: 471 cur, cur_ifcall = stack.pop(-1) 472 473 cur_ifv = self.interfaces[cur.name] 474 if cur != interface: 475 476 for av in cur_ifv.access: 477 self.map_add_av(ifv, av, cur_ifcall) 478 479 # If we have already fully expanded this interface 480 # there is no reason to descend further. 481 if cur_ifv.expanded: 482 continue 483 484 for ifcall in cur.interface_calls(): 485 if ifcall.ifname == interface.name: 486 self.o(_("Found circular interface class")) 487 return 488 try: 489 newif = if_by_name[ifcall.ifname] 490 except KeyError: 491 self.o(_("Missing interface definition for %s" % ifcall.ifname)) 492 continue 493 494 stack.append((newif, ifcall)) 495 496 497 def expand_ifcalls(self, headers): 498 # Create a map of interface names to interfaces - 499 # this mirrors the interface vector map we already 500 # have. 501 if_by_name = { } 502 503 for i in itertools.chain(headers.interfaces(), headers.templates()): 504 if_by_name[i.name] = i 505 506 507 for interface in itertools.chain(headers.interfaces(), headers.templates()): 508 self.do_expand_ifcalls(interface, if_by_name) 509 510