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.RegisterParallelSingletonModuleType("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.SingletonModule { 120 f := &compatTestModule{} 121 f.AddProperties(&f.properties) 122 android.InitAndroidModule(f) 123 android.AddLoadHook(f, func(ctx android.LoadHookContext) { 124 f.loadHook(ctx) 125 }) 126 return f 127} 128 129type compatTestModule struct { 130 android.SingletonModuleBase 131 properties struct { 132 // Default modules for conf 133 Defaults []string 134 } 135 136 compatTestTimestamp android.ModuleOutPath 137} 138 139func (f *compatTestModule) createPlatPubVersionedModule(ctx android.LoadHookContext, ver string) { 140 confName := fmt.Sprintf("pub_policy_%s.conf", ver) 141 cilName := fmt.Sprintf("pub_policy_%s.cil", ver) 142 platPubVersionedName := fmt.Sprintf("plat_pub_versioned_%s.cil", ver) 143 144 ctx.CreateModule(policyConfFactory, &nameProperties{ 145 Name: proptools.StringPtr(confName), 146 }, &policyConfProperties{ 147 Srcs: []string{ 148 fmt.Sprintf(":se_build_files{.plat_public_%s}", ver), 149 fmt.Sprintf(":se_build_files{.system_ext_public_%s}", ver), 150 fmt.Sprintf(":se_build_files{.product_public_%s}", ver), 151 ":se_build_files{.reqd_mask}", 152 }, 153 Installable: proptools.BoolPtr(false), 154 }, &struct { 155 Defaults []string 156 }{ 157 Defaults: f.properties.Defaults, 158 }) 159 160 ctx.CreateModule(policyCilFactory, &nameProperties{ 161 Name: proptools.StringPtr(cilName), 162 }, &policyCilProperties{ 163 Src: proptools.StringPtr(":" + confName), 164 Filter_out: []string{":reqd_policy_mask.cil"}, 165 Secilc_check: proptools.BoolPtr(false), 166 Installable: proptools.BoolPtr(false), 167 }) 168 169 ctx.CreateModule(versionedPolicyFactory, &nameProperties{ 170 Name: proptools.StringPtr(platPubVersionedName), 171 }, &versionedPolicyProperties{ 172 Base: proptools.StringPtr(":" + cilName), 173 Target_policy: proptools.StringPtr(":" + cilName), 174 Version: proptools.StringPtr(ver), 175 Installable: proptools.BoolPtr(false), 176 }) 177} 178 179func (f *compatTestModule) createCompatTestModule(ctx android.LoadHookContext, ver string) { 180 srcs := []string{ 181 ":plat_sepolicy.cil", 182 ":system_ext_sepolicy.cil", 183 ":product_sepolicy.cil", 184 fmt.Sprintf(":plat_%s.cil", ver), 185 fmt.Sprintf(":%s.compat.cil", ver), 186 fmt.Sprintf(":system_ext_%s.cil", ver), 187 fmt.Sprintf(":system_ext_%s.compat.cil", ver), 188 fmt.Sprintf(":product_%s.cil", ver), 189 } 190 191 if ver == ctx.DeviceConfig().BoardSepolicyVers() { 192 srcs = append(srcs, 193 ":plat_pub_versioned.cil", 194 ":vendor_sepolicy.cil", 195 ":odm_sepolicy.cil", 196 ) 197 } else { 198 srcs = append(srcs, fmt.Sprintf(":plat_pub_versioned_%s.cil", ver)) 199 } 200 201 compatTestName := fmt.Sprintf("%s_compat_test", ver) 202 ctx.CreateModule(policyBinaryFactory, &nameProperties{ 203 Name: proptools.StringPtr(compatTestName), 204 }, &policyBinaryProperties{ 205 Srcs: srcs, 206 Ignore_neverallow: proptools.BoolPtr(true), 207 Installable: proptools.BoolPtr(false), 208 }) 209} 210 211func (f *compatTestModule) loadHook(ctx android.LoadHookContext) { 212 for _, ver := range ctx.DeviceConfig().PlatformSepolicyCompatVersions() { 213 f.createPlatPubVersionedModule(ctx, ver) 214 f.createCompatTestModule(ctx, ver) 215 } 216} 217 218func (f *compatTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { 219 for _, ver := range ctx.DeviceConfig().PlatformSepolicyCompatVersions() { 220 ctx.AddDependency(f, compatTestDepTag, fmt.Sprintf("%s_compat_test", ver)) 221 } 222} 223 224func (f *compatTestModule) GenerateSingletonBuildActions(ctx android.SingletonContext) { 225 // does nothing; se_compat_test is a singeton because two compat test modules don't make sense. 226} 227 228func (f *compatTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 229 var inputs android.Paths 230 ctx.VisitDirectDepsWithTag(compatTestDepTag, func(child android.Module) { 231 outputs := android.OutputFilesForModule(ctx, child, "") 232 if len(outputs) != 1 { 233 panic(fmt.Errorf("Module %q should produce exactly one output, but did %q", ctx.OtherModuleName(child), outputs.Strings())) 234 } 235 236 inputs = append(inputs, outputs[0]) 237 }) 238 239 f.compatTestTimestamp = android.PathForModuleOut(ctx, "timestamp") 240 rule := android.NewRuleBuilder(pctx, ctx) 241 rule.Command().Text("touch").Output(f.compatTestTimestamp).Implicits(inputs) 242 rule.Build("compat", "compat test timestamp for: "+f.Name()) 243} 244 245func (f *compatTestModule) AndroidMkEntries() []android.AndroidMkEntries { 246 return []android.AndroidMkEntries{android.AndroidMkEntries{ 247 Class: "FAKE", 248 // OutputFile is needed, even though BUILD_PHONY_PACKAGE doesn't use it. 249 // Without OutputFile this module won't be exported to Makefile. 250 OutputFile: android.OptionalPathForPath(f.compatTestTimestamp), 251 Include: "$(BUILD_PHONY_PACKAGE)", 252 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 253 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 254 entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", f.compatTestTimestamp.String()) 255 }, 256 }, 257 }} 258} 259