Re: [RFC PATCH v2] ovl: don't follow redirects if redirect_dir=off

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

 



On Wed, Dec 6, 2017 at 6:33 PM, Amir Goldstein <amir73il@xxxxxxxxx> wrote:
> On Wed, Dec 6, 2017 at 5:03 PM, Miklos Szeredi <miklos@xxxxxxxxxx> wrote:
>> Overlayfs is following redirects even when redirects are disabled. If this
>> is unintentional (probably the majority of cases) then this can be a
>> problem.  E.g. upper layer comes from untrusted USB drive, and attacker
>> crafts a redirect to enable read access to otherwise unreadable
>> directories.
>>
>> If "redirect=off", then turn off following as well as creation of
>> redirects.  If "redirect=follow", then turn on following, but turn off
>> creation of redirects (which is what "redirect=off" does now).
>>
>
> s/redirect=/redirect_dir=/
>
>
>> This is a backward incompatible change, so make it dependent on a config
>> option.
>>
>> Reported-by: David Howells <dhowells@xxxxxxxxxx>
>> Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxxxxx>
>> ---
>> v2:
>>  - added module option for always following (defaulting to Kconfig)
>>  - added mount option "nofollow" to force not following even if default is to
>>    always follow
>>  - fixed .show_options()
>>  - added more documentation to overlayfs.txt and removed some from Kconfig
>>
>>  Documentation/filesystems/overlayfs.txt |   34 +++++++++++++++++++
>>  fs/overlayfs/Kconfig                    |   10 +++++
>>  fs/overlayfs/namei.c                    |   16 +++++++++
>>  fs/overlayfs/ovl_entry.h                |    2 +
>>  fs/overlayfs/super.c                    |   56 ++++++++++++++++++++++----------
>>  5 files changed, 102 insertions(+), 16 deletions(-)
>>
>> --- a/fs/overlayfs/namei.c
>> +++ b/fs/overlayfs/namei.c
>> @@ -681,6 +681,22 @@ struct dentry *ovl_lookup(struct inode *
>>                 if (d.stop)
>>                         break;
>>
>> +               /*
>> +                * Following redirects can have security consequences: it's like
>> +                * a symlink into the lower layer without the permission checks.
>> +                * This is only a problem if the upper layer is untrusted (e.g
>> +                * comes from an USB drive).  This can allow a non-readable file
>> +                * or directory to become readable.
>> +                *
>> +                * Only following redirects when redirects are enabled disables
>> +                * this attack vector when not necessary.
>> +                */
>> +               err = -EPERM;
>> +               if (d.redirect && !ofs->config.redirect_follow) {
>> +                       pr_warn_ratelimited("overlay: refusing to follow redirect for (%pd2)\n", dentry);
>> +                       goto out_put;
>> +               }
>> +
>>                 if (d.redirect && d.redirect[0] == '/' && poe != roe) {
>>                         poe = roe;
>>
>> --- a/fs/overlayfs/Kconfig
>> +++ b/fs/overlayfs/Kconfig
>> @@ -24,6 +24,16 @@ config OVERLAY_FS_REDIRECT_DIR
>>           an overlay which has redirects on a kernel that doesn't support this
>>           feature will have unexpected results.
>>
>> +config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW
>> +       bool "Overlayfs: follow redirects even if redirects are turned off"
>> +       default y
>> +       depends on OVERLAY_FS
>> +       help
>> +         Disable this to get a possibly more secure configuration, but that
>> +         might not be backward compatible with previous kernels.
>> +
>> +         For more information, see Documentation/filesystems/overlayfs.txt
>> +
>>  config OVERLAY_FS_INDEX
>>         bool "Overlayfs: turn on inodes index feature by default"
>>         depends on OVERLAY_FS
>> --- a/fs/overlayfs/ovl_entry.h
>> +++ b/fs/overlayfs/ovl_entry.h
>> @@ -14,6 +14,8 @@ struct ovl_config {
>>         char *workdir;
>>         bool default_permissions;
>>         bool redirect_dir;
>> +       bool redirect_follow;
>
> IMO the configuration modes would be better describes by:
>
> enum ovl_redirect {
>    OVL_REDIRECT_OFF,
>    OVL_REDIRECT_FOLLOW,
>    OVL_REDIRECT_ON,
> }
>
> Making the combination (!redirect_follow && redirect_dir) impossible
>
> instead of testing ofs->config.redirect_follow, can test
> ofs->config.redirect >= OVL_REDIRECT_FOLLOW
>
> Then I could later add:
>     OVL_REDIRECT_FIX > OVL_REDIRECT_ON
>
> to restore redirect from origin.
>
>> +       const char *redirect_mode;
>>         bool index;
>>  };
>>
>> --- a/fs/overlayfs/super.c
>> +++ b/fs/overlayfs/super.c
>> @@ -33,6 +33,13 @@ module_param_named(redirect_dir, ovl_red
>>  MODULE_PARM_DESC(ovl_redirect_dir_def,
>>                  "Default to on or off for the redirect_dir feature");
>>
>> +static bool ovl_redirect_always_follow =
>> +       IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW);
>> +module_param_named(redirect_always_follow, ovl_redirect_always_follow,
>> +                  bool, 0644);
>> +MODULE_PARM_DESC(ovl_redirect_always_follow,
>> +                "Follow redirects even if redirect_dir feature is turned off");
>> +
>>  static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
>>  module_param_named(index, ovl_index_def, bool, 0644);
>>  MODULE_PARM_DESC(ovl_index_def,
>> @@ -232,6 +239,7 @@ static void ovl_free_fs(struct ovl_fs *o
>>         kfree(ofs->config.lowerdir);
>>         kfree(ofs->config.upperdir);
>>         kfree(ofs->config.workdir);
>> +       kfree(ofs->config.redirect_mode);
>>         if (ofs->creator_cred)
>>                 put_cred(ofs->creator_cred);
>>         kfree(ofs);
>> @@ -295,6 +303,11 @@ static bool ovl_force_readonly(struct ov
>>         return (!ofs->upper_mnt || !ofs->workdir);
>>  }
>>
>> +static const char *ovl_def_mode_str(void)
>
> ovl_redirect_mode_def()?
>
>> +{
>> +       return ovl_redirect_dir_def ? "on" : "off";
>> +}
>> +
>>  /**
>>   * ovl_show_options
>>   *
>> @@ -313,12 +326,10 @@ static int ovl_show_options(struct seq_f
>>         }
>>         if (ofs->config.default_permissions)
>>                 seq_puts(m, ",default_permissions");
>> -       if (ofs->config.redirect_dir != ovl_redirect_dir_def)
>> -               seq_printf(m, ",redirect_dir=%s",
>> -                          ofs->config.redirect_dir ? "on" : "off");
>> +       if (strcmp(ofs->config.redirect_mode, ovl_def_mode_str()) != 0)
>> +               seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode);
>>         if (ofs->config.index != ovl_index_def)
>> -               seq_printf(m, ",index=%s",
>> -                          ofs->config.index ? "on" : "off");
>> +               seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
>>         return 0;
>>  }
>>
>> @@ -348,8 +359,7 @@ enum {
>>         OPT_UPPERDIR,
>>         OPT_WORKDIR,
>>         OPT_DEFAULT_PERMISSIONS,
>> -       OPT_REDIRECT_DIR_ON,
>> -       OPT_REDIRECT_DIR_OFF,
>> +       OPT_REDIRECT_DIR,
>>         OPT_INDEX_ON,
>>         OPT_INDEX_OFF,
>>         OPT_ERR,
>> @@ -360,8 +370,7 @@ static const match_table_t ovl_tokens =
>>         {OPT_UPPERDIR,                  "upperdir=%s"},
>>         {OPT_WORKDIR,                   "workdir=%s"},
>>         {OPT_DEFAULT_PERMISSIONS,       "default_permissions"},
>> -       {OPT_REDIRECT_DIR_ON,           "redirect_dir=on"},
>> -       {OPT_REDIRECT_DIR_OFF,          "redirect_dir=off"},
>> +       {OPT_REDIRECT_DIR,              "redirect_dir=%s"},
>>         {OPT_INDEX_ON,                  "index=on"},
>>         {OPT_INDEX_OFF,                 "index=off"},
>>         {OPT_ERR,                       NULL}
>> @@ -428,12 +437,11 @@ static int ovl_parse_opt(char *opt, stru
>>                         config->default_permissions = true;
>>                         break;
>>
>> -               case OPT_REDIRECT_DIR_ON:
>> -                       config->redirect_dir = true;
>> -                       break;
>> -
>> -               case OPT_REDIRECT_DIR_OFF:
>> -                       config->redirect_dir = false;
>> +               case OPT_REDIRECT_DIR:
>> +                       kfree(config->redirect_mode);
>> +                       config->redirect_mode = match_strdup(&args[0]);
>> +                       if (!config->redirect_mode)
>> +                               return -ENOMEM;
>>                         break;
>>
>>                 case OPT_INDEX_ON:
>> @@ -1160,12 +1168,28 @@ static int ovl_fill_super(struct super_b
>>         if (!cred)
>>                 goto out_err;
>>
>> -       ofs->config.redirect_dir = ovl_redirect_dir_def;
>> +       ofs->config.redirect_mode = kstrdup(ovl_def_mode_str(), GFP_KERNEL);
>> +       if (!ofs->config.redirect_mode)
>> +               goto out_err;
>> +
>>         ofs->config.index = ovl_index_def;
>>         err = ovl_parse_opt((char *) data, &ofs->config);
>>         if (err)
>>                 goto out_err;
>>
>
> And then this would look nicer IMO, maybe even can use token parser:
>
>> +       if (strcmp(ofs->config.redirect_mode, "on") == 0) {
>> +               ofs->config.redirect_dir = true;
>> +               ofs->config.redirect_follow = true;
>
>                     ofs->config.redirect = OVL_REDIRECT_ON
>
>> +       } else if (strcmp(ofs->config.redirect_mode, "follow") == 0) {
>> +               ofs->config.redirect_follow = true;
>
>                     ofs->config.redirect = OVL_REDIRECT_FOLLOW
>
>> +       } else if (strcmp(ofs->config.redirect_mode, "off") == 0) {
>> +               if (ovl_redirect_always_follow)
>> +                       ofs->config.redirect_follow = true;
>
>                              ofs->config.redirect = OVL_REDIRECT_FOLLOW;
>
>> +       } else if (strcmp(ofs->config.redirect_mode, "nofollow") != 0) {
>> +               pr_err("overlayfs: bad mount option \"redirect_dir=%s\"\n",
>> +                       ofs->config.redirect_mode);
>> +       }
>> +
>
> Helper? ovl_parse_redirect_mode()?
>

And better call this helper from within ovl_parse_opt() instead of polluting
  ovl_fill_super().

Amir.
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux