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