* [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information
@ 2007-11-09 22:22 Eric Paris
2007-11-09 22:46 ` Casey Schaufler
2007-11-28 17:04 ` Eric Paris
0 siblings, 2 replies; 5+ messages in thread
From: Eric Paris @ 2007-11-09 22:22 UTC (permalink / raw)
To: selinux, LSM List, linux-fsdevel; +Cc: sds, jmorris, Casey Schaufler
Adds security_get_sb_mnt_opts, security_set_sb_mnt_opts, and
security_clont_sb_mnt_opts to the LSM and to SELinux. This will allow
filesystems to directly own and control all of their mount options if
they so choose. This interface deals only with option identifiers and
strings so it should generic enough for any LSM which may come in the
future. Filesystems which pass text mount data around in the kernel
(almost all of them) need not currently make use of this interface for
SELinux sake since it will still parse those strings as it always has.
An LSM would need to implement these functions only if they had mount
time options, such as selinux has context= or fscontext=. If the LSM
has no mount time options they could simply not implement and let the
dummy ops take care of things.
A LSM other than SELinux would need to define new option numbers in
security.h (or could reuse if they have the same basic meaning I guess)
and any FS which decides to own there own security options would need to
be patched to use this new interface for every possible LSM. This is
because it was stated to me very clearly that LSM's should not attempt
to understand FS mount data and the burdon to understand security should
be in the FS which owns the options.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
For now the only forseen user of this interface is NFS. NFS uses a
binary blob in kernel for mount data (it uses this blob irrespective of
the binary vs. text mount options it can get from userspace.) NFS must
then set its own mount options explicitly so we need some interface for
it to do so.
include/linux/security.h | 36 ++
security/dummy.c | 26 ++
security/security.c | 20 +
security/selinux/hooks.c | 749 ++++++++++++++++++++++++-------------
security/selinux/include/objsec.h | 1 +
5 files changed, 578 insertions(+), 254 deletions(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index ac05083..dcbb792 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -34,6 +34,12 @@
#include <linux/xfrm.h>
#include <net/flow.h>
+/* only a char in selinux superblock security struct flags */
+#define FSCONTEXT_MNT 0x01
+#define CONTEXT_MNT 0x02
+#define ROOTCONTEXT_MNT 0x04
+#define DEFCONTEXT_MNT 0x08
+
/*
* Bounding set
*/
@@ -261,6 +267,22 @@ struct request_sock;
* Update module state after a successful pivot.
* @old_nd contains the nameidata structure for the old root.
* @new_nd contains the nameidata structure for the new root.
+ * @sb_get_mnt_opts:
+ * Get the security relevant mount options used for a superblock
+ * @sb the superblock to get security mount options from
+ * @mount_options array for pointers to mount options
+ * @mount_flags array of ints specifying what each mount options is
+ * @num_opts number of options in the arrays
+ * @sb_set_mnt_opts:
+ * Set the security relevant mount options used for a superblock
+ * @sb the superblock to set security mount options for
+ * @mount_options array for pointers to mount options
+ * @mount_flags array of ints specifying what each mount options is
+ * @num_opts number of options in the arrays
+ * @sb_clone_mnt_opts:
+ * Copy all security options from a given superblock to another
+ * @oldsb old superblock which contain information to clone
+ * @newsb new superblock which needs filled in
*
* Security hooks for inode operations.
*
@@ -1242,6 +1264,13 @@ struct security_operations {
struct nameidata * new_nd);
void (*sb_post_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
+ int (*sb_get_mnt_opts) (const struct super_block *sb,
+ char ***mount_options, int **flags,
+ int *num_opts);
+ int (*sb_set_mnt_opts) (struct super_block *sb, char **mount_options,
+ int *flags, int num_opts);
+ void (*sb_clone_mnt_opts) (const struct super_block *oldsb,
+ struct super_block *newsb);
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
@@ -1499,6 +1528,13 @@ void security_sb_post_mountroot(void);
void security_sb_post_addmount(struct vfsmount *mnt, struct nameidata *mountpoint_nd);
int security_sb_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd);
void security_sb_post_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd);
+int security_sb_get_mnt_opts(const struct super_block *sb, char ***mount_options,
+ int **flags, int *num_opts);
+int security_sb_set_mnt_opts(struct super_block *sb, char **mount_options,
+ int *flags, int num_opts);
+void security_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb);
+
int security_inode_alloc(struct inode *inode);
void security_inode_free(struct inode *inode);
int security_inode_init_security(struct inode *inode, struct inode *dir,
diff --git a/security/dummy.c b/security/dummy.c
index 6d895ad..22d9663 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -245,6 +245,29 @@ static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata
return;
}
+static int dummy_sb_get_mnt_opts(const struct super_block *sb, char ***mount_options,
+ int **flags, int *num_opts)
+{
+ *mount_options = NULL;
+ *flags = NULL;
+ *num_opts = 0;
+ return 0;
+}
+
+static int dummy_sb_set_mnt_opts(struct super_block *sb, char **mount_options,
+ int *flags, int num_opts)
+{
+ if (unlikely(num_opts))
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static void dummy_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
+{
+ return;
+}
+
static int dummy_inode_alloc_security (struct inode *inode)
{
return 0;
@@ -998,6 +1021,9 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, sb_post_addmount);
set_to_dummy_if_null(ops, sb_pivotroot);
set_to_dummy_if_null(ops, sb_post_pivotroot);
+ set_to_dummy_if_null(ops, sb_get_mnt_opts);
+ set_to_dummy_if_null(ops, sb_set_mnt_opts);
+ set_to_dummy_if_null(ops, sb_clone_mnt_opts);
set_to_dummy_if_null(ops, inode_alloc_security);
set_to_dummy_if_null(ops, inode_free_security);
set_to_dummy_if_null(ops, inode_init_security);
diff --git a/security/security.c b/security/security.c
index 0e1f1f1..f48fb30 100644
--- a/security/security.c
+++ b/security/security.c
@@ -308,6 +308,26 @@ void security_sb_post_pivotroot(struct nameidata *old_nd, struct nameidata *new_
security_ops->sb_post_pivotroot(old_nd, new_nd);
}
+int security_sb_get_mnt_opts (const struct super_block *sb,
+ char ***mount_options,
+ int **flags, int *num_opts)
+{
+ return security_ops->sb_get_mnt_opts(sb, mount_options, flags, num_opts);
+}
+
+int security_sb_set_mnt_opts (struct super_block *sb,
+ char **mount_options,
+ int *flags, int num_opts)
+{
+ return security_ops->sb_set_mnt_opts(sb, mount_options, flags, num_opts);
+}
+
+void security_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
+{
+ security_ops->sb_clone_mnt_opts(oldsb, newsb);
+}
+
int security_inode_alloc(struct inode *inode)
{
inode->i_security = NULL;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9f3124b..e710764 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -82,6 +82,8 @@
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+#define NUM_SEL_MNT_OPTS 4
+
extern unsigned int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
extern int selinux_compat_net;
@@ -321,8 +323,8 @@ enum {
Opt_error = -1,
Opt_context = 1,
Opt_fscontext = 2,
- Opt_defcontext = 4,
- Opt_rootcontext = 8,
+ Opt_defcontext = 3,
+ Opt_rootcontext = 4,
};
static match_table_t tokens = {
@@ -366,150 +368,318 @@ static int may_context_mount_inode_relabel(u32 sid,
return rc;
}
-static int try_context_mount(struct super_block *sb, void *data)
+static int sb_finish_set_opts(struct super_block *sb)
{
- char *context = NULL, *defcontext = NULL;
- char *fscontext = NULL, *rootcontext = NULL;
- const char *name;
- u32 sid;
- int alloc = 0, rc = 0, seen = 0;
- struct task_security_struct *tsec = current->security;
struct superblock_security_struct *sbsec = sb->s_security;
+ struct dentry *root = sb->s_root;
+ struct inode *root_inode = root->d_inode;
+ int rc = 0;
- if (!data)
- goto out;
+ if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+ /* Make sure that the xattr handler exists and that no
+ error other than -ENODATA is returned by getxattr on
+ the root directory. -ENODATA is ok, as this may be
+ the first boot of the SELinux kernel before we have
+ assigned xattr values to the filesystem. */
+ if (!root_inode->i_op->getxattr) {
+ printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
+ "xattr support\n", sb->s_id, sb->s_type->name);
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc < 0 && rc != -ENODATA) {
+ if (rc == -EOPNOTSUPP)
+ printk(KERN_WARNING "SELinux: (dev %s, type "
+ "%s) has no security xattr handler\n",
+ sb->s_id, sb->s_type->name);
+ else
+ printk(KERN_WARNING "SELinux: (dev %s, type "
+ "%s) getxattr errno %d\n", sb->s_id,
+ sb->s_type->name, -rc);
+ goto out;
+ }
+ }
- name = sb->s_type->name;
+ sbsec->initialized = 1;
- if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
+ if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
+ printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
+ sb->s_id, sb->s_type->name);
+ }
+ else {
+ printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
+ sb->s_id, sb->s_type->name,
+ labeling_behaviors[sbsec->behavior-1]);
+ }
- /* NFS we understand. */
- if (!strcmp(name, "nfs")) {
- struct nfs_mount_data *d = data;
+ /* Initialize the root inode. */
+ rc = inode_doinit_with_dentry(root_inode, root);
- if (d->version < NFS_MOUNT_VERSION)
- goto out;
+ /* Initialize any other inodes associated with the superblock, e.g.
+ inodes created prior to initial policy load or inodes created
+ during get_sb by a pseudo filesystem that directly
+ populates itself. */
+ spin_lock(&sbsec->isec_lock);
+next_inode:
+ if (!list_empty(&sbsec->isec_head)) {
+ struct inode_security_struct *isec =
+ list_entry(sbsec->isec_head.next,
+ struct inode_security_struct, list);
+ struct inode *inode = isec->inode;
+ spin_unlock(&sbsec->isec_lock);
+ inode = igrab(inode);
+ if (inode) {
+ if (!IS_PRIVATE (inode))
+ inode_doinit(inode);
+ iput(inode);
+ }
+ spin_lock(&sbsec->isec_lock);
+ list_del_init(&isec->list);
+ goto next_inode;
+ }
+ spin_unlock(&sbsec->isec_lock);
+out:
+ return rc;
+}
- if (d->context[0]) {
- context = d->context;
- seen |= Opt_context;
- }
- } else
- goto out;
+/*
+ * This function should allow an FS to ask what it's mount security
+ * options were so it can use those later for submounts, displaying
+ * mount options, or whatever.
+ */
+static int selinux_get_mnt_opts(const struct super_block *sb,
+ char ***mount_options, int **mnt_opts_flags,
+ int *num_opts)
+{
+ int rc = 0, i;
+ struct superblock_security_struct *sbsec = sb->s_security;
+ char *context = NULL;
+ u32 len;
+ char tmp;
- } else {
- /* Standard string-based options. */
- char *p, *options = data;
+ *num_opts = 0;
+ *mount_options = NULL;
+ *mnt_opts_flags = NULL;
- while ((p = strsep(&options, "|")) != NULL) {
- int token;
- substring_t args[MAX_OPT_ARGS];
+ if (!sbsec->initialized)
+ return -EINVAL;
- if (!*p)
- continue;
+ if (!ss_initialized)
+ return -EINVAL;
- token = match_token(p, tokens, args);
+ /*
+ * if we ever use sbsec flags for anything other than tracking mount
+ * settings this is going to need a mask
+ */
+ tmp = sbsec->flags;
+ /* count the number of mount options for this sb */
+ for(i = 0; i < 8; i++) {
+ if (tmp & 0x01)
+ (*num_opts)++;
+ tmp >>= 1;
+ }
- switch (token) {
- case Opt_context:
- if (seen & (Opt_context|Opt_defcontext)) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- context = match_strdup(&args[0]);
- if (!context) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_context;
- break;
+ *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC);
+ if (!*mount_options) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
- case Opt_fscontext:
- if (seen & Opt_fscontext) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- fscontext = match_strdup(&args[0]);
- if (!fscontext) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_fscontext;
- break;
+ *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC);
+ if (!*mnt_opts_flags) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
- case Opt_rootcontext:
- if (seen & Opt_rootcontext) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- rootcontext = match_strdup(&args[0]);
- if (!rootcontext) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_rootcontext;
- break;
+ i = 0;
+ if (sbsec->flags & FSCONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->sid, &context, &len);
+ if (rc)
+ goto out_free;
+ (*mount_options)[i] = context;
+ (*mnt_opts_flags)[i++] = FSCONTEXT_MNT;
+ }
+ if (sbsec->flags & CONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
+ if (rc)
+ goto out_free;
+ (*mount_options)[i] = context;
+ (*mnt_opts_flags)[i++] = CONTEXT_MNT;
+ }
+ if (sbsec->flags & DEFCONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->def_sid, &context, &len);
+ if (rc)
+ goto out_free;
+ (*mount_options)[i] = context;
+ (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT;
+ }
+ if (sbsec->flags & ROOTCONTEXT_MNT) {
+ struct inode *root = sbsec->sb->s_root->d_inode;
+ struct inode_security_struct *isec = root->i_security;
- case Opt_defcontext:
- if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
- rc = -EINVAL;
- printk(KERN_WARNING "SELinux: "
- "defcontext option is invalid "
- "for this filesystem type\n");
- goto out_free;
- }
- if (seen & (Opt_context|Opt_defcontext)) {
- rc = -EINVAL;
- printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
- goto out_free;
- }
- defcontext = match_strdup(&args[0]);
- if (!defcontext) {
- rc = -ENOMEM;
- goto out_free;
- }
- if (!alloc)
- alloc = 1;
- seen |= Opt_defcontext;
- break;
+ rc = security_sid_to_context(isec->sid, &context, &len);
+ if (rc)
+ goto out_free;
+ (*mount_options)[i] = context;
+ (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT;
+ }
- default:
- rc = -EINVAL;
- printk(KERN_WARNING "SELinux: unknown mount "
- "option\n");
- goto out_free;
+ BUG_ON(i != *num_opts);
- }
- }
- }
+ return 0;
+
+out_free:
+ /* don't leak context string if security_sid_to_context had an error */
+ if(*mount_options && i)
+ for (; i > 0; i--)
+ kfree((*mount_options)[i-1]);
+ kfree(*mount_options);
+ *mount_options = NULL;
+ kfree(*mnt_opts_flags);
+ *mnt_opts_flags = NULL;
+ *num_opts = 0;
+ return rc;
+}
+
+static int bad_option(struct superblock_security_struct *sbsec, char flag,
+ u32 old_sid, u32 new_sid)
+{
+ /* check if the old mount command had the same options */
+ if (sbsec->initialized)
+ if (!(sbsec->flags & flag) ||
+ (old_sid != new_sid))
+ return 1;
+
+ /* check if we were passed the same options twice,
+ * aka someone passed context=a,context=b
+ */
+ if (!sbsec->initialized)
+ if (sbsec->flags & flag)
+ return 1;
+ return 0;
+}
+/*
+ * Allow filesystems with binary mount data to explicitly set mount point labeling.
+ */
+int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
+ int *flags, int num_opts)
+{
+ int rc = 0, i;
+ struct task_security_struct *tsec = current->security;
+ struct superblock_security_struct *sbsec = sb->s_security;
+ const char *name = sb->s_type->name;
+ struct inode *inode = sbsec->sb->s_root->d_inode;
+ struct inode_security_struct *root_isec = inode->i_security;
+ u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
+ u32 defcontext_sid = 0;
+
+ mutex_lock(&sbsec->lock);
- if (!seen)
+ if (!ss_initialized) {
+ if (!num_opts) {
+ /* Defer initialization until selinux_complete_init,
+ after the initial policy is loaded and the security
+ server is ready to handle calls. */
+ spin_lock(&sb_security_lock);
+ if (list_empty(&sbsec->list))
+ list_add(&sbsec->list, &superblock_security_head);
+ spin_unlock(&sb_security_lock);
+ goto out;
+ }
+ rc = -EINVAL;
+ printk(KERN_WARNING "Unable to set superblock options before "
+ "the security server is initialized\n");
goto out;
+ }
- /* sets the context of the superblock for the fs being mounted. */
- if (fscontext) {
- rc = security_context_to_sid(fscontext, strlen(fscontext), &sid);
+ /*
+ * parse the mount options, check if they are valid sids.
+ * 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;
+ rc = security_context_to_sid(mount_options[i],
+ strlen(mount_options[i]), &sid);
if (rc) {
printk(KERN_WARNING "SELinux: security_context_to_sid"
"(%s) failed for (dev %s, type %s) errno=%d\n",
- fscontext, sb->s_id, name, rc);
- goto out_free;
+ mount_options[i], sb->s_id, name, rc);
+ goto out;
}
+ switch(flags[i]) {
+ case FSCONTEXT_MNT:
+ fscontext_sid = sid;
+
+ 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 (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 (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 (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
+ defcontext_sid))
+ goto out_double_mount;
- rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ sbsec->flags |= DEFCONTEXT_MNT;
+
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (sbsec->initialized) {
+ /* previously mounted with options, but not on this attempt? */
+ if (sbsec->flags && !num_opts)
+ goto out_double_mount;
+ rc = 0;
+ goto out;
+ }
+
+ if (strcmp(sb->s_type->name, "proc") == 0)
+ sbsec->proc = 1;
+
+ /* Determine the labeling behavior to use for this filesystem type. */
+ rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ if (rc) {
+ printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
+ __FUNCTION__, sb->s_type->name, rc);
+ goto out;
+ }
+
+ /* sets the context of the superblock for the fs being mounted. */
+ if (fscontext_sid) {
+
+ rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
if (rc)
- goto out_free;
+ goto out;
- sbsec->sid = sid;
+ sbsec->sid = fscontext_sid;
}
/*
@@ -517,182 +687,250 @@ static int try_context_mount(struct super_block *sb, void *data)
* sets the label used on all file below the mountpoint, and will set
* the superblock context if not already set.
*/
- if (context) {
- rc = security_context_to_sid(context, strlen(context), &sid);
- if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- context, sb->s_id, name, rc);
- goto out_free;
- }
-
- if (!fscontext) {
- rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ if (context_sid) {
+ if (!fscontext_sid) {
+ rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
if (rc)
- goto out_free;
- sbsec->sid = sid;
+ goto out;
+ sbsec->sid = context_sid;
} else {
- rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
if (rc)
- goto out_free;
+ goto out;
}
- sbsec->mntpoint_sid = sid;
+ if (!rootcontext_sid)
+ rootcontext_sid = context_sid;
+ sbsec->mntpoint_sid = context_sid;
sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
}
- if (rootcontext) {
- struct inode *inode = sb->s_root->d_inode;
- struct inode_security_struct *isec = inode->i_security;
- rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid);
- if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- rootcontext, sb->s_id, name, rc);
- goto out_free;
- }
-
- rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+ if (rootcontext_sid) {
+ rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
if (rc)
- goto out_free;
+ goto out;
- isec->sid = sid;
- isec->initialized = 1;
+ root_isec->sid = rootcontext_sid;
+ root_isec->initialized = 1;
}
- if (defcontext) {
- rc = security_context_to_sid(defcontext, strlen(defcontext), &sid);
- if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- defcontext, sb->s_id, name, rc);
- goto out_free;
+ if (defcontext_sid) {
+ if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: defcontext option is "
+ "invalid for this filesystem type\n");
+ goto out;
}
- if (sid == sbsec->def_sid)
- goto out_free;
-
- rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
- if (rc)
- goto out_free;
+ if (defcontext_sid != sbsec->def_sid) {
+ rc = may_context_mount_inode_relabel(defcontext_sid,
+ sbsec, tsec);
+ if (rc)
+ goto out;
+ }
- sbsec->def_sid = sid;
+ sbsec->def_sid = defcontext_sid;
}
-out_free:
- if (alloc) {
- kfree(context);
- kfree(defcontext);
- kfree(fscontext);
- kfree(rootcontext);
- }
+ rc = sb_finish_set_opts(sb);
out:
+ mutex_unlock(&sbsec->lock);
return rc;
+out_double_mount:
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different "
+ "security settings for (dev %s, type %s)\n", sb->s_id, name);
+ goto out;
}
-static int superblock_doinit(struct super_block *sb, void *data)
+static void selinux_sb_clone_mnt_opts (const struct super_block *oldsb,
+ struct super_block *newsb)
{
- struct superblock_security_struct *sbsec = sb->s_security;
- struct dentry *root = sb->s_root;
- struct inode *inode = root->d_inode;
- int rc = 0;
+ const struct superblock_security_struct *oldsbsec = oldsb->s_security;
+ struct superblock_security_struct *newsbsec = newsb->s_security;
- mutex_lock(&sbsec->lock);
- if (sbsec->initialized)
- goto out;
+ int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
+ int set_context = (oldsbsec->flags & CONTEXT_MNT);
+ int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT);
- if (!ss_initialized) {
- /* Defer initialization until selinux_complete_init,
- after the initial policy is loaded and the security
- server is ready to handle calls. */
- spin_lock(&sb_security_lock);
- if (list_empty(&sbsec->list))
- list_add(&sbsec->list, &superblock_security_head);
- spin_unlock(&sb_security_lock);
- goto out;
+ /* we can't error, we can't save the info, this shouldn't get called
+ * this early in the boot process. */
+ BUG_ON(!ss_initialized);
+
+ /* this might go away sometime down the line if there is a new user
+ * of clone, but for now, nfs better not get here... */
+ BUG_ON(newsbsec->initialized);
+
+ /* how can we clone if the old one wasn't set up?? */
+ BUG_ON(!oldsbsec->initialized);
+
+ mutex_lock(&newsbsec->lock);
+
+ newsbsec->flags = oldsbsec->flags;
+
+ newsbsec->sid = oldsbsec->sid;
+ newsbsec->def_sid = oldsbsec->def_sid;
+ newsbsec->behavior = oldsbsec->behavior;
+
+ if (set_context) {
+ u32 sid = oldsbsec->mntpoint_sid;
+
+ if (!set_fscontext)
+ newsbsec->sid = sid;
+ if (!set_rootcontext) {
+ struct inode *newinode = newsb->s_root->d_inode;
+ struct inode_security_struct *newisec = newinode->i_security;
+ newisec->sid = sid;
+ }
+ newsbsec->mntpoint_sid = sid;
}
+ if (set_rootcontext) {
+ const struct inode *oldinode = oldsb->s_root->d_inode;
+ const struct inode_security_struct *oldisec = oldinode->i_security;
+ struct inode *newinode = newsb->s_root->d_inode;
+ struct inode_security_struct *newisec = newinode->i_security;
- /* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
- if (rc) {
- printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
- __FUNCTION__, sb->s_type->name, rc);
- goto out;
+ newisec->sid = oldisec->sid;
}
- rc = try_context_mount(sb, data);
- if (rc)
+ sb_finish_set_opts(newsb);
+ mutex_unlock(&newsbsec->lock);
+}
+
+/*
+ * string mount options parsing and call set the sbsec
+ */
+static int superblock_doinit(struct super_block *sb, void *data)
+{
+ char *context = NULL, *defcontext = NULL;
+ char *fscontext = NULL, *rootcontext = NULL;
+ int rc = 0;
+ char *p, *options = data;
+ /* selinux only know about a fixed number of mount options */
+ char *mnt_opts[NUM_SEL_MNT_OPTS];
+ int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0;
+
+ if (!data)
goto out;
- if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
- /* Make sure that the xattr handler exists and that no
- error other than -ENODATA is returned by getxattr on
- the root directory. -ENODATA is ok, as this may be
- the first boot of the SELinux kernel before we have
- assigned xattr values to the filesystem. */
- if (!inode->i_op->getxattr) {
- printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
- "xattr support\n", sb->s_id, sb->s_type->name);
- rc = -EOPNOTSUPP;
- goto out;
- }
- rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
- if (rc < 0 && rc != -ENODATA) {
- if (rc == -EOPNOTSUPP)
- printk(KERN_WARNING "SELinux: (dev %s, type "
- "%s) has no security xattr handler\n",
- sb->s_id, sb->s_type->name);
- else
- printk(KERN_WARNING "SELinux: (dev %s, type "
- "%s) getxattr errno %d\n", sb->s_id,
- sb->s_type->name, -rc);
+ /* with the nfs patch this will become a goto out; */
+ if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
+ const char *name = sb->s_type->name;
+ /* NFS we understand. */
+ if (!strcmp(name, "nfs")) {
+ struct nfs_mount_data *d = data;
+
+ if (d->version != NFS_MOUNT_VERSION)
+ goto out;
+
+ if (d->context[0]) {
+ context = kstrdup(d->context, GFP_KERNEL);
+ if (!context) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
+ goto build_flags;
+ } else
goto out;
- }
}
- if (strcmp(sb->s_type->name, "proc") == 0)
- sbsec->proc = 1;
+ /* Standard string-based options. */
+ while ((p = strsep(&options, "|")) != NULL) {
+ int token;
+ substring_t args[MAX_OPT_ARGS];
- sbsec->initialized = 1;
+ if (!*p)
+ continue;
- if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
- printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
- sb->s_id, sb->s_type->name);
- }
- else {
- printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
- sb->s_id, sb->s_type->name,
- labeling_behaviors[sbsec->behavior-1]);
- }
+ token = match_token(p, tokens, args);
- /* Initialize the root inode. */
- rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
+ switch (token) {
+ case Opt_context:
+ if (context || defcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ context = match_strdup(&args[0]);
+ if (!context) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ case Opt_fscontext:
+ if (fscontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ fscontext = match_strdup(&args[0]);
+ if (!fscontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ case Opt_rootcontext:
+ if (rootcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ rootcontext = match_strdup(&args[0]);
+ if (!rootcontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ case Opt_defcontext:
+ if (context || defcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ defcontext = match_strdup(&args[0]);
+ if (!defcontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: unknown mount option\n");
+ goto out_err;
- /* Initialize any other inodes associated with the superblock, e.g.
- inodes created prior to initial policy load or inodes created
- during get_sb by a pseudo filesystem that directly
- populates itself. */
- spin_lock(&sbsec->isec_lock);
-next_inode:
- if (!list_empty(&sbsec->isec_head)) {
- struct inode_security_struct *isec =
- list_entry(sbsec->isec_head.next,
- struct inode_security_struct, list);
- struct inode *inode = isec->inode;
- spin_unlock(&sbsec->isec_lock);
- inode = igrab(inode);
- if (inode) {
- if (!IS_PRIVATE (inode))
- inode_doinit(inode);
- iput(inode);
}
- spin_lock(&sbsec->isec_lock);
- list_del_init(&isec->list);
- goto next_inode;
}
- spin_unlock(&sbsec->isec_lock);
+
+build_flags:
+ if (fscontext) {
+ mnt_opts[num_mnt_opts] = fscontext;
+ mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
+ }
+ if (context) {
+ mnt_opts[num_mnt_opts] = context;
+ mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
+ }
+ if (rootcontext) {
+ mnt_opts[num_mnt_opts] = rootcontext;
+ mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
+ }
+ if (defcontext) {
+ mnt_opts[num_mnt_opts] = defcontext;
+ mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
+ }
+
out:
- mutex_unlock(&sbsec->lock);
+ rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts);
+out_err:
+ kfree(context);
+ kfree(defcontext);
+ kfree(fscontext);
+ kfree(rootcontext);
return rc;
}
@@ -4800,6 +5038,9 @@ static struct security_operations selinux_ops = {
.sb_statfs = selinux_sb_statfs,
.sb_mount = selinux_mount,
.sb_umount = selinux_umount,
+ .sb_get_mnt_opts = selinux_get_mnt_opts,
+ .sb_set_mnt_opts = selinux_set_mnt_opts,
+ .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts,
.inode_alloc_security = selinux_inode_alloc_security,
.inode_free_security = selinux_inode_free_security,
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 642a9fd..4138a80 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -65,6 +65,7 @@ struct superblock_security_struct {
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
unsigned int behavior; /* labeling behavior */
unsigned char initialized; /* initialization flag */
+ unsigned char flags; /* which mount options were specified */
unsigned char proc; /* proc fs */
struct mutex lock;
struct list_head isec_head;
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information
2007-11-09 22:22 [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information Eric Paris
@ 2007-11-09 22:46 ` Casey Schaufler
2007-11-10 6:41 ` Eric Paris
2007-11-28 17:04 ` Eric Paris
1 sibling, 1 reply; 5+ messages in thread
From: Casey Schaufler @ 2007-11-09 22:46 UTC (permalink / raw)
To: Eric Paris, selinux, LSM List, linux-fsdevel
Cc: sds, jmorris, Casey Schaufler
--- Eric Paris <eparis@redhat.com> wrote:
> Adds security_get_sb_mnt_opts, security_set_sb_mnt_opts, and
> security_clont_sb_mnt_opts to the LSM and to SELinux. This will allow
> filesystems to directly own and control all of their mount options if
> they so choose.
I understand why you would want get_sb_mnt_opts(), but what
is the value for set_sb_mnt_opts() and what is the purpose of
clone_sb_mnt_opts()?
> This interface deals only with option identifiers and
> strings so it should generic enough for any LSM which may come in the
> future. Filesystems which pass text mount data around in the kernel
> (almost all of them) need not currently make use of this interface for
> SELinux sake since it will still parse those strings as it always has.
If SELinux is still dealing with strings on it's own what is
the point of these hooks?
> An LSM would need to implement these functions only if they had mount
> time options, such as selinux has context= or fscontext=. If the LSM
> has no mount time options they could simply not implement and let the
> dummy ops take care of things.
Smack and SELinux currently deal with options in sb_kern_mount(), with
help from sb_copy_data(). Why change the implementation?
> A LSM other than SELinux would need to define new option numbers in
> security.h
I don't think it is a good idea to require that LSM specific
information be stored outside the scope of the LSM.
> (or could reuse if they have the same basic meaning I guess)
> and any FS which decides to own there own security options would need to
> be patched to use this new interface for every possible LSM. This is
> because it was stated to me very clearly that LSM's should not attempt
> to understand FS mount data and the burdon to understand security should
> be in the FS which owns the options.
Perhaps a mount option prefix then. "Smack.root", "SELinux.context",
that sort of thing. An LSM writer shouldn't have to patch security.h
every time she wants to add a mount option.
> Signed-off-by: Eric Paris <eparis@redhat.com>
>
> ---
>
> For now the only forseen user of this interface is NFS. NFS uses a
> binary blob in kernel for mount data (it uses this blob irrespective of
> the binary vs. text mount options it can get from userspace.) NFS must
> then set its own mount options explicitly so we need some interface for
> it to do so.
>
>
> include/linux/security.h | 36 ++
> security/dummy.c | 26 ++
> security/security.c | 20 +
> security/selinux/hooks.c | 749
> ++++++++++++++++++++++++-------------
> security/selinux/include/objsec.h | 1 +
> 5 files changed, 578 insertions(+), 254 deletions(-)
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index ac05083..dcbb792 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -34,6 +34,12 @@
> #include <linux/xfrm.h>
> #include <net/flow.h>
>
> +/* only a char in selinux superblock security struct flags */
> +#define FSCONTEXT_MNT 0x01
> +#define CONTEXT_MNT 0x02
> +#define ROOTCONTEXT_MNT 0x04
> +#define DEFCONTEXT_MNT 0x08
> +
> /*
> * Bounding set
> */
> @@ -261,6 +267,22 @@ struct request_sock;
> * Update module state after a successful pivot.
> * @old_nd contains the nameidata structure for the old root.
> * @new_nd contains the nameidata structure for the new root.
> + * @sb_get_mnt_opts:
> + * Get the security relevant mount options used for a superblock
> + * @sb the superblock to get security mount options from
> + * @mount_options array for pointers to mount options
> + * @mount_flags array of ints specifying what each mount options is
> + * @num_opts number of options in the arrays
> + * @sb_set_mnt_opts:
> + * Set the security relevant mount options used for a superblock
> + * @sb the superblock to set security mount options for
> + * @mount_options array for pointers to mount options
> + * @mount_flags array of ints specifying what each mount options is
> + * @num_opts number of options in the arrays
> + * @sb_clone_mnt_opts:
> + * Copy all security options from a given superblock to another
> + * @oldsb old superblock which contain information to clone
> + * @newsb new superblock which needs filled in
> *
> * Security hooks for inode operations.
> *
> @@ -1242,6 +1264,13 @@ struct security_operations {
> struct nameidata * new_nd);
> void (*sb_post_pivotroot) (struct nameidata * old_nd,
> struct nameidata * new_nd);
> + int (*sb_get_mnt_opts) (const struct super_block *sb,
> + char ***mount_options, int **flags,
> + int *num_opts);
> + int (*sb_set_mnt_opts) (struct super_block *sb, char **mount_options,
> + int *flags, int num_opts);
> + void (*sb_clone_mnt_opts) (const struct super_block *oldsb,
> + struct super_block *newsb);
>
> int (*inode_alloc_security) (struct inode *inode);
> void (*inode_free_security) (struct inode *inode);
> @@ -1499,6 +1528,13 @@ void security_sb_post_mountroot(void);
> void security_sb_post_addmount(struct vfsmount *mnt, struct nameidata
> *mountpoint_nd);
> int security_sb_pivotroot(struct nameidata *old_nd, struct nameidata
> *new_nd);
> void security_sb_post_pivotroot(struct nameidata *old_nd, struct nameidata
> *new_nd);
> +int security_sb_get_mnt_opts(const struct super_block *sb, char
> ***mount_options,
> + int **flags, int *num_opts);
> +int security_sb_set_mnt_opts(struct super_block *sb, char **mount_options,
> + int *flags, int num_opts);
> +void security_sb_clone_mnt_opts(const struct super_block *oldsb,
> + struct super_block *newsb);
> +
> int security_inode_alloc(struct inode *inode);
> void security_inode_free(struct inode *inode);
> int security_inode_init_security(struct inode *inode, struct inode *dir,
> diff --git a/security/dummy.c b/security/dummy.c
> index 6d895ad..22d9663 100644
> --- a/security/dummy.c
> +++ b/security/dummy.c
> @@ -245,6 +245,29 @@ static void dummy_sb_post_pivotroot (struct nameidata
> *old_nd, struct nameidata
> return;
> }
>
> +static int dummy_sb_get_mnt_opts(const struct super_block *sb, char
> ***mount_options,
> + int **flags, int *num_opts)
> +{
> + *mount_options = NULL;
> + *flags = NULL;
> + *num_opts = 0;
> + return 0;
> +}
> +
> +static int dummy_sb_set_mnt_opts(struct super_block *sb, char
> **mount_options,
> + int *flags, int num_opts)
> +{
> + if (unlikely(num_opts))
> + return -EOPNOTSUPP;
> + return 0;
> +}
> +
> +static void dummy_sb_clone_mnt_opts(const struct super_block *oldsb,
> + struct super_block *newsb)
> +{
> + return;
> +}
> +
> static int dummy_inode_alloc_security (struct inode *inode)
> {
> return 0;
> @@ -998,6 +1021,9 @@ void security_fixup_ops (struct security_operations
> *ops)
> set_to_dummy_if_null(ops, sb_post_addmount);
> set_to_dummy_if_null(ops, sb_pivotroot);
> set_to_dummy_if_null(ops, sb_post_pivotroot);
> + set_to_dummy_if_null(ops, sb_get_mnt_opts);
> + set_to_dummy_if_null(ops, sb_set_mnt_opts);
> + set_to_dummy_if_null(ops, sb_clone_mnt_opts);
> set_to_dummy_if_null(ops, inode_alloc_security);
> set_to_dummy_if_null(ops, inode_free_security);
> set_to_dummy_if_null(ops, inode_init_security);
> diff --git a/security/security.c b/security/security.c
> index 0e1f1f1..f48fb30 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -308,6 +308,26 @@ void security_sb_post_pivotroot(struct nameidata
> *old_nd, struct nameidata *new_
> security_ops->sb_post_pivotroot(old_nd, new_nd);
> }
>
> +int security_sb_get_mnt_opts (const struct super_block *sb,
> + char ***mount_options,
> + int **flags, int *num_opts)
> +{
> + return security_ops->sb_get_mnt_opts(sb, mount_options, flags, num_opts);
> +}
> +
> +int security_sb_set_mnt_opts (struct super_block *sb,
> + char **mount_options,
> + int *flags, int num_opts)
> +{
> + return security_ops->sb_set_mnt_opts(sb, mount_options, flags, num_opts);
> +}
> +
> +void security_sb_clone_mnt_opts(const struct super_block *oldsb,
> + struct super_block *newsb)
> +{
> + security_ops->sb_clone_mnt_opts(oldsb, newsb);
> +}
> +
> int security_inode_alloc(struct inode *inode)
> {
> inode->i_security = NULL;
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 9f3124b..e710764 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -82,6 +82,8 @@
> #define XATTR_SELINUX_SUFFIX "selinux"
> #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
>
> +#define NUM_SEL_MNT_OPTS 4
> +
> extern unsigned int policydb_loaded_version;
> extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
> extern int selinux_compat_net;
> @@ -321,8 +323,8 @@ enum {
> Opt_error = -1,
> Opt_context = 1,
> Opt_fscontext = 2,
> - Opt_defcontext = 4,
> - Opt_rootcontext = 8,
> + Opt_defcontext = 3,
> + Opt_rootcontext = 4,
> };
>
> static match_table_t tokens = {
> @@ -366,150 +368,318 @@ static int may_context_mount_inode_relabel(u32 sid,
> return rc;
> }
>
> -static int try_context_mount(struct super_block *sb, void *data)
> +static int sb_finish_set_opts(struct super_block *sb)
> {
> - char *context = NULL, *defcontext = NULL;
> - char *fscontext = NULL, *rootcontext = NULL;
> - const char *name;
> - u32 sid;
> - int alloc = 0, rc = 0, seen = 0;
> - struct task_security_struct *tsec = current->security;
> struct superblock_security_struct *sbsec = sb->s_security;
> + struct dentry *root = sb->s_root;
> + struct inode *root_inode = root->d_inode;
> + int rc = 0;
>
> - if (!data)
> - goto out;
> + if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
> + /* Make sure that the xattr handler exists and that no
> + error other than -ENODATA is returned by getxattr on
> + the root directory. -ENODATA is ok, as this may be
> + the first boot of the SELinux kernel before we have
> + assigned xattr values to the filesystem. */
> + if (!root_inode->i_op->getxattr) {
> + printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
> + "xattr support\n", sb->s_id, sb->s_type->name);
> + rc = -EOPNOTSUPP;
> + goto out;
> + }
> + rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
> + if (rc < 0 && rc != -ENODATA) {
> + if (rc == -EOPNOTSUPP)
> + printk(KERN_WARNING "SELinux: (dev %s, type "
> + "%s) has no security xattr handler\n",
> + sb->s_id, sb->s_type->name);
> + else
> + printk(KERN_WARNING "SELinux: (dev %s, type "
> + "%s) getxattr errno %d\n", sb->s_id,
> + sb->s_type->name, -rc);
> + goto out;
> + }
> + }
>
> - name = sb->s_type->name;
> + sbsec->initialized = 1;
>
> - if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
> + if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
> + printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown
> behavior\n",
> + sb->s_id, sb->s_type->name);
> + }
> + else {
> + printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
> + sb->s_id, sb->s_type->name,
> + labeling_behaviors[sbsec->behavior-1]);
> + }
>
> - /* NFS we understand. */
> - if (!strcmp(name, "nfs")) {
> - struct nfs_mount_data *d = data;
> + /* Initialize the root inode. */
> + rc = inode_doinit_with_dentry(root_inode, root);
>
> - if (d->version < NFS_MOUNT_VERSION)
> - goto out;
> + /* Initialize any other inodes associated with the superblock, e.g.
> + inodes created prior to initial policy load or inodes created
> + during get_sb by a pseudo filesystem that directly
> + populates itself. */
> + spin_lock(&sbsec->isec_lock);
> +next_inode:
> + if (!list_empty(&sbsec->isec_head)) {
> + struct inode_security_struct *isec =
> + list_entry(sbsec->isec_head.next,
> + struct inode_security_struct, list);
> + struct inode *inode = isec->inode;
> + spin_unlock(&sbsec->isec_lock);
> + inode = igrab(inode);
> + if (inode) {
> + if (!IS_PRIVATE (inode))
> + inode_doinit(inode);
> + iput(inode);
> + }
> + spin_lock(&sbsec->isec_lock);
> + list_del_init(&isec->list);
> + goto next_inode;
> + }
> + spin_unlock(&sbsec->isec_lock);
> +out:
> + return rc;
> +}
>
> - if (d->context[0]) {
> - context = d->context;
> - seen |= Opt_context;
> - }
> - } else
> - goto out;
> +/*
> + * This function should allow an FS to ask what it's mount security
> + * options were so it can use those later for submounts, displaying
> + * mount options, or whatever.
> + */
> +static int selinux_get_mnt_opts(const struct super_block *sb,
> + char ***mount_options, int **mnt_opts_flags,
> + int *num_opts)
> +{
> + int rc = 0, i;
> + struct superblock_security_struct *sbsec = sb->s_security;
> + char *context = NULL;
> + u32 len;
> + char tmp;
>
> - } else {
> - /* Standard string-based options. */
> - char *p, *options = data;
> + *num_opts = 0;
> + *mount_options = NULL;
> + *mnt_opts_flags = NULL;
>
> - while ((p = strsep(&options, "|")) != NULL) {
> - int token;
> - substring_t args[MAX_OPT_ARGS];
> + if (!sbsec->initialized)
> + return -EINVAL;
>
> - if (!*p)
> - continue;
> + if (!ss_initialized)
> + return -EINVAL;
>
> - token = match_token(p, tokens, args);
> + /*
> + * if we ever use sbsec flags for anything other than tracking mount
> + * settings this is going to need a mask
> + */
> + tmp = sbsec->flags;
> + /* count the number of mount options for this sb */
> + for(i = 0; i < 8; i++) {
> + if (tmp & 0x01)
> + (*num_opts)++;
> + tmp >>= 1;
> + }
>
> - switch (token) {
> - case Opt_context:
> - if (seen & (Opt_context|Opt_defcontext)) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - context = match_strdup(&args[0]);
> - if (!context) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_context;
> - break;
> + *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC);
> + if (!*mount_options) {
> + rc = -ENOMEM;
> + goto out_free;
> + }
>
> - case Opt_fscontext:
> - if (seen & Opt_fscontext) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - fscontext = match_strdup(&args[0]);
> - if (!fscontext) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_fscontext;
> - break;
> + *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC);
> + if (!*mnt_opts_flags) {
> + rc = -ENOMEM;
> + goto out_free;
> + }
>
> - case Opt_rootcontext:
> - if (seen & Opt_rootcontext) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - rootcontext = match_strdup(&args[0]);
> - if (!rootcontext) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_rootcontext;
> - break;
> + i = 0;
> + if (sbsec->flags & FSCONTEXT_MNT) {
> + rc = security_sid_to_context(sbsec->sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = FSCONTEXT_MNT;
> + }
> + if (sbsec->flags & CONTEXT_MNT) {
> + rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = CONTEXT_MNT;
> + }
> + if (sbsec->flags & DEFCONTEXT_MNT) {
> + rc = security_sid_to_context(sbsec->def_sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT;
> + }
> + if (sbsec->flags & ROOTCONTEXT_MNT) {
> + struct inode *root = sbsec->sb->s_root->d_inode;
> + struct inode_security_struct *isec = root->i_security;
>
> - case Opt_defcontext:
> - if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
> - rc = -EINVAL;
> - printk(KERN_WARNING "SELinux: "
> - "defcontext option is invalid "
> - "for this filesystem type\n");
> - goto out_free;
> - }
> - if (seen & (Opt_context|Opt_defcontext)) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - defcontext = match_strdup(&args[0]);
> - if (!defcontext) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_defcontext;
> - break;
> + rc = security_sid_to_context(isec->sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT;
> + }
>
> - default:
> - rc = -EINVAL;
> - printk(KERN_WARNING "SELinux: unknown mount "
> - "option\n");
> - goto out_free;
> + BUG_ON(i != *num_opts);
>
> - }
> - }
> - }
> + return 0;
> +
> +out_free:
> + /* don't leak context string if security_sid_to_context had an error */
> + if(*mount_options && i)
> + for (; i > 0; i--)
> + kfree((*mount_options)[i-1]);
> + kfree(*mount_options);
> + *mount_options = NULL;
> + kfree(*mnt_opts_flags);
> + *mnt_opts_flags = NULL;
> + *num_opts = 0;
> + return rc;
> +}
> +
> +static int bad_option(struct superblock_security_struct *sbsec, char flag,
> + u32 old_sid, u32 new_sid)
> +{
> + /* check if the old mount command had the same options */
> + if (sbsec->initialized)
> + if (!(sbsec->flags & flag) ||
> + (old_sid != new_sid))
> + return 1;
> +
> + /* check if we were passed the same options twice,
> + * aka someone passed context=a,context=b
> + */
> + if (!sbsec->initialized)
> + if (sbsec->flags & flag)
> + return 1;
> + return 0;
> +}
> +/*
> + * Allow filesystems with binary mount data to explicitly set mount point
> labeling.
> + */
> +int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
> + int *flags, int num_opts)
> +{
> + int rc = 0, i;
> + struct task_security_struct *tsec = current->security;
> + struct superblock_security_struct *sbsec = sb->s_security;
> + const char *name = sb->s_type->name;
> + struct inode *inode = sbsec->sb->s_root->d_inode;
> + struct inode_security_struct *root_isec = inode->i_security;
> + u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
> + u32 defcontext_sid = 0;
> +
> + mutex_lock(&sbsec->lock);
>
> - if (!seen)
> + if (!ss_initialized) {
> + if (!num_opts) {
> + /* Defer initialization until selinux_complete_init,
> + after the initial policy is loaded and the security
> + server is ready to handle calls. */
> + spin_lock(&sb_security_lock);
> + if (list_empty(&sbsec->list))
> + list_add(&sbsec->list, &superblock_security_head);
> + spin_unlock(&sb_security_lock);
> + goto out;
> + }
> + rc = -EINVAL;
> + printk(KERN_WARNING "Unable to set superblock options before "
> + "the security server is initialized\n");
> goto out;
> + }
>
> - /* sets the context of the superblock for the fs being mounted. */
> - if (fscontext) {
> - rc = security_context_to_sid(fscontext, strlen(fscontext), &sid);
> + /*
> + * parse the mount options, check if they are valid sids.
> + * 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;
> + rc = security_context_to_sid(mount_options[i],
> + strlen(mount_options[i]), &sid);
> if (rc) {
> printk(KERN_WARNING "SELinux: security_context_to_sid"
> "(%s) failed for (dev %s, type %s) errno=%d\n",
> - fscontext, sb->s_id, name, rc);
> - goto out_free;
> + mount_options[i], sb->s_id, name, rc);
> + goto out;
> }
> + switch(flags[i]) {
> + case FSCONTEXT_MNT:
> + fscontext_sid = sid;
> +
> + 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 (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 (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 (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
> + defcontext_sid))
> + goto out_double_mount;
>
> - rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> + sbsec->flags |= DEFCONTEXT_MNT;
> +
> + break;
> + default:
> + rc = -EINVAL;
> + goto out;
> + }
> + }
> +
> + if (sbsec->initialized) {
> + /* previously mounted with options, but not on this attempt? */
> + if (sbsec->flags && !num_opts)
> + goto out_double_mount;
> + rc = 0;
> + goto out;
> + }
> +
> + if (strcmp(sb->s_type->name, "proc") == 0)
> + sbsec->proc = 1;
> +
> + /* Determine the labeling behavior to use for this filesystem type. */
> + rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
> + if (rc) {
> + printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
> + __FUNCTION__, sb->s_type->name, rc);
> + goto out;
> + }
> +
> + /* sets the context of the superblock for the fs being mounted. */
> + if (fscontext_sid) {
> +
> + rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> + goto out;
>
> - sbsec->sid = sid;
> + sbsec->sid = fscontext_sid;
> }
>
> /*
> @@ -517,182 +687,250 @@ static int try_context_mount(struct super_block *sb,
> void *data)
> * sets the label used on all file below the mountpoint, and will set
> * the superblock context if not already set.
> */
> - if (context) {
> - rc = security_context_to_sid(context, strlen(context), &sid);
> - if (rc) {
> - printk(KERN_WARNING "SELinux: security_context_to_sid"
> - "(%s) failed for (dev %s, type %s) errno=%d\n",
> - context, sb->s_id, name, rc);
> - goto out_free;
> - }
> -
> - if (!fscontext) {
> - rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> + if (context_sid) {
> + if (!fscontext_sid) {
> + rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> - sbsec->sid = sid;
> + goto out;
> + sbsec->sid = context_sid;
> } else {
> - rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> + rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> + goto out;
> }
> - sbsec->mntpoint_sid = sid;
> + if (!rootcontext_sid)
> + rootcontext_sid = context_sid;
>
> + sbsec->mntpoint_sid = context_sid;
> sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
> }
>
> - if (rootcontext) {
> - struct inode *inode = sb->s_root->d_inode;
> - struct inode_security_struct *isec = inode->i_security;
> - rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid);
> - if (rc) {
> - printk(KERN_WARNING "SELinux: security_context_to_sid"
> - "(%s) failed for (dev %s, type %s) errno=%d\n",
> - rootcontext, sb->s_id, name, rc);
> - goto out_free;
> - }
> -
> - rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> + if (rootcontext_sid) {
> + rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> + goto out;
>
> - isec->sid = sid;
> - isec->initialized = 1;
> + root_isec->sid = rootcontext_sid;
> + root_isec->initialized = 1;
> }
>
> - if (defcontext) {
> - rc = security_context_to_sid(defcontext, strlen(defcontext), &sid);
> - if (rc) {
> - printk(KERN_WARNING "SELinux: security_context_to_sid"
> - "(%s) failed for (dev %s, type %s) errno=%d\n",
> - defcontext, sb->s_id, name, rc);
> - goto out_free;
> + if (defcontext_sid) {
> + if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
> + rc = -EINVAL;
> + printk(KERN_WARNING "SELinux: defcontext option is "
> + "invalid for this filesystem type\n");
> + goto out;
> }
>
> - if (sid == sbsec->def_sid)
> - goto out_free;
> -
> - rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> - if (rc)
> - goto out_free;
> + if (defcontext_sid != sbsec->def_sid) {
> + rc = may_context_mount_inode_relabel(defcontext_sid,
> + sbsec, tsec);
> + if (rc)
> + goto out;
> + }
>
> - sbsec->def_sid = sid;
> + sbsec->def_sid = defcontext_sid;
> }
>
> -out_free:
> - if (alloc) {
> - kfree(context);
> - kfree(defcontext);
> - kfree(fscontext);
> - kfree(rootcontext);
> - }
> + rc = sb_finish_set_opts(sb);
> out:
> + mutex_unlock(&sbsec->lock);
> return rc;
> +out_double_mount:
> + rc = -EINVAL;
> + printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different "
> + "security settings for (dev %s, type %s)\n", sb->s_id, name);
> + goto out;
> }
>
> -static int superblock_doinit(struct super_block *sb, void *data)
> +static void selinux_sb_clone_mnt_opts (const struct super_block *oldsb,
> + struct super_block *newsb)
> {
> - struct superblock_security_struct *sbsec = sb->s_security;
> - struct dentry *root = sb->s_root;
> - struct inode *inode = root->d_inode;
> - int rc = 0;
> + const struct superblock_security_struct *oldsbsec = oldsb->s_security;
> + struct superblock_security_struct *newsbsec = newsb->s_security;
>
> - mutex_lock(&sbsec->lock);
> - if (sbsec->initialized)
> - goto out;
> + int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
> + int set_context = (oldsbsec->flags & CONTEXT_MNT);
> + int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT);
>
> - if (!ss_initialized) {
> - /* Defer initialization until selinux_complete_init,
> - after the initial policy is loaded and the security
> - server is ready to handle calls. */
> - spin_lock(&sb_security_lock);
> - if (list_empty(&sbsec->list))
> - list_add(&sbsec->list, &superblock_security_head);
> - spin_unlock(&sb_security_lock);
> - goto out;
> + /* we can't error, we can't save the info, this shouldn't get called
> + * this early in the boot process. */
> + BUG_ON(!ss_initialized);
> +
> + /* this might go away sometime down the line if there is a new user
> + * of clone, but for now, nfs better not get here... */
> + BUG_ON(newsbsec->initialized);
> +
> + /* how can we clone if the old one wasn't set up?? */
> + BUG_ON(!oldsbsec->initialized);
> +
> + mutex_lock(&newsbsec->lock);
> +
> + newsbsec->flags = oldsbsec->flags;
> +
> + newsbsec->sid = oldsbsec->sid;
> + newsbsec->def_sid = oldsbsec->def_sid;
> + newsbsec->behavior = oldsbsec->behavior;
> +
> + if (set_context) {
> + u32 sid = oldsbsec->mntpoint_sid;
> +
> + if (!set_fscontext)
> + newsbsec->sid = sid;
> + if (!set_rootcontext) {
> + struct inode *newinode = newsb->s_root->d_inode;
> + struct inode_security_struct *newisec = newinode->i_security;
> + newisec->sid = sid;
> + }
> + newsbsec->mntpoint_sid = sid;
> }
> + if (set_rootcontext) {
> + const struct inode *oldinode = oldsb->s_root->d_inode;
> + const struct inode_security_struct *oldisec = oldinode->i_security;
> + struct inode *newinode = newsb->s_root->d_inode;
> + struct inode_security_struct *newisec = newinode->i_security;
>
> - /* Determine the labeling behavior to use for this filesystem type. */
> - rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
> - if (rc) {
> - printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
> - __FUNCTION__, sb->s_type->name, rc);
> - goto out;
> + newisec->sid = oldisec->sid;
> }
>
> - rc = try_context_mount(sb, data);
> - if (rc)
> + sb_finish_set_opts(newsb);
> + mutex_unlock(&newsbsec->lock);
> +}
> +
> +/*
> + * string mount options parsing and call set the sbsec
> + */
> +static int superblock_doinit(struct super_block *sb, void *data)
> +{
> + char *context = NULL, *defcontext = NULL;
> + char *fscontext = NULL, *rootcontext = NULL;
> + int rc = 0;
> + char *p, *options = data;
> + /* selinux only know about a fixed number of mount options */
> + char *mnt_opts[NUM_SEL_MNT_OPTS];
> + int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0;
> +
> + if (!data)
> goto out;
>
> - if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
> - /* Make sure that the xattr handler exists and that no
> - error other than -ENODATA is returned by getxattr on
> - the root directory. -ENODATA is ok, as this may be
> - the first boot of the SELinux kernel before we have
> - assigned xattr values to the filesystem. */
> - if (!inode->i_op->getxattr) {
> - printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
> - "xattr support\n", sb->s_id, sb->s_type->name);
> - rc = -EOPNOTSUPP;
> - goto out;
> - }
> - rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
> - if (rc < 0 && rc != -ENODATA) {
> - if (rc == -EOPNOTSUPP)
> - printk(KERN_WARNING "SELinux: (dev %s, type "
> - "%s) has no security xattr handler\n",
> - sb->s_id, sb->s_type->name);
> - else
> - printk(KERN_WARNING "SELinux: (dev %s, type "
> - "%s) getxattr errno %d\n", sb->s_id,
> - sb->s_type->name, -rc);
> + /* with the nfs patch this will become a goto out; */
> + if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
> + const char *name = sb->s_type->name;
> + /* NFS we understand. */
> + if (!strcmp(name, "nfs")) {
> + struct nfs_mount_data *d = data;
> +
> + if (d->version != NFS_MOUNT_VERSION)
> + goto out;
> +
> + if (d->context[0]) {
> + context = kstrdup(d->context, GFP_KERNEL);
> + if (!context) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + }
> + goto build_flags;
> + } else
> goto out;
> - }
> }
>
> - if (strcmp(sb->s_type->name, "proc") == 0)
> - sbsec->proc = 1;
> + /* Standard string-based options. */
> + while ((p = strsep(&options, "|")) != NULL) {
> + int token;
> + substring_t args[MAX_OPT_ARGS];
>
> - sbsec->initialized = 1;
> + if (!*p)
> + continue;
>
> - if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
> - printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown
> behavior\n",
> - sb->s_id, sb->s_type->name);
> - }
> - else {
> - printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
> - sb->s_id, sb->s_type->name,
> - labeling_behaviors[sbsec->behavior-1]);
> - }
> + token = match_token(p, tokens, args);
>
> - /* Initialize the root inode. */
> - rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
> + switch (token) {
> + case Opt_context:
> + if (context || defcontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + context = match_strdup(&args[0]);
> + if (!context) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + case Opt_fscontext:
> + if (fscontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + fscontext = match_strdup(&args[0]);
> + if (!fscontext) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + case Opt_rootcontext:
> + if (rootcontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + rootcontext = match_strdup(&args[0]);
> + if (!rootcontext) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + case Opt_defcontext:
> + if (context || defcontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + defcontext = match_strdup(&args[0]);
> + if (!defcontext) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + default:
> + rc = -EINVAL;
> + printk(KERN_WARNING "SELinux: unknown mount option\n");
> + goto out_err;
>
> - /* Initialize any other inodes associated with the superblock, e.g.
> - inodes created prior to initial policy load or inodes created
> - during get_sb by a pseudo filesystem that directly
> - populates itself. */
> - spin_lock(&sbsec->isec_lock);
> -next_inode:
> - if (!list_empty(&sbsec->isec_head)) {
> - struct inode_security_struct *isec =
> - list_entry(sbsec->isec_head.next,
> - struct inode_security_struct, list);
> - struct inode *inode = isec->inode;
> - spin_unlock(&sbsec->isec_lock);
> - inode = igrab(inode);
> - if (inode) {
> - if (!IS_PRIVATE (inode))
> - inode_doinit(inode);
> - iput(inode);
> }
> - spin_lock(&sbsec->isec_lock);
> - list_del_init(&isec->list);
> - goto next_inode;
> }
> - spin_unlock(&sbsec->isec_lock);
> +
> +build_flags:
> + if (fscontext) {
> + mnt_opts[num_mnt_opts] = fscontext;
> + mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
> + }
> + if (context) {
> + mnt_opts[num_mnt_opts] = context;
> + mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
> + }
> + if (rootcontext) {
> + mnt_opts[num_mnt_opts] = rootcontext;
> + mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
> + }
> + if (defcontext) {
> + mnt_opts[num_mnt_opts] = defcontext;
> + mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
> + }
> +
> out:
> - mutex_unlock(&sbsec->lock);
> + rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts);
> +out_err:
> + kfree(context);
> + kfree(defcontext);
> + kfree(fscontext);
> + kfree(rootcontext);
> return rc;
> }
>
> @@ -4800,6 +5038,9 @@ static struct security_operations selinux_ops = {
> .sb_statfs = selinux_sb_statfs,
> .sb_mount = selinux_mount,
> .sb_umount = selinux_umount,
> + .sb_get_mnt_opts = selinux_get_mnt_opts,
> + .sb_set_mnt_opts = selinux_set_mnt_opts,
> + .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts,
>
> .inode_alloc_security = selinux_inode_alloc_security,
> .inode_free_security = selinux_inode_free_security,
> diff --git a/security/selinux/include/objsec.h
> b/security/selinux/include/objsec.h
> index 642a9fd..4138a80 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -65,6 +65,7 @@ struct superblock_security_struct {
> u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
> unsigned int behavior; /* labeling behavior */
> unsigned char initialized; /* initialization flag */
> + unsigned char flags; /* which mount options were specified */
> unsigned char proc; /* proc fs */
> struct mutex lock;
> struct list_head isec_head;
>
>
>
>
Casey Schaufler
casey@schaufler-ca.com
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information
2007-11-09 22:46 ` Casey Schaufler
@ 2007-11-10 6:41 ` Eric Paris
2007-11-11 6:19 ` Casey Schaufler
0 siblings, 1 reply; 5+ messages in thread
From: Eric Paris @ 2007-11-10 6:41 UTC (permalink / raw)
To: casey; +Cc: selinux, LSM List, linux-fsdevel, sds, jmorris
On Fri, 2007-11-09 at 14:46 -0800, Casey Schaufler wrote:
> --- Eric Paris <eparis@redhat.com> wrote:
>
> > Adds security_get_sb_mnt_opts, security_set_sb_mnt_opts, and
> > security_clont_sb_mnt_opts to the LSM and to SELinux. This will allow
> > filesystems to directly own and control all of their mount options if
> > they so choose.
>
> I understand why you would want get_sb_mnt_opts(), but what
> is the value for set_sb_mnt_opts() and what is the purpose of
> clone_sb_mnt_opts()?
set is really the most important one. NFS knows when it creates a
superblock (using SELinux as an example) that it wants to set
context=blah. Thus it calls into set_sb_mnt_opts with the flag for
"context=" and "blah." get_sb_mnt_opts will likely get used in the
future for /proc/mounts to be able to report the security options.
clone is really just to make it easy for the FS. If they know "i want
the new one to look like this old one" they can just call into clone_
and don't have to worry about dealing freeing memory or anything like
that...
>
> > This interface deals only with option identifiers and
> > strings so it should generic enough for any LSM which may come in the
> > future. Filesystems which pass text mount data around in the kernel
> > (almost all of them) need not currently make use of this interface for
> > SELinux sake since it will still parse those strings as it always has.
>
> If SELinux is still dealing with strings on it's own what is
> the point of these hooks?
The point is that not all filesystems use strings. NFS is the real in
kernel kicker. See things like fs/nfs/namespace.c:nfs_do_clone_mount()
where they pass a binary blob into the vfs which arrives to the LSM as a
binary blob which it cannot parse. (note NFS also uses
nfs_parsed_mount_data and nfs_mount_data)
Since the LSM can't (ok, "isn't allowed" according to previous
discussions with vfs/fs people) deal with those binary blobs to get its
options some method must be created for those filesystems to pass that
data in a usable way.
>
> > An LSM would need to implement these functions only if they had mount
> > time options, such as selinux has context= or fscontext=. If the LSM
> > has no mount time options they could simply not implement and let the
> > dummy ops take care of things.
>
> Smack and SELinux currently deal with options in sb_kern_mount(), with
> help from sb_copy_data(). Why change the implementation?
I don't plan to change anything for any FS that passes text options, but
we aren't allowed to parse binary blobs (nor can we actually even know
for sure what blobs we are dealing with currently in the LSM even if we
were 'allowed' to parse them and get the needed data directly)
> > A LSM other than SELinux would need to define new option numbers in
> > security.h
>
> I don't think it is a good idea to require that LSM specific
> information be stored outside the scope of the LSM.
Its either that or FS specific knowledge inside the LSM. See
security/selinux/hooks.c:try_context_mount() for an example of NFS
specific knowledge inside an LSM. This current implementation has bugs
since we don't know if *data is any of the 3 above named structs. This
patch doesn't fix those issues, but lays the groundwork for a fix...
> > (or could reuse if they have the same basic meaning I guess)
> > and any FS which decides to own there own security options would need to
> > be patched to use this new interface for every possible LSM. This is
> > because it was stated to me very clearly that LSM's should not attempt
> > to understand FS mount data and the burdon to understand security should
> > be in the FS which owns the options.
>
> Perhaps a mount option prefix then. "Smack.root", "SELinux.context",
> that sort of thing. An LSM writer shouldn't have to patch security.h
> every time she wants to add a mount option.
I did originally namespace these things such as SELINUX__CONTEXT_MNT but
later wondered what the point was. If another LSM decided to somehow
make use of the same infrastructure in FS that have binary mount data
and they used context= they should be able to use a generic CONTEXT_MNT
rather than pretend that flag has some special meaning. It actually
works nicely for other LSMs since once I get finished with NFS it should
support 3 mount options which although maybe not named nicely for non
SELinux LSMs will be usable without any FS changes...
And while I don't think its a great thing that every LSM is going to be
adding things to the generic security.h if they want things to work,
they are going to have to add things to the generic structures used by
filesystems which use binary mount data.
We have this tight coupling of information. 3 options.
1) make the LSM very FS knowledgeable (what SELinux currently does for
NFS and is maybe not a great idea and has already been strongly pooped
on on list before)
2) make the FS know what LSM is running and what options it is allowed
to send (obviously a terrible unscalable mess if we have to do this for
more than NFS and we still need a get/set type interface)
3) make the interface generic and knowledgeable of everything so there
is little/no cross knowledge. If the FS happens to send an selinux mnt
opt flag to SMACK (say a user mounts his NFS filesystem with fscontext=^
when running SMACK) then SMACK should return an EINVAL since it does not
implement such a thing.
-Eric
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information
2007-11-10 6:41 ` Eric Paris
@ 2007-11-11 6:19 ` Casey Schaufler
0 siblings, 0 replies; 5+ messages in thread
From: Casey Schaufler @ 2007-11-11 6:19 UTC (permalink / raw)
To: Eric Paris, casey; +Cc: selinux, LSM List, linux-fsdevel, sds, jmorris
--- Eric Paris <eparis@redhat.com> wrote:
>
> On Fri, 2007-11-09 at 14:46 -0800, Casey Schaufler wrote:
> > --- Eric Paris <eparis@redhat.com> wrote:
> >
> > > Adds security_get_sb_mnt_opts, security_set_sb_mnt_opts, and
> > > security_clont_sb_mnt_opts to the LSM and to SELinux. This will allow
> > > filesystems to directly own and control all of their mount options if
> > > they so choose.
> >
> > I understand why you would want get_sb_mnt_opts(), but what
> > is the value for set_sb_mnt_opts() and what is the purpose of
> > clone_sb_mnt_opts()?
>
> set is really the most important one. NFS knows when it creates a
> superblock (using SELinux as an example) that it wants to set
> context=blah. Thus it calls into set_sb_mnt_opts with the flag for
> "context=" and "blah." get_sb_mnt_opts will likely get used in the
> future for /proc/mounts to be able to report the security options.
> clone is really just to make it easy for the FS. If they know "i want
> the new one to look like this old one" they can just call into clone_
> and don't have to worry about dealing freeing memory or anything like
> that...
> >
> > > This interface deals only with option identifiers and
> > > strings so it should generic enough for any LSM which may come in the
> > > future. Filesystems which pass text mount data around in the kernel
> > > (almost all of them) need not currently make use of this interface for
> > > SELinux sake since it will still parse those strings as it always has.
> >
> > If SELinux is still dealing with strings on it's own what is
> > the point of these hooks?
>
> The point is that not all filesystems use strings. NFS is the real in
> kernel kicker. See things like fs/nfs/namespace.c:nfs_do_clone_mount()
> where they pass a binary blob into the vfs which arrives to the LSM as a
> binary blob which it cannot parse. (note NFS also uses
> nfs_parsed_mount_data and nfs_mount_data)
Nothing like a real example to make your point. Yup, I see it.
> Since the LSM can't (ok, "isn't allowed" according to previous
> discussions with vfs/fs people) deal with those binary blobs to get its
> options some method must be created for those filesystems to pass that
> data in a usable way.
> >
> > > An LSM would need to implement these functions only if they had mount
> > > time options, such as selinux has context= or fscontext=. If the LSM
> > > has no mount time options they could simply not implement and let the
> > > dummy ops take care of things.
> >
> > Smack and SELinux currently deal with options in sb_kern_mount(), with
> > help from sb_copy_data(). Why change the implementation?
>
> I don't plan to change anything for any FS that passes text options, but
> we aren't allowed to parse binary blobs (nor can we actually even know
> for sure what blobs we are dealing with currently in the LSM even if we
> were 'allowed' to parse them and get the needed data directly)
Ick.
> > > A LSM other than SELinux would need to define new option numbers in
> > > security.h
> >
> > I don't think it is a good idea to require that LSM specific
> > information be stored outside the scope of the LSM.
>
> Its either that or FS specific knowledge inside the LSM. See
> security/selinux/hooks.c:try_context_mount() for an example of NFS
> specific knowledge inside an LSM. This current implementation has bugs
> since we don't know if *data is any of the 3 above named structs. This
> patch doesn't fix those issues, but lays the groundwork for a fix...
If the filesystem interface requires different behavior for
different filesystem types I'd say it's broken, but given that
that is the historical behavior I suppose we'll have to either
live with it or suggest a better way. Looks as if that's your
plan here.
> > > (or could reuse if they have the same basic meaning I guess)
> > > and any FS which decides to own there own security options would need to
> > > be patched to use this new interface for every possible LSM. This is
> > > because it was stated to me very clearly that LSM's should not attempt
> > > to understand FS mount data and the burdon to understand security should
> > > be in the FS which owns the options.
> >
> > Perhaps a mount option prefix then. "Smack.root", "SELinux.context",
> > that sort of thing. An LSM writer shouldn't have to patch security.h
> > every time she wants to add a mount option.
>
> I did originally namespace these things such as SELINUX__CONTEXT_MNT but
> later wondered what the point was. If another LSM decided to somehow
> make use of the same infrastructure in FS that have binary mount data
> and they used context= they should be able to use a generic CONTEXT_MNT
> rather than pretend that flag has some special meaning. It actually
> works nicely for other LSMs since once I get finished with NFS it should
> support 3 mount options which although maybe not named nicely for non
> SELinux LSMs will be usable without any FS changes...
I hear rumblings about reviving the notion of LSM stacking, in which
case a limited number of shared mount options might not work so well.
> And while I don't think its a great thing that every LSM is going to be
> adding things to the generic security.h if they want things to work,
> they are going to have to add things to the generic structures used by
> filesystems which use binary mount data.
Sorry if I sound ignorant/not-up-to-date/stoopid, but what about the
notion of a mechanism to support generic mount options and FS specific
ones such that the FS never sees anything but what it expects, but
there's also a "blob" for other schemes, including LSMs?
> We have this tight coupling of information. 3 options.
>
> 1) make the LSM very FS knowledgeable (what SELinux currently does for
> NFS and is maybe not a great idea and has already been strongly pooped
> on on list before)
Not a good idea, I confess.
> 2) make the FS know what LSM is running and what options it is allowed
> to send (obviously a terrible unscalable mess if we have to do this for
> more than NFS and we still need a get/set type interface)
Again, I agree it's not pretty.
> 3) make the interface generic and knowledgeable of everything so there
> is little/no cross knowledge. If the FS happens to send an selinux mnt
> opt flag to SMACK (say a user mounts his NFS filesystem with fscontext=^
> when running SMACK) then SMACK should return an EINVAL since it does not
> implement such a thing.
Ok, yes, I think I see. Since the FS "owns" the mount options it has
to be the FS that calls security_<whatever> to pass along to the LSM.
A FS developer who is uncooperative with LSM developers may choose not
to pass along any or all LSM specific mount options, although it might
be reasonable to expect that new filesystems would be expected to do
so if they want to get mainlined. A new LSM would be expected to deal
with existing mainline filesystems that do not pass along the mount
options the LSM might want.
By tackling NFS for SELinux you are providing the prototype of how
the interaction ought to go forward from both the FS and LSM points
of view.
Thanks for the explaination.
Casey Schaufler
casey@schaufler-ca.com
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information
2007-11-09 22:22 [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information Eric Paris
2007-11-09 22:46 ` Casey Schaufler
@ 2007-11-28 17:04 ` Eric Paris
1 sibling, 0 replies; 5+ messages in thread
From: Eric Paris @ 2007-11-28 17:04 UTC (permalink / raw)
To: Eric Paris
Cc: selinux, LSM List, linux-fsdevel, sds, jmorris, Casey Schaufler,
chrisw
Any complaints or questions left here? I've got more people reporting
problems with NFS/SELinux and this is the first (and hardest) step to
making NFS and any genic LSM play nicely. If there are not any
problems how should this be pushed to linus? Through James Morris's
git tree? Through Chris Wright's LSM tree?
And I did get a:
Acked-by: Stephen D. Smalley <sds@tycho.nsa.gov>
Which I don't think ever appeared on all the lists for everyone interested.
-Eric
> Adds security_get_sb_mnt_opts, security_set_sb_mnt_opts, and
> security_clont_sb_mnt_opts to the LSM and to SELinux. This will allow
> filesystems to directly own and control all of their mount options if
> they so choose. This interface deals only with option identifiers and
> strings so it should generic enough for any LSM which may come in the
> future. Filesystems which pass text mount data around in the kernel
> (almost all of them) need not currently make use of this interface for
> SELinux sake since it will still parse those strings as it always has.
>
> An LSM would need to implement these functions only if they had mount
> time options, such as selinux has context= or fscontext=. If the LSM
> has no mount time options they could simply not implement and let the
> dummy ops take care of things.
>
> A LSM other than SELinux would need to define new option numbers in
> security.h (or could reuse if they have the same basic meaning I guess)
> and any FS which decides to own there own security options would need to
> be patched to use this new interface for every possible LSM. This is
> because it was stated to me very clearly that LSM's should not attempt
> to understand FS mount data and the burdon to understand security should
> be in the FS which owns the options.
>
> Signed-off-by: Eric Paris <eparis@redhat.com>
>
> ---
>
> For now the only forseen user of this interface is NFS. NFS uses a
> binary blob in kernel for mount data (it uses this blob irrespective of
> the binary vs. text mount options it can get from userspace.) NFS must
> then set its own mount options explicitly so we need some interface for
> it to do so.
>
>
> include/linux/security.h | 36 ++
> security/dummy.c | 26 ++
> security/security.c | 20 +
> security/selinux/hooks.c | 749 ++++++++++++++++++++++++-------------
> security/selinux/include/objsec.h | 1 +
> 5 files changed, 578 insertions(+), 254 deletions(-)
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index ac05083..dcbb792 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -34,6 +34,12 @@
> #include <linux/xfrm.h>
> #include <net/flow.h>
>
> +/* only a char in selinux superblock security struct flags */
> +#define FSCONTEXT_MNT 0x01
> +#define CONTEXT_MNT 0x02
> +#define ROOTCONTEXT_MNT 0x04
> +#define DEFCONTEXT_MNT 0x08
> +
> /*
> * Bounding set
> */
> @@ -261,6 +267,22 @@ struct request_sock;
> * Update module state after a successful pivot.
> * @old_nd contains the nameidata structure for the old root.
> * @new_nd contains the nameidata structure for the new root.
> + * @sb_get_mnt_opts:
> + * Get the security relevant mount options used for a superblock
> + * @sb the superblock to get security mount options from
> + * @mount_options array for pointers to mount options
> + * @mount_flags array of ints specifying what each mount options is
> + * @num_opts number of options in the arrays
> + * @sb_set_mnt_opts:
> + * Set the security relevant mount options used for a superblock
> + * @sb the superblock to set security mount options for
> + * @mount_options array for pointers to mount options
> + * @mount_flags array of ints specifying what each mount options is
> + * @num_opts number of options in the arrays
> + * @sb_clone_mnt_opts:
> + * Copy all security options from a given superblock to another
> + * @oldsb old superblock which contain information to clone
> + * @newsb new superblock which needs filled in
> *
> * Security hooks for inode operations.
> *
> @@ -1242,6 +1264,13 @@ struct security_operations {
> struct nameidata * new_nd);
> void (*sb_post_pivotroot) (struct nameidata * old_nd,
> struct nameidata * new_nd);
> + int (*sb_get_mnt_opts) (const struct super_block *sb,
> + char ***mount_options, int **flags,
> + int *num_opts);
> + int (*sb_set_mnt_opts) (struct super_block *sb, char **mount_options,
> + int *flags, int num_opts);
> + void (*sb_clone_mnt_opts) (const struct super_block *oldsb,
> + struct super_block *newsb);
>
> int (*inode_alloc_security) (struct inode *inode);
> void (*inode_free_security) (struct inode *inode);
> @@ -1499,6 +1528,13 @@ void security_sb_post_mountroot(void);
> void security_sb_post_addmount(struct vfsmount *mnt, struct nameidata *mountpoint_nd);
> int security_sb_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd);
> void security_sb_post_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd);
> +int security_sb_get_mnt_opts(const struct super_block *sb, char ***mount_options,
> + int **flags, int *num_opts);
> +int security_sb_set_mnt_opts(struct super_block *sb, char **mount_options,
> + int *flags, int num_opts);
> +void security_sb_clone_mnt_opts(const struct super_block *oldsb,
> + struct super_block *newsb);
> +
> int security_inode_alloc(struct inode *inode);
> void security_inode_free(struct inode *inode);
> int security_inode_init_security(struct inode *inode, struct inode *dir,
> diff --git a/security/dummy.c b/security/dummy.c
> index 6d895ad..22d9663 100644
> --- a/security/dummy.c
> +++ b/security/dummy.c
> @@ -245,6 +245,29 @@ static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata
> return;
> }
>
> +static int dummy_sb_get_mnt_opts(const struct super_block *sb, char ***mount_options,
> + int **flags, int *num_opts)
> +{
> + *mount_options = NULL;
> + *flags = NULL;
> + *num_opts = 0;
> + return 0;
> +}
> +
> +static int dummy_sb_set_mnt_opts(struct super_block *sb, char **mount_options,
> + int *flags, int num_opts)
> +{
> + if (unlikely(num_opts))
> + return -EOPNOTSUPP;
> + return 0;
> +}
> +
> +static void dummy_sb_clone_mnt_opts(const struct super_block *oldsb,
> + struct super_block *newsb)
> +{
> + return;
> +}
> +
> static int dummy_inode_alloc_security (struct inode *inode)
> {
> return 0;
> @@ -998,6 +1021,9 @@ void security_fixup_ops (struct security_operations *ops)
> set_to_dummy_if_null(ops, sb_post_addmount);
> set_to_dummy_if_null(ops, sb_pivotroot);
> set_to_dummy_if_null(ops, sb_post_pivotroot);
> + set_to_dummy_if_null(ops, sb_get_mnt_opts);
> + set_to_dummy_if_null(ops, sb_set_mnt_opts);
> + set_to_dummy_if_null(ops, sb_clone_mnt_opts);
> set_to_dummy_if_null(ops, inode_alloc_security);
> set_to_dummy_if_null(ops, inode_free_security);
> set_to_dummy_if_null(ops, inode_init_security);
> diff --git a/security/security.c b/security/security.c
> index 0e1f1f1..f48fb30 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -308,6 +308,26 @@ void security_sb_post_pivotroot(struct nameidata *old_nd, struct nameidata *new_
> security_ops->sb_post_pivotroot(old_nd, new_nd);
> }
>
> +int security_sb_get_mnt_opts (const struct super_block *sb,
> + char ***mount_options,
> + int **flags, int *num_opts)
> +{
> + return security_ops->sb_get_mnt_opts(sb, mount_options, flags, num_opts);
> +}
> +
> +int security_sb_set_mnt_opts (struct super_block *sb,
> + char **mount_options,
> + int *flags, int num_opts)
> +{
> + return security_ops->sb_set_mnt_opts(sb, mount_options, flags, num_opts);
> +}
> +
> +void security_sb_clone_mnt_opts(const struct super_block *oldsb,
> + struct super_block *newsb)
> +{
> + security_ops->sb_clone_mnt_opts(oldsb, newsb);
> +}
> +
> int security_inode_alloc(struct inode *inode)
> {
> inode->i_security = NULL;
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 9f3124b..e710764 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -82,6 +82,8 @@
> #define XATTR_SELINUX_SUFFIX "selinux"
> #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
>
> +#define NUM_SEL_MNT_OPTS 4
> +
> extern unsigned int policydb_loaded_version;
> extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
> extern int selinux_compat_net;
> @@ -321,8 +323,8 @@ enum {
> Opt_error = -1,
> Opt_context = 1,
> Opt_fscontext = 2,
> - Opt_defcontext = 4,
> - Opt_rootcontext = 8,
> + Opt_defcontext = 3,
> + Opt_rootcontext = 4,
> };
>
> static match_table_t tokens = {
> @@ -366,150 +368,318 @@ static int may_context_mount_inode_relabel(u32 sid,
> return rc;
> }
>
> -static int try_context_mount(struct super_block *sb, void *data)
> +static int sb_finish_set_opts(struct super_block *sb)
> {
> - char *context = NULL, *defcontext = NULL;
> - char *fscontext = NULL, *rootcontext = NULL;
> - const char *name;
> - u32 sid;
> - int alloc = 0, rc = 0, seen = 0;
> - struct task_security_struct *tsec = current->security;
> struct superblock_security_struct *sbsec = sb->s_security;
> + struct dentry *root = sb->s_root;
> + struct inode *root_inode = root->d_inode;
> + int rc = 0;
>
> - if (!data)
> - goto out;
> + if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
> + /* Make sure that the xattr handler exists and that no
> + error other than -ENODATA is returned by getxattr on
> + the root directory. -ENODATA is ok, as this may be
> + the first boot of the SELinux kernel before we have
> + assigned xattr values to the filesystem. */
> + if (!root_inode->i_op->getxattr) {
> + printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
> + "xattr support\n", sb->s_id, sb->s_type->name);
> + rc = -EOPNOTSUPP;
> + goto out;
> + }
> + rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
> + if (rc < 0 && rc != -ENODATA) {
> + if (rc == -EOPNOTSUPP)
> + printk(KERN_WARNING "SELinux: (dev %s, type "
> + "%s) has no security xattr handler\n",
> + sb->s_id, sb->s_type->name);
> + else
> + printk(KERN_WARNING "SELinux: (dev %s, type "
> + "%s) getxattr errno %d\n", sb->s_id,
> + sb->s_type->name, -rc);
> + goto out;
> + }
> + }
>
> - name = sb->s_type->name;
> + sbsec->initialized = 1;
>
> - if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
> + if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
> + printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
> + sb->s_id, sb->s_type->name);
> + }
> + else {
> + printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
> + sb->s_id, sb->s_type->name,
> + labeling_behaviors[sbsec->behavior-1]);
> + }
>
> - /* NFS we understand. */
> - if (!strcmp(name, "nfs")) {
> - struct nfs_mount_data *d = data;
> + /* Initialize the root inode. */
> + rc = inode_doinit_with_dentry(root_inode, root);
>
> - if (d->version < NFS_MOUNT_VERSION)
> - goto out;
> + /* Initialize any other inodes associated with the superblock, e.g.
> + inodes created prior to initial policy load or inodes created
> + during get_sb by a pseudo filesystem that directly
> + populates itself. */
> + spin_lock(&sbsec->isec_lock);
> +next_inode:
> + if (!list_empty(&sbsec->isec_head)) {
> + struct inode_security_struct *isec =
> + list_entry(sbsec->isec_head.next,
> + struct inode_security_struct, list);
> + struct inode *inode = isec->inode;
> + spin_unlock(&sbsec->isec_lock);
> + inode = igrab(inode);
> + if (inode) {
> + if (!IS_PRIVATE (inode))
> + inode_doinit(inode);
> + iput(inode);
> + }
> + spin_lock(&sbsec->isec_lock);
> + list_del_init(&isec->list);
> + goto next_inode;
> + }
> + spin_unlock(&sbsec->isec_lock);
> +out:
> + return rc;
> +}
>
> - if (d->context[0]) {
> - context = d->context;
> - seen |= Opt_context;
> - }
> - } else
> - goto out;
> +/*
> + * This function should allow an FS to ask what it's mount security
> + * options were so it can use those later for submounts, displaying
> + * mount options, or whatever.
> + */
> +static int selinux_get_mnt_opts(const struct super_block *sb,
> + char ***mount_options, int **mnt_opts_flags,
> + int *num_opts)
> +{
> + int rc = 0, i;
> + struct superblock_security_struct *sbsec = sb->s_security;
> + char *context = NULL;
> + u32 len;
> + char tmp;
>
> - } else {
> - /* Standard string-based options. */
> - char *p, *options = data;
> + *num_opts = 0;
> + *mount_options = NULL;
> + *mnt_opts_flags = NULL;
>
> - while ((p = strsep(&options, "|")) != NULL) {
> - int token;
> - substring_t args[MAX_OPT_ARGS];
> + if (!sbsec->initialized)
> + return -EINVAL;
>
> - if (!*p)
> - continue;
> + if (!ss_initialized)
> + return -EINVAL;
>
> - token = match_token(p, tokens, args);
> + /*
> + * if we ever use sbsec flags for anything other than tracking mount
> + * settings this is going to need a mask
> + */
> + tmp = sbsec->flags;
> + /* count the number of mount options for this sb */
> + for(i = 0; i < 8; i++) {
> + if (tmp & 0x01)
> + (*num_opts)++;
> + tmp >>= 1;
> + }
>
> - switch (token) {
> - case Opt_context:
> - if (seen & (Opt_context|Opt_defcontext)) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - context = match_strdup(&args[0]);
> - if (!context) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_context;
> - break;
> + *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC);
> + if (!*mount_options) {
> + rc = -ENOMEM;
> + goto out_free;
> + }
>
> - case Opt_fscontext:
> - if (seen & Opt_fscontext) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - fscontext = match_strdup(&args[0]);
> - if (!fscontext) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_fscontext;
> - break;
> + *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC);
> + if (!*mnt_opts_flags) {
> + rc = -ENOMEM;
> + goto out_free;
> + }
>
> - case Opt_rootcontext:
> - if (seen & Opt_rootcontext) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - rootcontext = match_strdup(&args[0]);
> - if (!rootcontext) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_rootcontext;
> - break;
> + i = 0;
> + if (sbsec->flags & FSCONTEXT_MNT) {
> + rc = security_sid_to_context(sbsec->sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = FSCONTEXT_MNT;
> + }
> + if (sbsec->flags & CONTEXT_MNT) {
> + rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = CONTEXT_MNT;
> + }
> + if (sbsec->flags & DEFCONTEXT_MNT) {
> + rc = security_sid_to_context(sbsec->def_sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT;
> + }
> + if (sbsec->flags & ROOTCONTEXT_MNT) {
> + struct inode *root = sbsec->sb->s_root->d_inode;
> + struct inode_security_struct *isec = root->i_security;
>
> - case Opt_defcontext:
> - if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
> - rc = -EINVAL;
> - printk(KERN_WARNING "SELinux: "
> - "defcontext option is invalid "
> - "for this filesystem type\n");
> - goto out_free;
> - }
> - if (seen & (Opt_context|Opt_defcontext)) {
> - rc = -EINVAL;
> - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> - goto out_free;
> - }
> - defcontext = match_strdup(&args[0]);
> - if (!defcontext) {
> - rc = -ENOMEM;
> - goto out_free;
> - }
> - if (!alloc)
> - alloc = 1;
> - seen |= Opt_defcontext;
> - break;
> + rc = security_sid_to_context(isec->sid, &context, &len);
> + if (rc)
> + goto out_free;
> + (*mount_options)[i] = context;
> + (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT;
> + }
>
> - default:
> - rc = -EINVAL;
> - printk(KERN_WARNING "SELinux: unknown mount "
> - "option\n");
> - goto out_free;
> + BUG_ON(i != *num_opts);
>
> - }
> - }
> - }
> + return 0;
> +
> +out_free:
> + /* don't leak context string if security_sid_to_context had an error */
> + if(*mount_options && i)
> + for (; i > 0; i--)
> + kfree((*mount_options)[i-1]);
> + kfree(*mount_options);
> + *mount_options = NULL;
> + kfree(*mnt_opts_flags);
> + *mnt_opts_flags = NULL;
> + *num_opts = 0;
> + return rc;
> +}
> +
> +static int bad_option(struct superblock_security_struct *sbsec, char flag,
> + u32 old_sid, u32 new_sid)
> +{
> + /* check if the old mount command had the same options */
> + if (sbsec->initialized)
> + if (!(sbsec->flags & flag) ||
> + (old_sid != new_sid))
> + return 1;
> +
> + /* check if we were passed the same options twice,
> + * aka someone passed context=a,context=b
> + */
> + if (!sbsec->initialized)
> + if (sbsec->flags & flag)
> + return 1;
> + return 0;
> +}
> +/*
> + * Allow filesystems with binary mount data to explicitly set mount point labeling.
> + */
> +int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
> + int *flags, int num_opts)
> +{
> + int rc = 0, i;
> + struct task_security_struct *tsec = current->security;
> + struct superblock_security_struct *sbsec = sb->s_security;
> + const char *name = sb->s_type->name;
> + struct inode *inode = sbsec->sb->s_root->d_inode;
> + struct inode_security_struct *root_isec = inode->i_security;
> + u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
> + u32 defcontext_sid = 0;
> +
> + mutex_lock(&sbsec->lock);
>
> - if (!seen)
> + if (!ss_initialized) {
> + if (!num_opts) {
> + /* Defer initialization until selinux_complete_init,
> + after the initial policy is loaded and the security
> + server is ready to handle calls. */
> + spin_lock(&sb_security_lock);
> + if (list_empty(&sbsec->list))
> + list_add(&sbsec->list, &superblock_security_head);
> + spin_unlock(&sb_security_lock);
> + goto out;
> + }
> + rc = -EINVAL;
> + printk(KERN_WARNING "Unable to set superblock options before "
> + "the security server is initialized\n");
> goto out;
> + }
>
> - /* sets the context of the superblock for the fs being mounted. */
> - if (fscontext) {
> - rc = security_context_to_sid(fscontext, strlen(fscontext), &sid);
> + /*
> + * parse the mount options, check if they are valid sids.
> + * 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;
> + rc = security_context_to_sid(mount_options[i],
> + strlen(mount_options[i]), &sid);
> if (rc) {
> printk(KERN_WARNING "SELinux: security_context_to_sid"
> "(%s) failed for (dev %s, type %s) errno=%d\n",
> - fscontext, sb->s_id, name, rc);
> - goto out_free;
> + mount_options[i], sb->s_id, name, rc);
> + goto out;
> }
> + switch(flags[i]) {
> + case FSCONTEXT_MNT:
> + fscontext_sid = sid;
> +
> + 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 (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 (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 (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
> + defcontext_sid))
> + goto out_double_mount;
>
> - rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> + sbsec->flags |= DEFCONTEXT_MNT;
> +
> + break;
> + default:
> + rc = -EINVAL;
> + goto out;
> + }
> + }
> +
> + if (sbsec->initialized) {
> + /* previously mounted with options, but not on this attempt? */
> + if (sbsec->flags && !num_opts)
> + goto out_double_mount;
> + rc = 0;
> + goto out;
> + }
> +
> + if (strcmp(sb->s_type->name, "proc") == 0)
> + sbsec->proc = 1;
> +
> + /* Determine the labeling behavior to use for this filesystem type. */
> + rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
> + if (rc) {
> + printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
> + __FUNCTION__, sb->s_type->name, rc);
> + goto out;
> + }
> +
> + /* sets the context of the superblock for the fs being mounted. */
> + if (fscontext_sid) {
> +
> + rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> + goto out;
>
> - sbsec->sid = sid;
> + sbsec->sid = fscontext_sid;
> }
>
> /*
> @@ -517,182 +687,250 @@ static int try_context_mount(struct super_block *sb, void *data)
> * sets the label used on all file below the mountpoint, and will set
> * the superblock context if not already set.
> */
> - if (context) {
> - rc = security_context_to_sid(context, strlen(context), &sid);
> - if (rc) {
> - printk(KERN_WARNING "SELinux: security_context_to_sid"
> - "(%s) failed for (dev %s, type %s) errno=%d\n",
> - context, sb->s_id, name, rc);
> - goto out_free;
> - }
> -
> - if (!fscontext) {
> - rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
> + if (context_sid) {
> + if (!fscontext_sid) {
> + rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> - sbsec->sid = sid;
> + goto out;
> + sbsec->sid = context_sid;
> } else {
> - rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> + rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> + goto out;
> }
> - sbsec->mntpoint_sid = sid;
> + if (!rootcontext_sid)
> + rootcontext_sid = context_sid;
>
> + sbsec->mntpoint_sid = context_sid;
> sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
> }
>
> - if (rootcontext) {
> - struct inode *inode = sb->s_root->d_inode;
> - struct inode_security_struct *isec = inode->i_security;
> - rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid);
> - if (rc) {
> - printk(KERN_WARNING "SELinux: security_context_to_sid"
> - "(%s) failed for (dev %s, type %s) errno=%d\n",
> - rootcontext, sb->s_id, name, rc);
> - goto out_free;
> - }
> -
> - rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> + if (rootcontext_sid) {
> + rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
> if (rc)
> - goto out_free;
> + goto out;
>
> - isec->sid = sid;
> - isec->initialized = 1;
> + root_isec->sid = rootcontext_sid;
> + root_isec->initialized = 1;
> }
>
> - if (defcontext) {
> - rc = security_context_to_sid(defcontext, strlen(defcontext), &sid);
> - if (rc) {
> - printk(KERN_WARNING "SELinux: security_context_to_sid"
> - "(%s) failed for (dev %s, type %s) errno=%d\n",
> - defcontext, sb->s_id, name, rc);
> - goto out_free;
> + if (defcontext_sid) {
> + if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
> + rc = -EINVAL;
> + printk(KERN_WARNING "SELinux: defcontext option is "
> + "invalid for this filesystem type\n");
> + goto out;
> }
>
> - if (sid == sbsec->def_sid)
> - goto out_free;
> -
> - rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
> - if (rc)
> - goto out_free;
> + if (defcontext_sid != sbsec->def_sid) {
> + rc = may_context_mount_inode_relabel(defcontext_sid,
> + sbsec, tsec);
> + if (rc)
> + goto out;
> + }
>
> - sbsec->def_sid = sid;
> + sbsec->def_sid = defcontext_sid;
> }
>
> -out_free:
> - if (alloc) {
> - kfree(context);
> - kfree(defcontext);
> - kfree(fscontext);
> - kfree(rootcontext);
> - }
> + rc = sb_finish_set_opts(sb);
> out:
> + mutex_unlock(&sbsec->lock);
> return rc;
> +out_double_mount:
> + rc = -EINVAL;
> + printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different "
> + "security settings for (dev %s, type %s)\n", sb->s_id, name);
> + goto out;
> }
>
> -static int superblock_doinit(struct super_block *sb, void *data)
> +static void selinux_sb_clone_mnt_opts (const struct super_block *oldsb,
> + struct super_block *newsb)
> {
> - struct superblock_security_struct *sbsec = sb->s_security;
> - struct dentry *root = sb->s_root;
> - struct inode *inode = root->d_inode;
> - int rc = 0;
> + const struct superblock_security_struct *oldsbsec = oldsb->s_security;
> + struct superblock_security_struct *newsbsec = newsb->s_security;
>
> - mutex_lock(&sbsec->lock);
> - if (sbsec->initialized)
> - goto out;
> + int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
> + int set_context = (oldsbsec->flags & CONTEXT_MNT);
> + int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT);
>
> - if (!ss_initialized) {
> - /* Defer initialization until selinux_complete_init,
> - after the initial policy is loaded and the security
> - server is ready to handle calls. */
> - spin_lock(&sb_security_lock);
> - if (list_empty(&sbsec->list))
> - list_add(&sbsec->list, &superblock_security_head);
> - spin_unlock(&sb_security_lock);
> - goto out;
> + /* we can't error, we can't save the info, this shouldn't get called
> + * this early in the boot process. */
> + BUG_ON(!ss_initialized);
> +
> + /* this might go away sometime down the line if there is a new user
> + * of clone, but for now, nfs better not get here... */
> + BUG_ON(newsbsec->initialized);
> +
> + /* how can we clone if the old one wasn't set up?? */
> + BUG_ON(!oldsbsec->initialized);
> +
> + mutex_lock(&newsbsec->lock);
> +
> + newsbsec->flags = oldsbsec->flags;
> +
> + newsbsec->sid = oldsbsec->sid;
> + newsbsec->def_sid = oldsbsec->def_sid;
> + newsbsec->behavior = oldsbsec->behavior;
> +
> + if (set_context) {
> + u32 sid = oldsbsec->mntpoint_sid;
> +
> + if (!set_fscontext)
> + newsbsec->sid = sid;
> + if (!set_rootcontext) {
> + struct inode *newinode = newsb->s_root->d_inode;
> + struct inode_security_struct *newisec = newinode->i_security;
> + newisec->sid = sid;
> + }
> + newsbsec->mntpoint_sid = sid;
> }
> + if (set_rootcontext) {
> + const struct inode *oldinode = oldsb->s_root->d_inode;
> + const struct inode_security_struct *oldisec = oldinode->i_security;
> + struct inode *newinode = newsb->s_root->d_inode;
> + struct inode_security_struct *newisec = newinode->i_security;
>
> - /* Determine the labeling behavior to use for this filesystem type. */
> - rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
> - if (rc) {
> - printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
> - __FUNCTION__, sb->s_type->name, rc);
> - goto out;
> + newisec->sid = oldisec->sid;
> }
>
> - rc = try_context_mount(sb, data);
> - if (rc)
> + sb_finish_set_opts(newsb);
> + mutex_unlock(&newsbsec->lock);
> +}
> +
> +/*
> + * string mount options parsing and call set the sbsec
> + */
> +static int superblock_doinit(struct super_block *sb, void *data)
> +{
> + char *context = NULL, *defcontext = NULL;
> + char *fscontext = NULL, *rootcontext = NULL;
> + int rc = 0;
> + char *p, *options = data;
> + /* selinux only know about a fixed number of mount options */
> + char *mnt_opts[NUM_SEL_MNT_OPTS];
> + int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0;
> +
> + if (!data)
> goto out;
>
> - if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
> - /* Make sure that the xattr handler exists and that no
> - error other than -ENODATA is returned by getxattr on
> - the root directory. -ENODATA is ok, as this may be
> - the first boot of the SELinux kernel before we have
> - assigned xattr values to the filesystem. */
> - if (!inode->i_op->getxattr) {
> - printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
> - "xattr support\n", sb->s_id, sb->s_type->name);
> - rc = -EOPNOTSUPP;
> - goto out;
> - }
> - rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
> - if (rc < 0 && rc != -ENODATA) {
> - if (rc == -EOPNOTSUPP)
> - printk(KERN_WARNING "SELinux: (dev %s, type "
> - "%s) has no security xattr handler\n",
> - sb->s_id, sb->s_type->name);
> - else
> - printk(KERN_WARNING "SELinux: (dev %s, type "
> - "%s) getxattr errno %d\n", sb->s_id,
> - sb->s_type->name, -rc);
> + /* with the nfs patch this will become a goto out; */
> + if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
> + const char *name = sb->s_type->name;
> + /* NFS we understand. */
> + if (!strcmp(name, "nfs")) {
> + struct nfs_mount_data *d = data;
> +
> + if (d->version != NFS_MOUNT_VERSION)
> + goto out;
> +
> + if (d->context[0]) {
> + context = kstrdup(d->context, GFP_KERNEL);
> + if (!context) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + }
> + goto build_flags;
> + } else
> goto out;
> - }
> }
>
> - if (strcmp(sb->s_type->name, "proc") == 0)
> - sbsec->proc = 1;
> + /* Standard string-based options. */
> + while ((p = strsep(&options, "|")) != NULL) {
> + int token;
> + substring_t args[MAX_OPT_ARGS];
>
> - sbsec->initialized = 1;
> + if (!*p)
> + continue;
>
> - if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
> - printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
> - sb->s_id, sb->s_type->name);
> - }
> - else {
> - printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
> - sb->s_id, sb->s_type->name,
> - labeling_behaviors[sbsec->behavior-1]);
> - }
> + token = match_token(p, tokens, args);
>
> - /* Initialize the root inode. */
> - rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
> + switch (token) {
> + case Opt_context:
> + if (context || defcontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + context = match_strdup(&args[0]);
> + if (!context) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + case Opt_fscontext:
> + if (fscontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + fscontext = match_strdup(&args[0]);
> + if (!fscontext) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + case Opt_rootcontext:
> + if (rootcontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + rootcontext = match_strdup(&args[0]);
> + if (!rootcontext) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + case Opt_defcontext:
> + if (context || defcontext) {
> + rc = -EINVAL;
> + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
> + goto out_err;
> + }
> + defcontext = match_strdup(&args[0]);
> + if (!defcontext) {
> + rc = -ENOMEM;
> + goto out_err;
> + }
> + break;
> +
> + default:
> + rc = -EINVAL;
> + printk(KERN_WARNING "SELinux: unknown mount option\n");
> + goto out_err;
>
> - /* Initialize any other inodes associated with the superblock, e.g.
> - inodes created prior to initial policy load or inodes created
> - during get_sb by a pseudo filesystem that directly
> - populates itself. */
> - spin_lock(&sbsec->isec_lock);
> -next_inode:
> - if (!list_empty(&sbsec->isec_head)) {
> - struct inode_security_struct *isec =
> - list_entry(sbsec->isec_head.next,
> - struct inode_security_struct, list);
> - struct inode *inode = isec->inode;
> - spin_unlock(&sbsec->isec_lock);
> - inode = igrab(inode);
> - if (inode) {
> - if (!IS_PRIVATE (inode))
> - inode_doinit(inode);
> - iput(inode);
> }
> - spin_lock(&sbsec->isec_lock);
> - list_del_init(&isec->list);
> - goto next_inode;
> }
> - spin_unlock(&sbsec->isec_lock);
> +
> +build_flags:
> + if (fscontext) {
> + mnt_opts[num_mnt_opts] = fscontext;
> + mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
> + }
> + if (context) {
> + mnt_opts[num_mnt_opts] = context;
> + mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
> + }
> + if (rootcontext) {
> + mnt_opts[num_mnt_opts] = rootcontext;
> + mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
> + }
> + if (defcontext) {
> + mnt_opts[num_mnt_opts] = defcontext;
> + mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
> + }
> +
> out:
> - mutex_unlock(&sbsec->lock);
> + rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts);
> +out_err:
> + kfree(context);
> + kfree(defcontext);
> + kfree(fscontext);
> + kfree(rootcontext);
> return rc;
> }
>
> @@ -4800,6 +5038,9 @@ static struct security_operations selinux_ops = {
> .sb_statfs = selinux_sb_statfs,
> .sb_mount = selinux_mount,
> .sb_umount = selinux_umount,
> + .sb_get_mnt_opts = selinux_get_mnt_opts,
> + .sb_set_mnt_opts = selinux_set_mnt_opts,
> + .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts,
>
> .inode_alloc_security = selinux_inode_alloc_security,
> .inode_free_security = selinux_inode_free_security,
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index 642a9fd..4138a80 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -65,6 +65,7 @@ struct superblock_security_struct {
> u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
> unsigned int behavior; /* labeling behavior */
> unsigned char initialized; /* initialization flag */
> + unsigned char flags; /* which mount options were specified */
> unsigned char proc; /* proc fs */
> struct mutex lock;
> struct list_head isec_head;
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2007-11-28 17:04 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-09 22:22 [PATCH -v3] SELinux: Add get, set, and cloning of superblock security information Eric Paris
2007-11-09 22:46 ` Casey Schaufler
2007-11-10 6:41 ` Eric Paris
2007-11-11 6:19 ` Casey Schaufler
2007-11-28 17:04 ` Eric Paris
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).