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 "fmt" 19 20 "github.com/google/blueprint/proptools" 21 22 "android/soong/android" 23) 24 25var ( 26 compatTestDepTag = dependencyTag{name: "compat_test"} 27) 28 29func init() { 30 ctx := android.InitRegistrationContext 31 ctx.RegisterModuleType("se_compat_cil", compatCilFactory) 32 ctx.RegisterModuleType("se_compat_test", compatTestFactory) 33} 34 35// se_compat_cil collects and installs backwards compatibility cil files. 36func compatCilFactory() android.Module { 37 c := &compatCil{} 38 c.AddProperties(&c.properties) 39 android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) 40 return c 41} 42 43type compatCil struct { 44 android.ModuleBase 45 properties compatCilProperties 46 installSource android.OptionalPath 47 installPath android.InstallPath 48} 49 50type compatCilProperties struct { 51 // List of source files. Can reference se_build_files type modules with the ":module" syntax. 52 Srcs []string `android:"path"` 53 54 // Output file name. Defaults to module name if unspecified. 55 Stem *string 56 57 // Target version that this module supports. This module will be ignored if platform sepolicy 58 // version is same as this module's version. 59 Version *string 60} 61 62func (c *compatCil) stem() string { 63 return proptools.StringDefault(c.properties.Stem, c.Name()) 64} 65 66func (c *compatCil) expandSeSources(ctx android.ModuleContext) android.Paths { 67 return android.PathsForModuleSrc(ctx, c.properties.Srcs) 68} 69 70func (c *compatCil) shouldSkipBuild(ctx android.ModuleContext) bool { 71 return proptools.String(c.properties.Version) == ctx.DeviceConfig().PlatformSepolicyVersion() 72} 73 74func (c *compatCil) GenerateAndroidBuildActions(ctx android.ModuleContext) { 75 if c.ProductSpecific() || c.SocSpecific() || c.DeviceSpecific() { 76 ctx.ModuleErrorf("Compat cil files only support system and system_ext partitions") 77 } 78 79 if c.shouldSkipBuild(ctx) { 80 return 81 } 82 83 srcPaths := c.expandSeSources(ctx) 84 out := android.PathForModuleGen(ctx, c.Name()) 85 ctx.Build(pctx, android.BuildParams{ 86 Rule: android.Cat, 87 Inputs: srcPaths, 88 Output: out, 89 Description: "Combining compat cil for " + c.Name(), 90 }) 91 92 c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux", "mapping") 93 c.installSource = android.OptionalPathForPath(out) 94 ctx.InstallFile(c.installPath, c.stem(), out) 95 96 if c.installSource.Valid() { 97 ctx.SetOutputFiles(android.Paths{c.installSource.Path()}, "") 98 } 99} 100 101func (c *compatCil) AndroidMkEntries() []android.AndroidMkEntries { 102 if !c.installSource.Valid() { 103 return nil 104 } 105 return []android.AndroidMkEntries{android.AndroidMkEntries{ 106 Class: "ETC", 107 OutputFile: c.installSource, 108 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 109 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 110 entries.SetPath("LOCAL_MODULE_PATH", c.installPath) 111 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) 112 }, 113 }, 114 }} 115} 116 117// se_compat_test checks if compat files ({ver}.cil, {ver}.compat.cil) files are compatible with 118// current policy. 119func compatTestFactory() android.Module { 120 f := &compatTestModule{} 121 f.AddProperties(&f.properties) 122 android.InitAndroidArchModule(f, android.DeviceSupported, android.MultilibCommon) 123 android.AddLoadHook(f, func(ctx android.LoadHookContext) { 124 f.loadHook(ctx) 125 }) 126 return f 127} 128 129type compatTestModule struct { 130 android.ModuleBase 131 properties struct { 132 // Default modules for conf 133 Defaults []string 134 } 135 136 compatTestTimestamp android.ModuleOutPath 137} 138 139func (f *compatTestModule) createCompatTestModule(ctx android.LoadHookContext, ver string) { 140 srcs := []string{ 141 ":plat_sepolicy.cil", 142 ":system_ext_sepolicy.cil", 143 ":product_sepolicy.cil", 144 fmt.Sprintf(":plat_%s.cil", ver), 145 fmt.Sprintf(":%s.compat.cil", ver), 146 fmt.Sprintf(":system_ext_%s.cil", ver), 147 fmt.Sprintf(":system_ext_%s.compat.cil", ver), 148 fmt.Sprintf(":product_%s.cil", ver), 149 } 150 151 if ver == ctx.DeviceConfig().BoardSepolicyVers() { 152 srcs = append(srcs, 153 ":plat_pub_versioned.cil", 154 ":vendor_sepolicy.cil", 155 ":odm_sepolicy.cil", 156 ) 157 } else { 158 srcs = append(srcs, fmt.Sprintf(":%s_plat_pub_versioned.cil", ver)) 159 } 160 161 compatTestName := fmt.Sprintf("%s_compat_test", ver) 162 ctx.CreateModule(policyBinaryFactory, &nameProperties{ 163 Name: proptools.StringPtr(compatTestName), 164 }, &policyBinaryProperties{ 165 Srcs: srcs, 166 Ignore_neverallow: proptools.BoolPtr(true), 167 Installable: proptools.BoolPtr(false), 168 }) 169} 170 171func (f *compatTestModule) loadHook(ctx android.LoadHookContext) { 172 for _, ver := range ctx.DeviceConfig().PlatformSepolicyCompatVersions() { 173 f.createCompatTestModule(ctx, ver) 174 } 175} 176 177func (f *compatTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { 178 for _, ver := range ctx.DeviceConfig().PlatformSepolicyCompatVersions() { 179 ctx.AddDependency(f, compatTestDepTag, fmt.Sprintf("%s_compat_test", ver)) 180 } 181} 182 183func (f *compatTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 184 if ctx.ModuleName() != "sepolicy_compat_test" || ctx.ModuleDir() != "system/sepolicy/compat" { 185 // two compat test modules don't make sense. 186 ctx.ModuleErrorf("There can only be 1 se_compat_test module named sepolicy_compat_test in system/sepolicy/compat") 187 } 188 var inputs android.Paths 189 ctx.VisitDirectDepsWithTag(compatTestDepTag, func(child android.Module) { 190 outputs := android.OutputFilesForModule(ctx, child, "") 191 if len(outputs) != 1 { 192 panic(fmt.Errorf("Module %q should produce exactly one output, but did %q", ctx.OtherModuleName(child), outputs.Strings())) 193 } 194 195 inputs = append(inputs, outputs[0]) 196 }) 197 198 f.compatTestTimestamp = android.PathForModuleOut(ctx, "timestamp") 199 rule := android.NewRuleBuilder(pctx, ctx) 200 rule.Command().Text("touch").Output(f.compatTestTimestamp).Implicits(inputs) 201 rule.Build("compat", "compat test timestamp for: "+f.Name()) 202} 203 204func (f *compatTestModule) AndroidMkEntries() []android.AndroidMkEntries { 205 return []android.AndroidMkEntries{android.AndroidMkEntries{ 206 Class: "FAKE", 207 // OutputFile is needed, even though BUILD_PHONY_PACKAGE doesn't use it. 208 // Without OutputFile this module won't be exported to Makefile. 209 OutputFile: android.OptionalPathForPath(f.compatTestTimestamp), 210 Include: "$(BUILD_PHONY_PACKAGE)", 211 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 212 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 213 entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", f.compatTestTimestamp.String()) 214 }, 215 }, 216 }} 217} 218