- Resolves: CVE-2024-45779 CVE-2024-45778 CVE-2025-1118 - Resolves: CVE-2025-0677 CVE-2024-45782 CVE-2025-0690 - Resolves: CVE-2024-45783 CVE-2025-0624 CVE-2024-45776 - Resolves: CVE-2025-0622 CVE-2024-45774 CVE-2024-45775 - Resolves: CVE-2024-45781 CVE-2024-45780 - Resolves: #RHEL-79700 - Resolves: #RHEL-79341 - Resolves: #RHEL-79875 - Resolves: #RHEL-79849 - Resolves: #RHEL-79707 - Resolves: #RHEL-79857 - Resolves: #RHEL-79709 - Resolves: #RHEL-79846 - Resolves: #RHEL-75737 - Resolves: #RHEL-79713 - Resolves: #RHEL-73785 - Resolves: #RHEL-73787 - Resolves: #RHEL-79704 - Resolves: #RHEL-79702 Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
604 lines
22 KiB
Diff
604 lines
22 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Maxim Fomin <maxim@fomin.one>
|
|
Date: Wed, 28 Dec 2022 17:20:00 +0000
|
|
Subject: [PATCH] disk/plainmount: Support plain encryption mode
|
|
|
|
This patch adds support for plain encryption mode, plain dm-crypt, via
|
|
new module/command named "plainmount".
|
|
|
|
Signed-off-by: Maxim Fomin <maxim@fomin.one>
|
|
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
|
Reviewed-by: Glenn Washburn <development@efficientek.com>
|
|
---
|
|
docs/grub.texi | 80 ++++++++
|
|
grub-core/Makefile.core.def | 5 +
|
|
grub-core/disk/plainmount.c | 458 ++++++++++++++++++++++++++++++++++++++++++++
|
|
3 files changed, 543 insertions(+)
|
|
create mode 100644 grub-core/disk/plainmount.c
|
|
|
|
diff --git a/docs/grub.texi b/docs/grub.texi
|
|
index 913c3bff3..50c37d6ff 100644
|
|
--- a/docs/grub.texi
|
|
+++ b/docs/grub.texi
|
|
@@ -4052,6 +4052,7 @@ you forget a command, you can run the command @command{help}
|
|
* parttool:: Modify partition table entries
|
|
* password:: Set a clear-text password
|
|
* password_pbkdf2:: Set a hashed password
|
|
+* plainmount:: Open device encrypted in plain mode
|
|
* play:: Play a tune
|
|
* probe:: Retrieve device info
|
|
* rdmsr:: Read values from model-specific registers
|
|
@@ -4344,6 +4345,14 @@ function is supported, as Argon2 is not yet supported.
|
|
|
|
Also, note that, unlike filesystem UUIDs, UUIDs for encrypted devices must be
|
|
specified without dash separators.
|
|
+
|
|
+Successfully decrypted disks are named as (cryptoX) and have increasing numeration
|
|
+suffix for each new decrypted disk. If the encrypted disk hosts some higher level
|
|
+of abstraction (like LVM2 or MDRAID) it will be created under a separate device
|
|
+namespace in addition to the cryptodisk namespace.
|
|
+
|
|
+Support for plain encryption mode (plain dm-crypt) is provided via separate
|
|
+@command{@pxref{plainmount}} command.
|
|
@end deffn
|
|
|
|
@node cutmem
|
|
@@ -4990,6 +4999,77 @@ to generate password hashes. @xref{Security}.
|
|
@end deffn
|
|
|
|
|
|
+@node plainmount
|
|
+@subsection plainmount
|
|
+
|
|
+@deffn Command plainmount device @option{-c} cipher @option{-s} key size [@option{-h} hash]
|
|
+[@option{-S} sector size] [@option{-p} password] [@option{-u} uuid]
|
|
+[[@option{-d} keyfile] [@option{-O} keyfile offset]]
|
|
+
|
|
+
|
|
+Setup access to the encrypted device in plain mode. Offset of the encrypted
|
|
+data at the device is specified in terms of 512 byte sectors using the blocklist
|
|
+syntax and loopback device. The following example shows how to specify 1MiB
|
|
+offset:
|
|
+
|
|
+@example
|
|
+loopback node (hd0,gpt1)2048+
|
|
+plainmount node @var{...}
|
|
+@end example
|
|
+
|
|
+The @command{plainmount} command can be used to open LUKS encrypted volume
|
|
+if its master key and parameters (key size, cipher, offset, etc) are known.
|
|
+
|
|
+There are two ways to specify a password: a keyfile and a secret passphrase.
|
|
+The keyfile path parameter has higher priority than the secret passphrase
|
|
+parameter and is specified with the option @option{-d}. Password data obtained
|
|
+from keyfiles is not hashed and is used directly as a cipher key. An optional
|
|
+offset of password data in the keyfile can be specified with the option
|
|
+@option{-O} or directly with the option @option{-d} and GRUB blocklist syntax,
|
|
+if the keyfile data can be accessed from a device and is 512 byte aligned.
|
|
+The following example shows both methods to specify password data in the
|
|
+keyfile at offset 1MiB:
|
|
+
|
|
+@example
|
|
+plainmount -d (hd0,gpt1)2048+ @var{...}
|
|
+plainmount -d (hd0,gpt1)+ -O 1048576 @var{...}
|
|
+@end example
|
|
+
|
|
+If no keyfile is specified then the password is set to the string specified
|
|
+by option @option{-p} or is requested interactively from the console. In both
|
|
+cases the provided password is hashed with the algorithm specified by the
|
|
+option @option{-h}. This option is mandatory if no keyfile is specified, but
|
|
+it can be set to @samp{plain} which means that no hashing is done and such
|
|
+password is used directly as a key.
|
|
+
|
|
+Cipher @option{-c} and keysize @option{-s} options specify the cipher algorithm
|
|
+and the key size respectively and are mandatory options. Cipher must be specified
|
|
+with the mode separated by a dash (for example, @samp{aes-xts-plain64}). Key size
|
|
+option @option{-s} is the key size of the cipher in bits, not to be confused with
|
|
+the offset of the key data in a keyfile specified with the @option{-O} option. It
|
|
+must not exceed 1024 bits, so a 32 byte key would be specified as 256 bits
|
|
+
|
|
+The optional parameter @option{-S} specifies encrypted device sector size. It
|
|
+must be at least 512 bytes long (default value) and a power of 2. @footnote{Current
|
|
+implementation of cryptsetup supports only 512/1024/2048/4096 byte sectors}.
|
|
+Disk sector size is configured when creating the encrypted volume. Attempting
|
|
+to decrypt volumes with a different sector size than it was created with will
|
|
+not result in an error, but will decrypt to random bytes and thus prevent
|
|
+accessing the volume (in some cases the filesystem driver can detect the presence
|
|
+of a filesystem, but nevertheless will refuse to mount it).
|
|
+
|
|
+By default new plainmount devices will be given a UUID starting with
|
|
+'109fea84-a6b7-34a8-4bd1-1c506305a401' where the last digits are incremented
|
|
+by one for each plainmounted device beyond the first up to 2^10 devices.
|
|
+
|
|
+All encryption arguments (cipher, hash, key size, disk offset and disk sector
|
|
+size) must match the parameters used to create the volume. If any of them does
|
|
+not match the actual arguments used during the initial encryption, plainmount
|
|
+will create virtual device with the garbage data and GRUB will report unknown
|
|
+filesystem for such device.
|
|
+@end deffn
|
|
+
|
|
+
|
|
@node play
|
|
@subsection play
|
|
|
|
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
|
|
index 505c3b63d..b8e29c788 100644
|
|
--- a/grub-core/Makefile.core.def
|
|
+++ b/grub-core/Makefile.core.def
|
|
@@ -1239,6 +1239,11 @@ module = {
|
|
common = disk/cryptodisk.c;
|
|
};
|
|
|
|
+module = {
|
|
+ name = plainmount;
|
|
+ common = disk/plainmount.c;
|
|
+};
|
|
+
|
|
module = {
|
|
name = json;
|
|
common = lib/json/json.c;
|
|
diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c
|
|
new file mode 100644
|
|
index 000000000..47e64805f
|
|
--- /dev/null
|
|
+++ b/grub-core/disk/plainmount.c
|
|
@@ -0,0 +1,458 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2022 Free Software Foundation, Inc.
|
|
+ *
|
|
+ * GRUB is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GRUB is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+/* plaimount.c - Open device encrypted in plain mode. */
|
|
+
|
|
+#include <grub/cryptodisk.h>
|
|
+#include <grub/dl.h>
|
|
+#include <grub/err.h>
|
|
+#include <grub/extcmd.h>
|
|
+#include <grub/partition.h>
|
|
+#include <grub/file.h>
|
|
+
|
|
+GRUB_MOD_LICENSE ("GPLv3+");
|
|
+
|
|
+#define PLAINMOUNT_DEFAULT_SECTOR_SIZE 512
|
|
+#define PLAINMOUNT_DEFAULT_UUID "109fea84-a6b7-34a8-4bd1-1c506305a400"
|
|
+
|
|
+
|
|
+enum PLAINMOUNT_OPTION
|
|
+ {
|
|
+ OPTION_HASH,
|
|
+ OPTION_CIPHER,
|
|
+ OPTION_KEY_SIZE,
|
|
+ OPTION_SECTOR_SIZE,
|
|
+ OPTION_PASSWORD,
|
|
+ OPTION_KEYFILE,
|
|
+ OPTION_KEYFILE_OFFSET,
|
|
+ OPTION_UUID
|
|
+ };
|
|
+
|
|
+static const struct grub_arg_option options[] =
|
|
+ {
|
|
+ /* TRANSLATORS: It's still restricted to this module only. */
|
|
+ {"hash", 'h', 0, N_("Password hash"), 0, ARG_TYPE_STRING},
|
|
+ {"cipher", 'c', 0, N_("Password cipher"), 0, ARG_TYPE_STRING},
|
|
+ {"key-size", 's', 0, N_("Key size (in bits)"), 0, ARG_TYPE_INT},
|
|
+ {"sector-size", 'S', 0, N_("Device sector size"), 0, ARG_TYPE_INT},
|
|
+ {"password", 'p', 0, N_("Password (key)"), 0, ARG_TYPE_STRING},
|
|
+ {"keyfile", 'd', 0, N_("Keyfile path"), 0, ARG_TYPE_STRING},
|
|
+ {"keyfile-offset", 'O', 0, N_("Keyfile offset"), 0, ARG_TYPE_INT},
|
|
+ {"uuid", 'u', 0, N_("Set device UUID"), 0, ARG_TYPE_STRING},
|
|
+ {0, 0, 0, 0, 0, 0}
|
|
+ };
|
|
+
|
|
+/* Cryptodisk setkey() function wrapper */
|
|
+static grub_err_t
|
|
+plainmount_setkey (grub_cryptodisk_t dev, grub_uint8_t *key,
|
|
+ grub_size_t size)
|
|
+{
|
|
+ gcry_err_code_t code = grub_cryptodisk_setkey (dev, key, size);
|
|
+ if (code != GPG_ERR_NO_ERROR)
|
|
+ {
|
|
+ grub_dprintf ("plainmount", "failed to set cipher key with code: %d\n", code);
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot set specified key"));
|
|
+ }
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/* Configure cryptodisk uuid */
|
|
+static void plainmount_set_uuid (grub_cryptodisk_t dev, const char *user_uuid)
|
|
+{
|
|
+ grub_size_t pos = 0;
|
|
+
|
|
+ /* Size of user_uuid is checked in main func */
|
|
+ if (user_uuid != NULL)
|
|
+ grub_strcpy (dev->uuid, user_uuid);
|
|
+ else
|
|
+ {
|
|
+ /*
|
|
+ * Set default UUID. Last digits start from 1 and are incremented for
|
|
+ * each new plainmount device by snprintf().
|
|
+ */
|
|
+ grub_snprintf (dev->uuid, sizeof (dev->uuid) - 1, "%36lx", dev->id + 1);
|
|
+ while (dev->uuid[++pos] == ' ');
|
|
+ grub_memcpy (dev->uuid, PLAINMOUNT_DEFAULT_UUID, pos);
|
|
+ }
|
|
+ COMPILE_TIME_ASSERT (sizeof (dev->uuid) >= sizeof (PLAINMOUNT_DEFAULT_UUID));
|
|
+}
|
|
+
|
|
+/* Configure cryptodevice sector size (-S option) */
|
|
+static grub_err_t
|
|
+plainmount_configure_sectors (grub_cryptodisk_t dev, grub_disk_t disk,
|
|
+ grub_size_t sector_size)
|
|
+{
|
|
+ dev->total_sectors = grub_disk_native_sectors (disk);
|
|
+ if (dev->total_sectors == GRUB_DISK_SIZE_UNKNOWN)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot determine disk %s size"),
|
|
+ disk->name);
|
|
+
|
|
+ /* Convert size to sectors */
|
|
+ dev->log_sector_size = grub_log2ull (sector_size);
|
|
+ dev->total_sectors = grub_convert_sector (dev->total_sectors,
|
|
+ GRUB_DISK_SECTOR_BITS,
|
|
+ dev->log_sector_size);
|
|
+ if (dev->total_sectors == 0)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE,
|
|
+ N_("cannot set specified sector size on disk %s"),
|
|
+ disk->name);
|
|
+
|
|
+ grub_dprintf ("plainmount", "log_sector_size=%d, total_sectors=%"
|
|
+ PRIuGRUB_UINT64_T"\n", dev->log_sector_size, dev->total_sectors);
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/* Hashes a password into a key and stores it with the cipher. */
|
|
+static grub_err_t
|
|
+plainmount_configure_password (grub_cryptodisk_t dev, const char *hash,
|
|
+ grub_uint8_t *key_data, grub_size_t key_size,
|
|
+ grub_size_t password_size)
|
|
+{
|
|
+ grub_uint8_t *derived_hash, *dh;
|
|
+ char *p;
|
|
+ unsigned int round, i, len, size;
|
|
+ grub_size_t alloc_size;
|
|
+ grub_err_t err = GRUB_ERR_NONE;
|
|
+
|
|
+ /* Support none (plain) hash */
|
|
+ if (grub_strcmp (hash, "plain") == 0)
|
|
+ {
|
|
+ dev->hash = NULL;
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* Hash argument was checked at main func */
|
|
+ dev->hash = grub_crypto_lookup_md_by_name (hash);
|
|
+ len = dev->hash->mdlen;
|
|
+
|
|
+ alloc_size = grub_max (password_size, key_size);
|
|
+ /*
|
|
+ * Allocate buffer for the password and for an added prefix character
|
|
+ * for each hash round ('alloc_size' may not be a multiple of 'len').
|
|
+ */
|
|
+ p = grub_zalloc (alloc_size + (alloc_size / len) + 1);
|
|
+ derived_hash = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN * 2);
|
|
+ if (p == NULL || derived_hash == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
|
|
+ goto fail;
|
|
+ }
|
|
+ dh = derived_hash;
|
|
+
|
|
+ /*
|
|
+ * Hash password. Adapted from cryptsetup.
|
|
+ * https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/lib/crypt_plain.c
|
|
+ */
|
|
+ for (round = 0, size = alloc_size; size; round++, dh += len, size -= len)
|
|
+ {
|
|
+ for (i = 0; i < round; i++)
|
|
+ p[i] = 'A';
|
|
+
|
|
+ grub_memcpy (p + i, (char*) key_data, password_size);
|
|
+
|
|
+ if (len > size)
|
|
+ len = size;
|
|
+
|
|
+ grub_crypto_hash (dev->hash, dh, p, password_size + round);
|
|
+ }
|
|
+ grub_memcpy (key_data, derived_hash, key_size);
|
|
+
|
|
+ fail:
|
|
+ grub_free (p);
|
|
+ grub_free (derived_hash);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Read key material from keyfile */
|
|
+static grub_err_t
|
|
+plainmount_configure_keyfile (char *keyfile, grub_uint8_t *key_data,
|
|
+ grub_size_t key_size, grub_size_t keyfile_offset)
|
|
+{
|
|
+ grub_file_t g_keyfile = grub_file_open (keyfile, GRUB_FILE_TYPE_NONE);
|
|
+ if (g_keyfile == NULL)
|
|
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("cannot open keyfile %s"),
|
|
+ keyfile);
|
|
+
|
|
+ if (grub_file_seek (g_keyfile, keyfile_offset) == (grub_off_t) - 1)
|
|
+ return grub_error (GRUB_ERR_FILE_READ_ERROR,
|
|
+ N_("cannot seek keyfile at offset %"PRIuGRUB_SIZE),
|
|
+ keyfile_offset);
|
|
+
|
|
+ if (key_size > (g_keyfile->size - keyfile_offset))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Specified key size (%"
|
|
+ PRIuGRUB_SIZE") is too small for keyfile size (%"
|
|
+ PRIuGRUB_UINT64_T") and offset (%"PRIuGRUB_SIZE")"),
|
|
+ key_size, g_keyfile->size, keyfile_offset);
|
|
+
|
|
+ if (grub_file_read (g_keyfile, key_data, key_size) != (grub_ssize_t) key_size)
|
|
+ return grub_error (GRUB_ERR_FILE_READ_ERROR, N_("error reading key file"));
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/* Plainmount command entry point */
|
|
+static grub_err_t
|
|
+grub_cmd_plainmount (grub_extcmd_context_t ctxt, int argc, char **args)
|
|
+{
|
|
+ struct grub_arg_list *state = ctxt->state;
|
|
+ grub_cryptodisk_t dev = NULL;
|
|
+ grub_disk_t disk = NULL;
|
|
+ const gcry_md_spec_t *gcry_hash;
|
|
+ char *diskname, *disklast = NULL, *cipher, *mode, *hash, *keyfile, *uuid;
|
|
+ grub_size_t len, key_size, sector_size, keyfile_offset = 0, password_size = 0;
|
|
+ grub_err_t err;
|
|
+ const char *p;
|
|
+ grub_uint8_t *key_data;
|
|
+
|
|
+ if (argc < 1)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("device name required"));
|
|
+
|
|
+ /* Check whether required arguments are specified */
|
|
+ if (!state[OPTION_CIPHER].set || !state[OPTION_KEY_SIZE].set)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "cipher and key size must be set");
|
|
+ if (!state[OPTION_HASH].set && !state[OPTION_KEYFILE].set)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "hash algorithm must be set");
|
|
+
|
|
+ /* Check hash */
|
|
+ if (!state[OPTION_KEYFILE].set)
|
|
+ {
|
|
+ gcry_hash = grub_crypto_lookup_md_by_name (state[OPTION_HASH].arg);
|
|
+ if (!gcry_hash)
|
|
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("couldn't load hash %s"),
|
|
+ state[OPTION_HASH].arg);
|
|
+
|
|
+ if (gcry_hash->mdlen > GRUB_CRYPTODISK_MAX_KEYLEN)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("hash length %"PRIuGRUB_SIZE" exceeds maximum %d bits"),
|
|
+ gcry_hash->mdlen * GRUB_CHAR_BIT,
|
|
+ GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT);
|
|
+ }
|
|
+
|
|
+ /* Check cipher mode */
|
|
+ if (!grub_strchr (state[OPTION_CIPHER].arg,'-'))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("invalid cipher mode, must be of format cipher-mode"));
|
|
+
|
|
+ /* Check password size */
|
|
+ if (state[OPTION_PASSWORD].set && grub_strlen (state[OPTION_PASSWORD].arg) >
|
|
+ GRUB_CRYPTODISK_MAX_PASSPHRASE)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("password exceeds maximium size"));
|
|
+
|
|
+ /* Check uuid length */
|
|
+ if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) >
|
|
+ GRUB_CRYPTODISK_MAX_UUID_LENGTH - 1)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("specified UUID exceeds maximum size"));
|
|
+ if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) == 1)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("specified UUID too short"));
|
|
+
|
|
+ /* Parse plainmount arguments */
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ keyfile_offset = state[OPTION_KEYFILE_OFFSET].set ?
|
|
+ grub_strtoull (state[OPTION_KEYFILE_OFFSET].arg, &p, 0) : 0;
|
|
+ if (state[OPTION_KEYFILE_OFFSET].set &&
|
|
+ (state[OPTION_KEYFILE_OFFSET].arg[0] == '\0' || *p != '\0' ||
|
|
+ grub_errno != GRUB_ERR_NONE))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized keyfile offset"));
|
|
+
|
|
+ sector_size = state[OPTION_SECTOR_SIZE].set ?
|
|
+ grub_strtoull (state[OPTION_SECTOR_SIZE].arg, &p, 0) :
|
|
+ PLAINMOUNT_DEFAULT_SECTOR_SIZE;
|
|
+ if (state[OPTION_SECTOR_SIZE].set && (state[OPTION_SECTOR_SIZE].arg[0] == '\0' ||
|
|
+ *p != '\0' || grub_errno != GRUB_ERR_NONE))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized sector size"));
|
|
+
|
|
+ /* Check key size */
|
|
+ key_size = grub_strtoull (state[OPTION_KEY_SIZE].arg, &p, 0);
|
|
+ if (state[OPTION_KEY_SIZE].arg[0] == '\0' || *p != '\0' ||
|
|
+ grub_errno != GRUB_ERR_NONE)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized key size"));
|
|
+ if ((key_size % GRUB_CHAR_BIT) != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("key size is not multiple of %d bits"), GRUB_CHAR_BIT);
|
|
+ key_size = key_size / GRUB_CHAR_BIT;
|
|
+ if (key_size > GRUB_CRYPTODISK_MAX_KEYLEN)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("key size %"PRIuGRUB_SIZE" exceeds maximum %d bits"),
|
|
+ key_size * GRUB_CHAR_BIT,
|
|
+ GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT);
|
|
+
|
|
+ /* Check disk sector size */
|
|
+ if (sector_size < GRUB_DISK_SECTOR_SIZE)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("sector size -S must be at least %d"),
|
|
+ GRUB_DISK_SECTOR_SIZE);
|
|
+ if ((sector_size & (sector_size - 1)) != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("sector size -S %"PRIuGRUB_SIZE" is not power of 2"),
|
|
+ sector_size);
|
|
+
|
|
+ /* Allocate all stuff here */
|
|
+ hash = state[OPTION_HASH].set ? grub_strdup (state[OPTION_HASH].arg) : NULL;
|
|
+ cipher = grub_strdup (state[OPTION_CIPHER].arg);
|
|
+ keyfile = state[OPTION_KEYFILE].set ?
|
|
+ grub_strdup (state[OPTION_KEYFILE].arg) : NULL;
|
|
+ dev = grub_zalloc (sizeof *dev);
|
|
+ key_data = grub_zalloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
|
|
+ uuid = state[OPTION_UUID].set ? grub_strdup (state[OPTION_UUID].arg) : NULL;
|
|
+ if ((state[OPTION_HASH].set && hash == NULL) || cipher == NULL || dev == NULL ||
|
|
+ (state[OPTION_KEYFILE].set && keyfile == NULL) || key_data == NULL ||
|
|
+ (state[OPTION_UUID].set && uuid == NULL))
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ /* Copy user password from -p option */
|
|
+ if (state[OPTION_PASSWORD].set)
|
|
+ {
|
|
+ /*
|
|
+ * Password from the '-p' option is limited to C-string.
|
|
+ * Arbitrary data keys are supported via keyfiles.
|
|
+ */
|
|
+ password_size = grub_strlen (state[OPTION_PASSWORD].arg);
|
|
+ grub_strcpy ((char*) key_data, state[OPTION_PASSWORD].arg);
|
|
+ }
|
|
+
|
|
+ /* Set cipher mode (tested above) */
|
|
+ mode = grub_strchr (cipher,'-');
|
|
+ *mode++ = '\0';
|
|
+
|
|
+ /* Check cipher */
|
|
+ err = grub_cryptodisk_setcipher (dev, cipher, mode);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ {
|
|
+ if (err == GRUB_ERR_FILE_NOT_FOUND)
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s"), cipher);
|
|
+ else if (err == GRUB_ERR_BAD_ARGUMENT)
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid mode %s"), mode);
|
|
+ else
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s or mode %s"),
|
|
+ cipher, mode);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ /* Open SOURCE disk */
|
|
+ diskname = args[0];
|
|
+ len = grub_strlen (diskname);
|
|
+ if (len && diskname[0] == '(' && diskname[len - 1] == ')')
|
|
+ {
|
|
+ disklast = &diskname[len - 1];
|
|
+ *disklast = '\0';
|
|
+ diskname++;
|
|
+ }
|
|
+ disk = grub_disk_open (diskname);
|
|
+ if (disk == NULL)
|
|
+ {
|
|
+ if (disklast)
|
|
+ *disklast = ')';
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot open disk %s"), diskname);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ /* Get password from console */
|
|
+ if (!state[OPTION_KEYFILE].set && key_data[0] == '\0')
|
|
+ {
|
|
+ char *part = grub_partition_get_name (disk->partition);
|
|
+ grub_printf_ (N_("Enter passphrase for %s%s%s: "), disk->name,
|
|
+ disk->partition != NULL ? "," : "",
|
|
+ part != NULL ? part : N_("UNKNOWN"));
|
|
+ grub_free (part);
|
|
+
|
|
+ if (!grub_password_get ((char*) key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE - 1))
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("error reading password"));
|
|
+ goto fail;
|
|
+ }
|
|
+ /*
|
|
+ * Password from interactive console is limited to C-string.
|
|
+ * Arbitrary data keys are supported via keyfiles.
|
|
+ */
|
|
+ password_size = grub_strlen ((char*) key_data);
|
|
+ }
|
|
+
|
|
+ /* Warn if hash and keyfile are both provided */
|
|
+ if (state[OPTION_KEYFILE].set && state[OPTION_HASH].arg)
|
|
+ grub_printf_ (N_("warning: hash is ignored if keyfile is specified\n"));
|
|
+
|
|
+ /* Warn if -p option is specified with keyfile */
|
|
+ if (state[OPTION_PASSWORD].set && state[OPTION_KEYFILE].set)
|
|
+ grub_printf_ (N_("warning: password specified with -p option "
|
|
+ "is ignored if keyfile is provided\n"));
|
|
+
|
|
+ /* Warn of -O is provided without keyfile */
|
|
+ if (state[OPTION_KEYFILE_OFFSET].set && !state[OPTION_KEYFILE].set)
|
|
+ grub_printf_ (N_("warning: keyfile offset option -O "
|
|
+ "specified without keyfile option -d\n"));
|
|
+
|
|
+ grub_dprintf ("plainmount", "parameters: cipher=%s, hash=%s, key_size=%"
|
|
+ PRIuGRUB_SIZE ", keyfile=%s, keyfile offset=%" PRIuGRUB_SIZE "\n",
|
|
+ cipher, hash, key_size, keyfile, keyfile_offset);
|
|
+
|
|
+ err = plainmount_configure_sectors (dev, disk, sector_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto fail;
|
|
+
|
|
+ /* Configure keyfile or password */
|
|
+ if (state[OPTION_KEYFILE].set)
|
|
+ err = plainmount_configure_keyfile (keyfile, key_data, key_size, keyfile_offset);
|
|
+ else
|
|
+ err = plainmount_configure_password (dev, hash, key_data, key_size, password_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto fail;
|
|
+
|
|
+ err = plainmount_setkey (dev, key_data, key_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto fail;
|
|
+
|
|
+ err = grub_cryptodisk_insert (dev, diskname, disk);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto fail;
|
|
+
|
|
+ dev->modname = "plainmount";
|
|
+ dev->source_disk = disk;
|
|
+ plainmount_set_uuid (dev, uuid);
|
|
+
|
|
+ fail:
|
|
+ grub_free (hash);
|
|
+ grub_free (cipher);
|
|
+ grub_free (keyfile);
|
|
+ grub_free (key_data);
|
|
+ grub_free (uuid);
|
|
+ if (err != GRUB_ERR_NONE && disk != NULL)
|
|
+ grub_disk_close (disk);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ grub_free (dev);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_extcmd_t cmd;
|
|
+GRUB_MOD_INIT (plainmount)
|
|
+{
|
|
+ cmd = grub_register_extcmd ("plainmount", grub_cmd_plainmount, 0,
|
|
+ N_("-c cipher -s key-size [-h hash] [-S sector-size]"
|
|
+ " [-o offset] [-p password] [-u uuid] "
|
|
+ " [[-d keyfile] [-O keyfile offset]] <SOURCE>"),
|
|
+ N_("Open partition encrypted in plain mode."),
|
|
+ options);
|
|
+}
|
|
+
|
|
+GRUB_MOD_FINI (plainmount)
|
|
+{
|
|
+ grub_unregister_extcmd (cmd);
|
|
+}
|