Skip to content

Commit 438c84c

Browse files
author
Miklos Szeredi
committed
ovl: don't follow redirects if redirect_dir=off
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_dir=off", then turn off following as well as creation of redirects. If "redirect_dir=follow", then turn on following, but turn off creation of redirects (which is what "redirect_dir=off" does now). This is a backward incompatible change, so make it dependent on a config option. Reported-by: David Howells <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent ae64f9b commit 438c84c

File tree

5 files changed

+113
-17
lines changed

5 files changed

+113
-17
lines changed

Documentation/filesystems/overlayfs.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,40 @@ handle it in two different ways:
156156
root of the overlay. Finally the directory is moved to the new
157157
location.
158158

159+
There are several ways to tune the "redirect_dir" feature.
160+
161+
Kernel config options:
162+
163+
- OVERLAY_FS_REDIRECT_DIR:
164+
If this is enabled, then redirect_dir is turned on by default.
165+
- OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW:
166+
If this is enabled, then redirects are always followed by default. Enabling
167+
this results in a less secure configuration. Enable this option only when
168+
worried about backward compatibility with kernels that have the redirect_dir
169+
feature and follow redirects even if turned off.
170+
171+
Module options (can also be changed through /sys/module/overlay/parameters/*):
172+
173+
- "redirect_dir=BOOL":
174+
See OVERLAY_FS_REDIRECT_DIR kernel config option above.
175+
- "redirect_always_follow=BOOL":
176+
See OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW kernel config option above.
177+
- "redirect_max=NUM":
178+
The maximum number of bytes in an absolute redirect (default is 256).
179+
180+
Mount options:
181+
182+
- "redirect_dir=on":
183+
Redirects are enabled.
184+
- "redirect_dir=follow":
185+
Redirects are not created, but followed.
186+
- "redirect_dir=off":
187+
Redirects are not created and only followed if "redirect_always_follow"
188+
feature is enabled in the kernel/module config.
189+
- "redirect_dir=nofollow":
190+
Redirects are not created and not followed (equivalent to "redirect_dir=off"
191+
if "redirect_always_follow" feature is not enabled).
192+
159193
Non-directories
160194
---------------
161195

fs/overlayfs/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ config OVERLAY_FS_REDIRECT_DIR
2424
an overlay which has redirects on a kernel that doesn't support this
2525
feature will have unexpected results.
2626

27+
config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW
28+
bool "Overlayfs: follow redirects even if redirects are turned off"
29+
default y
30+
depends on OVERLAY_FS
31+
help
32+
Disable this to get a possibly more secure configuration, but that
33+
might not be backward compatible with previous kernels.
34+
35+
For more information, see Documentation/filesystems/overlayfs.txt
36+
2737
config OVERLAY_FS_INDEX
2838
bool "Overlayfs: turn on inodes index feature by default"
2939
depends on OVERLAY_FS

fs/overlayfs/namei.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
681681
if (d.stop)
682682
break;
683683

684+
/*
685+
* Following redirects can have security consequences: it's like
686+
* a symlink into the lower layer without the permission checks.
687+
* This is only a problem if the upper layer is untrusted (e.g
688+
* comes from an USB drive). This can allow a non-readable file
689+
* or directory to become readable.
690+
*
691+
* Only following redirects when redirects are enabled disables
692+
* this attack vector when not necessary.
693+
*/
694+
err = -EPERM;
695+
if (d.redirect && !ofs->config.redirect_follow) {
696+
pr_warn_ratelimited("overlay: refusing to follow redirect for (%pd2)\n", dentry);
697+
goto out_put;
698+
}
699+
684700
if (d.redirect && d.redirect[0] == '/' && poe != roe) {
685701
poe = roe;
686702

fs/overlayfs/ovl_entry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ struct ovl_config {
1414
char *workdir;
1515
bool default_permissions;
1616
bool redirect_dir;
17+
bool redirect_follow;
18+
const char *redirect_mode;
1719
bool index;
1820
};
1921

fs/overlayfs/super.c

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
3333
MODULE_PARM_DESC(ovl_redirect_dir_def,
3434
"Default to on or off for the redirect_dir feature");
3535

36+
static bool ovl_redirect_always_follow =
37+
IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW);
38+
module_param_named(redirect_always_follow, ovl_redirect_always_follow,
39+
bool, 0644);
40+
MODULE_PARM_DESC(ovl_redirect_always_follow,
41+
"Follow redirects even if redirect_dir feature is turned off");
42+
3643
static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
3744
module_param_named(index, ovl_index_def, bool, 0644);
3845
MODULE_PARM_DESC(ovl_index_def,
@@ -232,6 +239,7 @@ static void ovl_free_fs(struct ovl_fs *ofs)
232239
kfree(ofs->config.lowerdir);
233240
kfree(ofs->config.upperdir);
234241
kfree(ofs->config.workdir);
242+
kfree(ofs->config.redirect_mode);
235243
if (ofs->creator_cred)
236244
put_cred(ofs->creator_cred);
237245
kfree(ofs);
@@ -295,6 +303,11 @@ static bool ovl_force_readonly(struct ovl_fs *ofs)
295303
return (!ofs->upper_mnt || !ofs->workdir);
296304
}
297305

306+
static const char *ovl_redirect_mode_def(void)
307+
{
308+
return ovl_redirect_dir_def ? "on" : "off";
309+
}
310+
298311
/**
299312
* ovl_show_options
300313
*
@@ -313,12 +326,10 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
313326
}
314327
if (ofs->config.default_permissions)
315328
seq_puts(m, ",default_permissions");
316-
if (ofs->config.redirect_dir != ovl_redirect_dir_def)
317-
seq_printf(m, ",redirect_dir=%s",
318-
ofs->config.redirect_dir ? "on" : "off");
329+
if (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0)
330+
seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode);
319331
if (ofs->config.index != ovl_index_def)
320-
seq_printf(m, ",index=%s",
321-
ofs->config.index ? "on" : "off");
332+
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
322333
return 0;
323334
}
324335

@@ -348,8 +359,7 @@ enum {
348359
OPT_UPPERDIR,
349360
OPT_WORKDIR,
350361
OPT_DEFAULT_PERMISSIONS,
351-
OPT_REDIRECT_DIR_ON,
352-
OPT_REDIRECT_DIR_OFF,
362+
OPT_REDIRECT_DIR,
353363
OPT_INDEX_ON,
354364
OPT_INDEX_OFF,
355365
OPT_ERR,
@@ -360,8 +370,7 @@ static const match_table_t ovl_tokens = {
360370
{OPT_UPPERDIR, "upperdir=%s"},
361371
{OPT_WORKDIR, "workdir=%s"},
362372
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
363-
{OPT_REDIRECT_DIR_ON, "redirect_dir=on"},
364-
{OPT_REDIRECT_DIR_OFF, "redirect_dir=off"},
373+
{OPT_REDIRECT_DIR, "redirect_dir=%s"},
365374
{OPT_INDEX_ON, "index=on"},
366375
{OPT_INDEX_OFF, "index=off"},
367376
{OPT_ERR, NULL}
@@ -390,10 +399,37 @@ static char *ovl_next_opt(char **s)
390399
return sbegin;
391400
}
392401

402+
static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode)
403+
{
404+
if (strcmp(mode, "on") == 0) {
405+
config->redirect_dir = true;
406+
/*
407+
* Does not make sense to have redirect creation without
408+
* redirect following.
409+
*/
410+
config->redirect_follow = true;
411+
} else if (strcmp(mode, "follow") == 0) {
412+
config->redirect_follow = true;
413+
} else if (strcmp(mode, "off") == 0) {
414+
if (ovl_redirect_always_follow)
415+
config->redirect_follow = true;
416+
} else if (strcmp(mode, "nofollow") != 0) {
417+
pr_err("overlayfs: bad mount option \"redirect_dir=%s\"\n",
418+
mode);
419+
return -EINVAL;
420+
}
421+
422+
return 0;
423+
}
424+
393425
static int ovl_parse_opt(char *opt, struct ovl_config *config)
394426
{
395427
char *p;
396428

429+
config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
430+
if (!config->redirect_mode)
431+
return -ENOMEM;
432+
397433
while ((p = ovl_next_opt(&opt)) != NULL) {
398434
int token;
399435
substring_t args[MAX_OPT_ARGS];
@@ -428,12 +464,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
428464
config->default_permissions = true;
429465
break;
430466

431-
case OPT_REDIRECT_DIR_ON:
432-
config->redirect_dir = true;
433-
break;
434-
435-
case OPT_REDIRECT_DIR_OFF:
436-
config->redirect_dir = false;
467+
case OPT_REDIRECT_DIR:
468+
kfree(config->redirect_mode);
469+
config->redirect_mode = match_strdup(&args[0]);
470+
if (!config->redirect_mode)
471+
return -ENOMEM;
437472
break;
438473

439474
case OPT_INDEX_ON:
@@ -458,7 +493,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
458493
config->workdir = NULL;
459494
}
460495

461-
return 0;
496+
return ovl_parse_redirect_mode(config, config->redirect_mode);
462497
}
463498

464499
#define OVL_WORKDIR_NAME "work"
@@ -1160,7 +1195,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
11601195
if (!cred)
11611196
goto out_err;
11621197

1163-
ofs->config.redirect_dir = ovl_redirect_dir_def;
11641198
ofs->config.index = ovl_index_def;
11651199
err = ovl_parse_opt((char *) data, &ofs->config);
11661200
if (err)

0 commit comments

Comments
 (0)