[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH 1/2] plainmount: Support decryption of devices encrypted in p
From: |
Glenn Washburn |
Subject: |
Re: [PATCH 1/2] plainmount: Support decryption of devices encrypted in plain mode. |
Date: |
Mon, 31 Jan 2022 20:30:38 -0600 |
On Sun, 30 Jan 2022 19:40:43 +0000
Maxim Fomin <maxim@fomin.one> wrote:
> This patch introduces support for plain encryption mode (plain dm-crypt) via
> new module and command named 'plainmount'. The command allows to open devices
> encrypted in plain mode (without LUKS) with following syntax:
>
> plainmount <SOURCE> -h hash -c cipher -o offset -s size -k key-size
> -z sector-size -d keyfile -O keyfile offset -l keyfile-size
>
> <SOURCE> can be some partition or GPT UUID, keyfile can be <UUID>/some/path.
I'm not in favor of the keyfile UUID prefix for paths. Why not just use
the normal device syntax?
> Despite moving plain mode code into separate module, it still depends on
> cryptodisk.c because cryptodisk module defines whole grub infrastructure
> of encrypted devices.
>
> Principal feature of this patch is supporting GPT partition UUID to point to
> specific partition. The patch also includes two other minor features: using
> disk block as a key (instead of keyfile) and support for 1024/2048/4096 byte
> blocks in plain mode (cryptsetup added large block support in plain mode after
> introducing them in LUKS2 mode - since circa version 2.4).
>
> This is fully reworked version of '0004-Cryptomount-support-plain-dm-crypt'
> patch which is member of John Lane crypto enhancement patch series. It also
> includes '0007-Add-support-for-using-a-whole-device-as-a-keyfile' patch. They
> were sent in grub-devel mailing list as:
>
> From a8f9e3dcece89c179e89414abe89985c7ab1e03f Mon Sep 17 00:00:00 2001
> From: John Lane <john@lane.uk.net>
> Date: Fri, 26 Jun 2015 22:09:52 +0100
> Subject: [PATCH 4/7] Cryptomount support plain dm-crypt
>
> From ef720d0d44b8d97a83950ced0df1ce1bcf8cd988 Mon Sep 17 00:00:00 2001
> From: Paul Gideon Dann <pdgiddie@gmail.com>
> Date: Tue, 19 Jul 2016 12:36:37 +0100
> Subject: [PATCH 7/7] Add support for using a whole device as a keyfile
> ---
> grub-core/Makefile.core.def | 5 +
> grub-core/disk/plainmount.c | 678 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 683 insertions(+)
> create mode 100644 grub-core/disk/plainmount.c
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 8022e1c0a..ce8478055 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -1184,6 +1184,11 @@ module = {
> common = disk/luks.c;
> };
>
> +module = {
> + name = plainmount;
> + common = disk/plainmount.c;
> +};
> +
> module = {
> name = luks2;
> common = disk/luks2.c;
> diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c
> new file mode 100644
> index 000000000..4654cec96
> --- /dev/null
> +++ b/grub-core/disk/plainmount.c
> @@ -0,0 +1,678 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2003,2007,2010,2011,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 <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/cryptodisk.h>
> +#include <grub/dl.h>
> +#include <grub/err.h>
> +#include <grub/extcmd.h>
> +#include <grub/partition.h>
> +#include <grub/file.h>
> +#include <grub/gpt_partition.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +#define GRUB_PLAINMOUNT_UUID "00000000000000000000000000000000"
> +#define GRUB_PLAINMOUNT_CIPHER "aes-cbc-essiv:sha256"
> +#define GRUB_PLAINMOUNT_DIGEST "ripemd160"
> +#define GRUB_PLAINMOUNT_KEY_SIZE 256
> +#define GRUB_PLAINMOUNT_SECTOR_SIZE 512
> +
> +static const struct grub_arg_option options[] =
> + {
> + /* TRANSLATORS: It's still restricted to this module only. */
It would be nice to allow specifying a password as an argument (-p)
like in cryptomount for consistency. It'll allow tests to no need to
write a keyfile also.
> + {"hash", 'h', 0, N_("Password hash."), 0, ARG_TYPE_STRING},
> + {"cipher", 'c', 0, N_("Password cipher."), 0, ARG_TYPE_STRING},
> + {"offset", 'o', 0, N_("Device offset (512 bit blocks)."), 0,
> ARG_TYPE_INT},
s/bit/byte/
And why 512-byte blocks? The LUKS2 header stores offset as bytes,
perhaps bytes should be used here too.
> + {"size", 'b', 0, N_("Size of device (512 byte blocks)."), 0,
> ARG_TYPE_INT},
Same as above.
> + {"key-size", 's', 0, N_("Key size (in bits)."), 0, ARG_TYPE_INT},
> + {"sector-size", 'z', 0, N_("Device sector size."), 0, ARG_TYPE_INT},
'S' instead of 'z' makes more sense to me.
> + {"keyfile", 'd', 0, N_("Keyfile/disk path."), 0, ARG_TYPE_STRING},
> + {"keyfile-offset", 'O', 0, N_("Keyfile offset (512 bit blocks)."), 0,
> ARG_TYPE_INT},
s/bit/byte/
> + {"keyfile-size", 'l', 0, N_("Keyfile data size (in bits)."), 0,
> ARG_TYPE_INT},
> + {0, 0, 0, 0, 0, 0}
> + };
> +
> +struct grub_plainmount_args
> +{
> + char *key_data, *cipher, *mode, *hash, *keyfile;
> + grub_size_t offset, size, key_size, sector_size, keyfile_offset,
> keyfile_size;
> + grub_disk_t disk;
> +};
> +typedef struct grub_plainmount_args *grub_plainmount_args_t;
> +
> +struct grub_plainmount_iterate_args
> +{
> + char *uuid, *diskname;
> +};
> +
> +
> +/* Disk iterate callback */
> +static int grub_plainmount_scan_real (const char *name, void *data)
> +{
> + int ret = 0;
> + struct grub_plainmount_iterate_args *args = data;
> + grub_disk_t source = NULL, disk = NULL;
> + struct grub_partition *partition;
> + struct grub_gpt_partentry entry;
> + grub_gpt_part_guid_t *guid;
> + /* UUID format: AAAABBBB-CCCC-DDDD-EEEE-FFFFFFFFFFFF + '\0' */
> + char uuid[37] = "";
> +
> + source = grub_disk_open (name);
> + if (!source)
> + goto exit;
> + if (!source->partition)
> + goto exit;
> + partition = source->partition;
> + if (grub_strcmp (partition->partmap->name, "gpt") != 0)
> + goto exit;
As noted elsewhere, I'm not in favor of these checks, nor the forcing
of the volume to be on a partition.
> + disk = grub_disk_open (source->name);
> + if (!disk)
> + goto exit;
> + if (grub_disk_read (disk, partition->offset, partition->index,
> + sizeof(entry), &entry) != GRUB_ERR_NONE)
> + goto exit;
> + guid = &entry.guid;
> + grub_snprintf (uuid, sizeof(uuid),
> + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
> + grub_le_to_cpu32 (guid->data1),
> + grub_le_to_cpu16 (guid->data2),
> + grub_le_to_cpu16 (guid->data3),
> + guid->data4[0], guid->data4[1], guid->data4[2],
> + guid->data4[3], guid->data4[4], guid->data4[5],
> + guid->data4[6], guid->data4[7]);
> + if (grub_strcasecmp (args->uuid, uuid) == 0)
> + {
> + args->diskname = grub_strdup (name);
> + ret = 1;
> + }
> +
> +exit:
> + if (source)
> + grub_disk_close (source);
> + if (disk)
> + grub_disk_close (disk);
> + return ret;
> +}
> +
> +
> +/* Get partition name from UUID */
> +static char* plainmount_get_diskname_from_uuid (char *uuid)
> +{
> + struct grub_plainmount_iterate_args args = {uuid, NULL};
> + if (grub_device_iterate (&grub_plainmount_scan_real, &args) == 1
> + && args.diskname)
> + return args.diskname;
> + else
> + return NULL;
> +}
> +
> +
> +/* Support use case: -d <UUID>/dir/keyfile */
> +static char*
> +plainmount_uuid_path_to_disk_path (char *uuid_path)
> +{
> + char *slash = grub_strchr (uuid_path, '/');
> + if (slash)
> + {
> + *slash = '\0';
> + char *diskname = plainmount_get_diskname_from_uuid (uuid_path);
> + if (!diskname)
> + {
> + *slash = '/';
> + return NULL;
> + }
> +
> + /* "(" + diskname + ")/" + path_after_first_slash + '\0' */
> + int str_size = grub_strlen ("(") +
> + grub_strlen (diskname) +
> + grub_strlen (")/") +
> + grub_strlen (slash+1) + 1; /* "some/path" */
> + char *new_diskname = grub_zalloc (str_size);
> + if (!new_diskname)
> + {
> + grub_free (diskname);
> + *slash = '/';
> + return NULL;
> + }
> + grub_snprintf (new_diskname, str_size, "(%s)/%s", diskname, slash+1);
> + *slash = '/';
> + return new_diskname;
> + }
> + else
> + return plainmount_get_diskname_from_uuid (uuid_path);
> +}
> +
> +
> +/* Configure cryptodevice sector size (-z option), default - 512 byte */
> +static grub_err_t
> +plainmount_configure_sectors (grub_cryptodisk_t dev, grub_plainmount_args_t
> cargs)
> +{
> + grub_disk_addr_t total_sectors;
> +
> + /* Check whether disk can be accessed */
> + if (!cargs->size &&
> + grub_disk_native_sectors (cargs->disk) == GRUB_DISK_SIZE_UNKNOWN)
> + return grub_error (GRUB_ERR_BAD_DEVICE,
> + N_("cannot determine disk %s size"),
> + cargs->disk->name);
> +
> + /* cryptsetup allows only 512/1024/2048/4096 byte sectors */
I prefer using code like in luks2.c, something like (without the bad
formatting):
/* Sector size should be one of 512, 1024, 2048, or 4096. */
if (!(cargs->sector_size == 512 || cargs->sector_size == 1024 ||
cargs->sector_size == 2048 || cargs->sector_size == 4096))
{
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("invalid sector size -z %"PRIuGRUB_SIZE
", only 512/1024/2048/4096 are allowed"),
cargs->sector_size);
}
dev->log_sector_size = grub_log2ull(cargs->sector_size);
> + switch (cargs->sector_size)
> + {
> + case 512:
> + dev->log_sector_size = 9;
> + break;
> + case 1024:
> + dev->log_sector_size = 10;
> + break;
> + case 2048:
> + dev->log_sector_size = 11;
> + break;
> + case 4096:
> + dev->log_sector_size = 12;
> + break;
> + default:
> + grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("invalid sector size -z %"PRIuGRUB_SIZE
> + ", only 512/1024/2048/4096 are allowed"),
> + cargs->sector_size);
> + grub_print_error ();
> + return GRUB_ERR_BAD_ARGUMENT;
Should just set the error and return. Further up the call stack the
error should be printed. So do something like
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("invalid sector size -z %"PRIuGRUB_SIZE
", only 512/1024/2048/4096 are allowed"),
cargs->sector_size);
> + }
> +
> + /* Offset is always given in terms of number of 512 byte sectors. */
> + dev->offset_sectors = grub_divmod64 (cargs->offset*512,
512 should be a constant, assuming we still want to have input in
512-byte sectors.
> + cargs->sector_size, NULL);
> +
> + if (cargs->size)
> + total_sectors = cargs->size;
> + else
> + total_sectors = grub_disk_native_sectors (cargs->disk);
> +
> + /* Calculate disk sectors in terms of log_sector_size */
> + total_sectors = grub_convert_sector (total_sectors, GRUB_DISK_SECTOR_BITS,
> + dev->log_sector_size);
> + dev->total_sectors = total_sectors - dev->offset_sectors;
> + grub_dprintf ("plainmount", "log_sector_size=%d,
> total_sectors=%"PRIuGRUB_SIZE
> + ", offset_sectors=%"PRIuGRUB_SIZE"\n", dev->log_sector_size,
> + dev->total_sectors, dev->offset_sectors);
> + return GRUB_ERR_NONE;
> +}
> +
> +
> +/* Hashes a password into a key and stores it with cipher. */
> +static grub_err_t
> +plainmount_configure_password (grub_cryptodisk_t dev, grub_plainmount_args_t
> cargs)
> +{
> + const gcry_md_spec_t *hash = NULL;
> + grub_uint8_t derived_hash[GRUB_CRYPTODISK_MAX_KEYLEN * 2], *dh =
> derived_hash;
> + char *p;
> + unsigned int round, i;
> + unsigned int len, size;
> + char *part = NULL;
> + gcry_err_code_t code;
> +
> + /* Check hash */
> + hash = grub_crypto_lookup_md_by_name (cargs->hash);
> + if (!hash)
> + return grub_error (GRUB_ERR_FILE_NOT_FOUND,
> + N_("couldn't load %s hash (perhaps a typo?)"),
> + cargs->hash);
> +
> + /* Check key size */
> + if (cargs->key_size > GRUB_CRYPTODISK_MAX_KEYLEN ||
> + hash->mdlen > GRUB_CRYPTODISK_MAX_KEYLEN)
Should this check for the message digest length of the hash function be
a different error? And why is it needed anyway?
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("invalid key size %"PRIuGRUB_SIZE
> + " (exceeds maximum %d bits)"),
> + cargs->key_size, GRUB_CRYPTODISK_MAX_KEYLEN *
> 8);
> + dev->hash = hash;
> +
> + grub_disk_t source = cargs->disk;
> + part = grub_partition_get_name (source->partition);
> + grub_printf_ (N_("Enter passphrase for %s%s%s: "), source->name,
> + source->partition != NULL ? "," : "",
> + part != NULL ? part : N_("UNKNOWN"));
> + grub_free (part);
> +
> + if (!grub_password_get (cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE))
> + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("password not supplied"));
It would be nice if this was refactored to look like the recent changes
to cryptodisk (ie. not asking for password here, but passing in the
password/keyfile data).
> +
> + /* Hack to support the "none" hash */
> + if (dev->hash)
> + len = dev->hash->mdlen;
> + else
> + len = cargs->key_size;
> +
> + p = grub_malloc (cargs->key_size + 2 + cargs->key_size / len);
> + if (!p)
> + return GRUB_ERR_OUT_OF_MEMORY;
> +
> + for (round = 0, size = cargs->key_size; size; round++, dh += len, size -=
> len)
> + {
> + for (i = 0; i < round; i++)
> + p[i] = 'A';
> +
> + grub_strcpy (p + i, cargs->key_data);
> +
> + if (len > size)
> + len = size;
> +
> + grub_crypto_hash (dev->hash, dh, p, grub_strlen (p));
> + }
> + grub_free (p);
> + code = grub_cryptodisk_setkey (dev, derived_hash, cargs->key_size);
> + grub_dprintf ("plainmount", "password crypto status is %d\n", code);
This should be done in the if block below, no one cares if the code is
success.
> + if (code != GPG_ERR_NO_ERROR)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("cannot set key from password. "
> + "Check keysize/hash/cipher options."));
I don't like that the GPG error is getting swallowed. Perhaps reutrn
grub_crypto_gcry_error (code) here.
> + else
> + return GRUB_ERR_NONE;
> +}
> +
> +
> +/* Read keyfile as a file */
> +static grub_err_t
> +plainmount_configure_keyfile (grub_cryptodisk_t dev, grub_plainmount_args_t
> cargs)
> +{
> + grub_file_t keyfile;
> + grub_err_t err;
> + gcry_err_code_t code;
> +
> + keyfile = grub_file_open (cargs->keyfile, GRUB_FILE_TYPE_NONE);
> + if (!keyfile)
> + {
> + /* Try to parse keyfile path as UUID path */
> + char *real_path = plainmount_uuid_path_to_disk_path (cargs->keyfile);
> + if (!real_path)
> + {
> + err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
> + N_("cannot open keyfile %s as UUID or real
> path"),
> + cargs->keyfile);
> + goto error;
> + }
> + grub_dprintf ("plainmount", "UUID %s converted to %s\n",
> + cargs->keyfile, real_path);
> + keyfile = grub_file_open (real_path, GRUB_FILE_TYPE_NONE);
> + if (!keyfile)
> + {
> + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("cannot open keyfile
> %s"),
> + real_path);
> + goto error;
> + }
> + }
> +
> + if (grub_file_seek (keyfile, cargs->keyfile_offset) == (grub_off_t)-1)
> + {
> + err = grub_error (GRUB_ERR_FILE_READ_ERROR,
> + N_("cannot seek keyfile at offset %"PRIuGRUB_SIZE),
> + cargs->keyfile_offset);
> + goto error;
> + }
> +
> + if (cargs->keyfile_size)
> + {
> + if (cargs->keyfile_size > (keyfile->size - cargs->keyfile_offset))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("Specified key size (%"PRIuGRUB_SIZE") is too
> small "
> + "for keyfile size (%"PRIuGRUB_SIZE") and
> offset (%"
> + PRIuGRUB_SIZE")"),
> + cargs->keyfile_size, keyfile->size,
> + cargs->keyfile_offset);
> + goto error;
> + }
> +
> + cargs->key_size = cargs->keyfile_size;
> + }
> + else
> + cargs->key_size = keyfile->size - cargs->keyfile_offset;
> +
> + if (grub_file_read (keyfile, cargs->key_data, cargs->key_size) !=
> + (grub_ssize_t) cargs->key_size)
> + {
> + err = grub_error (GRUB_ERR_FILE_READ_ERROR, N_("error reading key
> file"));
> + goto error;
> + }
> +
> + code = grub_cryptodisk_setkey (dev, (grub_uint8_t*) cargs->key_data,
> + cargs->key_size);
> + grub_dprintf ("plainmount", "keyfile: setkey() status %d\n", code);
> + if (code != GPG_ERR_NO_ERROR)
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("cannot set key from keyfile %s. "
> + "Check keysize/cipher/hash options."),
> + cargs->keyfile);
Ditto.
> + goto error;
> + }
> + else
> + return GRUB_ERR_NONE;
> +
> +error:
> + grub_print_error ();
Probabl shouldn't do this either and let it happen further up the call
stack.
> + return err;
> +}
> +
> +
> +/* Read keyfile as a disk segment */
> +static grub_err_t
> +plainmount_configure_keydisk (grub_cryptodisk_t dev, grub_plainmount_args_t
> cargs)
> +{
> + grub_err_t err;
> + grub_disk_t keydisk = NULL;
> + char* keydisk_name = NULL;
> + gcry_err_code_t code;
> + grub_uint64_t total_sectors;
> +
> + keydisk_name = grub_file_get_device_name (cargs->keyfile);
> + keydisk = keydisk_name ? grub_disk_open (keydisk_name) : NULL;
> + if (!keydisk)
> + {
> + /* Try to parse keyfile path as UUID path */
> + keydisk_name = plainmount_uuid_path_to_disk_path (cargs->keyfile);
> + if (!keydisk_name)
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("unable to open disk %s as UUID or real path"),
> + cargs->keyfile);
> + goto error;
> + }
> + keydisk = grub_disk_open (keydisk_name);
> + if (!keydisk)
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unable to open disk
> %s"),
> + keydisk_name);
> + goto error;
> + }
> + }
> +
> + total_sectors = grub_disk_native_sectors (keydisk);
> + if (total_sectors == GRUB_DISK_SIZE_UNKNOWN)
> + {
> + err = grub_error (GRUB_ERR_BAD_DEVICE,
> + N_("unable to determine size of disk %s"),
> + keydisk_name);
> + goto error;
> + }
> + total_sectors = grub_convert_sector (total_sectors, GRUB_DISK_SECTOR_BITS,
> + keydisk->log_sector_size);
> +
> + if (GRUB_ERR_NONE != grub_disk_read (keydisk, 0, cargs->keyfile_offset,
> + cargs->keyfile_size, cargs->key_data))
> + {
> + err = grub_error (GRUB_ERR_READ_ERROR, N_("failed to read from disk
> %s"),
> + keydisk_name);
> + goto error;
> + }
> + code = grub_cryptodisk_setkey (dev, (grub_uint8_t*) cargs->key_data,
> + cargs->key_size);
> + grub_dprintf ("plainmount", "keydisk: setkey() status %d\n", code);
> + if (code != GPG_ERR_NO_ERROR)
> + {
> + grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("cannot set key from keydisk %s. "
> + "Check keysize/cipher/hash options."),
> + cargs->keyfile);
Here also.
> + goto error;
> + }
> + err = GRUB_ERR_NONE;
> + goto cleanup;
> +
> +error:
> + grub_print_error ();
Also, let's not.
> +
> +cleanup:
> + grub_free (keydisk_name);
> + if (keydisk)
> + grub_disk_close (keydisk);
> + return err;
> +}
> +
> +
> +/* 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;
> + struct grub_plainmount_args cargs = {0};
> + grub_cryptodisk_t dev = NULL;
> + char *diskname = NULL, *disklast = NULL;
> + grub_size_t len;
> + grub_err_t err = GRUB_ERR_BUG;
> + const char *p = NULL;
> +
> + if (argc < 1)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("device name required"));
> +
> + /* Open SOURCE disk */
> + diskname = args[0];
> + len = grub_strlen (diskname);
> + if (len && diskname[0] == '(' && diskname[len - 1] == ')')
> + {
> + disklast = &diskname[len - 1];
> + *disklast = '\0';
> + diskname++;
> + }
> + cargs.disk = grub_disk_open (diskname);
> + if (!cargs.disk)
> + {
> + if (disklast)
> + *disklast = ')';
> + char *real_name = plainmount_get_diskname_from_uuid (diskname);
> + if (real_name)
> + {
> + /* diskname must point to hdX,gptY, not to UUID */
> + diskname = real_name;
> + grub_dprintf ("plainmount", "deduced partition %s from UUID %s\n",
> + real_name, args[0]);
> + cargs.disk = grub_disk_open (diskname);
> + if (!cargs.disk)
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("cannot open disk %s specified as UUID
> %s"),
> + diskname, args[0]);
> + goto error;
> + }
> + }
> + else
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("cannot open disk %s by name or by UUID"),
> diskname);
> + goto error;
> + }
> + }
> +
> + /* Process plainmount command arguments */
> + cargs.hash = grub_strdup (state[0].set ? state[0].arg :
> GRUB_PLAINMOUNT_DIGEST);
> + cargs.cipher = grub_strdup (state[1].set ? state[1].arg :
> GRUB_PLAINMOUNT_CIPHER);
> + cargs.keyfile = state[6].set ? grub_strdup (state[6].arg) : NULL;
> + if (!cargs.hash || !cargs.cipher || (!cargs.keyfile && state[6].set))
> + {
> + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
> + goto error;
> + }
> + cargs.offset = state[2].set ? grub_strtoul (state[2].arg, &p, 0) : 0;
Why not use grub_strtoull? And its a good idea to se grub_errno =
GRUB_ERR_NONE before this so you don't get false positives.
> + if (state[2].set && (*p != '\0' || grub_errno != GRUB_ERR_NONE))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized disk
> offset"));
> + goto error;
> + }
> + cargs.size = (state[3].set ? grub_strtoul (state[3].arg, &p, 0) : 0) * 512;
Ditto.
> + if (state[3].set && (*p != '\0' || grub_errno != GRUB_ERR_NONE))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized disk size"));
> + goto error;
> + }
> + cargs.key_size = (state[4].set ? grub_strtoul (state[4].arg, &p, 0) :
> + GRUB_PLAINMOUNT_KEY_SIZE) / 8;
> + if (state[4].set && (*p != '\0' || grub_errno != GRUB_ERR_NONE))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized key size"));
> + goto error;
> + }
> + cargs.sector_size = state[5].set ? grub_strtoul (state[5].arg, &p, 0) :
> + GRUB_PLAINMOUNT_SECTOR_SIZE;
> + if (state[5].set && (*p != '\0' || grub_errno != GRUB_ERR_NONE))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized sector
> size"));
> + goto error;
> + }
> + cargs.keyfile_offset = (state[7].set ? grub_strtoul (state[7].arg, &p, 0)
> : 0) * 512;
> + if (state[7].set && (*p != '\0' || grub_errno != GRUB_ERR_NONE))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized keyfile
> offset"));
> + goto error;
> + }
> + cargs.keyfile_size = (state[8].set ? grub_strtoul (state[8].arg, &p, 0) :
> 0) / 8;
> + if (state[8].set && (*p != '\0' || grub_errno != GRUB_ERR_NONE))
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized keyfile
> size"));
> + goto error;
> + }
> +
> + /* Check cipher mode */
> + cargs.mode = grub_strchr (cargs.cipher,'-');
> + if (!cargs.mode)
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher mode"));
> + goto error;
> + }
> + else
> + *cargs.mode++ = 0;
s/0/'\0'/
> +
> + /* Check keyfile size */
> + if (cargs.keyfile && cargs.keyfile_size > GRUB_CRYPTODISK_MAX_KEYLEN)
> + {
> + err = grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("key file size exceeds maximum size (%d)"),
> + GRUB_CRYPTODISK_MAX_KEYLEN);
> + goto error;
> + }
> +
> + /* Create cryptodisk object and test cipher */
> + dev = grub_zalloc (sizeof *dev);
> + if (!dev)
> + {
> + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
> + goto error;
> + }
> +
> + /* Check cipher */
> + if (grub_cryptodisk_setcipher (dev, cargs.cipher, cargs.mode) !=
> GRUB_ERR_NONE)
> + {
> + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s"),
> cargs.cipher);
> + goto error;
> + }
> +
> + /* Warn if hash and keyfile are both provided */
> + if (cargs.keyfile && state[0].arg)
> + grub_printf_ (N_("warning: hash parameter is ignored if keyfile is
> specified\n"));
> +
> + /* Warn if key file args are provided without key file */
> + if (!state[6].set && (state[7].set || state[8].set))
> + grub_printf_ (N_("warning: keyfile offset (-O) and size (-l) arguments "
> + "are ignored without keyfile (-d)\n"));
> +
> + /* Warn if hash was not set */
> + if (!state[0].set && !cargs.keyfile)
> + grub_printf_ (N_("warning: using password and hash is not set, using
> default %s\n"),
> + cargs.hash);
> +
> + /* Warn if cipher was not set */
> + if (!state[1].set)
> + grub_printf_ (N_("warning: cipher not set, using default %s\n"),
> + GRUB_PLAINMOUNT_CIPHER);
> +
> + /* Warn if key size was not set */
> + if (!state[4].set)
> + grub_printf_ (N_("warning: key size not set, using default
> %"PRIuGRUB_SIZE" bits\n"),
> + cargs.key_size * 8);
> +
> + err = plainmount_configure_sectors (dev, &cargs);
> + if (err != GRUB_ERR_NONE)
> + goto error;
> +
> + grub_dprintf ("plainmount",
> + "parameters: cipher=%s, hash=%s, key_size=%"PRIuGRUB_SIZE",
> keyfile=%s, "
> + "keyfile offset=%"PRIuGRUB_SIZE", key file
> size=%"PRIuGRUB_SIZE"\n",
> + cargs.cipher, cargs.hash, cargs.key_size,
> + cargs.keyfile ? cargs.keyfile : NULL,
> + cargs.keyfile_offset, cargs.keyfile_size);
> +
> + dev->modname = "plainmount";
> + dev->source_disk = cargs.disk;
> + grub_memcpy (dev->uuid, GRUB_PLAINMOUNT_UUID, sizeof (dev->uuid));
I don't like this because this makes the collection of all dev uuids
not unique if there are more than one plainmount volume mounted. I
haven't thought about how this might cause related problems, but its a
concern. Would it better to create a ramdom prefix and allow for say
256 plainmounts? Maybe use a module level static global to keep track
of number of plain mounts.
> + COMPILE_TIME_ASSERT (sizeof (dev->uuid) >= sizeof (GRUB_PLAINMOUNT_UUID));
> +
> + /* For password or keyfile */
> + cargs.key_data = grub_zalloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
> + if (!cargs.key_data)
> + {
> + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
> + goto error;
> + }
> +
> + /* Configure keyfile/keydisk/password */
> + if (cargs.keyfile)
> + if (grub_strchr (cargs.keyfile, '/'))
> + err = plainmount_configure_keyfile (dev, &cargs);
> + else
> + err = plainmount_configure_keydisk (dev, &cargs);
> + else /* password */
> + err = plainmount_configure_password (dev, &cargs);
> + if (err != GRUB_ERR_NONE)
> + goto error;
> +
> + err = grub_cryptodisk_insert (dev, diskname, cargs.disk);
> + if (err == GRUB_ERR_NONE)
> + {
> + grub_printf_ ("disk %s mounted as crypto%"PRIuGRUB_SIZE" in plain
> mode.\n",
> + dev->source, dev->id);
This should be a grub_dprintf().
> + return err;
> + }
> + else
> + grub_printf_ (N_("cannot initialize cryptodisk. "
> + "Check cipher/key size/hash arguments\n"));
This isn't that useful, I would drop it. An error will get printed up
the call stack when a return value other than GRUB_ERR_NONE is returned.
> +
> +error:
> + grub_free (cargs.hash);
> + grub_free (cargs.cipher);
> + grub_free (cargs.keyfile);
> + grub_free (cargs.key_data);
These frees should be done even if no error, otherwise you're going to
have a memory leak in the successful case.
> + if (cargs.disk)
> + grub_disk_close (cargs.disk);
> + return err;
> +}
> +
> +static grub_extcmd_t cmd;
> +GRUB_MOD_INIT (plainmount)
> +{
> + cmd = grub_register_extcmd ("plainmount", grub_cmd_plainmount, 0,
> + N_("[-h hash] [-c cipher] [-o offset] [-s size] "
> + "[-k key-size] [-z sector-size] [-d keyfile] "
> + "[-O keyfile offset] [-l keyfile-size] <SOURCE>"),
This might be more accurate if this
[-d keyfile] [-O keyfile offset] [-l keyfile-size]
were prelaced by
[-d keyfile [-O keyfile offset] [-l keyfile-size]]
> + N_("Open partition encrypted in plain mode."),
> options);
> +}
> +
> +GRUB_MOD_FINI (plainmount)
> +{
> + grub_unregister_extcmd (cmd);
> +}
> --
> 2.35.1
>
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel