On Fri, 2007-11-09 at 08:29 -0800, Casey Schaufler wrote:
> --- Stephen Smalley <[EMAIL PROTECTED]> wrote:
> 
> > On Thu, 2007-11-08 at 16:37 -0500, 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 will allow
> > > filesystems to directly own and control all of their mount options if
> > > they so choose.
> > > 
> > > Signed-off-by: Eric Paris <[EMAIL PROTECTED]>
> > 
> > Acked-by:  Stephen D. Smalley <[EMAIL PROTECTED]>
> 
> Shouldn't this get cross posted to the LSM list?

Yes, I was reminded of that yesterday and will do so today.
>  
> > > 
> > > ---
> > > 
> > >  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;
> > > 
> > > 
> > > 
> > > --
> > > This message was distributed to subscribers of the selinux mailing list.
> > > If you no longer wish to subscribe, send mail to [EMAIL PROTECTED]
> > with
> > > the words "unsubscribe selinux" without quotes as the message.
> > -- 
> > Stephen Smalley
> > National Security Agency
> > 
> > 
> > --
> > This message was distributed to subscribers of the selinux mailing list.
> > If you no longer wish to subscribe, send mail to [EMAIL PROTECTED] with
> > the words "unsubscribe selinux" without quotes as the message.
> > 
> > 
> > 
> 
> 
> Casey Schaufler
> [EMAIL PROTECTED]

-
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to