Skip to content

Commit

Permalink
Merge branch 'morten/cli'
Browse files Browse the repository at this point in the history
  • Loading branch information
Foxboron committed Jun 5, 2021
2 parents 10ff8d2 + ae1aec1 commit 5d528ff
Show file tree
Hide file tree
Showing 25 changed files with 1,204 additions and 734 deletions.
54 changes: 24 additions & 30 deletions bundles.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"

"github.com/foxboron/sbctl/logging"
)

type Bundle struct {
Expand Down Expand Up @@ -37,15 +38,29 @@ func ReadBundleDatabase(dbpath string) (Bundles, error) {
return bundles, nil
}

func WriteBundleDatabase(dbpath string, bundles Bundles) {
func WriteBundleDatabase(dbpath string, bundles Bundles) error {
data, err := json.MarshalIndent(bundles, "", " ")
if err != nil {
log.Fatal(err)
return err
}
err = os.WriteFile(dbpath, data, 0644)
if err != nil {
log.Fatal(err)
return err
}
return nil
}

func BundleIter(fn func(s *Bundle) error) error {
files, err := ReadBundleDatabase(BundleDBPath)
if err != nil {
return err
}
for _, s := range files {
if err := fn(s); err != nil {
return err
}
}
return nil
}

func GetEfistub() string {
Expand Down Expand Up @@ -83,7 +98,7 @@ func NewBundle() *Bundle {
}
}

func GenerateBundle(bundle *Bundle) bool {
func GenerateBundle(bundle *Bundle) (bool, error) {
args := []string{
"--add-section", fmt.Sprintf(".osrel=%s", bundle.OSRelease), "--change-section-vma", ".osrel=0x20000",
"--add-section", fmt.Sprintf(".cmdline=%s", bundle.Cmdline), "--change-section-vma", ".cmdline=0x30000",
Expand All @@ -101,33 +116,12 @@ func GenerateBundle(bundle *Bundle) bool {
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
if errors.Is(err, exec.ErrNotFound) {
err2.Printf(err.Error())
return false
return false, err
}
if exitError, ok := err.(*exec.ExitError); ok {
return exitError.ExitCode() == 0
return exitError.ExitCode() == 0, nil
}
}
msg.Printf("Wrote EFI bundle %s", bundle.Output)
return true
}

func FormatBundle(name string, bundle *Bundle) {
msg.Printf("Bundle: %s", name)
if bundle.AMDMicrocode != "" {
msg2.Printf("AMD Microcode: %s", bundle.AMDMicrocode)
}
if bundle.IntelMicrocode != "" {
msg2.Printf("Intel Microcode: %s", bundle.IntelMicrocode)
}
msg2.Printf("Kernel Image: %s", bundle.KernelImage)
msg2.Printf("Initramfs Image: %s", bundle.Initramfs)
msg2.Printf("Cmdline: %s", bundle.Cmdline)
msg2.Printf("OS Release: %s", bundle.OSRelease)
msg2.Printf("EFI Stub Image: %s", bundle.EFIStub)
msg2.Printf("ESP Location: %s", bundle.ESP)
if bundle.Splash != "" {
msg2.Printf("Splash Image: %s", bundle.Splash)
}
msg2.Printf("Output: %s", bundle.Output)
logging.Print("Wrote EFI bundle %s\n", bundle.Output)
return true, nil
}
103 changes: 103 additions & 0 deletions cmd/sbctl/bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package main

import (
"os"
"path/filepath"

"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/logging"
"github.com/spf13/cobra"
)

var (
amducode string
intelucode string
splashImg string
osRelease string
efiStub string
kernelImg string
cmdline string
initramfs string
espPath string
saveBundle bool
)

var bundleCmd = &cobra.Command{
Use: "bundle",
Short: "Bundle the needed files for an EFI stub image",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
logging.Print("Requires a file to sign...\n")
os.Exit(1)
}
checkFiles := []string{amducode, intelucode, splashImg, osRelease, efiStub, kernelImg, cmdline, initramfs}
for _, path := range checkFiles {
if path == "" {
continue
}
if _, err := os.Stat(path); os.IsNotExist(err) {
logging.Print("%s does not exist!\n", path)
os.Exit(1)
}
}
bundle := sbctl.NewBundle()
output, err := filepath.Abs(args[0])
if err != nil {
return err
}
// Fail early if user wants to save bundle but doesn't have permissions
var bundles sbctl.Bundles
if saveBundle {
// "err" needs to have been declared before this, otherwise it's necessary
// to use ":=", which shadows the "bundles" variable
bundles, err = sbctl.ReadBundleDatabase(sbctl.BundleDBPath)
if err != nil {
return err
}
}
bundle.Output = output
bundle.IntelMicrocode = intelucode
bundle.AMDMicrocode = amducode
bundle.KernelImage = kernelImg
bundle.Initramfs = initramfs
bundle.Cmdline = cmdline
bundle.Splash = splashImg
bundle.OSRelease = osRelease
bundle.EFIStub = efiStub
bundle.ESP = espPath
if err = sbctl.CreateBundle(*bundle); err != nil {
return err
}
logging.Print("Wrote EFI bundle %s\n", bundle.Output)
if saveBundle {
bundles[bundle.Output] = bundle
err := sbctl.WriteBundleDatabase(sbctl.BundleDBPath, bundles)
if err != nil {
return err
}
}
return nil
},
}

func bundleCmdFlags(cmd *cobra.Command) {
esp := sbctl.GetESP()
f := cmd.Flags()
f.StringVarP(&amducode, "amducode", "a", "", "AMD microcode location")
f.StringVarP(&intelucode, "intelucode", "i", "", "Intel microcode location")
f.StringVarP(&splashImg, "splash-img", "l", "", "Boot splash image location")
f.StringVarP(&osRelease, "os-release", "o", "/usr/lib/os-release", "OS Release file location")
f.StringVarP(&efiStub, "efi-stub", "e", "/usr/lib/systemd/boot/efi/linuxx64.efi.stub", "EFI Stub location")
f.StringVarP(&kernelImg, "kernel-img", "k", "/boot/vmlinuz-linux", "Kernel image location")
f.StringVarP(&cmdline, "cmdline", "c", "/etc/kernel/cmdline", "Cmdline location")
f.StringVarP(&initramfs, "initramfs", "f", "/boot/initramfs-linux.img", "Initramfs location")
f.StringVarP(&espPath, "esp", "p", esp, "ESP location")
f.BoolVarP(&saveBundle, "save", "s", false, "save bundle to the database")
}

func init() {
bundleCmdFlags(bundleCmd)
CliCommands = append(CliCommands, cliCommand{
Cmd: bundleCmd,
})
}
51 changes: 51 additions & 0 deletions cmd/sbctl/completions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"os"

"github.com/spf13/cobra"
)

var completionCmd = &cobra.Command{Use: "completion"}

func completionBashCmd() *cobra.Command {
var completionCmd = &cobra.Command{
Use: "bash",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenBashCompletion(os.Stdout)
},
}
return completionCmd
}

func completionZshCmd() *cobra.Command {
var completionCmd = &cobra.Command{
Use: "zsh",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenZshCompletion(os.Stdout)
},
}
return completionCmd
}

func completionFishCmd() *cobra.Command {
var completionCmd = &cobra.Command{
Use: "fish",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenFishCompletion(os.Stdout, true)
},
}
return completionCmd
}

func init() {
completionCmd.AddCommand(completionBashCmd())
completionCmd.AddCommand(completionZshCmd())
completionCmd.AddCommand(completionFishCmd())
CliCommands = append(CliCommands, cliCommand{
Cmd: completionCmd,
})
}
32 changes: 32 additions & 0 deletions cmd/sbctl/create-keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"fmt"

"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/logging"
"github.com/spf13/cobra"
)

var createKeysCmd = &cobra.Command{
Use: "create-keys",
Short: "Create a set of secure boot signing keys",
RunE: func(cmd *cobra.Command, args []string) error {
if !sbctl.CheckIfKeysInitialized(sbctl.KeysPath) {
logging.Print("Creating secure boot keys...")
err := sbctl.InitializeSecureBootKeys(sbctl.DatabasePath)
if err != nil {
return fmt.Errorf("couldn't initialize secure boot: %w", err)
}
} else {
logging.Ok("Secure boot keys has already been created!")
}
return nil
},
}

func init() {
CliCommands = append(CliCommands, cliCommand{
Cmd: createKeysCmd,
})
}
46 changes: 46 additions & 0 deletions cmd/sbctl/enroll-keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"errors"
"fmt"

"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/logging"
"github.com/spf13/cobra"
)

var enrollKeysCmd = &cobra.Command{
Use: "enroll-keys",
Short: "Enroll the current keys to EFI",
RunE: func(cmd *cobra.Command, args []string) error {
var isImmutable bool
for _, file := range sbctl.EfivarFSFiles {
err := sbctl.IsImmutable(file)
if errors.Is(err, sbctl.ErrImmutable) {
isImmutable = true
logging.Warn("File is immutable: %s", file)
} else if errors.Is(err, sbctl.ErrNotImmutable) {
continue
} else if err != nil {
return fmt.Errorf("couldn't read file: %s", file)
}
}
if isImmutable {
return sbctl.ErrImmutable
}
logging.Print("Syncing keys to EFI variables...")
synced := sbctl.SBKeySync(sbctl.KeysPath)
if !synced {
return errors.New("couldn't sync keys")
}
logging.Println("")
logging.Ok("Synced keys!")
return nil
},
}

func init() {
CliCommands = append(CliCommands, cliCommand{
Cmd: enrollKeysCmd,
})
}
69 changes: 69 additions & 0 deletions cmd/sbctl/generate-bundles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"errors"
"fmt"

"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/logging"
"github.com/spf13/cobra"
)

var (
sign bool
)

var generateBundlesCmd = &cobra.Command{
Use: "generate-bundles",
Short: "Generate all EFI stub bundles",
RunE: func(cmd *cobra.Command, args []string) error {
logging.Println("Generating EFI bundles....")
out_create := true
out_sign := true
err := sbctl.BundleIter(func(bundle *sbctl.Bundle) error {
err := sbctl.CreateBundle(*bundle)
if err != nil {
fmt.Println(err)
out_create = false
return nil
}
logging.Print("Wrote EFI bundle %s\n", bundle.Output)
if sign {
file := bundle.Output
err = sbctl.SignFile(sbctl.DBKey, sbctl.DBCert, file, file, "")
if errors.Is(err, sbctl.ErrAlreadySigned) {
logging.Unknown("Bundle has already been signed")
} else if err != nil {
out_sign = false
} else {
logging.Ok("Signed %s", file)
}
}
return nil
})

if !out_create {
return errors.New("error generating EFI bundles")
}

if !out_sign {
return errors.New("error signing EFI bundles")
}
if err != nil {
return err
}
return nil
},
}

func generateBundlesCmdFlags(cmd *cobra.Command) {
f := cmd.Flags()
f.BoolVarP(&sign, "sign", "s", false, "Sign all the generated bundles")
}

func init() {
generateBundlesCmdFlags(generateBundlesCmd)
CliCommands = append(CliCommands, cliCommand{
Cmd: generateBundlesCmd,
})
}
Loading

0 comments on commit 5d528ff

Please sign in to comment.