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

status: Warn about firmware quirks #189

Merged
merged 1 commit into from
Mar 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
31 changes: 21 additions & 10 deletions cmd/sbctl/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/foxboron/sbctl"
"github.com/foxboron/sbctl/fs"
"github.com/foxboron/sbctl/logging"
"github.com/foxboron/sbctl/quirks"
"github.com/spf13/cobra"
)

Expand All @@ -19,20 +20,22 @@ var statusCmd = &cobra.Command{
}

type Status struct {
Installed bool `json:"installed"`
GUID string `json:"guid"`
SetupMode bool `json:"setup_mode"`
SecureBoot bool `json:"secure_boot"`
Vendors []string `json:"vendors"`
Installed bool `json:"installed"`
GUID string `json:"guid"`
SetupMode bool `json:"setup_mode"`
SecureBoot bool `json:"secure_boot"`
Vendors []string `json:"vendors"`
FirmwareQuirks []quirks.Quirk `json:"firmware_quirks"`
}

func NewStatus() *Status {
return &Status{
Installed: false,
GUID: "",
SetupMode: false,
SecureBoot: false,
Vendors: []string{},
Installed: false,
GUID: "",
SetupMode: false,
SecureBoot: false,
Vendors: []string{},
FirmwareQuirks: []quirks.Quirk{},
}
}

Expand Down Expand Up @@ -67,6 +70,13 @@ func PrintStatus(s *Status) {
} else {
logging.Println("none")
}
if len(s.FirmwareQuirks) > 0 {
logging.Print("Firmware:\t")
logging.Print(logging.Warnf("Your firmware has known quirks"))
for _, quirk := range s.FirmwareQuirks {
logging.Println("\t\t- " + quirk.ID + ": " + quirk.Name + " (" + quirk.Severity + ")\n\t\t " + quirk.Link)
}
}
}

func RunStatus(cmd *cobra.Command, args []string) error {
Expand All @@ -90,6 +100,7 @@ func RunStatus(cmd *cobra.Command, args []string) error {
if keys := sbctl.GetEnrolledVendorCerts(); len(keys) > 0 {
stat.Vendors = keys
}
stat.FirmwareQuirks = quirks.CheckFirmwareQuirks()
if cmdOptions.JsonOutput {
if err := JsonOut(stat); err != nil {
return err
Expand Down
152 changes: 152 additions & 0 deletions cmd/sbctl/status_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package main

import (
"reflect"
"testing"
"testing/fstest"

"github.com/foxboron/go-uefi/efi/efitest"
"github.com/foxboron/sbctl/quirks"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -38,3 +41,152 @@ func TestStatusOn(t *testing.T) {
t.Fatal("secure boot is not enabled")
}
}

func TestFQ0001DateMethod(t *testing.T) {
SetFS(
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_date": {Data: []byte("01/06/2023\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_version": {Data: []byte("A.30\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_name": {Data: []byte("PRO Z790-A WIFI (MS-7E07)\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_vendor": {Data: []byte("Micro-Star International Co., Ltd.\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/chassis_type": {Data: []byte("3\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_name": {Data: []byte("MS-7E07\n")}},
efitest.SecureBootOn(),
)

if err := captureJsonOutput(&out, func() error {
return RunStatus(&cobra.Command{}, []string{})
}); err != nil {
t.Fatal(err)
}

fq0001 := quirks.Quirk{}
for _, quirk := range out.FirmwareQuirks {
if quirk.ID == "FQ0001" {
fq0001 = quirk
}
}

if reflect.ValueOf(fq0001).IsZero() {
t.Fatal("quirk not detected")
} else if fq0001.Method != "date" {
t.Fatal("expected 'date' method, got '" + fq0001.Method + "'")
}
}

func TestFQ0001DeviceMethod(t *testing.T) {
SetFS(
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_date": {Data: []byte("12/29/2021\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_version": {Data: []byte("1.80\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_name": {Data: []byte("MAG X570 TOMAHAWK WIFI (MS-7C84)\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_vendor": {Data: []byte("Micro-Star International Co., Ltd.\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/chassis_type": {Data: []byte("3\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_name": {Data: []byte("MS-7C84\n")}},
efitest.SecureBootOn(),
)

if err := captureJsonOutput(&out, func() error {
return RunStatus(&cobra.Command{}, []string{})
}); err != nil {
t.Fatal(err)
}

fq0001 := quirks.Quirk{}
for _, quirk := range out.FirmwareQuirks {
if quirk.ID == "FQ0001" {
fq0001 = quirk
}
}

if reflect.ValueOf(fq0001).IsZero() {
t.Fatal("quirk not detected")
} else if fq0001.Method != "device_name" {
t.Fatal("expected 'device_name' method, got '" + fq0001.Method + "'")
}
}

func TestFQ0001ExplicitlyUnaffected(t *testing.T) {
SetFS(
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_date": {Data: []byte("03/31/2022\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_version": {Data: []byte("1.B0\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_name": {Data: []byte("MAG Z490 TOMAHAWK (MS-7C80)\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_vendor": {Data: []byte("Micro-Star International Co., Ltd.\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/chassis_type": {Data: []byte("3\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_name": {Data: []byte("MS-7C80\n")}},
efitest.SecureBootOn(),
)

if err := captureJsonOutput(&out, func() error {
return RunStatus(&cobra.Command{}, []string{})
}); err != nil {
t.Fatal(err)
}

fq0001 := quirks.Quirk{}
for _, quirk := range out.FirmwareQuirks {
if quirk.ID == "FQ0001" {
fq0001 = quirk
}
}

if !reflect.ValueOf(fq0001).IsZero() {
t.Fatal("quirk got detected, with method '" + fq0001.Method + "'")
}
}

func TestFQ0001WrongChassis(t *testing.T) {
SetFS(
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_date": {Data: []byte("01/06/2023\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_version": {Data: []byte("A.30\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_name": {Data: []byte("PRO Z790-A WIFI (MS-7E07)\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_vendor": {Data: []byte("Micro-Star International Co., Ltd.\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/chassis_type": {Data: []byte("5\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_name": {Data: []byte("MS-7E07\n")}},
efitest.SecureBootOn(),
)

if err := captureJsonOutput(&out, func() error {
return RunStatus(&cobra.Command{}, []string{})
}); err != nil {
t.Fatal(err)
}

fq0001 := quirks.Quirk{}
for _, quirk := range out.FirmwareQuirks {
if quirk.ID == "FQ0001" {
fq0001 = quirk
}
}

if !reflect.ValueOf(fq0001).IsZero() {
t.Fatal("quirk got detected using '" + fq0001.Method + "' method")
}
}

func TestFQ0001WrongVendor(t *testing.T) {
SetFS(
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_date": {Data: []byte("01/06/2023\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_version": {Data: []byte("A.30\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_name": {Data: []byte("PRO Z790-A WIFI (MS-7E07)\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_vendor": {Data: []byte("More-Security Issues Co., Ltd.\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/chassis_type": {Data: []byte("3\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_name": {Data: []byte("MS-7E07\n")}},
efitest.SecureBootOn(),
)

if err := captureJsonOutput(&out, func() error {
return RunStatus(&cobra.Command{}, []string{})
}); err != nil {
t.Fatal(err)
}

fq0001 := quirks.Quirk{}
for _, quirk := range out.FirmwareQuirks {
if quirk.ID == "FQ0001" {
fq0001 = quirk
}
}

if !reflect.ValueOf(fq0001).IsZero() {
t.Fatal("quirk got detected using '" + fq0001.Method + "' method")
}
}
51 changes: 51 additions & 0 deletions dmi/dmi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package dmi

import (
"strings"
"time"

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

var Table = DMI{}

type DMI struct {
BoardName string `json:"board_name"`
BoardVendor string `json:"board_vendor"`
BoardVersion string `json:"board_version"`
ChassisType string `json:"chassis_type"`
FirmwareDate time.Time `json:"firmware_date"`
FirmwareRelease string `json:"firmware_release"`
FirmwareVendor string `json:"firmware_vendor"`
FirmwareVersion string `json:"firmware_version"`
ProductFamily string `json:"product_family"`
ProductName string `json:"product_name"`
ProductSKU string `json:"product_sku"`
ProductVersion string `json:"product_version"`
SystemVendor string `json:"system_vendor"`
}

func readValue(filename string) string {
f, _ := fs.ReadFile("/sys/devices/virtual/dmi/id/" + filename)
return strings.TrimSpace(string(f))
}

func ParseDMI() DMI {
dmi := DMI{}

dmi.BoardName = readValue("board_name")
dmi.BoardVendor = readValue("board_vendor")
dmi.BoardVersion = readValue("board_version")
dmi.ChassisType = readValue("chassis_type")
dmi.FirmwareDate, _ = time.Parse("01/02/2006", readValue("bios_date"))
dmi.FirmwareRelease = readValue("bios_release")
dmi.FirmwareVendor = readValue("bios_vendor")
dmi.FirmwareVersion = readValue("bios_version")
dmi.ProductFamily = readValue("product_family")
dmi.ProductName = readValue("product_name")
dmi.ProductSKU = readValue("product_sku")
dmi.ProductVersion = readValue("product_version")
dmi.SystemVendor = readValue("sys_vendor")

return dmi
}
63 changes: 63 additions & 0 deletions dmi/dmi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package dmi

import (
"testing"
"testing/fstest"
"time"

"github.com/foxboron/go-uefi/efi/efitest"
"github.com/foxboron/sbctl/fs"
)

func TestDMIParse(t *testing.T) {
f := efitest.NewFS().With(
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_date": {Data: []byte("01/13/2023\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_release": {Data: []byte("HorribleFirmwareRelease\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_vendor": {Data: []byte("EmbarrassedFirmwareVendor\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/bios_version": {Data: []byte("InsecureFirmwareVersion\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_name": {Data: []byte("BadBoardName\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_vendor": {Data: []byte("IncompetentBoardVendor\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/board_version": {Data: []byte("WeirdBoardVersion\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/chassis_type": {Data: []byte("3\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_family": {Data: []byte("MediocreProductFamily\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_name": {Data: []byte("AwfulProductName\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_sku": {Data: []byte("RandomProductSKU\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/product_version": {Data: []byte("CrazyProductVersion\n")}},
fstest.MapFS{"/sys/devices/virtual/dmi/id/sys_vendor": {Data: []byte("EvilSystemVendor\n")}},
efitest.SecureBootOn(),
).SetFS()
fs.SetFS(f.ToAfero())

dmiTable := ParseDMI()

if dmiTable.BoardName != "BadBoardName" {
t.Fatal("BoardName: expected 'BadBoardName', got '" + dmiTable.BoardName + "'")
}
if dmiTable.BoardVendor != "IncompetentBoardVendor" {
t.Fatal("BoardVendor: expected 'IncompetentBoardVendor', got '" + dmiTable.BoardVendor + "'")
}
if dmiTable.BoardVersion != "WeirdBoardVersion" {
t.Fatal("BoardVersion: expected 'WeirdBoardVersion', got '" + dmiTable.BoardVersion + "'")
}
if dmiTable.ChassisType != "3" {
t.Fatal("ChassisType: expected '3', got '" + dmiTable.ChassisType + "'")
}
if dmiTable.FirmwareDate != time.Date(2023, 1, 13, 0, 0, 0, 0, time.UTC) {
t.Fatal("FirmwareDate: expected '2023-01-13', got '" + dmiTable.FirmwareDate.Format("2006-01-02") + "'")
}
if dmiTable.ProductFamily != "MediocreProductFamily" {
t.Fatal("ProductFamily: expected 'MediocreProductFamily', got '" + dmiTable.ProductFamily + "'")
}
if dmiTable.ProductName != "AwfulProductName" {
t.Fatal("ProductName: expected 'AwfulProductName', got '" + dmiTable.ProductName + "'")
}
if dmiTable.ProductSKU != "RandomProductSKU" {
t.Fatal("RandomProductSKU: expected 'RandomProductSKU', got '" + dmiTable.ProductSKU + "'")
}
if dmiTable.ProductVersion != "CrazyProductVersion" {
t.Fatal("ProductVersion: expected 'CrazyProductVersion' , got '" + dmiTable.ProductVersion + "'")
}
if dmiTable.SystemVendor != "EvilSystemVendor" {
t.Fatal("SystemVendor: expected 'EvilSystemVendor', got '" + dmiTable.SystemVendor + "'")
}
}
Loading