[NETFILTER]: Introduce NF_INET_ hook values
[deliverable/linux.git] / security / selinux / hooks.c
index d8bc4172819c96bcefc49d69bf42f6619d6e4f04..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>
@@ -47,7 +48,7 @@
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
 #include <net/icmp.h>
-#include <net/ip.h>            /* for sysctl_local_port_range[] */
+#include <net/ip.h>            /* for local_port_range[] */
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
 #include <asm/uaccess.h>
 #include <asm/ioctls.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;
@@ -316,10 +320,11 @@ static inline int inode_doinit(struct inode *inode)
 }
 
 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 = {
@@ -327,6 +332,7 @@ static match_table_t tokens = {
        {Opt_fscontext, "fscontext=%s"},
        {Opt_defcontext, "defcontext=%s"},
        {Opt_rootcontext, "rootcontext=%s"},
+       {Opt_error, NULL},
 };
 
 #define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"
@@ -362,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;
+
+                       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;
 
-               rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+       /* 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;
        }
 
        /*
@@ -513,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;
 }
 
@@ -1907,6 +2148,9 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
                spin_unlock_irq(&current->sighand->siglock);
        }
 
+       /* Always clear parent death signal on SID transitions. */
+       current->pdeath_signal = 0;
+
        /* Check whether the new SID can inherit resource limits
           from the old SID.  If not, reset all soft limits to
           the lower of the current task's hard limit and the init
@@ -2290,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;
@@ -2300,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)
@@ -2386,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
@@ -2457,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;
@@ -2481,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);
@@ -2720,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)
@@ -2828,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);
 }
 
@@ -2857,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);
 }
 
@@ -2910,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)
@@ -3227,8 +3527,6 @@ static int selinux_socket_post_create(struct socket *sock, int family,
 /* Range of port numbers used to automatically bind.
    Need to determine whether we should perform a name_bind
    permission check between the socket and the port number. */
-#define ip_local_port_range_0 sysctl_local_port_range[0]
-#define ip_local_port_range_1 sysctl_local_port_range[1]
 
 static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
 {
@@ -3271,20 +3569,27 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                        addrp = (char *)&addr6->sin6_addr.s6_addr;
                }
 
-               if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
-                          snum > ip_local_port_range_1)) {
-                       err = security_port_sid(sk->sk_family, sk->sk_type,
-                                               sk->sk_protocol, snum, &sid);
-                       if (err)
-                               goto out;
-                       AVC_AUDIT_DATA_INIT(&ad,NET);
-                       ad.u.net.sport = htons(snum);
-                       ad.u.net.family = family;
-                       err = avc_has_perm(isec->sid, sid,
-                                          isec->sclass,
-                                          SOCKET__NAME_BIND, &ad);
-                       if (err)
-                               goto out;
+               if (snum) {
+                       int low, high;
+
+                       inet_get_local_port_range(&low, &high);
+
+                       if (snum < max(PROT_SOCK, low) || snum > high) {
+                               err = security_port_sid(sk->sk_family,
+                                                       sk->sk_type,
+                                                       sk->sk_protocol, snum,
+                                                       &sid);
+                               if (err)
+                                       goto out;
+                               AVC_AUDIT_DATA_INIT(&ad,NET);
+                               ad.u.net.sport = htons(snum);
+                               ad.u.net.family = family;
+                               err = avc_has_perm(isec->sid, sid,
+                                                  isec->sclass,
+                                                  SOCKET__NAME_BIND, &ad);
+                               if (err)
+                                       goto out;
+                       }
                }
                
                switch(isec->sclass) {
@@ -3922,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 *),
@@ -3931,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;
@@ -3967,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 */
@@ -4478,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)
@@ -4656,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);
@@ -4746,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,
@@ -4768,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,
@@ -4785,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,
@@ -4834,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,
 
@@ -4842,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,
@@ -4979,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,
 };
 
@@ -4989,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,
 };
 
This page took 0.041151 seconds and 5 git commands to generate.