• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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