Add infrastructure to allow firmware to verify the integrity of GRUB by use of a Linux-kernel-module-style appended signature. We initially target powerpc-ieee1275, but the code should be extensible to other platforms. Usually these signatures are appended to a file without modifying the ELF file itself. (This is what the 'sign-file' tool does, for example.) The verifier loads the signed file from the file system and looks at the end of the file for the appended signature. However, on powerpc-ieee1275 platforms, the bootloader is often stored directly in the PReP partition as raw bytes without a file-system. This makes determining the location of an appended signature more difficult. To address this, we add a new ELF Note. The name field of shall be the string "Appended-Signature", zero-padded to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values for the string "ASig"). It must be the final section in the ELF binary. The description shall contain the appended signature structure as defined by the Linux kernel. The description will also be padded to be a multiple of 4 bytes. The padding shall be added before the appended signature structure (not at the end) so that the final bytes of a signed ELF file are the appended signature magic. A subsequent patch documents how to create a GRUB core.img validly signed under this scheme. Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com> Signed-off-by: Daniel Axtens <dja@axtens.net> Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
371 lines
10 KiB
C
371 lines
10 KiB
C
/* grub-mkimage.c - make a bootable image */
|
||
/*
|
||
* GRUB -- GRand Unified Bootloader
|
||
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 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/>.
|
||
*/
|
||
|
||
#include <config.h>
|
||
#include <grub/types.h>
|
||
#include <grub/elf.h>
|
||
#include <grub/aout.h>
|
||
#include <grub/i18n.h>
|
||
#include <grub/kernel.h>
|
||
#include <grub/disk.h>
|
||
#include <grub/emu/misc.h>
|
||
#include <grub/util/misc.h>
|
||
#include <grub/util/resolve.h>
|
||
#include <grub/misc.h>
|
||
#include <grub/offsets.h>
|
||
#include <grub/crypto.h>
|
||
#include <grub/dl.h>
|
||
#include <time.h>
|
||
#include <multiboot.h>
|
||
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <assert.h>
|
||
#include <grub/efi/pe32.h>
|
||
#include <grub/uboot/image.h>
|
||
#include <grub/arm/reloc.h>
|
||
#include <grub/ia64/reloc.h>
|
||
#include <grub/osdep/hostfile.h>
|
||
#include <grub/util/install.h>
|
||
#include <grub/emu/config.h>
|
||
|
||
#define _GNU_SOURCE 1
|
||
|
||
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
|
||
#pragma GCC diagnostic ignored "-Wmissing-declarations"
|
||
#include <argp.h>
|
||
#pragma GCC diagnostic error "-Wmissing-prototypes"
|
||
#pragma GCC diagnostic error "-Wmissing-declarations"
|
||
|
||
|
||
#include "progname.h"
|
||
|
||
|
||
|
||
static struct argp_option options[] = {
|
||
{"directory", 'd', N_("DIR"), 0,
|
||
/* TRANSLATORS: platform here isn't identifier. It can be translated. */
|
||
N_("use images and modules under DIR [default=%s/<platform>]"), 0},
|
||
{"prefix", 'p', N_("DIR"), 0, N_("set prefix directory"), 0},
|
||
{"memdisk", 'm', N_("FILE"), 0,
|
||
/* TRANSLATORS: "memdisk" here isn't an identifier, it can be translated.
|
||
"embed" is a verb (command description). "*/
|
||
N_("embed FILE as a memdisk image\n"
|
||
"Implies `-p (memdisk)/boot/grub' and overrides any prefix supplied previously,"
|
||
" but the prefix itself can be overridden by later options"), 0},
|
||
{"dtb", 'D', N_("FILE"), 0, N_("embed FILE as a device tree (DTB)\n"), 0},
|
||
/* TRANSLATORS: "embed" is a verb (command description). "*/
|
||
{"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
|
||
/* TRANSLATORS: "embed" is a verb (command description). "*/
|
||
{"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0},
|
||
/* TRANSLATORS: NOTE is a name of segment. */
|
||
{"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0},
|
||
{"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0},
|
||
{"format", 'O', N_("FORMAT"), 0, 0, 0},
|
||
{"compression", 'C', "(xz|none|auto)", 0, N_("choose the compression to use for core image"), 0},
|
||
{"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
|
||
{"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
|
||
{"disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, N_("disable command line interface access"), 0},
|
||
{"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0},
|
||
{"verbose", 'v', 0, 0, N_("print verbose messages."), 0},
|
||
{ 0, 0, 0, 0, 0, 0 }
|
||
};
|
||
|
||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||
|
||
static char *
|
||
help_filter (int key, const char *text, void *input __attribute__ ((unused)))
|
||
{
|
||
switch (key)
|
||
{
|
||
case 'd':
|
||
return xasprintf (text, grub_util_get_pkglibdir ());
|
||
case 'O':
|
||
{
|
||
char *formats = grub_install_get_image_targets_string (), *ret;
|
||
ret = xasprintf ("%s\n%s %s", _("generate an image in FORMAT"),
|
||
_("available formats:"), formats);
|
||
free (formats);
|
||
return ret;
|
||
}
|
||
default:
|
||
return (char *) text;
|
||
}
|
||
}
|
||
|
||
#pragma GCC diagnostic error "-Wformat-nonliteral"
|
||
|
||
struct arguments
|
||
{
|
||
size_t nmodules;
|
||
size_t modules_max;
|
||
char **modules;
|
||
char *output;
|
||
char *dir;
|
||
char *prefix;
|
||
char *memdisk;
|
||
char *dtb;
|
||
char **pubkeys;
|
||
size_t npubkeys;
|
||
char *font;
|
||
char *config;
|
||
char *sbat;
|
||
int note;
|
||
int disable_shim_lock;
|
||
int disable_cli;
|
||
size_t appsig_size;
|
||
const struct grub_install_image_target_desc *image_target;
|
||
grub_compression_t comp;
|
||
};
|
||
|
||
static error_t
|
||
argp_parser (int key, char *arg, struct argp_state *state)
|
||
{
|
||
/* Get the input argument from argp_parse, which we
|
||
know is a pointer to our arguments structure. */
|
||
struct arguments *arguments = state->input;
|
||
const char *end;
|
||
|
||
switch (key)
|
||
{
|
||
case 'o':
|
||
if (arguments->output)
|
||
free (arguments->output);
|
||
|
||
arguments->output = xstrdup (arg);
|
||
break;
|
||
|
||
case 'O':
|
||
{
|
||
arguments->image_target = grub_install_get_image_target (arg);
|
||
if (!arguments->image_target)
|
||
{
|
||
printf (_("unknown target format %s\n"), arg);
|
||
argp_usage (state);
|
||
exit (1);
|
||
}
|
||
break;
|
||
}
|
||
case 'd':
|
||
if (arguments->dir)
|
||
free (arguments->dir);
|
||
|
||
arguments->dir = xstrdup (arg);
|
||
break;
|
||
|
||
case 'n':
|
||
arguments->note = 1;
|
||
break;
|
||
|
||
case 'S':
|
||
arguments->appsig_size = grub_strtoul (arg, &end, 10);
|
||
if (*arg == '\0' || *end != '\0')
|
||
grub_util_error (_("non-numeric or invalid appended signature size `%s'"), arg);
|
||
else if (arguments->appsig_size == 0)
|
||
grub_util_error (_("appended signature size `%s', and it should not be zero"), arg);
|
||
break;
|
||
|
||
case 'm':
|
||
if (arguments->memdisk)
|
||
free (arguments->memdisk);
|
||
|
||
arguments->memdisk = xstrdup (arg);
|
||
|
||
if (arguments->prefix)
|
||
free (arguments->prefix);
|
||
|
||
arguments->prefix = xstrdup ("(memdisk)/boot/grub");
|
||
break;
|
||
|
||
case 'D':
|
||
if (arguments->dtb)
|
||
free (arguments->dtb);
|
||
|
||
arguments->dtb = xstrdup (arg);
|
||
break;
|
||
|
||
case 'k':
|
||
arguments->pubkeys = xrealloc (arguments->pubkeys,
|
||
sizeof (arguments->pubkeys[0])
|
||
* (arguments->npubkeys + 1));
|
||
arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg);
|
||
break;
|
||
|
||
case 'c':
|
||
if (arguments->config)
|
||
free (arguments->config);
|
||
|
||
arguments->config = xstrdup (arg);
|
||
break;
|
||
|
||
case 'C':
|
||
if (grub_strcmp (arg, "xz") == 0)
|
||
{
|
||
#ifdef HAVE_LIBLZMA
|
||
arguments->comp = GRUB_COMPRESSION_XZ;
|
||
#else
|
||
grub_util_error ("%s",
|
||
_("grub-mkimage is compiled without XZ support"));
|
||
#endif
|
||
}
|
||
else if (grub_strcmp (arg, "none") == 0)
|
||
arguments->comp = GRUB_COMPRESSION_NONE;
|
||
else if (grub_strcmp (arg, "auto") == 0)
|
||
arguments->comp = GRUB_COMPRESSION_AUTO;
|
||
else
|
||
grub_util_error (_("Unknown compression format %s"), arg);
|
||
break;
|
||
|
||
case 'p':
|
||
if (arguments->prefix)
|
||
free (arguments->prefix);
|
||
|
||
arguments->prefix = xstrdup (arg);
|
||
break;
|
||
|
||
case 's':
|
||
if (arguments->sbat)
|
||
free (arguments->sbat);
|
||
|
||
arguments->sbat = xstrdup (arg);
|
||
break;
|
||
|
||
case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
|
||
arguments->disable_shim_lock = 1;
|
||
break;
|
||
|
||
case GRUB_INSTALL_OPTIONS_DISABLE_CLI:
|
||
arguments->disable_cli = 1;
|
||
break;
|
||
|
||
case 'v':
|
||
verbosity++;
|
||
break;
|
||
case ARGP_KEY_ARG:
|
||
assert (arguments->nmodules < arguments->modules_max);
|
||
arguments->modules[arguments->nmodules++] = xstrdup(arg);
|
||
break;
|
||
|
||
default:
|
||
return ARGP_ERR_UNKNOWN;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static struct argp argp = {
|
||
options, argp_parser, N_("[OPTION]... [MODULES]"),
|
||
N_("Make a bootable image of GRUB."),
|
||
NULL, help_filter, NULL
|
||
};
|
||
|
||
int
|
||
main (int argc, char *argv[])
|
||
{
|
||
FILE *fp = stdout;
|
||
struct arguments arguments;
|
||
unsigned i;
|
||
|
||
grub_util_host_init (&argc, &argv);
|
||
|
||
memset (&arguments, 0, sizeof (struct arguments));
|
||
arguments.comp = GRUB_COMPRESSION_AUTO;
|
||
arguments.modules_max = argc + 1;
|
||
arguments.modules = xmalloc ((arguments.modules_max + 1)
|
||
* sizeof (arguments.modules[0]));
|
||
memset (arguments.modules, 0, (arguments.modules_max + 1)
|
||
* sizeof (arguments.modules[0]));
|
||
|
||
if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0)
|
||
{
|
||
fprintf (stderr, "%s", _("Error in parsing command line arguments\n"));
|
||
exit(1);
|
||
}
|
||
|
||
if (!arguments.image_target)
|
||
{
|
||
char *program = xstrdup(program_name);
|
||
printf ("%s\n", _("Target format not specified (use the -O option)."));
|
||
argp_help (&argp, stderr, ARGP_HELP_STD_USAGE, program);
|
||
free (program);
|
||
exit(1);
|
||
}
|
||
|
||
if (!arguments.prefix)
|
||
{
|
||
char *program = xstrdup(program_name);
|
||
printf ("%s\n", _("Prefix not specified (use the -p option)."));
|
||
argp_help (&argp, stderr, ARGP_HELP_STD_USAGE, program);
|
||
free (program);
|
||
exit(1);
|
||
}
|
||
|
||
if (arguments.output)
|
||
{
|
||
fp = grub_util_fopen (arguments.output, "wb");
|
||
if (! fp)
|
||
grub_util_error (_("cannot open `%s': %s"), arguments.output,
|
||
strerror (errno));
|
||
}
|
||
|
||
if (!arguments.dir)
|
||
{
|
||
const char *dn = grub_util_get_target_dirname (arguments.image_target);
|
||
const char *pkglibdir = grub_util_get_pkglibdir ();
|
||
char *ptr;
|
||
arguments.dir = xmalloc (grub_strlen (pkglibdir) + grub_strlen (dn) + 2);
|
||
ptr = grub_stpcpy (arguments.dir, pkglibdir);
|
||
*ptr++ = '/';
|
||
strcpy (ptr, dn);
|
||
}
|
||
|
||
grub_install_generate_image (arguments.dir, arguments.prefix, fp,
|
||
arguments.output, arguments.modules,
|
||
arguments.memdisk, arguments.pubkeys,
|
||
arguments.npubkeys, arguments.config,
|
||
arguments.image_target, arguments.note,
|
||
arguments.appsig_size,
|
||
arguments.comp, arguments.dtb,
|
||
arguments.sbat, arguments.disable_shim_lock,
|
||
arguments.disable_cli);
|
||
|
||
if (grub_util_file_sync (fp) < 0)
|
||
grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout",
|
||
strerror (errno));
|
||
if (fclose (fp) == EOF)
|
||
grub_util_error (_("cannot close `%s': %s"), arguments.output ? : "stdout",
|
||
strerror (errno));
|
||
|
||
for (i = 0; i < arguments.nmodules; i++)
|
||
free (arguments.modules[i]);
|
||
|
||
free (arguments.dir);
|
||
free (arguments.prefix);
|
||
free (arguments.modules);
|
||
|
||
if (arguments.output)
|
||
free (arguments.output);
|
||
|
||
if (arguments.sbat)
|
||
free (arguments.sbat);
|
||
|
||
return 0;
|
||
}
|