Skip to content

Commit

Permalink
feature: support deleting efi variables.
Browse files Browse the repository at this point in the history
this is potentially VERY dangerous btw
  • Loading branch information
0x5a17ed committed May 7, 2022
1 parent dd02fcd commit fdf1c9f
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 14 deletions.
46 changes: 32 additions & 14 deletions efi/efivario/context_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ func (it *varNameIterator) Err() error {
return it.err
}

func convertNameGuid(name string, guid efiguid.GUID) (lpName, lpGuid *uint16, err error) {
lpName, err = syscall.UTF16PtrFromString(name)
if err != nil {
err = fmt.Errorf("utf16(%q): %w", name, err)
return
}

lpGuid, err = syscall.UTF16PtrFromString(guid.Braced())
if err != nil {
err = fmt.Errorf("utf16(%q): %w", guid, err)
return
}

return
}

// WindowsContext provides an implementation of the Context API
// for the windows platform.
type WindowsContext struct{}
Expand Down Expand Up @@ -131,15 +147,9 @@ func (c WindowsContext) GetSizeHint(name string, guid efiguid.GUID) (int64, erro
}

func (c WindowsContext) Get(name string, guid efiguid.GUID, out []byte) (a Attributes, n int, err error) {
lpName, err := syscall.UTF16PtrFromString(name)
lpName, lpGuid, err := convertNameGuid(name, guid)
if err != nil {
err = fmt.Errorf("efivario/Get: utf16(name): %w", err)
return
}

lpGuid, err := syscall.UTF16PtrFromString(guid.Braced())
if err != nil {
err = fmt.Errorf("efivario/Get: utf16(guid): %w", err)
err = fmt.Errorf("efivario/Get: %w", err)
return
}

Expand All @@ -159,19 +169,27 @@ func (c WindowsContext) Get(name string, guid efiguid.GUID, out []byte) (a Attri
}

func (c WindowsContext) Set(name string, guid efiguid.GUID, attributes Attributes, value []byte) error {
lpName, err := syscall.UTF16PtrFromString(name)
lpName, lpGuid, err := convertNameGuid(name, guid)
if err != nil {
return fmt.Errorf("efivario/Set: utf16(%q): %w", name, err)
return fmt.Errorf("efivario/Set: %w", err)
}

lpGuid, err := syscall.UTF16PtrFromString(guid.Braced())
err = efiwindows.SetFirmwareEnvironmentVariableEx(lpName, lpGuid, value, (uint32)(attributes))
if err != nil {
return fmt.Errorf("efivario/Set: utf16(%q): %w", guid, err)
return fmt.Errorf("efivario/Set: %w", err)
}
return nil
}

err = efiwindows.SetFirmwareEnvironmentVariableEx(lpName, lpGuid, value, (uint32)(attributes))
func (c WindowsContext) Delete(name string, guid efiguid.GUID) error {
lpName, lpGuid, err := convertNameGuid(name, guid)
if err != nil {
return fmt.Errorf("efivario/Set: %w", err)
return fmt.Errorf("efivario/Delete: %w", err)
}

err = efiwindows.SetFirmwareEnvironmentVariableEx(lpName, lpGuid, nil, 0)
if err != nil {
return fmt.Errorf("efivario/Delete: %w", err)
}
return nil
}
Expand Down
4 changes: 4 additions & 0 deletions efi/efivario/contextbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ type Context interface {
// Set writes a specific EFI variable.
Set(name string, guid efiguid.GUID, attrs Attributes, value []byte) error

// Delete causes the entire variable to be removed from
// existence completely.
Delete(name string, guid efiguid.GUID) error

// VariableNames returns an Iterator which enumerates all
// EFI variables that are currently set on the current system.
VariableNames() (VariableNameIterator, error)
Expand Down
28 changes: 28 additions & 0 deletions efi/efivario/fscontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,30 @@ func (c FsContext) writeEfiVarFileName(name string, value []byte, attrs Attribut
return nil
}

func (c FsContext) deleteEfiFile(name string) error {
guard, err := openSafeguard(c.fs, name)
if err != nil {
return fmt.Errorf("efivario/delete: guard open: %w", err)
}
if guard != nil {
defer multierr.AppendInvoke(&err, multierr.Invoke(func() error {
if err := guard.Close(); err != nil {
return fmt.Errorf("efivario/set: guard close: %w", err)
}
return nil
}))
}

if _, err := guard.disable(); err != nil {
return fmt.Errorf("efivario/delete: guard disable: %w", err)
}

if err := c.fs.Remove(name); err != nil {
return fmt.Errorf("efivario/delete: remove: %w", err)
}
return nil
}

func (c FsContext) GetSizeHint(name string, guid efiguid.GUID) (int64, error) {
fi, err := c.fs.Stat(getFileName(name, guid))
if err != nil {
Expand All @@ -220,6 +244,10 @@ func (c FsContext) Set(name string, guid efiguid.GUID, attributes Attributes, va
return c.writeEfiVarFileName(getFileName(name, guid), value, attributes)
}

func (c FsContext) Delete(name string, guid efiguid.GUID) error {
return c.deleteEfiFile(getFileName(name, guid))
}

func NewFileSystemContext(fs afero.Fs) *FsContext {
return &FsContext{fs: fs}
}
16 changes: 16 additions & 0 deletions efi/efivario/fscontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ func (s *FsContextTestSuite) TestSetExistingVariable() {
require.NoError(s.T(), err)
}

// TestDelete tests deleting an existing variable.
func (s *FsContextTestSuite) TestDelete() {
// given that ...
f, err := s.context.fs.Create("TestVar-3CD99F3F-4B2B-43EB-AC29-F0890A4772B7")
require.NoError(s.T(), err)
defer func() { require.NoError(s.T(), f.Close()) }()

// when ...
err = s.context.Delete("TestVar", testGuid)
require.NoError(s.T(), err)

// then ...
_, err = s.context.fs.Stat("TestVar-3CD99F3F-4B2B-43EB-AC29-F0890A4772B7")
require.True(s.T(), errors.Is(err, afero.ErrFileNotFound))
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestFsContextTestSuite(t *testing.T) {
Expand Down

0 comments on commit fdf1c9f

Please sign in to comment.