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