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