• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 run secilc to check compiled policy or not. Defaults to true
328	Secilc_check *bool
329
330	// Whether to ignore neverallow when running secilc check. Defaults to
331	// SELINUX_IGNORE_NEVERALLOWS.
332	Ignore_neverallow *bool
333
334	// Whether this module is directly installable to one of the partitions. Default is true
335	Installable *bool
336}
337
338type policyCil struct {
339	android.ModuleBase
340
341	properties policyCilProperties
342
343	installSource android.Path
344	installPath   android.InstallPath
345}
346
347// se_policy_cil compiles a policy.conf file to a cil file with checkpolicy, and optionally runs
348// secilc to check the output cil file. Affected by SELINUX_IGNORE_NEVERALLOWS.
349func policyCilFactory() android.Module {
350	c := &policyCil{}
351	c.AddProperties(&c.properties)
352	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
353	return c
354}
355
356func (c *policyCil) Installable() bool {
357	return proptools.BoolDefault(c.properties.Installable, true)
358}
359
360func (c *policyCil) stem() string {
361	return proptools.StringDefault(c.properties.Stem, c.Name())
362}
363
364func (c *policyCil) compileConfToCil(ctx android.ModuleContext, conf android.Path) android.OutputPath {
365	cil := pathForModuleOut(ctx, c.stem())
366	rule := android.NewRuleBuilder(pctx, ctx)
367	checkpolicyCmd := rule.Command().BuiltTool("checkpolicy").
368		Flag("-C"). // Write CIL
369		Flag("-M"). // Enable MLS
370		FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
371		FlagWithOutput("-o ", cil).
372		Input(conf)
373
374	if proptools.Bool(c.properties.Decompile_binary) {
375		checkpolicyCmd.Flag("-b") // Read binary
376	}
377
378	if len(c.properties.Filter_out) > 0 {
379		rule.Command().BuiltTool("build_sepolicy").
380			Text("filter_out").
381			Flag("-f").
382			Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)).
383			FlagWithOutput("-t ", cil)
384	}
385
386	if len(c.properties.Additional_cil_files) > 0 {
387		rule.Command().Text("cat").
388			Inputs(android.PathsForModuleSrc(ctx, c.properties.Additional_cil_files)).
389			Text(">> ").Output(cil)
390	}
391
392	if proptools.BoolDefault(c.properties.Secilc_check, true) {
393		secilcCmd := rule.Command().BuiltTool("secilc").
394			Flag("-m").                 // Multiple decls
395			FlagWithArg("-M ", "true"). // Enable MLS
396			Flag("-G").                 // expand and remove auto generated attributes
397			FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
398			Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). // Also add cil files which are filtered out
399			Text(cil.String()).
400			FlagWithArg("-o ", os.DevNull).
401			FlagWithArg("-f ", os.DevNull)
402
403		if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) {
404			secilcCmd.Flag("-N")
405		}
406	}
407
408	rule.Build("cil", "Building cil for "+ctx.ModuleName())
409	return cil
410}
411
412func (c *policyCil) GenerateAndroidBuildActions(ctx android.ModuleContext) {
413	if proptools.String(c.properties.Src) == "" {
414		ctx.PropertyErrorf("src", "must be specified")
415		return
416	}
417	conf := android.PathForModuleSrc(ctx, *c.properties.Src)
418	cil := c.compileConfToCil(ctx, conf)
419
420	if !c.Installable() {
421		c.SkipInstall()
422	}
423
424	if c.InstallInDebugRamdisk() {
425		// for userdebug_plat_sepolicy.cil
426		c.installPath = android.PathForModuleInstall(ctx)
427	} else {
428		c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux")
429	}
430	c.installSource = cil
431	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
432
433	ctx.SetOutputFiles(android.Paths{c.installSource}, "")
434}
435
436func (c *policyCil) AndroidMkEntries() []android.AndroidMkEntries {
437	return []android.AndroidMkEntries{android.AndroidMkEntries{
438		OutputFile: android.OptionalPathForPath(c.installSource),
439		Class:      "ETC",
440		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
441			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
442				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable())
443				entries.SetPath("LOCAL_MODULE_PATH", c.installPath)
444				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
445			},
446		},
447	}}
448}
449
450type policyBinaryProperties struct {
451	// Name of the output. Default is {module_name}
452	Stem *string
453
454	// Cil files to be compiled.
455	Srcs []string `android:"path"`
456
457	// Whether to ignore neverallow when running secilc check. Defaults to
458	// SELINUX_IGNORE_NEVERALLOWS.
459	Ignore_neverallow *bool
460
461	// Whether this module is directly installable to one of the partitions. Default is true
462	Installable *bool
463
464	// List of domains that are allowed to be in permissive mode on user builds.
465	Permissive_domains_on_user_builds []string
466}
467
468type policyBinary struct {
469	android.ModuleBase
470
471	properties policyBinaryProperties
472
473	installSource android.Path
474	installPath   android.InstallPath
475}
476
477// se_policy_binary compiles cil files to a binary sepolicy file with secilc.  Usually sources of
478// se_policy_binary come from outputs of se_policy_cil modules.
479func policyBinaryFactory() android.Module {
480	c := &policyBinary{}
481	c.AddProperties(&c.properties)
482	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
483	return c
484}
485
486func (c *policyBinary) InstallInRoot() bool {
487	return c.InstallInRecovery()
488}
489
490func (c *policyBinary) Installable() bool {
491	return proptools.BoolDefault(c.properties.Installable, true)
492}
493
494func (c *policyBinary) stem() string {
495	return proptools.StringDefault(c.properties.Stem, c.Name())
496}
497
498func (c *policyBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
499	if len(c.properties.Srcs) == 0 {
500		ctx.PropertyErrorf("srcs", "must be specified")
501		return
502	}
503	bin := pathForModuleOut(ctx, c.stem()+"_policy")
504	rule := android.NewRuleBuilder(pctx, ctx)
505	secilcCmd := rule.Command().BuiltTool("secilc").
506		Flag("-m").                 // Multiple decls
507		FlagWithArg("-M ", "true"). // Enable MLS
508		Flag("-G").                 // expand and remove auto generated attributes
509		FlagWithArg("-c ", strconv.Itoa(PolicyVers)).
510		Inputs(android.PathsForModuleSrc(ctx, c.properties.Srcs)).
511		FlagWithOutput("-o ", bin).
512		FlagWithArg("-f ", os.DevNull)
513
514	if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) {
515		secilcCmd.Flag("-N")
516	}
517	rule.Temporary(bin)
518
519	// permissive check is performed only in user build (not debuggable).
520	if !ctx.Config().Debuggable() {
521		permissiveDomains := pathForModuleOut(ctx, c.stem()+"_permissive")
522		cmd := rule.Command().BuiltTool("sepolicy-analyze").
523			Input(bin).
524			Text("permissive")
525		// Filter-out domains listed in permissive_domains_on_user_builds
526		allowedDomains := c.properties.Permissive_domains_on_user_builds
527		if len(allowedDomains) != 0 {
528			cmd.Text("| { grep -Fxv")
529			for _, d := range allowedDomains {
530				cmd.FlagWithArg("-e ", proptools.ShellEscape(d))
531			}
532			cmd.Text(" || true; }") // no match doesn't fail the cmd
533		}
534		cmd.Text(" > ").Output(permissiveDomains)
535		rule.Temporary(permissiveDomains)
536
537		msg := `==========\n` +
538			`ERROR: permissive domains not allowed in user builds\n` +
539			`List of invalid domains:`
540
541		rule.Command().Text("if test").
542			FlagWithInput("-s ", permissiveDomains).
543			Text("; then echo").
544			Flag("-e").
545			Text(`"` + msg + `"`).
546			Text("&& cat ").
547			Input(permissiveDomains).
548			Text("; exit 1; fi")
549	}
550
551	out := pathForModuleOut(ctx, c.stem())
552	rule.Command().Text("cp").
553		Flag("-f").
554		Input(bin).
555		Output(out)
556
557	rule.DeleteTemporaryFiles()
558	rule.Build("secilc", "Compiling cil files for "+ctx.ModuleName())
559
560	if !c.Installable() {
561		c.SkipInstall()
562	}
563
564	if c.InstallInRecovery() {
565		// install in root
566		c.installPath = android.PathForModuleInstall(ctx)
567	} else {
568		c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux")
569	}
570	c.installSource = out
571	ctx.InstallFile(c.installPath, c.stem(), c.installSource)
572
573	ctx.SetOutputFiles(android.Paths{c.installSource}, "")
574}
575
576func (c *policyBinary) AndroidMkEntries() []android.AndroidMkEntries {
577	return []android.AndroidMkEntries{android.AndroidMkEntries{
578		OutputFile: android.OptionalPathForPath(c.installSource),
579		Class:      "ETC",
580		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
581			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
582				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable())
583				entries.SetPath("LOCAL_MODULE_PATH", c.installPath)
584				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem())
585			},
586		},
587	}}
588}
589