blob: 9fdc43c07c741611a6ab235aed7e10e63775e03d [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.
Steven Moreland7f116502020-11-03 23:18:32 +0000112 TypesFc, Files = self.__GetTypesAndFilesByFilePathPrefix(MatchPrefix, DoNotMatchPrefix)
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800113 violators = TypesFc.intersection(TypesPol)
114 ret = ""
115 if len(violators) > 0:
116 ret += "The following types on "
117 ret += " ".join(str(x) for x in sorted(MatchPrefix))
118 ret += " must not be associated with the "
119 ret += "\"" + Attr + "\" attribute: "
120 ret += " ".join(str(x) for x in sorted(violators)) + "\n"
Steven Moreland7f116502020-11-03 23:18:32 +0000121 ret += " corresponding to files: "
122 ret += " ".join(str(x) for x in sorted(Files)) + "\n"
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800123 return ret
124
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700125 # Check that all types for "filesystem" have "attribute" associated with them
126 # for types labeled in genfs_contexts.
127 def AssertGenfsFilesystemTypesHaveAttr(self, Filesystem, Attr):
128 TypesPol = self.QueryTypeAttribute(Attr, True)
129 TypesGenfs = self.__GenfsDict[Filesystem]
130 violators = TypesGenfs.difference(TypesPol)
131
132 ret = ""
133 if len(violators) > 0:
134 ret += "The following types in " + Filesystem
135 ret += " must be associated with the "
136 ret += "\"" + Attr + "\" attribute: "
137 ret += " ".join(str(x) for x in sorted(violators)) + "\n"
138 return ret
139
Dan Cashman91d398d2017-09-26 12:58:29 -0700140 # Check that path prefixes that match MatchPrefix, and do not Match
141 # DoNotMatchPrefix have the attribute Attr.
142 # For example assert that all types in /sys, and not in /sys/kernel/debugfs
143 # have the sysfs_type attribute.
144 def AssertPathTypesHaveAttr(self, MatchPrefix, DoNotMatchPrefix, Attr):
145 # Query policy for the types associated with Attr
146 TypesPol = self.QueryTypeAttribute(Attr, True)
147 # Search file_contexts to find paths/types that should be associated with
148 # Attr.
Steven Moreland7f116502020-11-03 23:18:32 +0000149 TypesFc, Files = self.__GetTypesAndFilesByFilePathPrefix(MatchPrefix, DoNotMatchPrefix)
Dan Cashman91d398d2017-09-26 12:58:29 -0700150 violators = TypesFc.difference(TypesPol)
151
152 ret = ""
153 if len(violators) > 0:
154 ret += "The following types on "
155 ret += " ".join(str(x) for x in sorted(MatchPrefix))
156 ret += " must be associated with the "
157 ret += "\"" + Attr + "\" attribute: "
158 ret += " ".join(str(x) for x in sorted(violators)) + "\n"
Steven Moreland7f116502020-11-03 23:18:32 +0000159 ret += " corresponding to files: "
160 ret += " ".join(str(x) for x in sorted(Files)) + "\n"
Dan Cashman91d398d2017-09-26 12:58:29 -0700161 return ret
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700162
Inseob Kim1b8b1f62020-10-23 15:16:11 +0900163 def AssertPropertyOwnersAreExclusive(self):
164 systemProps = self.QueryTypeAttribute('system_property_type', True)
165 vendorProps = self.QueryTypeAttribute('vendor_property_type', True)
166 violators = systemProps.intersection(vendorProps)
167 ret = ""
168 if len(violators) > 0:
169 ret += "The following types have both system_property_type "
170 ret += "and vendor_property_type: "
171 ret += " ".join(str(x) for x in sorted(violators)) + "\n"
172 return ret
173
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700174 # Return all file_contexts entries that map to the input Type.
175 def QueryFc(self, Type):
176 if Type in self.__FcDict:
177 return self.__FcDict[Type]
178 else:
179 return None
180
181 # Return all attributes associated with a type if IsAttr=False or
182 # all types associated with an attribute if IsAttr=True
183 def QueryTypeAttribute(self, Type, IsAttr):
Dan Cashman91d398d2017-09-26 12:58:29 -0700184 TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP,
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100185 create_string_buffer(Type.encode("ascii")), IsAttr)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700186 if (TypeIterP == None):
187 sys.exit("Failed to initialize type iterator")
Dan Cashman91d398d2017-09-26 12:58:29 -0700188 buf = create_string_buffer(self.__BUFSIZE)
189 TypeAttr = set()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700190 while True:
Dan Cashman91d398d2017-09-26 12:58:29 -0700191 ret = self.__libsepolwrap.get_type(buf, self.__BUFSIZE,
192 self.__policydbP, TypeIterP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700193 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100194 TypeAttr.add(buf.value.decode("ascii"))
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700195 continue
196 if ret == 1:
197 break;
198 # We should never get here.
199 sys.exit("Failed to import policy")
Dan Cashman91d398d2017-09-26 12:58:29 -0700200 self.__libsepolwrap.destroy_type_iter(TypeIterP)
201 return TypeAttr
202
203 def __TERuleMatch(self, Rule, **kwargs):
204 # Match source type
205 if ("scontext" in kwargs and
206 len(kwargs['scontext']) > 0 and
207 Rule.sctx not in kwargs['scontext']):
208 return False
209 # Match target type
210 if ("tcontext" in kwargs and
211 len(kwargs['tcontext']) > 0 and
212 Rule.tctx not in kwargs['tcontext']):
213 return False
214 # Match target class
215 if ("tclass" in kwargs and
216 len(kwargs['tclass']) > 0 and
217 not bool(set([Rule.tclass]) & kwargs['tclass'])):
218 return False
219 # Match any perms
220 if ("perms" in kwargs and
221 len(kwargs['perms']) > 0 and
222 not bool(Rule.perms & kwargs['perms'])):
223 return False
224 return True
225
226 # resolve a type to its attributes or
227 # resolve an attribute to its types and attributes
228 # For example if scontext is the domain attribute, then we need to
229 # include all types with the domain attribute such as untrusted_app and
230 # priv_app and all the attributes of those types such as appdomain.
231 def ResolveTypeAttribute(self, Type):
232 types = self.GetAllTypes(False)
233 attributes = self.GetAllTypes(True)
234
235 if Type in types:
236 return self.QueryTypeAttribute(Type, False)
237 elif Type in attributes:
238 TypesAndAttributes = set()
239 Types = self.QueryTypeAttribute(Type, True)
240 TypesAndAttributes |= Types
241 for T in Types:
242 TypesAndAttributes |= self.QueryTypeAttribute(T, False)
243 return TypesAndAttributes
244 else:
245 return set()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700246
247 # Return all TERules that match:
248 # (any scontext) or (any tcontext) or (any tclass) or (any perms),
249 # perms.
250 # Any unspecified paramenter will match all.
251 #
252 # Example: QueryTERule(tcontext=["foo", "bar"], perms=["entrypoint"])
253 # Will return any rule with:
254 # (tcontext="foo" or tcontext="bar") and ("entrypoint" in perms)
255 def QueryTERule(self, **kwargs):
Dan Cashman91d398d2017-09-26 12:58:29 -0700256 if len(self.__Rules) == 0:
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700257 self.__InitTERules()
Dan Cashman91d398d2017-09-26 12:58:29 -0700258
259 # add any matching types and attributes for scontext and tcontext
260 if ("scontext" in kwargs and len(kwargs['scontext']) > 0):
261 scontext = set()
262 for sctx in kwargs['scontext']:
263 scontext |= self.ResolveTypeAttribute(sctx)
Sandro26152e92022-08-05 11:35:39 +0000264 if (len(scontext) == 0):
265 return []
Dan Cashman91d398d2017-09-26 12:58:29 -0700266 kwargs['scontext'] = scontext
267 if ("tcontext" in kwargs and len(kwargs['tcontext']) > 0):
268 tcontext = set()
269 for tctx in kwargs['tcontext']:
270 tcontext |= self.ResolveTypeAttribute(tctx)
Sandro26152e92022-08-05 11:35:39 +0000271 if (len(tcontext) == 0):
272 return []
Dan Cashman91d398d2017-09-26 12:58:29 -0700273 kwargs['tcontext'] = tcontext
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700274 for Rule in self.__Rules:
Dan Cashman91d398d2017-09-26 12:58:29 -0700275 if self.__TERuleMatch(Rule, **kwargs):
276 yield Rule
277
278 # Same as QueryTERule but only using the expanded ruleset.
279 # i.e. all attributes have been expanded to their various types.
280 def QueryExpandedTERule(self, **kwargs):
281 if len(self.__ExpandedRules) == 0:
282 self.__InitExpandedTERules()
283 for Rule in self.__ExpandedRules:
284 if self.__TERuleMatch(Rule, **kwargs):
285 yield Rule
286
287 def GetAllTypes(self, isAttr):
288 TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP, None, isAttr)
289 if (TypeIterP == None):
290 sys.exit("Failed to initialize type iterator")
291 buf = create_string_buffer(self.__BUFSIZE)
292 AllTypes = set()
293 while True:
294 ret = self.__libsepolwrap.get_type(buf, self.__BUFSIZE,
295 self.__policydbP, TypeIterP)
296 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100297 AllTypes.add(buf.value.decode("ascii"))
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700298 continue
Dan Cashman91d398d2017-09-26 12:58:29 -0700299 if ret == 1:
300 break;
301 # We should never get here.
302 sys.exit("Failed to import policy")
303 self.__libsepolwrap.destroy_type_iter(TypeIterP)
304 return AllTypes
305
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800306 def __ExactMatchPathPrefix(self, pathregex, prefix):
307 pattern = re.compile('^' + pathregex + "$")
308 if pattern.match(prefix):
309 return True
310 return False
311
312 # Return a tuple (prefix, i) where i is the index of the most specific
313 # match of prefix in the sorted file_contexts. This is useful for limiting a
314 # file_contexts search to matches that are more specific and omitting less
315 # specific matches. For example, finding all matches to prefix /data/vendor
316 # should not include /data(/.*)? if /data/vendor(/.*)? is also specified.
317 def __FcSortedIndex(self, prefix):
318 index = 0
319 for i in range(0, len(self.__FcSorted)):
320 if self.__ExactMatchPathPrefix(self.__FcSorted[i].path, prefix):
321 index = i
322 return prefix, index
323
324 # Return a tuple of (path, Type) for all matching paths. Use the sorted
325 # file_contexts and index returned from __FcSortedIndex() to limit results
326 # to results that are more specific than the prefix.
327 def __MatchPathPrefixTypes(self, prefix, index):
328 PathType = []
329 for i in range(index, len(self.__FcSorted)):
330 if MatchPathPrefix(self.__FcSorted[i].path, prefix):
ThiƩbaud Weksteenb75b4d22021-11-24 14:44:28 +1100331 PathType.append((self.__FcSorted[i].path, self.__FcSorted[i].type))
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800332 return PathType
333
334 # Return types that match MatchPrefixes but do not match
335 # DoNotMatchPrefixes
Steven Moreland7f116502020-11-03 23:18:32 +0000336 def __GetTypesAndFilesByFilePathPrefix(self, MatchPrefixes, DoNotMatchPrefixes):
Dan Cashman91d398d2017-09-26 12:58:29 -0700337 Types = set()
Steven Moreland7f116502020-11-03 23:18:32 +0000338 Files = set()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700339
Jeff Vander Stoep370a52f2018-02-08 09:54:59 -0800340 MatchPrefixesWithIndex = []
341 for MatchPrefix in MatchPrefixes:
342 MatchPrefixesWithIndex.append(self.__FcSortedIndex(MatchPrefix))
343
344 for MatchPrefixWithIndex in MatchPrefixesWithIndex:
345 PathTypes = self.__MatchPathPrefixTypes(*MatchPrefixWithIndex)
346 for PathType in PathTypes:
347 if MatchPathPrefixes(PathType[0], DoNotMatchPrefixes):
348 continue
349 Types.add(PathType[1])
Steven Moreland7f116502020-11-03 23:18:32 +0000350 Files.add(PathType[0])
351 return Types, Files
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700352
Dan Cashman91d398d2017-09-26 12:58:29 -0700353 def __GetTERules(self, policydbP, avtabIterP, Rules):
354 if Rules is None:
355 Rules = set()
356 buf = create_string_buffer(self.__BUFSIZE)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700357 ret = 0
358 while True:
Dan Cashman91d398d2017-09-26 12:58:29 -0700359 ret = self.__libsepolwrap.get_allow_rule(buf, self.__BUFSIZE,
360 policydbP, avtabIterP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700361 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100362 Rule = TERule(buf.value.decode("ascii"))
Dan Cashman91d398d2017-09-26 12:58:29 -0700363 Rules.add(Rule)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700364 continue
365 if ret == 1:
366 break;
367 # We should never get here.
368 sys.exit("Failed to import policy")
369
370 def __InitTERules(self):
Dan Cashman91d398d2017-09-26 12:58:29 -0700371 avtabIterP = self.__libsepolwrap.init_avtab(self.__policydbP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700372 if (avtabIterP == None):
373 sys.exit("Failed to initialize avtab")
Dan Cashman91d398d2017-09-26 12:58:29 -0700374 self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
375 self.__libsepolwrap.destroy_avtab(avtabIterP)
376 avtabIterP = self.__libsepolwrap.init_cond_avtab(self.__policydbP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700377 if (avtabIterP == None):
378 sys.exit("Failed to initialize conditional avtab")
Dan Cashman91d398d2017-09-26 12:58:29 -0700379 self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
380 self.__libsepolwrap.destroy_avtab(avtabIterP)
381
382 def __InitExpandedTERules(self):
383 avtabIterP = self.__libsepolwrap.init_expanded_avtab(self.__policydbP)
384 if (avtabIterP == None):
385 sys.exit("Failed to initialize avtab")
386 self.__GetTERules(self.__policydbP, avtabIterP, self.__ExpandedRules)
387 self.__libsepolwrap.destroy_expanded_avtab(avtabIterP)
388 avtabIterP = self.__libsepolwrap.init_expanded_cond_avtab(self.__policydbP)
389 if (avtabIterP == None):
390 sys.exit("Failed to initialize conditional avtab")
391 self.__GetTERules(self.__policydbP, avtabIterP, self.__ExpandedRules)
392 self.__libsepolwrap.destroy_expanded_avtab(avtabIterP)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700393
394 # load ctypes-ified libsepol wrapper
Jeff Vander Stoep1fc06822017-05-31 15:36:07 -0700395 def __InitLibsepolwrap(self, LibPath):
Jeff Vander Stoep3ca843a2017-10-04 09:42:29 -0700396 lib = CDLL(LibPath)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700397
Dan Cashman91d398d2017-09-26 12:58:29 -0700398 # int get_allow_rule(char *out, size_t len, void *policydbp, void *avtab_iterp);
399 lib.get_allow_rule.restype = c_int
400 lib.get_allow_rule.argtypes = [c_char_p, c_size_t, c_void_p, c_void_p];
401 # void *load_policy(const char *policy_path);
402 lib.load_policy.restype = c_void_p
403 lib.load_policy.argtypes = [c_char_p]
404 # void destroy_policy(void *policydbp);
405 lib.destroy_policy.argtypes = [c_void_p]
406 # void *init_expanded_avtab(void *policydbp);
407 lib.init_expanded_avtab.restype = c_void_p
408 lib.init_expanded_avtab.argtypes = [c_void_p]
409 # void *init_expanded_cond_avtab(void *policydbp);
410 lib.init_expanded_cond_avtab.restype = c_void_p
411 lib.init_expanded_cond_avtab.argtypes = [c_void_p]
412 # void destroy_expanded_avtab(void *avtab_iterp);
413 lib.destroy_expanded_avtab.argtypes = [c_void_p]
414 # void *init_avtab(void *policydbp);
415 lib.init_avtab.restype = c_void_p
416 lib.init_avtab.argtypes = [c_void_p]
417 # void *init_cond_avtab(void *policydbp);
418 lib.init_cond_avtab.restype = c_void_p
419 lib.init_cond_avtab.argtypes = [c_void_p]
420 # void destroy_avtab(void *avtab_iterp);
421 lib.destroy_avtab.argtypes = [c_void_p]
422 # int get_type(char *out, size_t max_size, void *policydbp, void *type_iterp);
423 lib.get_type.restype = c_int
424 lib.get_type.argtypes = [c_char_p, c_size_t, c_void_p, c_void_p]
425 # void *init_type_iter(void *policydbp, const char *type, bool is_attr);
426 lib.init_type_iter.restype = c_void_p
427 lib.init_type_iter.argtypes = [c_void_p, c_char_p, c_bool]
428 # void destroy_type_iter(void *type_iterp);
429 lib.destroy_type_iter.argtypes = [c_void_p]
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700430 # void *init_genfs_iter(void *policydbp)
431 lib.init_genfs_iter.restype = c_void_p
432 lib.init_genfs_iter.argtypes = [c_void_p]
433 # int get_genfs(char *out, size_t max_size, void *genfs_iterp);
434 lib.get_genfs.restype = c_int
435 lib.get_genfs.argtypes = [c_char_p, c_size_t, c_void_p, c_void_p]
436 # void destroy_genfs_iter(void *genfs_iterp)
437 lib.destroy_genfs_iter.argtypes = [c_void_p]
Dan Cashman91d398d2017-09-26 12:58:29 -0700438
439 self.__libsepolwrap = lib
440
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700441 def __GenfsDictAdd(self, Dict, buf):
Jonas 5 Perssonaa9d4212021-02-17 12:15:33 +0100442 fs, buf = buf.split(' ', 1)
443 path, context = buf.rsplit(' ', 1)
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700444 Type = context.split(":")[2]
445 if not fs in Dict:
446 Dict[fs] = {Type}
447 else:
448 Dict[fs].add(Type)
449
450 def __InitGenfsCon(self):
451 self.__GenfsDict = {}
452 GenfsIterP = self.__libsepolwrap.init_genfs_iter(self.__policydbP)
453 if (GenfsIterP == None):
454 sys.exit("Failed to retreive genfs entries")
455 buf = create_string_buffer(self.__BUFSIZE)
456 while True:
457 ret = self.__libsepolwrap.get_genfs(buf, self.__BUFSIZE,
458 self.__policydbP, GenfsIterP)
459 if ret == 0:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100460 self.__GenfsDictAdd(self.__GenfsDict, buf.value.decode("ascii"))
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700461 continue
462 if ret == 1:
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100463 self.__GenfsDictAdd(self.__GenfsDict, buf.value.decode("ascii"))
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700464 break;
465 # We should never get here.
466 sys.exit("Failed to get genfs entries")
467 self.__libsepolwrap.destroy_genfs_iter(GenfsIterP)
Dan Cashman91d398d2017-09-26 12:58:29 -0700468
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700469 # load file_contexts
470 def __InitFC(self, FcPaths):
Inseob Kimeb0d40a2023-09-04 19:02:53 +0900471 self.__FcDict = {}
Dan Cashman91d398d2017-09-26 12:58:29 -0700472 if FcPaths is None:
473 return
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700474 fc = []
475 for path in FcPaths:
476 if not os.path.exists(path):
477 sys.exit("file_contexts file " + path + " does not exist.")
478 fd = open(path, "r")
479 fc += fd.readlines()
480 fd.close()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700481 for i in fc:
482 rec = i.split()
483 try:
484 t = rec[-1].split(":")[2]
485 if t in self.__FcDict:
486 self.__FcDict[t].append(rec[0])
487 else:
488 self.__FcDict[t] = [rec[0]]
489 except:
490 pass
ThiƩbaud Weksteenb75b4d22021-11-24 14:44:28 +1100491 self.__FcSorted = fc_sort.sort(FcPaths)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700492
493 # load policy
494 def __InitPolicy(self, PolicyPath):
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +1100495 cPolicyPath = create_string_buffer(PolicyPath.encode("ascii"))
Dan Cashman91d398d2017-09-26 12:58:29 -0700496 self.__policydbP = self.__libsepolwrap.load_policy(cPolicyPath)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700497 if (self.__policydbP is None):
498 sys.exit("Failed to load policy")
499
Jeff Vander Stoep1fc06822017-05-31 15:36:07 -0700500 def __init__(self, PolicyPath, FcPaths, LibPath):
501 self.__InitLibsepolwrap(LibPath)
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700502 self.__InitFC(FcPaths)
503 self.__InitPolicy(PolicyPath)
Jeff Vander Stoep1b828442018-03-21 17:27:20 -0700504 self.__InitGenfsCon()
Jeff Vander Stoepbdfc0302017-05-25 09:53:47 -0700505
506 def __del__(self):
507 if self.__policydbP is not None:
Dan Cashman91d398d2017-09-26 12:58:29 -0700508 self.__libsepolwrap.destroy_policy(self.__policydbP)
Inseob Kimeb0d40a2023-09-04 19:02:53 +0900509
510coredomainAllowlist = {
511 # TODO: how do we make sure vendor_init doesn't have bad coupling with
512 # /vendor? It is the only system process which is not coredomain.
513 'vendor_init',
514 # TODO(b/152813275): need to avoid allowlist for rootdir
515 "modprobe",
516 "slideshow",
517 }
518
519class scontext:
520 def __init__(self):
521 self.fromSystem = False
522 self.fromVendor = False
523 self.coredomain = False
524 self.appdomain = False
525 self.attributes = set()
526 self.entrypoints = []
527 self.entrypointpaths = []
528 self.error = ""
529
530class TestPolicy:
531 """A policy loaded in memory with its domains easily accessible."""
532
533 def __init__(self):
534 self.alldomains = {}
535 self.coredomains = set()
536 self.appdomains = set()
537 self.vendordomains = set()
538 self.pol = None
539
540 # compat vars
541 self.alltypes = set()
542 self.oldalltypes = set()
543 self.compatMapping = None
544 self.pubtypes = set()
545
546 def GetAllDomains(self):
547 for result in self.pol.QueryTypeAttribute("domain", True):
548 self.alldomains[result] = scontext()
549
550 def GetAppDomains(self):
551 for d in self.alldomains:
552 # The application of the "appdomain" attribute is trusted because core
553 # selinux policy contains neverallow rules that enforce that only zygote
554 # and runas spawned processes may transition to processes that have
555 # the appdomain attribute.
556 if "appdomain" in self.alldomains[d].attributes:
557 self.alldomains[d].appdomain = True
558 self.appdomains.add(d)
559
560 def GetCoreDomains(self):
561 for d in self.alldomains:
562 domain = self.alldomains[d]
563 # TestCoredomainViolations will verify if coredomain was incorrectly
564 # applied.
565 if "coredomain" in domain.attributes:
566 domain.coredomain = True
567 self.coredomains.add(d)
568 # check whether domains are executed off of /system or /vendor
569 if d in coredomainAllowlist:
570 continue
571 # TODO(b/153112003): add checks to prevent app domains from being
572 # incorrectly labeled as coredomain. Apps don't have entrypoints as
573 # they're always dynamically transitioned to by zygote.
574 if d in self.appdomains:
575 continue
576 # TODO(b/153112747): need to handle cases where there is a dynamic
577 # transition OR there happens to be no context in AOSP files.
578 if not domain.entrypointpaths:
579 continue
580
581 for path in domain.entrypointpaths:
582 vendor = any(MatchPathPrefix(path, prefix) for prefix in
583 ["/vendor", "/odm"])
584 system = any(MatchPathPrefix(path, prefix) for prefix in
585 ["/init", "/system_ext", "/product" ])
586
587 # only mark entrypoint as system if it is not in legacy /system/vendor
588 if MatchPathPrefix(path, "/system/vendor"):
589 vendor = True
590 elif MatchPathPrefix(path, "/system"):
591 system = True
592
593 if not vendor and not system:
594 domain.error += "Unrecognized entrypoint for " + d + " at " + path + "\n"
595
596 domain.fromSystem = domain.fromSystem or system
597 domain.fromVendor = domain.fromVendor or vendor
598
599 ###
600 # Add the entrypoint type and path(s) to each domain.
601 #
602 def GetDomainEntrypoints(self):
603 for x in self.pol.QueryExpandedTERule(tclass=set(["file"]), perms=set(["entrypoint"])):
604 if not x.sctx in self.alldomains:
605 continue
606 self.alldomains[x.sctx].entrypoints.append(str(x.tctx))
607 # postinstall_file represents a special case specific to A/B OTAs.
608 # Update_engine mounts a partition and relabels it postinstall_file.
609 # There is no file_contexts entry associated with postinstall_file
610 # so skip the lookup.
611 if x.tctx == "postinstall_file":
612 continue
613 entrypointpath = self.pol.QueryFc(x.tctx)
614 if not entrypointpath:
615 continue
616 self.alldomains[x.sctx].entrypointpaths.extend(entrypointpath)
617
618 ###
619 # Get attributes associated with each domain
620 #
621 def GetAttributes(self):
622 for domain in self.alldomains:
623 for result in self.pol.QueryTypeAttribute(domain, False):
624 self.alldomains[domain].attributes.add(result)
625
626 def setup(self, pol):
627 self.pol = pol
628 self.GetAllDomains()
629 self.GetAttributes()
630 self.GetDomainEntrypoints()
631 self.GetAppDomains()
632 self.GetCoreDomains()
633
634 def GetAllTypes(self, basepol, oldpol):
635 self.alltypes = basepol.GetAllTypes(False)
636 self.oldalltypes = oldpol.GetAllTypes(False)
637
638 # setup for the policy compatibility tests
639 def compatSetup(self, basepol, oldpol, mapping, types):
640 self.GetAllTypes(basepol, oldpol)
641 self.compatMapping = mapping
642 self.pubtypes = types
643
644 def DomainsWithAttribute(self, attr):
645 domains = []
646 for domain in self.alldomains:
647 if attr in self.alldomains[domain].attributes:
648 domains.append(domain)
649 return domains
650
651 def PrintScontexts(self):
652 for d in sorted(self.alldomains.keys()):
653 sctx = self.alldomains[d]
654 print(d)
655 print("\tcoredomain="+str(sctx.coredomain))
656 print("\tappdomain="+str(sctx.appdomain))
657 print("\tfromSystem="+str(sctx.fromSystem))
658 print("\tfromVendor="+str(sctx.fromVendor))
659 print("\tattributes="+str(sctx.attributes))
660 print("\tentrypoints="+str(sctx.entrypoints))
661 print("\tentrypointpaths=")
662 if sctx.entrypointpaths is not None:
663 for path in sctx.entrypointpaths:
664 print("\t\t"+str(path))