blob: 6e159e9f27d5261bc3e580754c8e23d5e0fc410b [file] [log] [blame]
Inseob Kim0de7fcc2021-12-22 23:06:53 +09001// 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 {
Inseob Kim085f22f2023-11-09 11:13:01 +090032 // Default modules for conf
33 Defaults []string
34
Inseob Kim0de7fcc2021-12-22 23:06:53 +090035 // Policy files to be tested.
36 Srcs []string `android:"path"`
37}
38
39type neverallowTestModule struct {
40 android.ModuleBase
41 properties neverallowTestProperties
Inseob Kim6c6f53b2023-04-26 11:03:35 +090042 testTimestamp android.OutputPath
Inseob Kim0de7fcc2021-12-22 23:06:53 +090043}
44
45type nameProperties struct {
46 Name *string
47}
48
49var checkpolicyTag = dependencyTag{name: "checkpolicy"}
50var sepolicyAnalyzeTag = dependencyTag{name: "sepolicy_analyze"}
51
52// se_neverallow_test builds given policy files and checks whether any neverallow violations exist.
53// This module creates two conf files, one with build test and one without build test. Policy with
54// build test will be compiled with checkpolicy, and policy without build test will be tested with
55// sepolicy-analyze's neverallow tool. This module's check can be skipped by setting
56// SELINUX_IGNORE_NEVERALLOWS := true.
57func neverallowTestFactory() android.Module {
58 n := &neverallowTestModule{}
59 n.AddProperties(&n.properties)
60 android.InitAndroidModule(n)
61 android.AddLoadHook(n, func(ctx android.LoadHookContext) {
62 n.loadHook(ctx)
63 })
64 return n
65}
66
67// Child conf module name for checkpolicy test.
68func (n *neverallowTestModule) checkpolicyConfModuleName() string {
69 return n.Name() + ".checkpolicy.conf"
70}
71
72// Child conf module name for sepolicy-analyze test.
73func (n *neverallowTestModule) sepolicyAnalyzeConfModuleName() string {
74 return n.Name() + ".sepolicy_analyze.conf"
75}
76
77func (n *neverallowTestModule) loadHook(ctx android.LoadHookContext) {
78 checkpolicyConf := n.checkpolicyConfModuleName()
79 ctx.CreateModule(policyConfFactory, &nameProperties{
80 Name: proptools.StringPtr(checkpolicyConf),
81 }, &policyConfProperties{
82 Srcs: n.properties.Srcs,
83 Build_variant: proptools.StringPtr("user"),
84 Installable: proptools.BoolPtr(false),
Inseob Kim085f22f2023-11-09 11:13:01 +090085 }, &struct {
86 Defaults []string
87 }{
88 Defaults: n.properties.Defaults,
Inseob Kim0de7fcc2021-12-22 23:06:53 +090089 })
90
91 sepolicyAnalyzeConf := n.sepolicyAnalyzeConfModuleName()
92 ctx.CreateModule(policyConfFactory, &nameProperties{
93 Name: proptools.StringPtr(sepolicyAnalyzeConf),
94 }, &policyConfProperties{
95 Srcs: n.properties.Srcs,
96 Build_variant: proptools.StringPtr("user"),
97 Exclude_build_test: proptools.BoolPtr(true),
98 Installable: proptools.BoolPtr(false),
Inseob Kim085f22f2023-11-09 11:13:01 +090099 }, &struct {
100 Defaults []string
101 }{
102 Defaults: n.properties.Defaults,
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900103 })
104}
105
106func (n *neverallowTestModule) DepsMutator(ctx android.BottomUpMutatorContext) {
107 ctx.AddDependency(n, checkpolicyTag, n.checkpolicyConfModuleName())
108 ctx.AddDependency(n, sepolicyAnalyzeTag, n.sepolicyAnalyzeConfModuleName())
109}
110
111func (n *neverallowTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
Inseob Kim6c6f53b2023-04-26 11:03:35 +0900112 n.testTimestamp = pathForModuleOut(ctx, "timestamp")
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900113 if ctx.Config().SelinuxIgnoreNeverallows() {
114 // just touch
115 android.WriteFileRule(ctx, n.testTimestamp, "")
116 return
117 }
118
119 var checkpolicyConfPaths android.Paths
120 var sepolicyAnalyzeConfPaths android.Paths
121
122 ctx.VisitDirectDeps(func(child android.Module) {
123 depTag := ctx.OtherModuleDependencyTag(child)
124 if depTag != checkpolicyTag && depTag != sepolicyAnalyzeTag {
125 return
126 }
127
128 o, ok := child.(android.OutputFileProducer)
129 if !ok {
130 panic(fmt.Errorf("Module %q isn't an OutputFileProducer", ctx.OtherModuleName(child)))
131 }
132
133 outputs, err := o.OutputFiles("")
134 if err != nil {
135 panic(fmt.Errorf("Module %q error while producing output: %v", ctx.OtherModuleName(child), err))
136 }
137
138 switch ctx.OtherModuleDependencyTag(child) {
139 case checkpolicyTag:
140 checkpolicyConfPaths = outputs
141 case sepolicyAnalyzeTag:
142 sepolicyAnalyzeConfPaths = outputs
143 }
144 })
145
146 if len(checkpolicyConfPaths) != 1 {
147 panic(fmt.Errorf("Module %q should produce exactly one output", n.checkpolicyConfModuleName()))
148 }
149
150 if len(sepolicyAnalyzeConfPaths) != 1 {
151 panic(fmt.Errorf("Module %q should produce exactly one output", n.sepolicyAnalyzeConfModuleName()))
152 }
153
154 checkpolicyConfPath := checkpolicyConfPaths[0]
155 sepolicyAnalyzeConfPath := sepolicyAnalyzeConfPaths[0]
156
157 rule := android.NewRuleBuilder(pctx, ctx)
158
159 // Step 1. Build a binary policy from the conf file including build test
Inseob Kim6c6f53b2023-04-26 11:03:35 +0900160 binaryPolicy := pathForModuleOut(ctx, "policy")
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900161 rule.Command().BuiltTool("checkpolicy").
162 Flag("-M").
163 FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
164 FlagWithOutput("-o ", binaryPolicy).
165 Input(checkpolicyConfPath)
Thiébaud Weksteen9ebf0c82022-01-25 21:07:00 +1100166 rule.Build("neverallow_checkpolicy", "Neverallow check: "+ctx.ModuleName())
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900167
168 // Step 2. Run sepolicy-analyze with the conf file without the build test and binary policy
169 // file from Step 1
Thiébaud Weksteen9ebf0c82022-01-25 21:07:00 +1100170 rule = android.NewRuleBuilder(pctx, ctx)
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900171 msg := `sepolicy-analyze failed. This is most likely due to the use\n` +
172 `of an expanded attribute in a neverallow assertion. Please fix\n` +
173 `the policy.`
174
175 rule.Command().BuiltTool("sepolicy-analyze").
176 Input(binaryPolicy).
177 Text("neverallow").
178 Flag("-w").
179 FlagWithInput("-f ", sepolicyAnalyzeConfPath).
180 Text("|| (echo").
181 Flag("-e").
182 Text(`"` + msg + `"`).
183 Text("; exit 1)")
184
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900185 rule.Command().Text("touch").Output(n.testTimestamp)
Thiébaud Weksteen9ebf0c82022-01-25 21:07:00 +1100186 rule.Build("neverallow_sepolicy-analyze", "Neverallow check: "+ctx.ModuleName())
Inseob Kim0de7fcc2021-12-22 23:06:53 +0900187}
188
189func (n *neverallowTestModule) AndroidMkEntries() []android.AndroidMkEntries {
190 return []android.AndroidMkEntries{android.AndroidMkEntries{
191 OutputFile: android.OptionalPathForPath(n.testTimestamp),
192 Class: "ETC",
193 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
194 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
195 entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
196 },
197 },
198 }}
199}