summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--security/selinux/hooks.c262
1 files changed, 101 insertions, 161 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index caf7ca7abfc1..238907d69e8b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -433,16 +433,17 @@ static void superblock_free_security(struct super_block *sb)
kfree(sbsec);
}
+struct selinux_mnt_opts {
+ const char *fscontext, *context, *rootcontext, *defcontext;
+};
+
static void selinux_free_mnt_opts(void *mnt_opts)
{
- struct security_mnt_opts *opts = mnt_opts;
- int i;
-
- if (opts->mnt_opts)
- for (i = 0; i < opts->num_mnt_opts; i++)
- kfree(opts->mnt_opts[i]);
- kfree(opts->mnt_opts);
- kfree(opts->mnt_opts_flags);
+ struct selinux_mnt_opts *opts = mnt_opts;
+ kfree(opts->fscontext);
+ kfree(opts->context);
+ kfree(opts->rootcontext);
+ kfree(opts->defcontext);
kfree(opts);
}
@@ -624,6 +625,17 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
return 0;
}
+static int parse_sid(struct super_block *sb, const char *s, u32 *sid)
+{
+ int rc = security_context_str_to_sid(&selinux_state, s,
+ sid, GFP_KERNEL);
+ if (rc)
+ pr_warn("SELinux: security_context_str_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ s, sb->s_id, sb->s_type->name, rc);
+ return rc;
+}
+
/*
* Allow filesystems with binary mount data to explicitly set mount point
* labeling information.
@@ -634,22 +646,18 @@ static int selinux_set_mnt_opts(struct super_block *sb,
unsigned long *set_kern_flags)
{
const struct cred *cred = current_cred();
- int rc = 0, i;
struct superblock_security_struct *sbsec = sb->s_security;
- const char *name = sb->s_type->name;
struct dentry *root = sbsec->sb->s_root;
+ struct selinux_mnt_opts *opts = mnt_opts;
struct inode_security_struct *root_isec;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
u32 defcontext_sid = 0;
- struct security_mnt_opts *opts = mnt_opts;
- char **mount_options = opts ? opts->mnt_opts : NULL;
- int *flags = opts ? opts->mnt_opts_flags : NULL;
- int num_opts = opts ? opts->num_mnt_opts : 0;
+ int rc = 0;
mutex_lock(&sbsec->lock);
if (!selinux_state.initialized) {
- if (!num_opts) {
+ if (!opts) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
@@ -679,7 +687,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
* will be used for both mounts)
*/
if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
- && (num_opts == 0))
+ && !opts)
goto out;
root_isec = backing_inode_security_novalidate(root);
@@ -689,68 +697,48 @@ static int selinux_set_mnt_opts(struct super_block *sb,
* also check if someone is trying to mount the same sb more
* than once with different security options.
*/
- for (i = 0; i < num_opts; i++) {
- u32 sid;
-
- if (flags[i] == SBLABEL_MNT)
- continue;
- rc = security_context_str_to_sid(&selinux_state,
- mount_options[i], &sid,
- GFP_KERNEL);
- if (rc) {
- pr_warn("SELinux: security_context_str_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- mount_options[i], sb->s_id, name, rc);
- goto out;
- }
- switch (flags[i]) {
- case FSCONTEXT_MNT:
- fscontext_sid = sid;
-
+ if (opts) {
+ if (opts->fscontext) {
+ rc = parse_sid(sb, opts->fscontext, &fscontext_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
fscontext_sid))
goto out_double_mount;
-
sbsec->flags |= FSCONTEXT_MNT;
- break;
- case CONTEXT_MNT:
- context_sid = sid;
-
+ }
+ if (opts->context) {
+ rc = parse_sid(sb, opts->context, &context_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
context_sid))
goto out_double_mount;
-
sbsec->flags |= CONTEXT_MNT;
- break;
- case ROOTCONTEXT_MNT:
- rootcontext_sid = sid;
-
+ }
+ if (opts->rootcontext) {
+ rc = parse_sid(sb, opts->rootcontext, &rootcontext_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
rootcontext_sid))
goto out_double_mount;
-
sbsec->flags |= ROOTCONTEXT_MNT;
-
- break;
- case DEFCONTEXT_MNT:
- defcontext_sid = sid;
-
+ }
+ if (opts->defcontext) {
+ rc = parse_sid(sb, opts->defcontext, &defcontext_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
defcontext_sid))
goto out_double_mount;
-
sbsec->flags |= DEFCONTEXT_MNT;
-
- break;
- default:
- rc = -EINVAL;
- goto out;
}
}
if (sbsec->flags & SE_SBINITIALIZED) {
/* previously mounted with options, but not on this attempt? */
- if ((sbsec->flags & SE_MNTMASK) && !num_opts)
+ if ((sbsec->flags & SE_MNTMASK) && !opts)
goto out_double_mount;
rc = 0;
goto out;
@@ -883,7 +871,8 @@ out:
out_double_mount:
rc = -EINVAL;
pr_warn("SELinux: mount invalid. Same superblock, different "
- "security settings for (dev %s, type %s)\n", sb->s_id, name);
+ "security settings for (dev %s, type %s)\n", sb->s_id,
+ sb->s_type->name);
goto out;
}
@@ -998,20 +987,9 @@ out:
static int selinux_parse_opts_str(char *options,
void **mnt_opts)
{
+ struct selinux_mnt_opts *opts = *mnt_opts;
char *p;
- char *context = NULL, *defcontext = NULL;
- char *fscontext = NULL, *rootcontext = NULL;
- int rc, num_mnt_opts = 0;
- struct security_mnt_opts *opts = *mnt_opts;
-
- if (!opts) {
- opts = kzalloc(sizeof(struct security_mnt_opts), GFP_KERNEL);
- *mnt_opts = opts;
- if (!opts)
- return -ENOMEM;
- }
-
- opts->num_mnt_opts = 0;
+ int rc;
/* Standard string-based options. */
while ((p = strsep(&options, "|")) != NULL) {
@@ -1023,54 +1001,60 @@ static int selinux_parse_opts_str(char *options,
token = match_token(p, tokens, args);
+ if (!opts) {
+ opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
+ if (!opts)
+ return -ENOMEM;
+ }
+
switch (token) {
case Opt_context:
- if (context || defcontext) {
+ if (opts->context || opts->defcontext) {
rc = -EINVAL;
pr_warn(SEL_MOUNT_FAIL_MSG);
goto out_err;
}
- context = match_strdup(&args[0]);
- if (!context) {
+ opts->context = match_strdup(&args[0]);
+ if (!opts->context) {
rc = -ENOMEM;
goto out_err;
}
break;
case Opt_fscontext:
- if (fscontext) {
+ if (opts->fscontext) {
rc = -EINVAL;
pr_warn(SEL_MOUNT_FAIL_MSG);
goto out_err;
}
- fscontext = match_strdup(&args[0]);
- if (!fscontext) {
+ opts->fscontext = match_strdup(&args[0]);
+ if (!opts->fscontext) {
rc = -ENOMEM;
goto out_err;
}
break;
case Opt_rootcontext:
- if (rootcontext) {
+ if (opts->rootcontext) {
rc = -EINVAL;
pr_warn(SEL_MOUNT_FAIL_MSG);
goto out_err;
}
- rootcontext = match_strdup(&args[0]);
- if (!rootcontext) {
+ opts->rootcontext = match_strdup(&args[0]);
+ if (!opts->rootcontext) {
rc = -ENOMEM;
goto out_err;
}
break;
case Opt_defcontext:
- if (context || defcontext) {
+ if (opts->context || opts->defcontext) {
rc = -EINVAL;
pr_warn(SEL_MOUNT_FAIL_MSG);
goto out_err;
}
- defcontext = match_strdup(&args[0]);
- if (!defcontext) {
+ opts->defcontext = match_strdup(&args[0]);
+ if (!opts->defcontext) {
rc = -ENOMEM;
goto out_err;
}
@@ -1084,43 +1068,12 @@ static int selinux_parse_opts_str(char *options,
}
}
-
- rc = -ENOMEM;
- opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
- if (!opts->mnt_opts)
- goto out_err;
-
- opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
- GFP_KERNEL);
- if (!opts->mnt_opts_flags)
- goto out_err;
-
- if (fscontext) {
- opts->mnt_opts[num_mnt_opts] = fscontext;
- opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
- }
- if (context) {
- opts->mnt_opts[num_mnt_opts] = context;
- opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
- }
- if (rootcontext) {
- opts->mnt_opts[num_mnt_opts] = rootcontext;
- opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
- }
- if (defcontext) {
- opts->mnt_opts[num_mnt_opts] = defcontext;
- opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
- }
-
- opts->num_mnt_opts = num_mnt_opts;
+ *mnt_opts = opts;
return 0;
out_err:
- security_free_mnt_opts(mnt_opts);
- kfree(context);
- kfree(defcontext);
- kfree(fscontext);
- kfree(rootcontext);
+ if (opts)
+ selinux_free_mnt_opts(opts);
return rc;
}
@@ -2752,10 +2705,10 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
{
- struct security_mnt_opts *opts = mnt_opts;
- int i, *flags;
- char **mount_options;
+ struct selinux_mnt_opts *opts = mnt_opts;
struct superblock_security_struct *sbsec = sb->s_security;
+ u32 sid;
+ int rc;
if (!(sbsec->flags & SE_SBINITIALIZED))
return 0;
@@ -2763,48 +2716,35 @@ static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
if (!opts)
return 0;
- mount_options = opts->mnt_opts;
- flags = opts->mnt_opts_flags;
-
- for (i = 0; i < opts->num_mnt_opts; i++) {
- u32 sid;
- int rc;
-
- if (flags[i] == SBLABEL_MNT)
- continue;
- rc = security_context_str_to_sid(&selinux_state,
- mount_options[i], &sid,
- GFP_KERNEL);
- if (rc) {
- pr_warn("SELinux: security_context_str_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- mount_options[i], sb->s_id, sb->s_type->name, rc);
+ if (opts->fscontext) {
+ rc = parse_sid(sb, opts->fscontext, &sid);
+ if (rc)
return rc;
- }
- switch (flags[i]) {
- case FSCONTEXT_MNT:
- if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
- goto out_bad_option;
- break;
- case CONTEXT_MNT:
- if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
- goto out_bad_option;
- break;
- case ROOTCONTEXT_MNT: {
- struct inode_security_struct *root_isec;
- root_isec = backing_inode_security(sb->s_root);
-
- if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
- goto out_bad_option;
- break;
- }
- case DEFCONTEXT_MNT:
- if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
- goto out_bad_option;
- break;
- default:
- return -EINVAL;
- }
+ if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+ goto out_bad_option;
+ }
+ if (opts->context) {
+ rc = parse_sid(sb, opts->context, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+ goto out_bad_option;
+ }
+ if (opts->rootcontext) {
+ struct inode_security_struct *root_isec;
+ root_isec = backing_inode_security(sb->s_root);
+ rc = parse_sid(sb, opts->rootcontext, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+ goto out_bad_option;
+ }
+ if (opts->defcontext) {
+ rc = parse_sid(sb, opts->defcontext, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+ goto out_bad_option;
}
return 0;