Skip to content

Commit

Permalink
Bump sdk to v0.1.8 (#349)
Browse files Browse the repository at this point in the history
* Bump sdk to v0.1.8

Signed-off-by: Mauro Morales <[email protected]>

* Use new signing methods

Signed-off-by: Mauro Morales <[email protected]>

---------

Signed-off-by: Mauro Morales <[email protected]>
  • Loading branch information
mauromorales committed May 24, 2024
1 parent 9b78efc commit 5b59372
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 8 deletions.
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/jaypipes/ghw v0.12.0
github.com/joho/godotenv v1.5.1
github.com/kairos-io/kairos-sdk v0.1.7
github.com/kairos-io/kairos-sdk v0.1.8
github.com/kairos-io/kcrypt v0.11.1
github.com/labstack/echo/v4 v4.12.0
github.com/mitchellh/mapstructure v1.5.0
Expand All @@ -44,6 +44,11 @@ require (
k8s.io/mount-utils v0.27.4
)

require (
github.com/edsrzf/mmap-go v1.1.0
github.com/foxboron/go-uefi v0.0.0-20240522180132-205d5597883a
)

require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
Expand Down Expand Up @@ -87,7 +92,6 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/eliukblau/pixterm v1.3.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/foxboron/go-uefi v0.0.0-20240128152106-48be911532c2 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/eliukblau/pixterm v1.3.1 h1:XeouQViH+lmzCa7sMUoK2cd7qlgHYGLIjwRKaOdJbKA=
github.com/eliukblau/pixterm v1.3.1/go.mod h1:on5ueknFt+ZFVvIVVzQ7/JXwPjv5fJd8Q1Ybh7XixfU=
Expand All @@ -149,8 +151,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikgeiser/promptkit v0.9.0 h1:3qL1mS/ntCrXdb8sTP/ka82CJ9kEQaGuYXNrYJkWYBc=
github.com/erikgeiser/promptkit v0.9.0/go.mod h1:pU9dtogSe3Jlc2AY77EP7R4WFP/vgD4v+iImC83KsCo=
github.com/foxboron/go-uefi v0.0.0-20240128152106-48be911532c2 h1:qGlg/7H49H30Eu7nkCBA7YxNmW30ephqBf7xIxlAGuQ=
github.com/foxboron/go-uefi v0.0.0-20240128152106-48be911532c2/go.mod h1:ffg/fkDeOYicEQLoO2yFFGt00KUTYVXI+rfnc8il6vQ=
github.com/foxboron/go-uefi v0.0.0-20240522180132-205d5597883a h1:Q/VIO3QAlaF95JqVVF39udInPR76lu02yrMDInavm8Q=
github.com/foxboron/go-uefi v0.0.0-20240522180132-205d5597883a/go.mod h1:ffg/fkDeOYicEQLoO2yFFGt00KUTYVXI+rfnc8il6vQ=
github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Expand Down Expand Up @@ -286,8 +288,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 h1:G+9t9cEtnC9jFiTxyptEKuNIAbiN5ZCQzX2a74lj3xg=
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004/go.mod h1:KmHnJWQrgEvbuy0vcvj00gtMqbvNn1L+3YUZLK/B92c=
github.com/kairos-io/kairos-sdk v0.1.7 h1:h2H1/sG4+4xEPh0zMFFtl4yEgzrXI8IDdDiQZe4ib6g=
github.com/kairos-io/kairos-sdk v0.1.7/go.mod h1:sR1X4B3F1nkaECQ1vdsJ78OIkfLfyB22/aIpdRQJ/Mo=
github.com/kairos-io/kairos-sdk v0.1.8 h1:TKigA+3Nmzn/NLztbLVBLacpx0cK1oJl1AoZarohU98=
github.com/kairos-io/kairos-sdk v0.1.8/go.mod h1:asSOyJanH10Cnxl9zx5RzyYNMhEworaiMh/7uRnS4GA=
github.com/kairos-io/kcrypt v0.11.1 h1:azIX1QI5dEzVLvgftNleCY4AyklhTXewCoi4eTC7jhU=
github.com/kairos-io/kcrypt v0.11.1/go.mod h1:Gz1izzOWwbnJwtq+XqiZQ8cPktWcDIKw03YM1PWAk4c=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
Expand Down
162 changes: 160 additions & 2 deletions pkg/uki/common.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package uki

import (
"bytes"
"crypto/x509"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"strings"

sdkTypes "github.com/kairos-io/kairos-sdk/types"

"github.com/edsrzf/mmap-go"
"github.com/foxboron/go-uefi/authenticode"
"github.com/foxboron/go-uefi/efi"
"github.com/foxboron/go-uefi/efi/signature"
"github.com/foxboron/go-uefi/pkcs7"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
Expand Down Expand Up @@ -153,3 +159,155 @@ func copyFile(src, dst string) error {

return destinationFile.Close()
}

// checkArtifactSignatureIsValid checks that a given efi artifact is signed properly with a signature that would allow it to
// boot correctly in the current node if secureboot is enabled
func checkArtifactSignatureIsValid(fs v1.FS, artifact string, logger sdkTypes.KairosLogger) error {
var err error
logger.Logger.Info().Str("what", artifact).Msg("Checking artifact for valid signature")
info, err := fs.Stat(artifact)
if errors.Is(err, os.ErrNotExist) {
logger.Warnf("%s does not exist", artifact)
return fmt.Errorf("%s does not exist", artifact)
} else if errors.Is(err, os.ErrPermission) {
logger.Warnf("%s permission denied. Can't read file", artifact)
return fmt.Errorf("%s permission denied. Can't read file", artifact)
} else if err != nil {
return err
}
if info.Size() == 0 {
logger.Warnf("%s file is empty denied", artifact)
return fmt.Errorf("%s file has zero size", artifact)
}
logger.Logger.Debug().Str("what", artifact).Msg("Reading artifact")

// MMAP the file, seems to save memory rather than reading the full file
// Unfortunately we have to do some type conversion to keep using the v1.Fs
f, err := fs.Open(artifact)
defer f.Close()
if err != nil {
return err
}
// type conversion, ugh
fOS := f.(*os.File)
data, err := mmap.Map(fOS, mmap.RDONLY, 0)
defer data.Unmap()
if err != nil {
return err
}

// Get sha256 of the artifact
// Note that this is a PEFile, so it's a bit different from a normal file as there are some sections that need to be
// excluded when calculating the sha
logger.Logger.Debug().Str("what", artifact).Msg("Parsing PE artifact")
file, _ := peparser.NewBytes(data, &peparser.Options{Fast: true})
err = file.Parse()
if err != nil {
logger.Logger.Error().Err(err).Msg("parsing PE file for hash")
return err
}

logger.Logger.Debug().Str("what", artifact).Msg("Checking if its an EFI file")
// Check for proper header in the efi file
if file.DOSHeader.Magic != peparser.ImageDOSZMSignature && file.DOSHeader.Magic != peparser.ImageDOSSignature {
logger.Error(fmt.Errorf("no pe file header: %d", file.DOSHeader.Magic))
return fmt.Errorf("no pe file header: %d", file.DOSHeader.Magic)
}

// Get hash to compare in dbx if we have hashes
hashArtifact := hex.EncodeToString(file.Authentihash())

logger.Logger.Debug().Str("what", artifact).Msg("Getting DB certs")
// We need to read the current db database to have the proper certs to check against
db, err := efi.Getdb()
if err != nil {
logger.Logger.Error().Err(err).Msg("Getting DB certs")
return err
}

dbCerts := signatures.ExtractCertsFromSignatureDatabase(db)

logger.Logger.Debug().Str("what", artifact).Msg("Getting signatures from artifact")
// Get signatures from the artifact
binary, err := authenticode.Parse(bytes.NewReader(data))
if err != nil {
return fmt.Errorf("%s: %w", artifact, err)
}
if binary.Datadir.Size == 0 {
return fmt.Errorf("no signatures in the file %s", artifact)
}

sigs, err := binary.Signatures()
if err != nil {
return fmt.Errorf("%s: %w", artifact, err)
}

logger.Logger.Debug().Str("what", artifact).Msg("Getting DBX certs")
dbx, err := efi.Getdbx()
if err != nil {
logger.Logger.Error().Err(err).Msg("getting DBX certs")
return err
}

// First check the dbx database as it has precedence, on match, return immediately
for _, k := range *dbx {
switch k.SignatureType {
case signature.CERT_SHA256_GUID: // SHA256 hash
// Compare it against the dbx
for _, k1 := range k.Signatures {
shaSign := hex.EncodeToString(k1.Data)
logger.Logger.Debug().Str("artifact", string(hashArtifact)).Str("signature", shaSign).Msg("Comparing hashes")
if hashArtifact == shaSign {
return fmt.Errorf("hash appears on DBX: %s", hashArtifact)
}

}
case signature.CERT_X509_GUID: // Certificate
var result []*x509.Certificate
for _, k1 := range k.Signatures {
certificates, err := x509.ParseCertificates(k1.Data)
if err != nil {
continue
}
result = append(result, certificates...)
}
for _, sig := range sigs {
for _, cert := range result {
logger.Logger.Debug().Str("what", artifact).Str("subject", cert.Subject.CommonName).Msg("checking signature")
p, err := pkcs7.ParsePKCS7(sig.Certificate)
if err != nil {
logger.Logger.Info().Str("error", err.Error()).Msg("parsing signature")
return err
}
ok, _ := p.Verify(cert)
// If cert matches then it means its blacklisted so return error
if ok {
return fmt.Errorf("artifact is signed with a blacklisted cert")
}

}
}
default:
logger.Logger.Debug().Str("what", artifact).Str("cert type", string(signature.ValidEFISignatureSchemes[k.SignatureType])).Msg("not supported type of cert")
}
}

// Now check against the DB to see if its allowed
for _, sig := range sigs {
for _, cert := range dbCerts {
logger.Logger.Debug().Str("what", artifact).Str("subject", cert.Subject.CommonName).Msg("checking signature")
p, err := pkcs7.ParsePKCS7(sig.Certificate)
if err != nil {
logger.Logger.Info().Str("error", err.Error()).Msg("parsing signature")
return err
}
ok, _ := p.Verify(cert)
if ok {
logger.Logger.Info().Str("what", artifact).Str("subject", cert.Subject.CommonName).Msg("verified")
return nil
}
}
}
// If we reach this point, we need to fail as we haven't matched anything, so default is to fail
return fmt.Errorf("could not find a signature in EFIVars DB that matches the artifact")
}

0 comments on commit 5b59372

Please sign in to comment.