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 representing basic access. 22 23SELinux - at the most basic level - represents access as 24the 4-tuple subject (type or context), target (type or context), 25object class, permission. The policy language elaborates this basic 26access to faciliate more concise rules (e.g., allow rules can have multiple 27source or target types - see refpolicy for more information). 28 29This module has objects for representing the most basic access (AccessVector) 30and sets of that access (AccessVectorSet). These objects are used in Madison 31in a variety of ways, but they are the fundamental representation of access. 32""" 33 34from . import refpolicy 35from . import util 36 37from selinux import audit2why 38 39def is_idparam(id): 40 """Determine if an id is a paramater in the form $N, where N is 41 an integer. 42 43 Returns: 44 True if the id is a paramater 45 False if the id is not a paramater 46 """ 47 if len(id) > 1 and id[0] == '$': 48 try: 49 int(id[1:]) 50 except ValueError: 51 return False 52 return True 53 else: 54 return False 55 56class AccessVector(util.Comparison): 57 """ 58 An access vector is the basic unit of access in SELinux. 59 60 Access vectors are the most basic representation of access within 61 SELinux. It represents the access a source type has to a target 62 type in terms of an object class and a set of permissions. 63 64 Access vectors are distinct from AVRules in that they can only 65 store a single source type, target type, and object class. The 66 simplicity of AccessVectors makes them useful for storing access 67 in a form that is easy to search and compare. 68 69 The source, target, and object are stored as string. No checking 70 done to verify that the strings are valid SELinux identifiers. 71 Identifiers in the form $N (where N is an integer) are reserved as 72 interface parameters and are treated as wild cards in many 73 circumstances. 74 75 Properties: 76 .src_type - The source type allowed access. [String or None] 77 .tgt_type - The target type to which access is allowed. [String or None] 78 .obj_class - The object class to which access is allowed. [String or None] 79 .perms - The permissions allowed to the object class. [IdSet] 80 .audit_msgs - The audit messages that generated this access vector [List of strings] 81 .xperms - Extended permissions attached to the AV. [Dictionary {operation: xperm set}] 82 """ 83 def __init__(self, init_list=None): 84 if init_list: 85 self.from_list(init_list) 86 else: 87 self.src_type = None 88 self.tgt_type = None 89 self.obj_class = None 90 self.perms = refpolicy.IdSet() 91 92 self.audit_msgs = [] 93 self.type = audit2why.TERULE 94 self.data = [] 95 self.xperms = {} 96 # when implementing __eq__ also __hash__ is needed on py2 97 # if object is muttable __hash__ should be None 98 self.__hash__ = None 99 100 # The direction of the information flow represented by this 101 # access vector - used for matching 102 self.info_flow_dir = None 103 104 def from_list(self, list): 105 """Initialize an access vector from a list. 106 107 Initialize an access vector from a list treating the list as 108 positional arguments - i.e., 0 = src_type, 1 = tgt_type, etc. 109 All of the list elements 3 and greater are treated as perms. 110 For example, the list ['foo_t', 'bar_t', 'file', 'read', 'write'] 111 would create an access vector list with the source type 'foo_t', 112 target type 'bar_t', object class 'file', and permissions 'read' 113 and 'write'. 114 115 This format is useful for very simple storage to strings or disc 116 (see to_list) and for initializing access vectors. 117 """ 118 if len(list) < 4: 119 raise ValueError("List must contain at least four elements %s" % str(list)) 120 self.src_type = list[0] 121 self.tgt_type = list[1] 122 self.obj_class = list[2] 123 self.perms = refpolicy.IdSet(list[3:]) 124 125 def to_list(self): 126 """ 127 Convert an access vector to a list. 128 129 Convert an access vector to a list treating the list as positional 130 values. See from_list for more information on how an access vector 131 is represented in a list. 132 """ 133 l = [self.src_type, self.tgt_type, self.obj_class] 134 l.extend(sorted(self.perms)) 135 return l 136 137 def merge(self, av): 138 """Add permissions and extended permissions from AV""" 139 self.perms.update(av.perms) 140 141 for op in av.xperms: 142 if op not in self.xperms: 143 self.xperms[op] = refpolicy.XpermSet() 144 self.xperms[op].extend(av.xperms[op]) 145 146 def __str__(self): 147 return self.to_string() 148 149 def to_string(self): 150 return "allow %s %s:%s %s;" % (self.src_type, self.tgt_type, 151 self.obj_class, self.perms.to_space_str()) 152 153 def _compare(self, other, method): 154 try: 155 x = list(self.perms) 156 a = (self.src_type, self.tgt_type, self.obj_class, x) 157 y = list(other.perms) 158 x.sort() 159 y.sort() 160 b = (other.src_type, other.tgt_type, other.obj_class, y) 161 return method(a, b) 162 except (AttributeError, TypeError): 163 # trying to compare to foreign type 164 return NotImplemented 165 166 167def avrule_to_access_vectors(avrule): 168 """Convert an avrule into a list of access vectors. 169 170 AccessVectors and AVRules are similary, but differ in that 171 an AVRule can more than one source type, target type, and 172 object class. This function expands a single avrule into a 173 list of one or more AccessVectors representing the access 174 defined in the AVRule. 175 176 177 """ 178 if isinstance(avrule, AccessVector): 179 return [avrule] 180 a = [] 181 for src_type in avrule.src_types: 182 for tgt_type in avrule.tgt_types: 183 for obj_class in avrule.obj_classes: 184 access = AccessVector() 185 access.src_type = src_type 186 access.tgt_type = tgt_type 187 access.obj_class = obj_class 188 access.perms = avrule.perms.copy() 189 a.append(access) 190 return a 191 192class AccessVectorSet: 193 """A non-overlapping set of access vectors. 194 195 An AccessVectorSet is designed to store one or more access vectors 196 that are non-overlapping. Access can be added to the set 197 incrementally and access vectors will be added or merged as 198 necessary. For example, adding the following access vectors using 199 add_av: 200 allow $1 etc_t : read; 201 allow $1 etc_t : write; 202 allow $1 var_log_t : read; 203 Would result in an access vector set with the access vectors: 204 allow $1 etc_t : { read write}; 205 allow $1 var_log_t : read; 206 """ 207 def __init__(self): 208 """Initialize an access vector set. 209 """ 210 self.src = {} 211 # The information flow direction of this access vector 212 # set - see objectmodel.py for more information. This 213 # stored here to speed up searching - see matching.py. 214 self.info_dir = None 215 216 def __iter__(self): 217 """Iterate over all of the unique access vectors in the set.""" 218 for tgts in self.src.values(): 219 for objs in tgts.values(): 220 for av in objs.values(): 221 yield av 222 223 def __len__(self): 224 """Return the number of unique access vectors in the set. 225 226 Because of the inernal representation of the access vector set, 227 __len__ is not a constant time operation. Worst case is O(N) 228 where N is the number of unique access vectors, but the common 229 case is probably better. 230 """ 231 l = 0 232 for tgts in self.src.values(): 233 for objs in tgts.values(): 234 l += len(objs) 235 return l 236 237 def to_list(self): 238 """Return the unique access vectors in the set as a list. 239 240 The format of the returned list is a set of nested lists, 241 each access vector represented by a list. This format is 242 designed to be simply serializable to a file. 243 244 For example, consider an access vector set with the following 245 access vectors: 246 allow $1 user_t : file read; 247 allow $1 etc_t : file { read write}; 248 to_list would return the following: 249 [[$1, user_t, file, read] 250 [$1, etc_t, file, read, write]] 251 252 See AccessVector.to_list for more information. 253 """ 254 l = [] 255 for av in self: 256 l.append(av.to_list()) 257 258 return l 259 260 def from_list(self, l): 261 """Add access vectors stored in a list. 262 263 See to list for more information on the list format that this 264 method accepts. 265 266 This will add all of the access from the list. Any existing 267 access vectors in the set will be retained. 268 """ 269 for av in l: 270 self.add_av(AccessVector(av)) 271 272 def add(self, src_type, tgt_type, obj_class, perms, audit_msg=None, avc_type=audit2why.TERULE, data=[]): 273 """Add an access vector to the set. 274 """ 275 av = AccessVector() 276 av.src_type = src_type 277 av.tgt_type = tgt_type 278 av.obj_class = obj_class 279 av.perms = perms 280 av.data = data 281 av.type = avc_type 282 283 self.add_av(av, audit_msg) 284 285 def add_av(self, av, audit_msg=None): 286 """Add an access vector to the set.""" 287 tgt = self.src.setdefault(av.src_type, { }) 288 cls = tgt.setdefault(av.tgt_type, { }) 289 290 if (av.obj_class, av.type) in cls: 291 cls[av.obj_class, av.type].merge(av) 292 else: 293 cls[av.obj_class, av.type] = av 294 295 if audit_msg: 296 cls[av.obj_class, av.type].audit_msgs.append(audit_msg) 297 298def avs_extract_types(avs): 299 types = refpolicy.IdSet() 300 for av in avs: 301 types.add(av.src_type) 302 types.add(av.tgt_type) 303 304 return types 305 306def avs_extract_obj_perms(avs): 307 perms = { } 308 for av in avs: 309 if av.obj_class in perms: 310 s = perms[av.obj_class] 311 else: 312 s = refpolicy.IdSet() 313 perms[av.obj_class] = s 314 s.update(av.perms) 315 return perms 316 317class RoleTypeSet: 318 """A non-overlapping set of role type statements. 319 320 This clas allows the incremental addition of role type statements and 321 maintains a non-overlapping list of statements. 322 """ 323 def __init__(self): 324 """Initialize an access vector set.""" 325 self.role_types = {} 326 327 def __iter__(self): 328 """Iterate over all of the unique role allows statements in the set.""" 329 for role_type in self.role_types.values(): 330 yield role_type 331 332 def __len__(self): 333 """Return the unique number of role allow statements.""" 334 return len(self.role_types.keys()) 335 336 def add(self, role, type): 337 if role in self.role_types: 338 role_type = self.role_types[role] 339 else: 340 role_type = refpolicy.RoleType() 341 role_type.role = role 342 self.role_types[role] = role_type 343 344 role_type.types.add(type) 345