* Convert from filesystem to in-memory representation.
*/
static struct posix_acl *
-ext4_acl_from_disk(const void *value, size_t size)
+ext4_acl_from_disk(struct super_block *sb, const void *value, size_t size)
{
const char *end = (char *)value + size;
int n, count;
if ((char *)value > end)
goto fail;
acl->a_entries[n].e_uid =
- make_kuid(&init_user_ns,
+ make_kuid(sb->s_user_ns,
le32_to_cpu(entry->e_id));
+ if (!uid_valid(acl->a_entries[n].e_uid))
+ goto fail;
break;
case ACL_GROUP:
value = (char *)value + sizeof(ext4_acl_entry);
if ((char *)value > end)
goto fail;
acl->a_entries[n].e_gid =
- make_kgid(&init_user_ns,
+ make_kgid(sb->s_user_ns,
le32_to_cpu(entry->e_id));
+ if (!gid_valid(acl->a_entries[n].e_gid))
+ goto fail;
break;
default:
* Convert from in-memory to filesystem representation.
*/
static void *
-ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
+ext4_acl_to_disk(struct super_block *sb, const struct posix_acl *acl,
+ size_t *size)
{
ext4_acl_header *ext_acl;
char *e;
size_t n;
+ uid_t uid;
+ gid_t gid;
*size = ext4_acl_size(acl->a_count);
ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
entry->e_perm = cpu_to_le16(acl_e->e_perm);
switch (acl_e->e_tag) {
case ACL_USER:
- entry->e_id = cpu_to_le32(
- from_kuid(&init_user_ns, acl_e->e_uid));
+ uid = from_kuid(sb->s_user_ns, acl_e->e_uid);
+ if (uid == (uid_t)-1)
+ goto fail;
+ entry->e_id = cpu_to_le32(uid);
e += sizeof(ext4_acl_entry);
break;
case ACL_GROUP:
- entry->e_id = cpu_to_le32(
- from_kgid(&init_user_ns, acl_e->e_gid));
+ gid = from_kgid(sb->s_user_ns, acl_e->e_gid);
+ if (gid == (gid_t)-1)
+ goto fail;
+ entry->e_id = cpu_to_le32(gid);
e += sizeof(ext4_acl_entry);
break;
retval = ext4_xattr_get(inode, name_index, "", value, retval);
}
if (retval > 0)
- acl = ext4_acl_from_disk(value, retval);
+ acl = ext4_acl_from_disk(inode->i_sb, value, retval);
else if (retval == -ENODATA || retval == -ENOSYS)
acl = NULL;
else
return -EINVAL;
}
if (acl) {
- value = ext4_acl_to_disk(acl, &size);
+ value = ext4_acl_to_disk(inode->i_sb, acl, &size);
if (IS_ERR(value))
return (int)PTR_ERR(value);
}
/* Hm, nope. Are (enough) root reserved clusters available? */
if (uid_eq(sbi->s_resuid, current_fsuid()) ||
- (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) && in_group_p(sbi->s_resgid)) ||
- capable(CAP_SYS_RESOURCE) ||
+ (!gid_eq(sbi->s_resgid, make_kgid(sbi->s_sb->s_user_ns, 0)) && in_group_p(sbi->s_resgid)) ||
+ ns_capable(sbi->s_sb->s_user_ns, CAP_SYS_RESOURCE) ||
(flags & EXT4_MB_USE_ROOT_BLOCKS)) {
if (free_clusters >= (nclusters + dirty_clusters +
if (!dir || !dir->i_nlink)
return ERR_PTR(-EPERM);
+ /* Supplied owner must be valid */
+ if (owner && (owner[0] == (uid_t)-1 || owner[1] == (uid_t)-1))
+ return ERR_PTR(-EOVERFLOW);
+
if ((ext4_encrypted_inode(dir) ||
DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT))
ei->i_projid = EXT4_I(dir)->i_projid;
else
- ei->i_projid = make_kprojid(&init_user_ns, EXT4_DEF_PROJID);
+ ei->i_projid = make_kprojid(sb->s_user_ns, EXT4_DEF_PROJID);
err = dquot_initialize(inode);
if (err)
}
i_uid_write(inode, i_uid);
i_gid_write(inode, i_gid);
- ei->i_projid = make_kprojid(&init_user_ns, i_projid);
+ ei->i_projid = make_kprojid(sb->s_user_ns, i_projid);
set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
i_uid = i_uid_read(inode);
i_gid = i_gid_read(inode);
- i_projid = from_kprojid(&init_user_ns, ei->i_projid);
+ i_projid = from_kprojid(sb->s_user_ns, ei->i_projid);
if (!(test_opt(inode->i_sb, NO_UID32))) {
raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
}
}
- BUG_ON(!ext4_has_feature_project(inode->i_sb) &&
- i_projid != EXT4_DEF_PROJID);
+ if (i_projid != (projid_t)-1) {
+ BUG_ON(!ext4_has_feature_project(inode->i_sb) &&
+ i_projid != EXT4_DEF_PROJID);
- if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
- EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))
- raw_inode->i_projid = cpu_to_le32(i_projid);
+ if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
+ EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))
+ raw_inode->i_projid = cpu_to_le32(i_projid);
+ }
ext4_inode_csum_set(inode, raw_inode, ei);
spin_unlock(&ei->i_raw_lock);
* the relevant capability.
*/
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
- if (!capable(CAP_SYS_RESOURCE))
+ if (!ns_capable(inode->i_sb->s_user_ns, CAP_SYS_RESOURCE))
goto flags_out;
}
if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
return -EOPNOTSUPP;
- kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
+ kprojid = make_kprojid(sb->s_user_ns, (projid_t)projid);
+ if (!projid_valid(kprojid))
+ return -EOVERFLOW;
if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
return 0;
struct fstrim_range range;
int ret = 0;
- if (!capable(CAP_SYS_ADMIN))
+ if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
return -EPERM;
if (!blk_queue_discard(q))
fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
if (ext4_has_feature_project(inode->i_sb)) {
- fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
+ fa.fsx_projid = (__u32)from_kprojid_munged(sb->s_user_ns,
EXT4_I(inode)->i_projid);
}
return -EPERM;
if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
- (!projid_eq(EXT4_I(dir)->i_projid,
- EXT4_I(old_dentry->d_inode)->i_projid)))
+ (!projid_valid_eq(EXT4_I(dir)->i_projid,
+ EXT4_I(old_dentry->d_inode)->i_projid)))
return -EXDEV;
err = dquot_initialize(dir);
u8 old_file_type;
if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) &&
- (!projid_eq(EXT4_I(new_dir)->i_projid,
- EXT4_I(old_dentry->d_inode)->i_projid)))
+ (!projid_valid_eq(EXT4_I(new_dir)->i_projid,
+ EXT4_I(old_dentry->d_inode)->i_projid)))
return -EXDEV;
retval = dquot_initialize(old.dir);
return -EPERM;
if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT) &&
- !projid_eq(EXT4_I(new_dir)->i_projid,
- EXT4_I(old_dentry->d_inode)->i_projid)) ||
+ !projid_valid_eq(EXT4_I(new_dir)->i_projid,
+ EXT4_I(old_dentry->d_inode)->i_projid)) ||
(ext4_test_inode_flag(old_dir, EXT4_INODE_PROJINHERIT) &&
- !projid_eq(EXT4_I(old_dir)->i_projid,
- EXT4_I(new_dentry->d_inode)->i_projid)))
+ !projid_valid_eq(EXT4_I(old_dir)->i_projid,
+ EXT4_I(new_dentry->d_inode)->i_projid)))
return -EXDEV;
retval = dquot_initialize(old.dir);
{
int ret = 0;
- if (!capable(CAP_SYS_RESOURCE))
+ if (!ns_capable(sb->s_user_ns, CAP_SYS_RESOURCE))
return -EPERM;
/*
#include <linux/crc16.h>
#include <linux/cleancache.h>
#include <linux/uaccess.h>
+#include <linux/user_namespace.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
.name = "ext2",
.mount = ext4_mount,
.kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .fs_flags = FS_REQUIRES_DEV | FS_USERNS_MOUNT,
};
MODULE_ALIAS_FS("ext2");
MODULE_ALIAS("ext2");
.name = "ext3",
.mount = ext4_mount,
.kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .fs_flags = FS_REQUIRES_DEV | FS_USERNS_MOUNT,
};
MODULE_ALIAS_FS("ext3");
MODULE_ALIAS("ext3");
return -1;
}
+ if (token == Opt_err_panic && !capable(CAP_SYS_ADMIN)) {
+ ext4_msg(sb, KERN_ERR,
+ "Mount option \"%s\" not allowed for unprivileged mounts",
+ opt);
+ return -1;
+ }
+
if (args->from && !(m->flags & MOPT_STRING) && match_int(args, &arg))
return -1;
if (args->from && (m->flags & MOPT_GTE0) && (arg < 0))
} else if (token == Opt_stripe) {
sbi->s_stripe = arg;
} else if (token == Opt_resuid) {
- uid = make_kuid(current_user_ns(), arg);
+ uid = make_kuid(sb->s_user_ns, arg);
if (!uid_valid(uid)) {
ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
return -1;
}
sbi->s_resuid = uid;
} else if (token == Opt_resgid) {
- gid = make_kgid(current_user_ns(), arg);
+ gid = make_kgid(sb->s_user_ns, arg);
if (!gid_valid(gid)) {
ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
return -1;
return -1;
}
+ /*
+ * Refuse access for unprivileged mounts if the user does
+ * not have rw access to the journal device via the supplied
+ * path.
+ */
+ if (!capable(CAP_SYS_ADMIN) &&
+ inode_permission(d_inode(path.dentry), MAY_READ|MAY_WRITE)) {
+ ext4_msg(sb, KERN_ERR,
+ "error: Insufficient access to journal path %s",
+ journal_path);
+ return -1;
+ }
+
journal_inode = d_inode(path.dentry);
if (!S_ISBLK(journal_inode->i_mode)) {
ext4_msg(sb, KERN_ERR, "error: journal path %s "
SEQ_OPTS_PRINT("%s", token2str(m->token));
}
- if (nodefs || !uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT4_DEF_RESUID)) ||
+ if (nodefs || !uid_eq(sbi->s_resuid, make_kuid(sb->s_user_ns, EXT4_DEF_RESUID)) ||
le16_to_cpu(es->s_def_resuid) != EXT4_DEF_RESUID)
SEQ_OPTS_PRINT("resuid=%u",
- from_kuid_munged(&init_user_ns, sbi->s_resuid));
- if (nodefs || !gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT4_DEF_RESGID)) ||
+ from_kuid_munged(sb->s_user_ns, sbi->s_resuid));
+ if (nodefs || !gid_eq(sbi->s_resgid, make_kgid(sb->s_user_ns, EXT4_DEF_RESGID)) ||
le16_to_cpu(es->s_def_resgid) != EXT4_DEF_RESGID)
SEQ_OPTS_PRINT("resgid=%u",
- from_kgid_munged(&init_user_ns, sbi->s_resgid));
+ from_kgid_munged(sb->s_user_ns, sbi->s_resgid));
def_errors = nodefs ? -1 : le16_to_cpu(es->s_errors);
if (test_opt(sb, ERRORS_RO) && def_errors != EXT4_ERRORS_RO)
SEQ_OPTS_PUTS("errors=remount-ro");
else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_WBACK)
set_opt(sb, WRITEBACK_DATA);
- if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_PANIC)
+ if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_PANIC) {
+ if (!capable(CAP_SYS_ADMIN))
+ goto failed_mount;
set_opt(sb, ERRORS_PANIC);
- else if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_CONTINUE)
+ } else if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_CONTINUE) {
set_opt(sb, ERRORS_CONT);
- else
+ } else {
set_opt(sb, ERRORS_RO);
+ }
/* block_validity enabled by default; disable with noblock_validity */
set_opt(sb, BLOCK_VALIDITY);
if (def_mount_opts & EXT4_DEFM_DISCARD)
set_opt(sb, DISCARD);
- sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));
- sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));
+ sbi->s_resuid = make_kuid(sb->s_user_ns, le16_to_cpu(es->s_def_resuid));
+ if (!uid_valid(sbi->s_resuid))
+ sbi->s_resuid = make_kuid(sb->s_user_ns, EXT4_DEF_RESUID);
+ sbi->s_resgid = make_kgid(sb->s_user_ns, le16_to_cpu(es->s_def_resgid));
+ if (!gid_valid(sbi->s_resgid))
+ sbi->s_resgid = make_kgid(sb->s_user_ns, EXT4_DEF_RESGID);
sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ;
sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME;
sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME;
ext4_blkdev_remove(sbi);
brelse(bh);
out_fail:
+ /* sb->s_user_ns will be put when sb is destroyed */
sb->s_fs_info = NULL;
kfree(sbi->s_blockgroup_lock);
out_free_base:
.name = "ext4",
.mount = ext4_mount,
.kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .fs_flags = FS_REQUIRES_DEV | FS_USERNS_MOUNT,
};
MODULE_ALIAS_FS("ext4");
return !projid_eq(projid, INVALID_PROJID);
}
+static inline bool projid_valid_eq(kprojid_t left, kprojid_t right)
+{
+ return projid_eq(left, right) && projid_valid(left);
+}
+
#ifdef CONFIG_USER_NS
extern kprojid_t make_kprojid(struct user_namespace *from, projid_t projid);