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 parameter 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 # Handle 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 parameters from an access vector. 108 109 Extract the parameters (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 parameters 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 ambiguous, 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 return ret 157 158def role_extract_params(role, params): 159 if access.is_idparam(role.role): 160 return __param_insert(role.role, refpolicy.ROLE, None, params) 161 162def type_rule_extract_params(rule, params): 163 def extract_from_set(set, type): 164 ret = 0 165 for x in set: 166 if access.is_idparam(x): 167 if __param_insert(x, type, None, params): 168 ret = 1 169 return ret 170 171 ret = 0 172 if extract_from_set(rule.src_types, refpolicy.SRC_TYPE): 173 ret = 1 174 175 if extract_from_set(rule.tgt_types, refpolicy.TGT_TYPE): 176 ret = 1 177 178 if extract_from_set(rule.obj_classes, refpolicy.OBJ_CLASS): 179 ret = 1 180 181 if access.is_idparam(rule.dest_type): 182 if __param_insert(rule.dest_type, refpolicy.DEST_TYPE, None, params): 183 ret = 1 184 185 return ret 186 187def ifcall_extract_params(ifcall, params): 188 ret = 0 189 for arg in ifcall.args: 190 if access.is_idparam(arg): 191 # Assume interface arguments are source types. Fairly safe 192 # assumption for most interfaces 193 if __param_insert(arg, refpolicy.SRC_TYPE, None, params): 194 ret = 1 195 196 return ret 197 198class AttributeVector: 199 def __init__(self): 200 self.name = "" 201 self.access = access.AccessVectorSet() 202 203 def add_av(self, av): 204 self.access.add_av(av) 205 206class AttributeSet: 207 def __init__(self): 208 self.attributes = { } 209 210 def add_attr(self, attr): 211 self.attributes[attr.name] = attr 212 213 def from_file(self, fd): 214 def parse_attr(line): 215 fields = line[1:-1].split() 216 if len(fields) != 2 or fields[0] != "Attribute": 217 raise SyntaxError("Syntax error Attribute statement %s" % line) 218 a = AttributeVector() 219 a.name = fields[1] 220 221 return a 222 223 a = None 224 for line in fd: 225 line = line[:-1] 226 if line[0] == "[": 227 if a: 228 self.add_attr(a) 229 a = parse_attr(line) 230 elif a: 231 l = line.split(",") 232 av = access.AccessVector(l) 233 a.add_av(av) 234 if a: 235 self.add_attr(a) 236 237class InterfaceVector: 238 def __init__(self, interface=None, attributes={}): 239 # Enabled is a loose concept currently - we are essentially 240 # not enabling interfaces that we can't handle currently. 241 # See InterfaceVector.add_ifv for more information. 242 self.enabled = True 243 self.name = "" 244 # The access that is enabled by this interface - eventually 245 # this will include indirect access from typeattribute 246 # statements. 247 self.access = access.AccessVectorSet() 248 # Parameters are stored in a dictionary (key: param name 249 # value: Param object). 250 self.params = { } 251 if interface: 252 self.from_interface(interface, attributes) 253 self.expanded = False 254 255 def from_interface(self, interface, attributes={}): 256 self.name = interface.name 257 258 # Add allow rules 259 for avrule in interface.avrules(): 260 if avrule.rule_type != refpolicy.AVRule.ALLOW: 261 continue 262 # Handle some policy bugs 263 if "dontaudit" in interface.name: 264 #print "allow rule in interface: %s" % interface 265 continue 266 avs = access.avrule_to_access_vectors(avrule) 267 for av in avs: 268 self.add_av(av) 269 270 # Add typeattribute access 271 if attributes: 272 for typeattribute in interface.typeattributes(): 273 for attr in typeattribute.attributes: 274 if attr not in attributes.attributes: 275 # print "missing attribute " + attr 276 continue 277 attr_vec = attributes.attributes[attr] 278 for a in attr_vec.access: 279 av = copy.copy(a) 280 if av.src_type == attr_vec.name: 281 av.src_type = typeattribute.type 282 if av.tgt_type == attr_vec.name: 283 av.tgt_type = typeattribute.type 284 self.add_av(av) 285 286 287 # Extract parameters from roles 288 for role in interface.roles(): 289 if role_extract_params(role, self.params): 290 pass 291 #print "found conflicting role param %s for interface %s" % \ 292 # (role.name, interface.name) 293 # Extract parameters from type rules 294 for rule in interface.typerules(): 295 if type_rule_extract_params(rule, self.params): 296 pass 297 #print "found conflicting params in rule %s in interface %s" % \ 298 # (str(rule), interface.name) 299 300 for ifcall in interface.interface_calls(): 301 if ifcall_extract_params(ifcall, self.params): 302 pass 303 #print "found conflicting params in ifcall %s in interface %s" % \ 304 # (str(ifcall), interface.name) 305 306 307 def add_av(self, av): 308 if av_extract_params(av, self.params) == 1: 309 pass 310 #print "found conflicting perms [%s]" % str(av) 311 self.access.add_av(av) 312 313 def to_string(self): 314 s = [] 315 s.append("[InterfaceVector %s]" % self.name) 316 for av in self.access: 317 s.append(str(av)) 318 return "\n".join(s) 319 320 def __str__(self): 321 return self.__repr__() 322 323 def __repr__(self): 324 return "<InterfaceVector %s:%s>" % (self.name, self.enabled) 325 326 327class InterfaceSet: 328 def __init__(self, output=None): 329 self.interfaces = { } 330 self.tgt_type_map = { } 331 self.tgt_type_all = [] 332 self.output = output 333 334 def o(self, str): 335 if self.output: 336 self.output.write(str + "\n") 337 338 def to_file(self, fd): 339 for iv in sorted(self.interfaces.values(), key=lambda x: x.name): 340 fd.write("[InterfaceVector %s " % iv.name) 341 for param in sorted(iv.params.values(), key=lambda x: x.name): 342 fd.write("%s:%s " % (param.name, refpolicy.field_to_str[param.type])) 343 fd.write("]\n") 344 avl = sorted(iv.access.to_list()) 345 for av in avl: 346 fd.write(",".join(av)) 347 fd.write("\n") 348 349 def from_file(self, fd): 350 def parse_ifv(line): 351 fields = line[1:-1].split() 352 if len(fields) < 2 or fields[0] != "InterfaceVector": 353 raise SyntaxError("Syntax error InterfaceVector statement %s" % line) 354 ifv = InterfaceVector() 355 ifv.name = fields[1] 356 if len(fields) == 2: 357 return 358 for field in fields[2:]: 359 p = field.split(":") 360 if len(p) != 2: 361 raise SyntaxError("Invalid param in InterfaceVector statement %s" % line) 362 param = Param() 363 param.name = p[0] 364 param.type = refpolicy.str_to_field[p[1]] 365 ifv.params[param.name] = param 366 return ifv 367 368 ifv = None 369 for line in fd: 370 line = line[:-1] 371 if line[0] == "[": 372 if ifv: 373 self.add_ifv(ifv) 374 ifv = parse_ifv(line) 375 elif ifv: 376 l = line.split(",") 377 av = access.AccessVector(l) 378 ifv.add_av(av) 379 if ifv: 380 self.add_ifv(ifv) 381 382 self.index() 383 384 def add_ifv(self, ifv): 385 self.interfaces[ifv.name] = ifv 386 387 def index(self): 388 for ifv in self.interfaces.values(): 389 tgt_types = set() 390 for av in ifv.access: 391 if access.is_idparam(av.tgt_type): 392 self.tgt_type_all.append(ifv) 393 tgt_types = set() 394 break 395 tgt_types.add(av.tgt_type) 396 397 for type in tgt_types: 398 l = self.tgt_type_map.setdefault(type, []) 399 l.append(ifv) 400 401 def add(self, interface, attributes={}): 402 ifv = InterfaceVector(interface, attributes) 403 self.add_ifv(ifv) 404 405 def add_headers(self, headers, output=None, attributes={}): 406 for i in itertools.chain(headers.interfaces(), headers.templates()): 407 self.add(i, attributes) 408 409 self.expand_ifcalls(headers) 410 self.index() 411 412 def map_param(self, id, ifcall): 413 if access.is_idparam(id): 414 num = int(id[1:]) 415 if num > len(ifcall.args): 416 # Tell caller to drop this because it must have 417 # been generated from an optional param. 418 return None 419 else: 420 arg = ifcall.args[num - 1] 421 if isinstance(arg, list): 422 return arg 423 else: 424 return [arg] 425 else: 426 return [id] 427 428 def map_add_av(self, ifv, av, ifcall): 429 src_types = self.map_param(av.src_type, ifcall) 430 if src_types is None: 431 return 432 433 tgt_types = self.map_param(av.tgt_type, ifcall) 434 if tgt_types is None: 435 return 436 437 obj_classes = self.map_param(av.obj_class, ifcall) 438 if obj_classes is None: 439 return 440 441 new_perms = refpolicy.IdSet() 442 for perm in av.perms: 443 p = self.map_param(perm, ifcall) 444 if p is None: 445 continue 446 else: 447 new_perms.update(p) 448 if len(new_perms) == 0: 449 return 450 451 for src_type in src_types: 452 for tgt_type in tgt_types: 453 for obj_class in obj_classes: 454 ifv.access.add(src_type, tgt_type, obj_class, new_perms) 455 456 def do_expand_ifcalls(self, interface, if_by_name): 457 # Descend an interface call tree adding the access 458 # from each interface. This is a depth first walk 459 # of the tree. 460 461 stack = [(interface, None)] 462 ifv = self.interfaces[interface.name] 463 ifv.expanded = True 464 465 while len(stack) > 0: 466 cur, cur_ifcall = stack.pop(-1) 467 468 cur_ifv = self.interfaces[cur.name] 469 if cur != interface: 470 471 for av in cur_ifv.access: 472 self.map_add_av(ifv, av, cur_ifcall) 473 474 # If we have already fully expanded this interface 475 # there is no reason to descend further. 476 if cur_ifv.expanded: 477 continue 478 479 for ifcall in cur.interface_calls(): 480 if ifcall.ifname == interface.name: 481 self.o(_("Found circular interface class")) 482 return 483 try: 484 newif = if_by_name[ifcall.ifname] 485 except KeyError: 486 self.o(_("Missing interface definition for %s" % ifcall.ifname)) 487 continue 488 489 stack.append((newif, ifcall)) 490 491 492 def expand_ifcalls(self, headers): 493 # Create a map of interface names to interfaces - 494 # this mirrors the interface vector map we already 495 # have. 496 if_by_name = { } 497 498 for i in itertools.chain(headers.interfaces(), headers.templates()): 499 if_by_name[i.name] = i 500 501 502 for interface in itertools.chain(headers.interfaces(), headers.templates()): 503 self.do_expand_ifcalls(interface, if_by_name) 504 505