Inseob Kim | 7e8bd1e | 2021-03-17 18:59:43 +0900 | [diff] [blame] | 1 | // Copyright (C) 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 | |
| 15 | package selinux |
| 16 | |
| 17 | import ( |
| 18 | "fmt" |
| 19 | "os" |
| 20 | "strconv" |
| 21 | |
| 22 | "github.com/google/blueprint/proptools" |
| 23 | |
| 24 | "android/soong/android" |
| 25 | ) |
| 26 | |
| 27 | const ( |
| 28 | // TODO: sync with Android.mk |
| 29 | MlsSens = 1 |
| 30 | MlsCats = 1024 |
| 31 | PolicyVers = 30 |
| 32 | ) |
| 33 | |
| 34 | func init() { |
| 35 | android.RegisterModuleType("se_policy_conf", policyConfFactory) |
Inseob Kim | df1a0de | 2021-03-17 19:05:02 +0900 | [diff] [blame^] | 36 | android.RegisterModuleType("se_policy_cil", policyCilFactory) |
Inseob Kim | 7e8bd1e | 2021-03-17 18:59:43 +0900 | [diff] [blame] | 37 | } |
| 38 | |
| 39 | type policyConfProperties struct { |
| 40 | // Name of the output. Default is {module_name} |
| 41 | Stem *string |
| 42 | |
| 43 | // Policy files to be compiled to cil file. |
| 44 | Srcs []string `android:"path"` |
| 45 | |
| 46 | // Target build variant (user / userdebug / eng). Default follows the current lunch target |
| 47 | Build_variant *string |
| 48 | |
| 49 | // Whether to exclude build test or not. Default is false |
| 50 | Exclude_build_test *bool |
| 51 | |
| 52 | // Whether to include asan specific policies or not. Default follows the current lunch target |
| 53 | With_asan *bool |
| 54 | |
| 55 | // Whether to build CTS specific policy or not. Default is false |
| 56 | Cts *bool |
| 57 | |
| 58 | // Whether this module is directly installable to one of the partitions. Default is true |
| 59 | Installable *bool |
| 60 | } |
| 61 | |
| 62 | type policyConf struct { |
| 63 | android.ModuleBase |
| 64 | |
| 65 | properties policyConfProperties |
| 66 | |
| 67 | installSource android.Path |
| 68 | installPath android.InstallPath |
| 69 | } |
| 70 | |
| 71 | // se_policy_conf merges collection of policy files into a policy.conf file to be processed by |
| 72 | // checkpolicy. |
| 73 | func policyConfFactory() android.Module { |
| 74 | c := &policyConf{} |
| 75 | c.AddProperties(&c.properties) |
| 76 | android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) |
| 77 | return c |
| 78 | } |
| 79 | |
| 80 | func (c *policyConf) installable() bool { |
| 81 | return proptools.BoolDefault(c.properties.Installable, true) |
| 82 | } |
| 83 | |
| 84 | func (c *policyConf) stem() string { |
| 85 | return proptools.StringDefault(c.properties.Stem, c.Name()) |
| 86 | } |
| 87 | |
| 88 | func (c *policyConf) buildVariant(ctx android.ModuleContext) string { |
| 89 | if variant := proptools.String(c.properties.Build_variant); variant != "" { |
| 90 | return variant |
| 91 | } |
| 92 | if ctx.Config().Eng() { |
| 93 | return "eng" |
| 94 | } |
| 95 | if ctx.Config().Debuggable() { |
| 96 | return "userdebug" |
| 97 | } |
| 98 | return "user" |
| 99 | } |
| 100 | |
| 101 | func (c *policyConf) cts() bool { |
| 102 | return proptools.Bool(c.properties.Cts) |
| 103 | } |
| 104 | |
| 105 | func (c *policyConf) withAsan(ctx android.ModuleContext) string { |
| 106 | isAsanDevice := android.InList("address", ctx.Config().SanitizeDevice()) |
| 107 | return strconv.FormatBool(proptools.BoolDefault(c.properties.With_asan, isAsanDevice)) |
| 108 | } |
| 109 | |
| 110 | func (c *policyConf) sepolicySplit(ctx android.ModuleContext) string { |
| 111 | if c.cts() { |
| 112 | return "cts" |
| 113 | } |
| 114 | return strconv.FormatBool(ctx.DeviceConfig().SepolicySplit()) |
| 115 | } |
| 116 | |
| 117 | func (c *policyConf) compatibleProperty(ctx android.ModuleContext) string { |
| 118 | if c.cts() { |
| 119 | return "cts" |
| 120 | } |
| 121 | return "true" |
| 122 | } |
| 123 | |
| 124 | func (c *policyConf) trebleSyspropNeverallow(ctx android.ModuleContext) string { |
| 125 | if c.cts() { |
| 126 | return "cts" |
| 127 | } |
| 128 | return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenTrebleSyspropNeverallow()) |
| 129 | } |
| 130 | |
| 131 | func (c *policyConf) enforceSyspropOwner(ctx android.ModuleContext) string { |
| 132 | if c.cts() { |
| 133 | return "cts" |
| 134 | } |
| 135 | return strconv.FormatBool(!ctx.DeviceConfig().BuildBrokenEnforceSyspropOwner()) |
| 136 | } |
| 137 | |
| 138 | func (c *policyConf) transformPolicyToConf(ctx android.ModuleContext) android.OutputPath { |
| 139 | conf := android.PathForModuleOut(ctx, "conf").OutputPath |
| 140 | rule := android.NewRuleBuilder(pctx, ctx) |
| 141 | rule.Command().Tool(ctx.Config().PrebuiltBuildTool(ctx, "m4")). |
| 142 | Flag("--fatal-warnings"). |
| 143 | FlagForEachArg("-D ", ctx.DeviceConfig().SepolicyM4Defs()). |
| 144 | FlagWithArg("-D mls_num_sens=", strconv.Itoa(MlsSens)). |
| 145 | FlagWithArg("-D mls_num_cats=", strconv.Itoa(MlsCats)). |
| 146 | FlagWithArg("-D target_arch=", ctx.DeviceConfig().DeviceArch()). |
| 147 | FlagWithArg("-D target_with_asan=", c.withAsan(ctx)). |
| 148 | FlagWithArg("-D target_with_native_coverage=", strconv.FormatBool(ctx.DeviceConfig().ClangCoverageEnabled() || ctx.DeviceConfig().GcovCoverageEnabled())). |
| 149 | FlagWithArg("-D target_build_variant=", c.buildVariant(ctx)). |
| 150 | FlagWithArg("-D target_full_treble=", c.sepolicySplit(ctx)). |
| 151 | FlagWithArg("-D target_compatible_property=", c.compatibleProperty(ctx)). |
| 152 | FlagWithArg("-D target_treble_sysprop_neverallow=", c.trebleSyspropNeverallow(ctx)). |
| 153 | FlagWithArg("-D target_enforce_sysprop_owner=", c.enforceSyspropOwner(ctx)). |
| 154 | FlagWithArg("-D target_exclude_build_test=", strconv.FormatBool(proptools.Bool(c.properties.Exclude_build_test))). |
| 155 | FlagWithArg("-D target_requires_insecure_execmem_for_swiftshader=", strconv.FormatBool(ctx.DeviceConfig().RequiresInsecureExecmemForSwiftshader())). |
| 156 | Flag("-s"). |
| 157 | Inputs(android.PathsForModuleSrc(ctx, c.properties.Srcs)). |
| 158 | Text("> ").Output(conf) |
| 159 | |
| 160 | rule.Build("conf", "Transform policy to conf: "+ctx.ModuleName()) |
| 161 | return conf |
| 162 | } |
| 163 | |
| 164 | func (c *policyConf) DepsMutator(ctx android.BottomUpMutatorContext) { |
| 165 | // do nothing |
| 166 | } |
| 167 | |
| 168 | func (c *policyConf) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| 169 | c.installSource = c.transformPolicyToConf(ctx) |
| 170 | c.installPath = android.PathForModuleInstall(ctx, "etc") |
| 171 | ctx.InstallFile(c.installPath, c.stem(), c.installSource) |
| 172 | |
| 173 | if !c.installable() { |
| 174 | c.SkipInstall() |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | func (c *policyConf) AndroidMkEntries() []android.AndroidMkEntries { |
| 179 | return []android.AndroidMkEntries{android.AndroidMkEntries{ |
| 180 | OutputFile: android.OptionalPathForPath(c.installSource), |
| 181 | Class: "ETC", |
| 182 | ExtraEntries: []android.AndroidMkExtraEntriesFunc{ |
| 183 | func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { |
| 184 | entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.installable()) |
| 185 | entries.SetPath("LOCAL_MODULE_PATH", c.installPath.ToMakePath()) |
| 186 | entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) |
| 187 | }, |
| 188 | }, |
| 189 | }} |
| 190 | } |
| 191 | |
| 192 | func (c *policyConf) OutputFiles(tag string) (android.Paths, error) { |
| 193 | if tag == "" { |
| 194 | return android.Paths{c.installSource}, nil |
| 195 | } |
| 196 | return nil, fmt.Errorf("Unknown tag %q", tag) |
| 197 | } |
| 198 | |
| 199 | var _ android.OutputFileProducer = (*policyConf)(nil) |
Inseob Kim | df1a0de | 2021-03-17 19:05:02 +0900 | [diff] [blame^] | 200 | |
| 201 | type policyCilProperties struct { |
| 202 | // Name of the output. Default is {module_name} |
| 203 | Stem *string |
| 204 | |
| 205 | // Policy file to be compiled to cil file. |
| 206 | Src *string `android:"path"` |
| 207 | |
| 208 | // Additional cil files to be added in the end of the output. This is to support workarounds |
| 209 | // which are not supported by the policy language. |
| 210 | Additional_cil_files []string `android:"path"` |
| 211 | |
| 212 | // Cil files to be filtered out by the filter_out tool of "build_sepolicy". Used to build |
| 213 | // exported policies |
| 214 | Filter_out []string `android:"path"` |
| 215 | |
| 216 | // Whether to remove line markers (denoted by ;;) out of compiled cil files. Defaults to false |
| 217 | Remove_line_marker *bool |
| 218 | |
| 219 | // Whether to run secilc to check compiled policy or not. Defaults to true |
| 220 | Secilc_check *bool |
| 221 | |
| 222 | // Whether to ignore neverallow when running secilc check. Defaults to |
| 223 | // SELINUX_IGNORE_NEVERALLOWS. |
| 224 | Ignore_neverallow *bool |
| 225 | |
| 226 | // Whether this module is directly installable to one of the partitions. Default is true |
| 227 | Installable *bool |
| 228 | } |
| 229 | |
| 230 | type policyCil struct { |
| 231 | android.ModuleBase |
| 232 | |
| 233 | properties policyCilProperties |
| 234 | |
| 235 | installSource android.Path |
| 236 | installPath android.InstallPath |
| 237 | } |
| 238 | |
| 239 | // se_policy_cil compiles a policy.conf file to a cil file with checkpolicy, and optionally runs |
| 240 | // secilc to check the output cil file. Affected by SELINUX_IGNORE_NEVERALLOWS. |
| 241 | func policyCilFactory() android.Module { |
| 242 | c := &policyCil{} |
| 243 | c.AddProperties(&c.properties) |
| 244 | android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon) |
| 245 | return c |
| 246 | } |
| 247 | |
| 248 | func (c *policyCil) Installable() bool { |
| 249 | return proptools.BoolDefault(c.properties.Installable, true) |
| 250 | } |
| 251 | |
| 252 | func (c *policyCil) stem() string { |
| 253 | return proptools.StringDefault(c.properties.Stem, c.Name()) |
| 254 | } |
| 255 | |
| 256 | func (c *policyCil) compileConfToCil(ctx android.ModuleContext, conf android.Path) android.OutputPath { |
| 257 | cil := android.PathForModuleOut(ctx, c.stem()).OutputPath |
| 258 | rule := android.NewRuleBuilder(pctx, ctx) |
| 259 | rule.Command().BuiltTool("checkpolicy"). |
| 260 | Flag("-C"). // Write CIL |
| 261 | Flag("-M"). // Enable MLS |
| 262 | FlagWithArg("-c ", strconv.Itoa(PolicyVers)). |
| 263 | FlagWithOutput("-o ", cil). |
| 264 | Input(conf) |
| 265 | |
| 266 | if len(c.properties.Additional_cil_files) > 0 { |
| 267 | rule.Command().Text("cat"). |
| 268 | Inputs(android.PathsForModuleSrc(ctx, c.properties.Additional_cil_files)). |
| 269 | Text(">> ").Output(cil) |
| 270 | } |
| 271 | |
| 272 | if len(c.properties.Filter_out) > 0 { |
| 273 | rule.Command().BuiltTool("build_sepolicy"). |
| 274 | Text("filter_out"). |
| 275 | Flag("-f"). |
| 276 | Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). |
| 277 | FlagWithOutput("-t ", cil) |
| 278 | } |
| 279 | |
| 280 | if proptools.Bool(c.properties.Remove_line_marker) { |
| 281 | rule.Command().Text("grep -v"). |
| 282 | Text(proptools.ShellEscape(";;")). |
| 283 | Text(cil.String()). |
| 284 | Text(">"). |
| 285 | Text(cil.String() + ".tmp"). |
| 286 | Text("&& mv"). |
| 287 | Text(cil.String() + ".tmp"). |
| 288 | Text(cil.String()) |
| 289 | } |
| 290 | |
| 291 | if proptools.BoolDefault(c.properties.Secilc_check, true) { |
| 292 | secilcCmd := rule.Command().BuiltTool("secilc"). |
| 293 | Flag("-m"). // Multiple decls |
| 294 | FlagWithArg("-M ", "true"). // Enable MLS |
| 295 | Flag("-G"). // expand and remove auto generated attributes |
| 296 | FlagWithArg("-c ", strconv.Itoa(PolicyVers)). |
| 297 | Inputs(android.PathsForModuleSrc(ctx, c.properties.Filter_out)). // Also add cil files which are filtered out |
| 298 | Text(cil.String()). |
| 299 | FlagWithArg("-o ", os.DevNull). |
| 300 | FlagWithArg("-f ", os.DevNull) |
| 301 | |
| 302 | if proptools.BoolDefault(c.properties.Ignore_neverallow, ctx.Config().SelinuxIgnoreNeverallows()) { |
| 303 | secilcCmd.Flag("-N") |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | rule.Build("cil", "Building cil for "+ctx.ModuleName()) |
| 308 | return cil |
| 309 | } |
| 310 | |
| 311 | func (c *policyCil) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| 312 | if proptools.String(c.properties.Src) == "" { |
| 313 | ctx.PropertyErrorf("src", "must be specified") |
| 314 | return |
| 315 | } |
| 316 | conf := android.PathForModuleSrc(ctx, *c.properties.Src) |
| 317 | cil := c.compileConfToCil(ctx, conf) |
| 318 | |
| 319 | c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux") |
| 320 | c.installSource = cil |
| 321 | ctx.InstallFile(c.installPath, c.stem(), c.installSource) |
| 322 | |
| 323 | if !c.Installable() { |
| 324 | c.SkipInstall() |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | func (c *policyCil) AndroidMkEntries() []android.AndroidMkEntries { |
| 329 | return []android.AndroidMkEntries{android.AndroidMkEntries{ |
| 330 | OutputFile: android.OptionalPathForPath(c.installSource), |
| 331 | Class: "ETC", |
| 332 | ExtraEntries: []android.AndroidMkExtraEntriesFunc{ |
| 333 | func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { |
| 334 | entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !c.Installable()) |
| 335 | entries.SetPath("LOCAL_MODULE_PATH", c.installPath.ToMakePath()) |
| 336 | entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.stem()) |
| 337 | }, |
| 338 | }, |
| 339 | }} |
| 340 | } |
| 341 | |
| 342 | func (c *policyCil) OutputFiles(tag string) (android.Paths, error) { |
| 343 | if tag == "" { |
| 344 | return android.Paths{c.installSource}, nil |
| 345 | } |
| 346 | return nil, fmt.Errorf("Unknown tag %q", tag) |
| 347 | } |
| 348 | |
| 349 | var _ android.OutputFileProducer = (*policyCil)(nil) |