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