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 optparse import OptionParser 16from optparse import Option, OptionValueError 17import os 18import pkgutil 19import policy 20import re 21import shutil 22import sys 23import tempfile 24 25SHARED_LIB_EXTENSION = '.dylib' if sys.platform == 'darwin' else '.so' 26 27############################################################# 28# Tests 29############################################################# 30def TestDataTypeViolations(pol): 31 return pol.AssertPathTypesHaveAttr(["/data/"], [], "data_file_type") 32 33def TestSystemTypeViolations(pol): 34 partitions = ["/system/", "/system_ext/", "/product/"] 35 exceptions = [ 36 # devices before treble don't have a vendor partition 37 "/system/vendor/", 38 39 # overlay files are mounted over vendor 40 "/product/overlay/", 41 "/product/vendor_overlay/", 42 "/system/overlay/", 43 "/system/product/overlay/", 44 "/system/product/vendor_overlay/", 45 "/system/system_ext/overlay/", 46 "/system_ext/overlay/", 47 ] 48 49 return pol.AssertPathTypesHaveAttr(partitions, exceptions, "system_file_type") 50 51def TestBpffsTypeViolations(pol): 52 return pol.AssertGenfsFilesystemTypesHaveAttr("bpf", "bpffs_type") 53 54def TestProcTypeViolations(pol): 55 return pol.AssertGenfsFilesystemTypesHaveAttr("proc", "proc_type") 56 57def TestSysfsTypeViolations(pol): 58 ret = pol.AssertGenfsFilesystemTypesHaveAttr("sysfs", "sysfs_type") 59 ret += pol.AssertPathTypesHaveAttr(["/sys/"], ["/sys/kernel/debug/", 60 "/sys/kernel/tracing"], "sysfs_type") 61 return ret 62 63def TestDebugfsTypeViolations(pol): 64 ret = pol.AssertGenfsFilesystemTypesHaveAttr("debugfs", "debugfs_type") 65 ret += pol.AssertPathTypesHaveAttr(["/sys/kernel/debug/", 66 "/sys/kernel/tracing"], [], "debugfs_type") 67 return ret 68 69def TestTracefsTypeViolations(pol): 70 ret = pol.AssertGenfsFilesystemTypesHaveAttr("tracefs", "tracefs_type") 71 ret += pol.AssertPathTypesHaveAttr(["/sys/kernel/tracing"], [], "tracefs_type") 72 ret += pol.AssertPathTypesDoNotHaveAttr(["/sys/kernel/debug"], 73 ["/sys/kernel/debug/tracing"], "tracefs_type", 74 []) 75 return ret 76 77def TestVendorTypeViolations(pol): 78 partitions = ["/vendor/", "/odm/"] 79 exceptions = [ 80 "/vendor/etc/selinux/", 81 "/vendor/odm/etc/selinux/", 82 "/odm/etc/selinux/", 83 ] 84 return pol.AssertPathTypesHaveAttr(partitions, exceptions, "vendor_file_type") 85 86def TestCoreDataTypeViolations(pol): 87 ret = pol.AssertPathTypesHaveAttr(["/data/"], ["/data/vendor", 88 "/data/vendor_ce", "/data/vendor_de"], "core_data_file_type") 89 ret += pol.AssertPathTypesDoNotHaveAttr(["/data/vendor/", "/data/vendor_ce/", 90 "/data/vendor_de/"], [], "core_data_file_type") 91 return ret 92 93def TestPropertyTypeViolations(pol): 94 return pol.AssertPropertyOwnersAreExclusive() 95 96def TestAppDataTypeViolations(pol): 97 # Types with the app_data_file_type should only be used for app data files 98 # (/data/data/package.name etc) via seapp_contexts, and never applied 99 # explicitly to other files. 100 partitions = [ 101 "/data/", 102 "/vendor/", 103 "/odm/", 104 "/product/", 105 ] 106 exceptions = [ 107 # These are used for app data files for the corresponding user and 108 # assorted other files. 109 # TODO(b/172812577): Use different types for the different purposes 110 "shell_data_file", 111 "bluetooth_data_file", 112 "nfc_data_file", 113 "radio_data_file", 114 ] 115 return pol.AssertPathTypesDoNotHaveAttr(partitions, [], "app_data_file_type", 116 exceptions) 117def TestDmaHeapDevTypeViolations(pol): 118 return pol.AssertPathTypesHaveAttr(["/dev/dma_heap/"], [], 119 "dmabuf_heap_device_type") 120 121def TestCoredomainViolations(test_policy): 122 # verify that all domains launched from /system have the coredomain 123 # attribute 124 ret = "" 125 126 for d in test_policy.alldomains: 127 domain = test_policy.alldomains[d] 128 if domain.fromSystem and domain.fromVendor: 129 ret += "The following domain is system and vendor: " + d + "\n" 130 131 for domain in test_policy.alldomains.values(): 132 ret += domain.error 133 134 violators = [] 135 for d in test_policy.alldomains: 136 domain = test_policy.alldomains[d] 137 if domain.fromSystem and "coredomain" not in domain.attributes: 138 violators.append(d); 139 if len(violators) > 0: 140 ret += "The following domain(s) must be associated with the " 141 ret += "\"coredomain\" attribute because they are executed off of " 142 ret += "/system:\n" 143 ret += " ".join(str(x) for x in sorted(violators)) + "\n" 144 145 # verify that all domains launched form /vendor do not have the coredomain 146 # attribute 147 violators = [] 148 for d in test_policy.alldomains: 149 domain = test_policy.alldomains[d] 150 if domain.fromVendor and "coredomain" in domain.attributes: 151 violators.append(d) 152 if len(violators) > 0: 153 ret += "The following domains must not be associated with the " 154 ret += "\"coredomain\" attribute because they are executed off of " 155 ret += "/vendor or /system/vendor:\n" 156 ret += " ".join(str(x) for x in sorted(violators)) + "\n" 157 158 return ret 159 160def TestViolatorAttribute(test_policy, attribute): 161 # TODO(b/113124961): re-enable once all violator attributes are removed. 162 return "" 163 164 # ret = "" 165 # return ret 166 167 # violators = test_policy.DomainsWithAttribute(attribute) 168 # if len(violators) > 0: 169 # ret += "SELinux: The following domains violate the Treble ban " 170 # ret += "against use of the " + attribute + " attribute: " 171 # ret += " ".join(str(x) for x in sorted(violators)) + "\n" 172 # return ret 173 174def TestViolatorAttributes(test_policy): 175 ret = "" 176 ret += TestViolatorAttribute(test_policy, "socket_between_core_and_vendor_violators") 177 ret += TestViolatorAttribute(test_policy, "vendor_executes_system_violators") 178 return ret 179 180def TestIsolatedAttributeConsistency(test_policy): 181 permissionAllowList = { 182 # access given from technical_debt.cil 183 "codec2_config_prop" : ["file"], 184 "device_config_nnapi_native_prop":["file"], 185 "hal_allocator_default":["binder", "fd"], 186 "hal_codec2": ["binder", "fd"], 187 "hal_codec2_hwservice":["hwservice_manager"], 188 "hal_graphics_allocator": ["binder", "fd"], 189 "hal_graphics_allocator_service":["service_manager"], 190 "hal_graphics_allocator_hwservice":["hwservice_manager"], 191 "hal_graphics_allocator_server":["binder", "service_manager"], 192 "hal_graphics_mapper_hwservice":["hwservice_manager"], 193 "hal_graphics_mapper_service":["service_manager"], 194 "hal_neuralnetworks": ["binder", "fd"], 195 "hal_neuralnetworks_service": ["service_manager"], 196 "hal_neuralnetworks_hwservice":["hwservice_manager"], 197 "hal_omx_hwservice":["hwservice_manager"], 198 "hidl_allocator_hwservice":["hwservice_manager"], 199 "hidl_manager_hwservice":["hwservice_manager"], 200 "hidl_memory_hwservice":["hwservice_manager"], 201 "hidl_token_hwservice":["hwservice_manager"], 202 "hwservicemanager":["binder"], 203 "hwservicemanager_prop":["file"], 204 "mediacodec":["binder", "fd"], 205 "mediaswcodec":["binder", "fd"], 206 "media_variant_prop":["file"], 207 "nnapi_ext_deny_product_prop":["file"], 208 "servicemanager":["fd"], 209 "toolbox_exec": ["file"], 210 # extra types being granted to isolated_compute_app 211 "isolated_compute_allowed":["service_manager", "chr_file"], 212 } 213 214 def resolveHalServerSubtype(target): 215 # permission given as a client in technical_debt.cil 216 hal_server_attributes = [ 217 "hal_codec2_server", 218 "hal_graphics_allocator_server", 219 "hal_neuralnetworks_server"] 220 221 for attr in hal_server_attributes: 222 if target in test_policy.pol.QueryTypeAttribute(Type=attr, IsAttr=True): 223 return attr.rsplit("_", 1)[0] 224 return target 225 226 def checkIsolatedComputeAllowed(tctx, tclass): 227 # check if the permission is in isolated_compute_allowed 228 allowedMemberTypes = test_policy.pol.QueryTypeAttribute(Type="isolated_compute_allowed_service", IsAttr=True) \ 229 .union(test_policy.pol.QueryTypeAttribute(Type="isolated_compute_allowed_device", IsAttr=True)) 230 return tctx in allowedMemberTypes and tclass in permissionAllowList["isolated_compute_allowed"] 231 232 def checkPermissions(permissions): 233 violated_permissions = [] 234 for perm in permissions: 235 tctx, tclass, p = perm.split(":") 236 tctx = resolveHalServerSubtype(tctx) 237 # check unwanted permissions 238 if not checkIsolatedComputeAllowed(tctx, tclass) and \ 239 ( tctx not in permissionAllowList \ 240 or tclass not in permissionAllowList[tctx] \ 241 or ( p == "write") \ 242 or ( p == "rw_file_perms") ): 243 violated_permissions += [perm] 244 return violated_permissions 245 246 ret = "" 247 248 isolatedMemberTypes = test_policy.pol.QueryTypeAttribute(Type="isolated_app_all", IsAttr=True) 249 baseRules = test_policy.pol.QueryExpandedTERule(scontext=["isolated_app"]) 250 basePermissionSet = set([":".join([rule.tctx, rule.tclass, perm]) 251 for rule in baseRules for perm in rule.perms]) 252 for subType in isolatedMemberTypes: 253 if subType == "isolated_app" : continue 254 currentTypeRule = test_policy.pol.QueryExpandedTERule(scontext=[subType]) 255 typePermissionSet = set([":".join([rule.tctx, rule.tclass, perm]) 256 for rule in currentTypeRule for perm in rule.perms 257 if not rule.tctx in [subType, subType + "_userfaultfd"]]) 258 deltaPermissionSet = typePermissionSet.difference(basePermissionSet) 259 violated_permissions = checkPermissions(list(deltaPermissionSet)) 260 for perm in violated_permissions: 261 ret += "allow %s %s:%s %s \n" % (subType, *perm.split(":")) 262 263 if ret: 264 ret = ("Found prohibited permission granted for isolated like types. " + \ 265 "Please replace your allow statements that involve \"-isolated_app\" with " + \ 266 "\"-isolated_app_all\". Violations are shown as the following: \n") + ret 267 return ret 268 269def TestDevTypeViolations(pol): 270 exceptions = [ 271 "/dev/socket", 272 ] 273 exceptionTypes = [ 274 "boringssl_self_test_marker", # /dev/boringssl/selftest 275 "cgroup_rc_file", # /dev/cgroup.rc 276 "dev_cpu_variant", # /dev/cpu_variant:{arch} 277 "fscklogs", # /dev/fscklogs 278 "properties_serial", # /dev/__properties__/properties_serial 279 "property_info", # /dev/__properties__/property_info 280 "runtime_event_log_tags_file", # /dev/event-log-tags 281 ] 282 return pol.AssertPathTypesHaveAttr(["/dev"], exceptions, 283 "dev_type", exceptionTypes) 284 285### 286# extend OptionParser to allow the same option flag to be used multiple times. 287# This is used to allow multiple file_contexts files and tests to be 288# specified. 289# 290class MultipleOption(Option): 291 ACTIONS = Option.ACTIONS + ("extend",) 292 STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) 293 TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) 294 ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",) 295 296 def take_action(self, action, dest, opt, value, values, parser): 297 if action == "extend": 298 values.ensure_value(dest, []).append(value) 299 else: 300 Option.take_action(self, action, dest, opt, value, values, parser) 301 302TEST_NAMES = [ name for name in dir() if name.startswith('Test') ] 303 304def do_main(libpath): 305 """ 306 Args: 307 libpath: string, path to libsepolwrap.so 308 """ 309 usage = "sepolicy_tests -f vendor_file_contexts -f " 310 usage +="plat_file_contexts -p policy [--test test] [--help]" 311 parser = OptionParser(option_class=MultipleOption, usage=usage) 312 parser.add_option("-f", "--file_contexts", dest="file_contexts", 313 metavar="FILE", action="extend", type="string") 314 parser.add_option("-p", "--policy", dest="policy", metavar="FILE") 315 parser.add_option("-t", "--test", dest="test", action="extend", 316 help="Test options include "+str(TEST_NAMES)) 317 318 (options, args) = parser.parse_args() 319 320 if not options.policy: 321 sys.exit("Must specify monolithic policy file\n" + parser.usage) 322 if not os.path.exists(options.policy): 323 sys.exit("Error: policy file " + options.policy + " does not exist\n" 324 + parser.usage) 325 326 if not options.file_contexts: 327 sys.exit("Error: Must specify file_contexts file(s)\n" + parser.usage) 328 for f in options.file_contexts: 329 if not os.path.exists(f): 330 sys.exit("Error: File_contexts file " + f + " does not exist\n" + 331 parser.usage) 332 333 pol = policy.Policy(options.policy, options.file_contexts, libpath) 334 test_policy = policy.TestPolicy() 335 test_policy.setup(pol) 336 337 results = "" 338 # If an individual test is not specified, run all tests. 339 if options.test is None or "TestBpffsTypeViolations" in options.test: 340 results += TestBpffsTypeViolations(pol) 341 if options.test is None or "TestDataTypeViolations" in options.test: 342 results += TestDataTypeViolations(pol) 343 if options.test is None or "TestProcTypeViolations" in options.test: 344 results += TestProcTypeViolations(pol) 345 if options.test is None or "TestSysfsTypeViolations" in options.test: 346 results += TestSysfsTypeViolations(pol) 347 if options.test is None or "TestSystemTypeViolations" in options.test: 348 results += TestSystemTypeViolations(pol) 349 if options.test is None or "TestDebugfsTypeViolations" in options.test: 350 results += TestDebugfsTypeViolations(pol) 351 if options.test is None or "TestTracefsTypeViolations" in options.test: 352 results += TestTracefsTypeViolations(pol) 353 if options.test is None or "TestVendorTypeViolations" in options.test: 354 results += TestVendorTypeViolations(pol) 355 if options.test is None or "TestCoreDataTypeViolations" in options.test: 356 results += TestCoreDataTypeViolations(pol) 357 if options.test is None or "TestPropertyTypeViolations" in options.test: 358 results += TestPropertyTypeViolations(pol) 359 if options.test is None or "TestAppDataTypeViolations" in options.test: 360 results += TestAppDataTypeViolations(pol) 361 if options.test is None or "TestDmaHeapDevTypeViolations" in options.test: 362 results += TestDmaHeapDevTypeViolations(pol) 363 if options.test is None or "TestCoredomainViolations" in options.test: 364 results += TestCoredomainViolations(test_policy) 365 if options.test is None or "TestViolatorAttributes" in options.test: 366 results += TestViolatorAttributes(test_policy) 367 if options.test is None or "TestIsolatedAttributeConsistency" in options.test: 368 results += TestIsolatedAttributeConsistency(test_policy) 369 370 # dev type test won't be run as default 371 if options.test and "TestDevTypeViolations" in options.test: 372 results += TestDevTypeViolations(pol) 373 374 if len(results) > 0: 375 sys.exit(results) 376 377if __name__ == '__main__': 378 temp_dir = tempfile.mkdtemp() 379 try: 380 libname = "libsepolwrap" + SHARED_LIB_EXTENSION 381 libpath = os.path.join(temp_dir, libname) 382 with open(libpath, "wb") as f: 383 blob = pkgutil.get_data("sepolicy_tests", libname) 384 if not blob: 385 sys.exit("Error: libsepolwrap does not exist. Is this binary corrupted?\n") 386 f.write(blob) 387 do_main(libpath) 388 finally: 389 shutil.rmtree(temp_dir) 390