Skip to content
This repository was archived by the owner on May 29, 2020. It is now read-only.
Closed
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
58 changes: 58 additions & 0 deletions docs/grub-dev.texi
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ This edition documents version @value{VERSION}.
* Video Subsystem::
* PFF2 Font File Format::
* Graphical Menu Software Design::
* Verifiers framework::
* Copying This Manual:: Copying This Manual
* Index::
@end menu
Expand Down Expand Up @@ -1949,6 +1950,63 @@ the graphics mode that was in use before @code{grub_video_setup()} was called
might fix some of the problems.


@node Verifiers framework
@chapter Verifiers framework

To register your own verifier call @samp{grub_verifier_register} with a structure
pointing to your functions.

The interface is inspired by the hash interface with @samp{init}/@samp{write}/@samp{fini}.

There are essentially 2 ways of using it, hashing and whole-file verification.

With the hashing approach:
During @samp{init} you decide whether you want to check the given file and init context.
In @samp{write} you update your hashing state.
In @samp{fini} you check that the hash matches the expected value/passes some check/...

With whole-file verification:
During @samp{init} you decide whether you want to check the given file and init context.
In @samp{write} you verify the file and return an error if it fails.
You don't have @samp{fini}.

Additional @samp{verify_string} receives various strings like kernel parameters
to verify. Returning no error means successful verification and an error stops
the current action.

Detailed description of the API:

Every time a file is opened your @samp{init} function is called with file descriptor
and file type. Your function can have the following outcomes:

@itemize

@item returning no error and setting @samp{*flags} to @samp{GRUB_VERIFY_FLAGS_DEFER_AUTH}.
In this case verification is deferred to other active verifiers. Verification
fails if nobody cares or selected verifier fails.

@item returning no error and setting @samp{*flags} to @samp{GRUB_VERIFY_FLAGS_SKIP_VERIFICATION}.
In this case your verifier will not be called anymore and it is assumed to have
skipped verification.

@item returning no error and not setting @samp{*flags} to @samp{GRUB_VERIFY_FLAGS_SKIP_VERIFICATION}
In this case verification is done as described in the following section.

@item returning an error. Then opening of the file will fail due to failed verification.

@end itemize

In the third case your @samp{write} will be called with chunks of the file. If
you need the whole file in a single chunk then during @samp{init} set the bit
@samp{GRUB_VERIFY_FLAGS_SINGLE_CHUNK} in @samp{*flags}. During @samp{init} you
may set @samp{*context} if you need additional context. At every iteration you
may return an error and the file will be considered as having failed the
verification. If you return no error then verification continues.

Optionally at the end of the file @samp{fini}, if it exists, is called with just
the context. If you return no error during any of @samp{init}, @samp{write} and
@samp{fini} then the file is considered as having succeded verification.

@node Copying This Manual
@appendix Copying This Manual

Expand Down
15 changes: 15 additions & 0 deletions docs/grub.texi
Original file line number Diff line number Diff line change
Expand Up @@ -5465,6 +5465,7 @@ environment variables and commands are listed in the same order.
@menu
* Authentication and authorisation:: Users and access control
* Using digital signatures:: Booting digitally signed code
* UEFI secure boot and shim:: Booting digitally signed PE files
@end menu

@node Authentication and authorisation
Expand Down Expand Up @@ -5627,6 +5628,20 @@ or BIOS) configuration to cause the machine to boot from a different
(attacker-controlled) device. GRUB is at best only one link in a
secure boot chain.

@node UEFI secure boot and shim
@section UEFI secure boot and shim support

The GRUB, except the @command{chainloader} command, works with the UEFI secure
boot and the shim. This functionality is provided by the shim_lock module. It
is recommend to build in this and other required modules into the @file{core.img}.
All modules not stored in the @file{core.img} and the ACPI tables for the
@command{acpi} command have to be signed, e.g. using PGP. Additionally, the
@command{iorw} and the @command{memrw} commands are prohibited if the UEFI
secure boot is enabled. This is done due to security reasons. All above
mentioned requirements are enforced by the shim_lock module. And itself it
is a persistent module which means that it cannot be unloaded if it was
loaded into the memory.

@node Platform limitations
@chapter Platform limitations

Expand Down
4 changes: 4 additions & 0 deletions grub-core/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/tis.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slaunch.h
endif

if COND_i386_efi
Expand All @@ -108,6 +110,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slaunch.h
endif

if COND_i386_coreboot
Expand Down Expand Up @@ -164,6 +167,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slaunch.h
endif

if COND_ia64_efi
Expand Down
25 changes: 23 additions & 2 deletions grub-core/Makefile.core.def
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ kernel = {

x86 = kern/i386/tsc.c;
x86 = kern/i386/tsc_pit.c;
x86 = kern/tpm.c;
x86 = kern/tis.c;
i386_efi = kern/i386/efi/tsc.c;
x86_64_efi = kern/i386/efi/tsc.c;
i386_efi = kern/i386/tsc_pmtimer.c;
Expand Down Expand Up @@ -889,12 +891,23 @@ module = {
};

module = {
name = verify;
common = commands/verify.c;
name = pgp;
common = commands/pgp.c;
cflags = '$(CFLAGS_POSIX)';
cppflags = '-I$(srcdir)/lib/posix_wrap';
};

module = {
name = verifiers;
common = commands/verifiers.c;
};

module = {
name = shim_lock;
common = commands/efi/shim_lock.c;
enable = x86_64_efi;
};

module = {
name = hdparm;
common = commands/hdparm.c;
Expand Down Expand Up @@ -1695,6 +1708,14 @@ module = {
enable = noemu;
};

module = {
name = slaunch;
x86 = loader/i386/slaunch.c;
x86 = loader/i386/slaunch_txt.c;
x86 = loader/i386/slaunch_skinit.c;
enable = x86;
};

module = {
name = fdt;
arm64 = loader/efi/fdt.c;
Expand Down
2 changes: 1 addition & 1 deletion grub-core/commands/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args)
grub_size_t size;
char *buf;

file = grub_file_open (args[i]);
file = grub_file_open (args[i], GRUB_FILE_TYPE_ACPI_TABLE);
if (! file)
{
free_tables ();
Expand Down
4 changes: 2 additions & 2 deletions grub-core/commands/blocklist.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ grub_cmd_blocklist (grub_command_t cmd __attribute__ ((unused)),
if (argc < 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));

grub_file_filter_disable_compression ();
file = grub_file_open (args[0]);
file = grub_file_open (args[0], GRUB_FILE_TYPE_PRINT_BLOCKLIST
| GRUB_FILE_TYPE_NO_DECOMPRESS);
if (! file)
return grub_errno;

Expand Down
2 changes: 1 addition & 1 deletion grub-core/commands/cat.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ grub_cmd_cat (grub_extcmd_context_t ctxt, int argc, char **args)
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));

file = grub_file_open (args[0]);
file = grub_file_open (args[0], GRUB_FILE_TYPE_CAT);
if (! file)
return grub_errno;

Expand Down
4 changes: 2 additions & 2 deletions grub-core/commands/cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ grub_cmd_cmp (grub_command_t cmd __attribute__ ((unused)),
grub_printf_ (N_("Compare file `%s' with `%s':\n"), args[0],
args[1]);

file1 = grub_file_open (args[0]);
file2 = grub_file_open (args[1]);
file1 = grub_file_open (args[0], GRUB_FILE_TYPE_CMP);
file2 = grub_file_open (args[1], GRUB_FILE_TYPE_CMP);
if (! file1 || ! file2)
goto cleanup;

Expand Down
4 changes: 2 additions & 2 deletions grub-core/commands/efi/loadbios.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ grub_cmd_loadbios (grub_command_t cmd __attribute__ ((unused)),

if (argc > 1)
{
file = grub_file_open (argv[1]);
file = grub_file_open (argv[1], GRUB_FILE_TYPE_VBE_DUMP);
if (! file)
return grub_errno;

Expand All @@ -183,7 +183,7 @@ grub_cmd_loadbios (grub_command_t cmd __attribute__ ((unused)),
return grub_errno;
}

file = grub_file_open (argv[0]);
file = grub_file_open (argv[0], GRUB_FILE_TYPE_VBE_DUMP);
if (! file)
return grub_errno;

Expand Down
141 changes: 141 additions & 0 deletions grub-core/commands/efi/shim_lock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2017 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*
* EFI shim lock verifier.
*/

#include <grub/dl.h>
#include <grub/efi/efi.h>
#include <grub/err.h>
#include <grub/file.h>
#include <grub/misc.h>
#include <grub/verify.h>

GRUB_MOD_LICENSE ("GPLv3+");

#define GRUB_EFI_SHIM_LOCK_GUID \
{ 0x605dab50, 0xe046, 0x4300, \
{ 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } \
}

struct grub_efi_shim_lock_protocol
{
grub_efi_status_t
(*verify) (void *buffer, grub_uint32_t size);
};
typedef struct grub_efi_shim_lock_protocol grub_efi_shim_lock_protocol_t;

static grub_efi_guid_t shim_lock_guid = GRUB_EFI_SHIM_LOCK_GUID;
static grub_efi_shim_lock_protocol_t *sl;

/* List of modules which cannot be loaded if UEFI secure boot mode is enabled. */
static const char * const disabled_mods[] = {"iorw", "memrw", NULL};

static grub_err_t
shim_lock_init (grub_file_t io, enum grub_file_type type,
void **context __attribute__ ((unused)),
enum grub_verify_flags *flags)
{
const char *b, *e;
int i;

*flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;

if (!sl)
return GRUB_ERR_NONE;

switch (type & GRUB_FILE_TYPE_MASK)
{
case GRUB_FILE_TYPE_GRUB_MODULE:
/* Establish GRUB module name. */
b = grub_strrchr (io->name, '/');
e = grub_strrchr (io->name, '.');

b = b ? (b + 1) : io->name;
e = e ? e : io->name + grub_strlen (io->name);
e = (e > b) ? e : io->name + grub_strlen (io->name);

for (i = 0; disabled_mods[i]; i++)
if (!grub_strncmp (b, disabled_mods[i], grub_strlen (b) - grub_strlen (e)))
{
grub_error (GRUB_ERR_ACCESS_DENIED,
N_("module cannot be loaded in UEFI secure boot mode: %s"),
io->name);
return GRUB_ERR_ACCESS_DENIED;
}

/* Fall through. */

case GRUB_FILE_TYPE_ACPI_TABLE:
*flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;

return GRUB_ERR_NONE;

case GRUB_FILE_TYPE_LINUX_KERNEL:
case GRUB_FILE_TYPE_MULTIBOOT_KERNEL:
case GRUB_FILE_TYPE_BSD_KERNEL:
case GRUB_FILE_TYPE_XNU_KERNEL:
case GRUB_FILE_TYPE_PLAN9_KERNEL:
for (i = 0; disabled_mods[i]; i++)
if (grub_dl_get (disabled_mods[i]))
{
grub_error (GRUB_ERR_ACCESS_DENIED,
N_("cannot boot due to dangerous module in memory: %s"),
disabled_mods[i]);
return GRUB_ERR_ACCESS_DENIED;
}

*flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;

/* Fall through. */

default:
return GRUB_ERR_NONE;
}
}

static grub_err_t
shim_lock_write (void *context __attribute__ ((unused)), void *buf, grub_size_t size)
{
if (sl->verify (buf, size) != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad shim signature"));

return GRUB_ERR_NONE;
}

struct grub_file_verifier shim_lock =
{
.name = "shim_lock",
.init = shim_lock_init,
.write = shim_lock_write
};

GRUB_MOD_INIT(shim_lock)
{
sl = grub_efi_locate_protocol (&shim_lock_guid, 0);
grub_verifier_register (&shim_lock);

if (!sl)
return;

grub_dl_set_persistent (mod);
}

GRUB_MOD_FINI(shim_lock)
{
grub_verifier_unregister (&shim_lock);
}
5 changes: 3 additions & 2 deletions grub-core/commands/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ grub_cmd_file (grub_extcmd_context_t ctxt, int argc, char **args)
if (type == -1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no type specified");

file = grub_file_open (args[0]);
file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL);
if (!file)
return grub_errno;
switch (type)
Expand Down Expand Up @@ -546,7 +546,8 @@ grub_cmd_file (grub_extcmd_context_t ctxt, int argc, char **args)
case IS_XNU64:
case IS_XNU32:
{
macho = grub_macho_open (args[0], (type == IS_XNU64));
macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL,
(type == IS_XNU64));
if (!macho)
break;
/* FIXME: more checks? */
Expand Down
Loading