Merge branch 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs mount API prep from Al Viro: "Mount API prereqs. Mostly that's LSM mount options cleanups. There are several minor fixes in there, but nothing earth-shattering (leaks on failure exits, mostly)" * 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (27 commits) mount_fs: suppress MAC on MS_SUBMOUNT as well as MS_KERNMOUNT smack: rewrite smack_sb_eat_lsm_opts() smack: get rid of match_token() smack: take the guts of smack_parse_opts_str() into a new helper LSM: new method: ->sb_add_mnt_opt() selinux: rewrite selinux_sb_eat_lsm_opts() selinux: regularize Opt_... names a bit selinux: switch away from match_token() selinux: new helper - selinux_add_opt() LSM: bury struct security_mnt_opts smack: switch to private smack_mnt_opts selinux: switch to private struct selinux_mnt_opts LSM: hide struct security_mnt_opts from any generic code selinux: kill selinux_sb_get_mnt_opts() LSM: turn sb_eat_lsm_opts() into a method nfs_remount(): don't leak, don't ignore LSM options quietly btrfs: sanitize security_mnt_opts use selinux; don't open-code a loop in sb_finish_set_opts() LSM: split ->sb_set_mnt_opts() out of ->sb_kern_mount() new helper: security_sb_eat_lsm_opts() ...
This commit is contained in:
@@ -59,14 +59,31 @@ static LIST_HEAD(smk_ipv6_port_list);
|
||||
static struct kmem_cache *smack_inode_cache;
|
||||
int smack_enabled;
|
||||
|
||||
static const match_table_t smk_mount_tokens = {
|
||||
{Opt_fsdefault, SMK_FSDEFAULT "%s"},
|
||||
{Opt_fsfloor, SMK_FSFLOOR "%s"},
|
||||
{Opt_fshat, SMK_FSHAT "%s"},
|
||||
{Opt_fsroot, SMK_FSROOT "%s"},
|
||||
{Opt_fstransmute, SMK_FSTRANS "%s"},
|
||||
{Opt_error, NULL},
|
||||
#define A(s) {"smack"#s, sizeof("smack"#s) - 1, Opt_##s}
|
||||
static struct {
|
||||
const char *name;
|
||||
int len;
|
||||
int opt;
|
||||
} smk_mount_opts[] = {
|
||||
A(fsdefault), A(fsfloor), A(fshat), A(fsroot), A(fstransmute)
|
||||
};
|
||||
#undef A
|
||||
|
||||
static int match_opt_prefix(char *s, int l, char **arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(smk_mount_opts); i++) {
|
||||
size_t len = smk_mount_opts[i].len;
|
||||
if (len > l || memcmp(s, smk_mount_opts[i].name, len))
|
||||
continue;
|
||||
if (len == l || s[len] != '=')
|
||||
continue;
|
||||
*arg = s + len + 1;
|
||||
return smk_mount_opts[i].opt;
|
||||
}
|
||||
return Opt_error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static char *smk_bu_mess[] = {
|
||||
@@ -567,175 +584,110 @@ static void smack_sb_free_security(struct super_block *sb)
|
||||
sb->s_security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_copy_data - copy mount options data for processing
|
||||
* @orig: where to start
|
||||
* @smackopts: mount options string
|
||||
*
|
||||
* Returns 0 on success or -ENOMEM on error.
|
||||
*
|
||||
* Copy the Smack specific mount options out of the mount
|
||||
* options list.
|
||||
*/
|
||||
static int smack_sb_copy_data(char *orig, char *smackopts)
|
||||
struct smack_mnt_opts {
|
||||
const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;
|
||||
};
|
||||
|
||||
static void smack_free_mnt_opts(void *mnt_opts)
|
||||
{
|
||||
char *cp, *commap, *otheropts, *dp;
|
||||
|
||||
otheropts = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (otheropts == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) {
|
||||
if (strstr(cp, SMK_FSDEFAULT) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSFLOOR) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSHAT) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSROOT) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSTRANS) == cp)
|
||||
dp = smackopts;
|
||||
else
|
||||
dp = otheropts;
|
||||
|
||||
commap = strchr(cp, ',');
|
||||
if (commap != NULL)
|
||||
*commap = '\0';
|
||||
|
||||
if (*dp != '\0')
|
||||
strcat(dp, ",");
|
||||
strcat(dp, cp);
|
||||
}
|
||||
|
||||
strcpy(orig, otheropts);
|
||||
free_page((unsigned long)otheropts);
|
||||
|
||||
return 0;
|
||||
struct smack_mnt_opts *opts = mnt_opts;
|
||||
kfree(opts->fsdefault);
|
||||
kfree(opts->fsfloor);
|
||||
kfree(opts->fshat);
|
||||
kfree(opts->fsroot);
|
||||
kfree(opts->fstransmute);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_parse_opts_str - parse Smack specific mount options
|
||||
* @options: mount options string
|
||||
* @opts: where to store converted mount opts
|
||||
*
|
||||
* Returns 0 on success or -ENOMEM on error.
|
||||
*
|
||||
* converts Smack specific mount options to generic security option format
|
||||
*/
|
||||
static int smack_parse_opts_str(char *options,
|
||||
struct security_mnt_opts *opts)
|
||||
static int smack_add_opt(int token, const char *s, void **mnt_opts)
|
||||
{
|
||||
char *p;
|
||||
char *fsdefault = NULL;
|
||||
char *fsfloor = NULL;
|
||||
char *fshat = NULL;
|
||||
char *fsroot = NULL;
|
||||
char *fstransmute = NULL;
|
||||
int rc = -ENOMEM;
|
||||
int num_mnt_opts = 0;
|
||||
int token;
|
||||
struct smack_mnt_opts *opts = *mnt_opts;
|
||||
|
||||
opts->num_mnt_opts = 0;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, smk_mount_tokens, args);
|
||||
|
||||
switch (token) {
|
||||
case Opt_fsdefault:
|
||||
if (fsdefault)
|
||||
goto out_opt_err;
|
||||
fsdefault = match_strdup(&args[0]);
|
||||
if (!fsdefault)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fsfloor:
|
||||
if (fsfloor)
|
||||
goto out_opt_err;
|
||||
fsfloor = match_strdup(&args[0]);
|
||||
if (!fsfloor)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fshat:
|
||||
if (fshat)
|
||||
goto out_opt_err;
|
||||
fshat = match_strdup(&args[0]);
|
||||
if (!fshat)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fsroot:
|
||||
if (fsroot)
|
||||
goto out_opt_err;
|
||||
fsroot = match_strdup(&args[0]);
|
||||
if (!fsroot)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fstransmute:
|
||||
if (fstransmute)
|
||||
goto out_opt_err;
|
||||
fstransmute = match_strdup(&args[0]);
|
||||
if (!fstransmute)
|
||||
goto out_err;
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
pr_warn("Smack: unknown mount option\n");
|
||||
goto out_err;
|
||||
}
|
||||
if (!opts) {
|
||||
opts = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return -ENOMEM;
|
||||
*mnt_opts = opts;
|
||||
}
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
|
||||
if (!opts->mnt_opts)
|
||||
goto out_err;
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!opts->mnt_opts_flags)
|
||||
goto out_err;
|
||||
|
||||
if (fsdefault) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsdefault;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
|
||||
switch (token) {
|
||||
case Opt_fsdefault:
|
||||
if (opts->fsdefault)
|
||||
goto out_opt_err;
|
||||
opts->fsdefault = s;
|
||||
break;
|
||||
case Opt_fsfloor:
|
||||
if (opts->fsfloor)
|
||||
goto out_opt_err;
|
||||
opts->fsfloor = s;
|
||||
break;
|
||||
case Opt_fshat:
|
||||
if (opts->fshat)
|
||||
goto out_opt_err;
|
||||
opts->fshat = s;
|
||||
break;
|
||||
case Opt_fsroot:
|
||||
if (opts->fsroot)
|
||||
goto out_opt_err;
|
||||
opts->fsroot = s;
|
||||
break;
|
||||
case Opt_fstransmute:
|
||||
if (opts->fstransmute)
|
||||
goto out_opt_err;
|
||||
opts->fstransmute = s;
|
||||
break;
|
||||
}
|
||||
if (fsfloor) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsfloor;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
|
||||
}
|
||||
if (fshat) {
|
||||
opts->mnt_opts[num_mnt_opts] = fshat;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
|
||||
}
|
||||
if (fsroot) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsroot;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
|
||||
}
|
||||
if (fstransmute) {
|
||||
opts->mnt_opts[num_mnt_opts] = fstransmute;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
|
||||
}
|
||||
|
||||
opts->num_mnt_opts = num_mnt_opts;
|
||||
return 0;
|
||||
|
||||
out_opt_err:
|
||||
rc = -EINVAL;
|
||||
pr_warn("Smack: duplicate mount options\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out_err:
|
||||
kfree(fsdefault);
|
||||
kfree(fsfloor);
|
||||
kfree(fshat);
|
||||
kfree(fsroot);
|
||||
kfree(fstransmute);
|
||||
return rc;
|
||||
static int smack_sb_eat_lsm_opts(char *options, void **mnt_opts)
|
||||
{
|
||||
char *from = options, *to = options;
|
||||
bool first = true;
|
||||
|
||||
while (1) {
|
||||
char *next = strchr(from, ',');
|
||||
int token, len, rc;
|
||||
char *arg = NULL;
|
||||
|
||||
if (next)
|
||||
len = next - from;
|
||||
else
|
||||
len = strlen(from);
|
||||
|
||||
token = match_opt_prefix(from, len, &arg);
|
||||
if (token != Opt_error) {
|
||||
arg = kmemdup_nul(arg, from + len - arg, GFP_KERNEL);
|
||||
rc = smack_add_opt(token, arg, mnt_opts);
|
||||
if (unlikely(rc)) {
|
||||
kfree(arg);
|
||||
if (*mnt_opts)
|
||||
smack_free_mnt_opts(*mnt_opts);
|
||||
*mnt_opts = NULL;
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
if (!first) { // copy with preceding comma
|
||||
from--;
|
||||
len++;
|
||||
}
|
||||
if (to != from)
|
||||
memmove(to, from, len);
|
||||
to += len;
|
||||
first = false;
|
||||
}
|
||||
if (!from[len])
|
||||
break;
|
||||
from += len + 1;
|
||||
}
|
||||
*to = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -751,7 +703,7 @@ out_err:
|
||||
* labels.
|
||||
*/
|
||||
static int smack_set_mnt_opts(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
void *mnt_opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
@@ -760,9 +712,8 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
struct superblock_smack *sp = sb->s_security;
|
||||
struct inode_smack *isp;
|
||||
struct smack_known *skp;
|
||||
int i;
|
||||
int num_opts = opts->num_mnt_opts;
|
||||
int transmute = 0;
|
||||
struct smack_mnt_opts *opts = mnt_opts;
|
||||
bool transmute = false;
|
||||
|
||||
if (sp->smk_flags & SMK_SB_INITIALIZED)
|
||||
return 0;
|
||||
@@ -771,7 +722,7 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
/*
|
||||
* Unprivileged mounts don't get to specify Smack values.
|
||||
*/
|
||||
if (num_opts)
|
||||
if (opts)
|
||||
return -EPERM;
|
||||
/*
|
||||
* Unprivileged mounts get root and default from the caller.
|
||||
@@ -787,48 +738,44 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
if (sb->s_user_ns != &init_user_ns &&
|
||||
sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
|
||||
sb->s_magic != RAMFS_MAGIC) {
|
||||
transmute = 1;
|
||||
transmute = true;
|
||||
sp->smk_flags |= SMK_SB_UNTRUSTED;
|
||||
}
|
||||
}
|
||||
|
||||
sp->smk_flags |= SMK_SB_INITIALIZED;
|
||||
|
||||
for (i = 0; i < num_opts; i++) {
|
||||
switch (opts->mnt_opts_flags[i]) {
|
||||
case FSDEFAULT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (opts) {
|
||||
if (opts->fsdefault) {
|
||||
skp = smk_import_entry(opts->fsdefault, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_default = skp;
|
||||
break;
|
||||
case FSFLOOR_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fsfloor) {
|
||||
skp = smk_import_entry(opts->fsfloor, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_floor = skp;
|
||||
break;
|
||||
case FSHAT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fshat) {
|
||||
skp = smk_import_entry(opts->fshat, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_hat = skp;
|
||||
break;
|
||||
case FSROOT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fsroot) {
|
||||
skp = smk_import_entry(opts->fsroot, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_root = skp;
|
||||
break;
|
||||
case FSTRANS_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fstransmute) {
|
||||
skp = smk_import_entry(opts->fstransmute, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_root = skp;
|
||||
transmute = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
transmute = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,37 +797,6 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_kern_mount - Smack specific mount processing
|
||||
* @sb: the file system superblock
|
||||
* @flags: the mount flags
|
||||
* @data: the smack mount options
|
||||
*
|
||||
* Returns 0 on success, an error code on failure
|
||||
*/
|
||||
static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
char *options = data;
|
||||
struct security_mnt_opts opts;
|
||||
|
||||
security_init_mnt_opts(&opts);
|
||||
|
||||
if (!options)
|
||||
goto out;
|
||||
|
||||
rc = smack_parse_opts_str(options, &opts);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
|
||||
|
||||
out_err:
|
||||
security_free_mnt_opts(&opts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_statfs - Smack check on statfs
|
||||
* @dentry: identifies the file system in question
|
||||
@@ -4673,11 +4589,10 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||
|
||||
LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
|
||||
LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
|
||||
LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
|
||||
LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
|
||||
LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
|
||||
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
|
||||
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
|
||||
|
||||
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
|
||||
|
||||
|
Referens i nytt ärende
Block a user