Skip to content
This repository was archived by the owner on Aug 20, 2024. It is now read-only.
Open
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
5 changes: 5 additions & 0 deletions features/dev/file.include/opt/gardenlinux/cli-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ if [ -f /usr/lib/git-core/git-sh-prompt ]; then
export PS1='\w$(__git_ps1 " (%s)")\$ '
fi

# As described here https://github.com/gardenlinux/gardenlinux/blob/169f15c70afbae7b578839520eeedb9ed2619c02/docs/02_operators/deployment/aws-secureboot.md?plain=1#L107
if [ -d /opt/gardenlinux/python-uefivars ]; then
export PATH=/opt/gardenlinux/third_party/python-uefivars:$PATH
fi

alias ls='ls --group-directories-first --time-style=long-iso --color=auto --classify'
alias ll='ls -lh'
alias la='ls -lah'
Expand Down
3 changes: 3 additions & 0 deletions features/imagebuilder/exec.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

set -eufo pipefail
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
This directory contains code and projects from third party sources.
Note their respective copyright information.

| Directory | Upstream Source | License |
| ----------------- | ------------------------------------------ | ------------------------------------------------------------------------------ |
| `python-uefivars` | https://github.com/awslabs/python-uefivars | MIT License Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
*~
*#
*.swp

*.DS_Store

__pycache__/
*.py[cod]
*$py.class
*.egg-info/

/.coverage
/.coverage.*
/.cache
/.pytest_cache
/.mypy_cache

/doc/_apidoc/
/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# uefivars

This is a set of Python modules and a helper application "uefivars" to
introspect and modify UEFI variable stores.

## Why do I need this?

UEFI variable stores are typically opaque to users. You access them using
UEFI runtime services as function calls. However, the data is then stored
in a binary data format. When running virtual machines or extracting UEFI
variable stores directly from Flash storage, you can receive and write that
binary data and thus modify variables directly.

This is useful in situations where you have incorrect UEFI variable data
and need to modify variables without runtime service access. It can also
be useful to analyze and introspect the variable store and check what data
is stored inside.

## How do I use it?

You can convert a variable store into human readable format by setting the
output type to json. This will show you all variables that are currently
present in the variable store.

```console
$ uefivars -i edk2 -o json -I OVMF_VARS.secboot.fd
[
{
"name": "SecureBootEnable",
"data": "AQ==",
"guid": "f0a30bc7-af08-4556-99c4-001009c93a44",
"attr": 3
},
[...]
]
```

In addition, you can convert from the human readable json representation back
into edk2 format:

```console
$ uefivars -i json -o edk2 -I vars.json -O OVMF_VARS.fd
```

Given any variable store (including an empty one) the `--PK` , `--KEK` , `--db` and `--dbx`
switches can be used to (over-)write the four SecureBoot variables from input files.
(Usually .esl files). For a general rundown of the key generation process the [ArchLinux](https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Creating_keys) wiki has proven itself
as a first point of guidance.

You can also use the tool to convert between the AWS EC2 uefi-data format
and edk2 to import and export UEFI variable stores between an EC2 instance
and QEMU:

```console
$ uefivars -i edk2 -o aws -I OVMF_VARS.fd -O uefi-data.aws
```

```console
$ uefivars -i aws -o edk2 -I uefi-data.aws -O OVMF_VARS.fd
```

## What formats are supported?

This package currently supports the aws, edk2 and json file formats.
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env python3
#
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT

import argparse
import sys
from .varstore import *
from .aws import AWSUEFIVarStore
from .edk2 import EDK2UEFIVarStore
from .json import JSONUEFIVarStore


MIN_PYTHON = (3, 0)
if sys.version_info < MIN_PYTHON:
sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON)

globalEfiGUID = bytes.fromhex("61 df e4 8b ca 93 d2 11 aa 0d 00 e0 98 03 2b 8c")
secureDatabaseGUID = bytes.fromhex("cb b2 19 d7 3a 3d 96 45 a3 bc da d0 0e 67 65 6f")

def Str2UEFIVarStore(s):
if s == 'aws':
return AWSUEFIVarStore
elif s == 'edk2':
return EDK2UEFIVarStore
elif s == 'json':
return JSONUEFIVarStore
elif s == 'none':
return UEFIVarStore
else:
raise SystemExit(
'Unknown Input type "{}", choose from ("aws, "json", "edk2", "none")'.format(s)
)

def ReadVar(arg, name, guid):
EFI_VARIABLE_NON_VOLATILE=0x00000001
EFI_VARIABLE_BOOTSERVICE_ACCESS=0x00000002
EFI_VARIABLE_RUNTIME_ACCESS=0x00000004
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS=0x00000020

attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS

varfile = open(arg, "rb")
vardata = varfile.read()

if(len(vardata) == 0):
print('Read empty variable "{}". Aborting'.format(name), file=sys.stderr)
sys.exit()

return UEFIVar(name, vardata, guid, attr)


def _parser():
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", help='Input type ("aws", "json", "edk2", "none")', required=True)
parser.add_argument("-o", "--output", help='Output type ("aws", "json", "edk2[,filesize=512]")', required=True)
parser.add_argument("-I", "--inputfile", help='Input file (stdin if not given)')
parser.add_argument("-O", "--outputfile", help='Output file (stdout if not given)')
parser.add_argument("-P", "--PK", help='Insert PK from given file (usually PK.esl)')
parser.add_argument("-K", "--KEK", help='Insert KEK from given file (usually KEK.esl)')
parser.add_argument("-b", "--db", help='Insert db from given file (usually db.esl)')
parser.add_argument("-x", "--dbx", help='Insert dbx from given file (usually dbx.esl)')

args = parser.parse_args()
return args


def main():
pk_found = -1
kek_found = -1
db_found = -1
dbx_found = -1
args = _parser()

inclass = Str2UEFIVarStore(args.input)

args.output = [s.strip() for s in args.output.split(",")]
output_options = args.output[1:]
args.output = args.output[0]

outclass = Str2UEFIVarStore(args.output)

if args.input == 'none':
indata = ''
else:
if args.inputfile:
infile = open(args.inputfile, "rb")
else:
infile = sys.stdin.buffer
print("Reading uefivars from stdin", file=sys.stderr)

indata = infile.read()

varstore = inclass(indata)

print("Read {} variables".format(varstore.vars.__len__()), file=sys.stderr)

for i, s in enumerate(varstore.vars):
var = varstore.vars[i]

if (var.name == 'PK' and var.guid == globalEfiGUID):
pk_found = i

if (var.name == 'KEK' and var.guid == globalEfiGUID):
kek_found = i

if (var.name == 'db' and var.guid == secureDatabaseGUID):
db_found = i

if (var.name == 'dbx' and var.guid == secureDatabaseGUID):
dbx_found = i

if (args.PK):
var = ReadVar(args.PK, 'PK', globalEfiGUID)
if (pk_found != -1):
print('Replacing PK', file=sys.stderr)
varstore.vars[pk_found] = var
else:
varstore.vars.append(var)
elif (pk_found == -1):
print('No PK (PlatformKey) was set; SecureBoot will not be enabled without a PK', file=sys.stderr)

if (args.KEK):
var = ReadVar(args.KEK, 'KEK', globalEfiGUID)
if (kek_found != -1):
print('Replacing KEK', file=sys.stderr)
varstore.vars[kek_found] = var
else:
varstore.vars.append(var)

if (args.db):
var = ReadVar(args.db, 'db', secureDatabaseGUID)
if (db_found != -1):
print('Replacing db', file=sys.stderr)
varstore.vars[db_found] = var
else:
varstore.vars.append(var)

if (args.dbx):
var = ReadVar(args.dbx, 'dbx', secureDatabaseGUID)
if (dbx_found != -1):
print('Replacing dbx', file=sys.stderr)
varstore.vars[dbx_found] = var
else:
varstore.vars.append(var)

# convert the format by changing the output class
varstore.__class__ = outclass
if output_options:
varstore.set_output_options(output_options)

if args.outputfile:
outfile = open(args.outputfile, "wb")
else:
outfile = sys.stdout.buffer
outfile.write(bytes(varstore))

print("Writen {} variables".format(varstore.vars.__len__()), file=sys.stderr)


if __name__ == '__main__':
main()
Loading