xref: /aosp_15_r20/system/sepolicy/build/soong/policy.go (revision e4a36f4174b17bbab9dc043f4a65dc8d87377290)
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	"os"
19	"sort"
20	"strconv"
21	"strings"
22
23	"github.com/google/blueprint/proptools"
24
25	"android/soong/android"
26)
27
28const (
29	MlsSens    = 1
30	MlsCats    = 1024
31	PolicyVers = 30
32)
33
34// This order should be kept. checkpolicy syntax requires it.
35var policyConfOrder = []string{
36	"flagging_macros",
37	"security_classes",
38	"initial_sids",
39	"access_vectors",
40	"global_macros",
41	"neverallow_macros",
42	"mls_macros",
43	"mls_decl",
44	"mls",
45	"policy_capabilities",
46	"te_macros",
47	"ioctl_defines",
48	"ioctl_macros",
49	"attributes|*.te",
50	"roles_decl",
51	"roles",
52	"users",
53	"initial_sid_contexts",
54	"fs_use",
55	"genfs_contexts",
56	"port_contexts",
57}
58
59func init() {
60	android.RegisterModuleType("se_policy_conf", policyConfFactory)
61	android.RegisterModuleType("se_policy_conf_defaults", policyConfDefaultFactory)
62	android.RegisterModuleType("se_policy_cil", policyCilFactory)
63	android.RegisterModuleType("se_policy_binary", policyBinaryFactory)
64}
65
66type policyConfProperties struct {
67	// Name of the output. Default is {module_name}
68	Stem *string
69
70	// Policy files to be compiled to cil file.
71	Srcs []string `android:"path"`
72
73	// Target build variant (user / userdebug / eng). Default follows the current lunch target
74	Build_variant *string
75
76	// Whether to exclude build test or not. Default is false
77	Exclude_build_test *bool
78
79	// Whether to include asan specific policies or not. Default follows the current lunch target
80	With_asan *bool
81
82	// Whether to build CTS specific policy or not. Default is false
83	Cts *bool
84
85	// Whether to build recovery specific policy or not. Default is false
86	Target_recovery *bool
87
88	// Whether this module is directly installable to one of the partitions. Default is true
89	Installable *bool
90
91	// Desired number of MLS categories. Defaults to 1024
92	Mls_cats *int64
93
94	// Board api level of policy files. Set "current" for RELEASE_BOARD_API_LEVEL, or a direct
95	// version string (e.g. "202404"). Defaults to "current"
96	Board_api_level *string
97}
98
99type policyConf struct {
100	android.ModuleBase
101	android.DefaultableModuleBase
102	flaggableModuleBase
103
104	properties policyConfProperties
105
106	installSource android.Path
107	installPath   android.InstallPath
108}
109
110var _ flaggableModule = (*policyConf)(nil)
111
112// se_policy_conf merges collection of policy files into a policy.conf file to be processed by
113// checkpolicy.
114func policyConfFactory() android.Module {
115	c := &policyConf{}
116	c.AddProperties(&c.properties)
117	initFlaggableModule(c)
118	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
119	android.InitDefaultableModule(c)
120	return c
121}
122
123type policyConfDefaults struct {
124	android.ModuleBase
125	android.DefaultsModuleBase
126}
127
128// se_policy_conf_defaults provides a set of properties that can be inherited by other
129// se_policy_conf_defaults modules. A module can use the properties from a se_policy_conf_defaults
130// using `defaults: ["<:default_module_name>"]`. Properties of both modules are merged (when
131// possible) by prepending the default module's values to the depending module's values.
132func policyConfDefaultFactory() android.Module {
133	c := &policyConfDefaults{}
134	c.AddProperties(
135		&policyConfProperties{},
136		&flaggableModuleProperties{},
137	)
138	android.InitDefaultsModule(c)
139	return c
140}
141
142func (c *policyConf) installable() bool {
143	return proptools.BoolDefault(c.properties.Installable, true)
144}
145
146func (c *policyConf) stem() string {
147	return proptools.StringDefault(c.properties.Stem, c.Name())
148}
149
150func (c *policyConf) buildVariant(ctx android.ModuleContext) string {
151	if variant := proptools.String(c.properties.Build_variant); variant != "" {
152		return variant
153	}
154	if ctx.Config().Eng() {
155		return "eng"
156	}
157	if ctx.Config().Debuggable() {
158		return "userdebug"
159	}
160	return "user"
161}
162
163func (c *policyConf) cts() bool {
164	return proptools.Bool(c.properties.Cts)
165}
166
167func (c *policyConf) isTargetRecovery() bool {
168	return proptools.Bool(c.properties.Target_recovery)
169}
170
171func (c *policyConf) withAsan(ctx android.ModuleContext) string {
172	isAsanDevice := android.InList("address", ctx.Config().SanitizeDevice())
173	return strconv.FormatBool(proptools.BoolDefault(c.properties.With_asan, isAsanDevice))
174}
175
176func (c *policyConf) sepolicySplit(ctx android.ModuleContext) string {
177	if c.cts() {
178		return "cts"
179	}
180	if c.isTargetRecovery() {
181		return "false"
182	}
183	return strconv.FormatBool(true)
184}
185
186func (c *policyConf) compatibleProperty(ctx android.ModuleContext) string {
187	if c.cts() {
188		return "cts"
189	}
190	if c.isTargetRecovery() {
191		return "false"
192	}
193	return "true"
194}
195
196func (c *policyConf) trebleSyspropNeverallow(ctx android.ModuleContext) string {
197	if c.cts() {
198		return "cts"
199	}
200	if c.isTargetRecovery() {
201		return "false"
202	}
203	return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenTrebleSyspropNeverallow())
204}
205
206func (c *policyConf) enforceSyspropOwner(ctx android.ModuleContext) string {
207	if c.cts() {
208		return "cts"
209	}
210	if c.isTargetRecovery() {
211		return "false"
212	}
213	return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenEnforceSyspropOwner())
214}
215
216func (c *policyConf) enforceDebugfsRestrictions(ctx android.ModuleContext) string {
217	if c.cts() {
218		return "cts"
219	}
220	return strconv.FormatBool(ctx.DeviceConfig().BuildDebugfsRestrictionsEnabled())
221}
222
223func (c *policyConf) mlsCats() int {
224	return proptools.IntDefault(c.properties.Mls_cats, MlsCats)
225}
226
227func findPolicyConfOrder(name string) int {
228	for idx, pattern := range policyConfOrder {
229		// We could use regexp but it seems like an overkill
230		if pattern == "attributes|*.te" && (name == "attributes" || strings.HasSuffix(name, ".te")) {
231			return idx
232		} else if pattern == name {
233			return idx
234		}
235	}
236	// name is not matched
237	return len(policyConfOrder)
238}
239
240func (c *policyConf) transformPolicyToConf(ctx android.ModuleContext) android.OutputPath {
241	conf := pathForModuleOut(ctx, c.stem())
242	rule := android.NewRuleBuilder(pctx, ctx)
243
244	srcs := android.PathsForModuleSrc(ctx, c.properties.Srcs)
245	sort.SliceStable(srcs, func(x, y int) bool {
246		return findPolicyConfOrder(srcs[x].Base()) < findPolicyConfOrder(srcs[y].Base())
247	})
248
249	flags := c.getBuildFlags(ctx)
250	rule.Command().Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")).
251		Flag("--fatal-warnings").
252		FlagForEachArg("-D ", ctx.DeviceConfig().SepolicyM4Defs()).
253		FlagWithArg("-D mls_num_sens=", strconv.Itoa(MlsSens)).
254		FlagWithArg("-D mls_num_cats=", strconv.Itoa(c.mlsCats())).
255		FlagWithArg("-D target_arch=", ctx.DeviceConfig().DeviceArch()).
256		FlagWithArg("-D target_with_asan=", c.withAsan(ctx)).
257		FlagWithArg("-D target_with_dexpreopt=", strconv.FormatBool(ctx.DeviceConfig().WithDexpreopt())).
258		FlagWithArg("-D target_with_native_coverage=", strconv.FormatBool(ctx.DeviceConfig().ClangCoverageEnabled() || ctx.DeviceConfig().GcovCoverageEnabled())).
259		FlagWithArg("-D target_build_variant=", c.buildVariant(ctx)).
260		FlagWithArg("-D target_full_treble=", c.sepolicySplit(ctx)).
261		FlagWithArg("-D target_compatible_property=", c.compatibleProperty(ctx)).
262		FlagWithArg("-D target_treble_sysprop_neverallow=", c.trebleSyspropNeverallow(ctx)).
263		FlagWithArg("-D target_enforce_sysprop_owner=", c.enforceSyspropOwner(ctx)).
264		FlagWithArg("-D target_exclude_build_test=", strconv.FormatBool(proptools.Bool(c.properties.Exclude_build_test))).
265		FlagWithArg("-D target_requires_insecure_execmem_for_swiftshader=", strconv.FormatBool(ctx.DeviceConfig().RequiresInsecureExecmemForSwiftshader())).
266		FlagWithArg("-D target_enforce_debugfs_restriction=", c.enforceDebugfsRestrictions(ctx)).
267		FlagWithArg("-D target_recovery=", strconv.FormatBool(c.isTargetRecovery())).
268		Flag(boardApiLevelToM4Macro(ctx, c.properties.Board_api_level)).
269		Flags(flagsToM4Macros(flags)).
270		Flag("-s").
271		Inputs(srcs).
272		Text("> ").Output(conf)
273
274	rule.Build("conf", "Transform policy to conf: "+ctx.ModuleName())
275	return conf
276}
277
278func (c *policyConf) DepsMutator(ctx android.BottomUpMutatorContext) {
279	c.flagDeps(ctx)
280}
281
282func (c *policyConf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
283	if !c.installable() {
284		c.SkipInstall()
285	}
286
287	c.installSource = c.transformPolicyToConf(ctx)
288	c.installPath = android.PathForModuleInstall(ctx, "etc")
289	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
290
291	ctx.SetOutputFiles(android.Paths{c.installSource}, "")
292}
293
294func (c *policyConf) AndroidMkEntries() []android.AndroidMkEntries {
295	return []android.AndroidMkEntries{android.AndroidMkEntries{
296		OutputFile: android.OptionalPathForPath(c.installSource),
297		Class:      "ETC",
298		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
299			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
300				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.installable())
301				entries.SetPath("LOCAL_MODULE_PATH", c.installPath)
302				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
303			},
304		},
305	}}
306}
307
308type policyCilProperties struct {
309	// Name of the output. Default is {module_name}
310	Stem *string
311
312	// Policy file to be compiled to cil file.
313	Src *string `android:"path"`
314
315	// If true, the input policy file is a binary policy that will be decompiled to a cil file.
316	// Defaults to false.
317	Decompile_binary *bool
318
319	// Additional cil files to be added in the end of the output. This is to support workarounds
320	// which are not supported by the policy language.
321	Additional_cil_files []string `android:"path"`
322
323	// Cil files to be filtered out by the filter_out tool of "build_sepolicy". Used to build
324	// exported policies
325	Filter_out []string `android:"path"`
326
327	// Whether to remove line markers (denoted by ;;) out of compiled cil files. Defaults to false
328	Remove_line_marker *bool
329
330	// Whether to run secilc to check compiled policy or not. Defaults to true
331	Secilc_check *bool
332
333	// Whether to ignore neverallow when running secilc check. Defaults to
334	// SELINUX_IGNORE_NEVERALLOWS.
335	Ignore_neverallow *bool
336
337	// Whether this module is directly installable to one of the partitions. Default is true
338	Installable *bool
339}
340
341type policyCil struct {
342	android.ModuleBase
343
344	properties policyCilProperties
345
346	installSource android.Path
347	installPath   android.InstallPath
348}
349
350// se_policy_cil compiles a policy.conf file to a cil file with checkpolicy, and optionally runs
351// secilc to check the output cil file. Affected by SELINUX_IGNORE_NEVERALLOWS.
352func policyCilFactory() android.Module {
353	c := &policyCil{}
354	c.AddProperties(&c.properties)
355	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
356	return c
357}
358
359func (c *policyCil) Installable() bool {
360	return proptools.BoolDefault(c.properties.Installable, true)
361}
362
363func (c *policyCil) stem() string {
364	return proptools.StringDefault(c.properties.Stem, c.Name())
365}
366
367func (c *policyCil) compileConfToCil(ctx android.ModuleContext, conf android.Path) android.OutputPath {
368	cil := pathForModuleOut(ctx, c.stem())
369	rule := android.NewRuleBuilder(pctx, ctx)
370	checkpolicyCmd := rule.Command().BuiltTool("checkpolicy").
371		Flag("-C"). // Write CIL
372		Flag("-M"). // Enable MLS
373		FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
374		FlagWithOutput("-o ", cil).
375		Input(conf)
376
377	if proptools.Bool(c.properties.Decompile_binary) {
378		checkpolicyCmd.Flag("-b") // Read binary
379	}
380
381	if len(c.properties.Filter_out) > 0 {
382		rule.Command().BuiltTool("build_sepolicy").
383			Text("filter_out").
384			Flag("-f").
385			Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)).
386			FlagWithOutput("-t ", cil)
387	}
388
389	if len(c.properties.Additional_cil_files) > 0 {
390		rule.Command().Text("cat").
391			Inputs(android.PathsForModuleSrc(ctx, c.properties.Additional_cil_files)).
392			Text(">> ").Output(cil)
393	}
394
395	if proptools.Bool(c.properties.Remove_line_marker) {
396		rule.Command().Text("grep -v").
397			Text(proptools.ShellEscape(";;")).
398			Text(cil.String()).
399			Text(">").
400			Text(cil.String() + ".tmp").
401			Text("&& mv").
402			Text(cil.String() + ".tmp").
403			Text(cil.String())
404	}
405
406	if proptools.BoolDefault(c.properties.Secilc_check, true) {
407		secilcCmd := rule.Command().BuiltTool("secilc").
408			Flag("-m").                 // Multiple decls
409			FlagWithArg("-M ", "true"). // Enable MLS
410			Flag("-G").                 // expand and remove auto generated attributes
411			FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
412			Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). // Also add cil files which are filtered out
413			Text(cil.String()).
414			FlagWithArg("-o ", os.DevNull).
415			FlagWithArg("-f ", os.DevNull)
416
417		if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) {
418			secilcCmd.Flag("-N")
419		}
420	}
421
422	rule.Build("cil", "Building cil for "+ctx.ModuleName())
423	return cil
424}
425
426func (c *policyCil) GenerateAndroidBuildActions(ctx android.ModuleContext) {
427	if proptools.String(c.properties.Src) == "" {
428		ctx.PropertyErrorf("src", "must be specified")
429		return
430	}
431	conf := android.PathForModuleSrc(ctx, *c.properties.Src)
432	cil := c.compileConfToCil(ctx, conf)
433
434	if !c.Installable() {
435		c.SkipInstall()
436	}
437
438	if c.InstallInDebugRamdisk() {
439		// for userdebug_plat_sepolicy.cil
440		c.installPath = android.PathForModuleInstall(ctx)
441	} else {
442		c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux")
443	}
444	c.installSource = cil
445	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
446
447	ctx.SetOutputFiles(android.Paths{c.installSource}, "")
448}
449
450func (c *policyCil) AndroidMkEntries() []android.AndroidMkEntries {
451	return []android.AndroidMkEntries{android.AndroidMkEntries{
452		OutputFile: android.OptionalPathForPath(c.installSource),
453		Class:      "ETC",
454		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
455			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
456				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable())
457				entries.SetPath("LOCAL_MODULE_PATH", c.installPath)
458				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
459			},
460		},
461	}}
462}
463
464type policyBinaryProperties struct {
465	// Name of the output. Default is {module_name}
466	Stem *string
467
468	// Cil files to be compiled.
469	Srcs []string `android:"path"`
470
471	// Whether to ignore neverallow when running secilc check. Defaults to
472	// SELINUX_IGNORE_NEVERALLOWS.
473	Ignore_neverallow *bool
474
475	// Whether this module is directly installable to one of the partitions. Default is true
476	Installable *bool
477
478	// List of domains that are allowed to be in permissive mode on user builds.
479	Permissive_domains_on_user_builds []string
480}
481
482type policyBinary struct {
483	android.ModuleBase
484
485	properties policyBinaryProperties
486
487	installSource android.Path
488	installPath   android.InstallPath
489}
490
491// se_policy_binary compiles cil files to a binary sepolicy file with secilc.  Usually sources of
492// se_policy_binary come from outputs of se_policy_cil modules.
493func policyBinaryFactory() android.Module {
494	c := &policyBinary{}
495	c.AddProperties(&c.properties)
496	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
497	return c
498}
499
500func (c *policyBinary) InstallInRoot() bool {
501	return c.InstallInRecovery()
502}
503
504func (c *policyBinary) Installable() bool {
505	return proptools.BoolDefault(c.properties.Installable, true)
506}
507
508func (c *policyBinary) stem() string {
509	return proptools.StringDefault(c.properties.Stem, c.Name())
510}
511
512func (c *policyBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
513	if len(c.properties.Srcs) == 0 {
514		ctx.PropertyErrorf("srcs", "must be specified")
515		return
516	}
517	bin := pathForModuleOut(ctx, c.stem()+"_policy")
518	rule := android.NewRuleBuilder(pctx, ctx)
519	secilcCmd := rule.Command().BuiltTool("secilc").
520		Flag("-m").                 // Multiple decls
521		FlagWithArg("-M ", "true"). // Enable MLS
522		Flag("-G").                 // expand and remove auto generated attributes
523		FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
524		Inputs(android.PathsForModuleSrc(ctx, c.properties.Srcs)).
525		FlagWithOutput("-o ", bin).
526		FlagWithArg("-f ", os.DevNull)
527
528	if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) {
529		secilcCmd.Flag("-N")
530	}
531	rule.Temporary(bin)
532
533	// permissive check is performed only in user build (not debuggable).
534	if !ctx.Config().Debuggable() {
535		permissiveDomains := pathForModuleOut(ctx, c.stem()+"_permissive")
536		cmd := rule.Command().BuiltTool("sepolicy-analyze").
537			Input(bin).
538			Text("permissive")
539		// Filter-out domains listed in permissive_domains_on_user_builds
540		allowedDomains := c.properties.Permissive_domains_on_user_builds
541		if len(allowedDomains) != 0 {
542			cmd.Text("| { grep -Fxv")
543			for _, d := range allowedDomains {
544				cmd.FlagWithArg("-e ", proptools.ShellEscape(d))
545			}
546			cmd.Text(" || true; }") // no match doesn't fail the cmd
547		}
548		cmd.Text(" > ").Output(permissiveDomains)
549		rule.Temporary(permissiveDomains)
550
551		msg := `==========\n` +
552			`ERROR: permissive domains not allowed in user builds\n` +
553			`List of invalid domains:`
554
555		rule.Command().Text("if test").
556			FlagWithInput("-s ", permissiveDomains).
557			Text("; then echo").
558			Flag("-e").
559			Text(`"` + msg + `"`).
560			Text("&& cat ").
561			Input(permissiveDomains).
562			Text("; exit 1; fi")
563	}
564
565	out := pathForModuleOut(ctx, c.stem())
566	rule.Command().Text("cp").
567		Flag("-f").
568		Input(bin).
569		Output(out)
570
571	rule.DeleteTemporaryFiles()
572	rule.Build("secilc", "Compiling cil files for "+ctx.ModuleName())
573
574	if !c.Installable() {
575		c.SkipInstall()
576	}
577
578	if c.InstallInRecovery() {
579		// install in root
580		c.installPath = android.PathForModuleInstall(ctx)
581	} else {
582		c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux")
583	}
584	c.installSource = out
585	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
586
587	ctx.SetOutputFiles(android.Paths{c.installSource}, "")
588}
589
590func (c *policyBinary) AndroidMkEntries() []android.AndroidMkEntries {
591	return []android.AndroidMkEntries{android.AndroidMkEntries{
592		OutputFile: android.OptionalPathForPath(c.installSource),
593		Class:      "ETC",
594		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
595			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
596				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable())
597				entries.SetPath("LOCAL_MODULE_PATH", c.installPath)
598				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
599			},
600		},
601	}}
602}
603