// Copyright 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package selinux import ( "github.com/google/blueprint/proptools" "fmt" "strconv" "android/soong/android" ) func init() { ctx := android.InitRegistrationContext ctx.RegisterModuleType("se_neverallow_test", neverallowTestFactory) } type neverallowTestProperties struct { // Default modules for conf Defaults []string // Policy files to be tested. Srcs []string `android:"path"` } type neverallowTestModule struct { android.ModuleBase properties neverallowTestProperties testTimestamp android.OutputPath } type nameProperties struct { Name *string } var checkpolicyTag = dependencyTag{name: "checkpolicy"} var sepolicyAnalyzeTag = dependencyTag{name: "sepolicy_analyze"} // se_neverallow_test builds given policy files and checks whether any neverallow violations exist. // This module creates two conf files, one with build test and one without build test. Policy with // build test will be compiled with checkpolicy, and policy without build test will be tested with // sepolicy-analyze's neverallow tool. This module's check can be skipped by setting // SELINUX_IGNORE_NEVERALLOWS := true. func neverallowTestFactory() android.Module { n := &neverallowTestModule{} n.AddProperties(&n.properties) android.InitAndroidModule(n) android.AddLoadHook(n, func(ctx android.LoadHookContext) { n.loadHook(ctx) }) return n } // Child conf module name for checkpolicy test. func (n *neverallowTestModule) checkpolicyConfModuleName() string { return n.Name() + ".checkpolicy.conf" } // Child conf module name for sepolicy-analyze test. func (n *neverallowTestModule) sepolicyAnalyzeConfModuleName() string { return n.Name() + ".sepolicy_analyze.conf" } func (n *neverallowTestModule) loadHook(ctx android.LoadHookContext) { checkpolicyConf := n.checkpolicyConfModuleName() ctx.CreateModule(policyConfFactory, &nameProperties{ Name: proptools.StringPtr(checkpolicyConf), }, &policyConfProperties{ Srcs: n.properties.Srcs, Build_variant: proptools.StringPtr("user"), Installable: proptools.BoolPtr(false), }, &struct { Defaults []string }{ Defaults: n.properties.Defaults, }) sepolicyAnalyzeConf := n.sepolicyAnalyzeConfModuleName() ctx.CreateModule(policyConfFactory, &nameProperties{ Name: proptools.StringPtr(sepolicyAnalyzeConf), }, &policyConfProperties{ Srcs: n.properties.Srcs, Build_variant: proptools.StringPtr("user"), Exclude_build_test: proptools.BoolPtr(true), Installable: proptools.BoolPtr(false), }, &struct { Defaults []string }{ Defaults: n.properties.Defaults, }) } func (n *neverallowTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddDependency(n, checkpolicyTag, n.checkpolicyConfModuleName()) ctx.AddDependency(n, sepolicyAnalyzeTag, n.sepolicyAnalyzeConfModuleName()) } func (n *neverallowTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { n.testTimestamp = pathForModuleOut(ctx, "timestamp") if ctx.Config().SelinuxIgnoreNeverallows() { // just touch android.WriteFileRule(ctx, n.testTimestamp, "") return } var checkpolicyConfPaths android.Paths var sepolicyAnalyzeConfPaths android.Paths ctx.VisitDirectDeps(func(child android.Module) { depTag := ctx.OtherModuleDependencyTag(child) if depTag != checkpolicyTag && depTag != sepolicyAnalyzeTag { return } o, ok := child.(android.OutputFileProducer) if !ok { panic(fmt.Errorf("Module %q isn't an OutputFileProducer", ctx.OtherModuleName(child))) } outputs, err := o.OutputFiles("") if err != nil { panic(fmt.Errorf("Module %q error while producing output: %v", ctx.OtherModuleName(child), err)) } switch ctx.OtherModuleDependencyTag(child) { case checkpolicyTag: checkpolicyConfPaths = outputs case sepolicyAnalyzeTag: sepolicyAnalyzeConfPaths = outputs } }) if len(checkpolicyConfPaths) != 1 { panic(fmt.Errorf("Module %q should produce exactly one output", n.checkpolicyConfModuleName())) } if len(sepolicyAnalyzeConfPaths) != 1 { panic(fmt.Errorf("Module %q should produce exactly one output", n.sepolicyAnalyzeConfModuleName())) } checkpolicyConfPath := checkpolicyConfPaths[0] sepolicyAnalyzeConfPath := sepolicyAnalyzeConfPaths[0] rule := android.NewRuleBuilder(pctx, ctx) // Step 1. Build a binary policy from the conf file including build test binaryPolicy := pathForModuleOut(ctx, "policy") rule.Command().BuiltTool("checkpolicy"). Flag("-M"). FlagWithArg("-c ", strconv.Itoa(PolicyVers)). FlagWithOutput("-o ", binaryPolicy). Input(checkpolicyConfPath) rule.Build("neverallow_checkpolicy", "Neverallow check: "+ctx.ModuleName()) // Step 2. Run sepolicy-analyze with the conf file without the build test and binary policy // file from Step 1 rule = android.NewRuleBuilder(pctx, ctx) msg := `sepolicy-analyze failed. This is most likely due to the use\n` + `of an expanded attribute in a neverallow assertion. Please fix\n` + `the policy.` rule.Command().BuiltTool("sepolicy-analyze"). Input(binaryPolicy). Text("neverallow"). Flag("-w"). FlagWithInput("-f ", sepolicyAnalyzeConfPath). Text("|| (echo"). Flag("-e"). Text(`"` + msg + `"`). Text("; exit 1)") rule.Command().Text("touch").Output(n.testTimestamp) rule.Build("neverallow_sepolicy-analyze", "Neverallow check: "+ctx.ModuleName()) } func (n *neverallowTestModule) AndroidMkEntries() []android.AndroidMkEntries { return []android.AndroidMkEntries{android.AndroidMkEntries{ OutputFile: android.OptionalPathForPath(n.testTimestamp), Class: "FAKE", Include: "$(BUILD_PHONY_PACKAGE)", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetPath("LOCAL_ADDITIONAL_DEPENDENCIES", n.testTimestamp) }, }, }} }