1# 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
15from ctypes import *
16import re
17import os
18import sys
19import platform
20import fc_sort
21
22###
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#
33COMMON_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
40def MatchPathPrefix(pathregex, prefix):
41    # 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.
73    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
88class 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:
99    __ExpandedRules = set()
100    __Rules = set()
101    __FcDict = None
102    __FcSorted = None
103    __GenfsDict = None
104    __libsepolwrap = None
105    __policydbP = None
106    __BUFSIZE = 2048
107
108    def AssertPathTypesDoNotHaveAttr(self, MatchPrefix, DoNotMatchPrefix, Attr, ExcludedTypes = []):
109        # Query policy for the types associated with Attr
110        TypesPol = self.QueryTypeAttribute(Attr, True) - set(ExcludedTypes)
111        # Search file_contexts to find types associated with input paths.
112        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
119        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 "
124            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"
128        return ret
129
130    # 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
145    # 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.
149    def AssertPathTypesHaveAttr(self, MatchPrefix, DoNotMatchPrefix, Attr, ExcludedTypes = []):
150        # Query policy for the types associated with Attr
151        TypesPol = self.QueryTypeAttribute(Attr, True) | set(ExcludedTypes)
152        # Search file_contexts to find paths/types that should be associated with
153        # Attr.
154        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)))
160
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 "
166            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"
170        return ret
171
172    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
183    # 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):
193        TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP,
194                        create_string_buffer(Type.encode("ascii")), IsAttr)
195        if (TypeIterP == None):
196            sys.exit("Failed to initialize type iterator")
197        buf = create_string_buffer(self.__BUFSIZE)
198        TypeAttr = set()
199        while True:
200            ret = self.__libsepolwrap.get_type(buf, self.__BUFSIZE,
201                    self.__policydbP, TypeIterP)
202            if ret == 0:
203                TypeAttr.add(buf.value.decode("ascii"))
204                continue
205            if ret == 1:
206                break;
207            # We should never get here.
208            sys.exit("Failed to import policy")
209        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()
255
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):
265        if len(self.__Rules) == 0:
266            self.__InitTERules()
267
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)
273            if (len(scontext) == 0):
274                return []
275            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)
280            if (len(tcontext) == 0):
281                return []
282            kwargs['tcontext'] = tcontext
283        for Rule in self.__Rules:
284            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:
306                AllTypes.add(buf.value.decode("ascii"))
307                continue
308            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
315    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):
340                PathType.append((self.__FcSorted[i].path, self.__FcSorted[i].type))
341        return PathType
342
343    # Return types that match MatchPrefixes but do not match
344    # DoNotMatchPrefixes
345    def __GetTypesAndFilesByFilePathPrefix(self, MatchPrefixes, DoNotMatchPrefixes):
346        ret = []
347
348        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
357                ret.append(PathType)
358        return ret
359
360    def __GetTERules(self, policydbP, avtabIterP, Rules):
361        if Rules is None:
362            Rules = set()
363        buf = create_string_buffer(self.__BUFSIZE)
364        ret = 0
365        while True:
366            ret = self.__libsepolwrap.get_allow_rule(buf, self.__BUFSIZE,
367                        policydbP, avtabIterP)
368            if ret == 0:
369                Rule = TERule(buf.value.decode("ascii"))
370                Rules.add(Rule)
371                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):
378        avtabIterP = self.__libsepolwrap.init_avtab(self.__policydbP)
379        if (avtabIterP == None):
380            sys.exit("Failed to initialize avtab")
381        self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
382        self.__libsepolwrap.destroy_avtab(avtabIterP)
383        avtabIterP = self.__libsepolwrap.init_cond_avtab(self.__policydbP)
384        if (avtabIterP == None):
385            sys.exit("Failed to initialize conditional avtab")
386        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)
400
401    # load ctypes-ified libsepol wrapper
402    def __InitLibsepolwrap(self, LibPath):
403        lib = CDLL(LibPath)
404
405        # 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]
437        # 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]
445
446        self.__libsepolwrap = lib
447
448    def __GenfsDictAdd(self, Dict, buf):
449        fs, buf = buf.split(' ', 1)
450        path, context = buf.rsplit(' ', 1)
451        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:
467                self.__GenfsDictAdd(self.__GenfsDict, buf.value.decode("ascii"))
468                continue
469            if ret == 1:
470                self.__GenfsDictAdd(self.__GenfsDict, buf.value.decode("ascii"))
471                break;
472            # We should never get here.
473            sys.exit("Failed to get genfs entries")
474        self.__libsepolwrap.destroy_genfs_iter(GenfsIterP)
475
476    # load file_contexts
477    def __InitFC(self, FcPaths):
478        self.__FcDict = {}
479        if FcPaths is None:
480            return
481        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()
488        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
498        self.__FcSorted = fc_sort.sort(FcPaths)
499
500    # load policy
501    def __InitPolicy(self, PolicyPath):
502        cPolicyPath = create_string_buffer(PolicyPath.encode("ascii"))
503        self.__policydbP = self.__libsepolwrap.load_policy(cPolicyPath)
504        if (self.__policydbP is None):
505            sys.exit("Failed to load policy")
506
507    def __init__(self, PolicyPath, FcPaths, LibPath):
508        self.__InitLibsepolwrap(LibPath)
509        self.__InitFC(FcPaths)
510        self.__InitPolicy(PolicyPath)
511        self.__InitGenfsCon()
512
513    def __del__(self):
514        if self.__policydbP is not None:
515            self.__libsepolwrap.destroy_policy(self.__policydbP)
516
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))
672