Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 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 | |
| 15 | package selinux |
| 16 | |
| 17 | import ( |
| 18 | "github.com/google/blueprint/proptools" |
| 19 | |
| 20 | "fmt" |
| 21 | "strconv" |
| 22 | |
| 23 | "android/soong/android" |
| 24 | ) |
| 25 | |
| 26 | func init() { |
| 27 | ctx := android.InitRegistrationContext |
| 28 | ctx.RegisterModuleType("se_neverallow_test", neverallowTestFactory) |
| 29 | } |
| 30 | |
| 31 | type neverallowTestProperties struct { |
| 32 | // Policy files to be tested. |
| 33 | Srcs []string `android:"path"` |
| 34 | } |
| 35 | |
| 36 | type neverallowTestModule struct { |
| 37 | android.ModuleBase |
| 38 | properties neverallowTestProperties |
Inseob Kim | 6c6f53b | 2023-04-26 11:03:35 +0900 | [diff] [blame^] | 39 | testTimestamp android.OutputPath |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 40 | } |
| 41 | |
| 42 | type nameProperties struct { |
| 43 | Name *string |
| 44 | } |
| 45 | |
| 46 | var checkpolicyTag = dependencyTag{name: "checkpolicy"} |
| 47 | var 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. |
| 54 | func 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. |
| 65 | func (n *neverallowTestModule) checkpolicyConfModuleName() string { |
| 66 | return n.Name() + ".checkpolicy.conf" |
| 67 | } |
| 68 | |
| 69 | // Child conf module name for sepolicy-analyze test. |
| 70 | func (n *neverallowTestModule) sepolicyAnalyzeConfModuleName() string { |
| 71 | return n.Name() + ".sepolicy_analyze.conf" |
| 72 | } |
| 73 | |
| 74 | func (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 | |
| 95 | func (n *neverallowTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { |
| 96 | ctx.AddDependency(n, checkpolicyTag, n.checkpolicyConfModuleName()) |
| 97 | ctx.AddDependency(n, sepolicyAnalyzeTag, n.sepolicyAnalyzeConfModuleName()) |
| 98 | } |
| 99 | |
| 100 | func (n *neverallowTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
Inseob Kim | 6c6f53b | 2023-04-26 11:03:35 +0900 | [diff] [blame^] | 101 | n.testTimestamp = pathForModuleOut(ctx, "timestamp") |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 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 |
Inseob Kim | 6c6f53b | 2023-04-26 11:03:35 +0900 | [diff] [blame^] | 149 | binaryPolicy := pathForModuleOut(ctx, "policy") |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 150 | rule.Command().BuiltTool("checkpolicy"). |
| 151 | Flag("-M"). |
| 152 | FlagWithArg("-c ", strconv.Itoa(PolicyVers)). |
| 153 | FlagWithOutput("-o ", binaryPolicy). |
| 154 | Input(checkpolicyConfPath) |
Thiébaud Weksteen | 9ebf0c8 | 2022-01-25 21:07:00 +1100 | [diff] [blame] | 155 | rule.Build("neverallow_checkpolicy", "Neverallow check: "+ctx.ModuleName()) |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 156 | |
| 157 | // Step 2. Run sepolicy-analyze with the conf file without the build test and binary policy |
| 158 | // file from Step 1 |
Thiébaud Weksteen | 9ebf0c8 | 2022-01-25 21:07:00 +1100 | [diff] [blame] | 159 | rule = android.NewRuleBuilder(pctx, ctx) |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 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 | |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 174 | rule.Command().Text("touch").Output(n.testTimestamp) |
Thiébaud Weksteen | 9ebf0c8 | 2022-01-25 21:07:00 +1100 | [diff] [blame] | 175 | rule.Build("neverallow_sepolicy-analyze", "Neverallow check: "+ctx.ModuleName()) |
Inseob Kim | 0de7fcc | 2021-12-22 23:06:53 +0900 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | func (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 | } |