1// Copyright (C) 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 15package selinux 16 17import ( 18 "os" 19 "sort" 20 "strconv" 21 "strings" 22 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26) 27 28const ( 29 MlsSens = 1 30 MlsCats = 1024 31 PolicyVers = 30 32) 33 34// This order should be kept. checkpolicy syntax requires it. 35var policyConfOrder = []string{ 36 "security_classes", 37 "initial_sids", 38 "access_vectors", 39 "global_macros", 40 "neverallow_macros", 41 "mls_macros", 42 "mls_decl", 43 "mls", 44 "policy_capabilities", 45 "te_macros", 46 "ioctl_defines", 47 "ioctl_macros", 48 "attributes|*.te", 49 "roles_decl", 50 "roles", 51 "users", 52 "initial_sid_contexts", 53 "fs_use", 54 "genfs_contexts", 55 "port_contexts", 56} 57 58func init() { 59 android.RegisterModuleType("se_policy_conf", policyConfFactory) 60 android.RegisterModuleType("se_policy_conf_defaults", policyConfDefaultFactory) 61 android.RegisterModuleType("se_policy_cil", policyCilFactory) 62 android.RegisterModuleType("se_policy_binary", policyBinaryFactory) 63} 64 65type policyConfProperties struct { 66 // Name of the output. Default is {module_name} 67 Stem *string 68 69 // Policy files to be compiled to cil file. 70 Srcs []string `android:"path"` 71 72 // Target build variant (user / userdebug / eng). Default follows the current lunch target 73 Build_variant *string 74 75 // Whether to exclude build test or not. Default is false 76 Exclude_build_test *bool 77 78 // Whether to include asan specific policies or not. Default follows the current lunch target 79 With_asan *bool 80 81 // Whether to build CTS specific policy or not. Default is false 82 Cts *bool 83 84 // Whether to build recovery specific policy or not. Default is false 85 Target_recovery *bool 86 87 // Whether this module is directly installable to one of the partitions. Default is true 88 Installable *bool 89 90 // Desired number of MLS categories. Defaults to 1024 91 Mls_cats *int64 92 93 // Whether to turn on board_api_level guard or not. Defaults to false 94 Board_api_level_guard *bool 95} 96 97type policyConf struct { 98 android.ModuleBase 99 android.DefaultableModuleBase 100 flaggableModuleBase 101 102 properties policyConfProperties 103 104 installSource android.Path 105 installPath android.InstallPath 106} 107 108var _ flaggableModule = (*policyConf)(nil) 109 110// se_policy_conf merges collection of policy files into a policy.conf file to be processed by 111// checkpolicy. 112func policyConfFactory() android.Module { 113 c := &policyConf{} 114 c.AddProperties(&c.properties) 115 initFlaggableModule(c) 116 android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) 117 android.InitDefaultableModule(c) 118 return c 119} 120 121type policyConfDefaults struct { 122 android.ModuleBase 123 android.DefaultsModuleBase 124} 125 126// se_policy_conf_defaults provides a set of properties that can be inherited by other 127// se_policy_conf_defaults modules. A module can use the properties from a se_policy_conf_defaults 128// using `defaults: ["<:default_module_name>"]`. Properties of both modules are merged (when 129// possible) by prepending the default module's values to the depending module's values. 130func policyConfDefaultFactory() android.Module { 131 c := &policyConfDefaults{} 132 c.AddProperties( 133 &policyConfProperties{}, 134 &flaggableModuleProperties{}, 135 ) 136 android.InitDefaultsModule(c) 137 return c 138} 139 140func (c *policyConf) installable() bool { 141 return proptools.BoolDefault(c.properties.Installable, true) 142} 143 144func (c *policyConf) stem() string { 145 return proptools.StringDefault(c.properties.Stem, c.Name()) 146} 147 148func (c *policyConf) buildVariant(ctx android.ModuleContext) string { 149 if variant := proptools.String(c.properties.Build_variant); variant != "" { 150 return variant 151 } 152 if ctx.Config().Eng() { 153 return "eng" 154 } 155 if ctx.Config().Debuggable() { 156 return "userdebug" 157 } 158 return "user" 159} 160 161func (c *policyConf) cts() bool { 162 return proptools.Bool(c.properties.Cts) 163} 164 165func (c *policyConf) isTargetRecovery() bool { 166 return proptools.Bool(c.properties.Target_recovery) 167} 168 169func (c *policyConf) withAsan(ctx android.ModuleContext) string { 170 isAsanDevice := android.InList("address", ctx.Config().SanitizeDevice()) 171 return strconv.FormatBool(proptools.BoolDefault(c.properties.With_asan, isAsanDevice)) 172} 173 174func (c *policyConf) sepolicySplit(ctx android.ModuleContext) string { 175 if c.cts() { 176 return "cts" 177 } 178 if c.isTargetRecovery() { 179 return "false" 180 } 181 return strconv.FormatBool(true) 182} 183 184func (c *policyConf) compatibleProperty(ctx android.ModuleContext) string { 185 if c.cts() { 186 return "cts" 187 } 188 if c.isTargetRecovery() { 189 return "false" 190 } 191 return "true" 192} 193 194func (c *policyConf) trebleSyspropNeverallow(ctx android.ModuleContext) string { 195 if c.cts() { 196 return "cts" 197 } 198 if c.isTargetRecovery() { 199 return "false" 200 } 201 return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenTrebleSyspropNeverallow()) 202} 203 204func (c *policyConf) enforceSyspropOwner(ctx android.ModuleContext) string { 205 if c.cts() { 206 return "cts" 207 } 208 if c.isTargetRecovery() { 209 return "false" 210 } 211 return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenEnforceSyspropOwner()) 212} 213 214func (c *policyConf) enforceDebugfsRestrictions(ctx android.ModuleContext) string { 215 if c.cts() { 216 return "cts" 217 } 218 return strconv.FormatBool(ctx.DeviceConfig().BuildDebugfsRestrictionsEnabled()) 219} 220 221func (c *policyConf) mlsCats() int { 222 return proptools.IntDefault(c.properties.Mls_cats, MlsCats) 223} 224 225func (c *policyConf) boardApiLevel(ctx android.ModuleContext) string { 226 if proptools.Bool(c.properties.Board_api_level_guard) { 227 return ctx.Config().VendorApiLevel() 228 } 229 // aribtrary value greater than any other vendor API levels 230 return "1000000" 231} 232 233func findPolicyConfOrder(name string) int { 234 for idx, pattern := range policyConfOrder { 235 // We could use regexp but it seems like an overkill 236 if pattern == "attributes|*.te" && (name == "attributes" || strings.HasSuffix(name, ".te")) { 237 return idx 238 } else if pattern == name { 239 return idx 240 } 241 } 242 // name is not matched 243 return len(policyConfOrder) 244} 245 246func (c *policyConf) transformPolicyToConf(ctx android.ModuleContext) android.OutputPath { 247 conf := pathForModuleOut(ctx, c.stem()) 248 rule := android.NewRuleBuilder(pctx, ctx) 249 250 srcs := android.PathsForModuleSrc(ctx, c.properties.Srcs) 251 sort.SliceStable(srcs, func(x, y int) bool { 252 return findPolicyConfOrder(srcs[x].Base()) < findPolicyConfOrder(srcs[y].Base()) 253 }) 254 255 flags := c.getBuildFlags(ctx) 256 rule.Command().Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")). 257 Flag("--fatal-warnings"). 258 FlagForEachArg("-D ", ctx.DeviceConfig().SepolicyM4Defs()). 259 FlagWithArg("-D mls_num_sens=", strconv.Itoa(MlsSens)). 260 FlagWithArg("-D mls_num_cats=", strconv.Itoa(c.mlsCats())). 261 FlagWithArg("-D target_arch=", ctx.DeviceConfig().DeviceArch()). 262 FlagWithArg("-D target_with_asan=", c.withAsan(ctx)). 263 FlagWithArg("-D target_with_dexpreopt=", strconv.FormatBool(ctx.DeviceConfig().WithDexpreopt())). 264 FlagWithArg("-D target_with_native_coverage=", strconv.FormatBool(ctx.DeviceConfig().ClangCoverageEnabled() || ctx.DeviceConfig().GcovCoverageEnabled())). 265 FlagWithArg("-D target_build_variant=", c.buildVariant(ctx)). 266 FlagWithArg("-D target_full_treble=", c.sepolicySplit(ctx)). 267 FlagWithArg("-D target_compatible_property=", c.compatibleProperty(ctx)). 268 FlagWithArg("-D target_treble_sysprop_neverallow=", c.trebleSyspropNeverallow(ctx)). 269 FlagWithArg("-D target_enforce_sysprop_owner=", c.enforceSyspropOwner(ctx)). 270 FlagWithArg("-D target_exclude_build_test=", strconv.FormatBool(proptools.Bool(c.properties.Exclude_build_test))). 271 FlagWithArg("-D target_requires_insecure_execmem_for_swiftshader=", strconv.FormatBool(ctx.DeviceConfig().RequiresInsecureExecmemForSwiftshader())). 272 FlagWithArg("-D target_enforce_debugfs_restriction=", c.enforceDebugfsRestrictions(ctx)). 273 FlagWithArg("-D target_recovery=", strconv.FormatBool(c.isTargetRecovery())). 274 FlagWithArg("-D target_board_api_level=", c.boardApiLevel(ctx)). 275 Flags(flagsToM4Macros(flags)). 276 Flag("-s"). 277 Inputs(srcs). 278 Text("> ").Output(conf) 279 280 rule.Build("conf", "Transform policy to conf: "+ctx.ModuleName()) 281 return conf 282} 283 284func (c *policyConf) DepsMutator(ctx android.BottomUpMutatorContext) { 285 c.flagDeps(ctx) 286} 287 288func (c *policyConf) GenerateAndroidBuildActions(ctx android.ModuleContext) { 289 if !c.installable() { 290 c.SkipInstall() 291 } 292 293 c.installSource = c.transformPolicyToConf(ctx) 294 c.installPath = android.PathForModuleInstall(ctx, "etc") 295 ctx.InstallFile(c.installPath, c.stem(), c.installSource) 296 297 ctx.SetOutputFiles(android.Paths{c.installSource}, "") 298} 299 300func (c *policyConf) AndroidMkEntries() []android.AndroidMkEntries { 301 return []android.AndroidMkEntries{android.AndroidMkEntries{ 302 OutputFile: android.OptionalPathForPath(c.installSource), 303 Class: "ETC", 304 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 305 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 306 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.installable()) 307 entries.SetPath("LOCAL_MODULE_PATH", c.installPath) 308 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) 309 }, 310 }, 311 }} 312} 313 314type policyCilProperties struct { 315 // Name of the output. Default is {module_name} 316 Stem *string 317 318 // Policy file to be compiled to cil file. 319 Src *string `android:"path"` 320 321 // If true, the input policy file is a binary policy that will be decompiled to a cil file. 322 // Defaults to false. 323 Decompile_binary *bool 324 325 // Additional cil files to be added in the end of the output. This is to support workarounds 326 // which are not supported by the policy language. 327 Additional_cil_files []string `android:"path"` 328 329 // Cil files to be filtered out by the filter_out tool of "build_sepolicy". Used to build 330 // exported policies 331 Filter_out []string `android:"path"` 332 333 // Whether to remove line markers (denoted by ;;) out of compiled cil files. Defaults to false 334 Remove_line_marker *bool 335 336 // Whether to run secilc to check compiled policy or not. Defaults to true 337 Secilc_check *bool 338 339 // Whether to ignore neverallow when running secilc check. Defaults to 340 // SELINUX_IGNORE_NEVERALLOWS. 341 Ignore_neverallow *bool 342 343 // Whether this module is directly installable to one of the partitions. Default is true 344 Installable *bool 345} 346 347type policyCil struct { 348 android.ModuleBase 349 350 properties policyCilProperties 351 352 installSource android.Path 353 installPath android.InstallPath 354} 355 356// se_policy_cil compiles a policy.conf file to a cil file with checkpolicy, and optionally runs 357// secilc to check the output cil file. Affected by SELINUX_IGNORE_NEVERALLOWS. 358func policyCilFactory() android.Module { 359 c := &policyCil{} 360 c.AddProperties(&c.properties) 361 android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) 362 return c 363} 364 365func (c *policyCil) Installable() bool { 366 return proptools.BoolDefault(c.properties.Installable, true) 367} 368 369func (c *policyCil) stem() string { 370 return proptools.StringDefault(c.properties.Stem, c.Name()) 371} 372 373func (c *policyCil) compileConfToCil(ctx android.ModuleContext, conf android.Path) android.OutputPath { 374 cil := pathForModuleOut(ctx, c.stem()) 375 rule := android.NewRuleBuilder(pctx, ctx) 376 checkpolicyCmd := rule.Command().BuiltTool("checkpolicy"). 377 Flag("-C"). // Write CIL 378 Flag("-M"). // Enable MLS 379 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 380 FlagWithOutput("-o ", cil). 381 Input(conf) 382 383 if proptools.Bool(c.properties.Decompile_binary) { 384 checkpolicyCmd.Flag("-b") // Read binary 385 } 386 387 if len(c.properties.Filter_out) > 0 { 388 rule.Command().BuiltTool("build_sepolicy"). 389 Text("filter_out"). 390 Flag("-f"). 391 Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). 392 FlagWithOutput("-t ", cil) 393 } 394 395 if len(c.properties.Additional_cil_files) > 0 { 396 rule.Command().Text("cat"). 397 Inputs(android.PathsForModuleSrc(ctx, c.properties.Additional_cil_files)). 398 Text(">> ").Output(cil) 399 } 400 401 if proptools.Bool(c.properties.Remove_line_marker) { 402 rule.Command().Text("grep -v"). 403 Text(proptools.ShellEscape(";;")). 404 Text(cil.String()). 405 Text(">"). 406 Text(cil.String() + ".tmp"). 407 Text("&& mv"). 408 Text(cil.String() + ".tmp"). 409 Text(cil.String()) 410 } 411 412 if proptools.BoolDefault(c.properties.Secilc_check, true) { 413 secilcCmd := rule.Command().BuiltTool("secilc"). 414 Flag("-m"). // Multiple decls 415 FlagWithArg("-M ", "true"). // Enable MLS 416 Flag("-G"). // expand and remove auto generated attributes 417 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 418 Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). // Also add cil files which are filtered out 419 Text(cil.String()). 420 FlagWithArg("-o ", os.DevNull). 421 FlagWithArg("-f ", os.DevNull) 422 423 if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) { 424 secilcCmd.Flag("-N") 425 } 426 } 427 428 rule.Build("cil", "Building cil for "+ctx.ModuleName()) 429 return cil 430} 431 432func (c *policyCil) GenerateAndroidBuildActions(ctx android.ModuleContext) { 433 if proptools.String(c.properties.Src) == "" { 434 ctx.PropertyErrorf("src", "must be specified") 435 return 436 } 437 conf := android.PathForModuleSrc(ctx, *c.properties.Src) 438 cil := c.compileConfToCil(ctx, conf) 439 440 if !c.Installable() { 441 c.SkipInstall() 442 } 443 444 if c.InstallInDebugRamdisk() { 445 // for userdebug_plat_sepolicy.cil 446 c.installPath = android.PathForModuleInstall(ctx) 447 } else { 448 c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") 449 } 450 c.installSource = cil 451 ctx.InstallFile(c.installPath, c.stem(), c.installSource) 452 453 ctx.SetOutputFiles(android.Paths{c.installSource}, "") 454} 455 456func (c *policyCil) AndroidMkEntries() []android.AndroidMkEntries { 457 return []android.AndroidMkEntries{android.AndroidMkEntries{ 458 OutputFile: android.OptionalPathForPath(c.installSource), 459 Class: "ETC", 460 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 461 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 462 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable()) 463 entries.SetPath("LOCAL_MODULE_PATH", c.installPath) 464 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) 465 }, 466 }, 467 }} 468} 469 470type policyBinaryProperties struct { 471 // Name of the output. Default is {module_name} 472 Stem *string 473 474 // Cil files to be compiled. 475 Srcs []string `android:"path"` 476 477 // Whether to ignore neverallow when running secilc check. Defaults to 478 // SELINUX_IGNORE_NEVERALLOWS. 479 Ignore_neverallow *bool 480 481 // Whether this module is directly installable to one of the partitions. Default is true 482 Installable *bool 483 484 // List of domains that are allowed to be in permissive mode on user builds. 485 Permissive_domains_on_user_builds []string 486} 487 488type policyBinary struct { 489 android.ModuleBase 490 491 properties policyBinaryProperties 492 493 installSource android.Path 494 installPath android.InstallPath 495} 496 497// se_policy_binary compiles cil files to a binary sepolicy file with secilc. Usually sources of 498// se_policy_binary come from outputs of se_policy_cil modules. 499func policyBinaryFactory() android.Module { 500 c := &policyBinary{} 501 c.AddProperties(&c.properties) 502 android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) 503 return c 504} 505 506func (c *policyBinary) InstallInRoot() bool { 507 return c.InstallInRecovery() 508} 509 510func (c *policyBinary) Installable() bool { 511 return proptools.BoolDefault(c.properties.Installable, true) 512} 513 514func (c *policyBinary) stem() string { 515 return proptools.StringDefault(c.properties.Stem, c.Name()) 516} 517 518func (c *policyBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) { 519 if len(c.properties.Srcs) == 0 { 520 ctx.PropertyErrorf("srcs", "must be specified") 521 return 522 } 523 bin := pathForModuleOut(ctx, c.stem()+"_policy") 524 rule := android.NewRuleBuilder(pctx, ctx) 525 secilcCmd := rule.Command().BuiltTool("secilc"). 526 Flag("-m"). // Multiple decls 527 FlagWithArg("-M ", "true"). // Enable MLS 528 Flag("-G"). // expand and remove auto generated attributes 529 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 530 Inputs(android.PathsForModuleSrc(ctx, c.properties.Srcs)). 531 FlagWithOutput("-o ", bin). 532 FlagWithArg("-f ", os.DevNull) 533 534 if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) { 535 secilcCmd.Flag("-N") 536 } 537 rule.Temporary(bin) 538 539 // permissive check is performed only in user build (not debuggable). 540 if !ctx.Config().Debuggable() { 541 permissiveDomains := pathForModuleOut(ctx, c.stem()+"_permissive") 542 cmd := rule.Command().BuiltTool("sepolicy-analyze"). 543 Input(bin). 544 Text("permissive") 545 // Filter-out domains listed in permissive_domains_on_user_builds 546 allowedDomains := c.properties.Permissive_domains_on_user_builds 547 if len(allowedDomains) != 0 { 548 cmd.Text("| { grep -Fxv") 549 for _, d := range allowedDomains { 550 cmd.FlagWithArg("-e ", proptools.ShellEscape(d)) 551 } 552 cmd.Text(" || true; }") // no match doesn't fail the cmd 553 } 554 cmd.Text(" > ").Output(permissiveDomains) 555 rule.Temporary(permissiveDomains) 556 557 msg := `==========\n` + 558 `ERROR: permissive domains not allowed in user builds\n` + 559 `List of invalid domains:` 560 561 rule.Command().Text("if test"). 562 FlagWithInput("-s ", permissiveDomains). 563 Text("; then echo"). 564 Flag("-e"). 565 Text(`"` + msg + `"`). 566 Text("&& cat "). 567 Input(permissiveDomains). 568 Text("; exit 1; fi") 569 } 570 571 out := pathForModuleOut(ctx, c.stem()) 572 rule.Command().Text("cp"). 573 Flag("-f"). 574 Input(bin). 575 Output(out) 576 577 rule.DeleteTemporaryFiles() 578 rule.Build("secilc", "Compiling cil files for "+ctx.ModuleName()) 579 580 if !c.Installable() { 581 c.SkipInstall() 582 } 583 584 if c.InstallInRecovery() { 585 // install in root 586 c.installPath = android.PathForModuleInstall(ctx) 587 } else { 588 c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") 589 } 590 c.installSource = out 591 ctx.InstallFile(c.installPath, c.stem(), c.installSource) 592 593 ctx.SetOutputFiles(android.Paths{c.installSource}, "") 594} 595 596func (c *policyBinary) AndroidMkEntries() []android.AndroidMkEntries { 597 return []android.AndroidMkEntries{android.AndroidMkEntries{ 598 OutputFile: android.OptionalPathForPath(c.installSource), 599 Class: "ETC", 600 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 601 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 602 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable()) 603 entries.SetPath("LOCAL_MODULE_PATH", c.installPath) 604 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) 605 }, 606 }, 607 }} 608} 609