On Wed, 2007-10-24 at 10:49 -0400, Stephen Smalley wrote: > On Fri, 2007-09-21 at 15:29 -0400, Eric Paris 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 is in > > preparation for filesystems (like NFS) to be able to own their own mount > > options. > > > > Signed-off-by: Eric Paris <eparis@xxxxxxxxxx> > > I think this should be queued up for -mm. > > Acked-by: Stephen Smalley <sds@xxxxxxxxxxxxx> I've got a new version which is fixed for -mm because of the changes to make lsm static. Will send in a couple hours. -Eric > > > > > --- > > > > I know this is really hard to review, if needed/wanted I'd be willing to > > make one patch that just rips all the guts of mounting FS out and then > > another patch which puts all this new stuff back.... > > > > include/linux/security.h | 72 ++++ > > security/dummy.c | 26 ++ > > security/selinux/hooks.c | 753 ++++++++++++++++++++++++------------- > > security/selinux/include/objsec.h | 1 + > > 4 files changed, 597 insertions(+), 255 deletions(-) > > > > diff --git a/include/linux/security.h b/include/linux/security.h > > index 1a15526..ed3d336 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 > > + > > struct ctl_table; > > > > /* > > @@ -248,6 +254,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. > > * > > @@ -1201,6 +1223,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); > > @@ -1594,6 +1623,26 @@ static inline void security_sb_post_pivotroot (struct nameidata *old_nd, > > security_ops->sb_post_pivotroot (old_nd, new_nd); > > } > > > > +static inline 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); > > +} > > + > > +static inline 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); > > +} > > + > > +static inline void security_sb_clone_mnt_opts(const struct super_block *oldsb, > > + struct super_block *newsb) > > +{ > > + security_ops->sb_clone_mnt_opts(oldsb, newsb); > > +} > > + > > static inline int security_inode_alloc (struct inode *inode) > > { > > inode->i_security = NULL; > > @@ -2334,6 +2383,29 @@ static inline void security_sb_post_pivotroot (struct nameidata *old_nd, > > struct nameidata *new_nd) > > { } > > > > +static inline int security_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 inline int security_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 inline void security_sb_clone_mnt_opts(const struct super_block *oldsb, > > + struct super_block *newsb) > > +{ } > > + > > static inline int security_inode_alloc (struct inode *inode) > > { > > return 0; > > diff --git a/security/dummy.c b/security/dummy.c > > index 853ec22..c3ac361 100644 > > --- a/security/dummy.c > > +++ b/security/dummy.c > > @@ -248,6 +248,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; > > @@ -996,6 +1019,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/selinux/hooks.c b/security/selinux/hooks.c > > index 0753b20..0a0e5a2 100644 > > --- a/security/selinux/hooks.c > > +++ b/security/selinux/hooks.c > > @@ -81,6 +81,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; > > @@ -319,8 +321,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 = { > > @@ -364,150 +366,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; > > + > > + 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(sid, sbsec, tsec); > > + rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec); > > if (rc) > > - goto out_free; > > + goto out; > > > > - sbsec->sid = sid; > > + sbsec->sid = fscontext_sid; > > } > > > > /* > > @@ -515,182 +685,252 @@ 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]) { > > + size_t len = strlen(d->context) + 1; > > + context = kmalloc(len, GFP_KERNEL); > > + if (!context) { > > + rc = -ENOMEM; > > + goto out; > > + } > > + strncpy(context, d->context, len); > > + context[len-1] = '\0'; > > + } > > + } else > > goto out; > > - } > > - } > > + } else { > > > > - 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); > > + } // else for binary mount data. delete with nfs patch > > + > > + 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; > > } > > > > @@ -4751,6 +4991,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 91b88f0..fc4768e 100644 > > --- a/security/selinux/include/objsec.h > > +++ b/security/selinux/include/objsec.h > > @@ -63,6 +63,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; > > > > > > > > -- > > This message was distributed to subscribers of the selinux mailing list. > > If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with > > the words "unsubscribe selinux" without quotes as the message. -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with the words "unsubscribe selinux" without quotes as the message.