Skip to content

Commit

Permalink
test: Handle Arch Linux eventlog PCR4 failures
Browse files Browse the repository at this point in the history
The Arch Linux event log does not extend the "Calling EFI Application"
event before extending EFI Application digests. Update tests to handle
(aka, ignore) this bad event log.
  • Loading branch information
alexmwu committed Mar 15, 2023
1 parent 8a28c5b commit f62c3ed
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 22 deletions.
32 changes: 18 additions & 14 deletions server/eventlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ type eventLog struct {
ExpectedEFIAppDigests map[pb.HashAlgo][]string
}

var archLinuxBadSecureBoot = "SecureBoot data len is 0, expected 1"
// The Arch Linux event log has two known failures due to our parser's strict checks.
var archLinuxKnownParsingFailures = []string{
"SecureBoot data len is 0, expected 1",
"found EFIBootServicesApplication in PCR4 before CallingEFIApp event",
}

// Agile Event Log from a RHEL 8 GCE instance with Secure Boot enabled
var Rhel8GCE = eventLog{
Expand Down Expand Up @@ -483,21 +487,21 @@ func TestParseEventLogs(t *testing.T) {
Bootloader
// This field handles known issues with event log parsing or bad event
// logs.
// An empty string will not attempt to pattern match the error result.
errorSubstr string
// Set to nil when the event log has no known issues.
errorSubstrs []string
}{
{Debian10GCE, "Debian10GCE", UnsupportedLoader, ""},
{Rhel8GCE, "Rhel8GCE", GRUB, ""},
{UbuntuAmdSevGCE, "UbuntuAmdSevGCE", GRUB, ""},
{Debian10GCE, "Debian10GCE", UnsupportedLoader, nil},
{Rhel8GCE, "Rhel8GCE", GRUB, nil},
{UbuntuAmdSevGCE, "UbuntuAmdSevGCE", GRUB, nil},
// TODO: remove once the fix is pulled in
// https://github.com/google/go-attestation/pull/222
{Ubuntu2104NoDbxGCE, "Ubuntu2104NoDbxGCE", GRUB, sbatErrorStr},
{Ubuntu2104NoSecureBootGCE, "Ubuntu2104NoSecureBootGCE", GRUB, sbatErrorStr},
{Ubuntu2104NoDbxGCE, "Ubuntu2104NoDbxGCE", GRUB, []string{sbatErrorStr}},
{Ubuntu2104NoSecureBootGCE, "Ubuntu2104NoSecureBootGCE", GRUB, []string{sbatErrorStr}},
// This event log has a SecureBoot variable length of 0.
{ArchLinuxWorkstation, "ArchLinuxWorkstation", UnsupportedLoader, archLinuxBadSecureBoot},
{COS85AmdSev, "COS85AmdSev", GRUB, ""},
{COS93AmdSev, "COS93AmdSev", GRUB, ""},
{COS101AmdSev, "COS101AmdSev", GRUB, ""},
{ArchLinuxWorkstation, "ArchLinuxWorkstation", UnsupportedLoader, archLinuxKnownParsingFailures},
{COS85AmdSev, "COS85AmdSev", GRUB, nil},
{COS93AmdSev, "COS93AmdSev", GRUB, nil},
{COS101AmdSev, "COS101AmdSev", GRUB, nil},
}

for _, log := range logs {
Expand All @@ -511,10 +515,10 @@ func TestParseEventLogs(t *testing.T) {
if !ok {
t.Errorf("ParseMachineState should return a GroupedError")
}
if log.errorSubstr == "" {
if len(log.errorSubstrs) == 0 {
t.Errorf("expected no errors in GroupedError, received (%v)", err)
}
if !gErr.containsOnlySubstring(log.errorSubstr) {
if !gErr.containsKnownSubstrings(log.errorSubstrs) {
t.Errorf("failed to parse and replay log: %v", err)
}
}
Expand Down
29 changes: 29 additions & 0 deletions server/grouped_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ func (gErr *GroupedError) containsSubstring(substr string) bool {
return false
}

// containsKnownSubstrings is used to match a set of known errors.
// Each substring must only match error in the GroupedError.
// In other words, there must not be overlap in the substring matches.
func (gErr *GroupedError) containsKnownSubstrings(substrs []string) bool {
if len(gErr.Errors) != len(substrs) {
return false
}
matchedGErr := make(map[string]bool)
for _, err := range gErr.Errors {
matchedGErr[err.Error()] = false
for _, substr := range substrs {
if strings.Contains(err.Error(), substr) {
if matchedGErr[err.Error()] {
// Duplicated match for the error.
return false
}
matchedGErr[err.Error()] = true
}
}
}

for _, matched := range matchedGErr {
if !matched {
return false
}
}
return true
}

func (gErr *GroupedError) containsOnlySubstring(substr string) bool {
if len(gErr.Errors) != 1 {
return false
Expand Down
84 changes: 84 additions & 0 deletions server/grouped_error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,87 @@ func TestCreateGroupedErrorFail(t *testing.T) {
t.Errorf("expected nil error!")
}
}

func TestContainsOnlySubstring(t *testing.T) {
wholeString := "err error errorz"
err := errors.New(wholeString)
outErr := GroupedError{Prefix: "foo:", Errors: []error{err}}
if !outErr.containsOnlySubstring("error") {
t.Errorf("expected a match for substring")
}
if !outErr.containsOnlySubstring("err") {
t.Errorf("expected a match for substring")
}
if !outErr.containsOnlySubstring("") {
t.Errorf("expected a match for substring")
}
if !outErr.containsOnlySubstring(wholeString) {
t.Errorf("expected a match for substring")
}
}

func TestContainsOnlySubstringsFalse(t *testing.T) {
wholeString := "err error errorz"
err := errors.New(wholeString)
outErr := GroupedError{Prefix: "foo:", Errors: []error{err}}

tests := []struct {
name string
substring string
}{
{"AdditionalCharacterStart", "." + wholeString},
{"AdditionalCharacterEnd", wholeString + "."},
{"RemovedCharacter", wholeString[:5] + wholeString[6:]},
{"ReplacedCharacter", wholeString[:5] + "." + wholeString[6:]},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if outErr.containsOnlySubstring(test.substring) {
t.Errorf("expected failed matching for substring")
}

})
}
}

func TestContainsKnownSubstrings(t *testing.T) {
err := errors.New("err error errorz")
err2 := errors.New("new newww newzz")
err3 := errors.New("iss issue issues")
outErr := GroupedError{Prefix: "foo:", Errors: []error{err, err2, err3}}
if !outErr.containsKnownSubstrings([]string{"error", " newzz", " issue "}) {
t.Errorf("expected a match for known substrings")
}
}

func TestContainsKnownSubstringsFalse(t *testing.T) {
err := errors.New("err error errorz")
err2 := errors.New("new newww newzz")
err3 := errors.New("iss issue issues")
outErr := GroupedError{Prefix: "foo:", Errors: []error{err, err2, err3}}

tests := []struct {
name string
substrings []string
}{
{"NoSubstrings", []string{}},
{"OneEmptySubstring", []string{""}},
// Should fail, since there is overlap between substrings.
{"AllEmptySubstrings", []string{"", "", ""}},
{"FewerSubstrings", []string{"err"}},
{"FewerSubstrings2", []string{"error", " issue "}},
{"MoreSubstrings", []string{"error", " newzz", " issue ", " issues"}},
{"MoreSubstrings5", []string{"error", " newzz", " issue ", " issues", "err"}},
{"OverlappingSubstrings", []string{"error", " err", " issue "}},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if outErr.containsKnownSubstrings(test.substrings) {
t.Errorf("expected failed matching for known substrings")
}

})
}
}
16 changes: 8 additions & 8 deletions server/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestEvaluatePolicySCRTM(t *testing.T) {
machineState, err := parsePCClientEventLog(ArchLinuxWorkstation.RawLog, ArchLinuxWorkstation.Banks[0], UnsupportedLoader)
if err != nil {
gErr := err.(*GroupedError)
if !gErr.containsOnlySubstring(archLinuxBadSecureBoot) {
if !gErr.containsKnownSubstrings(archLinuxKnownParsingFailures) {
t.Fatalf("failed to get machine state: %v", err)
}
}
Expand Down Expand Up @@ -125,23 +125,23 @@ func TestEvaluatePolicyFailure(t *testing.T) {
policy *pb.Policy
// This field handles known issues with event log parsing or bad event
// logs.
// An empty string will not attempt to pattern match the error result.
errorSubstr string
// Set to nil when the event log has no known issues.
errorSubstrs []string
}{
{"Debian10-SHA1", Debian10GCE, &badGcePolicyVersion, ""},
{"Debian10-SHA1", Debian10GCE, &badGcePolicySEV, ""},
{"Debian10-SHA1", Debian10GCE, &badGcePolicyVersion, nil},
{"Debian10-SHA1", Debian10GCE, &badGcePolicySEV, nil},
{"Ubuntu1804AmdSev-CryptoAgile", UbuntuAmdSevGCE, &badGcePolicySEVES,
""},
nil},
{"ArchLinuxWorkstation-CryptoAgile", ArchLinuxWorkstation,
&badPhysicalPolicy, archLinuxBadSecureBoot},
&badPhysicalPolicy, archLinuxKnownParsingFailures},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
machineState, err := parsePCClientEventLog(test.log.RawLog, test.log.Banks[0], UnsupportedLoader)
if err != nil {
gErr := err.(*GroupedError)
if test.errorSubstr != "" && !gErr.containsOnlySubstring(test.errorSubstr) {
if len(test.errorSubstrs) == 0 || !gErr.containsKnownSubstrings(test.errorSubstrs) {
t.Fatalf("failed to get machine state: %v", err)
}
}
Expand Down

0 comments on commit f62c3ed

Please sign in to comment.