diff options
author | 2024-03-29 17:44:07 +0900 | |
---|---|---|
committer | 2024-04-02 10:04:22 +0900 | |
commit | 5339184e323b455c86419e37d0491c67a1dbf114 (patch) | |
tree | 9d305b2fa83142bc05735d004366b023a76b636d | |
parent | 4d4eb59a2ba7961eb84705df5e9997fb3dab938e (diff) |
Add support to generate fsverity metadata
By setting fsverity.inputs, android_filesystem can now generate
.fsv_meta and BuildManifest.apk. It has been done by Makefile because
Makefile is the only one knowing all installed files. But now
android_filesystem is aware of all artifacts, so we can move fsverity
metadata generation into Soong.
Bug: 330282551
Test: m aosp_cf_system_x86_64 and see output
Change-Id: Iae4dd83eaede960c263bfba537211df4ff4b36bd
-rw-r--r-- | filesystem/Android.bp | 1 | ||||
-rw-r--r-- | filesystem/filesystem.go | 21 | ||||
-rw-r--r-- | filesystem/fsverity_metadata.go | 166 |
3 files changed, 182 insertions, 6 deletions
diff --git a/filesystem/Android.bp b/filesystem/Android.bp index cead5fc01..18dd5535c 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -18,6 +18,7 @@ bootstrap_go_package { "avb_gen_vbmeta_image.go", "bootimg.go", "filesystem.go", + "fsverity_metadata.go", "logical_partition.go", "raw_binary.go", "system_image.go", diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index efc889ccb..5ec7efe38 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -57,7 +57,7 @@ type filesystem struct { output android.OutputPath installDir android.InstallPath - // For testing. Keeps the result of CopyDepsToZip() + // For testing. Keeps the result of CopySpecsToDir() entries []string } @@ -120,6 +120,8 @@ type filesystemProperties struct { // modules would be installed to the same location as a make module, they will overwrite // the make version. Include_make_built_files string + + Fsverity fsverityProperties } // android_filesystem packages a set of modules and their transitive dependencies into a filesystem @@ -176,6 +178,10 @@ func (f *filesystem) installFileName() string { return f.BaseModuleName() + ".img" } +func (f *filesystem) partitionName() string { + return proptools.StringDefault(f.properties.Partition_name, f.Name()) +} + var pctx = android.NewPackageContext("android/soong/filesystem") func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -255,10 +261,12 @@ func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) androi builder := android.NewRuleBuilder(pctx, ctx) // Wipe the root dir to get rid of leftover files from prior builds builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) - f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir) + specs := f.gatherFilteredPackagingSpecs(ctx) + f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir) f.buildNonDepsFiles(ctx, builder, rootDir) f.addMakeBuiltFiles(ctx, builder, rootDir) + f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) // run host_init_verifier // Ideally we should have a concept of pluggable linters that verify the generated image. @@ -338,13 +346,12 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. addStr("avb_algorithm", algorithm) key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key)) addPath("avb_key_path", key) - partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name()) - addStr("partition_name", partitionName) + addStr("partition_name", f.partitionName()) avb_add_hashtree_footer_args := "--do_not_generate_fec" if hashAlgorithm := proptools.String(f.properties.Avb_hash_algorithm); hashAlgorithm != "" { avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm } - securityPatchKey := "com.android.build." + partitionName + ".security_patch" + securityPatchKey := "com.android.build." + f.partitionName() + ".security_patch" securityPatchValue := ctx.Config().PlatformSecurityPatch() avb_add_hashtree_footer_args += " --prop " + securityPatchKey + ":" + securityPatchValue addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args) @@ -388,9 +395,11 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) builder := android.NewRuleBuilder(pctx, ctx) // Wipe the root dir to get rid of leftover files from prior builds builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) - f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir) + specs := f.gatherFilteredPackagingSpecs(ctx) + f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir) f.buildNonDepsFiles(ctx, builder, rootDir) + f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath cmd := builder.Command(). diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go new file mode 100644 index 000000000..70f94e0a9 --- /dev/null +++ b/filesystem/fsverity_metadata.go @@ -0,0 +1,166 @@ +// Copyright (C) 2024 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 filesystem + +import ( + "path/filepath" + "strings" + + "android/soong/android" +) + +type fsverityProperties struct { + // Patterns of files for fsverity metadata generation. For each matched file, a .fsv_meta file + // will be generated and included to the filesystem image. + // etc/security/fsverity/BuildManifest.apk will also be generated which contains information + // about generated .fsv_meta files. + Inputs []string +} + +func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.OutputPath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) { + var buf strings.Builder + for _, spec := range matchedSpecs { + buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String()) + buf.WriteRune('\n') + } + android.WriteFileRuleVerbatim(ctx, outputPath, buf.String()) +} + +func (f *filesystem) buildFsverityMetadataFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir android.OutputPath, rebasedDir android.OutputPath) { + match := func(path string) bool { + for _, pattern := range f.properties.Fsverity.Inputs { + if matched, err := filepath.Match(pattern, path); matched { + return true + } else if err != nil { + ctx.PropertyErrorf("fsverity.inputs", "bad pattern %q", pattern) + return false + } + } + return false + } + + var matchedSpecs []android.PackagingSpec + for _, relPath := range android.SortedKeys(specs) { + if match(relPath) { + matchedSpecs = append(matchedSpecs, specs[relPath]) + } + } + + if len(matchedSpecs) == 0 { + return + } + + fsverityBuilderPath := android.PathForModuleOut(ctx, "fsverity_builder.sh") + metadataGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_metadata_generator") + fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity") + + cmd := builder.Command().Tool(fsverityBuilderPath) + + // STEP 1: generate .fsv_meta + var sb strings.Builder + sb.WriteString("set -e\n") + cmd.Implicit(metadataGeneratorPath).Implicit(fsverityPath) + for _, spec := range matchedSpecs { + // srcPath is copied by CopySpecsToDir() + srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage()) + destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta") + sb.WriteString(metadataGeneratorPath.String()) + sb.WriteString(" --fsverity-path ") + sb.WriteString(fsverityPath.String()) + sb.WriteString(" --signature none --hash-alg sha256 --output ") + sb.WriteString(destPath.String()) + sb.WriteRune(' ') + sb.WriteString(srcPath.String()) + sb.WriteRune('\n') + } + + // STEP 2: generate signed BuildManifest.apk + // STEP 2-1: generate build_manifest.pb + assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets") + manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb") + manifestGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_manifest_generator") + cmd.Implicit(manifestGeneratorPath) + sb.WriteString("rm -rf ") + sb.WriteString(assetsPath.String()) + sb.WriteString(" && mkdir -p ") + sb.WriteString(assetsPath.String()) + sb.WriteRune('\n') + sb.WriteString(manifestGeneratorPath.String()) + sb.WriteString(" --fsverity-path ") + sb.WriteString(fsverityPath.String()) + sb.WriteString(" --base-dir ") + sb.WriteString(rootDir.String()) + sb.WriteString(" --output ") + sb.WriteString(manifestPbPath.String()) + sb.WriteRune(' ') + + manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list") + f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath.OutputPath, matchedSpecs, rebasedDir) + sb.WriteRune('@') + sb.WriteString(manifestGeneratorListPath.String()) + sb.WriteRune('\n') + cmd.Implicit(manifestGeneratorListPath) + + // STEP 2-2: generate BuildManifest.apk (unsigned) + aapt2Path := ctx.Config().HostToolPath(ctx, "aapt2") + apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", "BuildManifest.apk") + manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml") + // package-export is currently generated by Makefile. + // TODO(b/330282551): fully migrate into Soong + frameworkResPath := android.PathForArbitraryOutput(ctx, "target/common/obj/APPS/framework-res_intermediates/package-export.apk") + cmd.Implicit(aapt2Path) + cmd.Implicit(manifestTemplatePath) + cmd.Implicit(frameworkResPath) + + sb.WriteString(aapt2Path.String()) + sb.WriteString(" link -o ") + sb.WriteString(apkPath.String()) + sb.WriteString(" -A ") + sb.WriteString(assetsPath.String()) + sb.WriteString(" -I ") + sb.WriteString(frameworkResPath.String()) + minSdkVersion := ctx.Config().PlatformSdkCodename() + if minSdkVersion == "REL" { + minSdkVersion = ctx.Config().PlatformSdkVersion().String() + } + sb.WriteString(" --min-sdk-version ") + sb.WriteString(minSdkVersion) + sb.WriteString(" --version-code ") + sb.WriteString(ctx.Config().PlatformSdkVersion().String()) + sb.WriteString(" --version-name ") + sb.WriteString(ctx.Config().AppsDefaultVersionName()) + sb.WriteString(" --manifest ") + sb.WriteString(manifestTemplatePath.String()) + sb.WriteString(" --rename-manifest-package com.android.security.fsverity_metadata.") + sb.WriteString(f.partitionName()) + sb.WriteRune('\n') + + // STEP 2-3: sign BuildManifest.apk + apksignerPath := ctx.Config().HostToolPath(ctx, "apksigner") + pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx) + cmd.Implicit(apksignerPath) + cmd.Implicit(pemPath) + cmd.Implicit(keyPath) + sb.WriteString(apksignerPath.String()) + sb.WriteString(" sign --in ") + sb.WriteString(apkPath.String()) + sb.WriteString(" --cert ") + sb.WriteString(pemPath.String()) + sb.WriteString(" --key ") + sb.WriteString(keyPath.String()) + sb.WriteRune('\n') + + android.WriteExecutableFileRuleVerbatim(ctx, fsverityBuilderPath, sb.String()) +} |