Lorenz Brun | a50e845 | 2020-09-09 17:09:27 +0200 | [diff] [blame] | 1 | // Copyright 2020 The Monogon Project Authors. |
| 2 | // |
| 3 | // SPDX-License-Identifier: Apache-2.0 |
| 4 | // |
| 5 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | // you may not use this file except in compliance with the License. |
| 7 | // You may obtain a copy of the License at |
| 8 | // |
| 9 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | // |
| 11 | // Unless required by applicable law or agreed to in writing, software |
| 12 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | // See the License for the specific language governing permissions and |
| 15 | // limitations under the License. |
| 16 | |
| 17 | // Taken and pruned from go-attestation under Apache 2.0 |
| 18 | package eventlog |
| 19 | |
| 20 | import ( |
| 21 | "bytes" |
| 22 | "errors" |
| 23 | "fmt" |
| 24 | |
| 25 | "github.com/google/certificate-transparency-go/x509" |
| 26 | |
| 27 | "git.monogon.dev/source/nexantic.git/core/pkg/tpm/eventlog/internal" |
| 28 | ) |
| 29 | |
| 30 | // SecurebootState describes the secure boot status of a machine, as determined |
| 31 | // by processing its event log. |
| 32 | type SecurebootState struct { |
| 33 | Enabled bool |
| 34 | |
| 35 | // PlatformKeys enumerates keys which can sign a key exchange key. |
| 36 | PlatformKeys []x509.Certificate |
| 37 | // PlatformKeys enumerates key hashes which can sign a key exchange key. |
| 38 | PlatformKeyHashes [][]byte |
| 39 | |
| 40 | // ExchangeKeys enumerates keys which can sign a database of permitted or |
| 41 | // forbidden keys. |
| 42 | ExchangeKeys []x509.Certificate |
| 43 | // ExchangeKeyHashes enumerates key hashes which can sign a database or |
| 44 | // permitted or forbidden keys. |
| 45 | ExchangeKeyHashes [][]byte |
| 46 | |
| 47 | // PermittedKeys enumerates keys which may sign binaries to run. |
| 48 | PermittedKeys []x509.Certificate |
| 49 | // PermittedHashes enumerates hashes which permit binaries to run. |
| 50 | PermittedHashes [][]byte |
| 51 | |
| 52 | // ForbiddenKeys enumerates keys which must not permit a binary to run. |
| 53 | ForbiddenKeys []x509.Certificate |
| 54 | // ForbiddenKeys enumerates hashes which must not permit a binary to run. |
| 55 | ForbiddenHashes [][]byte |
| 56 | |
| 57 | // PreSeparatorAuthority describes the use of a secure-boot key to authorize |
| 58 | // the execution of a binary before the separator. |
| 59 | PreSeparatorAuthority []x509.Certificate |
| 60 | // PostSeparatorAuthority describes the use of a secure-boot key to authorize |
| 61 | // the execution of a binary after the separator. |
| 62 | PostSeparatorAuthority []x509.Certificate |
| 63 | } |
| 64 | |
| 65 | // ParseSecurebootState parses a series of events to determine the |
| 66 | // configuration of secure boot on a device. An error is returned if |
| 67 | // the state cannot be determined, or if the event log is structured |
| 68 | // in such a way that it may have been tampered post-execution of |
| 69 | // platform firmware. |
| 70 | func ParseSecurebootState(events []Event) (*SecurebootState, error) { |
| 71 | // This algorithm verifies the following: |
| 72 | // - All events in PCR 7 have event types which are expected in PCR 7. |
| 73 | // - All events are parsable according to their event type. |
| 74 | // - All events have digests values corresponding to their data/event type. |
| 75 | // - No unverifiable events were present. |
| 76 | // - All variables are specified before the separator and never duplicated. |
| 77 | // - The SecureBoot variable has a value of 0 or 1. |
| 78 | // - If SecureBoot was 1 (enabled), authority events were present indicating |
| 79 | // keys were used to perform verification. |
| 80 | // - If SecureBoot was 1 (enabled), platform + exchange + database keys |
| 81 | // were specified. |
| 82 | // - No UEFI debugger was attached. |
| 83 | |
| 84 | var ( |
| 85 | out SecurebootState |
| 86 | seenSeparator bool |
| 87 | seenAuthority bool |
| 88 | seenVars = map[string]bool{} |
| 89 | ) |
| 90 | |
| 91 | for _, e := range events { |
| 92 | if e.Index != 7 { |
| 93 | continue |
| 94 | } |
| 95 | |
| 96 | et, err := internal.UntrustedParseEventType(uint32(e.Type)) |
| 97 | if err != nil { |
| 98 | return nil, fmt.Errorf("unrecognised event type: %v", err) |
| 99 | } |
| 100 | |
| 101 | digestVerify := e.digestEquals(e.Data) |
| 102 | switch et { |
| 103 | case internal.Separator: |
| 104 | if seenSeparator { |
| 105 | return nil, fmt.Errorf("duplicate separator at event %d", e.sequence) |
| 106 | } |
| 107 | seenSeparator = true |
| 108 | if !bytes.Equal(e.Data, []byte{0, 0, 0, 0}) { |
| 109 | return nil, fmt.Errorf("invalid separator data at event %d: %v", e.sequence, e.Data) |
| 110 | } |
| 111 | if digestVerify != nil { |
| 112 | return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.sequence, digestVerify) |
| 113 | } |
| 114 | |
| 115 | case internal.EFIAction: |
| 116 | if string(e.Data) == "UEFI Debug Mode" { |
| 117 | return nil, errors.New("a UEFI debugger was present during boot") |
| 118 | } |
| 119 | return nil, fmt.Errorf("event %d: unexpected EFI action event", e.sequence) |
| 120 | |
| 121 | case internal.EFIVariableDriverConfig: |
| 122 | v, err := internal.ParseUEFIVariableData(bytes.NewReader(e.Data)) |
| 123 | if err != nil { |
| 124 | return nil, fmt.Errorf("failed parsing EFI variable at event %d: %v", e.sequence, err) |
| 125 | } |
| 126 | if _, seenBefore := seenVars[v.VarName()]; seenBefore { |
| 127 | return nil, fmt.Errorf("duplicate EFI variable %q at event %d", v.VarName(), e.sequence) |
| 128 | } |
| 129 | seenVars[v.VarName()] = true |
| 130 | if seenSeparator { |
| 131 | return nil, fmt.Errorf("event %d: variable %q specified after separator", e.sequence, v.VarName()) |
| 132 | } |
| 133 | |
| 134 | if digestVerify != nil { |
| 135 | return nil, fmt.Errorf("invalid digest for variable %q on event %d: %v", v.VarName(), e.sequence, digestVerify) |
| 136 | } |
| 137 | |
| 138 | switch v.VarName() { |
| 139 | case "SecureBoot": |
| 140 | if len(v.VariableData) != 1 { |
| 141 | return nil, fmt.Errorf("event %d: SecureBoot data len is %d, expected 1", e.sequence, len(v.VariableData)) |
| 142 | } |
| 143 | out.Enabled = v.VariableData[0] == 1 |
| 144 | case "PK": |
| 145 | if out.PlatformKeys, out.PlatformKeyHashes, err = v.SignatureData(); err != nil { |
| 146 | return nil, fmt.Errorf("event %d: failed parsing platform keys: %v", e.sequence, err) |
| 147 | } |
| 148 | case "KEK": |
| 149 | if out.ExchangeKeys, out.ExchangeKeyHashes, err = v.SignatureData(); err != nil { |
| 150 | return nil, fmt.Errorf("event %d: failed parsing key exchange keys: %v", e.sequence, err) |
| 151 | } |
| 152 | case "db": |
| 153 | if out.PermittedKeys, out.PermittedHashes, err = v.SignatureData(); err != nil { |
| 154 | return nil, fmt.Errorf("event %d: failed parsing signature database: %v", e.sequence, err) |
| 155 | } |
| 156 | case "dbx": |
| 157 | if out.ForbiddenKeys, out.ForbiddenHashes, err = v.SignatureData(); err != nil { |
| 158 | return nil, fmt.Errorf("event %d: failed parsing forbidden signature database: %v", e.sequence, err) |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | case internal.EFIVariableAuthority: |
| 163 | a, err := internal.ParseUEFIVariableAuthority(bytes.NewReader(e.Data)) |
| 164 | if err != nil { |
| 165 | // Workaround for: https://github.com/google/go-attestation/issues/157 |
| 166 | if err == internal.ErrSigMissingGUID { |
| 167 | // Versions of shim which do not carry |
| 168 | // https://github.com/rhboot/shim/commit/8a27a4809a6a2b40fb6a4049071bf96d6ad71b50 |
| 169 | // have an erroneous additional byte in the event, which breaks digest |
| 170 | // verification. If verification failed, we try removing the last byte. |
| 171 | if digestVerify != nil { |
| 172 | digestVerify = e.digestEquals(e.Data[:len(e.Data)-1]) |
| 173 | } |
| 174 | } else { |
| 175 | return nil, fmt.Errorf("failed parsing EFI variable authority at event %d: %v", e.sequence, err) |
| 176 | } |
| 177 | } |
| 178 | seenAuthority = true |
| 179 | if digestVerify != nil { |
| 180 | return nil, fmt.Errorf("invalid digest for authority on event %d: %v", e.sequence, digestVerify) |
| 181 | } |
| 182 | if !seenSeparator { |
| 183 | out.PreSeparatorAuthority = append(out.PreSeparatorAuthority, a.Certs...) |
| 184 | } else { |
| 185 | out.PostSeparatorAuthority = append(out.PostSeparatorAuthority, a.Certs...) |
| 186 | } |
| 187 | |
| 188 | default: |
| 189 | return nil, fmt.Errorf("unexpected event type: %v", et) |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | if !out.Enabled { |
| 194 | return &out, nil |
| 195 | } |
| 196 | |
| 197 | if !seenAuthority { |
| 198 | return nil, errors.New("secure boot was enabled but no key was used") |
| 199 | } |
| 200 | if len(out.PlatformKeys) == 0 && len(out.PlatformKeyHashes) == 0 { |
| 201 | return nil, errors.New("secure boot was enabled but no platform keys were known") |
| 202 | } |
| 203 | if len(out.ExchangeKeys) == 0 && len(out.ExchangeKeyHashes) == 0 { |
| 204 | return nil, errors.New("secure boot was enabled but no key exchange keys were known") |
| 205 | } |
| 206 | if len(out.PermittedKeys) == 0 && len(out.PermittedHashes) == 0 { |
| 207 | return nil, errors.New("secure boot was enabled but no keys or hashes were permitted") |
| 208 | } |
| 209 | return &out, nil |
| 210 | } |