grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v10] plainmount: Support plain encryption mode


From: Maxim Fomin
Subject: [PATCH v10] plainmount: Support plain encryption mode
Date: Wed, 28 Dec 2022 17:20:00 +0000

>From e7f54a0b78353c01bf4c966f871b3e3590222743 Mon Sep 17 00:00:00 2001
From: Maxim Fomin <maxim@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 <maxim@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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]