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 """ 82 def __init__(self, init_list=None): 83 if init_list: 84 self.from_list(init_list) 85 else: 86 self.src_type = None 87 self.tgt_type = None 88 self.obj_class = None 89 self.perms = refpolicy.IdSet() 90 self.audit_msgs = [] 91 self.type = audit2why.TERULE 92 self.data = [] 93 # when implementing __eq__ also __hash__ is needed on py2 94 # if object is muttable __hash__ should be None 95 self.__hash__ = None 96 97 # The direction of the information flow represented by this 98 # access vector - used for matching 99 self.info_flow_dir = None 100 101 def from_list(self, list): 102 """Initialize an access vector from a list. 103 104 Initialize an access vector from a list treating the list as 105 positional arguments - i.e., 0 = src_type, 1 = tgt_type, etc. 106 All of the list elements 3 and greater are treated as perms. 107 For example, the list ['foo_t', 'bar_t', 'file', 'read', 'write'] 108 would create an access vector list with the source type 'foo_t', 109 target type 'bar_t', object class 'file', and permissions 'read' 110 and 'write'. 111 112 This format is useful for very simple storage to strings or disc 113 (see to_list) and for initializing access vectors. 114 """ 115 if len(list) < 4: 116 raise ValueError("List must contain at least four elements %s" % str(list)) 117 self.src_type = list[0] 118 self.tgt_type = list[1] 119 self.obj_class = list[2] 120 self.perms = refpolicy.IdSet(list[3:]) 121 122 def to_list(self): 123 """ 124 Convert an access vector to a list. 125 126 Convert an access vector to a list treating the list as positional 127 values. See from_list for more information on how an access vector 128 is represented in a list. 129 """ 130 l = [self.src_type, self.tgt_type, self.obj_class] 131 l.extend(self.perms) 132 return l 133 134 def __str__(self): 135 return self.to_string() 136 137 def to_string(self): 138 return "allow %s %s:%s %s;" % (self.src_type, self.tgt_type, 139 self.obj_class, self.perms.to_space_str()) 140 141 def _compare(self, other, method): 142 try: 143 x = list(self.perms) 144 a = (self.src_type, self.tgt_type, self.obj_class, x) 145 y = list(other.perms) 146 x.sort() 147 y.sort() 148 b = (other.src_type, other.tgt_type, other.obj_class, y) 149 return method(a, b) 150 except (AttributeError, TypeError): 151 # trying to compare to foreign type 152 return NotImplemented 153 154 155def avrule_to_access_vectors(avrule): 156 """Convert an avrule into a list of access vectors. 157 158 AccessVectors and AVRules are similary, but differ in that 159 an AVRule can more than one source type, target type, and 160 object class. This function expands a single avrule into a 161 list of one or more AccessVectors representing the access 162 defined in the AVRule. 163 164 165 """ 166 if isinstance(avrule, AccessVector): 167 return [avrule] 168 a = [] 169 for src_type in avrule.src_types: 170 for tgt_type in avrule.tgt_types: 171 for obj_class in avrule.obj_classes: 172 access = AccessVector() 173 access.src_type = src_type 174 access.tgt_type = tgt_type 175 access.obj_class = obj_class 176 access.perms = avrule.perms.copy() 177 a.append(access) 178 return a 179 180class AccessVectorSet: 181 """A non-overlapping set of access vectors. 182 183 An AccessVectorSet is designed to store one or more access vectors 184 that are non-overlapping. Access can be added to the set 185 incrementally and access vectors will be added or merged as 186 necessary. For example, adding the following access vectors using 187 add_av: 188 allow $1 etc_t : read; 189 allow $1 etc_t : write; 190 allow $1 var_log_t : read; 191 Would result in an access vector set with the access vectors: 192 allow $1 etc_t : { read write}; 193 allow $1 var_log_t : read; 194 """ 195 def __init__(self): 196 """Initialize an access vector set. 197 """ 198 self.src = {} 199 # The information flow direction of this access vector 200 # set - see objectmodel.py for more information. This 201 # stored here to speed up searching - see matching.py. 202 self.info_dir = None 203 204 def __iter__(self): 205 """Iterate over all of the unique access vectors in the set.""" 206 for tgts in self.src.values(): 207 for objs in tgts.values(): 208 for av in objs.values(): 209 yield av 210 211 def __len__(self): 212 """Return the number of unique access vectors in the set. 213 214 Because of the inernal representation of the access vector set, 215 __len__ is not a constant time operation. Worst case is O(N) 216 where N is the number of unique access vectors, but the common 217 case is probably better. 218 """ 219 l = 0 220 for tgts in self.src.values(): 221 for objs in tgts.values(): 222 l += len(objs) 223 return l 224 225 def to_list(self): 226 """Return the unique access vectors in the set as a list. 227 228 The format of the returned list is a set of nested lists, 229 each access vector represented by a list. This format is 230 designed to be simply serializable to a file. 231 232 For example, consider an access vector set with the following 233 access vectors: 234 allow $1 user_t : file read; 235 allow $1 etc_t : file { read write}; 236 to_list would return the following: 237 [[$1, user_t, file, read] 238 [$1, etc_t, file, read, write]] 239 240 See AccessVector.to_list for more information. 241 """ 242 l = [] 243 for av in self: 244 l.append(av.to_list()) 245 246 return l 247 248 def from_list(self, l): 249 """Add access vectors stored in a list. 250 251 See to list for more information on the list format that this 252 method accepts. 253 254 This will add all of the access from the list. Any existing 255 access vectors in the set will be retained. 256 """ 257 for av in l: 258 self.add_av(AccessVector(av)) 259 260 def add(self, src_type, tgt_type, obj_class, perms, audit_msg=None, avc_type=audit2why.TERULE, data=[]): 261 """Add an access vector to the set. 262 """ 263 tgt = self.src.setdefault(src_type, { }) 264 cls = tgt.setdefault(tgt_type, { }) 265 266 if (obj_class, avc_type) in cls: 267 access = cls[obj_class, avc_type] 268 else: 269 access = AccessVector() 270 access.src_type = src_type 271 access.tgt_type = tgt_type 272 access.obj_class = obj_class 273 access.data = data 274 access.type = avc_type 275 cls[obj_class, avc_type] = access 276 277 access.perms.update(perms) 278 if audit_msg: 279 access.audit_msgs.append(audit_msg) 280 281 def add_av(self, av, audit_msg=None): 282 """Add an access vector to the set.""" 283 self.add(av.src_type, av.tgt_type, av.obj_class, av.perms) 284 285 286def avs_extract_types(avs): 287 types = refpolicy.IdSet() 288 for av in avs: 289 types.add(av.src_type) 290 types.add(av.tgt_type) 291 292 return types 293 294def avs_extract_obj_perms(avs): 295 perms = { } 296 for av in avs: 297 if av.obj_class in perms: 298 s = perms[av.obj_class] 299 else: 300 s = refpolicy.IdSet() 301 perms[av.obj_class] = s 302 s.update(av.perms) 303 return perms 304 305class RoleTypeSet: 306 """A non-overlapping set of role type statements. 307 308 This clas allows the incremental addition of role type statements and 309 maintains a non-overlapping list of statements. 310 """ 311 def __init__(self): 312 """Initialize an access vector set.""" 313 self.role_types = {} 314 315 def __iter__(self): 316 """Iterate over all of the unique role allows statements in the set.""" 317 for role_type in self.role_types.values(): 318 yield role_type 319 320 def __len__(self): 321 """Return the unique number of role allow statements.""" 322 return len(self.role_types.keys()) 323 324 def add(self, role, type): 325 if role in self.role_types: 326 role_type = self.role_types[role] 327 else: 328 role_type = refpolicy.RoleType() 329 role_type.role = role 330 self.role_types[role] = role_type 331 332 role_type.types.add(type) 333