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 run secilc to check compiled policy or not. Defaults to true 328 Secilc_check *bool 329 330 // Whether to ignore neverallow when running secilc check. Defaults to 331 // SELINUX_IGNORE_NEVERALLOWS. 332 Ignore_neverallow *bool 333 334 // Whether this module is directly installable to one of the partitions. Default is true 335 Installable *bool 336} 337 338type policyCil struct { 339 android.ModuleBase 340 341 properties policyCilProperties 342 343 installSource android.Path 344 installPath android.InstallPath 345} 346 347// se_policy_cil compiles a policy.conf file to a cil file with checkpolicy, and optionally runs 348// secilc to check the output cil file. Affected by SELINUX_IGNORE_NEVERALLOWS. 349func policyCilFactory() android.Module { 350 c := &policyCil{} 351 c.AddProperties(&c.properties) 352 android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) 353 return c 354} 355 356func (c *policyCil) Installable() bool { 357 return proptools.BoolDefault(c.properties.Installable, true) 358} 359 360func (c *policyCil) stem() string { 361 return proptools.StringDefault(c.properties.Stem, c.Name()) 362} 363 364func (c *policyCil) compileConfToCil(ctx android.ModuleContext, conf android.Path) android.OutputPath { 365 cil := pathForModuleOut(ctx, c.stem()) 366 rule := android.NewRuleBuilder(pctx, ctx) 367 checkpolicyCmd := rule.Command().BuiltTool("checkpolicy"). 368 Flag("-C"). // Write CIL 369 Flag("-M"). // Enable MLS 370 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 371 FlagWithOutput("-o ", cil). 372 Input(conf) 373 374 if proptools.Bool(c.properties.Decompile_binary) { 375 checkpolicyCmd.Flag("-b") // Read binary 376 } 377 378 if len(c.properties.Filter_out) > 0 { 379 rule.Command().BuiltTool("build_sepolicy"). 380 Text("filter_out"). 381 Flag("-f"). 382 Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). 383 FlagWithOutput("-t ", cil) 384 } 385 386 if len(c.properties.Additional_cil_files) > 0 { 387 rule.Command().Text("cat"). 388 Inputs(android.PathsForModuleSrc(ctx, c.properties.Additional_cil_files)). 389 Text(">> ").Output(cil) 390 } 391 392 if proptools.BoolDefault(c.properties.Secilc_check, true) { 393 secilcCmd := rule.Command().BuiltTool("secilc"). 394 Flag("-m"). // Multiple decls 395 FlagWithArg("-M ", "true"). // Enable MLS 396 Flag("-G"). // expand and remove auto generated attributes 397 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 398 Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). // Also add cil files which are filtered out 399 Text(cil.String()). 400 FlagWithArg("-o ", os.DevNull). 401 FlagWithArg("-f ", os.DevNull) 402 403 if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) { 404 secilcCmd.Flag("-N") 405 } 406 } 407 408 rule.Build("cil", "Building cil for "+ctx.ModuleName()) 409 return cil 410} 411 412func (c *policyCil) GenerateAndroidBuildActions(ctx android.ModuleContext) { 413 if proptools.String(c.properties.Src) == "" { 414 ctx.PropertyErrorf("src", "must be specified") 415 return 416 } 417 conf := android.PathForModuleSrc(ctx, *c.properties.Src) 418 cil := c.compileConfToCil(ctx, conf) 419 420 if !c.Installable() { 421 c.SkipInstall() 422 } 423 424 if c.InstallInDebugRamdisk() { 425 // for userdebug_plat_sepolicy.cil 426 c.installPath = android.PathForModuleInstall(ctx) 427 } else { 428 c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") 429 } 430 c.installSource = cil 431 ctx.InstallFile(c.installPath, c.stem(), c.installSource) 432 433 ctx.SetOutputFiles(android.Paths{c.installSource}, "") 434} 435 436func (c *policyCil) AndroidMkEntries() []android.AndroidMkEntries { 437 return []android.AndroidMkEntries{android.AndroidMkEntries{ 438 OutputFile: android.OptionalPathForPath(c.installSource), 439 Class: "ETC", 440 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 441 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 442 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable()) 443 entries.SetPath("LOCAL_MODULE_PATH", c.installPath) 444 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) 445 }, 446 }, 447 }} 448} 449 450type policyBinaryProperties struct { 451 // Name of the output. Default is {module_name} 452 Stem *string 453 454 // Cil files to be compiled. 455 Srcs []string `android:"path"` 456 457 // Whether to ignore neverallow when running secilc check. Defaults to 458 // SELINUX_IGNORE_NEVERALLOWS. 459 Ignore_neverallow *bool 460 461 // Whether this module is directly installable to one of the partitions. Default is true 462 Installable *bool 463 464 // List of domains that are allowed to be in permissive mode on user builds. 465 Permissive_domains_on_user_builds []string 466} 467 468type policyBinary struct { 469 android.ModuleBase 470 471 properties policyBinaryProperties 472 473 installSource android.Path 474 installPath android.InstallPath 475} 476 477// se_policy_binary compiles cil files to a binary sepolicy file with secilc. Usually sources of 478// se_policy_binary come from outputs of se_policy_cil modules. 479func policyBinaryFactory() android.Module { 480 c := &policyBinary{} 481 c.AddProperties(&c.properties) 482 android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) 483 return c 484} 485 486func (c *policyBinary) InstallInRoot() bool { 487 return c.InstallInRecovery() 488} 489 490func (c *policyBinary) Installable() bool { 491 return proptools.BoolDefault(c.properties.Installable, true) 492} 493 494func (c *policyBinary) stem() string { 495 return proptools.StringDefault(c.properties.Stem, c.Name()) 496} 497 498func (c *policyBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) { 499 if len(c.properties.Srcs) == 0 { 500 ctx.PropertyErrorf("srcs", "must be specified") 501 return 502 } 503 bin := pathForModuleOut(ctx, c.stem()+"_policy") 504 rule := android.NewRuleBuilder(pctx, ctx) 505 secilcCmd := rule.Command().BuiltTool("secilc"). 506 Flag("-m"). // Multiple decls 507 FlagWithArg("-M ", "true"). // Enable MLS 508 Flag("-G"). // expand and remove auto generated attributes 509 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 510 Inputs(android.PathsForModuleSrc(ctx, c.properties.Srcs)). 511 FlagWithOutput("-o ", bin). 512 FlagWithArg("-f ", os.DevNull) 513 514 if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) { 515 secilcCmd.Flag("-N") 516 } 517 rule.Temporary(bin) 518 519 // permissive check is performed only in user build (not debuggable). 520 if !ctx.Config().Debuggable() { 521 permissiveDomains := pathForModuleOut(ctx, c.stem()+"_permissive") 522 cmd := rule.Command().BuiltTool("sepolicy-analyze"). 523 Input(bin). 524 Text("permissive") 525 // Filter-out domains listed in permissive_domains_on_user_builds 526 allowedDomains := c.properties.Permissive_domains_on_user_builds 527 if len(allowedDomains) != 0 { 528 cmd.Text("| { grep -Fxv") 529 for _, d := range allowedDomains { 530 cmd.FlagWithArg("-e ", proptools.ShellEscape(d)) 531 } 532 cmd.Text(" || true; }") // no match doesn't fail the cmd 533 } 534 cmd.Text(" > ").Output(permissiveDomains) 535 rule.Temporary(permissiveDomains) 536 537 msg := `==========\n` + 538 `ERROR: permissive domains not allowed in user builds\n` + 539 `List of invalid domains:` 540 541 rule.Command().Text("if test"). 542 FlagWithInput("-s ", permissiveDomains). 543 Text("; then echo"). 544 Flag("-e"). 545 Text(`"` + msg + `"`). 546 Text("&& cat "). 547 Input(permissiveDomains). 548 Text("; exit 1; fi") 549 } 550 551 out := pathForModuleOut(ctx, c.stem()) 552 rule.Command().Text("cp"). 553 Flag("-f"). 554 Input(bin). 555 Output(out) 556 557 rule.DeleteTemporaryFiles() 558 rule.Build("secilc", "Compiling cil files for "+ctx.ModuleName()) 559 560 if !c.Installable() { 561 c.SkipInstall() 562 } 563 564 if c.InstallInRecovery() { 565 // install in root 566 c.installPath = android.PathForModuleInstall(ctx) 567 } else { 568 c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") 569 } 570 c.installSource = out 571 ctx.InstallFile(c.installPath, c.stem(), c.installSource) 572 573 ctx.SetOutputFiles(android.Paths{c.installSource}, "") 574} 575 576func (c *policyBinary) AndroidMkEntries() []android.AndroidMkEntries { 577 return []android.AndroidMkEntries{android.AndroidMkEntries{ 578 OutputFile: android.OptionalPathForPath(c.installSource), 579 Class: "ETC", 580 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 581 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 582 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable()) 583 entries.SetPath("LOCAL_MODULE_PATH", c.installPath) 584 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) 585 }, 586 }, 587 }} 588} 589