From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.90_1) id 1oICoU-0004pk-QS for mharc-grub-devel@gnu.org; Sun, 31 Jul 2022 13:33:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:45738) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oICoR-0004ou-7e for grub-devel@gnu.org; Sun, 31 Jul 2022 13:33:12 -0400 Received: from mail-4323.proton.ch ([185.70.43.23]:54595) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oICoL-0000L6-P8 for grub-devel@gnu.org; Sun, 31 Jul 2022 13:33:10 -0400 Date: Sun, 31 Jul 2022 17:32:50 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fomin.one; s=protonmail; t=1659288775; x=1659547975; bh=F+ZMkVH9fB/i/kwdewE6hDrpeFRdWTdNcXrQHy5mVMo=; h=Date:To:From:Cc:Reply-To:Subject:Message-ID:Feedback-ID:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID; b=X61/XRlqxfj3VQbCZrpP3Cjiy7qAlg3et0YekZrsZC5hLNFGIIn94FCtmVEgh6Ps7 qw63IyqZuVxb+F/VTAGko8lCT8ENDty8ZC3ZpUKpDnHtd3NlZkA8XcGVPMLqyxTM3C yA7dOWPP3XxjJeIHQfM/sYAlNwMtdrpfVSdb5n9C1Zl/iqVO4UFdtHPZvDbTC31Wc5 D/19If9z4c7QIZV9OIabNRc6oFAPkw4qbLG8Ojjdu/gXauXtsjopdePTL3ZlnvwZuw HJHG+VtkY1EuIJLaqobIIMMTZQxoy7X00IYnMotkPWA33dp61qeoVASaC2TccLzxKw 6oJVzKheTYw2g== To: "grub-devel@gnu.org" From: Maxim Fomin Cc: "development@efficientek.com" , "ps@pks.im" Reply-To: Maxim Fomin Subject: [PATCH v6 1/1] plainmount: Support plain encryption mode Message-ID: Feedback-ID: 10594471:user:proton MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Received-SPF: pass client-ip=185.70.43.23; envelope-from=maxim@fomin.one; helo=mail-4323.proton.ch X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 31 Jul 2022 17:33:12 -0000 >From 683357e227467c05272facc7da534a82becc9d8a Mon Sep 17 00:00:00 2001 From: Maxim Fomin Date: Sun, 31 Jul 2022 14:06:42 +0100 Subject: [PATCH v6 1/1] 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 --- docs/grub.texi | 80 +++++++ grub-core/Makefile.core.def | 5 + grub-core/disk/plainmount.c | 450 ++++++++++++++++++++++++++++++++++++ 3 files changed, 535 insertions(+) create mode 100644 grub-core/disk/plainmount.c diff --git a/docs/grub.texi b/docs/grub.texi index af119dea3..22c73580c 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4265,6 +4265,7 @@ you forget a command, you can run the command @comman= d{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 @@ -4552,6 +4553,14 @@ function is supported, as Argon2 is not yet supporte= d. 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 nu= meration +suffix for each new decrypted disk. If the encrypted disk hosts some highe= r level +of abstraction (like LVM2 or MDRAID) it will be created under a separate d= evice +namespace in addition to the cryptodisk namespace. + +Support for plain encryption mode (plain dm-crypt) is provided via separat= e +@command{@pxref{plainmount}} command. @end deffn @node cutmem @@ -5060,6 +5069,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 encrypte= d +data at the device is specified in terms of 512 byte sectors with the bloc= klist +syntax and loopback device. The following example shows how to specify 1Mi= B +offset: + +@example +loopback node (hd0,gpt1)2048+ +plainmount node +@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 password: a keyfile and a secret passphrase. +Keyfile path parameter has higher priority than secret passphrase and is +specified with the option @option{-d}. Password data obtained from keyfile= s +is not hashed and is used directly as a cipher key. Optional offset of pas= sword +data in the keyfile can be specified with the option @option{-O} or direct= ly +with the option @option{-d} and GRUB blocklist syntax. The following examp= le +shows both methods to specify password data in the keyfile at offset 1MiB: + +@example +plainmount -d (hd0,gpt1)2048+ +plainmount -d (hd0,gpt1) -O 1048576 +@end example + +If no keyfile is specified then the password is set to data 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, b= ut +it can be set to @code{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, +'aes-xts-plain64'). Key size option @option{-s} is the key size of the cip= her, +not to be confused with the offset of the key data in a keyfile specified = with +the @option{-O} option. It must not exceed 128 bytes and must be specified= in +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. @footnot= e{Current +implementation of cryptsetup supports only 512/1024/2048/4096 byte sectors= }. +Disk sector size is configured when creating the encrypted volume. Attempt= ing +to decrypt volumes with a different sector size than it was created with w= ill +not result in an error, but will decrypt to random bytes and thus prevent +accessing the volume (in some cases filesystem driver can detect the files= ystem +presense, 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 increment= ed +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 sec= tor +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, plainmo= unt +will create virtual device with the garbage data and GRUB will report unkn= own +filesystem for such device. Writing data to such virtual device will resul= t in +the data loss if the underlying partition contained desired data. +@end deffn + + @node play @subsection play diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 715994872..3910b7670 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1174,6 +1174,11 @@ module =3D { common =3D disk/cryptodisk.c; }; +module =3D { + name =3D plainmount; + common =3D disk/plainmount.c; +}; + module =3D { name =3D json; common =3D lib/json/json.c; diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c new file mode 100644 index 000000000..8b735a425 --- /dev/null +++ b/grub-core/disk/plainmount.c @@ -0,0 +1,450 @@ +/* + * 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 . + */ + +/* plaimount.c - Open device encrypted in plain mode. */ + +#include +#include +#include +#include +#include +#include + + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define PLAINMOUNT_DEFAULT_SECTOR_SIZE 512 +#define PLAINMOUNT_DEFAULT_UUID "109fea84-a6b7-34a8-4bd1-1c506305a4= 00" + + +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[] =3D + { + /* 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, +=09=09 grub_size_t size) +{ + gcry_err_code_t code =3D grub_cryptodisk_setkey (dev, key, size); + if (code !=3D 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 k= ey")); + } + return GRUB_ERR_NONE; +} + + +/* Configure cryptodisk uuid */ +static void plainmount_set_uuid (grub_cryptodisk_t dev, const char *user_u= uid) +{ + grub_size_t pos =3D 0; + + /* Size of user_uuid is checked in main func */ + if (user_uuid !=3D NULL) + grub_memcpy (dev->uuid, user_uuid, grub_strlen (user_uuid)); + else + { + /* + * Set default UUID. Last digits start from 1 and are incremented fo= r + * each new plainmount device by snprintf(). + */ + grub_snprintf (dev->uuid, sizeof (dev->uuid)-1, "%36lx", dev->id+1); + while (dev->uuid[++pos] =3D=3D ' '); + grub_memcpy (dev->uuid, PLAINMOUNT_DEFAULT_UUID, pos); + } + COMPILE_TIME_ASSERT (sizeof (dev->uuid) >=3D 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, +=09=09=09 grub_size_t sector_size) +{ + dev->total_sectors =3D grub_disk_native_sectors (disk); + if (dev->total_sectors =3D=3D GRUB_DISK_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot determine disk %s s= ize"), +=09=09 disk->name); + + /* Convert size to sectors */ + dev->log_sector_size =3D grub_log2ull (sector_size); + dev->total_sectors =3D grub_convert_sector (dev->total_sectors, +=09=09=09=09=09 GRUB_DISK_SECTOR_BITS, +=09=09=09=09=09 dev->log_sector_size); + if (dev->total_sectors =3D=3D 0) + return grub_error (GRUB_ERR_BAD_DEVICE, +=09=09 N_("cannot set specified sector size on disk %s"), +=09=09 disk->name); + + grub_dprintf ("plainmount", "log_sector_size=3D%d, total_sectors=3D%" +=09=09PRIuGRUB_SIZE"\n", dev->log_sector_size, dev->total_sectors); + return GRUB_ERR_NONE; +} + + +/* Hashes a password into a key and stores it with cipher. */ +static grub_uint8_t* +plainmount_configure_password (grub_cryptodisk_t dev, const char *hash, +=09=09=09 grub_uint8_t *key_data, grub_size_t key_size) +{ + grub_uint8_t *derived_hash, *dh; + char *p; + unsigned int round, i; + unsigned int len, size; + + /* Support none (plain) hash */ + if (grub_strcmp (hash, "plain") =3D=3D 0) + { + dev->hash =3D NULL; + return key_data; + } + + /* Hash argument was checked at main func */ + dev->hash =3D grub_crypto_lookup_md_by_name (hash); + len =3D dev->hash->mdlen; + p =3D grub_malloc (key_size + 2 + (key_size / len)); + derived_hash =3D grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN * 2); + if (p =3D=3D NULL || derived_hash =3D=3D NULL) + { + grub_free (p); + grub_free (derived_hash); + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + return NULL; + } + dh =3D derived_hash; + + /* + * Hash password. Adapted from cryptsetup. + * https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/lib/crypt_plain.= c + */ + for (round =3D 0, size =3D key_size; size; round++, dh +=3D len, size -= =3D len) + { + for (i =3D 0; i < round; i++) +=09p[i] =3D 'A'; + + grub_strcpy (p + i, (char*) key_data); + + if (len > size) +=09len =3D size; + + grub_crypto_hash (dev->hash, dh, p, grub_strlen (p)); + } + grub_free (p); + return derived_hash; +} + + +/* Read key material from keyfile */ +static grub_err_t +plainmount_configure_keyfile (char *keyfile, grub_uint8_t *key_data, +=09=09=09 grub_size_t key_size, grub_size_t keyfile_offset) +{ + grub_file_t g_keyfile =3D grub_file_open (keyfile, GRUB_FILE_TYPE_NONE); + if (g_keyfile =3D=3D NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("cannot open keyfile %s= "), +=09=09 keyfile); + + if (grub_file_seek (g_keyfile, keyfile_offset) =3D=3D (grub_off_t)-1) + return grub_error (GRUB_ERR_FILE_READ_ERROR, +=09=09 N_("cannot seek keyfile at offset %"PRIuGRUB_SIZE), +=09=09 keyfile_offset); + + if (key_size > (g_keyfile->size - keyfile_offset)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Specified key size (%" +=09=09 PRIuGRUB_SIZE") is too small for keyfile size (%" +=09=09 PRIuGRUB_SIZE") and offset (%"PRIuGRUB_SIZE")"), +=09=09 key_size, g_keyfile->size, keyfile_offset); + + if (grub_file_read (g_keyfile, key_data, key_size) !=3D (grub_ssize_t) k= ey_size) + return grub_error (GRUB_ERR_FILE_READ_ERROR, N_("error reading key fil= e")); + 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 =3D ctxt->state; + grub_cryptodisk_t dev =3D NULL; + grub_disk_t disk =3D NULL; + const gcry_md_spec_t *gcry_hash; + char *diskname, *disklast =3D NULL, *cipher, *mode, *hash, *keyfile, *uu= id; + grub_size_t len, key_size, sector_size, keyfile_offset =3D 0; + grub_err_t err; + const char *p; + grub_uint8_t *key_data; + grub_uint8_t *hashed_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 se= t"); + + /* Check hash */ + if (!state[OPTION_KEYFILE].set) + { + gcry_hash =3D 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"), +=09=09=09 state[OPTION_HASH].arg); + + if (gcry_hash->mdlen > GRUB_CRYPTODISK_MAX_KEYLEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09=09 N_("hash length %"PRIuGRUB_SIZE" exceeds maximum %d bits"), +=09=09=09 gcry_hash->mdlen * GRUB_CHAR_BIT, +=09=09=09 GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT); + } + + /* Check cipher mode */ + if (!grub_strchr (state[OPTION_CIPHER].arg,'-')) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09 N_("invalid cipher mode, must be of format cipher-mode")); + + /* Check password size */ + if (state[OPTION_PASSWORD].set && grub_strlen (state[OPTION_PASSWORD].ar= g) > +=09=09 GRUB_CRYPTODISK_MAX_PASSPHRASE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09 N_("password exceeds maximium size")); + + /* Check uuid length */ + if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) > +=09=09=09=09sizeof (PLAINMOUNT_DEFAULT_UUID)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09 N_("specified UUID exceeds maximum size")); + + /* Parse plainmount arguments */ + grub_errno =3D GRUB_ERR_NONE; + keyfile_offset =3D state[OPTION_KEYFILE_OFFSET].set ? +=09=09 grub_strtoull (state[OPTION_KEYFILE_OFFSET].arg, &p, 0) : 0; + if (state[OPTION_KEYFILE_OFFSET].set && + (state[OPTION_KEYFILE_OFFSET].arg[0] =3D=3D '\0' || *p !=3D '\0' || + grub_errno !=3D GRUB_ERR_NONE)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized keyfile off= set")); + + sector_size =3D state[OPTION_SECTOR_SIZE].set ? +=09=09grub_strtoull (state[OPTION_SECTOR_SIZE].arg, &p, 0) : +=09=09PLAINMOUNT_DEFAULT_SECTOR_SIZE; + if (state[OPTION_SECTOR_SIZE].set && (state[OPTION_SECTOR_SIZE].arg[0] = =3D=3D '\0' || +=09=09=09=09=09*p !=3D '\0' || grub_errno !=3D GRUB_ERR_NONE)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized sector size= ")); + + /* Check key size */ + key_size =3D grub_strtoull (state[OPTION_KEY_SIZE].arg, &p, 0); + if (state[OPTION_KEY_SIZE].arg[0] =3D=3D '\0' || *p !=3D '\0' || + grub_errno !=3D GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized key size"))= ; + if (key_size % GRUB_CHAR_BIT !=3D 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09 N_("key size is not multiple of %d bits"), GRUB_CHAR_BIT); + key_size =3D key_size / GRUB_CHAR_BIT; + if (key_size > GRUB_CRYPTODISK_MAX_KEYLEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09 N_("key size %"PRIuGRUB_SIZE" exceeds maximum %d bits"), +=09=09 key_size * GRUB_CHAR_BIT, +=09=09 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, +=09=09 N_("sector size -S must be at least %d"), +=09=09 GRUB_DISK_SECTOR_SIZE); + if ((sector_size & (sector_size - 1)) !=3D 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +=09=09 N_("sector size -S %"PRIuGRUB_SIZE" is not power of 2"), +=09=09 sector_size); + + /* Allocate all stuff here */ + hash =3D state[OPTION_HASH].set ? grub_strdup (state[OPTION_HASH].arg) = : NULL; + cipher =3D grub_strdup (state[OPTION_CIPHER].arg); + keyfile =3D state[OPTION_KEYFILE].set ? +=09 grub_strdup (state[OPTION_KEYFILE].arg) : NULL; + dev =3D grub_zalloc (sizeof *dev); + key_data =3D grub_zalloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + uuid =3D state[OPTION_UUID].set ? grub_strdup (state[OPTION_UUID].arg) := NULL; + if ((hash =3D=3D NULL && state[OPTION_HASH].set) || cipher =3D=3D NULL |= | dev =3D=3D NULL || + (keyfile =3D=3D NULL && state[OPTION_KEYFILE].set) || key_data =3D= =3D NULL || + (uuid =3D=3D NULL && state[OPTION_UUID].set)) + { + err =3D grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + goto exit; + } + + /* Copy user password from -p option */ + if (state[OPTION_PASSWORD].set) + grub_memcpy (key_data, state[OPTION_PASSWORD].arg, +=09=09 grub_strlen (state[OPTION_PASSWORD].arg)); + + /* Copy user UUID from -u option */ + if (state[OPTION_UUID].set) + grub_memcpy (uuid, state[OPTION_UUID].arg, +=09=09 grub_strlen (state[OPTION_UUID].arg)); + + /* Set cipher mode (tested above) */ + mode =3D grub_strchr (cipher,'-'); + *mode++ =3D '\0'; + + /* Check cipher */ + if (grub_cryptodisk_setcipher (dev, cipher, mode) !=3D GRUB_ERR_NONE) + { + err =3D grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s"), = cipher); + goto exit; + } + + /* Open SOURCE disk */ + diskname =3D args[0]; + len =3D grub_strlen (diskname); + if (len && diskname[0] =3D=3D '(' && diskname[len - 1] =3D=3D ')') + { + disklast =3D &diskname[len - 1]; + *disklast =3D '\0'; + diskname++; + } + disk =3D grub_disk_open (diskname); + if (disk =3D=3D NULL) + { + if (disklast) + *disklast =3D ')'; + err =3D grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot open disk %s")= , diskname); + goto exit; + } + + /* Get password from console */ + if (keyfile =3D=3D NULL && key_data[0] =3D=3D '\0') + { + char *part =3D grub_partition_get_name (disk->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s: "), disk->name, +=09=09 disk->partition !=3D NULL ? "," : "", +=09=09 part !=3D NULL ? part : N_("UNKNOWN")); + grub_free (part); + + if (!grub_password_get ((char*)key_data, GRUB_CRYPTODISK_MAX_PASSPHRAS= E-1)) + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("error reading password")); + } + + /* Warn if hash and keyfile are both provided */ + if (keyfile !=3D NULL && 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 " +=09=09 "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 " +=09=09 "specified without keyfile option -d\n")); + + grub_dprintf ("plainmount", "parameters: cipher=3D%s, hash=3D%s, key_siz= e=3D%" +=09=09PRIuGRUB_SIZE", keyfile=3D%s, keyfile offset=3D%"PRIuGRUB_SIZE"\n", +=09=09cipher, hash, key_size, keyfile, keyfile_offset); + + err =3D plainmount_configure_sectors (dev, disk, sector_size); + if (err !=3D GRUB_ERR_NONE) + goto exit; + + /* Configure keyfile or password */ + if (keyfile !=3D NULL) + { + err =3D plainmount_configure_keyfile (keyfile, key_data, key_size, k= eyfile_offset); + if (err !=3D GRUB_ERR_NONE) + goto exit; + err =3D plainmount_setkey (dev, key_data, key_size); + if (err !=3D GRUB_ERR_NONE) + goto exit; + } + else + { + hashed_key_data =3D plainmount_configure_password (dev, hash, key_da= ta, key_size); + if (hashed_key_data =3D=3D NULL) + goto exit; + err =3D plainmount_setkey (dev, hashed_key_data, key_size); + if (err !=3D GRUB_ERR_NONE) + { + grub_free (hashed_key_data); + goto exit; + } + } + + err =3D grub_cryptodisk_insert (dev, diskname, disk); + if (err !=3D GRUB_ERR_NONE) + goto exit; + + dev->modname =3D "plainmount"; + dev->source_disk =3D disk; + plainmount_set_uuid (dev, uuid); + +exit: + grub_free (hash); + grub_free (cipher); + grub_free (keyfile); + grub_free (key_data); + grub_free (uuid); + if (err !=3D GRUB_ERR_NONE && disk) + grub_disk_close (disk); + if (err !=3D GRUB_ERR_NONE && dev) + grub_free (dev); + return err; +} + +static grub_extcmd_t cmd; +GRUB_MOD_INIT (plainmount) +{ + cmd =3D grub_register_extcmd ("plainmount", grub_cmd_plainmount, 0, +=09=09=09 N_("-c cipher -s key-size [-h hash] [-S sector-size]" +=09=09=09 " [-o offset] [-p password] [-u uuid] " +=09=09=09 " [[-d keyfile] [-O keyfile offset]] "), +=09=09=09 N_("Open partition encrypted in plain mode."), +=09=09=09 options); +} + +GRUB_MOD_FINI (plainmount) +{ + grub_unregister_extcmd (cmd); +} -- 2.37.1