Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for loading certificates from dbDefault #222

Merged
merged 2 commits into from
Jun 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions certs/builtin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package certs

import (
"fmt"
"os"

"github.com/foxboron/go-uefi/efi"
"github.com/foxboron/go-uefi/efi/attributes"
"github.com/foxboron/go-uefi/efi/signature"
"github.com/foxboron/go-uefi/efi/util"
)

var (
efiGlobalGuid = util.EFIGUID{0x8be4df61, 0x93ca, 0x11d2, [8]uint8{0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c}}
defaultSignatureDatabaseNames = map[string]string{
"dbx": "dbxDefault",
"db": "dbDefault",
"KEK": "KEKDefault",
"PK": "PKDefault",
}
)

type builtinSignatureDataEntry struct {
SignatureType util.EFIGUID
Data *signature.SignatureData
}

func GetBuiltinCertificates(db string) (*signature.SignatureDatabase, error) {
defaultName, ok := defaultSignatureDatabaseNames[db]
if !ok {
return nil, fmt.Errorf("%s is an unrecognized firmware database", db)
}
attr, buf, err := attributes.ReadEfivarsWithGuid(defaultName, efiGlobalGuid)
if err != nil {
if err == os.ErrNotExist {
// not finding a default db is not a failure!
return signature.NewSignatureDatabase(), nil
}
return nil, err
}

if attr&attributes.EFI_VARIABLE_NON_VOLATILE != 0 {
// If this variable has non-volatile storage, a malicious user could have created it.
// The EDK2 implementation of default Secure Boot stores marks them volatile.
return nil, fmt.Errorf("vendor default database is non-volatile (and is vulnerable to being tampered with)")
}

database, err := signature.ReadSignatureDatabase(buf)
if err != nil {
return nil, err
}

// Remove vendor certificates that are already covered by the built-in vendor database.
// TODO: Do we actually want this? The machine might be enrolled with more Microsoft certs then sbctl actually covers

detect := map[util.EFIGUID]string{}
for k, v := range oemGUID {
detect[v] = k
}

removals := make([]*builtinSignatureDataEntry, 0, 8)

for _, l := range database {
if l.SignatureType == signature.CERT_X509_GUID || l.SignatureType == signature.CERT_X509_SHA256_GUID {
for _, s := range l.Signatures {
if _, ok := detect[s.Owner]; ok {
removals = append(removals, &builtinSignatureDataEntry{
SignatureType: l.SignatureType,
Data: &s,
})
}
}
}
}

// Depending on the implementation of .RemoveSignature, this could be
// expensive; however, we don't expect dbDebault to be particularly huge.
for _, s := range removals {
database.RemoveSignature(s.SignatureType, s.Data)
}

return &database, nil
}

func GetSignatureDatabase(s string) (*signature.SignatureDatabase, error) {
switch s {
case "db":
return efi.Getdb()
case "KEK":
return efi.GetKEK()
case "PK":
return efi.GetPK()
}
return nil, nil
}

func BuiltinSignatureOwners() ([]string, error) {
ret := []string{}
for _, sbDatabase := range []string{"db", "KEK", "PK"} {
db, err := GetSignatureDatabase(sbDatabase)
if err != nil {
return nil, err
}
dbDefault, err := GetBuiltinCertificates(sbDatabase)
if err != nil {
return nil, err
}
for _, siglist := range *dbDefault {
if db.Exists(siglist.SignatureType, siglist) {
ret = append(ret, fmt.Sprintf("builtin-%s", sbDatabase))
}
}
}
return ret, nil
}
120 changes: 80 additions & 40 deletions cmd/sbctl/enroll-keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,31 @@ func (s *StringSet) Type() string {
return "[auth, esl]"
}

type FirmwareBuiltinFlags []string

func (f *FirmwareBuiltinFlags) String() string {
return strings.Join(*f, ",")
}

// Set must have pointer receiver so it doesn't change the value of a copy
func (f *FirmwareBuiltinFlags) Set(v string) error {
for _, val := range strings.Split(v, ",") {
*f = append(*f, val)
}
return nil
}

func (f *FirmwareBuiltinFlags) Type() string {
return ""
}

type EnrollKeysCmdOptions struct {
MicrosoftKeys bool
IgnoreImmutable bool
Force bool
TPMEventlogChecksums bool
Custom bool
BuiltinFirmwareCerts FirmwareBuiltinFlags
Export StringSet
}

Expand Down Expand Up @@ -113,53 +132,68 @@ func KeySync(guid util.EFIGUID, keydir string, oems []string) error {
}

// If we want OEM certs, we do that here
if len(oems) > 0 {
for _, oem := range oems {
switch oem {
case "tpm-eventlog":
logging.Print("\nWith checksums from the TPM Eventlog...")
eventlogDB, err := sbctl.GetEventlogChecksums(systemEventlog)
if err != nil {
return fmt.Errorf("could not enroll db keys: %w", err)
}
if len((*eventlogDB)) == 0 {
return fmt.Errorf("could not find any OpROM entries in the TPM eventlog")
}
sigdb.AppendDatabase(eventlogDB)
case "microsoft":
logging.Print("\nWith vendor keys from microsoft...")
for _, oem := range oems {
switch oem {
case "tpm-eventlog":
logging.Print("\nWith checksums from the TPM Eventlog...")
eventlogDB, err := sbctl.GetEventlogChecksums(systemEventlog)
if err != nil {
return fmt.Errorf("could not enroll db keys: %w", err)
}
if len((*eventlogDB)) == 0 {
return fmt.Errorf("could not find any OpROM entries in the TPM eventlog")
}
sigdb.AppendDatabase(eventlogDB)
case "microsoft":
logging.Print("\nWith vendor keys from microsoft...")

// db
oemSigDb, err := certs.GetOEMCerts(oem, "db")
if err != nil {
return fmt.Errorf("could not enroll db keys: %w", err)
}
sigdb.AppendDatabase(oemSigDb)
// db
oemSigDb, err := certs.GetOEMCerts(oem, "db")
if err != nil {
return fmt.Errorf("could not enroll db keys: %w", err)
}
sigdb.AppendDatabase(oemSigDb)

// KEK
oemSigKEK, err := certs.GetOEMCerts(oem, "KEK")
if err != nil {
return fmt.Errorf("could not enroll KEK keys: %w", err)
}
sigkek.AppendDatabase(oemSigKEK)
// KEK
oemSigKEK, err := certs.GetOEMCerts(oem, "KEK")
if err != nil {
return fmt.Errorf("could not enroll KEK keys: %w", err)
}
sigkek.AppendDatabase(oemSigKEK)

// We are not enrolling PK keys from Microsoft
case "custom":
logging.Print("\nWith custom keys...")
// We are not enrolling PK keys from Microsoft
case "custom":
logging.Print("\nWith custom keys...")

// db
customSigDb, err := certs.GetCustomCerts(keydir, "db")
if err != nil {
return fmt.Errorf("could not enroll custom db keys: %w", err)
}
sigdb.AppendDatabase(customSigDb)
// db
customSigDb, err := certs.GetCustomCerts(keydir, "db")
if err != nil {
return fmt.Errorf("could not enroll custom db keys: %w", err)
}
sigdb.AppendDatabase(customSigDb)

// KEK
customSigKEK, err := certs.GetCustomCerts(keydir, "KEK")
if err != nil {
return fmt.Errorf("could not enroll custom KEK keys: %w", err)
}
sigkek.AppendDatabase(customSigKEK)
case "firmware-builtin":
logging.Print("\nWith vendor certificates built into the firmware...")

// KEK
customSigKEK, err := certs.GetCustomCerts(keydir, "KEK")
for _, cert := range enrollKeysCmdOptions.BuiltinFirmwareCerts {
builtinSigDb, err := certs.GetBuiltinCertificates(cert)
if err != nil {
return fmt.Errorf("could not enroll custom KEK keys: %w", err)
return fmt.Errorf("could not enroll built-in firmware keys: %w", err)
}
switch cert {
case "db":
sigdb.AppendDatabase(builtinSigDb)
case "KEK":
sigkek.AppendDatabase(builtinSigDb)
case "PK":
sigpk.AppendDatabase(builtinSigDb)
}
sigkek.AppendDatabase(customSigKEK)
}
}
}
Expand Down Expand Up @@ -230,6 +264,9 @@ func RunEnrollKeys(cmd *cobra.Command, args []string) error {
if enrollKeysCmdOptions.Custom {
oems = append(oems, "custom")
}
if len(enrollKeysCmdOptions.BuiltinFirmwareCerts) >= 1 {
oems = append(oems, "firmware-builtin")
}
if !enrollKeysCmdOptions.IgnoreImmutable {
if err := sbctl.CheckImmutable(); err != nil {
return err
Expand Down Expand Up @@ -267,6 +304,9 @@ func vendorFlags(cmd *cobra.Command) {
f.BoolVarP(&enrollKeysCmdOptions.MicrosoftKeys, "microsoft", "m", false, "include microsoft keys into key enrollment")
f.BoolVarP(&enrollKeysCmdOptions.TPMEventlogChecksums, "tpm-eventlog", "t", false, "include TPM eventlog checksums into the db database")
f.BoolVarP(&enrollKeysCmdOptions.Custom, "custom", "c", false, "include custom db and KEK")
// f.BoolVarP(&enrollKeysCmdOptions.BuiltinFirmwareCerts, "firmware-builtin", "f", false, "include keys indicated by the firmware as being part of the default database")
l := f.VarPF(&enrollKeysCmdOptions.BuiltinFirmwareCerts, "firmware-builtin", "f", "include keys indicated by the firmware as being part of the default database")
l.NoOptDefVal = "db,KEK"
}

func enrollKeysCmdFlags(cmd *cobra.Command) {
Expand Down
4 changes: 4 additions & 0 deletions cmd/sbctl/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/foxboron/go-uefi/efi"
"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/certs"
"github.com/foxboron/sbctl/fs"
"github.com/foxboron/sbctl/logging"
"github.com/foxboron/sbctl/quirks"
Expand Down Expand Up @@ -100,6 +101,9 @@ func RunStatus(cmd *cobra.Command, args []string) error {
if keys := sbctl.GetEnrolledVendorCerts(); len(keys) > 0 {
stat.Vendors = keys
}
if keys, err := certs.BuiltinSignatureOwners(); err == nil {
stat.Vendors = append(stat.Vendors, keys...)
}
stat.FirmwareQuirks = quirks.CheckFirmwareQuirks()
if cmdOptions.JsonOutput {
if err := JsonOut(stat); err != nil {
Expand Down
11 changes: 11 additions & 0 deletions docs/sbctl.8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ EFI signing commands
Enroll custom KEK and db certificates from "/usr/share/secureboot/keys/custom/KEK/"
and "/usr/share/secureboot/keys/custom/db/", respectively.

*-f*, *--firmware-builtin*;;
Enroll signatures from dbDefault, KEKDefault or PKDefault. This
is usefull if sbctl does not vendor your OEM certificates, or
doesn't include all of them.

Valid values are "db", "KEK" or "PK" passed as a comma
delimitered string.

Default: "db,KEK"


*--yes-this-might-brick-my-machine*, **--yolo**;;
Ignore the Option ROM error and continue enrolling keys into the
UEFI firmware.
Expand Down