1// Copyright (C) 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	"fmt"
19	"os"
20	"strconv"
21
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25)
26
27const (
28	// TODO: sync with Android.mk
29	MlsSens    = 1
30	MlsCats    = 1024
31	PolicyVers = 30
32)
33
34func init() {
35	android.RegisterModuleType("se_policy_conf", policyConfFactory)
36	android.RegisterModuleType("se_policy_cil", policyCilFactory)
37}
38
39type policyConfProperties struct {
40	// Name of the output. Default is {module_name}
41	Stem *string
42
43	// Policy files to be compiled to cil file.
44	Srcs []string `android:"path"`
45
46	// Target build variant (user / userdebug / eng). Default follows the current lunch target
47	Build_variant *string
48
49	// Whether to exclude build test or not. Default is false
50	Exclude_build_test *bool
51
52	// Whether to include asan specific policies or not. Default follows the current lunch target
53	With_asan *bool
54
55	// Whether to build CTS specific policy or not. Default is false
56	Cts *bool
57
58	// Whether this module is directly installable to one of the partitions. Default is true
59	Installable *bool
60}
61
62type policyConf struct {
63	android.ModuleBase
64
65	properties policyConfProperties
66
67	installSource android.Path
68	installPath   android.InstallPath
69}
70
71// se_policy_conf merges collection of policy files into a policy.conf file to be processed by
72// checkpolicy.
73func policyConfFactory() android.Module {
74	c := &policyConf{}
75	c.AddProperties(&c.properties)
76	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
77	return c
78}
79
80func (c *policyConf) installable() bool {
81	return proptools.BoolDefault(c.properties.Installable, true)
82}
83
84func (c *policyConf) stem() string {
85	return proptools.StringDefault(c.properties.Stem, c.Name())
86}
87
88func (c *policyConf) buildVariant(ctx android.ModuleContext) string {
89	if variant := proptools.String(c.properties.Build_variant); variant != "" {
90		return variant
91	}
92	if ctx.Config().Eng() {
93		return "eng"
94	}
95	if ctx.Config().Debuggable() {
96		return "userdebug"
97	}
98	return "user"
99}
100
101func (c *policyConf) cts() bool {
102	return proptools.Bool(c.properties.Cts)
103}
104
105func (c *policyConf) withAsan(ctx android.ModuleContext) string {
106	isAsanDevice := android.InList("address", ctx.Config().SanitizeDevice())
107	return strconv.FormatBool(proptools.BoolDefault(c.properties.With_asan, isAsanDevice))
108}
109
110func (c *policyConf) sepolicySplit(ctx android.ModuleContext) string {
111	if c.cts() {
112		return "cts"
113	}
114	return strconv.FormatBool(ctx.DeviceConfig().SepolicySplit())
115}
116
117func (c *policyConf) compatibleProperty(ctx android.ModuleContext) string {
118	if c.cts() {
119		return "cts"
120	}
121	return "true"
122}
123
124func (c *policyConf) trebleSyspropNeverallow(ctx android.ModuleContext) string {
125	if c.cts() {
126		return "cts"
127	}
128	return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenTrebleSyspropNeverallow())
129}
130
131func (c *policyConf) enforceSyspropOwner(ctx android.ModuleContext) string {
132	if c.cts() {
133		return "cts"
134	}
135	return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenEnforceSyspropOwner())
136}
137
138func (c *policyConf) enforceDebugfsRestrictions(ctx android.ModuleContext) string {
139	if c.cts() {
140		return "cts"
141	}
142	return strconv.FormatBool(ctx.DeviceConfig().BuildDebugfsRestrictionsEnabled())
143}
144
145func (c *policyConf) transformPolicyToConf(ctx android.ModuleContext) android.OutputPath {
146	conf := android.PathForModuleOut(ctx, "conf").OutputPath
147	rule := android.NewRuleBuilder(pctx, ctx)
148	rule.Command().Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")).
149		Flag("--fatal-warnings").
150		FlagForEachArg("-D ", ctx.DeviceConfig().SepolicyM4Defs()).
151		FlagWithArg("-D mls_num_sens=", strconv.Itoa(MlsSens)).
152		FlagWithArg("-D mls_num_cats=", strconv.Itoa(MlsCats)).
153		FlagWithArg("-D target_arch=", ctx.DeviceConfig().DeviceArch()).
154		FlagWithArg("-D target_with_asan=", c.withAsan(ctx)).
155		FlagWithArg("-D target_with_dexpreopt=", strconv.FormatBool(ctx.DeviceConfig().WithDexpreopt())).
156		FlagWithArg("-D target_with_native_coverage=", strconv.FormatBool(ctx.DeviceConfig().ClangCoverageEnabled() || ctx.DeviceConfig().GcovCoverageEnabled())).
157		FlagWithArg("-D target_build_variant=", c.buildVariant(ctx)).
158		FlagWithArg("-D target_full_treble=", c.sepolicySplit(ctx)).
159		FlagWithArg("-D target_compatible_property=", c.compatibleProperty(ctx)).
160		FlagWithArg("-D target_treble_sysprop_neverallow=", c.trebleSyspropNeverallow(ctx)).
161		FlagWithArg("-D target_enforce_sysprop_owner=", c.enforceSyspropOwner(ctx)).
162		FlagWithArg("-D target_exclude_build_test=", strconv.FormatBool(proptools.Bool(c.properties.Exclude_build_test))).
163		FlagWithArg("-D target_requires_insecure_execmem_for_swiftshader=", strconv.FormatBool(ctx.DeviceConfig().RequiresInsecureExecmemForSwiftshader())).
164		FlagWithArg("-D target_enforce_debugfs_restriction=", c.enforceDebugfsRestrictions(ctx)).
165		Flag("-s").
166		Inputs(android.PathsForModuleSrc(ctx, c.properties.Srcs)).
167		Text("> ").Output(conf)
168
169	rule.Build("conf", "Transform policy to conf: "+ctx.ModuleName())
170	return conf
171}
172
173func (c *policyConf) DepsMutator(ctx android.BottomUpMutatorContext) {
174	// do nothing
175}
176
177func (c *policyConf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
178	c.installSource = c.transformPolicyToConf(ctx)
179	c.installPath = android.PathForModuleInstall(ctx, "etc")
180	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
181
182	if !c.installable() {
183		c.SkipInstall()
184	}
185}
186
187func (c *policyConf) AndroidMkEntries() []android.AndroidMkEntries {
188	return []android.AndroidMkEntries{android.AndroidMkEntries{
189		OutputFile: android.OptionalPathForPath(c.installSource),
190		Class:      "ETC",
191		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
192			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
193				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.installable())
194				entries.SetPath("LOCAL_MODULE_PATH", c.installPath.ToMakePath())
195				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
196			},
197		},
198	}}
199}
200
201func (c *policyConf) OutputFiles(tag string) (android.Paths, error) {
202	if tag == "" {
203		return android.Paths{c.installSource}, nil
204	}
205	return nil, fmt.Errorf("Unknown tag %q", tag)
206}
207
208var _ android.OutputFileProducer = (*policyConf)(nil)
209
210type policyCilProperties struct {
211	// Name of the output. Default is {module_name}
212	Stem *string
213
214	// Policy file to be compiled to cil file.
215	Src *string `android:"path"`
216
217	// Additional cil files to be added in the end of the output. This is to support workarounds
218	// which are not supported by the policy language.
219	Additional_cil_files []string `android:"path"`
220
221	// Cil files to be filtered out by the filter_out tool of "build_sepolicy". Used to build
222	// exported policies
223	Filter_out []string `android:"path"`
224
225	// Whether to remove line markers (denoted by ;;) out of compiled cil files. Defaults to false
226	Remove_line_marker *bool
227
228	// Whether to run secilc to check compiled policy or not. Defaults to true
229	Secilc_check *bool
230
231	// Whether to ignore neverallow when running secilc check. Defaults to
232	// SELINUX_IGNORE_NEVERALLOWS.
233	Ignore_neverallow *bool
234
235	// Whether this module is directly installable to one of the partitions. Default is true
236	Installable *bool
237}
238
239type policyCil struct {
240	android.ModuleBase
241
242	properties policyCilProperties
243
244	installSource android.Path
245	installPath   android.InstallPath
246}
247
248// se_policy_cil compiles a policy.conf file to a cil file with checkpolicy, and optionally runs
249// secilc to check the output cil file. Affected by SELINUX_IGNORE_NEVERALLOWS.
250func policyCilFactory() android.Module {
251	c := &policyCil{}
252	c.AddProperties(&c.properties)
253	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
254	return c
255}
256
257func (c *policyCil) Installable() bool {
258	return proptools.BoolDefault(c.properties.Installable, true)
259}
260
261func (c *policyCil) stem() string {
262	return proptools.StringDefault(c.properties.Stem, c.Name())
263}
264
265func (c *policyCil) compileConfToCil(ctx android.ModuleContext, conf android.Path) android.OutputPath {
266	cil := android.PathForModuleOut(ctx, c.stem()).OutputPath
267	rule := android.NewRuleBuilder(pctx, ctx)
268	rule.Command().BuiltTool("checkpolicy").
269		Flag("-C"). // Write CIL
270		Flag("-M"). // Enable MLS
271		FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
272		FlagWithOutput("-o ", cil).
273		Input(conf)
274
275	if len(c.properties.Additional_cil_files) > 0 {
276		rule.Command().Text("cat").
277			Inputs(android.PathsForModuleSrc(ctx, c.properties.Additional_cil_files)).
278			Text(">> ").Output(cil)
279	}
280
281	if len(c.properties.Filter_out) > 0 {
282		rule.Command().BuiltTool("build_sepolicy").
283			Text("filter_out").
284			Flag("-f").
285			Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)).
286			FlagWithOutput("-t ", cil)
287	}
288
289	if proptools.Bool(c.properties.Remove_line_marker) {
290		rule.Command().Text("grep -v").
291			Text(proptools.ShellEscape(";;")).
292			Text(cil.String()).
293			Text(">").
294			Text(cil.String() + ".tmp").
295			Text("&& mv").
296			Text(cil.String() + ".tmp").
297			Text(cil.String())
298	}
299
300	if proptools.BoolDefault(c.properties.Secilc_check, true) {
301		secilcCmd := rule.Command().BuiltTool("secilc").
302			Flag("-m").                 // Multiple decls
303			FlagWithArg("-M ", "true"). // Enable MLS
304			Flag("-G").                 // expand and remove auto generated attributes
305			FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
306			Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). // Also add cil files which are filtered out
307			Text(cil.String()).
308			FlagWithArg("-o ", os.DevNull).
309			FlagWithArg("-f ", os.DevNull)
310
311		if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) {
312			secilcCmd.Flag("-N")
313		}
314	}
315
316	rule.Build("cil", "Building cil for "+ctx.ModuleName())
317	return cil
318}
319
320func (c *policyCil) GenerateAndroidBuildActions(ctx android.ModuleContext) {
321	if proptools.String(c.properties.Src) == "" {
322		ctx.PropertyErrorf("src", "must be specified")
323		return
324	}
325	conf := android.PathForModuleSrc(ctx, *c.properties.Src)
326	cil := c.compileConfToCil(ctx, conf)
327
328	if c.InstallInDebugRamdisk() {
329		// for userdebug_plat_sepolicy.cil
330		c.installPath = android.PathForModuleInstall(ctx)
331	} else {
332		c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux")
333	}
334	c.installSource = cil
335	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
336
337	if !c.Installable() {
338		c.SkipInstall()
339	}
340}
341
342func (c *policyCil) AndroidMkEntries() []android.AndroidMkEntries {
343	return []android.AndroidMkEntries{android.AndroidMkEntries{
344		OutputFile: android.OptionalPathForPath(c.installSource),
345		Class:      "ETC",
346		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
347			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
348				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable())
349				entries.SetPath("LOCAL_MODULE_PATH", c.installPath.ToMakePath())
350				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
351			},
352		},
353	}}
354}
355
356func (c *policyCil) OutputFiles(tag string) (android.Paths, error) {
357	if tag == "" {
358		return android.Paths{c.installSource}, nil
359	}
360	return nil, fmt.Errorf("Unknown tag %q", tag)
361}
362
363var _ android.OutputFileProducer = (*policyCil)(nil)
364