From e7f54a0b78353c01bf4c966f871b3e3590222743 Mon Sep 17 00:00:00 2001 From: Maxim Fomin <ma...@fomin.one> Date: Wed, 28 Dec 2022 13:19:36 +0000 Subject: [PATCH v10] 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 <ma...@fomin.one> --- Interdiff against v9: diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c index eabedf4c3..47e64805f 100644 --- a/grub-core/disk/plainmount.c +++ b/grub-core/disk/plainmount.c @@ -326,7 +326,7 @@ grub_cmd_plainmount (grub_extcmd_context_t ctxt, int argc, char **args) * Arbitrary data keys are supported via keyfiles. */ password_size = grub_strlen (state[OPTION_PASSWORD].arg); - grub_memcpy (key_data, state[OPTION_PASSWORD].arg, password_size); + grub_strcpy ((char*) key_data, state[OPTION_PASSWORD].arg); } /* Set cipher mode (tested above) */ @@ -334,9 +334,16 @@ grub_cmd_plainmount (grub_extcmd_context_t ctxt, int argc, char **args) *mode++ = '\0'; /* Check cipher */ - if (grub_cryptodisk_setcipher (dev, cipher, mode) != GRUB_ERR_NONE) + err = grub_cryptodisk_setcipher (dev, cipher, mode); + if (err != GRUB_ERR_NONE) { - err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s"), cipher); + 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; } diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c index 631d3d58a..ed606b3f1 100644 --- a/grub-core/fs/affs.c +++ b/grub-core/fs/affs.c @@ -321,7 +321,6 @@ static int grub_affs_create_node (grub_fshelp_node_t dir, grub_fshelp_iterate_dir_hook_t hook, void *hook_data, struct grub_fshelp_node **node, - grub_uint32_t **hashtable, grub_uint32_t block, const struct grub_affs_file *fil) { struct grub_affs_data *data = dir->data; @@ -332,10 +331,7 @@ grub_affs_create_node (grub_fshelp_node_t dir, *node = grub_zalloc (sizeof (**node)); if (!*node) - { - grub_free (*hashtable); - return 1; - } + return 1; (*node)->data = data; (*node)->block = block; @@ -395,7 +391,6 @@ grub_affs_create_node (grub_fshelp_node_t dir, if (hook ((char *) name_u8, type, *node, hook_data)) { - grub_free (*hashtable); *node = 0; return 1; } @@ -460,11 +455,11 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir, if (grub_errno) goto fail; - if (grub_affs_create_node (dir, hook, hook_data, &node, &hashtable, - next, &file)) + if (grub_affs_create_node (dir, hook, hook_data, &node, next, &file)) { /* Node has been replaced in function. */ grub_free (orig_node); + grub_free (hashtable); return 1; } diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index a75876010..07cb3e3ac 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -416,6 +416,8 @@ read_bfs_file (grub_disk_t disk, len -= read_size; buf = (char *) buf + read_size; } + grub_free (l1_entries); + grub_free (l2_entries); return GRUB_ERR_NONE; } } diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ec72f7be3..19bff4610 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1982,7 +1982,12 @@ find_path (struct grub_btrfs_data *data, { err = get_root (data, key, tree, type); if (err) - return err; + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } } continue; } diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index 6337cbfcb..11393ca34 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -652,7 +652,10 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, + 2); if ((char *) pointer > node + btree->nodesize - 2) - return grub_error (GRUB_ERR_BAD_FS, "HFS+ key beyond end of node"); + { + grub_free (node); + return grub_error (GRUB_ERR_BAD_FS, "HFS+ key beyond end of node"); + } currnode = grub_be_to_cpu32 (grub_get_unaligned32 (pointer)); match = 1; diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index 91817ec1f..df9f7783b 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -279,7 +279,10 @@ grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off, /* Load a part of the System Usage Area. */ err = read_node (node, off, sua_size, sua); if (err) - return err; + { + grub_free (sua); + return err; + } for (entry = (struct grub_iso9660_susp_entry *) sua; (char *) entry < (char *) sua + sua_size - 1 && entry->len > 0; entry = (struct grub_iso9660_susp_entry *) @@ -309,7 +312,10 @@ grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off, err = grub_disk_read (node->data->disk, ce_block, off, sua_size, sua); if (err) - return err; + { + grub_free (sua); + return err; + } entry = (struct grub_iso9660_susp_entry *) sua; } diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c index 953df1191..5354951d1 100644 --- a/grub-core/fs/minix.c +++ b/grub-core/fs/minix.c @@ -374,7 +374,7 @@ grub_minix_lookup_symlink (struct grub_minix_data *data, grub_minix_ino_t ino) if (!symlink) return grub_errno; if (grub_minix_read_file (data, 0, 0, 0, sz, symlink) < 0) - return grub_errno; + goto fail; symlink[sz] = '\0'; @@ -384,10 +384,12 @@ grub_minix_lookup_symlink (struct grub_minix_data *data, grub_minix_ino_t ino) /* Now load in the old inode. */ if (grub_minix_read_inode (data, ino)) - return grub_errno; + goto fail; grub_minix_find_file (data, symlink); + fail: + grub_free(symlink); return grub_errno; } diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 3511e4e2c..bbdbe24ad 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -654,7 +654,7 @@ grub_ntfs_read_symlink (grub_fshelp_node_t node) struct grub_ntfs_file *mft; struct symlink_descriptor symdesc; grub_err_t err; - grub_uint8_t *buf16; + grub_uint8_t *buf16 = NULL; char *buf, *end; grub_size_t len; grub_uint8_t *pa; @@ -667,20 +667,20 @@ grub_ntfs_read_symlink (grub_fshelp_node_t node) return NULL; if (read_mft (mft->data, mft->buf, mft->ino)) - return NULL; + goto fail; pa = locate_attr (&mft->attr, mft, GRUB_NTFS_AT_SYMLINK); if (pa == NULL) { grub_error (GRUB_ERR_BAD_FS, "no $SYMLINK in MFT 0x%llx", (unsigned long long) mft->ino); - return NULL; + goto fail; } err = read_attr (&mft->attr, (grub_uint8_t *) &symdesc, 0, sizeof (struct symlink_descriptor), 1, 0, 0); if (err) - return NULL; + goto fail; switch (grub_cpu_to_le32 (symdesc.type)) { @@ -697,23 +697,22 @@ grub_ntfs_read_symlink (grub_fshelp_node_t node) default: grub_error (GRUB_ERR_BAD_FS, "symlink type invalid (%x)", grub_cpu_to_le32 (symdesc.type)); - return NULL; + goto fail; } buf16 = grub_malloc (len); if (!buf16) - return NULL; + goto fail; err = read_attr (&mft->attr, buf16, off, len, 1, 0, 0); if (err) - return NULL; + goto fail; buf = get_utf8 (buf16, len / 2); if (!buf) - { - grub_free (buf16); - return NULL; - } + goto fail; + + grub_free (mft->buf); grub_free (buf16); for (end = buf; *end; end++) @@ -728,6 +727,11 @@ grub_ntfs_read_symlink (grub_fshelp_node_t node) end -= 6; } return buf; + + fail: + grub_free (mft->buf); + grub_free (buf16); + return NULL; } static int diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 02b1f9b6d..a30e6ebe1 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -550,7 +550,10 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, + node->stack[node->stsize - 1].ino_chunk, node->stack[node->stsize - 1].ino_offset); if (err) - return 0; + { + grub_free (node); + return 0; + } if (hook ("..", GRUB_FSHELP_DIR, node, hook_data)) return 1; @@ -600,7 +603,10 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, grub_le_to_cpu64 (dir->data->sb.diroffset) + chunk, off); if (err) - return 0; + { + grub_free (buf); + return 0; + } off += grub_le_to_cpu16 (di.namelen) + 1; buf[grub_le_to_cpu16 (di.namelen) + 1] = 0; @@ -612,11 +618,17 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, if (grub_add (dir->stsize, 1, &sz) || grub_mul (sz, sizeof (dir->stack[0]), &sz) || grub_add (sz, sizeof (*node), &sz)) - return 0; + { + grub_free (buf); + return 0; + } node = grub_malloc (sz); if (! node) - return 0; + { + grub_free (buf); + return 0; + } grub_memcpy (node, dir, sz - sizeof(dir->stack[0])); diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c index 12e88ab62..7679ea309 100644 --- a/grub-core/fs/udf.c +++ b/grub-core/fs/udf.c @@ -510,6 +510,20 @@ grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) } len = U32 (extension->ae_len); + /* + * Ensure AE length is less than block size + * per UDF spec v2.01 section 2.3.11. + * + * node->data->lbshift is initialized by + * grub_udf_mount(). lbshift has a maximum value + * of 3 and it does not cause an overflow here. + */ + if (len < 0 || len > ((grub_ssize_t) 1 << node->data->lbshift)) + { + grub_error (GRUB_ERR_BAD_FS, "invalid ae length"); + goto fail; + } + ad = (struct grub_udf_short_ad *) (buf + sizeof (struct grub_udf_aed)); continue; @@ -563,6 +577,20 @@ grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) } len = U32 (extension->ae_len); + /* + * Ensure AE length is less than block size + * per UDF spec v2.01 section 2.3.11. + * + * node->data->lbshift is initialized by + * grub_udf_mount(). lbshift has a maximum value + * of 3 and it does not cause an overflow here. + */ + if (len < 0 || len > ((grub_ssize_t) 1 << node->data->lbshift)) + { + grub_error (GRUB_ERR_BAD_FS, "invalid ae length"); + goto fail; + } + ad = (struct grub_udf_long_ad *) (buf + sizeof (struct grub_udf_aed)); continue; diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index d6de7f1a2..b67407690 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -585,7 +585,10 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) if (grub_disk_read (node->data->disk, GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS), 0, node->data->bsize, leaf)) - return 0; + { + grub_free (leaf); + return 0; + } if ((!node->data->hascrc && grub_strncmp ((char *) leaf->magic, "BMAP", 4)) || @@ -751,6 +754,7 @@ static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename, if (err) { grub_print_error (); + grub_free (fdiro); return 0; } @@ -861,7 +865,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, blk << dirblk_log2, dirblk_size, dirblock, 0); if (numread != dirblk_size) - return 0; + { + grub_free (dirblock); + return 0; + } entries = (grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale)); diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 8d48fd50d..750177248 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -66,6 +66,9 @@ grub_file_open (const char *name, enum grub_file_type type) const char *file_name; grub_file_filter_id_t filter; + /* Reset grub_errno before we start. */ + grub_errno = GRUB_ERR_NONE; + device_name = grub_file_get_device_name (name); if (grub_errno) goto fail; diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index edd6c2bb1..10a367629 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -1085,9 +1085,22 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), addr_min = (grub_addr_t) prot_mode_target + prot_init_space; + /* Make sure the maximum address is able to store the initrd. */ + if (addr_max < aligned_size) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("the size of initrd is bigger than addr_max")); + goto fail; + } + /* Put the initrd as high as possible, 4KiB aligned. */ addr = (addr_max - aligned_size) & ~0xFFF; + grub_dprintf ("linux", + "Initrd at addr 0x%" PRIxGRUB_ADDR " which is expected in" + " ranger 0x%" PRIxGRUB_ADDR " ~ 0x%" PRIxGRUB_ADDR "\n", + addr, addr_min, addr_max); + if (addr < addr_min) { grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big"); diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c index 830360172..3948302d2 100644 --- a/grub-core/loader/linux.c +++ b/grub-core/loader/linux.c @@ -127,12 +127,23 @@ insert_dir (const char *name, struct dir **root, n->name = grub_strndup (cb, ce - cb); if (ptr) { + /* + * Create the substring with the trailing NUL byte + * to be included in the cpio header. + */ + char *tmp_name = grub_strndup (name, ce - name); + if (!tmp_name) { + grub_free (n->name); + grub_free (n); + return grub_errno; + } grub_dprintf ("linux", "Creating directory %s, %s\n", name, ce); - ptr = make_header (ptr, name, ce - name, + ptr = make_header (ptr, tmp_name, ce - name + 1, 040777, 0); + grub_free (tmp_name); } if (grub_add (*size, - ALIGN_UP ((ce - (char *) name) + ALIGN_UP ((ce - (char *) name + 1) + sizeof (struct newc_head), 4), size)) { @@ -191,7 +202,7 @@ grub_initrd_init (int argc, char *argv[], grub_initrd_close (initrd_ctx); return grub_errno; } - name_len = grub_strlen (initrd_ctx->components[i].newc_name); + name_len = grub_strlen (initrd_ctx->components[i].newc_name) + 1; if (grub_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + name_len, 4), &initrd_ctx->size) || @@ -205,7 +216,7 @@ grub_initrd_init (int argc, char *argv[], { if (grub_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) - + sizeof ("TRAILER!!!") - 1, 4), + + sizeof ("TRAILER!!!"), 4), &initrd_ctx->size)) goto overflow; free_dir (root); @@ -233,7 +244,7 @@ grub_initrd_init (int argc, char *argv[], initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); if (grub_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) - + sizeof ("TRAILER!!!") - 1, 4), + + sizeof ("TRAILER!!!"), 4), &initrd_ctx->size)) goto overflow; free_dir (root); @@ -297,14 +308,14 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, } ptr += dir_size; ptr = make_header (ptr, initrd_ctx->components[i].newc_name, - grub_strlen (initrd_ctx->components[i].newc_name), + grub_strlen (initrd_ctx->components[i].newc_name) + 1, 0100777, initrd_ctx->components[i].size); newc = 1; } else if (newc) { - ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!") - 1, + ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!"), 0, 0); free_dir (root); root = 0; @@ -327,7 +338,7 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, { grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); - ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!") - 1, 0, 0); + ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!"), 0, 0); } free_dir (root); root = 0; diff --git a/grub-core/normal/cmdline.c b/grub-core/normal/cmdline.c index 61f098244..9c6d9ade9 100644 --- a/grub-core/normal/cmdline.c +++ b/grub-core/normal/cmdline.c @@ -219,6 +219,8 @@ cl_set_pos (struct cmdline_term *cl_term, grub_size_t lpos) cl_term->pos.x = (cl_term->prompt_len + lpos) % cl_term->width; cl_term->pos.y = cl_term->ystart + (cl_term->prompt_len + lpos) / cl_term->width; + if (cl_term->pos.y >= cl_term->height) + cl_term->pos.y = cl_term->height - 1; grub_term_gotoxy (cl_term->term, cl_term->pos); } @@ -248,7 +250,10 @@ cl_print (struct cmdline_term *cl_term, grub_uint32_t c, { cl_term->pos.x = 0; if (cl_term->pos.y >= (unsigned) (cl_term->height - 1)) - cl_term->ystart--; + { + if (cl_term->ystart > 0) + cl_term->ystart--; + } else cl_term->pos.y++; grub_putcode ('\n', cl_term->term); diff --git a/grub-core/term/i386/pc/vga_text.c b/grub-core/term/i386/pc/vga_text.c index 669d06fad..b88fa9d2e 100644 --- a/grub-core/term/i386/pc/vga_text.c +++ b/grub-core/term/i386/pc/vga_text.c @@ -63,7 +63,8 @@ static grub_uint8_t cur_color = 0x7; static void screen_write_char (int x, int y, short c) { - VGA_TEXT_SCREEN[y * COLS + x] = grub_cpu_to_le16 (c); + if (x < COLS && y < ROWS && x >= 0 && y >= 0) + VGA_TEXT_SCREEN[y * COLS + x] = grub_cpu_to_le16 (c); } static short diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index ce431757c..6cb72403e 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -70,6 +70,8 @@ exec_show_error () { fi } +work_directory=${WORKDIR:-`mktemp -d "${TMPDIR:-/tmp}/grub-shell.XXXXXXXXXX"`} || exit 1 + . "${builddir}/grub-core/modinfo.sh" qemuopts="${GRUB_QEMU_OPTS}" serial_port=com0 @@ -79,7 +81,7 @@ pseries=n disk="hda " case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in *-emu) - device_map=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 + device_map="$work_directory/device.map" boot=emu console=console disk=0 @@ -313,14 +315,14 @@ for option in "$@"; do done if [ "x${source}" = x ] ; then - tmpfile=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 + tmpfile="$work_directory/testcase.cfg" while read REPLY; do echo "$REPLY" >> ${tmpfile} done source=${tmpfile} fi -cfgfile=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 +cfgfile="$work_directory/grub.cfg" cat <<EOF >${cfgfile} grubshell=yes enable_progress_indicator=0 @@ -354,7 +356,8 @@ if [ $trim = 1 ]; then echo "echo $trim_head" >>${cfgfile} fi -rom_directory=`mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 +rom_directory="$work_directory/rom_directory" +mkdir -p "$rom_directory" for mod in ${modules} do @@ -375,7 +378,7 @@ test -z "$debug" || echo "GRUB script: ${cfgfile}" >&2 test -z "$debug" || echo "GRUB testcase script: ${tmpfile}" >&2 test -z "$debug" || echo "Boot device: ${boot}" >&2 -isofile=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 +isofile="$work_directory/grub.iso" test -z "$debug" || echo "GRUB ISO file: ${isofile}" >&2 test -z "$debug" || echo "GRUB ROM directory: ${rom_directory}" >&2 @@ -451,7 +454,7 @@ if [ x$boot = xmips_qemu ]; then fi if [ x$boot = xcoreboot ]; then - imgfile=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 + imgfile="$work_directory/coreboot.img" cp "${GRUB_COREBOOT_ROM}" "${imgfile}" "${GRUB_CBFSTOOL}" "${imgfile}" add-payload -f "${rom_directory}/coreboot.elf" -n fallback/payload bootdev="-bios ${imgfile}" @@ -494,14 +497,15 @@ copy_extra_files() { } if [ x$boot = xnet ]; then - netdir=`mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 + netdir="$work_directory/netdir" + mkdir -p "$netdir" pkgdatadir="${builddir}" "${builddir}/grub-mknetdir" "--grub-mkimage=${builddir}/grub-mkimage" "--directory=${builddir}/grub-core" "--net-directory=$netdir" ${mkrescue_args} > /dev/null cp "${cfgfile}" "$netdir/boot/grub/grub.cfg" cp "${source}" "$netdir/boot/grub/testcase.cfg" [ -z "$files" ] || copy_extra_files "$netdir" $files timeout -s KILL $timeout "${qemu}" ${qemuopts} ${serial_null} -serial file:/dev/stdout -boot n -net "user,tftp=$netdir,bootfile=/boot/grub/${grub_modinfo_target_cpu}-${grub_modinfo_platform}/core.$netbootext" -net nic | cat | tr -d "\r" | do_trim elif [ x$boot = xemu ]; then - rootdir="$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")" + rootdir="$work_directory/rootdir" grubdir="$rootdir/boot/grub" mkdir -p "$grubdir/fonts" mkdir -p "$grubdir/themes" @@ -516,7 +520,7 @@ elif [ x$boot = xemu ]; then cp "${cfgfile}" "$grubdir/grub.cfg" cp "${source}" "$grubdir/testcase.cfg" [ -z "$files" ] || copy_extra_files "$rootdir" $files - roottar="$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")" + roottar="$work_directory/root.tar" (cd "$rootdir"; tar cf "$roottar" .) "${builddir}/grub-core/grub-emu" -m "$device_map" --memdisk "$roottar" -r memdisk -d "/boot/grub" | tr -d "\r" | do_trim test -n "$debug" || rm -rf "$rootdir" diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in index 44bf135b9..213ce1e57 100644 --- a/util/bash-completion.d/grub-completion.bash.in +++ b/util/bash-completion.d/grub-completion.bash.in @@ -52,14 +52,18 @@ __grubcomp () { COMPREPLY=() ;; *) - local IFS=' '$'\t'$'\n' - COMPREPLY=($(compgen -P "${2-}" -W "${1-}" -S "${4-}" -- "$cur")) + local line IFS=' '$'\t'$'\n' + COMPREPLY=() + while read -r line; do + COMPREPLY+=("${line}") + done < <(compgen -P "${2-}" -W "${1-}" -S "${4-}" -- "$cur") ;; esac } # Function that return long options from the help of the command # - arg: $1 (optional) command to get the long options from +# shellcheck disable=SC2120 __grub_get_options_from_help () { local prog @@ -112,28 +116,35 @@ __grub_get_last_option () { __grub_list_menuentries () { local cur="${COMP_WORDS[COMP_CWORD]}" - local config_file=$(__grub_dir)/grub.cfg + local config_file + config_file=$(__grub_dir)/grub.cfg if [ -f "$config_file" ];then - local IFS=$'\n' - COMPREPLY=( $(compgen \ - -W "$( awk -F "[\"']" '/menuentry/ { print $2 }' $config_file )" \ - -- "$cur" )) #'# Help emacs syntax highlighting + local line IFS=$'\n' + COMPREPLY=() + while read -r line; do + COMPREPLY+=("${line}") + done < <(compgen \ + -W "$( awk -F "[\"']" '/menuentry/ { print $2 }' $config_file )" \ + -- "$cur" ) #'# Help emacs syntax highlighting fi } __grub_list_modules () { - local grub_dir=$(__grub_dir) - local IFS=$'\n' - COMPREPLY=( $( compgen -f -X '!*/*.mod' -- "${grub_dir}/$cur" | { - while read -r tmp; do - [ -n $tmp ] && { - tmp=${tmp##*/} - printf '%s\n' ${tmp%.mod} - } - done - } - )) + local grub_dir + grub_dir=$(__grub_dir) + local line tmp IFS=$'\n' + COMPREPLY=() + while read -r line; do + COMPREPLY+=("${line}") + done < <(compgen -f -X '!*/*.mod' -- "${grub_dir}/$cur" | { + while read -r tmp; do + [ -n "$tmp" ] && { + tmp=${tmp##*/} + printf '%s\n' ${tmp%.mod} + } + done + }) } # diff --git a/util/grub-install.c b/util/grub-install.c index 1c0ece7f1..53b464804 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -827,6 +827,19 @@ fill_core_services (const char *core_services) free (sysv_plist); } +#ifdef __linux__ +static void +try_open (const char *path) +{ + FILE *f; + + f = grub_util_fopen (path, "r+"); + if (f == NULL) + grub_util_error (_("Unable to open %s: %s"), path, strerror (errno)); + fclose (f); +} +#endif + int main (int argc, char *argv[]) { @@ -1026,6 +1039,20 @@ main (int argc, char *argv[]) break; } + switch (platform) + { + case GRUB_INSTALL_PLATFORM_I386_IEEE1275: + case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: +#ifdef __linux__ + /* On Linux, ensure /dev/nvram is _functional_. */ + if (update_nvram) + try_open ("/dev/nvram"); +#endif + break; + default: + break; + } + /* Find the EFI System Partition. */ if (is_efi) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 7263f2983..cc393be7e 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -43,13 +43,11 @@ case ${GRUB_DEVICE} in ;; esac -if [ "x${GRUB_CMDLINE_LINUX_RECOVERY}" = "x" ] ; then - GRUB_CMDLINE_LINUX_RECOVERY=single -fi +: ${GRUB_CMDLINE_LINUX_RECOVERY:=single} # Default to disabling partition uuid support to maintian compatibility with # older kernels. -GRUB_DISABLE_LINUX_PARTUUID=${GRUB_DISABLE_LINUX_PARTUUID-true} +: ${GRUB_DISABLE_LINUX_PARTUUID=true} # btrfs may reside on multiple devices. We cannot pass them as value of root= parameter # and mounting btrfs requires user space scanning, so force UUID in this case. diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 386bfb9be..c1ebd0953 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -43,13 +43,11 @@ case ${GRUB_DEVICE} in ;; esac -if [ "x${GRUB_CMDLINE_LINUX_RECOVERY}" = "x" ] ; then - GRUB_CMDLINE_LINUX_RECOVERY=single -fi +: ${GRUB_CMDLINE_LINUX_RECOVERY:=single} # Default to disabling partition uuid support to maintian compatibility with # older kernels. -GRUB_DISABLE_LINUX_PARTUUID=${GRUB_DISABLE_LINUX_PARTUUID-true} +: ${GRUB_DISABLE_LINUX_PARTUUID=true} # btrfs may reside on multiple devices. We cannot pass them as value of root= parameter # and mounting btrfs requires user space scanning, so force UUID in this case. diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in index c1731e5bb..1c2365ddb 100644 --- a/util/grub.d/30_uefi-firmware.in +++ b/util/grub.d/30_uefi-firmware.in @@ -31,10 +31,12 @@ LABEL="UEFI Firmware Settings" gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2 cat << EOF -fwsetup --is-supported -if [ "\$grub_platform" = "efi" -a "\$?" = 0 ]; then - menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { - fwsetup - } +if [ "\$grub_platform" = "efi" ]; then + fwsetup --is-supported + if [ "\$?" = 0 ]; then + menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { + fwsetup + } + fi fi EOF 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 50c811a88..8b1a21166 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4306,6 +4306,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 @@ -4593,6 +4594,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 @@ -5155,6 +5164,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 95942fc8c..ba967aac8 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1184,6 +1184,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); +} -- 2.39.0 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel