diff --git a/tools/cmd/mkcosi/generator/cih.go b/tools/cmd/mkcosi/generator/cih.go index fc719cf1a..022c8bf44 100644 --- a/tools/cmd/mkcosi/generator/cih.go +++ b/tools/cmd/mkcosi/generator/cih.go @@ -29,9 +29,16 @@ import ( // cihPartitionDef describes one expected partition in a CIH image. type cihPartitionDef struct { - Name string // GPT partition name (e.g. "USR-A") - TypeGUID string // Partition type GUID, lowercase - UUID string // Unique partition GUID, lowercase; empty means "don't check" + Name string // GPT partition name (e.g. "USR-A") + TypeGUIDs []string // Acceptable partition type GUIDs, lowercase. Most + // entries have exactly one; ROOT and HASH-A/HASH-B carry both the + // amd64 and arm64 discoverable-partition-spec GUIDs, since a CIH + // image built for either architecture is valid and the image being + // inspected here was not necessarily produced on a host of the same + // architecture (e.g. cross-arch builds, or a build/inspection tool + // running natively rather than under arch-emulation). Do not key + // this off runtime.GOARCH -- see cih.go's isCIHImage doc comment. + UUID string // Unique partition GUID, lowercase; empty means "don't check" } // cihRequiredPartitions lists the partitions that must be present (by name and @@ -40,20 +47,29 @@ type cihPartitionDef struct { // partition UUIDs vary across builds and are not checked. // HASH-A and HASH-B are optional — images without them are still valid CIH. var cihRequiredPartitions = []cihPartitionDef{ - {Name: "EFI-SYSTEM", TypeGUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"}, - {Name: "USR-A", TypeGUID: "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", UUID: "7130c94a-213a-4e5a-8e26-6cce9662f132"}, - {Name: "USR-B", TypeGUID: "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", UUID: "e03dd35c-7c2d-4a47-b3fe-27f15780a57c"}, - {Name: "OEM", TypeGUID: "0fc63daf-8483-4772-8e79-3d69d8477de4"}, - {Name: "ROOT", TypeGUID: "4f68bce3-e8cd-4db1-96e7-fbcaf984b709"}, + {Name: "EFI-SYSTEM", TypeGUIDs: []string{"c12a7328-f81f-11d2-ba4b-00a0c93ec93b"}}, + {Name: "USR-A", TypeGUIDs: []string{"5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"}, UUID: "7130c94a-213a-4e5a-8e26-6cce9662f132"}, + {Name: "USR-B", TypeGUIDs: []string{"5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"}, UUID: "e03dd35c-7c2d-4a47-b3fe-27f15780a57c"}, + {Name: "OEM", TypeGUIDs: []string{"0fc63daf-8483-4772-8e79-3d69d8477de4"}}, + {Name: "ROOT", TypeGUIDs: []string{ + string(metadata.PartitionTypeRootAmd64), + string(metadata.PartitionTypeRootArm64), + }}, } // cihOptionalPartitions lists partitions that may or may not be present. // When present, both name+typeGUID and UUID must match. var cihOptionalPartitions = []cihPartitionDef{ - {Name: "BIOS-BOOT", TypeGUID: "21686148-6449-6e6f-744e-656564454649"}, - {Name: "OEM-CONFIG", TypeGUID: "c95dc21a-df0e-4340-8d7b-26cbfa9a03e0"}, - {Name: "HASH-A", TypeGUID: "77ff5f63-e7b6-4633-acf4-1565b864c0e6", UUID: "b736baf1-cdb4-4535-beba-ddaaa30ad7b7"}, - {Name: "HASH-B", TypeGUID: "77ff5f63-e7b6-4633-acf4-1565b864c0e6", UUID: "35bdf78b-c453-4661-98e6-f834f534ef5b"}, + {Name: "BIOS-BOOT", TypeGUIDs: []string{"21686148-6449-6e6f-744e-656564454649"}}, + {Name: "OEM-CONFIG", TypeGUIDs: []string{"c95dc21a-df0e-4340-8d7b-26cbfa9a03e0"}}, + {Name: "HASH-A", TypeGUIDs: []string{ + string(metadata.PartitionTypeUsrAmd64Verity), + string(metadata.PartitionTypeUsrArm64Verity), + }, UUID: "b736baf1-cdb4-4535-beba-ddaaa30ad7b7"}, + {Name: "HASH-B", TypeGUIDs: []string{ + string(metadata.PartitionTypeUsrAmd64Verity), + string(metadata.PartitionTypeUsrArm64Verity), + }, UUID: "35bdf78b-c453-4661-98e6-f834f534ef5b"}, } // cihMountPointByName maps CIH partition names to their logical mount points. @@ -72,37 +88,58 @@ var cihMountPointByName = map[string]string{ // name+typeGUID; those with a non-empty UUID are also verified by UUID. // Optional partitions (HASH-A/HASH-B) are validated when present. func isCIHImage(parsedGPT *gpt.ParsedGPT) bool { - type partKey struct { - name string + type partVal struct { typeGUID string + uuid string } - // Map name+typeGUID -> partition UUID for checking. - partMap := make(map[partKey]string, len(parsedGPT.Partitions)) + // Map partition name -> (typeGUID, UUID) for checking. GPT partition + // names are unique within a CIH image, so this does not need to be + // keyed by typeGUID as well. + partMap := make(map[string]partVal, len(parsedGPT.Partitions)) for _, p := range parsedGPT.Partitions { - partMap[partKey{ - name: p.GetName(), + partMap[p.GetName()] = partVal{ typeGUID: strings.ToLower(p.PartitionTypeGUID.String()), - }] = strings.ToLower(p.UniquePartitionGUID.String()) + uuid: strings.ToLower(p.UniquePartitionGUID.String()), + } + } + + // matchesTypeGUID reports whether actual is one of the acceptable type + // GUIDs for a partition definition. Some logical partitions (ROOT, + // HASH-A, HASH-B) accept either the amd64 or arm64 discoverable- + // partition-spec GUID: a CIH image is valid for either architecture, + // and the architecture of the image under inspection is not assumed + // to match the architecture of whatever is running this check. + matchesTypeGUID := func(def cihPartitionDef, actual string) bool { + for _, want := range def.TypeGUIDs { + if actual == want { + return true + } + } + return false } // All required partitions must be present with matching name+typeGUID. // Those with a specified UUID must also match. for _, req := range cihRequiredPartitions { - key := partKey{name: req.Name, typeGUID: req.TypeGUID} - actualUUID, found := partMap[key] - if !found { + actual, found := partMap[req.Name] + if !found || !matchesTypeGUID(req, actual.typeGUID) { return false } - if req.UUID != "" && actualUUID != req.UUID { + if req.UUID != "" && actual.uuid != req.UUID { return false } } - // Optional partitions: if present, their UUID must match. + // Optional partitions: if present, their type GUID and UUID must match. for _, opt := range cihOptionalPartitions { - key := partKey{name: opt.Name, typeGUID: opt.TypeGUID} - actualUUID, found := partMap[key] - if found && opt.UUID != "" && actualUUID != opt.UUID { + actual, found := partMap[opt.Name] + if !found { + continue + } + if !matchesTypeGUID(opt, actual.typeGUID) { + continue + } + if opt.UUID != "" && actual.uuid != opt.UUID { return false } }