1// Copyright (C) 2019 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 "io" 20 "strings" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26 "android/soong/sysprop" 27) 28 29type selinuxContextsProperties struct { 30 // Filenames under sepolicy directories, which will be used to generate contexts file. 31 Srcs []string `android:"path"` 32 33 Product_variables struct { 34 Debuggable struct { 35 Srcs []string 36 } 37 38 Address_sanitize struct { 39 Srcs []string 40 } 41 } 42 43 // Whether reqd_mask directory is included to sepolicy directories or not. 44 Reqd_mask *bool 45 46 // Whether the comments in generated contexts file will be removed or not. 47 Remove_comment *bool 48 49 // Whether the result context file is sorted with fc_sort or not. 50 Fc_sort *bool 51 52 // Make this module available when building for recovery 53 Recovery_available *bool 54} 55 56type fileContextsProperties struct { 57 // flatten_apex can be used to specify additional sources of file_contexts. 58 // Apex paths, /system/apex/{apex_name}, will be amended to the paths of file_contexts 59 // entries. 60 Flatten_apex struct { 61 Srcs []string 62 } 63} 64 65type selinuxContextsModule struct { 66 android.ModuleBase 67 68 properties selinuxContextsProperties 69 fileContextsProperties fileContextsProperties 70 build func(ctx android.ModuleContext, inputs android.Paths) android.Path 71 deps func(ctx android.BottomUpMutatorContext) 72 outputPath android.Path 73 installPath android.InstallPath 74} 75 76var ( 77 reuseContextsDepTag = dependencyTag{name: "reuseContexts"} 78 syspropLibraryDepTag = dependencyTag{name: "sysprop_library"} 79) 80 81func init() { 82 pctx.HostBinToolVariable("fc_sort", "fc_sort") 83 84 android.RegisterModuleType("file_contexts", fileFactory) 85 android.RegisterModuleType("hwservice_contexts", hwServiceFactory) 86 android.RegisterModuleType("property_contexts", propertyFactory) 87 android.RegisterModuleType("service_contexts", serviceFactory) 88 android.RegisterModuleType("keystore2_key_contexts", keystoreKeyFactory) 89} 90 91func (m *selinuxContextsModule) InstallInRoot() bool { 92 return m.InRecovery() 93} 94 95func (m *selinuxContextsModule) InstallInRecovery() bool { 96 // ModuleBase.InRecovery() checks the image variant 97 return m.InRecovery() 98} 99 100func (m *selinuxContextsModule) onlyInRecovery() bool { 101 // ModuleBase.InstallInRecovery() checks commonProperties.Recovery property 102 return m.ModuleBase.InstallInRecovery() 103} 104 105func (m *selinuxContextsModule) DepsMutator(ctx android.BottomUpMutatorContext) { 106 if m.deps != nil { 107 m.deps(ctx) 108 } 109 110 if m.InRecovery() && !m.onlyInRecovery() { 111 ctx.AddFarVariationDependencies([]blueprint.Variation{ 112 {Mutator: "image", Variation: android.CoreVariation}, 113 }, reuseContextsDepTag, ctx.ModuleName()) 114 } 115} 116 117func (m *selinuxContextsModule) propertyContextsDeps(ctx android.BottomUpMutatorContext) { 118 for _, lib := range sysprop.SyspropLibraries(ctx.Config()) { 119 ctx.AddFarVariationDependencies([]blueprint.Variation{}, syspropLibraryDepTag, lib) 120 } 121} 122 123func (m *selinuxContextsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 124 if m.InRecovery() { 125 // Installing context files at the root of the recovery partition 126 m.installPath = android.PathForModuleInstall(ctx) 127 } else { 128 m.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") 129 } 130 131 if m.InRecovery() && !m.onlyInRecovery() { 132 dep := ctx.GetDirectDepWithTag(m.Name(), reuseContextsDepTag) 133 134 if reuseDeps, ok := dep.(*selinuxContextsModule); ok { 135 m.outputPath = reuseDeps.outputPath 136 ctx.InstallFile(m.installPath, m.Name(), m.outputPath) 137 return 138 } 139 } 140 141 var inputs android.Paths 142 143 ctx.VisitDirectDepsWithTag(android.SourceDepTag, func(dep android.Module) { 144 segroup, ok := dep.(*fileGroup) 145 if !ok { 146 ctx.ModuleErrorf("srcs dependency %q is not an selinux filegroup", 147 ctx.OtherModuleName(dep)) 148 return 149 } 150 151 if ctx.ProductSpecific() { 152 inputs = append(inputs, segroup.ProductPrivateSrcs()...) 153 } else if ctx.SocSpecific() { 154 if ctx.DeviceConfig().BoardSepolicyVers() == ctx.DeviceConfig().PlatformSepolicyVersion() { 155 inputs = append(inputs, segroup.SystemVendorSrcs()...) 156 } 157 inputs = append(inputs, segroup.VendorSrcs()...) 158 } else if ctx.DeviceSpecific() { 159 inputs = append(inputs, segroup.OdmSrcs()...) 160 } else if ctx.SystemExtSpecific() { 161 inputs = append(inputs, segroup.SystemExtPrivateSrcs()...) 162 } else { 163 inputs = append(inputs, segroup.SystemPrivateSrcs()...) 164 inputs = append(inputs, segroup.SystemPublicSrcs()...) 165 } 166 167 if proptools.Bool(m.properties.Reqd_mask) { 168 if ctx.SocSpecific() || ctx.DeviceSpecific() { 169 inputs = append(inputs, segroup.VendorReqdMaskSrcs()...) 170 } else { 171 inputs = append(inputs, segroup.SystemReqdMaskSrcs()...) 172 } 173 } 174 }) 175 176 for _, src := range m.properties.Srcs { 177 // Module sources are handled above with VisitDirectDepsWithTag 178 if android.SrcIsModule(src) == "" { 179 inputs = append(inputs, android.PathForModuleSrc(ctx, src)) 180 } 181 } 182 183 m.outputPath = m.build(ctx, inputs) 184 ctx.InstallFile(m.installPath, ctx.ModuleName(), m.outputPath) 185} 186 187func newModule() *selinuxContextsModule { 188 m := &selinuxContextsModule{} 189 m.AddProperties( 190 &m.properties, 191 ) 192 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 193 android.AddLoadHook(m, func(ctx android.LoadHookContext) { 194 m.selinuxContextsHook(ctx) 195 }) 196 return m 197} 198 199func (m *selinuxContextsModule) selinuxContextsHook(ctx android.LoadHookContext) { 200 // TODO: clean this up to use build/soong/android/variable.go after b/79249983 201 var srcs []string 202 203 if ctx.Config().Debuggable() { 204 srcs = append(srcs, m.properties.Product_variables.Debuggable.Srcs...) 205 } 206 207 for _, sanitize := range ctx.Config().SanitizeDevice() { 208 if sanitize == "address" { 209 srcs = append(srcs, m.properties.Product_variables.Address_sanitize.Srcs...) 210 break 211 } 212 } 213 214 m.properties.Srcs = append(m.properties.Srcs, srcs...) 215} 216 217func (m *selinuxContextsModule) AndroidMk() android.AndroidMkData { 218 return android.AndroidMkData{ 219 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 220 nameSuffix := "" 221 if m.InRecovery() && !m.onlyInRecovery() { 222 nameSuffix = ".recovery" 223 } 224 fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") 225 fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) 226 fmt.Fprintln(w, "LOCAL_MODULE :=", name+nameSuffix) 227 data.Entries.WriteLicenseVariables(w) 228 fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") 229 if m.Owner() != "" { 230 fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", m.Owner()) 231 } 232 fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional") 233 fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", m.outputPath.String()) 234 fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", m.installPath.ToMakePath().String()) 235 fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name) 236 fmt.Fprintln(w, "include $(BUILD_PREBUILT)") 237 }, 238 } 239} 240 241func (m *selinuxContextsModule) ImageMutatorBegin(ctx android.BaseModuleContext) { 242 if proptools.Bool(m.properties.Recovery_available) && m.InstallInRecovery() { 243 ctx.PropertyErrorf("recovery_available", 244 "doesn't make sense at the same time as `recovery: true`") 245 } 246} 247 248func (m *selinuxContextsModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool { 249 return !m.InstallInRecovery() 250} 251 252func (m *selinuxContextsModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 253 return false 254} 255 256func (m *selinuxContextsModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 257 return false 258} 259 260func (m *selinuxContextsModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { 261 return false 262} 263 264func (m *selinuxContextsModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { 265 return m.InstallInRecovery() || proptools.Bool(m.properties.Recovery_available) 266} 267 268func (m *selinuxContextsModule) ExtraImageVariations(ctx android.BaseModuleContext) []string { 269 return nil 270} 271 272func (m *selinuxContextsModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { 273} 274 275var _ android.ImageInterface = (*selinuxContextsModule)(nil) 276 277func (m *selinuxContextsModule) buildGeneralContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 278 ret := android.PathForModuleGen(ctx, ctx.ModuleName()+"_m4out") 279 280 rule := android.NewRuleBuilder(pctx, ctx) 281 282 rule.Command(). 283 Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")). 284 Text("--fatal-warnings -s"). 285 FlagForEachArg("-D", ctx.DeviceConfig().SepolicyM4Defs()). 286 Inputs(inputs). 287 FlagWithOutput("> ", ret) 288 289 if proptools.Bool(m.properties.Remove_comment) { 290 rule.Temporary(ret) 291 292 remove_comment_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_remove_comment") 293 294 rule.Command(). 295 Text("sed -e 's/#.*$//' -e '/^$/d'"). 296 Input(ret). 297 FlagWithOutput("> ", remove_comment_output) 298 299 ret = remove_comment_output 300 } 301 302 if proptools.Bool(m.properties.Fc_sort) { 303 rule.Temporary(ret) 304 305 sorted_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_sorted") 306 307 rule.Command(). 308 Tool(ctx.Config().HostToolPath(ctx, "fc_sort")). 309 FlagWithInput("-i ", ret). 310 FlagWithOutput("-o ", sorted_output) 311 312 ret = sorted_output 313 } 314 315 rule.Build("selinux_contexts", "building contexts: "+m.Name()) 316 317 rule.DeleteTemporaryFiles() 318 319 return ret 320} 321 322func (m *selinuxContextsModule) buildFileContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 323 if m.properties.Fc_sort == nil { 324 m.properties.Fc_sort = proptools.BoolPtr(true) 325 } 326 327 rule := android.NewRuleBuilder(pctx, ctx) 328 329 if ctx.Config().FlattenApex() { 330 for _, src := range m.fileContextsProperties.Flatten_apex.Srcs { 331 if m := android.SrcIsModule(src); m != "" { 332 ctx.ModuleErrorf( 333 "Module srcs dependency %q is not supported for flatten_apex.srcs", m) 334 return nil 335 } 336 for _, path := range android.PathsForModuleSrcExcludes(ctx, []string{src}, nil) { 337 out := android.PathForModuleGen(ctx, "flattened_apex", path.Rel()) 338 apex_path := "/system/apex/" + strings.Replace( 339 strings.TrimSuffix(path.Base(), "-file_contexts"), 340 ".", "\\\\.", -1) 341 342 rule.Command(). 343 Text("awk '/object_r/{printf(\""+apex_path+"%s\\n\",$0)}'"). 344 Input(path). 345 FlagWithOutput("> ", out) 346 347 inputs = append(inputs, out) 348 } 349 } 350 } 351 352 rule.Build(m.Name(), "flattened_apex_file_contexts") 353 return m.buildGeneralContexts(ctx, inputs) 354} 355 356func fileFactory() android.Module { 357 m := newModule() 358 m.AddProperties(&m.fileContextsProperties) 359 m.build = m.buildFileContexts 360 return m 361} 362 363func (m *selinuxContextsModule) buildHwServiceContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 364 if m.properties.Remove_comment == nil { 365 m.properties.Remove_comment = proptools.BoolPtr(true) 366 } 367 368 return m.buildGeneralContexts(ctx, inputs) 369} 370 371func (m *selinuxContextsModule) checkVendorPropertyNamespace(ctx android.ModuleContext, inputs android.Paths) android.Paths { 372 shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel() 373 ApiLevelR := android.ApiLevelOrPanic(ctx, "R") 374 375 rule := android.NewRuleBuilder(pctx, ctx) 376 377 // This list is from vts_treble_sys_prop_test. 378 allowedPropertyPrefixes := []string{ 379 "ctl.odm.", 380 "ctl.vendor.", 381 "ctl.start$odm.", 382 "ctl.start$vendor.", 383 "ctl.stop$odm.", 384 "ctl.stop$vendor.", 385 "init.svc.odm.", 386 "init.svc.vendor.", 387 "ro.boot.", 388 "ro.hardware.", 389 "ro.odm.", 390 "ro.vendor.", 391 "odm.", 392 "persist.odm.", 393 "persist.vendor.", 394 "vendor.", 395 } 396 397 // persist.camera is also allowed for devices launching with R or eariler 398 if shippingApiLevel.LessThanOrEqualTo(ApiLevelR) { 399 allowedPropertyPrefixes = append(allowedPropertyPrefixes, "persist.camera.") 400 } 401 402 var allowedContextPrefixes []string 403 404 if shippingApiLevel.GreaterThanOrEqualTo(ApiLevelR) { 405 // This list is from vts_treble_sys_prop_test. 406 allowedContextPrefixes = []string{ 407 "vendor_", 408 "odm_", 409 } 410 } 411 412 var ret android.Paths 413 for _, input := range inputs { 414 cmd := rule.Command(). 415 BuiltTool("check_prop_prefix"). 416 FlagWithInput("--property-contexts ", input). 417 FlagForEachArg("--allowed-property-prefix ", proptools.ShellEscapeList(allowedPropertyPrefixes)). // contains shell special character '$' 418 FlagForEachArg("--allowed-context-prefix ", allowedContextPrefixes) 419 420 if !ctx.DeviceConfig().BuildBrokenVendorPropertyNamespace() { 421 cmd.Flag("--strict") 422 } 423 424 out := android.PathForModuleGen(ctx, "namespace_checked").Join(ctx, input.String()) 425 rule.Command().Text("cp -f").Input(input).Output(out) 426 ret = append(ret, out) 427 } 428 rule.Build("check_namespace", "checking namespace of "+ctx.ModuleName()) 429 return ret 430} 431 432func (m *selinuxContextsModule) buildPropertyContexts(ctx android.ModuleContext, inputs android.Paths) android.Path { 433 // vendor/odm properties are enforced for devices launching with Android Q or later. So, if 434 // vendor/odm, make sure that only vendor/odm properties exist. 435 shippingApiLevel := ctx.DeviceConfig().ShippingApiLevel() 436 ApiLevelQ := android.ApiLevelOrPanic(ctx, "Q") 437 if (ctx.SocSpecific() || ctx.DeviceSpecific()) && shippingApiLevel.GreaterThanOrEqualTo(ApiLevelQ) { 438 inputs = m.checkVendorPropertyNamespace(ctx, inputs) 439 } 440 441 builtCtxFile := m.buildGeneralContexts(ctx, inputs) 442 443 var apiFiles android.Paths 444 ctx.VisitDirectDepsWithTag(syspropLibraryDepTag, func(c android.Module) { 445 i, ok := c.(interface{ CurrentSyspropApiFile() android.OptionalPath }) 446 if !ok { 447 panic(fmt.Errorf("unknown dependency %q for %q", ctx.OtherModuleName(c), ctx.ModuleName())) 448 } 449 if api := i.CurrentSyspropApiFile(); api.Valid() { 450 apiFiles = append(apiFiles, api.Path()) 451 } 452 }) 453 454 // check compatibility with sysprop_library 455 if len(apiFiles) > 0 { 456 out := android.PathForModuleGen(ctx, ctx.ModuleName()+"_api_checked") 457 rule := android.NewRuleBuilder(pctx, ctx) 458 459 msg := `\n******************************\n` + 460 `API of sysprop_library doesn't match with property_contexts\n` + 461 `Please fix the breakage and rebuild.\n` + 462 `******************************\n` 463 464 rule.Command(). 465 Text("( "). 466 BuiltTool("sysprop_type_checker"). 467 FlagForEachInput("--api ", apiFiles). 468 FlagWithInput("--context ", builtCtxFile). 469 Text(" || ( echo").Flag("-e"). 470 Flag(`"` + msg + `"`). 471 Text("; exit 38) )") 472 473 rule.Command().Text("cp -f").Input(builtCtxFile).Output(out) 474 rule.Build("property_contexts_check_api", "checking API: "+m.Name()) 475 builtCtxFile = out 476 } 477 478 return builtCtxFile 479} 480 481func hwServiceFactory() android.Module { 482 m := newModule() 483 m.build = m.buildHwServiceContexts 484 return m 485} 486 487func propertyFactory() android.Module { 488 m := newModule() 489 m.build = m.buildPropertyContexts 490 m.deps = m.propertyContextsDeps 491 return m 492} 493 494func serviceFactory() android.Module { 495 m := newModule() 496 m.build = m.buildGeneralContexts 497 return m 498} 499 500func keystoreKeyFactory() android.Module { 501 m := newModule() 502 m.build = m.buildGeneralContexts 503 return m 504} 505