diff --git a/backport-disk-Implement-support-for-LUKS2.patch b/backport-disk-Implement-support-for-LUKS2.patch new file mode 100644 index 0000000000000000000000000000000000000000..af752cd1115aefb5d13a38ce4990e5fd9d94d4da --- /dev/null +++ b/backport-disk-Implement-support-for-LUKS2.patch @@ -0,0 +1,790 @@ +From 365e0cc3e7e44151c14dd29514c2f870b49f9755 Mon Sep 17 00:00:00 2001 +From: Patrick Steinhardt +Date: Fri, 27 Dec 2019 16:18:39 +0100 +Subject: [PATCH 0001/1108] disk: Implement support for LUKS2 + +With cryptsetup 2.0, a new version of LUKS was introduced that breaks +compatibility with the previous version due to various reasons. GRUB +currently lacks any support for LUKS2, making it impossible to decrypt +disks encrypted with that version. This commit implements support for +this new format. + +Note that LUKS1 and LUKS2 are quite different data formats. While they +do share the same disk signature in the first few bytes, representation +of encryption parameters is completely different between both versions. +While the former version one relied on a single binary header, only, +LUKS2 uses the binary header only in order to locate the actual metadata +which is encoded in JSON. Furthermore, the new data format is a lot more +complex to allow for more flexible setups, like e.g. having multiple +encrypted segments and other features that weren't previously possible. +Because of this, it was decided that it doesn't make sense to keep both +LUKS1 and LUKS2 support in the same module and instead to implement it +in two different modules luks and luks2. + +The proposed support for LUKS2 is able to make use of the metadata to +decrypt such disks. Note though that in the current version, only the +PBKDF2 key derival function is supported. This can mostly attributed to +the fact that the libgcrypt library currently has no support for either +Argon2i or Argon2id, which are the remaining KDFs supported by LUKS2. It +wouldn't have been much of a problem to bundle those algorithms with +GRUB itself, but it was decided against that in order to keep down the +number of patches required for initial LUKS2 support. Adding it in the +future would be trivial, given that the code structure is already in +place. + +Signed-off-by: Patrick Steinhardt +Reviewed-by: Daniel Kiper +--- + Makefile.util.def | 4 +- + docs/grub.texi | 5 +- + grub-core/Makefile.core.def | 8 + + grub-core/disk/luks2.c | 678 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 692 insertions(+), 3 deletions(-) + create mode 100644 grub-core/disk/luks2.c + +diff --git a/Makefile.util.def b/Makefile.util.def +index 969d32f00..94336392b 100644 +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -3,7 +3,7 @@ AutoGen definitions Makefile.tpl; + library = { + name = libgrubkern.a; + cflags = '$(CFLAGS_GNULIB)'; +- cppflags = '$(CPPFLAGS_GNULIB)'; ++ cppflags = '$(CPPFLAGS_GNULIB) -I$(srcdir)/grub-core/lib/json'; + + common = util/misc.c; + common = grub-core/kern/command.c; +@@ -36,7 +36,9 @@ library = { + common = grub-core/kern/misc.c; + common = grub-core/kern/partition.c; + common = grub-core/lib/crypto.c; ++ common = grub-core/lib/json/json.c; + common = grub-core/disk/luks.c; ++ common = grub-core/disk/luks2.c; + common = grub-core/disk/geli.c; + common = grub-core/disk/cryptodisk.c; + common = grub-core/disk/AFSplitter.c; +diff --git a/docs/grub.texi b/docs/grub.texi +index 08d699d49..1b25466c7 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -4211,8 +4211,9 @@ is requested interactively. Option @var{device} configures specific grub device + with specified @var{uuid}; option @option{-a} configures all detected encrypted + devices; option @option{-b} configures all geli containers that have boot flag set. + +-GRUB suports devices encrypted using LUKS and geli. Note that necessary modules (@var{luks} and @var{geli}) have to be loaded manually before this command can +-be used. ++GRUB suports devices encrypted using LUKS, LUKS2 and geli. Note that necessary ++modules (@var{luks}, @var{luks2} and @var{geli}) have to be loaded manually ++before this command can be used. + @end deffn + + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index db346a9f4..a0507a1fa 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1170,6 +1170,14 @@ module = { + common = disk/luks.c; + common = disk/AFSplitter.c; + }; + ++module = { ++ name = luks2; ++ common = disk/luks2.c; ++ common = lib/gnulib/base64.c; ++ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; ++ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/json'; ++}; ++ + module = { + name = geli; + common = disk/geli.c; +diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c +new file mode 100644 +index 000000000..49ee9c862 +--- /dev/null ++++ b/grub-core/disk/luks2.c +@@ -0,0 +1,678 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2019 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 . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++#define LUKS_MAGIC_1ST "LUKS\xBA\xBE" ++#define LUKS_MAGIC_2ND "SKUL\xBA\xBE" ++ ++#define MAX_PASSPHRASE 256 ++ ++enum grub_luks2_kdf_type ++{ ++ LUKS2_KDF_TYPE_ARGON2I, ++ LUKS2_KDF_TYPE_PBKDF2 ++}; ++typedef enum grub_luks2_kdf_type grub_luks2_kdf_type_t; ++ ++/* On disk LUKS header */ ++struct grub_luks2_header ++{ ++ char magic[6]; ++ grub_uint16_t version; ++ grub_uint64_t hdr_size; ++ grub_uint64_t seqid; ++ char label[48]; ++ char csum_alg[32]; ++ grub_uint8_t salt[64]; ++ char uuid[40]; ++ char subsystem[48]; ++ grub_uint64_t hdr_offset; ++ char _padding[184]; ++ grub_uint8_t csum[64]; ++ char _padding4096[7*512]; ++} GRUB_PACKED; ++typedef struct grub_luks2_header grub_luks2_header_t; ++ ++struct grub_luks2_keyslot ++{ ++ grub_int64_t key_size; ++ grub_int64_t priority; ++ struct ++ { ++ const char *encryption; ++ grub_uint64_t offset; ++ grub_uint64_t size; ++ grub_int64_t key_size; ++ } area; ++ struct ++ { ++ const char *hash; ++ grub_int64_t stripes; ++ } af; ++ struct ++ { ++ grub_luks2_kdf_type_t type; ++ const char *salt; ++ union ++ { ++ struct ++ { ++ grub_int64_t time; ++ grub_int64_t memory; ++ grub_int64_t cpus; ++ } argon2i; ++ struct ++ { ++ const char *hash; ++ grub_int64_t iterations; ++ } pbkdf2; ++ } u; ++ } kdf; ++}; ++typedef struct grub_luks2_keyslot grub_luks2_keyslot_t; ++ ++struct grub_luks2_segment ++{ ++ grub_uint64_t offset; ++ const char *size; ++ const char *encryption; ++ grub_int64_t sector_size; ++}; ++typedef struct grub_luks2_segment grub_luks2_segment_t; ++ ++struct grub_luks2_digest ++{ ++ /* Both keyslots and segments are interpreted as bitfields here */ ++ grub_uint64_t keyslots; ++ grub_uint64_t segments; ++ const char *salt; ++ const char *digest; ++ const char *hash; ++ grub_int64_t iterations; ++}; ++typedef struct grub_luks2_digest grub_luks2_digest_t; ++ ++gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, ++ grub_uint8_t * dst, grub_size_t blocksize, ++ grub_size_t blocknumbers); ++ ++static grub_err_t ++luks2_parse_keyslot (grub_luks2_keyslot_t *out, const grub_json_t *keyslot) ++{ ++ grub_json_t area, af, kdf; ++ const char *type; ++ ++ if (grub_json_getstring (&type, keyslot, "type")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid keyslot"); ++ else if (grub_strcmp (type, "luks2")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported keyslot type %s", type); ++ else if (grub_json_getint64 (&out->key_size, keyslot, "key_size")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing keyslot information"); ++ if (grub_json_getint64 (&out->priority, keyslot, "priority")) ++ out->priority = 1; ++ ++ if (grub_json_getvalue (&area, keyslot, "area") || ++ grub_json_getstring (&type, &area, "type")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid key area"); ++ else if (grub_strcmp (type, "raw")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported key area type: %s", type); ++ else if (grub_json_getuint64 (&out->area.offset, &area, "offset") || ++ grub_json_getuint64 (&out->area.size, &area, "size") || ++ grub_json_getstring (&out->area.encryption, &area, "encryption") || ++ grub_json_getint64 (&out->area.key_size, &area, "key_size")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing key area information"); ++ ++ if (grub_json_getvalue (&kdf, keyslot, "kdf") || ++ grub_json_getstring (&type, &kdf, "type") || ++ grub_json_getstring (&out->kdf.salt, &kdf, "salt")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid KDF"); ++ else if (!grub_strcmp (type, "argon2i") || !grub_strcmp (type, "argon2id")) ++ { ++ out->kdf.type = LUKS2_KDF_TYPE_ARGON2I; ++ if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") || ++ grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") || ++ grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing Argon2i parameters"); ++ } ++ else if (!grub_strcmp (type, "pbkdf2")) ++ { ++ out->kdf.type = LUKS2_KDF_TYPE_PBKDF2; ++ if (grub_json_getstring (&out->kdf.u.pbkdf2.hash, &kdf, "hash") || ++ grub_json_getint64 (&out->kdf.u.pbkdf2.iterations, &kdf, "iterations")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing PBKDF2 parameters"); ++ } ++ else ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported KDF type %s", type); ++ ++ if (grub_json_getvalue (&af, keyslot, "af") || ++ grub_json_getstring (&type, &af, "type")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing or invalid area"); ++ if (grub_strcmp (type, "luks1")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported AF type %s", type); ++ if (grub_json_getint64 (&out->af.stripes, &af, "stripes") || ++ grub_json_getstring (&out->af.hash, &af, "hash")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing AF parameters"); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++luks2_parse_segment (grub_luks2_segment_t *out, const grub_json_t *segment) ++{ ++ const char *type; ++ ++ if (grub_json_getstring (&type, segment, "type")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid segment type"); ++ else if (grub_strcmp (type, "crypt")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported segment type %s", type); ++ ++ if (grub_json_getuint64 (&out->offset, segment, "offset") || ++ grub_json_getstring (&out->size, segment, "size") || ++ grub_json_getstring (&out->encryption, segment, "encryption") || ++ grub_json_getint64 (&out->sector_size, segment, "sector_size")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing segment parameters", type); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++luks2_parse_digest (grub_luks2_digest_t *out, const grub_json_t *digest) ++{ ++ grub_json_t segments, keyslots, o; ++ grub_size_t i, size; ++ grub_uint64_t bit; ++ const char *type; ++ ++ if (grub_json_getstring (&type, digest, "type")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest type"); ++ else if (grub_strcmp (type, "pbkdf2")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported digest type %s", type); ++ ++ if (grub_json_getvalue (&segments, digest, "segments") || ++ grub_json_getvalue (&keyslots, digest, "keyslots") || ++ grub_json_getstring (&out->salt, digest, "salt") || ++ grub_json_getstring (&out->digest, digest, "digest") || ++ grub_json_getstring (&out->hash, digest, "hash") || ++ grub_json_getint64 (&out->iterations, digest, "iterations")) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing digest parameters"); ++ ++ if (grub_json_getsize (&size, &segments)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ "Digest references no segments", type); ++ ++ for (i = 0; i < size; i++) ++ { ++ if (grub_json_getchild (&o, &segments, i) || ++ grub_json_getuint64 (&bit, &o, NULL)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid segment"); ++ out->segments |= (1 << bit); ++ } ++ ++ if (grub_json_getsize (&size, &keyslots)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ "Digest references no keyslots", type); ++ ++ for (i = 0; i < size; i++) ++ { ++ if (grub_json_getchild (&o, &keyslots, i) || ++ grub_json_getuint64 (&bit, &o, NULL)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid keyslot"); ++ out->keyslots |= (1 << bit); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++luks2_get_keyslot (grub_luks2_keyslot_t *k, grub_luks2_digest_t *d, grub_luks2_segment_t *s, ++ const grub_json_t *root, grub_size_t i) ++{ ++ grub_json_t keyslots, keyslot, digests, digest, segments, segment; ++ grub_size_t j, size; ++ grub_uint64_t idx; ++ ++ /* Get nth keyslot */ ++ if (grub_json_getvalue (&keyslots, root, "keyslots") || ++ grub_json_getchild (&keyslot, &keyslots, i) || ++ grub_json_getuint64 (&idx, &keyslot, NULL) || ++ grub_json_getchild (&keyslot, &keyslot, 0) || ++ luks2_parse_keyslot (k, &keyslot)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not parse keyslot %"PRIuGRUB_SIZE, i); ++ ++ /* Get digest that matches the keyslot. */ ++ if (grub_json_getvalue (&digests, root, "digests") || ++ grub_json_getsize (&size, &digests)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not get digests"); ++ for (j = 0; j < size; j++) ++ { ++ if (grub_json_getchild (&digest, &digests, i) || ++ grub_json_getchild (&digest, &digest, 0) || ++ luks2_parse_digest (d, &digest)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not parse digest %"PRIuGRUB_SIZE, i); ++ ++ if ((d->keyslots & (1 << idx))) ++ break; ++ } ++ if (j == size) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "No digest for keyslot %"PRIuGRUB_SIZE); ++ ++ /* Get segment that matches the digest. */ ++ if (grub_json_getvalue (&segments, root, "segments") || ++ grub_json_getsize (&size, &segments)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not get segments"); ++ for (j = 0; j < size; j++) ++ { ++ if (grub_json_getchild (&segment, &segments, i) || ++ grub_json_getuint64 (&idx, &segment, NULL) || ++ grub_json_getchild (&segment, &segment, 0) || ++ luks2_parse_segment (s, &segment)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not parse segment %"PRIuGRUB_SIZE, i); ++ ++ if ((d->segments & (1 << idx))) ++ break; ++ } ++ if (j == size) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "No segment for digest %"PRIuGRUB_SIZE); ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* Determine whether to use primary or secondary header */ ++static grub_err_t ++luks2_read_header (grub_disk_t disk, grub_luks2_header_t *outhdr) ++{ ++ grub_luks2_header_t primary, secondary, *header = &primary; ++ grub_err_t ret; ++ ++ /* Read the primary LUKS header. */ ++ ret = grub_disk_read (disk, 0, 0, sizeof (primary), &primary); ++ if (ret) ++ return ret; ++ ++ /* Look for LUKS magic sequence. */ ++ if (grub_memcmp (primary.magic, LUKS_MAGIC_1ST, sizeof (primary.magic)) || ++ grub_be_to_cpu16 (primary.version) != 2) ++ return GRUB_ERR_BAD_SIGNATURE; ++ ++ /* Read the secondary header. */ ++ ret = grub_disk_read (disk, 0, grub_be_to_cpu64 (primary.hdr_size), sizeof (secondary), &secondary); ++ if (ret) ++ return ret; ++ ++ /* Look for LUKS magic sequence. */ ++ if (grub_memcmp (secondary.magic, LUKS_MAGIC_2ND, sizeof (secondary.magic)) || ++ grub_be_to_cpu16 (secondary.version) != 2) ++ return GRUB_ERR_BAD_SIGNATURE; ++ ++ if (grub_be_to_cpu64 (primary.seqid) < grub_be_to_cpu64 (secondary.seqid)) ++ header = &secondary; ++ grub_memcpy (outhdr, header, sizeof (*header)); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_cryptodisk_t ++luks2_scan (grub_disk_t disk, const char *check_uuid, int check_boot) ++{ ++ grub_cryptodisk_t cryptodisk; ++ grub_luks2_header_t header; ++ ++ if (check_boot) ++ return NULL; ++ ++ if (luks2_read_header (disk, &header)) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ return NULL; ++ } ++ ++ if (check_uuid && grub_strcasecmp (check_uuid, header.uuid) != 0) ++ return NULL; ++ ++ cryptodisk = grub_zalloc (sizeof (*cryptodisk)); ++ if (!cryptodisk) ++ return NULL; ++ ++ COMPILE_TIME_ASSERT (sizeof (cryptodisk->uuid) >= sizeof (header.uuid)); ++ ++ grub_memcpy (cryptodisk->uuid, header.uuid, sizeof (header.uuid)); ++ cryptodisk->modname = "luks2"; ++ return cryptodisk; ++} ++ ++static grub_err_t ++luks2_verify_key (grub_luks2_digest_t *d, grub_uint8_t *candidate_key, ++ grub_size_t candidate_key_len) ++{ ++ grub_uint8_t candidate_digest[GRUB_CRYPTODISK_MAX_KEYLEN]; ++ grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN], salt[GRUB_CRYPTODISK_MAX_KEYLEN]; ++ grub_size_t saltlen = sizeof (salt), digestlen = sizeof (digest); ++ const gcry_md_spec_t *hash; ++ gcry_err_code_t gcry_ret; ++ ++ /* Decode both digest and salt */ ++ if (!base64_decode (d->digest, grub_strlen (d->digest), (char *)digest, &digestlen)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest"); ++ if (!base64_decode (d->salt, grub_strlen (d->salt), (char *)salt, &saltlen)) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest salt"); ++ ++ /* Configure the hash used for the digest. */ ++ hash = grub_crypto_lookup_md_by_name (d->hash); ++ if (!hash) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", d->hash); ++ ++ /* Calculate the candidate key's digest */ ++ gcry_ret = grub_crypto_pbkdf2 (hash, ++ candidate_key, candidate_key_len, ++ salt, saltlen, ++ d->iterations, ++ candidate_digest, digestlen); ++ if (gcry_ret) ++ return grub_crypto_gcry_error (gcry_ret); ++ ++ if (grub_memcmp (candidate_digest, digest, digestlen) != 0) ++ return grub_error (GRUB_ERR_ACCESS_DENIED, "Mismatching digests"); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++luks2_decrypt_key (grub_uint8_t *out_key, ++ grub_disk_t disk, grub_cryptodisk_t crypt, ++ grub_luks2_keyslot_t *k, ++ const grub_uint8_t *passphrase, grub_size_t passphraselen) ++{ ++ grub_uint8_t area_key[GRUB_CRYPTODISK_MAX_KEYLEN]; ++ grub_uint8_t salt[GRUB_CRYPTODISK_MAX_KEYLEN]; ++ grub_uint8_t *split_key = NULL; ++ grub_size_t saltlen = sizeof (salt); ++ char cipher[32], *p;; ++ const gcry_md_spec_t *hash; ++ gcry_err_code_t gcry_ret; ++ grub_err_t ret; ++ ++ if (!base64_decode (k->kdf.salt, grub_strlen (k->kdf.salt), ++ (char *)salt, &saltlen)) ++ { ++ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid keyslot salt"); ++ goto err; ++ } ++ ++ /* Calculate the binary area key of the user supplied passphrase. */ ++ switch (k->kdf.type) ++ { ++ case LUKS2_KDF_TYPE_ARGON2I: ++ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Argon2 not supported"); ++ goto err; ++ case LUKS2_KDF_TYPE_PBKDF2: ++ hash = grub_crypto_lookup_md_by_name (k->kdf.u.pbkdf2.hash); ++ if (!hash) ++ { ++ ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", ++ k->kdf.u.pbkdf2.hash); ++ goto err; ++ } ++ ++ gcry_ret = grub_crypto_pbkdf2 (hash, (grub_uint8_t *) passphrase, ++ passphraselen, ++ salt, saltlen, ++ k->kdf.u.pbkdf2.iterations, ++ area_key, k->area.key_size); ++ if (gcry_ret) ++ { ++ ret = grub_crypto_gcry_error (gcry_ret); ++ goto err; ++ } ++ ++ break; ++ } ++ ++ /* Set up disk encryption parameters for the key area */ ++ grub_strncpy (cipher, k->area.encryption, sizeof (cipher)); ++ p = grub_memchr (cipher, '-', grub_strlen (cipher)); ++ if (!p) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid encryption"); ++ *p = '\0'; ++ ++ ret = grub_cryptodisk_setcipher (crypt, cipher, p + 1); ++ if (ret) ++ return ret; ++ ++ gcry_ret = grub_cryptodisk_setkey (crypt, area_key, k->area.key_size); ++ if (gcry_ret) ++ { ++ ret = grub_crypto_gcry_error (gcry_ret); ++ goto err; ++ } ++ ++ /* Read and decrypt the binary key area with the area key. */ ++ split_key = grub_malloc (k->area.size); ++ if (!split_key) ++ { ++ ret = grub_errno; ++ goto err; ++ } ++ ++ grub_errno = GRUB_ERR_NONE; ++ ret = grub_disk_read (disk, 0, k->area.offset, k->area.size, split_key); ++ if (ret) ++ { ++ grub_dprintf ("luks2", "Read error: %s\n", grub_errmsg); ++ goto err; ++ } ++ ++ gcry_ret = grub_cryptodisk_decrypt (crypt, split_key, k->area.size, 0); ++ if (gcry_ret) ++ { ++ ret = grub_crypto_gcry_error (gcry_ret); ++ goto err; ++ } ++ ++ /* Configure the hash used for anti-forensic merging. */ ++ hash = grub_crypto_lookup_md_by_name (k->af.hash); ++ if (!hash) ++ { ++ ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", ++ k->af.hash); ++ goto err; ++ } ++ ++ /* Merge the decrypted key material to get the candidate master key. */ ++ gcry_ret = AF_merge (hash, split_key, out_key, k->key_size, k->af.stripes); ++ if (gcry_ret) ++ { ++ ret = grub_crypto_gcry_error (gcry_ret); ++ goto err; ++ } ++ ++ grub_dprintf ("luks2", "Candidate key recovered\n"); ++ ++ err: ++ grub_free (split_key); ++ return ret; ++} ++ ++static grub_err_t ++luks2_recover_key (grub_disk_t disk, ++ grub_cryptodisk_t crypt) ++{ ++ grub_uint8_t candidate_key[GRUB_CRYPTODISK_MAX_KEYLEN]; ++ char passphrase[MAX_PASSPHRASE], cipher[32]; ++ char *json_header = NULL, *part = NULL, *ptr; ++ grub_size_t candidate_key_len = 0, i, size; ++ grub_luks2_header_t header; ++ grub_luks2_keyslot_t keyslot; ++ grub_luks2_digest_t digest; ++ grub_luks2_segment_t segment; ++ gcry_err_code_t gcry_ret; ++ grub_json_t *json = NULL, keyslots; ++ grub_err_t ret; ++ ++ ret = luks2_read_header (disk, &header); ++ if (ret) ++ return ret; ++ ++ json_header = grub_zalloc (grub_be_to_cpu64 (header.hdr_size) - sizeof (header)); ++ if (!json_header) ++ return GRUB_ERR_OUT_OF_MEMORY; ++ ++ /* Read the JSON area. */ ++ ret = grub_disk_read (disk, 0, grub_be_to_cpu64 (header.hdr_offset) + sizeof (header), ++ grub_be_to_cpu64 (header.hdr_size) - sizeof (header), json_header); ++ if (ret) ++ goto err; ++ ++ ptr = grub_memchr (json_header, 0, grub_be_to_cpu64 (header.hdr_size) - sizeof (header)); ++ if (!ptr) ++ goto err; ++ ++ ret = grub_json_parse (&json, json_header, grub_be_to_cpu64 (header.hdr_size)); ++ if (ret) ++ { ++ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid LUKS2 JSON header"); ++ goto err; ++ } ++ ++ /* Get the passphrase from the user. */ ++ if (disk->partition) ++ part = grub_partition_get_name (disk->partition); ++ grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), disk->name, ++ disk->partition ? "," : "", part ? : "", ++ crypt->uuid); ++ if (!grub_password_get (passphrase, MAX_PASSPHRASE)) ++ { ++ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); ++ goto err; ++ } ++ ++ if (grub_json_getvalue (&keyslots, json, "keyslots") || ++ grub_json_getsize (&size, &keyslots)) ++ { ++ ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not get keyslots"); ++ goto err; ++ } ++ ++ /* Try all keyslot */ ++ for (i = 0; i < size; i++) ++ { ++ ret = luks2_get_keyslot (&keyslot, &digest, &segment, json, i); ++ if (ret) ++ goto err; ++ ++ if (keyslot.priority == 0) ++ { ++ grub_dprintf ("luks2", "Ignoring keyslot %"PRIuGRUB_SIZE" due to priority\n", i); ++ continue; ++ } ++ ++ grub_dprintf ("luks2", "Trying keyslot %"PRIuGRUB_SIZE"\n", i); ++ ++ /* Set up disk according to keyslot's segment. */ ++ crypt->offset = grub_divmod64 (segment.offset, segment.sector_size, NULL); ++ crypt->log_sector_size = sizeof (unsigned int) * 8 ++ - __builtin_clz ((unsigned int) segment.sector_size) - 1; ++ if (grub_strcmp (segment.size, "dynamic") == 0) ++ crypt->total_length = grub_disk_get_size (disk) - crypt->offset; ++ else ++ crypt->total_length = grub_strtoull (segment.size, NULL, 10); ++ ++ ret = luks2_decrypt_key (candidate_key, disk, crypt, &keyslot, ++ (const grub_uint8_t *) passphrase, grub_strlen (passphrase)); ++ if (ret) ++ { ++ grub_dprintf ("luks2", "Decryption with keyslot %"PRIuGRUB_SIZE" failed", i); ++ continue; ++ } ++ ++ ret = luks2_verify_key (&digest, candidate_key, keyslot.key_size); ++ if (ret) ++ { ++ grub_dprintf ("luks2", "Could not open keyslot %"PRIuGRUB_SIZE"\n", i); ++ continue; ++ } ++ ++ /* ++ * TRANSLATORS: It's a cryptographic key slot: one element of an array ++ * where each element is either empty or holds a key. ++ */ ++ grub_printf_ (N_("Slot %"PRIuGRUB_SIZE" opened\n"), i); ++ ++ candidate_key_len = keyslot.key_size; ++ break; ++ } ++ if (candidate_key_len == 0) ++ { ++ ret = grub_error (GRUB_ERR_ACCESS_DENIED, "Invalid passphrase"); ++ goto err; ++ } ++ ++ /* Set up disk cipher. */ ++ grub_strncpy (cipher, segment.encryption, sizeof (cipher)); ++ ptr = grub_memchr (cipher, '-', grub_strlen (cipher)); ++ if (!ptr) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid encryption"); ++ *ptr = '\0'; ++ ++ ret = grub_cryptodisk_setcipher (crypt, cipher, ptr + 1); ++ if (ret) ++ goto err; ++ ++ /* Set the master key. */ ++ gcry_ret = grub_cryptodisk_setkey (crypt, candidate_key, candidate_key_len); ++ if (gcry_ret) ++ { ++ ret = grub_crypto_gcry_error (gcry_ret); ++ goto err; ++ } ++ ++ err: ++ grub_free (part); ++ grub_free (json_header); ++ grub_json_free (json); ++ return ret; ++} ++ ++static struct grub_cryptodisk_dev luks2_crypto = { ++ .scan = luks2_scan, ++ .recover_key = luks2_recover_key ++}; ++ ++GRUB_MOD_INIT (luks2) ++{ ++ grub_cryptodisk_dev_register (&luks2_crypto); ++} ++ ++GRUB_MOD_FINI (luks2) ++{ ++ grub_cryptodisk_dev_unregister (&luks2_crypto); ++} +-- +2.20.1 + diff --git a/backport-json-Implement-wrapping-interface.patch b/backport-json-Implement-wrapping-interface.patch new file mode 100644 index 0000000000000000000000000000000000000000..392f3bc892db55885b137eed8ab0dfa539037881 --- /dev/null +++ b/backport-json-Implement-wrapping-interface.patch @@ -0,0 +1,410 @@ +From c6a84545a3c21369bb2685fdbe3cd895733dedbd Mon Sep 17 00:00:00 2001 +From: Patrick Steinhardt +Date: Fri, 27 Dec 2019 16:18:35 +0100 +Subject: [PATCH] json: Implement wrapping interface + +While the newly added jsmn library provides the parsing interface, it +does not provide any kind of interface to act on parsed tokens. Instead, +the caller is expected to handle pointer arithmetics inside of the token +array in order to extract required information. While simple, this +requires users to know some of the inner workings of the library and is +thus quite an unintuitive interface. + +This commit adds a new interface on top of the jsmn parser that provides +convenience functions to retrieve values from the parsed json type, grub_json_t. + +Signed-off-by: Patrick Steinhardt +Reviewed-by: Daniel Kiper +--- + grub-core/lib/json/json.c | 244 ++++++++++++++++++++++++++++++++++++++ + grub-core/lib/json/json.h | 122 +++++++++++++++++++ + 2 files changed, 366 insertions(+) + create mode 100644 grub-core/lib/json/json.h + +diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c +index 2bddd8c46..412f26f17 100644 +--- a/grub-core/lib/json/json.c ++++ b/grub-core/lib/json/json.c +@@ -17,7 +17,251 @@ + */ + + #include ++#include + ++#define JSMN_STATIC + #include "jsmn.h" ++#include "json.h" + + GRUB_MOD_LICENSE ("GPLv3"); ++ ++grub_err_t ++grub_json_parse (grub_json_t **out, char *string, grub_size_t string_len) ++{ ++ grub_json_t *json = NULL; ++ jsmn_parser parser; ++ grub_err_t ret = GRUB_ERR_NONE; ++ int jsmn_ret; ++ ++ if (!string) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ json = grub_zalloc (sizeof (*json)); ++ if (!json) ++ return GRUB_ERR_OUT_OF_MEMORY; ++ json->string = string; ++ ++ /* ++ * Parse the string twice: first to determine how many tokens ++ * we need to allocate, second to fill allocated tokens. ++ */ ++ jsmn_init (&parser); ++ jsmn_ret = jsmn_parse (&parser, string, string_len, NULL, 0); ++ if (jsmn_ret <= 0) ++ { ++ ret = GRUB_ERR_BAD_ARGUMENT; ++ goto err; ++ } ++ ++ json->tokens = grub_malloc (sizeof (jsmntok_t) * jsmn_ret); ++ if (!json->tokens) ++ { ++ ret = GRUB_ERR_OUT_OF_MEMORY; ++ goto err; ++ } ++ ++ jsmn_init (&parser); ++ jsmn_ret = jsmn_parse (&parser, string, string_len, json->tokens, jsmn_ret); ++ if (jsmn_ret <= 0) ++ { ++ ret = GRUB_ERR_BAD_ARGUMENT; ++ goto err; ++ } ++ ++ *out = json; ++ ++ err: ++ if (ret && json) ++ { ++ grub_free (json->string); ++ grub_free (json->tokens); ++ grub_free (json); ++ } ++ return ret; ++} ++ ++void ++grub_json_free (grub_json_t *json) ++{ ++ if (json) ++ { ++ grub_free (json->tokens); ++ grub_free (json); ++ } ++} ++ ++grub_err_t ++grub_json_getsize (grub_size_t *out, const grub_json_t *json) ++{ ++ int size; ++ ++ size = ((jsmntok_t *)json->tokens)[json->idx].size; ++ if (size < 0) ++ return GRUB_ERR_OUT_OF_RANGE; ++ ++ *out = (grub_size_t) size; ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_json_gettype (grub_json_type_t *out, const grub_json_t *json) ++{ ++ switch (((jsmntok_t *)json->tokens)[json->idx].type) ++ { ++ case JSMN_OBJECT: ++ *out = GRUB_JSON_OBJECT; ++ break; ++ case JSMN_ARRAY: ++ *out = GRUB_JSON_ARRAY; ++ break; ++ case JSMN_STRING: ++ *out = GRUB_JSON_STRING; ++ break; ++ case JSMN_PRIMITIVE: ++ *out = GRUB_JSON_PRIMITIVE; ++ break; ++ default: ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_json_getchild (grub_json_t *out, const grub_json_t *parent, grub_size_t n) ++{ ++ grub_size_t offset = 1, size; ++ jsmntok_t *p; ++ ++ if (grub_json_getsize (&size, parent) || n >= size) ++ return GRUB_ERR_OUT_OF_RANGE; ++ ++ /* ++ * Skip the first n children. For each of the children, we need ++ * to skip their own potential children (e.g. if it's an ++ * array), as well. We thus add the children's size to n on ++ * each iteration. ++ */ ++ p = &((jsmntok_t *)parent->tokens)[parent->idx]; ++ while (n--) ++ n += p[offset++].size; ++ ++ out->string = parent->string; ++ out->tokens = parent->tokens; ++ out->idx = parent->idx + offset; ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_json_getvalue (grub_json_t *out, const grub_json_t *parent, const char *key) ++{ ++ grub_json_type_t type; ++ grub_size_t i, size; ++ ++ if (grub_json_gettype (&type, parent) || type != GRUB_JSON_OBJECT) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ if (grub_json_getsize (&size, parent)) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ for (i = 0; i < size; i++) ++ { ++ grub_json_t child; ++ const char *s; ++ ++ if (grub_json_getchild (&child, parent, i) || ++ grub_json_getstring (&s, &child, NULL) || ++ grub_strcmp (s, key) != 0) ++ continue; ++ ++ return grub_json_getchild (out, &child, 0); ++ } ++ ++ return GRUB_ERR_FILE_NOT_FOUND; ++} ++ ++static grub_err_t ++get_value (grub_json_type_t *out_type, const char **out_string, const grub_json_t *parent, const char *key) ++{ ++ const grub_json_t *p = parent; ++ grub_json_t child; ++ grub_err_t ret; ++ jsmntok_t *tok; ++ ++ if (key) ++ { ++ ret = grub_json_getvalue (&child, parent, key); ++ if (ret) ++ return ret; ++ p = &child; ++ } ++ ++ tok = &((jsmntok_t *) p->tokens)[p->idx]; ++ p->string[tok->end] = '\0'; ++ ++ *out_string = p->string + tok->start; ++ ++ return grub_json_gettype (out_type, p); ++} ++ ++grub_err_t ++grub_json_getstring (const char **out, const grub_json_t *parent, const char *key) ++{ ++ grub_json_type_t type; ++ const char *value; ++ grub_err_t ret; ++ ++ ret = get_value (&type, &value, parent, key); ++ if (ret) ++ return ret; ++ if (type != GRUB_JSON_STRING) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ *out = value; ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_json_getuint64 (grub_uint64_t *out, const grub_json_t *parent, const char *key) ++{ ++ grub_json_type_t type; ++ const char *value; ++ char *end; ++ grub_err_t ret; ++ ++ ret = get_value (&type, &value, parent, key); ++ if (ret) ++ return ret; ++ if (type != GRUB_JSON_STRING && type != GRUB_JSON_PRIMITIVE) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ grub_errno = GRUB_ERR_NONE; ++ *out = grub_strtoul (value, &end, 10); ++ if (grub_errno != GRUB_ERR_NONE || *end) ++ return GRUB_ERR_BAD_NUMBER; ++ ++ return GRUB_ERR_NONE; ++} ++ ++grub_err_t ++grub_json_getint64 (grub_int64_t *out, const grub_json_t *parent, const char *key) ++{ ++ grub_json_type_t type; ++ const char *value; ++ char *end; ++ grub_err_t ret; ++ ++ ret = get_value (&type, &value, parent, key); ++ if (ret) ++ return ret; ++ if (type != GRUB_JSON_STRING && type != GRUB_JSON_PRIMITIVE) ++ return GRUB_ERR_BAD_ARGUMENT; ++ ++ grub_errno = GRUB_ERR_NONE; ++ *out = grub_strtol (value, &end, 10); ++ if (grub_errno != GRUB_ERR_NONE || *end) ++ return GRUB_ERR_BAD_NUMBER; ++ ++ return GRUB_ERR_NONE; ++} +diff --git a/grub-core/lib/json/json.h b/grub-core/lib/json/json.h +new file mode 100644 +index 000000000..358e4bca3 +--- /dev/null ++++ b/grub-core/lib/json/json.h +@@ -0,0 +1,122 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2019 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 . ++ */ ++ ++#ifndef GRUB_JSON_JSON_H ++#define GRUB_JSON_JSON_H 1 ++ ++#include ++ ++enum grub_json_type ++{ ++ /* Unordered collection of key-value pairs. */ ++ GRUB_JSON_OBJECT, ++ /* Ordered list of zero or more values. */ ++ GRUB_JSON_ARRAY, ++ /* Zero or more Unicode characters. */ ++ GRUB_JSON_STRING, ++ /* Number, boolean or empty value. */ ++ GRUB_JSON_PRIMITIVE, ++ /* Invalid token. */ ++ GRUB_JSON_UNDEFINED, ++}; ++typedef enum grub_json_type grub_json_type_t; ++ ++struct grub_json ++{ ++ void *tokens; ++ char *string; ++ grub_size_t idx; ++}; ++typedef struct grub_json grub_json_t; ++ ++/* ++ * Parse a JSON-encoded string. Note that the string passed to ++ * this function will get modified on subsequent calls to ++ * grub_json_get*(). Returns the root object of the parsed JSON ++ * object, which needs to be free'd via grub_json_free(). ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_parse) (grub_json_t **out, ++ char *string, ++ grub_size_t string_len); ++ ++/* ++ * Free the structure and its contents. The string passed to ++ * grub_json_parse() will not be free'd. ++ */ ++extern void EXPORT_FUNC(grub_json_free) (grub_json_t *json); ++ ++/* ++ * Get the child count of a valid grub_json_t instance. Children ++ * are present for arrays, objects (dicts) and keys of a dict. ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_getsize) (grub_size_t *out, ++ const grub_json_t *json); ++ ++/* Get the type of a valid grub_json_t instance. */ ++extern grub_err_t EXPORT_FUNC(grub_json_gettype) (grub_json_type_t *out, ++ const grub_json_t *json); ++ ++/* ++ * Get n'th child of a valid object, array or key. Will return an ++ * error if no such child exists. The result does not need to be ++ * free'd. ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_getchild) (grub_json_t *out, ++ const grub_json_t *parent, ++ grub_size_t n); ++ ++/* ++ * Get value of key from a valid grub_json_t instance. The result ++ * does not need to be free'd. ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_getvalue) (grub_json_t *out, ++ const grub_json_t *parent, ++ const char *key); ++ ++/* ++ * Get the string representation of a valid grub_json_t instance. ++ * If a key is given and parent is a JSON object, this function ++ * will return the string value of a child mapping to the key. ++ * If no key is given, it will return the string value of the ++ * parent itself. ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_getstring) (const char **out, ++ const grub_json_t *parent, ++ const char *key); ++ ++/* ++ * Get the uint64 representation of a valid grub_json_t instance. ++ * Returns an error if the value pointed to by `parent` cannot be ++ * converted to an uint64. See grub_json_getstring() for details ++ * on the key parameter. ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_getuint64) (grub_uint64_t *out, ++ const grub_json_t *parent, ++ const char *key); ++ ++/* ++ * Get the int64 representation of a valid grub_json_t instance. ++ * Returns an error if the value pointed to by `parent` cannot be ++ * converted to an int64. See grub_json_getstring() for ++ * details on the key parameter. ++ */ ++extern grub_err_t EXPORT_FUNC(grub_json_getint64) (grub_int64_t *out, ++ const grub_json_t *parent, ++ const char *key); ++ ++#endif +-- +2.20.1 + diff --git a/backport-json-Import-upstream-jsmn-1.1.0 b/backport-json-Import-upstream-jsmn-1.1.0 new file mode 100644 index 0000000000000000000000000000000000000000..0ad8ab0765409201369966da44b0ae99759d2847 --- /dev/null +++ b/backport-json-Import-upstream-jsmn-1.1.0 @@ -0,0 +1,601 @@ +From 528938d503088ce0e2fe7d83f8f711efefaed0fd Mon Sep 17 00:00:00 2001 +From: Patrick Steinhardt +Date: Fri, 27 Dec 2019 16:18:34 +0100 +Subject: [PATCH] json: Import upstream jsmn-1.1.0 + +The upcoming support for LUKS2 encryption will require a JSON parser to +decode all parameters required for decryption of a drive. As there is +currently no other tool that requires JSON, and as gnulib does not +provide a parser, we need to introduce a new one into the code base. The +backend for the JSON implementation is going to be the jsmn library [1]. +It has several benefits that make it a very good fit for inclusion in +GRUB: + + - It is licensed under MIT. + - It is written in C89. + - It has no dependencies, not even libc. + - It is small with only about 500 lines of code. + - It doesn't do any dynamic memory allocation. + - It is testen on x86, amd64, ARM and AVR. + +The library itself comes as a single header, only, that contains both +declarations and definitions. The exposed interface is kind of +simplistic, though, and does not provide any convenience features +whatsoever. Thus there will be a separate interface provided by GRUB +around this parser that is going to be implemented in the following +commit. This change only imports jsmn.h from tag v1.1.0 and adds it +unmodified to a new json module with the following command: + +curl -L https://raw.githubusercontent.com/zserge/jsmn/v1.1.0/jsmn.h \ + -o grub-core/lib/json/jsmn.h + +Upstream jsmn commit hash: fdcef3ebf886fa210d14956d3c068a653e76a24e +Upstream jsmn commit name: Modernize (#149), 2019-04-20 + +[1]: https://github.com/zserge/jsmn + +Signed-off-by: Patrick Steinhardt +Reviewed-by: Daniel Kiper +--- + docs/grub-dev.texi | 14 ++ + grub-core/Makefile.core.def | 5 + + grub-core/lib/json/jsmn.h | 468 ++++++++++++++++++++++++++++++++++++ + grub-core/lib/json/json.c | 23 ++ + 4 files changed, 510 insertions(+) + create mode 100644 grub-core/lib/json/jsmn.h + create mode 100644 grub-core/lib/json/json.c + +diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi +index ee389fd83..df2350be0 100644 +--- a/docs/grub-dev.texi ++++ b/docs/grub-dev.texi +@@ -490,6 +490,7 @@ to update it. + + @menu + * Gnulib:: ++* jsmn:: + @end menu + + @node Gnulib +@@ -545,6 +546,19 @@ AC_SYS_LARGEFILE + + @end example + ++@node jsmn ++@section jsmn ++ ++jsmn is a minimalistic JSON parser which is implemented in a single header file ++@file{jsmn.h}. To import a different version of the jsmn parser, you may simply ++download the @file{jsmn.h} header from the desired tag or commit to the target ++directory: ++ ++@example ++curl -L https://raw.githubusercontent.com/zserge/jsmn/v1.1.0/jsmn.h \ ++ -o grub-core/lib/json/jsmn.h ++@end example ++ + @node Porting + @chapter Porting + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 269370417..037de4023 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1176,6 +1176,11 @@ module = { + common = disk/cryptodisk.c; + }; + ++module = { ++ name = json; ++ common = lib/json/json.c; ++}; ++ + module = { + name = luks; + common = disk/luks.c; +diff --git a/grub-core/lib/json/jsmn.h b/grub-core/lib/json/jsmn.h +new file mode 100644 +index 000000000..b95368a20 +--- /dev/null ++++ b/grub-core/lib/json/jsmn.h +@@ -0,0 +1,468 @@ ++/* ++ * MIT License ++ * ++ * Copyright (c) 2010 Serge Zaitsev ++ * ++ * 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. ++ */ ++#ifndef JSMN_H ++#define JSMN_H ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef JSMN_STATIC ++#define JSMN_API static ++#else ++#define JSMN_API extern ++#endif ++ ++/** ++ * JSON type identifier. Basic types are: ++ * o Object ++ * o Array ++ * o String ++ * o Other primitive: number, boolean (true/false) or null ++ */ ++typedef enum { ++ JSMN_UNDEFINED = 0, ++ JSMN_OBJECT = 1, ++ JSMN_ARRAY = 2, ++ JSMN_STRING = 3, ++ JSMN_PRIMITIVE = 4 ++} jsmntype_t; ++ ++enum jsmnerr { ++ /* Not enough tokens were provided */ ++ JSMN_ERROR_NOMEM = -1, ++ /* Invalid character inside JSON string */ ++ JSMN_ERROR_INVAL = -2, ++ /* The string is not a full JSON packet, more bytes expected */ ++ JSMN_ERROR_PART = -3 ++}; ++ ++/** ++ * JSON token description. ++ * type type (object, array, string etc.) ++ * start start position in JSON data string ++ * end end position in JSON data string ++ */ ++typedef struct { ++ jsmntype_t type; ++ int start; ++ int end; ++ int size; ++#ifdef JSMN_PARENT_LINKS ++ int parent; ++#endif ++} jsmntok_t; ++ ++/** ++ * JSON parser. Contains an array of token blocks available. Also stores ++ * the string being parsed now and current position in that string. ++ */ ++typedef struct { ++ unsigned int pos; /* offset in the JSON string */ ++ unsigned int toknext; /* next token to allocate */ ++ int toksuper; /* superior token node, e.g. parent object or array */ ++} jsmn_parser; ++ ++/** ++ * Create JSON parser over an array of tokens ++ */ ++JSMN_API void jsmn_init(jsmn_parser *parser); ++ ++/** ++ * Run JSON parser. It parses a JSON data string into and array of tokens, each ++ * describing ++ * a single JSON object. ++ */ ++JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, ++ jsmntok_t *tokens, const unsigned int num_tokens); ++ ++#ifndef JSMN_HEADER ++/** ++ * Allocates a fresh unused token from the token pool. ++ */ ++static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, ++ const size_t num_tokens) { ++ jsmntok_t *tok; ++ if (parser->toknext >= num_tokens) { ++ return NULL; ++ } ++ tok = &tokens[parser->toknext++]; ++ tok->start = tok->end = -1; ++ tok->size = 0; ++#ifdef JSMN_PARENT_LINKS ++ tok->parent = -1; ++#endif ++ return tok; ++} ++ ++/** ++ * Fills token type and boundaries. ++ */ ++static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, ++ const int start, const int end) { ++ token->type = type; ++ token->start = start; ++ token->end = end; ++ token->size = 0; ++} ++ ++/** ++ * Fills next available token with JSON primitive. ++ */ ++static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, ++ const size_t len, jsmntok_t *tokens, ++ const size_t num_tokens) { ++ jsmntok_t *token; ++ int start; ++ ++ start = parser->pos; ++ ++ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { ++ switch (js[parser->pos]) { ++#ifndef JSMN_STRICT ++ /* In strict mode primitive must be followed by "," or "}" or "]" */ ++ case ':': ++#endif ++ case '\t': ++ case '\r': ++ case '\n': ++ case ' ': ++ case ',': ++ case ']': ++ case '}': ++ goto found; ++ } ++ if (js[parser->pos] < 32 || js[parser->pos] >= 127) { ++ parser->pos = start; ++ return JSMN_ERROR_INVAL; ++ } ++ } ++#ifdef JSMN_STRICT ++ /* In strict mode primitive must be followed by a comma/object/array */ ++ parser->pos = start; ++ return JSMN_ERROR_PART; ++#endif ++ ++found: ++ if (tokens == NULL) { ++ parser->pos--; ++ return 0; ++ } ++ token = jsmn_alloc_token(parser, tokens, num_tokens); ++ if (token == NULL) { ++ parser->pos = start; ++ return JSMN_ERROR_NOMEM; ++ } ++ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); ++#ifdef JSMN_PARENT_LINKS ++ token->parent = parser->toksuper; ++#endif ++ parser->pos--; ++ return 0; ++} ++ ++/** ++ * Fills next token with JSON string. ++ */ ++static int jsmn_parse_string(jsmn_parser *parser, const char *js, ++ const size_t len, jsmntok_t *tokens, ++ const size_t num_tokens) { ++ jsmntok_t *token; ++ ++ int start = parser->pos; ++ ++ parser->pos++; ++ ++ /* Skip starting quote */ ++ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { ++ char c = js[parser->pos]; ++ ++ /* Quote: end of string */ ++ if (c == '\"') { ++ if (tokens == NULL) { ++ return 0; ++ } ++ token = jsmn_alloc_token(parser, tokens, num_tokens); ++ if (token == NULL) { ++ parser->pos = start; ++ return JSMN_ERROR_NOMEM; ++ } ++ jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); ++#ifdef JSMN_PARENT_LINKS ++ token->parent = parser->toksuper; ++#endif ++ return 0; ++ } ++ ++ /* Backslash: Quoted symbol expected */ ++ if (c == '\\' && parser->pos + 1 < len) { ++ int i; ++ parser->pos++; ++ switch (js[parser->pos]) { ++ /* Allowed escaped symbols */ ++ case '\"': ++ case '/': ++ case '\\': ++ case 'b': ++ case 'f': ++ case 'r': ++ case 'n': ++ case 't': ++ break; ++ /* Allows escaped symbol \uXXXX */ ++ case 'u': ++ parser->pos++; ++ for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; ++ i++) { ++ /* If it isn't a hex character we have an error */ ++ if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ ++ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ ++ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ ++ parser->pos = start; ++ return JSMN_ERROR_INVAL; ++ } ++ parser->pos++; ++ } ++ parser->pos--; ++ break; ++ /* Unexpected symbol */ ++ default: ++ parser->pos = start; ++ return JSMN_ERROR_INVAL; ++ } ++ } ++ } ++ parser->pos = start; ++ return JSMN_ERROR_PART; ++} ++ ++/** ++ * Parse JSON string and fill tokens. ++ */ ++JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, ++ jsmntok_t *tokens, const unsigned int num_tokens) { ++ int r; ++ int i; ++ jsmntok_t *token; ++ int count = parser->toknext; ++ ++ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { ++ char c; ++ jsmntype_t type; ++ ++ c = js[parser->pos]; ++ switch (c) { ++ case '{': ++ case '[': ++ count++; ++ if (tokens == NULL) { ++ break; ++ } ++ token = jsmn_alloc_token(parser, tokens, num_tokens); ++ if (token == NULL) { ++ return JSMN_ERROR_NOMEM; ++ } ++ if (parser->toksuper != -1) { ++ jsmntok_t *t = &tokens[parser->toksuper]; ++#ifdef JSMN_STRICT ++ /* In strict mode an object or array can't become a key */ ++ if (t->type == JSMN_OBJECT) { ++ return JSMN_ERROR_INVAL; ++ } ++#endif ++ t->size++; ++#ifdef JSMN_PARENT_LINKS ++ token->parent = parser->toksuper; ++#endif ++ } ++ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); ++ token->start = parser->pos; ++ parser->toksuper = parser->toknext - 1; ++ break; ++ case '}': ++ case ']': ++ if (tokens == NULL) { ++ break; ++ } ++ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); ++#ifdef JSMN_PARENT_LINKS ++ if (parser->toknext < 1) { ++ return JSMN_ERROR_INVAL; ++ } ++ token = &tokens[parser->toknext - 1]; ++ for (;;) { ++ if (token->start != -1 && token->end == -1) { ++ if (token->type != type) { ++ return JSMN_ERROR_INVAL; ++ } ++ token->end = parser->pos + 1; ++ parser->toksuper = token->parent; ++ break; ++ } ++ if (token->parent == -1) { ++ if (token->type != type || parser->toksuper == -1) { ++ return JSMN_ERROR_INVAL; ++ } ++ break; ++ } ++ token = &tokens[token->parent]; ++ } ++#else ++ for (i = parser->toknext - 1; i >= 0; i--) { ++ token = &tokens[i]; ++ if (token->start != -1 && token->end == -1) { ++ if (token->type != type) { ++ return JSMN_ERROR_INVAL; ++ } ++ parser->toksuper = -1; ++ token->end = parser->pos + 1; ++ break; ++ } ++ } ++ /* Error if unmatched closing bracket */ ++ if (i == -1) { ++ return JSMN_ERROR_INVAL; ++ } ++ for (; i >= 0; i--) { ++ token = &tokens[i]; ++ if (token->start != -1 && token->end == -1) { ++ parser->toksuper = i; ++ break; ++ } ++ } ++#endif ++ break; ++ case '\"': ++ r = jsmn_parse_string(parser, js, len, tokens, num_tokens); ++ if (r < 0) { ++ return r; ++ } ++ count++; ++ if (parser->toksuper != -1 && tokens != NULL) { ++ tokens[parser->toksuper].size++; ++ } ++ break; ++ case '\t': ++ case '\r': ++ case '\n': ++ case ' ': ++ break; ++ case ':': ++ parser->toksuper = parser->toknext - 1; ++ break; ++ case ',': ++ if (tokens != NULL && parser->toksuper != -1 && ++ tokens[parser->toksuper].type != JSMN_ARRAY && ++ tokens[parser->toksuper].type != JSMN_OBJECT) { ++#ifdef JSMN_PARENT_LINKS ++ parser->toksuper = tokens[parser->toksuper].parent; ++#else ++ for (i = parser->toknext - 1; i >= 0; i--) { ++ if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { ++ if (tokens[i].start != -1 && tokens[i].end == -1) { ++ parser->toksuper = i; ++ break; ++ } ++ } ++ } ++#endif ++ } ++ break; ++#ifdef JSMN_STRICT ++ /* In strict mode primitives are: numbers and booleans */ ++ case '-': ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ case 't': ++ case 'f': ++ case 'n': ++ /* And they must not be keys of the object */ ++ if (tokens != NULL && parser->toksuper != -1) { ++ const jsmntok_t *t = &tokens[parser->toksuper]; ++ if (t->type == JSMN_OBJECT || ++ (t->type == JSMN_STRING && t->size != 0)) { ++ return JSMN_ERROR_INVAL; ++ } ++ } ++#else ++ /* In non-strict mode every unquoted value is a primitive */ ++ default: ++#endif ++ r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); ++ if (r < 0) { ++ return r; ++ } ++ count++; ++ if (parser->toksuper != -1 && tokens != NULL) { ++ tokens[parser->toksuper].size++; ++ } ++ break; ++ ++#ifdef JSMN_STRICT ++ /* Unexpected char in strict mode */ ++ default: ++ return JSMN_ERROR_INVAL; ++#endif ++ } ++ } ++ ++ if (tokens != NULL) { ++ for (i = parser->toknext - 1; i >= 0; i--) { ++ /* Unmatched opened object or array */ ++ if (tokens[i].start != -1 && tokens[i].end == -1) { ++ return JSMN_ERROR_PART; ++ } ++ } ++ } ++ ++ return count; ++} ++ ++/** ++ * Creates a new parser based over a given buffer with an array of tokens ++ * available. ++ */ ++JSMN_API void jsmn_init(jsmn_parser *parser) { ++ parser->pos = 0; ++ parser->toknext = 0; ++ parser->toksuper = -1; ++} ++ ++#endif /* JSMN_HEADER */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* JSMN_H */ +diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c +new file mode 100644 +index 000000000..2bddd8c46 +--- /dev/null ++++ b/grub-core/lib/json/json.c +@@ -0,0 +1,23 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2019 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 . ++ */ ++ ++#include ++ ++#include "jsmn.h" ++ ++GRUB_MOD_LICENSE ("GPLv3"); +-- +2.20.1 + diff --git a/build-add-base64-include-file.patch b/build-add-base64-include-file.patch new file mode 100644 index 0000000000000000000000000000000000000000..859bac5870eb3399a75de09b80449082183b6481 --- /dev/null +++ b/build-add-base64-include-file.patch @@ -0,0 +1,835 @@ +From 38527f80237e6be00e6fd0660abcdad33741c991 Mon Sep 17 00:00:00 2001 +From: Yurii Huang +Date: Thu, 5 Sep 2024 18:40:36 +0800 +Subject: [PATCH] add base64 include file + +--- + grub-core/lib/gnulib/base64.c | 598 ++++++++++++++++++++++++++++++++++ + grub-core/lib/gnulib/base64.h | 68 ++++ + idx.h | 134 ++++++++ + 3 files changed, 800 insertions(+) + create mode 100644 grub-core/lib/gnulib/base64.c + create mode 100644 grub-core/lib/gnulib/base64.h + create mode 100644 idx.h + +diff --git a/grub-core/lib/gnulib/base64.c b/grub-core/lib/gnulib/base64.c +new file mode 100644 +index 0000000..6b6e5b0 +--- /dev/null ++++ b/grub-core/lib/gnulib/base64.c +@@ -0,0 +1,598 @@ ++/* base64.c -- Encode binary data using printable characters. ++ Copyright (C) 1999-2001, 2004-2006, 2009-2022 Free Software Foundation, Inc. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file 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 Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++/* Written by Simon Josefsson. Partially adapted from GNU MailUtils ++ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review ++ * from Paul Eggert, Bruno Haible, and Stepan Kasal. ++ * ++ * See also RFC 4648 . ++ * ++ * Be careful with error checking. Here is how you would typically ++ * use these functions: ++ * ++ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen); ++ * if (!ok) ++ * FAIL: input was not valid base64 ++ * if (out == NULL) ++ * FAIL: memory allocation error ++ * OK: data in OUT/OUTLEN ++ * ++ * idx_t outlen = base64_encode_alloc (in, inlen, &out); ++ * if (out == NULL && outlen == 0 && inlen != 0) ++ * FAIL: input too long ++ * if (out == NULL) ++ * FAIL: memory allocation error ++ * OK: data in OUT/OUTLEN. ++ * ++ */ ++ ++#include ++ ++/* Get prototype. */ ++#include "base64.h" ++ ++/* Get imalloc. */ ++#include ++ ++#include ++ ++/* Get UCHAR_MAX. */ ++#include ++ ++#include ++ ++/* Convert 'char' to 'unsigned char' without casting. */ ++static unsigned char ++to_uchar (char ch) ++{ ++ return ch; ++} ++ ++static const char b64c[64] = ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; ++ ++/* Base64 encode IN array of size INLEN into OUT array. OUT needs ++ to be of length >= BASE64_LENGTH(INLEN), and INLEN needs to be ++ a multiple of 3. */ ++static void ++base64_encode_fast (const char *restrict in, idx_t inlen, char *restrict out) ++{ ++ while (inlen) ++ { ++ *out++ = b64c[(to_uchar (in[0]) >> 2) & 0x3f]; ++ *out++ = b64c[((to_uchar (in[0]) << 4) + (to_uchar (in[1]) >> 4)) & 0x3f]; ++ *out++ = b64c[((to_uchar (in[1]) << 2) + (to_uchar (in[2]) >> 6)) & 0x3f]; ++ *out++ = b64c[to_uchar (in[2]) & 0x3f]; ++ ++ inlen -= 3; ++ in += 3; ++ } ++} ++ ++/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN. ++ If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as ++ possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero ++ terminate the output buffer. */ ++void ++base64_encode (const char *restrict in, idx_t inlen, ++ char *restrict out, idx_t outlen) ++{ ++ /* Note this outlen constraint can be enforced at compile time. ++ I.E. that the output buffer is exactly large enough to hold ++ the encoded inlen bytes. The inlen constraints (of corresponding ++ to outlen, and being a multiple of 3) can change at runtime ++ at the end of input. However the common case when reading ++ large inputs is to have both constraints satisfied, so we depend ++ on both in base_encode_fast(). */ ++ if (outlen % 4 == 0 && inlen == (outlen >> 2) * 3) ++ { ++ base64_encode_fast (in, inlen, out); ++ return; ++ } ++ ++ while (inlen && outlen) ++ { ++ *out++ = b64c[(to_uchar (in[0]) >> 2) & 0x3f]; ++ if (!--outlen) ++ break; ++ *out++ = b64c[((to_uchar (in[0]) << 4) ++ + (--inlen ? to_uchar (in[1]) >> 4 : 0)) ++ & 0x3f]; ++ if (!--outlen) ++ break; ++ *out++ = ++ (inlen ++ ? b64c[((to_uchar (in[1]) << 2) ++ + (--inlen ? to_uchar (in[2]) >> 6 : 0)) ++ & 0x3f] ++ : '='); ++ if (!--outlen) ++ break; ++ *out++ = inlen ? b64c[to_uchar (in[2]) & 0x3f] : '='; ++ if (!--outlen) ++ break; ++ if (inlen) ++ inlen--; ++ if (inlen) ++ in += 3; ++ } ++ ++ if (outlen) ++ *out = '\0'; ++} ++ ++/* Allocate a buffer and store zero terminated base64 encoded data ++ from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e., ++ the length of the encoded data, excluding the terminating zero. On ++ return, the OUT variable will hold a pointer to newly allocated ++ memory that must be deallocated by the caller. If output string ++ length would overflow, 0 is returned and OUT is set to NULL. If ++ memory allocation failed, OUT is set to NULL, and the return value ++ indicates length of the requested memory block, i.e., ++ BASE64_LENGTH(inlen) + 1. */ ++idx_t ++base64_encode_alloc (const char *in, idx_t inlen, char **out) ++{ ++ /* Check for overflow in outlen computation. ++ Treat negative INLEN as overflow, for better compatibility with ++ pre-2021-08-27 API, which used size_t. */ ++ idx_t in_over_3 = inlen / 3 + (inlen % 3 != 0), outlen; ++ if (! INT_MULTIPLY_OK (in_over_3, 4, &outlen) || inlen < 0) ++ { ++ *out = NULL; ++ return 0; ++ } ++ outlen++; ++ ++ *out = imalloc (outlen); ++ if (!*out) ++ return outlen; ++ ++ base64_encode (in, inlen, *out, outlen); ++ ++ return outlen - 1; ++} ++ ++/* With this approach this file works independent of the charset used ++ (think EBCDIC). However, it does assume that the characters in the ++ Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX ++ 1003.1-2001 require that char and unsigned char are 8-bit ++ quantities, though, taking care of that problem. But this may be a ++ potential problem on non-POSIX C99 platforms. ++ ++ IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_" ++ as the formal parameter rather than "x". */ ++#define B64(_) \ ++ ((_) == 'A' ? 0 \ ++ : (_) == 'B' ? 1 \ ++ : (_) == 'C' ? 2 \ ++ : (_) == 'D' ? 3 \ ++ : (_) == 'E' ? 4 \ ++ : (_) == 'F' ? 5 \ ++ : (_) == 'G' ? 6 \ ++ : (_) == 'H' ? 7 \ ++ : (_) == 'I' ? 8 \ ++ : (_) == 'J' ? 9 \ ++ : (_) == 'K' ? 10 \ ++ : (_) == 'L' ? 11 \ ++ : (_) == 'M' ? 12 \ ++ : (_) == 'N' ? 13 \ ++ : (_) == 'O' ? 14 \ ++ : (_) == 'P' ? 15 \ ++ : (_) == 'Q' ? 16 \ ++ : (_) == 'R' ? 17 \ ++ : (_) == 'S' ? 18 \ ++ : (_) == 'T' ? 19 \ ++ : (_) == 'U' ? 20 \ ++ : (_) == 'V' ? 21 \ ++ : (_) == 'W' ? 22 \ ++ : (_) == 'X' ? 23 \ ++ : (_) == 'Y' ? 24 \ ++ : (_) == 'Z' ? 25 \ ++ : (_) == 'a' ? 26 \ ++ : (_) == 'b' ? 27 \ ++ : (_) == 'c' ? 28 \ ++ : (_) == 'd' ? 29 \ ++ : (_) == 'e' ? 30 \ ++ : (_) == 'f' ? 31 \ ++ : (_) == 'g' ? 32 \ ++ : (_) == 'h' ? 33 \ ++ : (_) == 'i' ? 34 \ ++ : (_) == 'j' ? 35 \ ++ : (_) == 'k' ? 36 \ ++ : (_) == 'l' ? 37 \ ++ : (_) == 'm' ? 38 \ ++ : (_) == 'n' ? 39 \ ++ : (_) == 'o' ? 40 \ ++ : (_) == 'p' ? 41 \ ++ : (_) == 'q' ? 42 \ ++ : (_) == 'r' ? 43 \ ++ : (_) == 's' ? 44 \ ++ : (_) == 't' ? 45 \ ++ : (_) == 'u' ? 46 \ ++ : (_) == 'v' ? 47 \ ++ : (_) == 'w' ? 48 \ ++ : (_) == 'x' ? 49 \ ++ : (_) == 'y' ? 50 \ ++ : (_) == 'z' ? 51 \ ++ : (_) == '0' ? 52 \ ++ : (_) == '1' ? 53 \ ++ : (_) == '2' ? 54 \ ++ : (_) == '3' ? 55 \ ++ : (_) == '4' ? 56 \ ++ : (_) == '5' ? 57 \ ++ : (_) == '6' ? 58 \ ++ : (_) == '7' ? 59 \ ++ : (_) == '8' ? 60 \ ++ : (_) == '9' ? 61 \ ++ : (_) == '+' ? 62 \ ++ : (_) == '/' ? 63 \ ++ : -1) ++ ++static const signed char b64[0x100] = { ++ B64 (0), B64 (1), B64 (2), B64 (3), ++ B64 (4), B64 (5), B64 (6), B64 (7), ++ B64 (8), B64 (9), B64 (10), B64 (11), ++ B64 (12), B64 (13), B64 (14), B64 (15), ++ B64 (16), B64 (17), B64 (18), B64 (19), ++ B64 (20), B64 (21), B64 (22), B64 (23), ++ B64 (24), B64 (25), B64 (26), B64 (27), ++ B64 (28), B64 (29), B64 (30), B64 (31), ++ B64 (32), B64 (33), B64 (34), B64 (35), ++ B64 (36), B64 (37), B64 (38), B64 (39), ++ B64 (40), B64 (41), B64 (42), B64 (43), ++ B64 (44), B64 (45), B64 (46), B64 (47), ++ B64 (48), B64 (49), B64 (50), B64 (51), ++ B64 (52), B64 (53), B64 (54), B64 (55), ++ B64 (56), B64 (57), B64 (58), B64 (59), ++ B64 (60), B64 (61), B64 (62), B64 (63), ++ B64 (64), B64 (65), B64 (66), B64 (67), ++ B64 (68), B64 (69), B64 (70), B64 (71), ++ B64 (72), B64 (73), B64 (74), B64 (75), ++ B64 (76), B64 (77), B64 (78), B64 (79), ++ B64 (80), B64 (81), B64 (82), B64 (83), ++ B64 (84), B64 (85), B64 (86), B64 (87), ++ B64 (88), B64 (89), B64 (90), B64 (91), ++ B64 (92), B64 (93), B64 (94), B64 (95), ++ B64 (96), B64 (97), B64 (98), B64 (99), ++ B64 (100), B64 (101), B64 (102), B64 (103), ++ B64 (104), B64 (105), B64 (106), B64 (107), ++ B64 (108), B64 (109), B64 (110), B64 (111), ++ B64 (112), B64 (113), B64 (114), B64 (115), ++ B64 (116), B64 (117), B64 (118), B64 (119), ++ B64 (120), B64 (121), B64 (122), B64 (123), ++ B64 (124), B64 (125), B64 (126), B64 (127), ++ B64 (128), B64 (129), B64 (130), B64 (131), ++ B64 (132), B64 (133), B64 (134), B64 (135), ++ B64 (136), B64 (137), B64 (138), B64 (139), ++ B64 (140), B64 (141), B64 (142), B64 (143), ++ B64 (144), B64 (145), B64 (146), B64 (147), ++ B64 (148), B64 (149), B64 (150), B64 (151), ++ B64 (152), B64 (153), B64 (154), B64 (155), ++ B64 (156), B64 (157), B64 (158), B64 (159), ++ B64 (160), B64 (161), B64 (162), B64 (163), ++ B64 (164), B64 (165), B64 (166), B64 (167), ++ B64 (168), B64 (169), B64 (170), B64 (171), ++ B64 (172), B64 (173), B64 (174), B64 (175), ++ B64 (176), B64 (177), B64 (178), B64 (179), ++ B64 (180), B64 (181), B64 (182), B64 (183), ++ B64 (184), B64 (185), B64 (186), B64 (187), ++ B64 (188), B64 (189), B64 (190), B64 (191), ++ B64 (192), B64 (193), B64 (194), B64 (195), ++ B64 (196), B64 (197), B64 (198), B64 (199), ++ B64 (200), B64 (201), B64 (202), B64 (203), ++ B64 (204), B64 (205), B64 (206), B64 (207), ++ B64 (208), B64 (209), B64 (210), B64 (211), ++ B64 (212), B64 (213), B64 (214), B64 (215), ++ B64 (216), B64 (217), B64 (218), B64 (219), ++ B64 (220), B64 (221), B64 (222), B64 (223), ++ B64 (224), B64 (225), B64 (226), B64 (227), ++ B64 (228), B64 (229), B64 (230), B64 (231), ++ B64 (232), B64 (233), B64 (234), B64 (235), ++ B64 (236), B64 (237), B64 (238), B64 (239), ++ B64 (240), B64 (241), B64 (242), B64 (243), ++ B64 (244), B64 (245), B64 (246), B64 (247), ++ B64 (248), B64 (249), B64 (250), B64 (251), ++ B64 (252), B64 (253), B64 (254), B64 (255) ++}; ++ ++#if UCHAR_MAX == 255 ++# define uchar_in_range(c) true ++#else ++# define uchar_in_range(c) ((c) <= 255) ++#endif ++ ++/* Return true if CH is a character from the Base64 alphabet, and ++ false otherwise. Note that '=' is padding and not considered to be ++ part of the alphabet. */ ++bool ++isbase64 (char ch) ++{ ++ return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)]; ++} ++ ++/* Initialize decode-context buffer, CTX. */ ++void ++base64_decode_ctx_init (struct base64_decode_context *ctx) ++{ ++ ctx->i = 0; ++} ++ ++/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and ++ none of those four is a newline, then return *IN. Otherwise, copy up to ++ 4 - CTX->i non-newline bytes from that range into CTX->buf, starting at ++ index CTX->i and setting CTX->i to reflect the number of bytes copied, ++ and return CTX->buf. In either case, advance *IN to point to the byte ++ after the last one processed, and set *N_NON_NEWLINE to the number of ++ verified non-newline bytes accessible through the returned pointer. */ ++static char * ++get_4 (struct base64_decode_context *ctx, ++ char const *restrict *in, char const *restrict in_end, ++ idx_t *n_non_newline) ++{ ++ if (ctx->i == 4) ++ ctx->i = 0; ++ ++ if (ctx->i == 0) ++ { ++ char const *t = *in; ++ if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL) ++ { ++ /* This is the common case: no newline. */ ++ *in += 4; ++ *n_non_newline = 4; ++ return (char *) t; ++ } ++ } ++ ++ { ++ /* Copy non-newline bytes into BUF. */ ++ char const *p = *in; ++ while (p < in_end) ++ { ++ char c = *p++; ++ if (c != '\n') ++ { ++ ctx->buf[ctx->i++] = c; ++ if (ctx->i == 4) ++ break; ++ } ++ } ++ ++ *in = p; ++ *n_non_newline = ctx->i; ++ return ctx->buf; ++ } ++} ++ ++#define return_false \ ++ do \ ++ { \ ++ *outp = out; \ ++ return false; \ ++ } \ ++ while (false) ++ ++/* Decode up to four bytes of base64-encoded data, IN, of length INLEN ++ into the output buffer, *OUT, of size *OUTLEN bytes. Return true if ++ decoding is successful, false otherwise. If *OUTLEN is too small, ++ as many bytes as possible are written to *OUT. On return, advance ++ *OUT to point to the byte after the last one written, and decrement ++ *OUTLEN to reflect the number of bytes remaining in *OUT. */ ++static bool ++decode_4 (char const *restrict in, idx_t inlen, ++ char *restrict *outp, idx_t *outleft) ++{ ++ char *out = *outp; ++ if (inlen < 2) ++ return false; ++ ++ if (!isbase64 (in[0]) || !isbase64 (in[1])) ++ return false; ++ ++ if (*outleft) ++ { ++ *out++ = ((b64[to_uchar (in[0])] << 2) ++ | (b64[to_uchar (in[1])] >> 4)); ++ --*outleft; ++ } ++ ++ if (inlen == 2) ++ return_false; ++ ++ if (in[2] == '=') ++ { ++ if (inlen != 4) ++ return_false; ++ ++ if (in[3] != '=') ++ return_false; ++ } ++ else ++ { ++ if (!isbase64 (in[2])) ++ return_false; ++ ++ if (*outleft) ++ { ++ *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0) ++ | (b64[to_uchar (in[2])] >> 2)); ++ --*outleft; ++ } ++ ++ if (inlen == 3) ++ return_false; ++ ++ if (in[3] == '=') ++ { ++ if (inlen != 4) ++ return_false; ++ } ++ else ++ { ++ if (!isbase64 (in[3])) ++ return_false; ++ ++ if (*outleft) ++ { ++ *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0) ++ | b64[to_uchar (in[3])]); ++ --*outleft; ++ } ++ } ++ } ++ ++ *outp = out; ++ return true; ++} ++ ++/* Decode base64-encoded input array IN of length INLEN to output array ++ OUT that can hold *OUTLEN bytes. The input data may be interspersed ++ with newlines. Return true if decoding was successful, i.e. if the ++ input was valid base64 data, false otherwise. If *OUTLEN is too ++ small, as many bytes as possible will be written to OUT. On return, ++ *OUTLEN holds the length of decoded bytes in OUT. Note that as soon ++ as any non-alphabet, non-newline character is encountered, decoding ++ is stopped and false is returned. If INLEN is zero, then process ++ only whatever data is stored in CTX. ++ ++ Initially, CTX must have been initialized via base64_decode_ctx_init. ++ Subsequent calls to this function must reuse whatever state is recorded ++ in that buffer. It is necessary for when a quadruple of base64 input ++ bytes spans two input buffers. ++ ++ If CTX is NULL then newlines are treated as garbage and the input ++ buffer is processed as a unit. */ ++ ++bool ++base64_decode_ctx (struct base64_decode_context *ctx, ++ const char *restrict in, idx_t inlen, ++ char *restrict out, idx_t *outlen) ++{ ++ idx_t outleft = *outlen; ++ bool ignore_newlines = ctx != NULL; ++ bool flush_ctx = false; ++ unsigned int ctx_i = 0; ++ ++ if (ignore_newlines) ++ { ++ ctx_i = ctx->i; ++ flush_ctx = inlen == 0; ++ } ++ ++ ++ while (true) ++ { ++ idx_t outleft_save = outleft; ++ if (ctx_i == 0 && !flush_ctx) ++ { ++ while (true) ++ { ++ /* Save a copy of outleft, in case we need to re-parse this ++ block of four bytes. */ ++ outleft_save = outleft; ++ if (!decode_4 (in, inlen, &out, &outleft)) ++ break; ++ ++ in += 4; ++ inlen -= 4; ++ } ++ } ++ ++ if (inlen == 0 && !flush_ctx) ++ break; ++ ++ /* Handle the common case of 72-byte wrapped lines. ++ This also handles any other multiple-of-4-byte wrapping. */ ++ if (inlen && *in == '\n' && ignore_newlines) ++ { ++ ++in; ++ --inlen; ++ continue; ++ } ++ ++ /* Restore OUT and OUTLEFT. */ ++ out -= outleft_save - outleft; ++ outleft = outleft_save; ++ ++ { ++ char const *in_end = in + inlen; ++ char const *non_nl; ++ ++ if (ignore_newlines) ++ non_nl = get_4 (ctx, &in, in_end, &inlen); ++ else ++ non_nl = in; /* Might have nl in this case. */ ++ ++ /* If the input is empty or consists solely of newlines (0 non-newlines), ++ then we're done. Likewise if there are fewer than 4 bytes when not ++ flushing context and not treating newlines as garbage. */ ++ if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines)) ++ { ++ inlen = 0; ++ break; ++ } ++ if (!decode_4 (non_nl, inlen, &out, &outleft)) ++ break; ++ ++ inlen = in_end - in; ++ } ++ } ++ ++ *outlen -= outleft; ++ ++ return inlen == 0; ++} ++ ++/* Allocate an output buffer in *OUT, and decode the base64 encoded ++ data stored in IN of size INLEN to the *OUT buffer. On return, the ++ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL, ++ if the caller is not interested in the decoded length. *OUT may be ++ NULL to indicate an out of memory error, in which case *OUTLEN ++ contains the size of the memory block needed. The function returns ++ true on successful decoding and memory allocation errors. (Use the ++ *OUT and *OUTLEN parameters to differentiate between successful ++ decoding and memory error.) The function returns false if the ++ input was invalid, in which case *OUT is NULL and *OUTLEN is ++ undefined. */ ++bool ++base64_decode_alloc_ctx (struct base64_decode_context *ctx, ++ const char *in, idx_t inlen, char **out, ++ idx_t *outlen) ++{ ++ /* This may allocate a few bytes too many, depending on input, ++ but it's not worth the extra CPU time to compute the exact size. ++ The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the ++ input ends with "=" and minus another 1 if the input ends with "==". ++ Shifting before multiplying avoids the possibility of overflow. */ ++ idx_t needlen = 3 * ((inlen >> 2) + 1); ++ ++ *out = imalloc (needlen); ++ if (!*out) ++ return true; ++ ++ if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen)) ++ { ++ free (*out); ++ *out = NULL; ++ return false; ++ } ++ ++ if (outlen) ++ *outlen = needlen; ++ ++ return true; ++} +diff --git a/grub-core/lib/gnulib/base64.h b/grub-core/lib/gnulib/base64.h +new file mode 100644 +index 0000000..6b8b3dc +--- /dev/null ++++ b/grub-core/lib/gnulib/base64.h +@@ -0,0 +1,68 @@ ++/* base64.h -- Encode binary data using printable characters. ++ Copyright (C) 2004-2006, 2009-2022 Free Software Foundation, Inc. ++ Written by Simon Josefsson. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file 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 Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++#ifndef BASE64_H ++# define BASE64_H ++ ++/* Get idx_t. */ ++# include ++ ++/* Get bool. */ ++# include ++ ++# ifdef __cplusplus ++extern "C" { ++# endif ++ ++/* This uses that the expression (n+(k-1))/k means the smallest ++ integer >= n/k, i.e., the ceiling of n/k. */ ++# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4) ++ ++struct base64_decode_context ++{ ++ int i; ++ char buf[4]; ++}; ++ ++extern bool isbase64 (char ch) _GL_ATTRIBUTE_CONST; ++ ++extern void base64_encode (const char *restrict in, idx_t inlen, ++ char *restrict out, idx_t outlen); ++ ++extern idx_t base64_encode_alloc (const char *in, idx_t inlen, char **out); ++ ++extern void base64_decode_ctx_init (struct base64_decode_context *ctx); ++ ++extern bool base64_decode_ctx (struct base64_decode_context *ctx, ++ const char *restrict in, idx_t inlen, ++ char *restrict out, idx_t *outlen); ++ ++extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx, ++ const char *in, idx_t inlen, ++ char **out, idx_t *outlen); ++ ++#define base64_decode(in, inlen, out, outlen) \ ++ base64_decode_ctx (NULL, in, inlen, out, outlen) ++ ++#define base64_decode_alloc(in, inlen, out, outlen) \ ++ base64_decode_alloc_ctx (NULL, in, inlen, out, outlen) ++ ++# ifdef __cplusplus ++} ++# endif ++ ++#endif /* BASE64_H */ +diff --git a/idx.h b/idx.h +new file mode 100644 +index 0000000..c3669dd +--- /dev/null ++++ b/idx.h +@@ -0,0 +1,134 @@ ++/* A type for indices and sizes. ++ Copyright (C) 2020-2022 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _IDX_H ++#define _IDX_H ++ ++/* Get ptrdiff_t. */ ++#include ++ ++/* Get PTRDIFF_MAX. */ ++#include ++ ++/* The type 'idx_t' holds an (array) index or an (object) size. ++ Its implementation promotes to a signed integer type, ++ which can hold the values ++ 0..2^63-1 (on 64-bit platforms) or ++ 0..2^31-1 (on 32-bit platforms). ++ ++ Why a signed integer type? ++ ++ * Security: Signed types can be checked for overflow via ++ '-fsanitize=undefined', but unsigned types cannot. ++ ++ * Comparisons without surprises: ISO C99 § 6.3.1.8 specifies a few ++ surprising results for comparisons, such as ++ ++ (int) -3 < (unsigned long) 7 => false ++ (int) -3 < (unsigned int) 7 => false ++ and on 32-bit machines: ++ (long) -3 < (unsigned int) 7 => false ++ ++ This is surprising because the natural comparison order is by ++ value in the realm of infinite-precision signed integers (ℤ). ++ ++ The best way to get rid of such surprises is to use signed types ++ for numerical integer values, and use unsigned types only for ++ bit masks and enums. ++ ++ Why not use 'size_t' directly? ++ ++ * Because 'size_t' is an unsigned type, and a signed type is better. ++ See above. ++ ++ Why not use 'ssize_t'? ++ ++ * 'ptrdiff_t' is more portable; it is standardized by ISO C ++ whereas 'ssize_t' is standardized only by POSIX. ++ ++ * 'ssize_t' is not required to be as wide as 'size_t', and some ++ now-obsolete POSIX platforms had 'size_t' wider than 'ssize_t'. ++ ++ * Conversely, some now-obsolete platforms had 'ptrdiff_t' wider ++ than 'size_t', which can be a win and conforms to POSIX. ++ ++ Won't this cause a problem with objects larger than PTRDIFF_MAX? ++ ++ * Typical modern or large platforms do not allocate such objects, ++ so this is not much of a problem in practice; for example, you ++ can safely write 'idx_t len = strlen (s);'. To port to older ++ small platforms where allocations larger than PTRDIFF_MAX could ++ in theory be a problem, you can use Gnulib's ialloc module, or ++ functions like ximalloc in Gnulib's xalloc module. ++ ++ Why not use 'ptrdiff_t' directly? ++ ++ * Maintainability: When reading and modifying code, it helps to know that ++ a certain variable cannot have negative values. For example, when you ++ have a loop ++ ++ int n = ...; ++ for (int i = 0; i < n; i++) ... ++ ++ or ++ ++ ptrdiff_t n = ...; ++ for (ptrdiff_t i = 0; i < n; i++) ... ++ ++ you have to ask yourself "what if n < 0?". Whereas in ++ ++ idx_t n = ...; ++ for (idx_t i = 0; i < n; i++) ... ++ ++ you know that this case cannot happen. ++ ++ Similarly, when a programmer writes ++ ++ idx_t = ptr2 - ptr1; ++ ++ there is an implied assertion that ptr1 and ptr2 point into the same ++ object and that ptr1 <= ptr2. ++ ++ * Being future-proof: In the future, range types (integers which are ++ constrained to a certain range of values) may be added to C compilers ++ or to the C standard. Several programming languages (Ada, Haskell, ++ Common Lisp, Pascal) already have range types. Such range types may ++ help producing good code and good warnings. The type 'idx_t' could ++ then be typedef'ed to a range type that is signed after promotion. */ ++ ++/* In the future, idx_t could be typedef'ed to a signed range type. ++ The clang "extended integer types", supported in Clang 11 or newer ++ , ++ are a special case of range types. However, these types don't support binary ++ operators with plain integer types (e.g. expressions such as x > 1). ++ Therefore, they don't behave like signed types (and not like unsigned types ++ either). So, we cannot use them here. */ ++ ++/* Use the signed type 'ptrdiff_t'. */ ++/* Note: ISO C does not mandate that 'size_t' and 'ptrdiff_t' have the same ++ size, but it is so on all platforms we have seen since 1990. */ ++typedef ptrdiff_t idx_t; ++ ++/* IDX_MAX is the maximum value of an idx_t. */ ++#define IDX_MAX PTRDIFF_MAX ++ ++/* So far no need has been found for an IDX_WIDTH macro. ++ Perhaps there should be another macro IDX_VALUE_BITS that does not ++ count the sign bit and is therefore one less than PTRDIFF_WIDTH. */ ++ ++#endif /* _IDX_H */ +-- +2.27.0 + diff --git a/grub.patches b/grub.patches index 3b8ceb9de81524015c5703bb871b474294a4aa86..b482344f0ee5cb0642a7736734052dc149096671 100644 --- a/grub.patches +++ b/grub.patches @@ -464,4 +464,7 @@ Patch0463: backport-commands-acpi-Fix-calculation-of-ACPI-tables-address.patch Patch0464: backport-CVE-2024-1048-grub-set-bootflag-Conservative-partial-fix.patch Patch0465: backport-CVE-2024-1048-grub-set-bootflag-More-complete-fix.patch Patch0466: backport-CVE-2024-1048-grub-set-bootflag-Exit-calmly-when-not.patch - +Patch0467: backport-json-Import-upstream-jsmn-1.1.0 +Patch0468: backport-json-Implement-wrapping-interface.patch +Patch0478: backport-disk-Implement-support-for-LUKS2.patch +Patch0479: build-add-base64-include-file.patch diff --git a/grub2.spec b/grub2.spec index ba094ce1f03cf9165369a94ac0ab2f91f3ae1fef..ce8e3540ec4ec8c7d16b573ae8761f5de111cc60 100644 --- a/grub2.spec +++ b/grub2.spec @@ -8,7 +8,7 @@ Name: grub2 Epoch: 1 Version: 2.04 -Release: 39 +Release: 40 Summary: Bootloader with support for Linux, Multiboot and more License: GPLv3+ URL: http://www.gnu.org/software/grub/ @@ -442,6 +442,12 @@ rm -r /boot/grub2.tmp/ || : %{_datadir}/man/man* %changelog +* Thu Sep 5 2024 Yurii.Huang - 1:2.04-40 +- Type:feature +- CVE:NA +- SUG:NA +- DESC:disk: Implement support for LUKS2 + * Tue Jun 25 2024 zhangqiumiao - 1:2.04-39 - Type:bugfix - CVE:NA