grub-devel
[Top][All Lists]
Advanced

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

Re: [PATCH v6] load_env support for whitelisting, save_env support for c


From: Vladimir 'φ-coder/phcoder' Serbinenko
Subject: Re: [PATCH v6] load_env support for whitelisting, save_env support for check_signatures
Date: Fri, 27 Sep 2013 02:09:37 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130821 Icedove/17.0.8

Committed, thanks.
On 27.09.2013 01:54, Jon McCune wrote:
> Whitelisting which variables are read from an env file prevents a
> malicious environment block file from overwriting the value of
> security-critical environment variables such as check_signatures,
> while still allowing a properly constructed configuration file to
> offer "savedefault" and "one-shot" functionality.
> 
> Tested with 'make check' to the best of my ability. Failures (both
> before and after the changes proposed in this patch set, i.e.,
> unchanged by this patch set):
> gettext_strings_test, fddboot_test, grub_func_test
> 
> Changes in v6 of the patch:
> 
>   filter disable_...() semantics are such that save/restore is
>   not necessary. grub_file_open() will re-enable all disabled filters
>   right after opening the target file.
> 
> Changes in v5 of the patch:
> 
>   Drop whitespace changes.
> 
>   Drop grub-install script changes.
> 
>   Generalized hook support in grub_envblk_iterate().
>   Whitelist-specific hook data structures are self-contained in
>   loadenv.c.
> 
>   Add flag {-s, --skip-sig} to load_env command that explicitly
>   controls whether signature-checking is required.  Whitelisting
>   and signature checking are thus controlled independently, and
>   the user may now choose to load only whitelisted variables from
>   either of a signed or unsigned grubenv-style file.
> 
>   Add untrusted flag to open_envblk_file().  Replaces the v4
>   patch's function open_envblk_file_untrusted() with a flag
>   passed to the existing open_envblk_file().  Also restructured
>   open_envblk_file() to make it more readable with the additional
>   logic.
> 
>   open_envblk_file(), when untrusted == 1, disables filters using
>   the compression- and pubkey-specific methods in file.h. The
>   pubkey filter is saved and restored across untrusted file opens.
> 
>   save_env always calls grub_envblk_file() with untrusted set to 1.
>   The contents that are read from the file are discarded, as the
>   only purpose of reading the file is to construct the blocklist to
>   which the new environment block will be written.  Thus, the
>   actual contents of the file are not parsed and do not pose a
>   security risk.
> 
> Signed-off-by: Jon McCune <address@hidden>
> ---
>  grub-core/commands/loadenv.c | 108 
> ++++++++++++++++++++++++++++++-------------
>  grub-core/lib/envblk.c       |   5 +-
>  include/grub/lib/envblk.h    |   3 +-
>  util/grub-editenv.c          |   5 +-
>  4 files changed, 84 insertions(+), 37 deletions(-)
> 
> diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c
> index c0a42c5..0062c77 100644
> --- a/grub-core/commands/loadenv.c
> +++ b/grub-core/commands/loadenv.c
> @@ -35,45 +35,54 @@ static const struct grub_arg_option options[] =
>      /* TRANSLATORS: This option is used to override default filename
>         for loading and storing environment.  */
>      {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
> +    {"skip-sig", 's', 0,
> +     N_("Skip signature-checking of the environment file."), 0, 
> ARG_TYPE_NONE},
>      {0, 0, 0, 0, 0, 0}
>    };
>  
> +/* Opens 'filename' with compression filters disabled. Optionally disables 
> the
> +   PUBKEY filter (that insists upon properly signed files) as well.  PUBKEY
> +   filter is restored before the function returns. */
>  static grub_file_t
> -open_envblk_file (char *filename)
> +open_envblk_file (char *filename, int untrusted)
>  {
>    grub_file_t file;
> +  char *buf = 0;
>  
>    if (! filename)
>      {
>        const char *prefix;
> +      int len;
>  
>        prefix = grub_env_get ("prefix");
> -      if (prefix)
> -        {
> -          int len;
> -
> -          len = grub_strlen (prefix);
> -          filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
> -          if (! filename)
> -            return 0;
> -
> -          grub_strcpy (filename, prefix);
> -          filename[len] = '/';
> -          grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
> -       grub_file_filter_disable_compression ();
> -          file = grub_file_open (filename);
> -          grub_free (filename);
> -          return file;
> -        }
> -      else
> +      if (! prefix)
>          {
>            grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't 
> set"), "prefix");
>            return 0;
>          }
> +
> +      len = grub_strlen (prefix);
> +      buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
> +      if (! buf)
> +        return 0;
> +      filename = buf;
> +
> +      grub_strcpy (filename, prefix);
> +      filename[len] = '/';
> +      grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
>      }
>  
> +  /* The filters that are disabled will be re-enabled by the call to
> +     grub_file_open() after this particular file is opened. */
>    grub_file_filter_disable_compression ();
> -  return grub_file_open (filename);
> +  if (untrusted)
> +    grub_file_filter_disable_pubkey ();
> +
> +  file = grub_file_open (filename);
> +
> +  if (buf)
> +    grub_free (buf);
> +  return file;
>  }
>  
>  static grub_envblk_t
> @@ -114,24 +123,55 @@ read_envblk_file (grub_file_t file)
>    return envblk;
>  }
>  
> +struct grub_env_whitelist
> +{
> +  grub_size_t len;
> +  char **list;
> +};
> +typedef struct grub_env_whitelist grub_env_whitelist_t;
> +
> +static int
> +test_whitelist_membership (const char* name,
> +                           const grub_env_whitelist_t* whitelist)
> +{
> +  grub_size_t i;
> +
> +  for (i = 0; i < whitelist->len; i++)
> +    if (grub_strcmp (name, whitelist->list[i]) == 0)
> +      return 1;  /* found it */
> +
> +  return 0;  /* not found */
> +}
> +
>  /* Helper for grub_cmd_load_env.  */
>  static int
> -set_var (const char *name, const char *value)
> +set_var (const char *name, const char *value, void *whitelist)
>  {
> -  grub_env_set (name, value);
> +  if (! whitelist)
> +    {
> +      grub_env_set (name, value);
> +      return 0;
> +    }
> +
> +  if (test_whitelist_membership (name, (const 
> grub_env_whitelist_t*)whitelist))
> +    grub_env_set (name, value);
> +
>    return 0;
>  }
>  
>  static grub_err_t
> -grub_cmd_load_env (grub_extcmd_context_t ctxt,
> -                int argc __attribute__ ((unused)),
> -                char **args __attribute__ ((unused)))
> +grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
>  {
>    struct grub_arg_list *state = ctxt->state;
>    grub_file_t file;
>    grub_envblk_t envblk;
> +  grub_env_whitelist_t whitelist;
> +
> +  whitelist.len = argc;
> +  whitelist.list = args;
>  
> -  file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
> +  /* state[0] is the -f flag; state[1] is the --skip-sig flag */
> +  file = open_envblk_file ((state[0].set) ? state[0].arg : 0, state[1].set);
>    if (! file)
>      return grub_errno;
>  
> @@ -139,7 +179,8 @@ grub_cmd_load_env (grub_extcmd_context_t ctxt,
>    if (! envblk)
>      goto fail;
>  
> -  grub_envblk_iterate (envblk, set_var);
> +  /* argc > 0 indicates caller provided a whitelist of variables to read. */
> +  grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var);
>    grub_envblk_close (envblk);
>  
>   fail:
> @@ -149,7 +190,8 @@ grub_cmd_load_env (grub_extcmd_context_t ctxt,
>  
>  /* Print all variables in current context.  */
>  static int
> -print_var (const char *name, const char *value)
> +print_var (const char *name, const char *value,
> +           void *hook_data __attribute__ ((unused)))
>  {
>    grub_printf ("%s=%s\n", name, value);
>    return 0;
> @@ -164,7 +206,7 @@ grub_cmd_list_env (grub_extcmd_context_t ctxt,
>    grub_file_t file;
>    grub_envblk_t envblk;
>  
> -  file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
> +  file = open_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
>    if (! file)
>      return grub_errno;
>  
> @@ -172,7 +214,7 @@ grub_cmd_list_env (grub_extcmd_context_t ctxt,
>    if (! envblk)
>      goto fail;
>  
> -  grub_envblk_iterate (envblk, print_var);
> +  grub_envblk_iterate (envblk, NULL, print_var);
>    grub_envblk_close (envblk);
>  
>   fail:
> @@ -333,7 +375,8 @@ grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, 
> char **args)
>    if (! argc)
>      return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
>  
> -  file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
> +  file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
> +                           1 /* allow untrusted */);
>    if (! file)
>      return grub_errno;
>  
> @@ -386,7 +429,8 @@ static grub_extcmd_t cmd_load, cmd_list, cmd_save;
>  GRUB_MOD_INIT(loadenv)
>  {
>    cmd_load =
> -    grub_register_extcmd ("load_env", grub_cmd_load_env, 0, N_("[-f FILE]"),
> +    grub_register_extcmd ("load_env", grub_cmd_load_env, 0,
> +                       N_("[-f FILE] [-s|--skip-sig] 
> [whitelisted_variable_name] [...]"),
>                         N_("Load variables from environment block file."),
>                         options);
>    cmd_list =
> diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c
> index 311927b..230e0e9 100644
> --- a/grub-core/lib/envblk.c
> +++ b/grub-core/lib/envblk.c
> @@ -225,7 +225,8 @@ grub_envblk_delete (grub_envblk_t envblk, const char 
> *name)
>  
>  void
>  grub_envblk_iterate (grub_envblk_t envblk,
> -                     int hook (const char *name, const char *value))
> +                     void *hook_data,
> +                     int hook (const char *name, const char *value, void 
> *hook_data))
>  {
>    char *p, *pend;
>  
> @@ -285,7 +286,7 @@ grub_envblk_iterate (grub_envblk_t envblk,
>              }
>            *q = '\0';
>  
> -          ret = hook (name, value);
> +          ret = hook (name, value, hook_data);
>            grub_free (name);
>            if (ret)
>              return;
> diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h
> index 368ba53..c3e6559 100644
> --- a/include/grub/lib/envblk.h
> +++ b/include/grub/lib/envblk.h
> @@ -35,7 +35,8 @@ grub_envblk_t grub_envblk_open (char *buf, grub_size_t 
> size);
>  int grub_envblk_set (grub_envblk_t envblk, const char *name, const char 
> *value);
>  void grub_envblk_delete (grub_envblk_t envblk, const char *name);
>  void grub_envblk_iterate (grub_envblk_t envblk,
> -                          int hook (const char *name, const char *value));
> +                          void *hook_data,
> +                          int hook (const char *name, const char *value, 
> void *hook_data));
>  void grub_envblk_close (grub_envblk_t envblk);
>  
>  static inline char *
> diff --git a/util/grub-editenv.c b/util/grub-editenv.c
> index 9b51acf..591604b 100644
> --- a/util/grub-editenv.c
> +++ b/util/grub-editenv.c
> @@ -185,7 +185,8 @@ open_envblk_file (const char *name)
>  }
>  
>  static int
> -print_var (const char *varname, const char *value)
> +print_var (const char *varname, const char *value,
> +           void *hook_data __attribute__ ((unused)))
>  {
>    printf ("%s=%s\n", varname, value);
>    return 0;
> @@ -197,7 +198,7 @@ list_variables (const char *name)
>    grub_envblk_t envblk;
>  
>    envblk = open_envblk_file (name);
> -  grub_envblk_iterate (envblk, print_var);
> +  grub_envblk_iterate (envblk, NULL, print_var);
>    grub_envblk_close (envblk);
>  }
>  
> 


Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

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