efivar/0037-Implement-efivar-export-foo.var.patch

1195 lines
31 KiB
Diff
Raw Normal View History

From 488976e782a7307cc2df4e1a8aaea1f6dfa757dd Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Mon, 17 Jun 2019 15:04:11 -0400
Subject: [PATCH 37/63] Implement 'efivar --export=foo.var'
Signed-off-by: Peter Jones <pjones@redhat.com>
---
.gitignore | 17 +-
src/Makefile | 2 +-
src/crc32.h | 19 ++
src/efivar.c | 362 ++++++++++++++++++++------
src/export.c | 502 +++++++++++++++++++++++++++++++++---
src/gpt.c | 18 --
src/include/efivar/efivar.h | 3 +
7 files changed, 783 insertions(+), 140 deletions(-)
diff --git a/.gitignore b/.gitignore
index 5c3fd0e3f52..947d88eec42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,26 @@
+.*.c.P
+.*.h.P
+.*.d
.*.sw?
*~
*.a
+*.env
*.E
*.o
+*.map
*.pc
*.S
!src/guids.S
*.so
*.so.*
+*.spec
*.tar.*
-.*.c.P
-.*.h.P
-.*.d
+*.var
core.*
-*.spec
+cov-int
+vgcore.*
+scan-results/
src/efivar
src/efivar-static
src/makeguids
src/guid-symbols.c
-*.map
-cov-int
-scan-results/
diff --git a/src/Makefile b/src/Makefile
index ecbbc02e1f7..addfaa03c85 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -17,7 +17,7 @@ STATICTARGETS=$(STATICLIBTARGETS) $(STATICBINTARGETS)
LIBEFIBOOT_SOURCES = crc32.c creator.c disk.c gpt.c loadopt.c path-helpers.c \
linux.c $(wildcard linux-*.c)
LIBEFIBOOT_OBJECTS = $(patsubst %.c,%.o,$(LIBEFIBOOT_SOURCES))
-LIBEFIVAR_SOURCES = dp.c dp-acpi.c dp-hw.c dp-media.c dp-message.c \
+LIBEFIVAR_SOURCES = crc32.c dp.c dp-acpi.c dp-hw.c dp-media.c dp-message.c \
efivarfs.c error.c export.c guid.c guids.S guid-symbols.c \
lib.c vars.c
LIBEFIVAR_OBJECTS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(LIBEFIVAR_SOURCES)))
diff --git a/src/crc32.h b/src/crc32.h
index b5b975a5768..4e833aafac0 100644
--- a/src/crc32.h
+++ b/src/crc32.h
@@ -29,6 +29,25 @@
extern uint32_t crc32 (const void *buf, unsigned long len, uint32_t seed);
+/**
+ * efi_crc32() - EFI version of crc32 function
+ * @buf: buffer to calculate crc32 of
+ * @len - length of buf
+ *
+ * Description: Returns EFI-style CRC32 value for @buf
+ *
+ * This function uses the little endian Ethernet polynomial
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ * Note, the EFI Specification, v1.02, has a reference to
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
+ */
+static inline uint32_t
+efi_crc32(const void *buf, unsigned long len)
+{
+ return (crc32(buf, len, ~0L) ^ ~0L);
+}
+
+
#endif /* _CRC32_H */
// vim:fenc=utf-8:tw=75:noet
diff --git a/src/efivar.c b/src/efivar.c
index 7f16ab15bab..885a9af864b 100644
--- a/src/efivar.c
+++ b/src/efivar.c
@@ -46,6 +46,8 @@ extern int optind, opterr, optopt;
#define ACTION_LIST_GUIDS 0x08
#define ACTION_WRITE 0x10
#define ACTION_PRINT_DEC 0x20
+#define ACTION_IMPORT 0x40
+#define ACTION_EXPORT 0x80
#define EDIT_APPEND 0
#define EDIT_WRITE 1
@@ -173,31 +175,10 @@ bad_name:
}
static void
-show_variable(char *guid_name, int display_type)
+show_variable_data(efi_guid_t guid, const char *name, uint32_t attributes,
+ uint8_t *data, size_t data_size,
+ int display_type)
{
- efi_guid_t guid = efi_guid_empty;
- char *name = NULL;
- int rc;
-
- uint8_t *data = NULL;
- size_t data_size = 0;
- uint32_t attributes;
-
- parse_name(guid_name, &name, &guid);
- if (!name || efi_guid_is_empty(&guid)) {
- fprintf(stderr, "efivar: could not parse variable name.\n");
- show_errors();
- exit(1);
- }
-
- errno = 0;
- rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
- if (rc < 0) {
- fprintf(stderr, "efivar: show variable: %m\n");
- show_errors();
- exit(1);
- }
-
if (display_type == SHOW_VERBOSE) {
printf("GUID: "GUID_FORMAT "\n",
guid.a, guid.b, guid.c, bswap_16(guid.d),
@@ -257,6 +238,117 @@ show_variable(char *guid_name, int display_type)
}
printf("\n");
}
+}
+
+static void
+show_variable(char *guid_name, int display_type)
+{
+ efi_guid_t guid = efi_guid_empty;
+ char *name = NULL;
+ int rc;
+
+ uint8_t *data = NULL;
+ size_t data_size = 0;
+ uint32_t attributes;
+
+ parse_name(guid_name, &name, &guid);
+ if (!name || efi_guid_is_empty(&guid)) {
+ fprintf(stderr, "efivar: could not parse variable name.\n");
+ show_errors();
+ exit(1);
+ }
+
+ errno = 0;
+ rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
+ if (rc < 0) {
+ fprintf(stderr, "efivar: show variable: %m\n");
+ show_errors();
+ exit(1);
+ }
+
+ show_variable_data(guid, name, attributes,
+ data, data_size, display_type);
+
+ free(name);
+ if (data)
+ free(data);
+}
+
+static void
+save_variable_data(efi_variable_t *var, char *outfile, bool dmpstore)
+{
+ FILE *out = NULL;
+ ssize_t sz;
+ uint8_t *data = NULL;
+ size_t datasz = 0;
+ ssize_t (*export)(efi_variable_t *var, uint8_t *data, size_t size) =
+ dmpstore ? efi_variable_export_dmpstore : efi_variable_export;
+
+ out = fopen(outfile, "w");
+ if (!out)
+ err(1, "Could not open \"%s\" for writing", outfile);
+
+ sz = export(var, data, datasz);
+ data = calloc(sz, 1);
+ if (!data)
+ err(1, "Could not allocate memory");
+ datasz = sz;
+
+ sz = export(var, data, datasz);
+ if (sz < 0)
+ err(1, "Could not format data");
+ datasz = sz;
+
+ sz = fwrite(data, 1, datasz, out);
+ if (sz < (ssize_t)datasz)
+ err(1, "Could not write to \"%s\"", outfile);
+
+ fflush(out);
+ fclose(out);
+}
+
+static void
+save_variable(char *guid_name, char *outfile, bool dmpstore)
+{
+ efi_guid_t guid = efi_guid_empty;
+ char *name = NULL;
+ int rc;
+
+ uint8_t *data = NULL;
+ size_t data_size = 0;
+ uint32_t attributes = 7;
+ efi_variable_t *var;
+
+ parse_name(guid_name, &name, &guid);
+ if (!name || efi_guid_is_empty(&guid)) {
+ fprintf(stderr, "efivar: could not parse variable name.\n");
+ show_errors();
+ exit(1);
+ }
+
+ errno = 0;
+ rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
+ if (rc < 0) {
+ fprintf(stderr, "efivar: show variable: %m\n");
+ show_errors();
+ exit(1);
+ }
+
+ var = efi_variable_alloc();
+ if (!var) {
+ fprintf(stderr, "efivar: could not allocate variable storage.\n");
+ show_errors();
+ exit(1);
+ }
+
+ efi_variable_set_name(var, (unsigned char *)name);
+ efi_variable_set_guid(var, &guid);
+ efi_variable_set_attributes(var, attributes);
+ efi_variable_set_data(var, data, data_size);
+
+ save_variable_data(var, outfile, dmpstore);
+
+ efi_variable_free(var, false);
free(name);
if (data)
@@ -372,16 +464,18 @@ usage(int ret)
FILE *out = ret == 0 ? stdout : stderr;
fprintf(out,
"Usage: %s [OPTION...]\n"
+ " -t, --attributes=<attributes> attributes to use on append\n"
" -l, --list list current variables\n"
" -p, --print print variable specified by --name\n"
+ " -D, --dmpstore use DMPSTORE format when exporting\n"
" -d, --print-decimal print variable in decimal values specified\n"
" by --name\n"
" -n, --name=<guid-name> variable to manipulate, in the form\n"
" 8be4df61-93ca-11d2-aa0d-00e098032b8c-Boot0000\n"
" -a, --append append to variable specified by --name\n"
+ " -f, --datafile=<file> load or save variable contents from <file>\n"
" -e, --export=<file> export variable to <file>\n"
- " -f, --fromfile=<file> use data from <file>\n"
- " -t, --attributes=<attributes> attributes to use on append\n"
+ " -i, --import=<file> import variable from <file\n"
" -L, --list-guids show internal guid list\n"
" -w, --write write to variable specified by --name\n\n"
"Help options:\n"
@@ -398,55 +492,79 @@ int main(int argc, char *argv[])
int action = 0;
uint8_t *data = NULL;
size_t data_size = 0;
- char *name = NULL;
- char *file = NULL;
- uint32_t attributes = 0;
- char *sopts = "lpdn:af:t:Lw?";
- struct option lopts[] =
- { {"list", no_argument, 0, 'l'},
- {"print", no_argument, 0, 'p'},
- {"print-decimal", no_argument, 0, 'd'},
- {"name", required_argument, 0, 'n'},
- {"append", no_argument, 0, 'a'},
- {"fromfile", required_argument, 0, 'f'},
- {"attributes", required_argument, 0, 't'},
- {"list-guids", no_argument, 0, 'L'},
- {"write", no_argument, 0, 'w'},
- {"help", no_argument, 0, '?'},
- {"usage", no_argument, 0, 0},
- {0, 0, 0, 0}
- };
+ char *guid_name = NULL;
+ char *infile = NULL;
+ char *outfile = NULL;
+ char *datafile = NULL;
+ bool dmpstore = false;
+ int verbose = 0;
+ uint32_t attributes = EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS;
+ char *sopts = "aA:Dde:f:i:Llpn:vw?";
+ struct option lopts[] = {
+ {"append", no_argument, 0, 'a'},
+ {"attributes", required_argument, 0, 'A'},
+ {"datafile", required_argument, 0, 'f'},
+ {"dmpstore", no_argument, 0, 'D'},
+ {"export", required_argument, 0, 'e'},
+ {"help", no_argument, 0, '?'},
+ {"import", required_argument, 0, 'i'},
+ {"list", no_argument, 0, 'l'},
+ {"list-guids", no_argument, 0, 'L'},
+ {"name", required_argument, 0, 'n'},
+ {"print", no_argument, 0, 'p'},
+ {"print-decimal", no_argument, 0, 'd'},
+ {"usage", no_argument, 0, 0},
+ {"verbose", no_argument, 0, 'v'},
+ {"write", no_argument, 0, 'w'},
+ {0, 0, 0, 0}
+ };
while ((c = getopt_long(argc, argv, sopts, lopts, &i)) != -1) {
switch (c) {
- case 'l':
- action |= ACTION_LIST;
+ case 'A':
+ attributes = strtoul(optarg, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL)
+ err(1,
+ "invalid argument for -t: %s: %m\n",
+ optarg);
break;
- case 'p':
- action |= ACTION_PRINT;
+ case 'a':
+ action |= ACTION_APPEND;
+ break;
+ case 'D':
+ dmpstore = true;
break;
case 'd':
action |= ACTION_PRINT_DEC;
break;
- case 'n':
- name = optarg;
- break;
- case 'a':
- action |= ACTION_APPEND;
+ case 'e':
+ action |= ACTION_EXPORT;
+ outfile = optarg;
break;
case 'f':
- file = optarg;
+ datafile = optarg;
break;
- case 't':
- attributes = strtoul(optarg, NULL, 10);
- if (errno == ERANGE || errno == EINVAL)
- err(1,
- "invalid argument for -t: %s: %m\n",
- optarg);
+ case 'i':
+ action |= ACTION_IMPORT;
+ infile = optarg;
break;
case 'L':
action |= ACTION_LIST_GUIDS;
break;
+ case 'l':
+ action |= ACTION_LIST;
+ break;
+ case 'n':
+ guid_name = optarg;
+ break;
+ case 'p':
+ action |= ACTION_PRINT;
+ break;
+ case 'v':
+ verbose += 1;
+ break;
case 'w':
action |= ACTION_WRITE;
break;
@@ -460,7 +578,9 @@ int main(int argc, char *argv[])
}
}
- if (name)
+ efi_set_verbose(verbose, stderr);
+
+ if (guid_name && !outfile)
action |= ACTION_PRINT;
switch (action) {
@@ -468,23 +588,23 @@ int main(int argc, char *argv[])
list_all_variables();
break;
case ACTION_PRINT:
- validate_name(name);
- show_variable(name, SHOW_VERBOSE);
+ validate_name(guid_name);
+ show_variable(guid_name, SHOW_VERBOSE);
break;
case ACTION_PRINT_DEC | ACTION_PRINT:
- validate_name(name);
- show_variable(name, SHOW_DECIMAL);
+ validate_name(guid_name);
+ show_variable(guid_name, SHOW_DECIMAL);
break;
case ACTION_APPEND | ACTION_PRINT:
- validate_name(name);
- prepare_data(file, &data, &data_size);
- edit_variable(name, data, data_size, attributes,
+ validate_name(guid_name);
+ prepare_data(infile, &data, &data_size);
+ edit_variable(guid_name, data, data_size, attributes,
EDIT_APPEND);
break;
case ACTION_WRITE | ACTION_PRINT:
- validate_name(name);
- prepare_data(file, &data, &data_size);
- edit_variable(name, data, data_size, attributes,
+ validate_name(guid_name);
+ prepare_data(infile, &data, &data_size);
+ edit_variable(guid_name, data, data_size, attributes,
EDIT_WRITE);
break;
case ACTION_LIST_GUIDS: {
@@ -509,10 +629,108 @@ int main(int argc, char *argv[])
guid[i].symbol + strlen("efi_guid_"),
guid[i].symbol, guid[i].name);
}
+ break;
+ }
+ case ACTION_EXPORT:
+ if (datafile) {
+ char *name = NULL;
+ efi_guid_t guid = efi_guid_zero;
+ efi_variable_t *var;
+
+ parse_name(guid_name, &name, &guid);
+ prepare_data(datafile, &data, &data_size);
+
+ var = efi_variable_alloc();
+ if (!var)
+ err(1, "Could not allocate memory");
+
+ efi_variable_set_name(var, (unsigned char *)name);
+ efi_variable_set_guid(var, &guid);
+ efi_variable_set_attributes(var, attributes);
+ efi_variable_set_data(var, data, data_size);
+
+ save_variable_data(var, outfile, dmpstore);
+
+ efi_variable_free(var, false);
+ } else {
+ validate_name(guid_name);
+ save_variable(guid_name, outfile, dmpstore);
+ }
+ break;
+ case ACTION_IMPORT:
+ case ACTION_IMPORT | ACTION_PRINT:
+ case ACTION_IMPORT | ACTION_PRINT | ACTION_PRINT_DEC:
+ {
+ ssize_t sz;
+ efi_variable_t *var = NULL;
+ char *name;
+ efi_guid_t *guid;
+ uint64_t attributes;
+ int display_type = (action & ACTION_PRINT_DEC)
+ ? SHOW_VERBOSE|SHOW_DECIMAL
+ : SHOW_VERBOSE;
+
+
+ prepare_data(infile, &data, &data_size);
+ sz = efi_variable_import(data, data_size, &var);
+ if (sz < 0)
+ err(1, "Could not import data from \"%s\"", infile);
+
+ free(data);
+ data = NULL;
+ data_size = 0;
+
+ name = (char *)efi_variable_get_name(var);
+ efi_variable_get_guid(var, &guid);
+ efi_variable_get_attributes(var, &attributes);
+ efi_variable_get_data(var, &data, &data_size);
+
+ if (datafile) {
+ FILE *out;
+ int rc;
+
+ out = fopen(datafile, "w");
+ if (!out)
+ err(1, "Could not open \"%s\" for writing",
+ datafile);
+
+ rc = fwrite(data, data_size, 1, out);
+ if (rc < (long)data_size)
+ err(1, "Could not write to \"%s\"",
+ datafile);
+
+ fclose(out);
+ free(guid_name);
+ }
+ if (action & ACTION_PRINT)
+ show_variable_data(*guid, name,
+ ((uint32_t)(attributes & 0xffffffff)),
+ data, data_size, display_type);
+
+ efi_variable_free(var, false);
+ break;
+ }
+ case ACTION_IMPORT | ACTION_EXPORT:
+ {
+ efi_variable_t *var = NULL;
+ ssize_t sz;
+
+ if (datafile)
+ errx(1, "--datafile cannot be used with --import and --export");
+
+ prepare_data(infile, &data, &data_size);
+ sz = efi_variable_import(data, data_size, &var);
+ if (sz < 0)
+ err(1, "Could not import data from \"%s\"", infile);
+
+ save_variable_data(var, outfile, dmpstore);
+
+ efi_variable_free(var, false);
+ break;
+ }
case ACTION_USAGE:
default:
usage(EXIT_FAILURE);
- }
};
return 0;
diff --git a/src/export.c b/src/export.c
index 6b78412cce1..cfb021525ff 100644
--- a/src/export.c
+++ b/src/export.c
@@ -27,7 +27,7 @@
#include "efivar.h"
-#define EFIVAR_MAGIC 0xf3df1597
+#define EFIVAR_MAGIC 0xf3df1597u
#define ATTRS_UNSET 0xa5a5a5a5a5a5a5a5
#define ATTRS_MASK 0xffffffff
@@ -50,12 +50,166 @@ struct efi_variable {
* uint32_t data_len;
* char16_t name[];
* uint8_t data[];
- * uint32_t magic;
+ * uint32_t crc32;
+ * }
+ *
+ * Unfortunately the exported structure from dmpstore is:
+ * struct {
+ * uint32_t name_size; // in bytes
+ * uint32_t data_size; // in bytes
+ * char16_t name[];
+ * efi_guid_t guid;
+ * uint32_t attr;
+ * unit8_t data[];
+ * uint32_t crc32;
* }
*/
-ssize_t NONNULL(1, 3) PUBLIC
-efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
+#ifdef EFIVAR_BUILD_ENVIRONMENT
+#error wtf
+#endif
+
+ssize_t NONNULL(1, 3)
+efi_variable_import_dmpstore(uint8_t *data, size_t size,
+ efi_variable_t **var_out)
+{
+ efi_variable_t var;
+ uint32_t namesz;
+ uint32_t datasz;
+ size_t min = sizeof (uint32_t) /* name size */
+ + sizeof (uint32_t) /* data size */
+ + sizeof (char16_t) /* two bytes of name */
+ + sizeof (efi_guid_t) /* guid */
+ + sizeof (uint32_t) /* attr */
+ + 1 /* one byte of data */
+ + sizeof (uint32_t); /* crc32 */
+ size_t sz = sizeof (uint32_t) /* name size */
+ + sizeof (uint32_t) /* data size */
+ + sizeof (efi_guid_t) /* guid */
+ + sizeof (uint32_t) /* attr */
+ + sizeof (uint32_t); /* crc32 */
+ uint8_t *ptr = data;
+ uint32_t crc;
+ int saved_errno;
+
+ if (size <= min) {
+etoosmall:
+ errno = EINVAL;
+ efi_error("data size is too small for dmpstore variable (%zu < %zu)",
+ size, min);
+ return -1;
+ }
+
+ memset(&var, 0, sizeof(var));
+
+ namesz = *(uint32_t *)ptr;
+ debug("namesz:%"PRIu32, namesz);
+ ptr += sizeof(uint32_t);
+
+ if (namesz <= 2) {
+ errno = EINVAL;
+ debug("name size (%"PRIu32") must be greater than 2", namesz);
+ return -1;
+ }
+
+ if (namesz % 2 != 0) {
+ errno = EINVAL;
+ efi_error("name size (%"PRIu32") cannot be odd", namesz);
+ return -1;
+ }
+
+ datasz = *(uint32_t *)ptr;
+ ptr += sizeof(uint32_t);
+ debug("datasz:%"PRIu32, datasz);
+
+ if (datasz == 0) {
+ errno = EINVAL;
+ efi_error("data size (%"PRIu32") must be nonzero", datasz);
+ return -1;
+ }
+
+ if (add(sz, namesz, &sz)) {
+overflow:
+ errno = EOVERFLOW;
+ efi_error("arithmetic overflow computing allocation size");
+ return -1;
+ }
+
+ if (add(sz, datasz, &min))
+ goto overflow;
+
+ if (size < min)
+ goto etoosmall;
+ size = min;
+
+ if (!(ptr[namesz - 1] == 0 && ptr[namesz -2] == 0)) {
+ errno = EINVAL;
+ efi_error("variable name is not properly terminated.");
+ return -1;
+ }
+
+ crc = efi_crc32(data, size - sizeof(uint32_t));
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32", expected 0x%"PRIx32,
+ data, size - sizeof(uint32_t), crc,
+ *(uint32_t*)(data + size - sizeof(uint32_t)));
+
+ if (memcmp(data + size - sizeof(uint32_t),
+ &crc, sizeof(uint32_t))) {
+ errno = EINVAL;
+ efi_error("crc32 did not match");
+ return -1;
+ }
+
+ var.name = ucs2_to_utf8(ptr, -1);
+ if (!var.name)
+ goto oom;
+ ptr += namesz;
+
+ var.guid = malloc(sizeof (efi_guid_t));
+ if (!var.guid)
+ goto oom;
+ memcpy(var.guid, ptr, sizeof (efi_guid_t));
+ ptr += sizeof (efi_guid_t);
+
+ var.attrs = *(uint32_t *)ptr;
+ ptr += sizeof(uint32_t);
+
+ var.data_size = datasz;
+ var.data = malloc(datasz);
+ if (!var.data) {
+ efi_error("Could not allocate %"PRIu32" bytes", datasz);
+ goto oom;
+ }
+ memcpy(var.data, ptr, datasz);
+
+ if (!*var_out) {
+ *var_out =malloc(sizeof (var));
+ if (!*var_out)
+ goto oom;
+ memcpy(*var_out, &var, sizeof (var));
+ } else {
+ return -1;
+ }
+ return size;
+oom:
+ saved_errno = errno;
+
+ if (var.guid)
+ free(var.guid);
+
+ if (var.name)
+ free(var.name);
+
+ if (var.data)
+ free(var.data);
+
+ errno = saved_errno;
+ efi_error("Could not allocate memory");
+ return -1;
+}
+
+ssize_t NONNULL(1, 3)
+efi_variable_import_efivar(uint8_t *data, size_t datasz, efi_variable_t **var_out)
{
efi_variable_t var;
size_t min = sizeof (uint32_t) * 2 /* magic */
@@ -63,47 +217,83 @@ efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
+ sizeof (uint64_t) /* attr */
+ sizeof (efi_guid_t) /* guid */
+ sizeof (uint32_t) * 2 /* name_len and data_len */
- + sizeof (char16_t) /* two bytes of name */
- + 1; /* one byte of data */
+ + sizeof (char16_t) /* two bytes of name */
+ + 1 /* one byte of data */
+ + 4; /* crc32 */
+ uint32_t crc;
+ uint8_t *ptr = data;
+ uint32_t magic = EFIVAR_MAGIC;
+ int test;
+
errno = EINVAL;
- if (size <= min)
+ if (datasz <= min)
return -1;
- uint8_t *ptr = data;
- uint32_t magic = EFIVAR_MAGIC;
- if (memcmp(data, &magic, sizeof (uint32_t)) ||
- memcmp(data + size - sizeof (uint32_t), &magic,
- sizeof (uint32_t)))
+ test = memcmp(data, &magic, sizeof (uint32_t));
+ debug("test magic 0: cmp(0x%04x,0x%04x)->%d", *(uint32_t *)data, magic, test);
+ if (test) {
+ errno = EINVAL;
+ efi_error("MAGIC for file format did not match.");
return -1;
- size -= sizeof (uint32_t);
+ }
+
ptr += sizeof (uint32_t);
+ debug("test version");
if (*(uint32_t *)ptr == 1) {
ptr += sizeof (uint32_t);
+ debug("version 1");
+
var.attrs = *(uint64_t *)ptr;
- ptr += sizeof (uint32_t);
+ ptr += sizeof (uint64_t);
+ debug("var.attrs:0x%08"PRIx64, var.attrs);
var.guid = malloc(sizeof (efi_guid_t));
if (!var.guid)
return -1;
*var.guid = *(efi_guid_t *)ptr;
ptr += sizeof (efi_guid_t);
+ debug("var.guid:"GUID_FORMAT,
+ var.guid->a, var.guid->b, var.guid->c,
+ bswap_16(var.guid->d),
+ var.guid->e[0], var.guid->e[1], var.guid->e[2],
+ var.guid->e[3], var.guid->e[4], var.guid->e[5]);
uint32_t name_len = *(uint32_t *)ptr;
ptr += sizeof (uint32_t);
+ debug("name_len:%"PRIu32, name_len);
+
uint32_t data_len = *(uint32_t *)ptr;
ptr += sizeof (uint32_t);
+ debug("data_len:%"PRIu32, data_len);
+
+ min -= 3;
+ min += name_len;
+ min += data_len;
- if (name_len < 1 ||
- name_len != ((data + size) - ptr - data_len) ||
+ if (name_len < 2 ||
+ name_len > (datasz - data_len) ||
data_len < 1 ||
- data_len != ((data + size) - ptr - name_len)) {
+ data_len > (datasz - name_len)) {
int saved_errno = errno;
free(var.guid);
errno = saved_errno;
return -1;
}
+ crc = efi_crc32(data, datasz - sizeof(uint32_t));
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32", expected 0x%"PRIx32,
+ data, datasz - sizeof(uint32_t), crc,
+ *(uint32_t*)(data + datasz - sizeof(uint32_t)));
+
+ if (memcmp(data + datasz - sizeof (uint32_t), &crc,
+ sizeof (uint32_t))) {
+ free(var.guid);
+ errno = EINVAL;
+ efi_error("crc32 did not match");
+ return -1;
+ }
+
var.name = calloc(1, name_len + 1);
if (!var.name) {
int saved_errno = errno;
@@ -115,7 +305,8 @@ efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
char16_t *wname = (char16_t *)ptr;
for (uint32_t i = 0; i < name_len; i++)
var.name[i] = wname[i] & 0xff;
- ptr += name_len * 2;
+ ptr += name_len;
+ debug("name:%s", var.name);
var.data_size = data_len;
var.data = malloc(data_len);
@@ -143,31 +334,228 @@ efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
} else {
return -1;
}
- return size;
+ return min;
+}
+
+ssize_t NONNULL(1, 3) PUBLIC
+efi_variable_import(uint8_t *data, size_t size, efi_variable_t **var_out)
+{
+ ssize_t rc;
+
+ rc = efi_variable_import_efivar(data, size, var_out);
+ if (rc >= 0)
+ return rc;
+
+ rc = efi_variable_import_dmpstore(data, size, var_out);
+ return rc;
}
ssize_t NONNULL(1) PUBLIC
-efi_variable_export(efi_variable_t *var, uint8_t *data, size_t size)
+efi_variable_export_dmpstore(efi_variable_t *var, uint8_t *data, size_t datasz)
{
- size_t name_len = strlen((char *)var->name);
-
- size_t needed = sizeof (uint32_t) /* magic */
- + sizeof (uint32_t) /* version */
- + sizeof (uint64_t) /* attr */
- + sizeof (efi_guid_t) /* guid */
- + sizeof (uint32_t) /* name_len */
- + sizeof (uint32_t) /* data_len */
- + sizeof (char16_t) * name_len /* name */
- + var->data_size /* data */
- + sizeof (uint32_t); /* magic again */
-
- if (!data || size == 0) {
+ uint32_t tmpu32;
+ ssize_t tmpssz;
+ uint32_t namesz;
+ uint32_t needed = sizeof (uint32_t) /* name_size */
+ + sizeof (uint32_t) /* data_size */
+ + 2 /* name */
+ + sizeof (efi_guid_t) /* guid */
+ + sizeof (uint32_t) /* attrs */
+ + 1 /* data */
+ + 4; /* crc32 */
+ uint8_t *ptr;
+ uint32_t crc;
+
+ if (!var) {
+ errno = EINVAL;
+ efi_error("var cannot be NULL");
+ return -1;
+ }
+ if (!var->name) {
+ errno = EINVAL;
+ efi_error("var->name cannot be NULL");
+ return -1;
+ }
+ if (!var->data) {
+ errno = EINVAL;
+ efi_error("var->data cannot be NULL");
+ return -1;
+ }
+
+ debug("data: %p datasz: %zu", data, datasz);
+
+ namesz = utf8size(var->name, -1);
+ debug("sizeof(uint16_t):%zd * namesz:%"PRIu32, sizeof(uint16_t), namesz);
+ if (mul(sizeof (uint16_t), namesz, &namesz)) {
+overflow:
+ errno = EOVERFLOW;
+ efi_error("arithmetic overflow computing name size");
+ return -1;
+ }
+ debug("namesz -> %"PRIu32, namesz);
+
+ /*
+ * Remove our stand-ins for name size and data size before we add
+ * them back in.
+ */
+ needed -= 3;
+
+ debug("needed:%"PRIu32" + namesz:%"PRIu32, needed, namesz);
+ if (add(needed, namesz, &needed))
+ goto overflow;
+ debug("needed -> %"PRIu32, needed);
+
+ debug("needed:%"PRIu32" + var->data_size:%zd", needed, var->data_size);
+ if (add(needed, var->data_size, &needed))
+ goto overflow;
+ debug("needed -> %"PRIu32, needed);
+
+ if (!data || datasz == 0) {
+ debug("data: %p datasz: %zd -> returning needed size %"PRIu32,
+ data, datasz, needed);
return needed;
- } else if (size < needed) {
- return needed - size;
}
- uint8_t *ptr = data;
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
+ if (datasz < needed) {
+ efi_error("needed: %"PRIu32" datasz: %zd -> returning needed datasz %zu",
+ needed, datasz, needed - datasz);
+ return needed - datasz;
+ }
+
+ ptr = data;
+
+ tmpssz = utf8_to_ucs2(ptr + 8, datasz - 8, true, var->name);
+ if (tmpssz < 0) {
+ efi_error("UTF-8 to UCS-2 conversion failed");
+ return -1;
+ }
+ tmpu32 = tmpssz;
+ tmpu32 *= sizeof(uint16_t);
+
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
+ if (sub(namesz, tmpu32, &tmpu32))
+ goto overflow;
+ debug("tmpu32 -> %"PRIu32, tmpu32);
+
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
+ if (sub(namesz, tmpu32, &namesz))
+ goto overflow;
+ debug("namesz -> %"PRIu32, namesz);
+
+ debug("needed:%"PRIu32" - tmpu32:%"PRIu32, needed, tmpu32);
+ if (sub(needed, tmpu32, &needed))
+ goto overflow;
+ debug("needed -> %"PRIu32, needed);
+
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
+ if (datasz < needed) {
+ debug("needed: %"PRIu32" datasz: %zd -> returning needed datasz %"PRIu32,
+ needed, datasz, needed);
+ return needed;
+ }
+
+ *(uint32_t *)ptr = namesz;
+ ptr += sizeof (uint32_t);
+
+ *(uint32_t *)ptr = var->data_size;
+ ptr += sizeof (uint32_t);
+
+ ptr += namesz;
+
+ memcpy(ptr, var->guid, sizeof (efi_guid_t));
+ ptr += sizeof(efi_guid_t);
+
+ *(uint32_t *)ptr = var->attrs;
+ ptr += sizeof (uint32_t);
+
+ memcpy(ptr, var->data, var->data_size);
+ ptr += var->data_size;
+
+ crc = efi_crc32(data, needed - sizeof(uint32_t));
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32,
+ data, needed - sizeof(uint32_t), crc);
+ *(uint32_t *)ptr = crc;
+
+ return needed;
+}
+
+ssize_t NONNULL(1) PUBLIC
+efi_variable_export(efi_variable_t *var, uint8_t *data, size_t datasz)
+{
+ uint32_t tmpu32;
+ ssize_t tmpssz;
+ uint32_t namesz;
+ uint32_t needed = sizeof (uint32_t) /* magic */
+ + sizeof (uint32_t) /* version */
+ + sizeof (uint64_t) /* attr */
+ + sizeof (efi_guid_t) /* guid */
+ + sizeof (uint32_t) /* name_len */
+ + sizeof (uint32_t) /* data_len */
+ + 2 /* name */
+ + 1 /* data */
+ + 4; /* crc32 */
+ uint8_t *ptr;
+ uint32_t crc;
+
+ if (!var) {
+ errno = EINVAL;
+ efi_error("var cannot be NULL");
+ return -1;
+ }
+ if (!var->name) {
+ errno = EINVAL;
+ efi_error("var->name cannot be NULL");
+ return -1;
+ }
+ if (!var->data) {
+ errno = EINVAL;
+ efi_error("var->data cannot be NULL");
+ return -1;
+ }
+
+ debug("data: %p datasz: %zu", data, datasz);
+
+ namesz = utf8size(var->name, -1);
+ debug("sizeof(uint16_t):%zd * namesz:%"PRIu32, sizeof(uint16_t), namesz);
+ if (mul(sizeof (uint16_t), namesz, &namesz)) {
+overflow:
+ errno = EOVERFLOW;
+ efi_error("arithmetic overflow computing name size");
+ return -1;
+ }
+ debug("namesz -> %"PRIu32, namesz);
+
+ /*
+ * Remove our stand-ins for name size and data size before we add
+ * them back in.
+ */
+ needed -= 3;
+
+ debug("needed:%"PRIu32" + namesz:%"PRIu32, needed, namesz);
+ if (add(needed, namesz, &needed))
+ goto overflow;
+ debug("needed -> %"PRIu32, needed);
+
+ debug("needed:%"PRIu32" + var->data_size:%zd", needed, var->data_size);
+ if (add(needed, var->data_size, &needed))
+ goto overflow;
+ debug("needed -> %"PRIu32, needed);
+
+ if (!data || datasz == 0) {
+ debug("data: %p datasz: %zd -> returning needed datasz %"PRIu32,
+ data, datasz, needed);
+ return needed;
+ }
+
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
+ if (datasz < needed) {
+ efi_error("needed: %"PRIu32" datasz: %zd -> returning needed datasz %zd",
+ needed, datasz, needed - datasz);
+ return needed - datasz;
+ }
+
+ ptr = data;
*(uint32_t *)ptr = EFIVAR_MAGIC;
ptr += sizeof (uint32_t);
@@ -181,21 +569,51 @@ efi_variable_export(efi_variable_t *var, uint8_t *data, size_t size)
memcpy(ptr, var->guid, sizeof (efi_guid_t));
ptr += sizeof (efi_guid_t);
- *(uint32_t *)ptr = (uint32_t)(sizeof (char16_t) * name_len);
+ tmpssz = utf8_to_ucs2(ptr + 8, datasz - 8, true, var->name);
+ if (tmpssz < 0) {
+ efi_error("UTF-8 to UCS-2 conversion failed");
+ return -1;
+ }
+ tmpu32 = tmpssz;
+ tmpu32 *= sizeof(uint16_t);
+
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
+ if (sub(namesz, tmpu32, &tmpu32))
+ goto overflow;
+ debug("tmpu32 -> %"PRIu32, tmpu32);
+
+ debug("needed:%"PRIu32" - tmpu32:%"PRIu32, needed, tmpu32);
+ if (sub(needed, tmpu32, &needed))
+ goto overflow;
+ debug("needed -> %"PRIu32, needed);
+
+ debug("namesz:%"PRIu32" - tmpu32:%"PRIu32, namesz, tmpu32);
+ if (sub(namesz, tmpu32, &namesz))
+ goto overflow;
+ debug("namesz -> %"PRIu32, namesz);
+
+ debug("datasz:%zu needed: %"PRIu32, datasz, needed);
+ if (datasz < needed) {
+ efi_error("needed: %"PRIu32" datasz: %zd -> returning needed datasz %zd",
+ needed, datasz, needed - datasz);
+ return needed - datasz;
+ }
+
+ *(uint32_t *)ptr = namesz;
ptr += sizeof (uint32_t);
*(uint32_t *)ptr = var->data_size;
ptr += sizeof (uint32_t);
- for (uint32_t i = 0; i < name_len; i++) {
- *(char16_t *)ptr = var->name[i];
- ptr += sizeof (char16_t);
- }
+ ptr += namesz;
memcpy(ptr, var->data, var->data_size);
ptr += var->data_size;
- *(uint32_t *)ptr = EFIVAR_MAGIC;
+ crc = efi_crc32(data, needed - sizeof(uint32_t));
+ debug("efi_crc32(%p, %lu) -> 0x%"PRIx32,
+ data, needed - sizeof(uint32_t), crc);
+ *(uint32_t *)ptr = crc;
return needed;
}
diff --git a/src/gpt.c b/src/gpt.c
index aa4055b9812..8babafeb588 100644
--- a/src/gpt.c
+++ b/src/gpt.c
@@ -48,24 +48,6 @@ struct blkdev_ioctl_param {
char * block_contents;
};
-/**
- * efi_crc32() - EFI version of crc32 function
- * @buf: buffer to calculate crc32 of
- * @len - length of buf
- *
- * Description: Returns EFI-style CRC32 value for @buf
- *
- * This function uses the little endian Ethernet polynomial
- * but seeds the function with ~0, and xor's with ~0 at the end.
- * Note, the EFI Specification, v1.02, has a reference to
- * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
- */
-static inline uint32_t
-efi_crc32(const void *buf, unsigned long len)
-{
- return (crc32(buf, len, ~0L) ^ ~0L);
-}
-
/**
* is_pmbr_valid(): test Protective MBR for validity
* @mbr: pointer to a legacy mbr structure
diff --git a/src/include/efivar/efivar.h b/src/include/efivar/efivar.h
index 729b6fe80f7..8ad14b9be57 100644
--- a/src/include/efivar/efivar.h
+++ b/src/include/efivar/efivar.h
@@ -139,6 +139,9 @@ extern ssize_t efi_variable_import(uint8_t *data, size_t size,
extern ssize_t efi_variable_export(efi_variable_t *var, uint8_t *data,
size_t size)
__attribute__((__nonnull__ (1)));
+extern ssize_t efi_variable_export_dmpstore(efi_variable_t *var, uint8_t *data,
+ size_t size)
+ __attribute__((__nonnull__ (1)));
extern efi_variable_t *efi_variable_alloc(void)
__attribute__((__visibility__ ("default")));
--
2.26.2