"src/git@gitlab.cs.fau.de:i4refit/framework.git" did not exist on "bb77bc0c45b405e1fec6f690b897f67e065f0fe3"
Newer
Older
from optparse import OptionParser
from optparse import Option, OptionValueError
import os
import policy
from policy import MatchPathPrefix
import re
import sys
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
'''
Use file_contexts and policy to verify Treble requirements
are not violated.
'''
###
# Differentiate between domains that are part of the core Android platform and
# domains introduced by vendors
coreAppdomain = {
'bluetooth',
'ephemeral_app',
'isolated_app',
'nfc',
'platform_app',
'priv_app',
'radio',
'shared_relro',
'shell',
'system_app',
'untrusted_app',
'untrusted_app_25',
'untrusted_v2_app',
}
coredomainWhitelist = {
'adbd',
'kernel',
'postinstall',
'postinstall_dexopt',
'recovery',
'system_server',
}
coredomainWhitelist |= coreAppdomain
class scontext:
def __init__(self):
self.fromSystem = False
self.fromVendor = False
self.coredomain = False
self.appdomain = False
self.attributes = set()
self.entrypoints = []
self.entrypointpaths = []
def PrintScontexts():
for d in sorted(alldomains.keys()):
sctx = alldomains[d]
print d
print "\tcoredomain="+str(sctx.coredomain)
print "\tappdomain="+str(sctx.appdomain)
print "\tfromSystem="+str(sctx.fromSystem)
print "\tfromVendor="+str(sctx.fromVendor)
print "\tattributes="+str(sctx.attributes)
print "\tentrypoints="+str(sctx.entrypoints)
print "\tentrypointpaths="
if sctx.entrypointpaths is not None:
for path in sctx.entrypointpaths:
print "\t\t"+str(path)
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
alldomains = {}
coredomains = set()
appdomains = set()
vendordomains = set()
def GetAllDomains(pol):
global alldomains
for result in pol.QueryTypeAttribute("domain", True):
alldomains[result] = scontext()
def GetAppDomains():
global appdomains
global alldomains
for d in alldomains:
# The application of the "appdomain" attribute is trusted because core
# selinux policy contains neverallow rules that enforce that only zygote
# and runas spawned processes may transition to processes that have
# the appdomain attribute.
if "appdomain" in alldomains[d].attributes:
alldomains[d].appdomain = True
appdomains.add(d)
def GetCoreDomains():
global alldomains
global coredomains
for d in alldomains:
# TestCoredomainViolators will verify if coredomain was incorrectly
# applied.
if "coredomain" in alldomains[d].attributes:
alldomains[d].coredomain = True
coredomains.add(d)
# check whether domains are executed off of /system or /vendor
if d in coredomainWhitelist:
continue
# TODO, add checks to prevent app domains from being incorrectly
# labeled as coredomain. Apps don't have entrypoints as they're always
# dynamically transitioned to by zygote.
if d in appdomains:
continue
if not alldomains[d].entrypointpaths:
continue
for path in alldomains[d].entrypointpaths:
# Processes with entrypoint on /system
if ((MatchPathPrefix(path, "/system") and not
MatchPathPrefix(path, "/system/vendor")) or
MatchPathPrefix(path, "/init") or
MatchPathPrefix(path, "/charger")):
alldomains[d].fromSystem = True
# Processes with entrypoint on /vendor or /system/vendor
if (MatchPathPrefix(path, "/vendor") or
MatchPathPrefix(path, "/system/vendor")):
alldomains[d].fromVendor = True
###
# Add the entrypoint type and path(s) to each domain.
#
def GetDomainEntrypoints(pol):
global alldomains
for x in pol.QueryTERule(tclass="file", perms=["entrypoint"]):
if not x.sctx in alldomains:
continue
alldomains[x.sctx].entrypoints.append(str(x.tctx))
# postinstall_file represents a special case specific to A/B OTAs.
# Update_engine mounts a partition and relabels it postinstall_file.
# There is no file_contexts entry associated with postinstall_file
# so skip the lookup.
if x.tctx == "postinstall_file":
continue
entrypointpath = pol.QueryFc(x.tctx)
if not entrypointpath:
continue
alldomains[x.sctx].entrypointpaths.extend(entrypointpath)
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
###
# Get attributes associated with each domain
#
def GetAttributes(pol):
global alldomains
for domain in alldomains:
for result in pol.QueryTypeAttribute(domain, False):
alldomains[domain].attributes.add(result)
def setup(pol):
GetAllDomains(pol)
GetAttributes(pol)
GetDomainEntrypoints(pol)
GetAppDomains()
GetCoreDomains()
#############################################################
# Tests
#############################################################
def TestCoredomainViolations():
global alldomains
# verify that all domains launched from /system have the coredomain
# attribute
ret = ""
violators = []
for d in alldomains:
domain = alldomains[d]
if domain.fromSystem and "coredomain" not in domain.attributes:
violators.append(d);
if len(violators) > 0:
ret += "The following domain(s) must be associated with the "
ret += "\"coredomain\" attribute because they are executed off of "
ret += "/system:\n"
ret += " ".join(str(x) for x in sorted(violators)) + "\n"
# verify that all domains launched form /vendor do not have the coredomain
# attribute
violators = []
for d in alldomains:
domain = alldomains[d]
if domain.fromVendor and "coredomain" in domain.attributes:
violators.append(d)
if len(violators) > 0:
ret += "The following domains must not be associated with the "
ret += "\"coredomain\" attribute because they are executed off of "
ret += "/vendor or /system/vendor:\n"
ret += " ".join(str(x) for x in sorted(violators)) + "\n"
return ret
###
# extend OptionParser to allow the same option flag to be used multiple times.
# This is used to allow multiple file_contexts files and tests to be
# specified.
#
class MultipleOption(Option):
ACTIONS = Option.ACTIONS + ("extend",)
STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)
def take_action(self, action, dest, opt, value, values, parser):
if action == "extend":
values.ensure_value(dest, []).append(value)
else:
Option.take_action(self, action, dest, opt, value, values, parser)
Tests = ["CoredomainViolators"]
if __name__ == '__main__':
usage = "treble_sepolicy_tests.py -f nonplat_file_contexts -f "
usage +="plat_file_contexts -p policy [--test test] [--help]"
parser = OptionParser(option_class=MultipleOption, usage=usage)
parser.add_option("-f", "--file_contexts", dest="file_contexts",
metavar="FILE", action="extend", type="string")
parser.add_option("-p", "--policy", dest="policy", metavar="FILE")
parser.add_option("-l", "--library-path", dest="libpath", metavar="FILE")
parser.add_option("-t", "--test", dest="test", action="extend",
help="Test options include "+str(Tests))
(options, args) = parser.parse_args()
if not options.libpath:
sys.exit("Must specify path to host libraries\n" + parser.usage)
if not os.path.exists(options.libpath):
sys.exit("Error: library-path " + options.libpath + " does not exist\n"
+ parser.usage)
if not options.policy:
sys.exit("Must specify monolithic policy file\n" + parser.usage)
if not os.path.exists(options.policy):
sys.exit("Error: policy file " + options.policy + " does not exist\n"
+ parser.usage)
if not options.file_contexts:
sys.exit("Error: Must specify file_contexts file(s)\n" + parser.usage)
for f in options.file_contexts:
if not os.path.exists(f):
sys.exit("Error: File_contexts file " + f + " does not exist\n" +
parser.usage)
pol = policy.Policy(options.policy, options.file_contexts, options.libpath)
if DEBUG:
PrintScontexts()
results = ""
# If an individual test is not specified, run all tests.
if options.test is None or "CoredomainViolations" in options.tests:
results += TestCoredomainViolations()
if len(results) > 0:
sys.exit(results)