]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - security/selinux/hooks.c
splice: fix problem with atime not being updated
[linux-2.6-omap-h63xx.git] / security / selinux / hooks.c
index 3c3fff33d1ce9644ef2fdac0d00af18ca4742df4..64d414efb404e67d614d5279c8b44fac9431d1f4 100644 (file)
  *                          <dgoeddel@trustedcs.com>
  *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
  *                     Paul Moore, <paul.moore@hp.com>
+ *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
+ *                     Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
  *      as published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/ptrace.h>
 #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;
+extern struct security_operations *security_ops;
 
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing = 0;
@@ -319,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 = {
@@ -364,150 +368,317 @@ 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;
 
-       if (!seen)
+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 information.
+ */
+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 (!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;
 
-               rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+                       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(fscontext_sid, sbsec, tsec);
                if (rc)
-                       goto out_free;
+                       goto out;
 
-               sbsec->sid = sid;
+               sbsec->sid = fscontext_sid;
        }
 
        /*
@@ -515,182 +686,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;
 }
 
@@ -2295,6 +2534,25 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
        return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
 }
 
+static int selinux_inode_setotherxattr(struct dentry *dentry, char *name)
+{
+       if (!strncmp(name, XATTR_SECURITY_PREFIX,
+                    sizeof XATTR_SECURITY_PREFIX - 1)) {
+               if (!strcmp(name, XATTR_NAME_CAPS)) {
+                       if (!capable(CAP_SETFCAP))
+                               return -EPERM;
+               } else if (!capable(CAP_SYS_ADMIN)) {
+                       /* A different attribute in the security namespace.
+                          Restrict to administrator. */
+                       return -EPERM;
+               }
+       }
+
+       /* Not an attribute we recognize, so just check the
+          ordinary setattr permission. */
+       return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+}
+
 static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
 {
        struct task_security_struct *tsec = current->security;
@@ -2305,19 +2563,8 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
        u32 newsid;
        int rc = 0;
 
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        sbsec = inode->i_sb->s_security;
        if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
@@ -2391,31 +2638,14 @@ static int selinux_inode_listxattr (struct dentry *dentry)
 
 static int selinux_inode_removexattr (struct dentry *dentry, char *name)
 {
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. Might want a separate
-                  permission for removexattr. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        /* No one is allowed to remove a SELinux security label.
           You can change the label, but all data must be labeled. */
        return -EACCES;
 }
 
-static const char *selinux_inode_xattr_getsuffix(void)
-{
-      return XATTR_SELINUX_SUFFIX;
-}
-
 /*
  * Copy the in-core inode security context value to the user.  If the
  * getxattr() prior to this succeeded, check to see if we need to
@@ -2462,9 +2692,19 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
        return len;
 }
 
+static int selinux_inode_need_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_need_killpriv(dentry);
+}
+
+static int selinux_inode_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_killpriv(dentry);
+}
+
 /* file security operations */
 
-static int selinux_file_permission(struct file *file, int mask)
+static int selinux_revalidate_file_permission(struct file *file, int mask)
 {
        int rc;
        struct inode *inode = file->f_path.dentry->d_inode;
@@ -2486,6 +2726,25 @@ static int selinux_file_permission(struct file *file, int mask)
        return selinux_netlbl_inode_permission(inode, mask);
 }
 
+static int selinux_file_permission(struct file *file, int mask)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct task_security_struct *tsec = current->security;
+       struct file_security_struct *fsec = file->f_security;
+       struct inode_security_struct *isec = inode->i_security;
+
+       if (!mask) {
+               /* No permission to check.  Existence test. */
+               return 0;
+       }
+
+       if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+           && fsec->pseqno == avc_policy_seqno())
+               return selinux_netlbl_inode_permission(inode, mask);
+
+       return selinux_revalidate_file_permission(file, mask);
+}
+
 static int selinux_file_alloc_security(struct file *file)
 {
        return file_alloc_security(file);
@@ -2725,6 +2984,34 @@ static int selinux_file_receive(struct file *file)
        return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file)
+{
+       struct file_security_struct *fsec;
+       struct inode *inode;
+       struct inode_security_struct *isec;
+       inode = file->f_path.dentry->d_inode;
+       fsec = file->f_security;
+       isec = inode->i_security;
+       /*
+        * Save inode label and policy sequence number
+        * at open-time so that selinux_file_permission
+        * can determine whether revalidation is necessary.
+        * Task label is already saved in the file security
+        * struct as its SID.
+        */
+       fsec->isid = isec->sid;
+       fsec->pseqno = avc_policy_seqno();
+       /*
+        * Since the inode label or policy seqno may have changed
+        * between the selinux_inode_permission check and the saving
+        * of state above, recheck that access is still permitted.
+        * Otherwise, access might never be revalidated against the
+        * new inode label or new policy.
+        * This check is not redundant - do not remove.
+        */
+       return inode_has_perm(current, inode, file_to_av(file), NULL);
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)
@@ -2833,6 +3120,12 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
 
 static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
+       int rc;
+
+       rc = secondary_ops->task_setioprio(p, ioprio);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
@@ -2862,6 +3155,12 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
 
 static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
 {
+       int rc;
+
+       rc = secondary_ops->task_setscheduler(p, policy, lp);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
@@ -2915,11 +3214,7 @@ static int selinux_task_prctl(int option,
 
 static int selinux_task_wait(struct task_struct *p)
 {
-       u32 perm;
-
-       perm = signal_to_av(p->exit_signal);
-
-       return task_has_perm(p, current, perm);
+       return task_has_perm(p, current, PROCESS__SIGCHLD);
 }
 
 static void selinux_task_reparent_to_init(struct task_struct *p)
@@ -3932,7 +4227,7 @@ out:
 }
 
 static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
-                                              struct sk_buff **pskb,
+                                              struct sk_buff *skb,
                                               const struct net_device *in,
                                               const struct net_device *out,
                                               int (*okfn)(struct sk_buff *),
@@ -3941,7 +4236,6 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        char *addrp;
        int len, err = 0;
        struct sock *sk;
-       struct sk_buff *skb = *pskb;
        struct avc_audit_data ad;
        struct net_device *dev = (struct net_device *)out;
        struct sk_security_struct *sksec;
@@ -3977,23 +4271,23 @@ out:
 }
 
 static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
-                                               struct sk_buff **pskb,
+                                               struct sk_buff *skb,
                                                const struct net_device *in,
                                                const struct net_device *out,
                                                int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET);
+       return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET);
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 
 static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
-                                               struct sk_buff **pskb,
+                                               struct sk_buff *skb,
                                                const struct net_device *in,
                                                const struct net_device *out,
                                                int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6);
+       return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET6);
 }
 
 #endif /* IPV6 */
@@ -4488,19 +4782,6 @@ static int selinux_register_security (const char *name, struct security_operatio
        return 0;
 }
 
-static int selinux_unregister_security (const char *name, struct security_operations *ops)
-{
-       if (ops != secondary_ops) {
-               printk(KERN_ERR "%s:  trying to unregister a security module "
-                       "that is not registered.\n", __FUNCTION__);
-               return -EINVAL;
-       }
-
-       secondary_ops = original_ops;
-
-       return 0;
-}
-
 static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
 {
        if (inode)
@@ -4666,6 +4947,11 @@ static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
        return security_sid_to_context(secid, secdata, seclen);
 }
 
+static int selinux_secctx_to_secid(char *secdata, u32 seclen, u32 *secid)
+{
+       return security_context_to_sid(secdata, seclen, secid);
+}
+
 static void selinux_release_secctx(char *secdata, u32 seclen)
 {
        kfree(secdata);
@@ -4756,6 +5042,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,
@@ -4778,10 +5067,11 @@ static struct security_operations selinux_ops = {
        .inode_getxattr =               selinux_inode_getxattr,
        .inode_listxattr =              selinux_inode_listxattr,
        .inode_removexattr =            selinux_inode_removexattr,
-       .inode_xattr_getsuffix =        selinux_inode_xattr_getsuffix,
        .inode_getsecurity =            selinux_inode_getsecurity,
        .inode_setsecurity =            selinux_inode_setsecurity,
        .inode_listsecurity =           selinux_inode_listsecurity,
+       .inode_need_killpriv =          selinux_inode_need_killpriv,
+       .inode_killpriv =               selinux_inode_killpriv,
 
        .file_permission =              selinux_file_permission,
        .file_alloc_security =          selinux_file_alloc_security,
@@ -4795,6 +5085,8 @@ static struct security_operations selinux_ops = {
        .file_send_sigiotask =          selinux_file_send_sigiotask,
        .file_receive =                 selinux_file_receive,
 
+       .dentry_open =                  selinux_dentry_open,
+
        .task_create =                  selinux_task_create,
        .task_alloc_security =          selinux_task_alloc_security,
        .task_free_security =           selinux_task_free_security,
@@ -4844,7 +5136,6 @@ static struct security_operations selinux_ops = {
        .sem_semop =                    selinux_sem_semop,
 
        .register_security =            selinux_register_security,
-       .unregister_security =          selinux_unregister_security,
 
        .d_instantiate =                selinux_d_instantiate,
 
@@ -4852,6 +5143,7 @@ static struct security_operations selinux_ops = {
        .setprocattr =                  selinux_setprocattr,
 
        .secid_to_secctx =              selinux_secid_to_secctx,
+       .secctx_to_secid =              selinux_secctx_to_secid,
        .release_secctx =               selinux_release_secctx,
 
         .unix_stream_connect =         selinux_socket_unix_stream_connect,
@@ -4989,7 +5281,7 @@ static struct nf_hook_ops selinux_ipv4_op = {
        .hook =         selinux_ipv4_postroute_last,
        .owner =        THIS_MODULE,
        .pf =           PF_INET,
-       .hooknum =      NF_IP_POST_ROUTING,
+       .hooknum =      NF_INET_POST_ROUTING,
        .priority =     NF_IP_PRI_SELINUX_LAST,
 };
 
@@ -4999,7 +5291,7 @@ static struct nf_hook_ops selinux_ipv6_op = {
        .hook =         selinux_ipv6_postroute_last,
        .owner =        THIS_MODULE,
        .pf =           PF_INET6,
-       .hooknum =      NF_IP6_POST_ROUTING,
+       .hooknum =      NF_INET_POST_ROUTING,
        .priority =     NF_IP6_PRI_SELINUX_LAST,
 };