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/proptools" 23 24 "android/soong/android" 25) 26 27const ( 28 coreMode = "core" 29 recoveryMode = "recovery" 30) 31 32type selinuxContextsProperties struct { 33 // Filenames under sepolicy directories, which will be used to generate contexts file. 34 Srcs []string `android:"path"` 35 36 Product_variables struct { 37 Debuggable struct { 38 Srcs []string 39 } 40 41 Address_sanitize struct { 42 Srcs []string 43 } 44 } 45 46 // Whether reqd_mask directory is included to sepolicy directories or not. 47 Reqd_mask *bool 48 49 // Whether the comments in generated contexts file will be removed or not. 50 Remove_comment *bool 51 52 // Whether the result context file is sorted with fc_sort or not. 53 Fc_sort *bool 54 55 // Make this module available when building for recovery 56 Recovery_available *bool 57 58 InRecovery bool `blueprint:"mutated"` 59} 60 61type fileContextsProperties struct { 62 // flatten_apex can be used to specify additional sources of file_contexts. 63 // Apex paths, /system/apex/{apex_name}, will be amended to the paths of file_contexts 64 // entries. 65 Flatten_apex struct { 66 Srcs []string 67 } 68} 69 70type selinuxContextsModule struct { 71 android.ModuleBase 72 73 properties selinuxContextsProperties 74 fileContextsProperties fileContextsProperties 75 build func(ctx android.ModuleContext, inputs android.Paths) 76 outputPath android.ModuleGenPath 77 installPath android.InstallPath 78} 79 80var ( 81 reuseContextsDepTag = dependencyTag{name: "reuseContexts"} 82) 83 84func init() { 85 pctx.HostBinToolVariable("fc_sort", "fc_sort") 86 87 android.RegisterModuleType("file_contexts", fileFactory) 88 android.RegisterModuleType("hwservice_contexts", hwServiceFactory) 89 android.RegisterModuleType("property_contexts", propertyFactory) 90 android.RegisterModuleType("service_contexts", serviceFactory) 91 92 android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { 93 ctx.BottomUp("selinux_contexts", selinuxContextsMutator).Parallel() 94 }) 95} 96 97func (m *selinuxContextsModule) inRecovery() bool { 98 return m.properties.InRecovery || m.ModuleBase.InstallInRecovery() 99} 100 101func (m *selinuxContextsModule) onlyInRecovery() bool { 102 return m.ModuleBase.InstallInRecovery() 103} 104 105func (m *selinuxContextsModule) InstallInRecovery() bool { 106 return m.inRecovery() 107} 108 109func (m *selinuxContextsModule) InstallInRoot() bool { 110 return m.inRecovery() 111} 112 113func (m *selinuxContextsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 114 if m.inRecovery() { 115 // Installing context files at the root of the recovery partition 116 m.installPath = android.PathForModuleInstall(ctx) 117 } else { 118 m.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") 119 } 120 121 if m.inRecovery() && !m.onlyInRecovery() { 122 dep := ctx.GetDirectDepWithTag(m.Name(), reuseContextsDepTag) 123 124 if reuseDeps, ok := dep.(*selinuxContextsModule); ok { 125 m.outputPath = reuseDeps.outputPath 126 ctx.InstallFile(m.installPath, m.Name(), m.outputPath) 127 return 128 } 129 } 130 131 var inputs android.Paths 132 133 ctx.VisitDirectDepsWithTag(android.SourceDepTag, func(dep android.Module) { 134 segroup, ok := dep.(*fileGroup) 135 if !ok { 136 ctx.ModuleErrorf("srcs dependency %q is not an selinux filegroup", 137 ctx.OtherModuleName(dep)) 138 return 139 } 140 141 if ctx.ProductSpecific() { 142 inputs = append(inputs, segroup.ProductPrivateSrcs()...) 143 } else if ctx.SocSpecific() { 144 inputs = append(inputs, segroup.SystemVendorSrcs()...) 145 inputs = append(inputs, segroup.VendorSrcs()...) 146 } else if ctx.DeviceSpecific() { 147 inputs = append(inputs, segroup.OdmSrcs()...) 148 } else if ctx.SystemExtSpecific() { 149 inputs = append(inputs, segroup.SystemExtPrivateSrcs()...) 150 } else { 151 inputs = append(inputs, segroup.SystemPrivateSrcs()...) 152 153 if ctx.Config().ProductCompatibleProperty() { 154 inputs = append(inputs, segroup.SystemPublicSrcs()...) 155 } 156 } 157 158 if proptools.Bool(m.properties.Reqd_mask) { 159 inputs = append(inputs, segroup.SystemReqdMaskSrcs()...) 160 } 161 }) 162 163 for _, src := range m.properties.Srcs { 164 // Module sources are handled above with VisitDirectDepsWithTag 165 if android.SrcIsModule(src) == "" { 166 inputs = append(inputs, android.PathForModuleSrc(ctx, src)) 167 } 168 } 169 170 m.build(ctx, inputs) 171} 172 173func newModule() *selinuxContextsModule { 174 m := &selinuxContextsModule{} 175 m.AddProperties( 176 &m.properties, 177 ) 178 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) 179 android.AddLoadHook(m, func(ctx android.LoadHookContext) { 180 m.selinuxContextsHook(ctx) 181 }) 182 return m 183} 184 185func (m *selinuxContextsModule) selinuxContextsHook(ctx android.LoadHookContext) { 186 // TODO: clean this up to use build/soong/android/variable.go after b/79249983 187 var srcs []string 188 189 if ctx.Config().Debuggable() { 190 srcs = append(srcs, m.properties.Product_variables.Debuggable.Srcs...) 191 } 192 193 for _, sanitize := range ctx.Config().SanitizeDevice() { 194 if sanitize == "address" { 195 srcs = append(srcs, m.properties.Product_variables.Address_sanitize.Srcs...) 196 break 197 } 198 } 199 200 m.properties.Srcs = append(m.properties.Srcs, srcs...) 201} 202 203func (m *selinuxContextsModule) AndroidMk() android.AndroidMkData { 204 return android.AndroidMkData{ 205 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 206 nameSuffix := "" 207 if m.inRecovery() && !m.onlyInRecovery() { 208 nameSuffix = ".recovery" 209 } 210 fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") 211 fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) 212 fmt.Fprintln(w, "LOCAL_MODULE :=", name+nameSuffix) 213 fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") 214 if m.Owner() != "" { 215 fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", m.Owner()) 216 } 217 fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional") 218 fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", m.outputPath.String()) 219 fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", m.installPath.ToMakePath().String()) 220 fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name) 221 fmt.Fprintln(w, "include $(BUILD_PREBUILT)") 222 }, 223 } 224} 225 226func selinuxContextsMutator(ctx android.BottomUpMutatorContext) { 227 m, ok := ctx.Module().(*selinuxContextsModule) 228 if !ok { 229 return 230 } 231 232 var coreVariantNeeded bool = true 233 var recoveryVariantNeeded bool = false 234 if proptools.Bool(m.properties.Recovery_available) { 235 recoveryVariantNeeded = true 236 } 237 238 if m.ModuleBase.InstallInRecovery() { 239 recoveryVariantNeeded = true 240 coreVariantNeeded = false 241 } 242 243 var variants []string 244 if coreVariantNeeded { 245 variants = append(variants, coreMode) 246 } 247 if recoveryVariantNeeded { 248 variants = append(variants, recoveryMode) 249 } 250 mod := ctx.CreateVariations(variants...) 251 252 for i, v := range variants { 253 if v == recoveryMode { 254 m := mod[i].(*selinuxContextsModule) 255 m.properties.InRecovery = true 256 257 if coreVariantNeeded { 258 ctx.AddInterVariantDependency(reuseContextsDepTag, m, mod[i-1]) 259 } 260 } 261 } 262} 263 264func (m *selinuxContextsModule) buildGeneralContexts(ctx android.ModuleContext, inputs android.Paths) { 265 m.outputPath = android.PathForModuleGen(ctx, ctx.ModuleName()+"_m4out") 266 267 rule := android.NewRuleBuilder() 268 269 rule.Command(). 270 Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")). 271 Text("--fatal-warnings -s"). 272 FlagForEachArg("-D", ctx.DeviceConfig().SepolicyM4Defs()). 273 Inputs(inputs). 274 FlagWithOutput("> ", m.outputPath) 275 276 if proptools.Bool(m.properties.Remove_comment) { 277 rule.Temporary(m.outputPath) 278 279 remove_comment_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_remove_comment") 280 281 rule.Command(). 282 Text("sed -e 's/#.*$//' -e '/^$/d'"). 283 Input(m.outputPath). 284 FlagWithOutput("> ", remove_comment_output) 285 286 m.outputPath = remove_comment_output 287 } 288 289 if proptools.Bool(m.properties.Fc_sort) { 290 rule.Temporary(m.outputPath) 291 292 sorted_output := android.PathForModuleGen(ctx, ctx.ModuleName()+"_sorted") 293 294 rule.Command(). 295 Tool(ctx.Config().HostToolPath(ctx, "fc_sort")). 296 FlagWithInput("-i ", m.outputPath). 297 FlagWithOutput("-o ", sorted_output) 298 299 m.outputPath = sorted_output 300 } 301 302 rule.Build(pctx, ctx, "selinux_contexts", m.Name()) 303 304 rule.DeleteTemporaryFiles() 305 306 ctx.InstallFile(m.installPath, ctx.ModuleName(), m.outputPath) 307} 308 309func (m *selinuxContextsModule) buildFileContexts(ctx android.ModuleContext, inputs android.Paths) { 310 if m.properties.Fc_sort == nil { 311 m.properties.Fc_sort = proptools.BoolPtr(true) 312 } 313 314 rule := android.NewRuleBuilder() 315 316 if ctx.Config().FlattenApex() { 317 for _, src := range m.fileContextsProperties.Flatten_apex.Srcs { 318 if m := android.SrcIsModule(src); m != "" { 319 ctx.ModuleErrorf( 320 "Module srcs dependency %q is not supported for flatten_apex.srcs", m) 321 return 322 } 323 for _, path := range android.PathsForModuleSrcExcludes(ctx, []string{src}, nil) { 324 out := android.PathForModuleGen(ctx, "flattened_apex", path.Rel()) 325 apex_path := "/system/apex/" + strings.Replace( 326 strings.TrimSuffix(path.Base(), "-file_contexts"), 327 ".", "\\\\.", -1) 328 329 rule.Command(). 330 Text("awk '/object_r/{printf(\""+apex_path+"%s\\n\",$0)}'"). 331 Input(path). 332 FlagWithOutput("> ", out) 333 334 inputs = append(inputs, out) 335 } 336 } 337 } 338 339 rule.Build(pctx, ctx, m.Name(), "flattened_apex_file_contexts") 340 m.buildGeneralContexts(ctx, inputs) 341} 342 343func fileFactory() android.Module { 344 m := newModule() 345 m.AddProperties(&m.fileContextsProperties) 346 m.build = m.buildFileContexts 347 return m 348} 349 350func (m *selinuxContextsModule) buildHwServiceContexts(ctx android.ModuleContext, inputs android.Paths) { 351 if m.properties.Remove_comment == nil { 352 m.properties.Remove_comment = proptools.BoolPtr(true) 353 } 354 355 m.buildGeneralContexts(ctx, inputs) 356} 357 358func hwServiceFactory() android.Module { 359 m := newModule() 360 m.build = m.buildHwServiceContexts 361 return m 362} 363 364func propertyFactory() android.Module { 365 m := newModule() 366 m.build = m.buildGeneralContexts 367 return m 368} 369 370func serviceFactory() android.Module { 371 m := newModule() 372 m.build = m.buildGeneralContexts 373 return m 374} 375