// Copyright 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package selinux import ( "fmt" "path/filepath" "sort" "strings" "android/soong/android" ) func init() { android.RegisterModuleType("se_build_files", buildFilesFactory) } // se_build_files gathers policy files from sepolicy dirs, and acts like a filegroup. A tag with // partition(plat, system_ext, product) and scope(public, private) is used to select directories. // Supported tags are: "plat", "plat_public", "system_ext", "system_ext_public", "product", // "product_public", and "reqd_mask". func buildFilesFactory() android.Module { module := &buildFiles{} module.AddProperties(&module.properties) android.InitAndroidModule(module) return module } type buildFilesProperties struct { // list of source file suffixes used to collect selinux policy files. // Source files will be looked up in the following local directories: // system/sepolicy/{public, private, vendor, reqd_mask} // and directories specified by following config variables: // BOARD_SEPOLICY_DIRS, BOARD_ODM_SEPOLICY_DIRS // SYSTEM_EXT_PUBLIC_SEPOLICY_DIR, SYSTEM_EXT_PRIVATE_SEPOLICY_DIR Srcs []string } type buildFiles struct { android.ModuleBase properties buildFilesProperties srcs map[string]android.Paths } func (b *buildFiles) findSrcsInDirs(ctx android.ModuleContext, dirs ...string) android.Paths { result := android.Paths{} for _, file := range b.properties.Srcs { for _, dir := range dirs { path := filepath.Join(dir, file) files, err := ctx.GlobWithDeps(path, nil) if err != nil { ctx.ModuleErrorf("glob: %s", err.Error()) } for _, f := range files { result = append(result, android.PathForSource(ctx, f)) } } } return result } func (b *buildFiles) DepsMutator(ctx android.BottomUpMutatorContext) { // do nothing } func (b *buildFiles) OutputFiles(tag string) (android.Paths, error) { if paths, ok := b.srcs[tag]; ok { return paths, nil } return nil, fmt.Errorf("unknown tag %q. Supported tags are: %q", tag, strings.Join(android.SortedStringKeys(b.srcs), " ")) } var _ android.OutputFileProducer = (*buildFiles)(nil) type partition int const ( system partition = iota system_ext product ) type scope int const ( public scope = iota private ) type sepolicyDir struct { partition partition scope scope paths []string } func (p partition) String() string { switch p { case system: return "plat" case system_ext: return "system_ext" case product: return "product" default: panic(fmt.Sprintf("Unknown partition %#v", p)) } } func (b *buildFiles) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Sepolicy directories should be included in the following order. // - system_public // - system_private // - system_ext_public // - system_ext_private // - product_public // - product_private dirs := []sepolicyDir{ sepolicyDir{partition: system, scope: public, paths: []string{filepath.Join(ctx.ModuleDir(), "public")}}, sepolicyDir{partition: system, scope: private, paths: []string{filepath.Join(ctx.ModuleDir(), "private")}}, sepolicyDir{partition: system_ext, scope: public, paths: ctx.DeviceConfig().SystemExtPublicSepolicyDirs()}, sepolicyDir{partition: system_ext, scope: private, paths: ctx.DeviceConfig().SystemExtPrivateSepolicyDirs()}, sepolicyDir{partition: product, scope: public, paths: ctx.Config().ProductPublicSepolicyDirs()}, sepolicyDir{partition: product, scope: private, paths: ctx.Config().ProductPrivateSepolicyDirs()}, } if !sort.SliceIsSorted(dirs, func(i, j int) bool { if dirs[i].partition != dirs[j].partition { return dirs[i].partition < dirs[j].partition } return dirs[i].scope < dirs[j].scope }) { panic("dirs is not sorted") } // Exported cil policy files are built with the following policies. // // - plat_pub_policy.cil: exported 'system' // - system_ext_pub_policy.cil: exported 'system' and 'system_ext' // - pub_policy.cil: exported 'system', 'system_ext', and 'product' // // cil policy files are built with the following policies. // // - plat_policy.cil: 'system', including private // - system_ext_policy.cil: 'system_ext', including private // - product_sepolicy.cil: 'product', including private // // gatherDirsFor collects all needed directories for given partition and scope. For example, // // - gatherDirsFor(system_ext, private) will return system + system_ext (including private) // - gatherDirsFor(product, public) will return system + system_ext + product (public only) // // "dirs" should be sorted before calling this. gatherDirsFor := func(p partition, s scope) []string { var ret []string for _, d := range dirs { if d.partition <= p && d.scope <= s { ret = append(ret, d.paths...) } } return ret } reqdMaskDir := filepath.Join(ctx.ModuleDir(), "reqd_mask") b.srcs = make(map[string]android.Paths) b.srcs[".reqd_mask"] = b.findSrcsInDirs(ctx, reqdMaskDir) for _, p := range []partition{system, system_ext, product} { b.srcs["."+p.String()] = b.findSrcsInDirs(ctx, gatherDirsFor(p, private)...) // reqd_mask is needed for public policies b.srcs["."+p.String()+"_public"] = b.findSrcsInDirs(ctx, append(gatherDirsFor(p, public), reqdMaskDir)...) } // A special tag, "plat_vendor", includes minimized vendor policies required to boot. // - system/sepolicy/public // - system/sepolicy/reqd_mask // - system/sepolicy/vendor // This is for minimized vendor partition, e.g. microdroid's vendor platVendorDir := filepath.Join(ctx.ModuleDir(), "vendor") b.srcs[".plat_vendor"] = b.findSrcsInDirs(ctx, append(gatherDirsFor(system, public), reqdMaskDir, platVendorDir)...) }