1// Copyright 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 "github.com/google/blueprint/proptools" 19 20 "fmt" 21 "strconv" 22 23 "android/soong/android" 24) 25 26func init() { 27 ctx := android.InitRegistrationContext 28 ctx.RegisterModuleType("se_neverallow_test", neverallowTestFactory) 29} 30 31type neverallowTestProperties struct { 32 // Policy files to be tested. 33 Srcs []string `android:"path"` 34} 35 36type neverallowTestModule struct { 37 android.ModuleBase 38 properties neverallowTestProperties 39 testTimestamp android.ModuleOutPath 40} 41 42type nameProperties struct { 43 Name *string 44} 45 46var checkpolicyTag = dependencyTag{name: "checkpolicy"} 47var sepolicyAnalyzeTag = dependencyTag{name: "sepolicy_analyze"} 48 49// se_neverallow_test builds given policy files and checks whether any neverallow violations exist. 50// This module creates two conf files, one with build test and one without build test. Policy with 51// build test will be compiled with checkpolicy, and policy without build test will be tested with 52// sepolicy-analyze's neverallow tool. This module's check can be skipped by setting 53// SELINUX_IGNORE_NEVERALLOWS := true. 54func neverallowTestFactory() android.Module { 55 n := &neverallowTestModule{} 56 n.AddProperties(&n.properties) 57 android.InitAndroidModule(n) 58 android.AddLoadHook(n, func(ctx android.LoadHookContext) { 59 n.loadHook(ctx) 60 }) 61 return n 62} 63 64// Child conf module name for checkpolicy test. 65func (n *neverallowTestModule) checkpolicyConfModuleName() string { 66 return n.Name() + ".checkpolicy.conf" 67} 68 69// Child conf module name for sepolicy-analyze test. 70func (n *neverallowTestModule) sepolicyAnalyzeConfModuleName() string { 71 return n.Name() + ".sepolicy_analyze.conf" 72} 73 74func (n *neverallowTestModule) loadHook(ctx android.LoadHookContext) { 75 checkpolicyConf := n.checkpolicyConfModuleName() 76 ctx.CreateModule(policyConfFactory, &nameProperties{ 77 Name: proptools.StringPtr(checkpolicyConf), 78 }, &policyConfProperties{ 79 Srcs: n.properties.Srcs, 80 Build_variant: proptools.StringPtr("user"), 81 Installable: proptools.BoolPtr(false), 82 }) 83 84 sepolicyAnalyzeConf := n.sepolicyAnalyzeConfModuleName() 85 ctx.CreateModule(policyConfFactory, &nameProperties{ 86 Name: proptools.StringPtr(sepolicyAnalyzeConf), 87 }, &policyConfProperties{ 88 Srcs: n.properties.Srcs, 89 Build_variant: proptools.StringPtr("user"), 90 Exclude_build_test: proptools.BoolPtr(true), 91 Installable: proptools.BoolPtr(false), 92 }) 93} 94 95func (n *neverallowTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { 96 ctx.AddDependency(n, checkpolicyTag, n.checkpolicyConfModuleName()) 97 ctx.AddDependency(n, sepolicyAnalyzeTag, n.sepolicyAnalyzeConfModuleName()) 98} 99 100func (n *neverallowTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 101 n.testTimestamp = android.PathForModuleOut(ctx, "timestamp") 102 if ctx.Config().SelinuxIgnoreNeverallows() { 103 // just touch 104 android.WriteFileRule(ctx, n.testTimestamp, "") 105 return 106 } 107 108 var checkpolicyConfPaths android.Paths 109 var sepolicyAnalyzeConfPaths android.Paths 110 111 ctx.VisitDirectDeps(func(child android.Module) { 112 depTag := ctx.OtherModuleDependencyTag(child) 113 if depTag != checkpolicyTag && depTag != sepolicyAnalyzeTag { 114 return 115 } 116 117 o, ok := child.(android.OutputFileProducer) 118 if !ok { 119 panic(fmt.Errorf("Module %q isn't an OutputFileProducer", ctx.OtherModuleName(child))) 120 } 121 122 outputs, err := o.OutputFiles("") 123 if err != nil { 124 panic(fmt.Errorf("Module %q error while producing output: %v", ctx.OtherModuleName(child), err)) 125 } 126 127 switch ctx.OtherModuleDependencyTag(child) { 128 case checkpolicyTag: 129 checkpolicyConfPaths = outputs 130 case sepolicyAnalyzeTag: 131 sepolicyAnalyzeConfPaths = outputs 132 } 133 }) 134 135 if len(checkpolicyConfPaths) != 1 { 136 panic(fmt.Errorf("Module %q should produce exactly one output", n.checkpolicyConfModuleName())) 137 } 138 139 if len(sepolicyAnalyzeConfPaths) != 1 { 140 panic(fmt.Errorf("Module %q should produce exactly one output", n.sepolicyAnalyzeConfModuleName())) 141 } 142 143 checkpolicyConfPath := checkpolicyConfPaths[0] 144 sepolicyAnalyzeConfPath := sepolicyAnalyzeConfPaths[0] 145 146 rule := android.NewRuleBuilder(pctx, ctx) 147 148 // Step 1. Build a binary policy from the conf file including build test 149 binaryPolicy := android.PathForModuleOut(ctx, "policy") 150 rule.Command().BuiltTool("checkpolicy"). 151 Flag("-M"). 152 FlagWithArg("-c ", strconv.Itoa(PolicyVers)). 153 FlagWithOutput("-o ", binaryPolicy). 154 Input(checkpolicyConfPath) 155 rule.Build("neverallow_checkpolicy", "Neverallow check: "+ctx.ModuleName()) 156 157 // Step 2. Run sepolicy-analyze with the conf file without the build test and binary policy 158 // file from Step 1 159 rule = android.NewRuleBuilder(pctx, ctx) 160 msg := `sepolicy-analyze failed. This is most likely due to the use\n` + 161 `of an expanded attribute in a neverallow assertion. Please fix\n` + 162 `the policy.` 163 164 rule.Command().BuiltTool("sepolicy-analyze"). 165 Input(binaryPolicy). 166 Text("neverallow"). 167 Flag("-w"). 168 FlagWithInput("-f ", sepolicyAnalyzeConfPath). 169 Text("|| (echo"). 170 Flag("-e"). 171 Text(`"` + msg + `"`). 172 Text("; exit 1)") 173 174 rule.Command().Text("touch").Output(n.testTimestamp) 175 rule.Build("neverallow_sepolicy-analyze", "Neverallow check: "+ctx.ModuleName()) 176} 177 178func (n *neverallowTestModule) AndroidMkEntries() []android.AndroidMkEntries { 179 return []android.AndroidMkEntries{android.AndroidMkEntries{ 180 OutputFile: android.OptionalPathForPath(n.testTimestamp), 181 Class: "ETC", 182 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 183 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 184 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) 185 }, 186 }, 187 }} 188} 189