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