blob: 98133b7c4eed1f37fc1ca35a597f56875ee5ac93 [file] [log] [blame]
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +11001# Copyright 2021 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -070015from ctypes import *
16import re
17import os
Jeff Vander Stoep1fc06822017-05-31 15:36:07 -070018import sys
Jeff Vander Stoepe9777e32017-09-23 15:11:25 -070019import platform
Jeff Vander Stoep1ca7a4c2019-04-10 16:53:17 -070020import fc_sort
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -070021
Dan Cashman91d398d2017-09-26 12:58:29 -070022###
23# Check whether the regex will match a file path starting with the provided
24# prefix
25#
26# Compares regex entries in file_contexts with a path prefix. Regex entries
27# are often more specific than this file prefix. For example, the regex could
28# be /system/bin/foo\.sh and the prefix could be /system. This function
29# loops over the regex removing characters from the end until
30# 1) there is a match - return True or 2) run out of characters - return
31# False.
32#
Inseob Kim55d140f2023-09-26 11:19:13 +090033COMMON_PREFIXES = {
34 "/(vendor|system/vendor)": ["/vendor", "/system/vendor"],
35 "/(odm|vendor/odm)": ["/odm", "/vendor/odm"],
36 "/(product|system/product)": ["/product", "/system/product"],
37 "/(system_ext|system/system_ext)": ["/system_ext", "/system/system_ext"],
38}
39
Dan Cashman91d398d2017-09-26 12:58:29 -070040def MatchPathPrefix(pathregex, prefix):
Inseob Kim55d140f2023-09-26 11:19:13 +090041 # Before running regex compile loop, try two heuristics, because compiling
42 # regex is too expensive. These two can handle more than 90% out of all
43 # MatchPathPrefix calls.
44
45 # Heuristic 1: handle common prefixes for partitions
46 for c in COMMON_PREFIXES:
47 if not pathregex.startswith(c):
48 continue
49 found = False
50 for p in COMMON_PREFIXES[c]:
51 if prefix.startswith(p):
52 found = True
53 prefix = prefix[len(p):]
54 pathregex = pathregex[len(c):]
55 break
56 if not found:
57 return False
58
59 # Heuristic 2: compare normal characters as long as possible
60 idx = 0
61 while idx < len(prefix):
62 if idx == len(pathregex):
63 return False
64 if pathregex[idx] in fc_sort.META_CHARS or pathregex[idx] == '\\':
65 break
66 if pathregex[idx] != prefix[idx]:
67 return False
68 idx += 1
69 if idx == len(prefix):
70 return True
71
72 # Fall back to regex compile loop.
Dan Cashman91d398d2017-09-26 12:58:29 -070073 for i in range(len(pathregex), 0, -1):
74 try:
75 pattern = re.compile('^' + pathregex[0:i] + "$")
76 except:
77 continue
78 if pattern.match(prefix):
79 return True
80 return False
81
82def MatchPathPrefixes(pathregex, Prefixes):
83 for Prefix in Prefixes:
84 if MatchPathPrefix(pathregex, Prefix):
85 return True
86 return False
87
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -070088class TERule:
89 def __init__(self, rule):
90 data = rule.split(',')
91 self.flavor = data[0]
92 self.sctx = data[1]
93 self.tctx = data[2]
94 self.tclass = data[3]
95 self.perms = set((data[4].strip()).split(' '))
96 self.rule = rule
97
98class Policy:
Dan Cashman91d398d2017-09-26 12:58:29 -070099 __ExpandedRules = set()
100 __Rules = set()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700101 __FcDict = None
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800102 __FcSorted = None
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700103 __GenfsDict = None
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700104 __libsepolwrap = None
105 __policydbP = None
Dan Cashman91d398d2017-09-26 12:58:29 -0700106 __BUFSIZE = 2048
107
Alan Stokes668e74f2020-11-12 18:08:18 +0000108 def AssertPathTypesDoNotHaveAttr(self, MatchPrefix, DoNotMatchPrefix, Attr, ExcludedTypes = []):
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800109 # Query policy for the types associated with Attr
Alan Stokes668e74f2020-11-12 18:08:18 +0000110 TypesPol = self.QueryTypeAttribute(Attr, True) - set(ExcludedTypes)
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800111 # Search file_contexts to find types associated with input paths.
Inseob Kim34d6c642023-09-27 17:32:26 +0900112 PathTypes = self.__GetTypesAndFilesByFilePathPrefix(MatchPrefix, DoNotMatchPrefix)
113 violators = set()
114 for PathType in PathTypes:
115 filepath, filetype = PathType
116 if filetype in TypesPol:
117 violators.add((str(filetype), str(filepath)))
118
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800119 ret = ""
120 if len(violators) > 0:
121 ret += "The following types on "
122 ret += " ".join(str(x) for x in sorted(MatchPrefix))
123 ret += " must not be associated with the "
Inseob Kim34d6c642023-09-27 17:32:26 +0900124 ret += "\"" + Attr + "\" attribute.\n"
125 ret += "Violator types and corresponding paths:\n"
126 ret += "\n".join(str(x) for x in sorted(violators))
127 ret += "\n"
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800128 return ret
129
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700130 # Check that all types for "filesystem" have "attribute" associated with them
131 # for types labeled in genfs_contexts.
132 def AssertGenfsFilesystemTypesHaveAttr(self, Filesystem, Attr):
133 TypesPol = self.QueryTypeAttribute(Attr, True)
134 TypesGenfs = self.__GenfsDict[Filesystem]
135 violators = TypesGenfs.difference(TypesPol)
136
137 ret = ""
138 if len(violators) > 0:
139 ret += "The following types in " + Filesystem
140 ret += " must be associated with the "
141 ret += "\"" + Attr + "\" attribute: "
142 ret += " ".join(str(x) for x in sorted(violators)) + "\n"
143 return ret
144
Dan Cashman91d398d2017-09-26 12:58:29 -0700145 # Check that path prefixes that match MatchPrefix, and do not Match
146 # DoNotMatchPrefix have the attribute Attr.
147 # For example assert that all types in /sys, and not in /sys/kernel/debugfs
148 # have the sysfs_type attribute.
Inseob Kim3a9d91c2023-09-27 17:39:07 +0900149 def AssertPathTypesHaveAttr(self, MatchPrefix, DoNotMatchPrefix, Attr, ExcludedTypes = []):
Dan Cashman91d398d2017-09-26 12:58:29 -0700150 # Query policy for the types associated with Attr
Inseob Kim3a9d91c2023-09-27 17:39:07 +0900151 TypesPol = self.QueryTypeAttribute(Attr, True) | set(ExcludedTypes)
Dan Cashman91d398d2017-09-26 12:58:29 -0700152 # Search file_contexts to find paths/types that should be associated with
153 # Attr.
Inseob Kim34d6c642023-09-27 17:32:26 +0900154 PathTypes = self.__GetTypesAndFilesByFilePathPrefix(MatchPrefix, DoNotMatchPrefix)
155 violators = set()
156 for PathType in PathTypes:
157 filepath, filetype = PathType
158 if filetype not in TypesPol:
159 violators.add((str(filetype), str(filepath)))
Dan Cashman91d398d2017-09-26 12:58:29 -0700160
161 ret = ""
162 if len(violators) > 0:
163 ret += "The following types on "
164 ret += " ".join(str(x) for x in sorted(MatchPrefix))
165 ret += " must be associated with the "
Inseob Kim34d6c642023-09-27 17:32:26 +0900166 ret += "\"" + Attr + "\" attribute.\n"
167 ret += "Violator types and corresponding paths:\n"
168 ret += "\n".join(str(x) for x in sorted(violators))
169 ret += "\n"
Dan Cashman91d398d2017-09-26 12:58:29 -0700170 return ret
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700171
Inseob Kim1b8b1f62020-10-23 15:16:11 +0900172 def AssertPropertyOwnersAreExclusive(self):
173 systemProps = self.QueryTypeAttribute('system_property_type', True)
174 vendorProps = self.QueryTypeAttribute('vendor_property_type', True)
175 violators = systemProps.intersection(vendorProps)
176 ret = ""
177 if len(violators) > 0:
178 ret += "The following types have both system_property_type "
179 ret += "and vendor_property_type: "
180 ret += " ".join(str(x) for x in sorted(violators)) + "\n"
181 return ret
182
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700183 # Return all file_contexts entries that map to the input Type.
184 def QueryFc(self, Type):
185 if Type in self.__FcDict:
186 return self.__FcDict[Type]
187 else:
188 return None
189
190 # Return all attributes associated with a type if IsAttr=False or
191 # all types associated with an attribute if IsAttr=True
192 def QueryTypeAttribute(self, Type, IsAttr):
Dan Cashman91d398d2017-09-26 12:58:29 -0700193 TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP,
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100194 create_string_buffer(Type.encode("ascii")), IsAttr)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700195 if (TypeIterP == None):
196 sys.exit("Failed to initialize type iterator")
Dan Cashman91d398d2017-09-26 12:58:29 -0700197 buf = create_string_buffer(self.__BUFSIZE)
198 TypeAttr = set()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700199 while True:
Dan Cashman91d398d2017-09-26 12:58:29 -0700200 ret = self.__libsepolwrap.get_type(buf, self.__BUFSIZE,
201 self.__policydbP, TypeIterP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700202 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100203 TypeAttr.add(buf.value.decode("ascii"))
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700204 continue
205 if ret == 1:
206 break;
207 # We should never get here.
208 sys.exit("Failed to import policy")
Dan Cashman91d398d2017-09-26 12:58:29 -0700209 self.__libsepolwrap.destroy_type_iter(TypeIterP)
210 return TypeAttr
211
212 def __TERuleMatch(self, Rule, **kwargs):
213 # Match source type
214 if ("scontext" in kwargs and
215 len(kwargs['scontext']) > 0 and
216 Rule.sctx not in kwargs['scontext']):
217 return False
218 # Match target type
219 if ("tcontext" in kwargs and
220 len(kwargs['tcontext']) > 0 and
221 Rule.tctx not in kwargs['tcontext']):
222 return False
223 # Match target class
224 if ("tclass" in kwargs and
225 len(kwargs['tclass']) > 0 and
226 not bool(set([Rule.tclass]) & kwargs['tclass'])):
227 return False
228 # Match any perms
229 if ("perms" in kwargs and
230 len(kwargs['perms']) > 0 and
231 not bool(Rule.perms & kwargs['perms'])):
232 return False
233 return True
234
235 # resolve a type to its attributes or
236 # resolve an attribute to its types and attributes
237 # For example if scontext is the domain attribute, then we need to
238 # include all types with the domain attribute such as untrusted_app and
239 # priv_app and all the attributes of those types such as appdomain.
240 def ResolveTypeAttribute(self, Type):
241 types = self.GetAllTypes(False)
242 attributes = self.GetAllTypes(True)
243
244 if Type in types:
245 return self.QueryTypeAttribute(Type, False)
246 elif Type in attributes:
247 TypesAndAttributes = set()
248 Types = self.QueryTypeAttribute(Type, True)
249 TypesAndAttributes |= Types
250 for T in Types:
251 TypesAndAttributes |= self.QueryTypeAttribute(T, False)
252 return TypesAndAttributes
253 else:
254 return set()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700255
256 # Return all TERules that match:
257 # (any scontext) or (any tcontext) or (any tclass) or (any perms),
258 # perms.
259 # Any unspecified paramenter will match all.
260 #
261 # Example: QueryTERule(tcontext=["foo", "bar"], perms=["entrypoint"])
262 # Will return any rule with:
263 # (tcontext="foo" or tcontext="bar") and ("entrypoint" in perms)
264 def QueryTERule(self, **kwargs):
Dan Cashman91d398d2017-09-26 12:58:29 -0700265 if len(self.__Rules) == 0:
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700266 self.__InitTERules()
Dan Cashman91d398d2017-09-26 12:58:29 -0700267
268 # add any matching types and attributes for scontext and tcontext
269 if ("scontext" in kwargs and len(kwargs['scontext']) > 0):
270 scontext = set()
271 for sctx in kwargs['scontext']:
272 scontext |= self.ResolveTypeAttribute(sctx)
Sandro26152e92022-08-05 11:35:39 +0000273 if (len(scontext) == 0):
274 return []
Dan Cashman91d398d2017-09-26 12:58:29 -0700275 kwargs['scontext'] = scontext
276 if ("tcontext" in kwargs and len(kwargs['tcontext']) > 0):
277 tcontext = set()
278 for tctx in kwargs['tcontext']:
279 tcontext |= self.ResolveTypeAttribute(tctx)
Sandro26152e92022-08-05 11:35:39 +0000280 if (len(tcontext) == 0):
281 return []
Dan Cashman91d398d2017-09-26 12:58:29 -0700282 kwargs['tcontext'] = tcontext
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700283 for Rule in self.__Rules:
Dan Cashman91d398d2017-09-26 12:58:29 -0700284 if self.__TERuleMatch(Rule, **kwargs):
285 yield Rule
286
287 # Same as QueryTERule but only using the expanded ruleset.
288 # i.e. all attributes have been expanded to their various types.
289 def QueryExpandedTERule(self, **kwargs):
290 if len(self.__ExpandedRules) == 0:
291 self.__InitExpandedTERules()
292 for Rule in self.__ExpandedRules:
293 if self.__TERuleMatch(Rule, **kwargs):
294 yield Rule
295
296 def GetAllTypes(self, isAttr):
297 TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP, None, isAttr)
298 if (TypeIterP == None):
299 sys.exit("Failed to initialize type iterator")
300 buf = create_string_buffer(self.__BUFSIZE)
301 AllTypes = set()
302 while True:
303 ret = self.__libsepolwrap.get_type(buf, self.__BUFSIZE,
304 self.__policydbP, TypeIterP)
305 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100306 AllTypes.add(buf.value.decode("ascii"))
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700307 continue
Dan Cashman91d398d2017-09-26 12:58:29 -0700308 if ret == 1:
309 break;
310 # We should never get here.
311 sys.exit("Failed to import policy")
312 self.__libsepolwrap.destroy_type_iter(TypeIterP)
313 return AllTypes
314
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800315 def __ExactMatchPathPrefix(self, pathregex, prefix):
316 pattern = re.compile('^' + pathregex + "$")
317 if pattern.match(prefix):
318 return True
319 return False
320
321 # Return a tuple (prefix, i) where i is the index of the most specific
322 # match of prefix in the sorted file_contexts. This is useful for limiting a
323 # file_contexts search to matches that are more specific and omitting less
324 # specific matches. For example, finding all matches to prefix /data/vendor
325 # should not include /data(/.*)? if /data/vendor(/.*)? is also specified.
326 def __FcSortedIndex(self, prefix):
327 index = 0
328 for i in range(0, len(self.__FcSorted)):
329 if self.__ExactMatchPathPrefix(self.__FcSorted[i].path, prefix):
330 index = i
331 return prefix, index
332
333 # Return a tuple of (path, Type) for all matching paths. Use the sorted
334 # file_contexts and index returned from __FcSortedIndex() to limit results
335 # to results that are more specific than the prefix.
336 def __MatchPathPrefixTypes(self, prefix, index):
337 PathType = []
338 for i in range(index, len(self.__FcSorted)):
339 if MatchPathPrefix(self.__FcSorted[i].path, prefix):
ThiƩbaud Weksteenb75b4d22021-11-24 14:44:28 +1100340 PathType.append((self.__FcSorted[i].path, self.__FcSorted[i].type))
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800341 return PathType
342
343 # Return types that match MatchPrefixes but do not match
344 # DoNotMatchPrefixes
Steven Moreland7f116502020-11-03 23:18:32 +0000345 def __GetTypesAndFilesByFilePathPrefix(self, MatchPrefixes, DoNotMatchPrefixes):
Inseob Kim34d6c642023-09-27 17:32:26 +0900346 ret = []
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700347
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800348 MatchPrefixesWithIndex = []
349 for MatchPrefix in MatchPrefixes:
350 MatchPrefixesWithIndex.append(self.__FcSortedIndex(MatchPrefix))
351
352 for MatchPrefixWithIndex in MatchPrefixesWithIndex:
353 PathTypes = self.__MatchPathPrefixTypes(*MatchPrefixWithIndex)
354 for PathType in PathTypes:
355 if MatchPathPrefixes(PathType[0], DoNotMatchPrefixes):
356 continue
Inseob Kim34d6c642023-09-27 17:32:26 +0900357 ret.append(PathType)
358 return ret
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700359
Dan Cashman91d398d2017-09-26 12:58:29 -0700360 def __GetTERules(self, policydbP, avtabIterP, Rules):
361 if Rules is None:
362 Rules = set()
363 buf = create_string_buffer(self.__BUFSIZE)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700364 ret = 0
365 while True:
Dan Cashman91d398d2017-09-26 12:58:29 -0700366 ret = self.__libsepolwrap.get_allow_rule(buf, self.__BUFSIZE,
367 policydbP, avtabIterP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700368 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100369 Rule = TERule(buf.value.decode("ascii"))
Dan Cashman91d398d2017-09-26 12:58:29 -0700370 Rules.add(Rule)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700371 continue
372 if ret == 1:
373 break;
374 # We should never get here.
375 sys.exit("Failed to import policy")
376
377 def __InitTERules(self):
Dan Cashman91d398d2017-09-26 12:58:29 -0700378 avtabIterP = self.__libsepolwrap.init_avtab(self.__policydbP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700379 if (avtabIterP == None):
380 sys.exit("Failed to initialize avtab")
Dan Cashman91d398d2017-09-26 12:58:29 -0700381 self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
382 self.__libsepolwrap.destroy_avtab(avtabIterP)
383 avtabIterP = self.__libsepolwrap.init_cond_avtab(self.__policydbP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700384 if (avtabIterP == None):
385 sys.exit("Failed to initialize conditional avtab")
Dan Cashman91d398d2017-09-26 12:58:29 -0700386 self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
387 self.__libsepolwrap.destroy_avtab(avtabIterP)
388
389 def __InitExpandedTERules(self):
390 avtabIterP = self.__libsepolwrap.init_expanded_avtab(self.__policydbP)
391 if (avtabIterP == None):
392 sys.exit("Failed to initialize avtab")
393 self.__GetTERules(self.__policydbP, avtabIterP, self.__ExpandedRules)
394 self.__libsepolwrap.destroy_expanded_avtab(avtabIterP)
395 avtabIterP = self.__libsepolwrap.init_expanded_cond_avtab(self.__policydbP)
396 if (avtabIterP == None):
397 sys.exit("Failed to initialize conditional avtab")
398 self.__GetTERules(self.__policydbP, avtabIterP, self.__ExpandedRules)
399 self.__libsepolwrap.destroy_expanded_avtab(avtabIterP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700400
401 # load ctypes-ified libsepol wrapper
Jeff Vander Stoep1fc06822017-05-31 15:36:07 -0700402 def __InitLibsepolwrap(self, LibPath):
Jeff Vander Stoep3ca843a2017-10-04 09:42:29 -0700403 lib = CDLL(LibPath)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700404
Dan Cashman91d398d2017-09-26 12:58:29 -0700405 # int get_allow_rule(char *out, size_t len, void *policydbp, void *avtab_iterp);
406 lib.get_allow_rule.restype = c_int
407 lib.get_allow_rule.argtypes = [c_char_p, c_size_t, c_void_p, c_void_p];
408 # void *load_policy(const char *policy_path);
409 lib.load_policy.restype = c_void_p
410 lib.load_policy.argtypes = [c_char_p]
411 # void destroy_policy(void *policydbp);
412 lib.destroy_policy.argtypes = [c_void_p]
413 # void *init_expanded_avtab(void *policydbp);
414 lib.init_expanded_avtab.restype = c_void_p
415 lib.init_expanded_avtab.argtypes = [c_void_p]
416 # void *init_expanded_cond_avtab(void *policydbp);
417 lib.init_expanded_cond_avtab.restype = c_void_p
418 lib.init_expanded_cond_avtab.argtypes = [c_void_p]
419 # void destroy_expanded_avtab(void *avtab_iterp);
420 lib.destroy_expanded_avtab.argtypes = [c_void_p]
421 # void *init_avtab(void *policydbp);
422 lib.init_avtab.restype = c_void_p
423 lib.init_avtab.argtypes = [c_void_p]
424 # void *init_cond_avtab(void *policydbp);
425 lib.init_cond_avtab.restype = c_void_p
426 lib.init_cond_avtab.argtypes = [c_void_p]
427 # void destroy_avtab(void *avtab_iterp);
428 lib.destroy_avtab.argtypes = [c_void_p]
429 # int get_type(char *out, size_t max_size, void *policydbp, void *type_iterp);
430 lib.get_type.restype = c_int
431 lib.get_type.argtypes = [c_char_p, c_size_t, c_void_p, c_void_p]
432 # void *init_type_iter(void *policydbp, const char *type, bool is_attr);
433 lib.init_type_iter.restype = c_void_p
434 lib.init_type_iter.argtypes = [c_void_p, c_char_p, c_bool]
435 # void destroy_type_iter(void *type_iterp);
436 lib.destroy_type_iter.argtypes = [c_void_p]
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700437 # void *init_genfs_iter(void *policydbp)
438 lib.init_genfs_iter.restype = c_void_p
439 lib.init_genfs_iter.argtypes = [c_void_p]
440 # int get_genfs(char *out, size_t max_size, void *genfs_iterp);
441 lib.get_genfs.restype = c_int
442 lib.get_genfs.argtypes = [c_char_p, c_size_t, c_void_p, c_void_p]
443 # void destroy_genfs_iter(void *genfs_iterp)
444 lib.destroy_genfs_iter.argtypes = [c_void_p]
Dan Cashman91d398d2017-09-26 12:58:29 -0700445
446 self.__libsepolwrap = lib
447
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700448 def __GenfsDictAdd(self, Dict, buf):
Jonas 5 Perssonaa9d4212021-02-17 12:15:33 +0100449 fs, buf = buf.split(' ', 1)
450 path, context = buf.rsplit(' ', 1)
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700451 Type = context.split(":")[2]
452 if not fs in Dict:
453 Dict[fs] = {Type}
454 else:
455 Dict[fs].add(Type)
456
457 def __InitGenfsCon(self):
458 self.__GenfsDict = {}
459 GenfsIterP = self.__libsepolwrap.init_genfs_iter(self.__policydbP)
460 if (GenfsIterP == None):
461 sys.exit("Failed to retreive genfs entries")
462 buf = create_string_buffer(self.__BUFSIZE)
463 while True:
464 ret = self.__libsepolwrap.get_genfs(buf, self.__BUFSIZE,
465 self.__policydbP, GenfsIterP)
466 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100467 self.__GenfsDictAdd(self.__GenfsDict, buf.value.decode("ascii"))
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700468 continue
469 if ret == 1:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100470 self.__GenfsDictAdd(self.__GenfsDict, buf.value.decode("ascii"))
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700471 break;
472 # We should never get here.
473 sys.exit("Failed to get genfs entries")
474 self.__libsepolwrap.destroy_genfs_iter(GenfsIterP)
Dan Cashman91d398d2017-09-26 12:58:29 -0700475
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700476 # load file_contexts
477 def __InitFC(self, FcPaths):
Inseob Kimeb0d40a2023-09-04 19:02:53 +0900478 self.__FcDict = {}
Dan Cashman91d398d2017-09-26 12:58:29 -0700479 if FcPaths is None:
480 return
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700481 fc = []
482 for path in FcPaths:
483 if not os.path.exists(path):
484 sys.exit("file_contexts file " + path + " does not exist.")
485 fd = open(path, "r")
486 fc += fd.readlines()
487 fd.close()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700488 for i in fc:
489 rec = i.split()
490 try:
491 t = rec[-1].split(":")[2]
492 if t in self.__FcDict:
493 self.__FcDict[t].append(rec[0])
494 else:
495 self.__FcDict[t] = [rec[0]]
496 except:
497 pass
ThiƩbaud Weksteenb75b4d22021-11-24 14:44:28 +1100498 self.__FcSorted = fc_sort.sort(FcPaths)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700499
500 # load policy
501 def __InitPolicy(self, PolicyPath):
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100502 cPolicyPath = create_string_buffer(PolicyPath.encode("ascii"))
Dan Cashman91d398d2017-09-26 12:58:29 -0700503 self.__policydbP = self.__libsepolwrap.load_policy(cPolicyPath)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700504 if (self.__policydbP is None):
505 sys.exit("Failed to load policy")
506
Jeff Vander Stoep1fc06822017-05-31 15:36:07 -0700507 def __init__(self, PolicyPath, FcPaths, LibPath):
508 self.__InitLibsepolwrap(LibPath)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700509 self.__InitFC(FcPaths)
510 self.__InitPolicy(PolicyPath)
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700511 self.__InitGenfsCon()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700512
513 def __del__(self):
514 if self.__policydbP is not None:
Dan Cashman91d398d2017-09-26 12:58:29 -0700515 self.__libsepolwrap.destroy_policy(self.__policydbP)
Inseob Kimeb0d40a2023-09-04 19:02:53 +0900516
517coredomainAllowlist = {
518 # TODO: how do we make sure vendor_init doesn't have bad coupling with
519 # /vendor? It is the only system process which is not coredomain.
520 'vendor_init',
521 # TODO(b/152813275): need to avoid allowlist for rootdir
522 "modprobe",
523 "slideshow",
524 }
525
526class scontext:
527 def __init__(self):
528 self.fromSystem = False
529 self.fromVendor = False
530 self.coredomain = False
531 self.appdomain = False
532 self.attributes = set()
533 self.entrypoints = []
534 self.entrypointpaths = []
535 self.error = ""
536
537class TestPolicy:
538 """A policy loaded in memory with its domains easily accessible."""
539
540 def __init__(self):
541 self.alldomains = {}
542 self.coredomains = set()
543 self.appdomains = set()
544 self.vendordomains = set()
545 self.pol = None
546
547 # compat vars
548 self.alltypes = set()
549 self.oldalltypes = set()
550 self.compatMapping = None
551 self.pubtypes = set()
552
553 def GetAllDomains(self):
554 for result in self.pol.QueryTypeAttribute("domain", True):
555 self.alldomains[result] = scontext()
556
557 def GetAppDomains(self):
558 for d in self.alldomains:
559 # The application of the "appdomain" attribute is trusted because core
560 # selinux policy contains neverallow rules that enforce that only zygote
561 # and runas spawned processes may transition to processes that have
562 # the appdomain attribute.
563 if "appdomain" in self.alldomains[d].attributes:
564 self.alldomains[d].appdomain = True
565 self.appdomains.add(d)
566
567 def GetCoreDomains(self):
568 for d in self.alldomains:
569 domain = self.alldomains[d]
570 # TestCoredomainViolations will verify if coredomain was incorrectly
571 # applied.
572 if "coredomain" in domain.attributes:
573 domain.coredomain = True
574 self.coredomains.add(d)
575 # check whether domains are executed off of /system or /vendor
576 if d in coredomainAllowlist:
577 continue
578 # TODO(b/153112003): add checks to prevent app domains from being
579 # incorrectly labeled as coredomain. Apps don't have entrypoints as
580 # they're always dynamically transitioned to by zygote.
581 if d in self.appdomains:
582 continue
583 # TODO(b/153112747): need to handle cases where there is a dynamic
584 # transition OR there happens to be no context in AOSP files.
585 if not domain.entrypointpaths:
586 continue
587
588 for path in domain.entrypointpaths:
589 vendor = any(MatchPathPrefix(path, prefix) for prefix in
590 ["/vendor", "/odm"])
591 system = any(MatchPathPrefix(path, prefix) for prefix in
592 ["/init", "/system_ext", "/product" ])
593
594 # only mark entrypoint as system if it is not in legacy /system/vendor
595 if MatchPathPrefix(path, "/system/vendor"):
596 vendor = True
597 elif MatchPathPrefix(path, "/system"):
598 system = True
599
600 if not vendor and not system:
601 domain.error += "Unrecognized entrypoint for " + d + " at " + path + "\n"
602
603 domain.fromSystem = domain.fromSystem or system
604 domain.fromVendor = domain.fromVendor or vendor
605
606 ###
607 # Add the entrypoint type and path(s) to each domain.
608 #
609 def GetDomainEntrypoints(self):
610 for x in self.pol.QueryExpandedTERule(tclass=set(["file"]), perms=set(["entrypoint"])):
611 if not x.sctx in self.alldomains:
612 continue
613 self.alldomains[x.sctx].entrypoints.append(str(x.tctx))
614 # postinstall_file represents a special case specific to A/B OTAs.
615 # Update_engine mounts a partition and relabels it postinstall_file.
616 # There is no file_contexts entry associated with postinstall_file
617 # so skip the lookup.
618 if x.tctx == "postinstall_file":
619 continue
620 entrypointpath = self.pol.QueryFc(x.tctx)
621 if not entrypointpath:
622 continue
623 self.alldomains[x.sctx].entrypointpaths.extend(entrypointpath)
624
625 ###
626 # Get attributes associated with each domain
627 #
628 def GetAttributes(self):
629 for domain in self.alldomains:
630 for result in self.pol.QueryTypeAttribute(domain, False):
631 self.alldomains[domain].attributes.add(result)
632
633 def setup(self, pol):
634 self.pol = pol
635 self.GetAllDomains()
636 self.GetAttributes()
637 self.GetDomainEntrypoints()
638 self.GetAppDomains()
639 self.GetCoreDomains()
640
641 def GetAllTypes(self, basepol, oldpol):
642 self.alltypes = basepol.GetAllTypes(False)
643 self.oldalltypes = oldpol.GetAllTypes(False)
644
645 # setup for the policy compatibility tests
646 def compatSetup(self, basepol, oldpol, mapping, types):
647 self.GetAllTypes(basepol, oldpol)
648 self.compatMapping = mapping
649 self.pubtypes = types
650
651 def DomainsWithAttribute(self, attr):
652 domains = []
653 for domain in self.alldomains:
654 if attr in self.alldomains[domain].attributes:
655 domains.append(domain)
656 return domains
657
658 def PrintScontexts(self):
659 for d in sorted(self.alldomains.keys()):
660 sctx = self.alldomains[d]
661 print(d)
662 print("\tcoredomain="+str(sctx.coredomain))
663 print("\tappdomain="+str(sctx.appdomain))
664 print("\tfromSystem="+str(sctx.fromSystem))
665 print("\tfromVendor="+str(sctx.fromVendor))
666 print("\tattributes="+str(sctx.attributes))
667 print("\tentrypoints="+str(sctx.entrypoints))
668 print("\tentrypointpaths=")
669 if sctx.entrypointpaths is not None:
670 for path in sctx.entrypointpaths:
671 print("\t\t"+str(path))