summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig1
-rw-r--r--security/commoncap.c26
-rw-r--r--security/dummy.c38
-rw-r--r--security/inode.c11
-rw-r--r--security/keys/proc.c4
-rw-r--r--security/security.c31
-rw-r--r--security/selinux/Kconfig2
-rw-r--r--security/selinux/Makefile9
-rw-r--r--security/selinux/avc.c15
-rw-r--r--security/selinux/exports.c20
-rw-r--r--security/selinux/hooks.c1417
-rw-r--r--security/selinux/include/av_perm_to_string.h9
-rw-r--r--security/selinux/include/av_permissions.h9
-rw-r--r--security/selinux/include/avc.h2
-rw-r--r--security/selinux/include/class_to_string.h7
-rw-r--r--security/selinux/include/flask.h1
-rw-r--r--security/selinux/include/netif.h4
-rw-r--r--security/selinux/include/netlabel.h11
-rw-r--r--security/selinux/include/netnode.h32
-rw-r--r--security/selinux/include/objsec.h17
-rw-r--r--security/selinux/include/security.h24
-rw-r--r--security/selinux/include/xfrm.h12
-rw-r--r--security/selinux/netif.c263
-rw-r--r--security/selinux/netlabel.c82
-rw-r--r--security/selinux/netnode.c354
-rw-r--r--security/selinux/selinuxfs.c173
-rw-r--r--security/selinux/ss/avtab.c34
-rw-r--r--security/selinux/ss/avtab.h5
-rw-r--r--security/selinux/ss/conditional.c3
-rw-r--r--security/selinux/ss/ebitmap.c2
-rw-r--r--security/selinux/ss/mls.c87
-rw-r--r--security/selinux/ss/mls.h2
-rw-r--r--security/selinux/ss/policydb.c63
-rw-r--r--security/selinux/ss/policydb.h5
-rw-r--r--security/selinux/ss/services.c302
-rw-r--r--security/selinux/xfrm.c31
36 files changed, 2225 insertions, 883 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 8086e61..389e151 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -76,6 +76,7 @@ config SECURITY_NETWORK_XFRM
config SECURITY_CAPABILITIES
bool "Default Linux Capabilities"
depends on SECURITY
+ default y
help
This enables the "default" Linux capabilities functionality.
If you are unsure how to answer this question, answer Y.
diff --git a/security/commoncap.c b/security/commoncap.c
index bf67871..ea61bc7 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -59,6 +59,12 @@ int cap_netlink_recv(struct sk_buff *skb, int cap)
EXPORT_SYMBOL(cap_netlink_recv);
+/*
+ * NOTE WELL: cap_capable() cannot be used like the kernel's capable()
+ * function. That is, it has the reverse semantics: cap_capable()
+ * returns 0 when a task has a capability, but the kernel's capable()
+ * returns 1 for this case.
+ */
int cap_capable (struct task_struct *tsk, int cap)
{
/* Derived from include/linux/sched.h:capable. */
@@ -107,10 +113,11 @@ static inline int cap_block_setpcap(struct task_struct *target)
static inline int cap_inh_is_capped(void)
{
/*
- * return 1 if changes to the inheritable set are limited
- * to the old permitted set.
+ * Return 1 if changes to the inheritable set are limited
+ * to the old permitted set. That is, if the current task
+ * does *not* possess the CAP_SETPCAP capability.
*/
- return !cap_capable(current, CAP_SETPCAP);
+ return (cap_capable(current, CAP_SETPCAP) != 0);
}
#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
@@ -526,6 +533,19 @@ int cap_task_kill(struct task_struct *p, struct siginfo *info,
if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)))
return 0;
+ /*
+ * Running a setuid root program raises your capabilities.
+ * Killing your own setuid root processes was previously
+ * allowed.
+ * We must preserve legacy signal behavior in this case.
+ */
+ if (p->euid == 0 && p->uid == current->uid)
+ return 0;
+
+ /* sigcont is permitted within same session */
+ if (sig == SIGCONT && (task_session_nr(current) == task_session_nr(p)))
+ return 0;
+
if (secid)
/*
* Signal sent as a particular user.
diff --git a/security/dummy.c b/security/dummy.c
index 6d895ad..48d4b0a 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -225,22 +225,40 @@ static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags,
}
-static void dummy_sb_post_mountroot (void)
+static void dummy_sb_post_addmount (struct vfsmount *mnt, struct nameidata *nd)
{
return;
}
-static void dummy_sb_post_addmount (struct vfsmount *mnt, struct nameidata *nd)
+static int dummy_sb_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
+{
+ return 0;
+}
+
+static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
{
return;
}
-static int dummy_sb_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
+static int dummy_sb_get_mnt_opts(const struct super_block *sb, char ***mount_options,
+ int **flags, int *num_opts)
{
+ *mount_options = NULL;
+ *flags = NULL;
+ *num_opts = 0;
return 0;
}
-static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
+static int dummy_sb_set_mnt_opts(struct super_block *sb, char **mount_options,
+ int *flags, int num_opts)
+{
+ if (unlikely(num_opts))
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static void dummy_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
{
return;
}
@@ -426,7 +444,7 @@ static int dummy_file_mmap (struct file *file, unsigned long reqprot,
unsigned long addr,
unsigned long addr_only)
{
- if (addr < mmap_min_addr)
+ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
return -EACCES;
return 0;
}
@@ -928,6 +946,11 @@ static int dummy_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
return -EOPNOTSUPP;
}
+static int dummy_secctx_to_secid(char *secdata, u32 seclen, u32 *secid)
+{
+ return -EOPNOTSUPP;
+}
+
static void dummy_release_secctx(char *secdata, u32 seclen)
{
}
@@ -994,10 +1017,12 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, sb_umount_close);
set_to_dummy_if_null(ops, sb_umount_busy);
set_to_dummy_if_null(ops, sb_post_remount);
- set_to_dummy_if_null(ops, sb_post_mountroot);
set_to_dummy_if_null(ops, sb_post_addmount);
set_to_dummy_if_null(ops, sb_pivotroot);
set_to_dummy_if_null(ops, sb_post_pivotroot);
+ set_to_dummy_if_null(ops, sb_get_mnt_opts);
+ set_to_dummy_if_null(ops, sb_set_mnt_opts);
+ set_to_dummy_if_null(ops, sb_clone_mnt_opts);
set_to_dummy_if_null(ops, inode_alloc_security);
set_to_dummy_if_null(ops, inode_free_security);
set_to_dummy_if_null(ops, inode_init_security);
@@ -1086,6 +1111,7 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, getprocattr);
set_to_dummy_if_null(ops, setprocattr);
set_to_dummy_if_null(ops, secid_to_secctx);
+ set_to_dummy_if_null(ops, secctx_to_secid);
set_to_dummy_if_null(ops, release_secctx);
#ifdef CONFIG_SECURITY_NETWORK
set_to_dummy_if_null(ops, unix_stream_connect);
diff --git a/security/inode.c b/security/inode.c
index b28a8ac..acc6cf0 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -315,20 +315,19 @@ void securityfs_remove(struct dentry *dentry)
}
EXPORT_SYMBOL_GPL(securityfs_remove);
-static decl_subsys(security, NULL, NULL);
+static struct kobject *security_kobj;
static int __init securityfs_init(void)
{
int retval;
- kobj_set_kset_s(&security_subsys, kernel_subsys);
- retval = subsystem_register(&security_subsys);
- if (retval)
- return retval;
+ security_kobj = kobject_create_and_add("security", kernel_kobj);
+ if (!security_kobj)
+ return -EINVAL;
retval = register_filesystem(&fs_type);
if (retval)
- subsystem_unregister(&security_subsys);
+ kobject_put(security_kobj);
return retval;
}
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 3e0d0a6..6941260 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -26,7 +26,7 @@ static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
static void proc_keys_stop(struct seq_file *p, void *v);
static int proc_keys_show(struct seq_file *m, void *v);
-static struct seq_operations proc_keys_ops = {
+static const struct seq_operations proc_keys_ops = {
.start = proc_keys_start,
.next = proc_keys_next,
.stop = proc_keys_stop,
@@ -47,7 +47,7 @@ static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos);
static void proc_key_users_stop(struct seq_file *p, void *v);
static int proc_key_users_show(struct seq_file *m, void *v);
-static struct seq_operations proc_key_users_ops = {
+static const struct seq_operations proc_key_users_ops = {
.start = proc_key_users_start,
.next = proc_key_users_next,
.stop = proc_key_users_stop,
diff --git a/security/security.c b/security/security.c
index 0e1f1f1..ca475ca 100644
--- a/security/security.c
+++ b/security/security.c
@@ -288,11 +288,6 @@ void security_sb_post_remount(struct vfsmount *mnt, unsigned long flags, void *d
security_ops->sb_post_remount(mnt, flags, data);
}
-void security_sb_post_mountroot(void)
-{
- security_ops->sb_post_mountroot();
-}
-
void security_sb_post_addmount(struct vfsmount *mnt, struct nameidata *mountpoint_nd)
{
security_ops->sb_post_addmount(mnt, mountpoint_nd);
@@ -308,6 +303,26 @@ void security_sb_post_pivotroot(struct nameidata *old_nd, struct nameidata *new_
security_ops->sb_post_pivotroot(old_nd, new_nd);
}
+int security_sb_get_mnt_opts(const struct super_block *sb,
+ char ***mount_options,
+ int **flags, int *num_opts)
+{
+ return security_ops->sb_get_mnt_opts(sb, mount_options, flags, num_opts);
+}
+
+int security_sb_set_mnt_opts(struct super_block *sb,
+ char **mount_options,
+ int *flags, int num_opts)
+{
+ return security_ops->sb_set_mnt_opts(sb, mount_options, flags, num_opts);
+}
+
+void security_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
+{
+ security_ops->sb_clone_mnt_opts(oldsb, newsb);
+}
+
int security_inode_alloc(struct inode *inode)
{
inode->i_security = NULL;
@@ -816,6 +831,12 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
}
EXPORT_SYMBOL(security_secid_to_secctx);
+int security_secctx_to_secid(char *secdata, u32 seclen, u32 *secid)
+{
+ return security_ops->secctx_to_secid(secdata, seclen, secid);
+}
+EXPORT_SYMBOL(security_secctx_to_secid);
+
void security_release_secctx(char *secdata, u32 seclen)
{
return security_ops->release_secctx(secdata, seclen);
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index b32a459..2b517d6 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -145,7 +145,7 @@ config SECURITY_SELINUX_POLICYDB_VERSION_MAX
config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
int "NSA SELinux maximum supported policy format version value"
depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX
- range 15 21
+ range 15 22
default 19
help
This option sets the value for the maximum policy format version
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index dc3502e..00afd85 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -4,7 +4,14 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
-selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o
+selinux-y := avc.o \
+ hooks.o \
+ selinuxfs.o \
+ netlink.o \
+ nlmsgtab.o \
+ netif.o \
+ netnode.o \
+ exports.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 81b3dff..e8529e2 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -661,9 +661,18 @@ void avc_audit(u32 ssid, u32 tsid,
"daddr", "dest");
break;
}
- if (a->u.net.netif)
- audit_log_format(ab, " netif=%s",
- a->u.net.netif);
+ if (a->u.net.netif > 0) {
+ struct net_device *dev;
+
+ /* NOTE: we always use init's namespace */
+ dev = dev_get_by_index(&init_net,
+ a->u.net.netif);
+ if (dev) {
+ audit_log_format(ab, " netif=%s",
+ dev->name);
+ dev_put(dev);
+ }
+ }
break;
}
}
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
index b6f9694..87d2bb3 100644
--- a/security/selinux/exports.c
+++ b/security/selinux/exports.c
@@ -17,10 +17,14 @@
#include <linux/selinux.h>
#include <linux/fs.h>
#include <linux/ipc.h>
+#include <asm/atomic.h>
#include "security.h"
#include "objsec.h"
+/* SECMARK reference count */
+extern atomic_t selinux_secmark_refcount;
+
int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen)
{
if (selinux_enabled)
@@ -74,7 +78,7 @@ int selinux_string_to_sid(char *str, u32 *sid)
}
EXPORT_SYMBOL_GPL(selinux_string_to_sid);
-int selinux_relabel_packet_permission(u32 sid)
+int selinux_secmark_relabel_packet_permission(u32 sid)
{
if (selinux_enabled) {
struct task_security_struct *tsec = current->security;
@@ -84,4 +88,16 @@ int selinux_relabel_packet_permission(u32 sid)
}
return 0;
}
-EXPORT_SYMBOL_GPL(selinux_relabel_packet_permission);
+EXPORT_SYMBOL_GPL(selinux_secmark_relabel_packet_permission);
+
+void selinux_secmark_refcount_inc(void)
+{
+ atomic_inc(&selinux_secmark_refcount);
+}
+EXPORT_SYMBOL_GPL(selinux_secmark_refcount_inc);
+
+void selinux_secmark_refcount_dec(void)
+{
+ atomic_dec(&selinux_secmark_refcount);
+}
+EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9f3124b..be6de0b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -12,8 +12,8 @@
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* <dgoeddel@trustedcs.com>
- * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
- * Paul Moore, <paul.moore@hp.com>
+ * Copyright (C) 2006, 2007 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>
*
@@ -50,8 +50,11 @@
#include <net/icmp.h>
#include <net/ip.h> /* for local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
+#include <net/net_namespace.h>
+#include <net/netlabel.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
+#include <asm/atomic.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h> /* for network interface checks */
@@ -76,17 +79,23 @@
#include "avc.h"
#include "objsec.h"
#include "netif.h"
+#include "netnode.h"
#include "xfrm.h"
#include "netlabel.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;
+/* SECMARK reference count */
+atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
+
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
@@ -153,6 +162,21 @@ getsecurity_exit:
return len;
}
+/**
+ * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
+ *
+ * Description:
+ * This function checks the SECMARK reference counter to see if any SECMARK
+ * targets are currently configured, if the reference counter is greater than
+ * zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is
+ * enabled, false (0) if SECMARK is disabled.
+ *
+ */
+static int selinux_secmark_enabled(void)
+{
+ return (atomic_read(&selinux_secmark_refcount) > 0);
+}
+
/* Allocate and free functions for each kind of security blob. */
static int task_alloc_security(struct task_struct *task)
@@ -321,8 +345,8 @@ enum {
Opt_error = -1,
Opt_context = 1,
Opt_fscontext = 2,
- Opt_defcontext = 4,
- Opt_rootcontext = 8,
+ Opt_defcontext = 3,
+ Opt_rootcontext = 4,
};
static match_table_t tokens = {
@@ -366,150 +390,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;
+
+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;
+}
- if (!seen)
+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.
+ */
+static 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;
- rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+ if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
+ context_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= CONTEXT_MNT;
+ break;
+ case ROOTCONTEXT_MNT:
+ rootcontext_sid = sid;
+
+ if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
+ rootcontext_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= ROOTCONTEXT_MNT;
+
+ break;
+ case DEFCONTEXT_MNT:
+ defcontext_sid = sid;
+
+ if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
+ defcontext_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= DEFCONTEXT_MNT;
+
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (sbsec->initialized) {
+ /* previously mounted with options, but not on this attempt? */
+ if (sbsec->flags && !num_opts)
+ goto out_double_mount;
+ rc = 0;
+ goto out;
+ }
+
+ if (strcmp(sb->s_type->name, "proc") == 0)
+ sbsec->proc = 1;
+
+ /* Determine the labeling behavior to use for this filesystem type. */
+ rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ if (rc) {
+ printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
+ __FUNCTION__, sb->s_type->name, rc);
+ goto out;
+ }
+
+ /* sets the context of the superblock for the fs being mounted. */
+ if (fscontext_sid) {
+
+ rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
if (rc)
- goto out_free;
+ goto out;
- sbsec->sid = sid;
+ sbsec->sid = fscontext_sid;
}
/*
@@ -517,182 +708,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;
}
@@ -3158,7 +3417,7 @@ out:
#endif /* IPV6 */
static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
- char **addrp, int *len, int src, u8 *proto)
+ char **addrp, int src, u8 *proto)
{
int ret = 0;
@@ -3167,7 +3426,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
ret = selinux_parse_skb_ipv4(skb, ad, proto);
if (ret || !addrp)
break;
- *len = 4;
*addrp = (char *)(src ? &ad->u.net.v4info.saddr :
&ad->u.net.v4info.daddr);
break;
@@ -3177,7 +3435,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
ret = selinux_parse_skb_ipv6(skb, ad, proto);
if (ret || !addrp)
break;
- *len = 16;
*addrp = (char *)(src ? &ad->u.net.v6info.saddr :
&ad->u.net.v6info.daddr);
break;
@@ -3186,36 +3443,48 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
break;
}
+ if (unlikely(ret))
+ printk(KERN_WARNING
+ "SELinux: failure in selinux_parse_skb(),"
+ " unable to parse packet\n");
+
return ret;
}
/**
- * selinux_skb_extlbl_sid - Determine the external label of a packet
+ * selinux_skb_peerlbl_sid - Determine the peer label of a packet
* @skb: the packet
- * @sid: the packet's SID
+ * @family: protocol family
+ * @sid: the packet's peer label SID
*
* Description:
- * Check the various different forms of external packet labeling and determine
- * the external SID for the packet. If only one form of external labeling is
- * present then it is used, if both labeled IPsec and NetLabel labels are
- * present then the SELinux type information is taken from the labeled IPsec
- * SA and the MLS sensitivity label information is taken from the NetLabel
- * security attributes. This bit of "magic" is done in the call to
- * selinux_netlbl_skbuff_getsid().
+ * Check the various different forms of network peer labeling and determine
+ * the peer label/SID for the packet; most of the magic actually occurs in
+ * the security server function security_net_peersid_cmp(). The function
+ * returns zero if the value in @sid is valid (although it may be SECSID_NULL)
+ * or -EACCES if @sid is invalid due to inconsistencies with the different
+ * peer labels.
*
*/
-static void selinux_skb_extlbl_sid(struct sk_buff *skb, u32 *sid)
+static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
{
+ int err;
u32 xfrm_sid;
u32 nlbl_sid;
+ u32 nlbl_type;
selinux_skb_xfrm_sid(skb, &xfrm_sid);
- if (selinux_netlbl_skbuff_getsid(skb,
- (xfrm_sid == SECSID_NULL ?
- SECINITSID_NETMSG : xfrm_sid),
- &nlbl_sid) != 0)
- nlbl_sid = SECSID_NULL;
- *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
+ selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
+
+ err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
+ if (unlikely(err)) {
+ printk(KERN_WARNING
+ "SELinux: failure in selinux_skb_peerlbl_sid(),"
+ " unable to determine packet's peer label\n");
+ return -EACCES;
+ }
+
+ return 0;
}
/* socket security operations */
@@ -3281,6 +3550,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
if (sock->sk) {
sksec = sock->sk->sk_security;
sksec->sid = isec->sid;
+ sksec->sclass = isec->sclass;
err = selinux_netlbl_socket_post_create(sock);
}
@@ -3373,7 +3643,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
break;
}
- err = security_node_sid(family, addrp, addrlen, &sid);
+ err = sel_netnode_sid(addrp, family, &sid);
if (err)
goto out;
@@ -3584,131 +3854,182 @@ static int selinux_socket_unix_may_send(struct socket *sock,
return 0;
}
-static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
- struct avc_audit_data *ad, u16 family, char *addrp, int len)
+static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
+ u32 peer_sid,
+ struct avc_audit_data *ad)
{
- int err = 0;
- u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
- struct socket *sock;
- u16 sock_class = 0;
- u32 sock_sid = 0;
-
- read_lock_bh(&sk->sk_callback_lock);
- sock = sk->sk_socket;
- if (sock) {
- struct inode *inode;
- inode = SOCK_INODE(sock);
- if (inode) {
- struct inode_security_struct *isec;
- isec = inode->i_security;
- sock_sid = isec->sid;
- sock_class = isec->sclass;
- }
- }
- read_unlock_bh(&sk->sk_callback_lock);
- if (!sock_sid)
- goto out;
+ int err;
+ u32 if_sid;
+ u32 node_sid;
- if (!skb->dev)
- goto out;
+ err = sel_netif_sid(ifindex, &if_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(peer_sid, if_sid,
+ SECCLASS_NETIF, NETIF__INGRESS, ad);
+ if (err)
+ return err;
- err = sel_netif_sids(skb->dev, &if_sid, NULL);
+ err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
- goto out;
+ return err;
+ return avc_has_perm(peer_sid, node_sid,
+ SECCLASS_NODE, NODE__RECVFROM, ad);
+}
- switch (sock_class) {
+static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
+ struct sk_buff *skb,
+ struct avc_audit_data *ad,
+ u16 family,
+ char *addrp)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u16 sk_class;
+ u32 netif_perm, node_perm, recv_perm;
+ u32 port_sid, node_sid, if_sid, sk_sid;
+
+ sk_sid = sksec->sid;
+ sk_class = sksec->sclass;
+
+ switch (sk_class) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_RECV;
node_perm = NODE__UDP_RECV;
recv_perm = UDP_SOCKET__RECV_MSG;
break;
-
case SECCLASS_TCP_SOCKET:
netif_perm = NETIF__TCP_RECV;
node_perm = NODE__TCP_RECV;
recv_perm = TCP_SOCKET__RECV_MSG;
break;
-
case SECCLASS_DCCP_SOCKET:
netif_perm = NETIF__DCCP_RECV;
node_perm = NODE__DCCP_RECV;
recv_perm = DCCP_SOCKET__RECV_MSG;
break;
-
default:
netif_perm = NETIF__RAWIP_RECV;
node_perm = NODE__RAWIP_RECV;
+ recv_perm = 0;
break;
}
- err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ err = sel_netif_sid(skb->iif, &if_sid);
if (err)
- goto out;
-
- err = security_node_sid(family, addrp, len, &node_sid);
+ return err;
+ err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
if (err)
- goto out;
+ return err;
- err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
- goto out;
+ return err;
+ err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ if (err)
+ return err;
- if (recv_perm) {
- u32 port_sid;
+ if (!recv_perm)
+ return 0;
+ err = security_port_sid(sk->sk_family, sk->sk_type,
+ sk->sk_protocol, ntohs(ad->u.net.sport),
+ &port_sid);
+ if (unlikely(err)) {
+ printk(KERN_WARNING
+ "SELinux: failure in"
+ " selinux_sock_rcv_skb_iptables_compat(),"
+ " network port label not found\n");
+ return err;
+ }
+ return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad);
+}
- err = security_port_sid(sk->sk_family, sk->sk_type,
- sk->sk_protocol, ntohs(ad->u.net.sport),
- &port_sid);
- if (err)
- goto out;
+static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
+ struct avc_audit_data *ad,
+ u16 family, char *addrp)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u32 peer_sid;
+ u32 sk_sid = sksec->sid;
- err = avc_has_perm(sock_sid, port_sid,
- sock_class, recv_perm, ad);
+ if (selinux_compat_net)
+ err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
+ family, addrp);
+ else
+ err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__RECV, ad);
+ if (err)
+ return err;
+
+ if (selinux_policycap_netpeer) {
+ err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, peer_sid,
+ SECCLASS_PEER, PEER__RECV, ad);
+ } else {
+ err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
+ if (err)
+ return err;
+ err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
}
-out:
return err;
}
static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- u16 family;
- char *addrp;
- int len, err = 0;
- struct avc_audit_data ad;
+ int err;
struct sk_security_struct *sksec = sk->sk_security;
+ u16 family = sk->sk_family;
+ u32 sk_sid = sksec->sid;
+ struct avc_audit_data ad;
+ char *addrp;
- family = sk->sk_family;
if (family != PF_INET && family != PF_INET6)
- goto out;
+ return 0;
/* Handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
+ ad.u.net.netif = skb->iif;
ad.u.net.family = family;
-
- err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL);
+ err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
if (err)
- goto out;
+ return err;
- if (selinux_compat_net)
- err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family,
- addrp, len);
- else
- err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
- PACKET__RECV, &ad);
- if (err)
- goto out;
+ /* If any sort of compatibility mode is enabled then handoff processing
+ * to the selinux_sock_rcv_skb_compat() function to deal with the
+ * special handling. We do this in an attempt to keep this function
+ * as fast and as clean as possible. */
+ if (selinux_compat_net || !selinux_policycap_netpeer)
+ return selinux_sock_rcv_skb_compat(sk, skb, &ad,
+ family, addrp);
- err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad);
- if (err)
- goto out;
+ if (netlbl_enabled() || selinux_xfrm_enabled()) {
+ u32 peer_sid;
+
+ err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
+ if (err)
+ return err;
+ err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
+ peer_sid, &ad);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
+ PEER__RECV, &ad);
+ }
+
+ if (selinux_secmark_enabled()) {
+ err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__RECV, &ad);
+ if (err)
+ return err;
+ }
- err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
-out:
return err;
}
@@ -3759,18 +4080,25 @@ out:
static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
{
u32 peer_secid = SECSID_NULL;
- int err = 0;
+ u16 family;
- if (sock && sock->sk->sk_family == PF_UNIX)
+ if (sock)
+ family = sock->sk->sk_family;
+ else if (skb && skb->sk)
+ family = skb->sk->sk_family;
+ else
+ goto out;
+
+ if (sock && family == PF_UNIX)
selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
else if (skb)
- selinux_skb_extlbl_sid(skb, &peer_secid);
+ selinux_skb_peerlbl_sid(skb, family, &peer_secid);
- if (peer_secid == SECSID_NULL)
- err = -EINVAL;
+out:
*secid = peer_secid;
-
- return err;
+ if (peer_secid == SECSID_NULL)
+ return -EINVAL;
+ return 0;
}
static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
@@ -3790,6 +4118,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
newssec->sid = ssec->sid;
newssec->peer_sid = ssec->peer_sid;
+ newssec->sclass = ssec->sclass;
selinux_netlbl_sk_security_clone(ssec, newssec);
}
@@ -3813,6 +4142,7 @@ static void selinux_sock_graft(struct sock* sk, struct socket *parent)
if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
sk->sk_family == PF_UNIX)
isec->sid = sksec->sid;
+ sksec->sclass = isec->sclass;
selinux_netlbl_sock_graft(sk, parent);
}
@@ -3825,7 +4155,9 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
u32 newsid;
u32 peersid;
- selinux_skb_extlbl_sid(skb, &peersid);
+ err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
+ if (err)
+ return err;
if (peersid == SECSID_NULL) {
req->secid = sksec->sid;
req->peer_secid = SECSID_NULL;
@@ -3863,7 +4195,7 @@ static void selinux_inet_conn_established(struct sock *sk,
{
struct sk_security_struct *sksec = sk->sk_security;
- selinux_skb_extlbl_sid(skb, &sksec->peer_sid);
+ selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
}
static void selinux_req_classify_flow(const struct request_sock *req,
@@ -3910,149 +4242,260 @@ out:
#ifdef CONFIG_NETFILTER
-static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
- struct avc_audit_data *ad,
- u16 family, char *addrp, int len)
+static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
+ u16 family)
{
- int err = 0;
- u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
- struct socket *sock;
- struct inode *inode;
- struct inode_security_struct *isec;
+ char *addrp;
+ u32 peer_sid;
+ struct avc_audit_data ad;
+ u8 secmark_active;
+ u8 peerlbl_active;
- sock = sk->sk_socket;
- if (!sock)
- goto out;
+ if (!selinux_policycap_netpeer)
+ return NF_ACCEPT;
- inode = SOCK_INODE(sock);
- if (!inode)
- goto out;
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return NF_ACCEPT;
- isec = inode->i_security;
-
- err = sel_netif_sids(dev, &if_sid, NULL);
- if (err)
- goto out;
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
+ return NF_DROP;
+
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+ return NF_DROP;
+
+ if (peerlbl_active)
+ if (selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+ peer_sid, &ad) != 0)
+ return NF_DROP;
+
+ if (secmark_active)
+ if (avc_has_perm(peer_sid, skb->secmark,
+ SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_forward(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_forward(skb, in->ifindex, PF_INET);
+}
- switch (isec->sclass) {
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static unsigned int selinux_ipv6_forward(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_forward(skb, in->ifindex, PF_INET6);
+}
+#endif /* IPV6 */
+
+static int selinux_ip_postroute_iptables_compat(struct sock *sk,
+ int ifindex,
+ struct avc_audit_data *ad,
+ u16 family, char *addrp)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u16 sk_class;
+ u32 netif_perm, node_perm, send_perm;
+ u32 port_sid, node_sid, if_sid, sk_sid;
+
+ sk_sid = sksec->sid;
+ sk_class = sksec->sclass;
+
+ switch (sk_class) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_SEND;
node_perm = NODE__UDP_SEND;
send_perm = UDP_SOCKET__SEND_MSG;
break;
-
case SECCLASS_TCP_SOCKET:
netif_perm = NETIF__TCP_SEND;
node_perm = NODE__TCP_SEND;
send_perm = TCP_SOCKET__SEND_MSG;
break;
-
case SECCLASS_DCCP_SOCKET:
netif_perm = NETIF__DCCP_SEND;
node_perm = NODE__DCCP_SEND;
send_perm = DCCP_SOCKET__SEND_MSG;
break;
-
default:
netif_perm = NETIF__RAWIP_SEND;
node_perm = NODE__RAWIP_SEND;
+ send_perm = 0;
break;
}
- err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ err = sel_netif_sid(ifindex, &if_sid);
if (err)
- goto out;
+ return err;
+ err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ return err;
- err = security_node_sid(family, addrp, len, &node_sid);
+ err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
- goto out;
-
- err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ return err;
+ err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
if (err)
- goto out;
+ return err;
- if (send_perm) {
- u32 port_sid;
-
- err = security_port_sid(sk->sk_family,
- sk->sk_type,
- sk->sk_protocol,
- ntohs(ad->u.net.dport),
- &port_sid);
- if (err)
- goto out;
+ if (send_perm != 0)
+ return 0;
- err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- send_perm, ad);
+ err = security_port_sid(sk->sk_family, sk->sk_type,
+ sk->sk_protocol, ntohs(ad->u.net.dport),
+ &port_sid);
+ if (unlikely(err)) {
+ printk(KERN_WARNING
+ "SELinux: failure in"
+ " selinux_ip_postroute_iptables_compat(),"
+ " network port label not found\n");
+ return err;
}
-out:
- return err;
+ return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad);
}
-static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
- struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *),
- u16 family)
+static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
+ int ifindex,
+ struct avc_audit_data *ad,
+ u16 family,
+ char *addrp,
+ u8 proto)
{
- char *addrp;
- int len, err = 0;
- struct sock *sk;
- struct avc_audit_data ad;
- struct net_device *dev = (struct net_device *)out;
+ struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
- u8 proto;
-
- sk = skb->sk;
- if (!sk)
- goto out;
+ if (sk == NULL)
+ return NF_ACCEPT;
sksec = sk->sk_security;
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
+ if (selinux_compat_net) {
+ if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
+ ad, family, addrp))
+ return NF_DROP;
+ } else {
+ if (avc_has_perm(sksec->sid, skb->secmark,
+ SECCLASS_PACKET, PACKET__SEND, ad))
+ return NF_DROP;
+ }
- err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto);
- if (err)
- goto out;
+ if (selinux_policycap_netpeer)
+ if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
+ return NF_DROP;
- if (selinux_compat_net)
- err = selinux_ip_postroute_last_compat(sk, dev, &ad,
- family, addrp, len);
- else
- err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
- PACKET__SEND, &ad);
+ return NF_ACCEPT;
+}
- if (err)
- goto out;
+static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
+ u16 family)
+{
+ u32 secmark_perm;
+ u32 peer_sid;
+ struct sock *sk;
+ struct avc_audit_data ad;
+ char *addrp;
+ u8 proto;
+ u8 secmark_active;
+ u8 peerlbl_active;
- err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto);
-out:
- return err ? NF_DROP : NF_ACCEPT;
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
+ return NF_DROP;
+
+ /* If any sort of compatibility mode is enabled then handoff processing
+ * to the selinux_ip_postroute_compat() function to deal with the
+ * special handling. We do this in an attempt to keep this function
+ * as fast and as clean as possible. */
+ if (selinux_compat_net || !selinux_policycap_netpeer)
+ return selinux_ip_postroute_compat(skb, ifindex, &ad,
+ family, addrp, proto);
+
+ /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
+ * packet transformation so allow the packet to pass without any checks
+ * since we'll have another chance to perform access control checks
+ * when the packet is on it's final way out.
+ * NOTE: there appear to be some IPv6 multicast cases where skb->dst
+ * is NULL, in this case go ahead and apply access control. */
+ if (skb->dst != NULL && skb->dst->xfrm != NULL)
+ return NF_ACCEPT;
+
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return NF_ACCEPT;
+
+ /* if the packet is locally generated (skb->sk != NULL) then use the
+ * socket's label as the peer label, otherwise the packet is being
+ * forwarded through this system and we need to fetch the peer label
+ * directly from the packet */
+ sk = skb->sk;
+ if (sk) {
+ struct sk_security_struct *sksec = sk->sk_security;
+ peer_sid = sksec->sid;
+ secmark_perm = PACKET__SEND;
+ } else {
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+ return NF_DROP;
+ secmark_perm = PACKET__FORWARD_OUT;
+ }
+
+ if (secmark_active)
+ if (avc_has_perm(peer_sid, skb->secmark,
+ SECCLASS_PACKET, secmark_perm, &ad))
+ return NF_DROP;
+
+ if (peerlbl_active) {
+ u32 if_sid;
+ u32 node_sid;
+
+ if (sel_netif_sid(ifindex, &if_sid))
+ return NF_DROP;
+ if (avc_has_perm(peer_sid, if_sid,
+ SECCLASS_NETIF, NETIF__EGRESS, &ad))
+ return NF_DROP;
+
+ if (sel_netnode_sid(addrp, family, &node_sid))
+ return NF_DROP;
+ if (avc_has_perm(peer_sid, node_sid,
+ SECCLASS_NODE, NODE__SENDTO, &ad))
+ return NF_DROP;
+ }
+
+ return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
- struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
+ 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, skb, in, out, okfn, PF_INET);
+ return selinux_ip_postroute(skb, out->ifindex, PF_INET);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-
-static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
- struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
+ 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, skb, in, out, okfn, PF_INET6);
+ return selinux_ip_postroute(skb, out->ifindex, PF_INET6);
}
-
#endif /* IPV6 */
#endif /* CONFIG_NETFILTER */
@@ -4710,6 +5153,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);
@@ -4800,6 +5248,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,
@@ -4898,6 +5349,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,
@@ -5031,22 +5483,40 @@ security_initcall(selinux_init);
#if defined(CONFIG_NETFILTER)
-static struct nf_hook_ops selinux_ipv4_op = {
- .hook = selinux_ipv4_postroute_last,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_IP_POST_ROUTING,
- .priority = NF_IP_PRI_SELINUX_LAST,
+static struct nf_hook_ops selinux_ipv4_ops[] = {
+ {
+ .hook = selinux_ipv4_postroute,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP_PRI_SELINUX_LAST,
+ },
+ {
+ .hook = selinux_ipv4_forward,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
+ }
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static struct nf_hook_ops selinux_ipv6_op = {
- .hook = selinux_ipv6_postroute_last,
- .owner = THIS_MODULE,
- .pf = PF_INET6,
- .hooknum = NF_IP6_POST_ROUTING,
- .priority = NF_IP6_PRI_SELINUX_LAST,
+static struct nf_hook_ops selinux_ipv6_ops[] = {
+ {
+ .hook = selinux_ipv6_postroute,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP6_PRI_SELINUX_LAST,
+ },
+ {
+ .hook = selinux_ipv6_forward,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP6_PRI_SELINUX_FIRST,
+ }
};
#endif /* IPV6 */
@@ -5054,22 +5524,27 @@ static struct nf_hook_ops selinux_ipv6_op = {
static int __init selinux_nf_ip_init(void)
{
int err = 0;
+ u32 iter;
if (!selinux_enabled)
goto out;
printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n");
- err = nf_register_hook(&selinux_ipv4_op);
- if (err)
- panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
+ for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
+ err = nf_register_hook(&selinux_ipv4_ops[iter]);
+ if (err)
+ panic("SELinux: nf_register_hook for IPv4: error %d\n",
+ err);
+ }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-
- err = nf_register_hook(&selinux_ipv6_op);
- if (err)
- panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
-
+ for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
+ err = nf_register_hook(&selinux_ipv6_ops[iter]);
+ if (err)
+ panic("SELinux: nf_register_hook for IPv6: error %d\n",
+ err);
+ }
#endif /* IPV6 */
out:
@@ -5081,11 +5556,15 @@ __initcall(selinux_nf_ip_init);
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
static void selinux_nf_ip_exit(void)
{
+ u32 iter;
+
printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n");
- nf_unregister_hook(&selinux_ipv4_op);
+ for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
+ nf_unregister_hook(&selinux_ipv4_ops[iter]);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- nf_unregister_hook(&selinux_ipv6_op);
+ for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
+ nf_unregister_hook(&selinux_ipv6_ops[iter]);
#endif /* IPV6 */
}
#endif
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index 049bf69..399f868 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -37,6 +37,8 @@
S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest")
S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv")
S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send")
+ S_(SECCLASS_NODE, NODE__RECVFROM, "recvfrom")
+ S_(SECCLASS_NODE, NODE__SENDTO, "sendto")
S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv")
S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send")
S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv")
@@ -45,6 +47,8 @@
S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send")
S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv")
S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send")
+ S_(SECCLASS_NETIF, NETIF__INGRESS, "ingress")
+ S_(SECCLASS_NETIF, NETIF__EGRESS, "egress")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom")
@@ -149,6 +153,10 @@
S_(SECCLASS_PACKET, PACKET__SEND, "send")
S_(SECCLASS_PACKET, PACKET__RECV, "recv")
S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
+ S_(SECCLASS_PACKET, PACKET__FLOW_IN, "flow_in")
+ S_(SECCLASS_PACKET, PACKET__FLOW_OUT, "flow_out")
+ S_(SECCLASS_PACKET, PACKET__FORWARD_IN, "forward_in")
+ S_(SECCLASS_PACKET, PACKET__FORWARD_OUT, "forward_out")
S_(SECCLASS_KEY, KEY__VIEW, "view")
S_(SECCLASS_KEY, KEY__READ, "read")
S_(SECCLASS_KEY, KEY__WRITE, "write")
@@ -159,3 +167,4 @@
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
+ S_(SECCLASS_PEER, PEER__RECV, "recv")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index eda89a2..84c9abc 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -292,6 +292,8 @@
#define NODE__ENFORCE_DEST 0x00000040UL
#define NODE__DCCP_RECV 0x00000080UL
#define NODE__DCCP_SEND 0x00000100UL
+#define NODE__RECVFROM 0x00000200UL
+#define NODE__SENDTO 0x00000400UL
#define NETIF__TCP_RECV 0x00000001UL
#define NETIF__TCP_SEND 0x00000002UL
#define NETIF__UDP_RECV 0x00000004UL
@@ -300,6 +302,8 @@
#define NETIF__RAWIP_SEND 0x00000020UL
#define NETIF__DCCP_RECV 0x00000040UL
#define NETIF__DCCP_SEND 0x00000080UL
+#define NETIF__INGRESS 0x00000100UL
+#define NETIF__EGRESS 0x00000200UL
#define NETLINK_SOCKET__IOCTL 0x00000001UL
#define NETLINK_SOCKET__READ 0x00000002UL
#define NETLINK_SOCKET__WRITE 0x00000004UL
@@ -792,6 +796,10 @@
#define PACKET__SEND 0x00000001UL
#define PACKET__RECV 0x00000002UL
#define PACKET__RELABELTO 0x00000004UL
+#define PACKET__FLOW_IN 0x00000008UL
+#define PACKET__FLOW_OUT 0x00000010UL
+#define PACKET__FORWARD_IN 0x00000020UL
+#define PACKET__FORWARD_OUT 0x00000040UL
#define KEY__VIEW 0x00000001UL
#define KEY__READ 0x00000002UL
#define KEY__WRITE 0x00000004UL
@@ -824,3 +832,4 @@
#define DCCP_SOCKET__NODE_BIND 0x00400000UL
#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
#define MEMPROTECT__MMAP_ZERO 0x00000001UL
+#define PEER__RECV 0x00000001UL
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 553607a..80c28fa 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -51,7 +51,7 @@ struct avc_audit_data {
struct inode *inode;
} fs;
struct {
- char *netif;
+ int netif;
struct sock *sk;
u16 family;
__be16 dport;
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
index e77de0e..b1b0d1d 100644
--- a/security/selinux/include/class_to_string.h
+++ b/security/selinux/include/class_to_string.h
@@ -64,3 +64,10 @@
S_(NULL)
S_("dccp_socket")
S_("memprotect")
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_("peer")
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
index a9c2b20..09e9dd23 100644
--- a/security/selinux/include/flask.h
+++ b/security/selinux/include/flask.h
@@ -50,6 +50,7 @@
#define SECCLASS_KEY 58
#define SECCLASS_DCCP_SOCKET 60
#define SECCLASS_MEMPROTECT 61
+#define SECCLASS_PEER 68
/*
* Security identifier indices for initial entities
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
index 8bd6f99..ce23edd 100644
--- a/security/selinux/include/netif.h
+++ b/security/selinux/include/netif.h
@@ -7,6 +7,8 @@
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Paul Moore, <paul.moore@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -15,7 +17,7 @@
#ifndef _SELINUX_NETIF_H_
#define _SELINUX_NETIF_H_
-int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid);
+int sel_netif_sid(int ifindex, u32 *sid);
#endif /* _SELINUX_NETIF_H_ */
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 218e3f7..00a2809 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -46,13 +46,17 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
struct sk_security_struct *newssec);
-int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid);
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
+ u16 family,
+ u32 *type,
+ u32 *sid);
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
int selinux_netlbl_socket_post_create(struct socket *sock);
int selinux_netlbl_inode_permission(struct inode *inode, int mask);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
+ u16 family,
struct avc_audit_data *ad);
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
@@ -83,9 +87,11 @@ static inline void selinux_netlbl_sk_security_clone(
}
static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
- u32 base_sid,
+ u16 family,
+ u32 *type,
u32 *sid)
{
+ *type = NETLBL_NLTYPE_NONE;
*sid = SECSID_NULL;
return 0;
}
@@ -106,6 +112,7 @@ static inline int selinux_netlbl_inode_permission(struct inode *inode,
}
static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
+ u16 family,
struct avc_audit_data *ad)
{
return 0;
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
new file mode 100644
index 0000000..1b94450
--- /dev/null
+++ b/security/selinux/include/netnode.h
@@ -0,0 +1,32 @@
+/*
+ * Network node table
+ *
+ * SELinux must keep a mapping of network nodes to labels/SIDs. This
+ * mapping is maintained as part of the normal policy but a fast cache is
+ * needed to reduce the lookup overhead since most of these queries happen on
+ * a per-packet basis.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SELINUX_NETNODE_H
+#define _SELINUX_NETNODE_H
+
+int sel_netnode_sid(void *addr, u16 family, u32 *sid);
+
+#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 642a9fd..c6c2bb4 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -65,6 +65,7 @@ struct superblock_security_struct {
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
unsigned int behavior; /* labeling behavior */
unsigned char initialized; /* initialization flag */
+ unsigned char flags; /* which mount options were specified */
unsigned char proc; /* proc fs */
struct mutex lock;
struct list_head isec_head;
@@ -95,17 +96,25 @@ struct bprm_security_struct {
};
struct netif_security_struct {
- struct net_device *dev; /* back pointer */
- u32 if_sid; /* SID for this interface */
- u32 msg_sid; /* default SID for messages received on this interface */
+ int ifindex; /* device index */
+ u32 sid; /* SID for this interface */
+};
+
+struct netnode_security_struct {
+ union {
+ __be32 ipv4; /* IPv4 node address */
+ struct in6_addr ipv6; /* IPv6 node address */
+ } addr;
+ u32 sid; /* SID for this node */
+ u16 family; /* address family */
};
struct sk_security_struct {
struct sock *sk; /* back pointer to sk object */
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
-#ifdef CONFIG_NETLABEL
u16 sclass; /* sock security class */
+#ifdef CONFIG_NETLABEL
enum { /* NetLabel state */
NLBL_UNSET = 0,
NLBL_REQUIRE,
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 39337af..23137c1 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -25,13 +25,14 @@
#define POLICYDB_VERSION_MLS 19
#define POLICYDB_VERSION_AVTAB 20
#define POLICYDB_VERSION_RANGETRANS 21
+#define POLICYDB_VERSION_POLCAP 22
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
-#define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_POLCAP
#endif
struct netlbl_lsm_secattr;
@@ -39,8 +40,19 @@ struct netlbl_lsm_secattr;
extern int selinux_enabled;
extern int selinux_mls_enabled;
+/* Policy capabilities */
+enum {
+ POLICYDB_CAPABILITY_NETPEER,
+ __POLICYDB_CAPABILITY_MAX
+};
+#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
+
+extern int selinux_policycap_netpeer;
+
int security_load_policy(void * data, size_t len);
+int security_policycap_supported(unsigned int req_cap);
+
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
@@ -77,8 +89,7 @@ int security_get_user_sids(u32 callsid, char *username,
int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port,
u32 *out_sid);
-int security_netif_sid(char *name, u32 *if_sid,
- u32 *msg_sid);
+int security_netif_sid(char *name, u32 *if_sid);
int security_node_sid(u16 domain, void *addr, u32 addrlen,
u32 *out_sid);
@@ -88,10 +99,15 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
+int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+ u32 xfrm_sid,
+ u32 *peer_sid);
+
int security_get_classes(char ***classes, int *nclasses);
int security_get_permissions(char *class, char ***perms, int *nperms);
int security_get_reject_unknown(void);
int security_get_allow_unknown(void);
+int security_get_policycaps(int *len, int **values);
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
@@ -108,7 +124,6 @@ int security_genfs_sid(const char *fstype, char *name, u16 sclass,
#ifdef CONFIG_NETLABEL
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
- u32 base_sid,
u32 *sid);
int security_netlbl_sid_to_secattr(u32 sid,
@@ -116,7 +131,6 @@ int security_netlbl_sid_to_secattr(u32 sid,
#else
static inline int security_netlbl_secattr_to_sid(
struct netlbl_lsm_secattr *secattr,
- u32 base_sid,
u32 *sid)
{
return -EIDRM;
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 31929e3..36b0510 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -32,6 +32,13 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
}
#ifdef CONFIG_SECURITY_NETWORK_XFRM
+extern atomic_t selinux_xfrm_refcount;
+
+static inline int selinux_xfrm_enabled(void)
+{
+ return (atomic_read(&selinux_xfrm_refcount) > 0);
+}
+
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
struct avc_audit_data *ad);
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
@@ -43,6 +50,11 @@ static inline void selinux_xfrm_notify_policyload(void)
atomic_inc(&flow_cache_genid);
}
#else
+static inline int selinux_xfrm_enabled(void)
+{
+ return 0;
+}
+
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad)
{
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index e87ab94..013d311 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -7,6 +7,8 @@
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Paul Moore <paul.moore@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -29,14 +31,6 @@
#define SEL_NETIF_HASH_SIZE 64
#define SEL_NETIF_HASH_MAX 1024
-#undef DEBUG
-
-#ifdef DEBUG
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
struct sel_netif
{
struct list_head list;
@@ -49,174 +43,226 @@ static LIST_HEAD(sel_netif_list);
static DEFINE_SPINLOCK(sel_netif_lock);
static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
-static inline u32 sel_netif_hasfn(struct net_device *dev)
+/**
+ * sel_netif_hashfn - Hashing function for the interface table
+ * @ifindex: the network interface
+ *
+ * Description:
+ * This is the hashing function for the network interface table, it returns the
+ * bucket number for the given interface.
+ *
+ */
+static inline u32 sel_netif_hashfn(int ifindex)
{
- return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1));
+ return (ifindex & (SEL_NETIF_HASH_SIZE - 1));
}
-/*
- * All of the devices should normally fit in the hash, so we optimize
- * for that case.
+/**
+ * sel_netif_find - Search for an interface record
+ * @ifindex: the network interface
+ *
+ * Description:
+ * Search the network interface table and return the record matching @ifindex.
+ * If an entry can not be found in the table return NULL.
+ *
*/
-static inline struct sel_netif *sel_netif_find(struct net_device *dev)
+static inline struct sel_netif *sel_netif_find(int ifindex)
{
- struct list_head *pos;
- int idx = sel_netif_hasfn(dev);
+ int idx = sel_netif_hashfn(ifindex);
+ struct sel_netif *netif;
- __list_for_each_rcu(pos, &sel_netif_hash[idx]) {
- struct sel_netif *netif = list_entry(pos,
- struct sel_netif, list);
- if (likely(netif->nsec.dev == dev))
+ list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
+ /* all of the devices should normally fit in the hash, so we
+ * optimize for that case */
+ if (likely(netif->nsec.ifindex == ifindex))
return netif;
- }
+
return NULL;
}
+/**
+ * sel_netif_insert - Insert a new interface into the table
+ * @netif: the new interface record
+ *
+ * Description:
+ * Add a new interface record to the network interface hash table. Returns
+ * zero on success, negative values on failure.
+ *
+ */
static int sel_netif_insert(struct sel_netif *netif)
{
- int idx, ret = 0;
+ int idx;
- if (sel_netif_total >= SEL_NETIF_HASH_MAX) {
- ret = -ENOSPC;
- goto out;
- }
+ if (sel_netif_total >= SEL_NETIF_HASH_MAX)
+ return -ENOSPC;
- idx = sel_netif_hasfn(netif->nsec.dev);
+ idx = sel_netif_hashfn(netif->nsec.ifindex);
list_add_rcu(&netif->list, &sel_netif_hash[idx]);
sel_netif_total++;
-out:
- return ret;
+
+ return 0;
}
+/**
+ * sel_netif_free - Frees an interface entry
+ * @p: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that memory allocated to a hash table interface entry can be
+ * released safely.
+ *
+ */
static void sel_netif_free(struct rcu_head *p)
{
struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head);
-
- DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
kfree(netif);
}
+/**
+ * sel_netif_destroy - Remove an interface record from the table
+ * @netif: the existing interface record
+ *
+ * Description:
+ * Remove an existing interface record from the network interface table.
+ *
+ */
static void sel_netif_destroy(struct sel_netif *netif)
{
- DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
-
list_del_rcu(&netif->list);
sel_netif_total--;
call_rcu(&netif->rcu_head, sel_netif_free);
}
-static struct sel_netif *sel_netif_lookup(struct net_device *dev)
+/**
+ * sel_netif_sid_slow - Lookup the SID of a network interface using the policy
+ * @ifindex: the network interface
+ * @sid: interface SID
+ *
+ * Description:
+ * This function determines the SID of a network interface by quering the
+ * security policy. The result is added to the network interface table to
+ * speedup future queries. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+static int sel_netif_sid_slow(int ifindex, u32 *sid)
{
int ret;
- struct sel_netif *netif, *new;
- struct netif_security_struct *nsec;
-
- netif = sel_netif_find(dev);
- if (likely(netif != NULL))
- goto out;
-
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
- if (!new) {
- netif = ERR_PTR(-ENOMEM);
- goto out;
+ struct sel_netif *netif;
+ struct sel_netif *new = NULL;
+ struct net_device *dev;
+
+ /* NOTE: we always use init's network namespace since we don't
+ * currently support containers */
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (unlikely(dev == NULL)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netif_sid_slow(),"
+ " invalid network interface (%d)\n", ifindex);
+ return -ENOENT;
}
-
- nsec = &new->nsec;
- ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid);
- if (ret < 0) {
- kfree(new);
- netif = ERR_PTR(ret);
+ spin_lock_bh(&sel_netif_lock);
+ netif = sel_netif_find(ifindex);
+ if (netif != NULL) {
+ *sid = netif->nsec.sid;
+ ret = 0;
goto out;
}
-
- nsec->dev = dev;
-
- spin_lock_bh(&sel_netif_lock);
-
- netif = sel_netif_find(dev);
- if (netif) {
- spin_unlock_bh(&sel_netif_lock);
- kfree(new);
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (new == NULL) {
+ ret = -ENOMEM;
goto out;
}
-
+ ret = security_netif_sid(dev->name, &new->nsec.sid);
+ if (ret != 0)
+ goto out;
+ new->nsec.ifindex = ifindex;
ret = sel_netif_insert(new);
- spin_unlock_bh(&sel_netif_lock);
-
- if (ret) {
- kfree(new);
- netif = ERR_PTR(ret);
+ if (ret != 0)
goto out;
- }
+ *sid = new->nsec.sid;
- netif = new;
-
- DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name,
- nsec->if_sid, nsec->msg_sid);
out:
- return netif;
-}
-
-static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out)
-{
- if (if_sid_out)
- *if_sid_out = if_sid_in;
- if (msg_sid_out)
- *msg_sid_out = msg_sid_in;
-}
-
-static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
-{
- int ret = 0;
- u32 tmp_if_sid, tmp_msg_sid;
-
- ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid);
- if (!ret)
- sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid);
+ spin_unlock_bh(&sel_netif_lock);
+ dev_put(dev);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netif_sid_slow(),"
+ " unable to determine network interface label (%d)\n",
+ ifindex);
+ kfree(new);
+ }
return ret;
}
-int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
+/**
+ * sel_netif_sid - Lookup the SID of a network interface
+ * @ifindex: the network interface
+ * @sid: interface SID
+ *
+ * Description:
+ * This function determines the SID of a network interface using the fastest
+ * method possible. First the interface table is queried, but if an entry
+ * can't be found then the policy is queried and the result is added to the
+ * table to speedup future queries. Returns zero on success, negative values
+ * on failure.
+ *
+ */
+int sel_netif_sid(int ifindex, u32 *sid)
{
- int ret = 0;
struct sel_netif *netif;
rcu_read_lock();
- netif = sel_netif_lookup(dev);
- if (IS_ERR(netif)) {
+ netif = sel_netif_find(ifindex);
+ if (likely(netif != NULL)) {
+ *sid = netif->nsec.sid;
rcu_read_unlock();
- ret = sel_netif_sids_slow(dev, if_sid, msg_sid);
- goto out;
+ return 0;
}
- sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid);
rcu_read_unlock();
-out:
- return ret;
+
+ return sel_netif_sid_slow(ifindex, sid);
}
-static void sel_netif_kill(struct net_device *dev)
+/**
+ * sel_netif_kill - Remove an entry from the network interface table
+ * @ifindex: the network interface
+ *
+ * Description:
+ * This function removes the entry matching @ifindex from the network interface
+ * table if it exists.
+ *
+ */
+static void sel_netif_kill(int ifindex)
{
struct sel_netif *netif;
spin_lock_bh(&sel_netif_lock);
- netif = sel_netif_find(dev);
+ netif = sel_netif_find(ifindex);
if (netif)
sel_netif_destroy(netif);
spin_unlock_bh(&sel_netif_lock);
}
+/**
+ * sel_netif_flush - Flush the entire network interface table
+ *
+ * Description:
+ * Remove all entries from the network interface table.
+ *
+ */
static void sel_netif_flush(void)
{
int idx;
+ struct sel_netif *netif;
spin_lock_bh(&sel_netif_lock);
- for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) {
- struct sel_netif *netif;
-
+ for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)
list_for_each_entry(netif, &sel_netif_hash[idx], list)
sel_netif_destroy(netif);
- }
spin_unlock_bh(&sel_netif_lock);
}
@@ -239,7 +285,7 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
return NOTIFY_DONE;
if (event == NETDEV_DOWN)
- sel_netif_kill(dev);
+ sel_netif_kill(dev->ifindex);
return NOTIFY_DONE;
}
@@ -250,10 +296,10 @@ static struct notifier_block sel_netif_netdev_notifier = {
static __init int sel_netif_init(void)
{
- int i, err = 0;
+ int i, err;
if (!selinux_enabled)
- goto out;
+ return 0;
for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
INIT_LIST_HEAD(&sel_netif_hash[i]);
@@ -265,7 +311,6 @@ static __init int sel_netif_init(void)
if (err)
panic("avc_add_callback() failed, error %d\n", err);
-out:
return err;
}
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index d243ddc..0fa2be4 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -36,6 +36,33 @@
#include "security.h"
/**
+ * selinux_netlbl_sidlookup_cached - Cache a SID lookup
+ * @skb: the packet
+ * @secattr: the NetLabel security attributes
+ * @sid: the SID
+ *
+ * Description:
+ * Query the SELinux security server to lookup the correct SID for the given
+ * security attributes. If the query is successful, cache the result to speed
+ * up future lookups. Returns zero on success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
+ struct netlbl_lsm_secattr *secattr,
+ u32 *sid)
+{
+ int rc;
+
+ rc = security_netlbl_secattr_to_sid(secattr, sid);
+ if (rc == 0 &&
+ (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
+ (secattr->flags & NETLBL_SECATTR_CACHE))
+ netlbl_cache_add(skb, secattr);
+
+ return rc;
+}
+
+/**
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
* @sk: the socket to label
* @sid: the SID to use
@@ -53,10 +80,11 @@ static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr secattr;
+ netlbl_secattr_init(&secattr);
+
rc = security_netlbl_sid_to_secattr(sid, &secattr);
if (rc != 0)
- return rc;
-
+ goto sock_setsid_return;
rc = netlbl_sock_setattr(sk, &secattr);
if (rc == 0) {
spin_lock_bh(&sksec->nlbl_lock);
@@ -64,6 +92,8 @@ static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
spin_unlock_bh(&sksec->nlbl_lock);
}
+sock_setsid_return:
+ netlbl_secattr_destroy(&secattr);
return rc;
}
@@ -134,14 +164,14 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
* lock as other threads could have access to ssec */
rcu_read_lock();
selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family);
- newssec->sclass = ssec->sclass;
rcu_read_unlock();
}
/**
* selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
* @skb: the packet
- * @base_sid: the SELinux SID to use as a context for MLS only attributes
+ * @family: protocol family
+ * @type: NetLabel labeling protocol type
* @sid: the SID
*
* Description:
@@ -150,7 +180,10 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
* assign to the packet. Returns zero on success, negative values on failure.
*
*/
-int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
+ u16 family,
+ u32 *type,
+ u32 *sid)
{
int rc;
struct netlbl_lsm_secattr secattr;
@@ -161,15 +194,12 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
}
netlbl_secattr_init(&secattr);
- rc = netlbl_skbuff_getattr(skb, &secattr);
- if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) {
- rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid);
- if (rc == 0 &&
- (secattr.flags & NETLBL_SECATTR_CACHEABLE) &&
- (secattr.flags & NETLBL_SECATTR_CACHE))
- netlbl_cache_add(skb, &secattr);
- } else
+ rc = netlbl_skbuff_getattr(skb, family, &secattr);
+ if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+ rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
+ else
*sid = SECSID_NULL;
+ *type = secattr.type;
netlbl_secattr_destroy(&secattr);
return rc;
@@ -187,13 +217,10 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
*/
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
{
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr secattr;
u32 nlbl_peer_sid;
- sksec->sclass = isec->sclass;
-
rcu_read_lock();
if (sksec->nlbl_state != NLBL_REQUIRE) {
@@ -204,9 +231,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
netlbl_secattr_init(&secattr);
if (netlbl_sock_getattr(sk, &secattr) == 0 &&
secattr.flags != NETLBL_SECATTR_NONE &&
- security_netlbl_secattr_to_sid(&secattr,
- SECINITSID_NETMSG,
- &nlbl_peer_sid) == 0)
+ security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
sksec->peer_sid = nlbl_peer_sid;
netlbl_secattr_destroy(&secattr);
@@ -231,11 +256,8 @@ int selinux_netlbl_socket_post_create(struct socket *sock)
{
int rc = 0;
struct sock *sk = sock->sk;
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
struct sk_security_struct *sksec = sk->sk_security;
- sksec->sclass = isec->sclass;
-
rcu_read_lock();
if (sksec->nlbl_state == NLBL_REQUIRE)
rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
@@ -289,6 +311,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
* selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
* @sksec: the sock's sk_security_struct
* @skb: the packet
+ * @family: protocol family
* @ad: the audit data
*
* Description:
@@ -299,6 +322,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
*/
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
+ u16 family,
struct avc_audit_data *ad)
{
int rc;
@@ -310,16 +334,10 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
return 0;
netlbl_secattr_init(&secattr);
- rc = netlbl_skbuff_getattr(skb, &secattr);
- if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) {
- rc = security_netlbl_secattr_to_sid(&secattr,
- SECINITSID_NETMSG,
- &nlbl_sid);
- if (rc == 0 &&
- (secattr.flags & NETLBL_SECATTR_CACHEABLE) &&
- (secattr.flags & NETLBL_SECATTR_CACHE))
- netlbl_cache_add(skb, &secattr);
- } else
+ rc = netlbl_skbuff_getattr(skb, family, &secattr);
+ if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+ rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
+ else
nlbl_sid = SECINITSID_UNLABELED;
netlbl_secattr_destroy(&secattr);
if (rc != 0)
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
new file mode 100644
index 0000000..f3c526f
--- /dev/null
+++ b/security/selinux/netnode.c
@@ -0,0 +1,354 @@
+/*
+ * Network node table
+ *
+ * SELinux must keep a mapping of network nodes to labels/SIDs. This
+ * mapping is maintained as part of the normal policy but a fast cache is
+ * needed to reduce the lookup overhead since most of these queries happen on
+ * a per-packet basis.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ * This code is heavily based on the "netif" concept originally developed by
+ * James Morris <jmorris@redhat.com>
+ * (see security/selinux/netif.c for more information)
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <asm/bug.h>
+
+#include "objsec.h"
+
+#define SEL_NETNODE_HASH_SIZE 256
+#define SEL_NETNODE_HASH_BKT_LIMIT 16
+
+struct sel_netnode {
+ struct netnode_security_struct nsec;
+
+ struct list_head list;
+ struct rcu_head rcu;
+};
+
+/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
+ * for this is that I suspect most users will not make heavy use of both
+ * address families at the same time so one table will usually end up wasted,
+ * if this becomes a problem we can always add a hash table for each address
+ * family later */
+
+static LIST_HEAD(sel_netnode_list);
+static DEFINE_SPINLOCK(sel_netnode_lock);
+static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
+
+/**
+ * sel_netnode_free - Frees a node entry
+ * @p: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that memory allocated to a hash table node entry can be
+ * released safely.
+ *
+ */
+static void sel_netnode_free(struct rcu_head *p)
+{
+ struct sel_netnode *node = container_of(p, struct sel_netnode, rcu);
+ kfree(node);
+}
+
+/**
+ * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
+ * @addr: IPv4 address
+ *
+ * Description:
+ * This is the IPv4 hashing function for the node interface table, it returns
+ * the bucket number for the given IP address.
+ *
+ */
+static u32 sel_netnode_hashfn_ipv4(__be32 addr)
+{
+ /* at some point we should determine if the mismatch in byte order
+ * affects the hash function dramatically */
+ return (addr & (SEL_NETNODE_HASH_SIZE - 1));
+}
+
+/**
+ * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
+ * @addr: IPv6 address
+ *
+ * Description:
+ * This is the IPv6 hashing function for the node interface table, it returns
+ * the bucket number for the given IP address.
+ *
+ */
+static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
+{
+ /* just hash the least significant 32 bits to keep things fast (they
+ * are the most likely to be different anyway), we can revisit this
+ * later if needed */
+ return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
+}
+
+/**
+ * sel_netnode_find - Search for a node record
+ * @addr: IP address
+ * @family: address family
+ *
+ * Description:
+ * Search the network node table and return the record matching @addr. If an
+ * entry can not be found in the table return NULL.
+ *
+ */
+static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
+{
+ u32 idx;
+ struct sel_netnode *node;
+
+ switch (family) {
+ case PF_INET:
+ idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
+ break;
+ case PF_INET6:
+ idx = sel_netnode_hashfn_ipv6(addr);
+ break;
+ default:
+ BUG();
+ }
+
+ list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list)
+ if (node->nsec.family == family)
+ switch (family) {
+ case PF_INET:
+ if (node->nsec.addr.ipv4 == *(__be32 *)addr)
+ return node;
+ break;
+ case PF_INET6:
+ if (ipv6_addr_equal(&node->nsec.addr.ipv6,
+ addr))
+ return node;
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * sel_netnode_insert - Insert a new node into the table
+ * @node: the new node record
+ *
+ * Description:
+ * Add a new node record to the network address hash table. Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int sel_netnode_insert(struct sel_netnode *node)
+{
+ u32 idx;
+ u32 count = 0;
+ struct sel_netnode *iter;
+
+ switch (node->nsec.family) {
+ case PF_INET:
+ idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
+ break;
+ case PF_INET6:
+ idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
+ break;
+ default:
+ BUG();
+ }
+ list_add_rcu(&node->list, &sel_netnode_hash[idx]);
+
+ /* we need to impose a limit on the growth of the hash table so check
+ * this bucket to make sure it is within the specified bounds */
+ list_for_each_entry(iter, &sel_netnode_hash[idx], list)
+ if (++count > SEL_NETNODE_HASH_BKT_LIMIT) {
+ list_del_rcu(&iter->list);
+ call_rcu(&iter->rcu, sel_netnode_free);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * sel_netnode_destroy - Remove a node record from the table
+ * @node: the existing node record
+ *
+ * Description:
+ * Remove an existing node record from the network address table.
+ *
+ */
+static void sel_netnode_destroy(struct sel_netnode *node)
+{
+ list_del_rcu(&node->list);
+ call_rcu(&node->rcu, sel_netnode_free);
+}
+
+/**
+ * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
+ * @addr: the IP address
+ * @family: the address family
+ * @sid: node SID
+ *
+ * Description:
+ * This function determines the SID of a network address by quering the
+ * security policy. The result is added to the network address table to
+ * speedup future queries. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
+{
+ int ret;
+ struct sel_netnode *node;
+ struct sel_netnode *new = NULL;
+
+ spin_lock_bh(&sel_netnode_lock);
+ node = sel_netnode_find(addr, family);
+ if (node != NULL) {
+ *sid = node->nsec.sid;
+ ret = 0;
+ goto out;
+ }
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (new == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ switch (family) {
+ case PF_INET:
+ ret = security_node_sid(PF_INET,
+ addr, sizeof(struct in_addr),
+ &new->nsec.sid);
+ new->nsec.addr.ipv4 = *(__be32 *)addr;
+ break;
+ case PF_INET6:
+ ret = security_node_sid(PF_INET6,
+ addr, sizeof(struct in6_addr),
+ &new->nsec.sid);
+ ipv6_addr_copy(&new->nsec.addr.ipv6, addr);
+ break;
+ default:
+ BUG();
+ }
+ if (ret != 0)
+ goto out;
+ new->nsec.family = family;
+ ret = sel_netnode_insert(new);
+ if (ret != 0)
+ goto out;
+ *sid = new->nsec.sid;
+
+out:
+ spin_unlock_bh(&sel_netnode_lock);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netnode_sid_slow(),"
+ " unable to determine network node label\n");
+ kfree(new);
+ }
+ return ret;
+}
+
+/**
+ * sel_netnode_sid - Lookup the SID of a network address
+ * @addr: the IP address
+ * @family: the address family
+ * @sid: node SID
+ *
+ * Description:
+ * This function determines the SID of a network address using the fastest
+ * method possible. First the address table is queried, but if an entry
+ * can't be found then the policy is queried and the result is added to the
+ * table to speedup future queries. Returns zero on success, negative values
+ * on failure.
+ *
+ */
+int sel_netnode_sid(void *addr, u16 family, u32 *sid)
+{
+ struct sel_netnode *node;
+
+ rcu_read_lock();
+ node = sel_netnode_find(addr, family);
+ if (node != NULL) {
+ *sid = node->nsec.sid;
+ rcu_read_unlock();
+ return 0;
+ }
+ rcu_read_unlock();
+
+ return sel_netnode_sid_slow(addr, family, sid);
+}
+
+/**
+ * sel_netnode_flush - Flush the entire network address table
+ *
+ * Description:
+ * Remove all entries from the network address table.
+ *
+ */
+static void sel_netnode_flush(void)
+{
+ u32 idx;
+ struct sel_netnode *node;
+
+ spin_lock_bh(&sel_netnode_lock);
+ for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++)
+ list_for_each_entry(node, &sel_netnode_hash[idx], list)
+ sel_netnode_destroy(node);
+ spin_unlock_bh(&sel_netnode_lock);
+}
+
+static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ if (event == AVC_CALLBACK_RESET) {
+ sel_netnode_flush();
+ synchronize_net();
+ }
+ return 0;
+}
+
+static __init int sel_netnode_init(void)
+{
+ int iter;
+ int ret;
+
+ if (!selinux_enabled)
+ return 0;
+
+ for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++)
+ INIT_LIST_HEAD(&sel_netnode_hash[iter]);
+
+ ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (ret != 0)
+ panic("avc_add_callback() failed, error %d\n", ret);
+
+ return ret;
+}
+
+__initcall(sel_netnode_init);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index f5f3e6d..0341567 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -2,6 +2,11 @@
*
* Added conditional policy language extensions
*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for the policy capability bitmap
+ *
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
* This program is free software; you can redistribute it and/or modify
@@ -35,6 +40,11 @@
#include "objsec.h"
#include "conditional.h"
+/* Policy capability filenames */
+static char *policycap_names[] = {
+ "network_peer_controls"
+};
+
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
@@ -65,12 +75,16 @@ static DEFINE_MUTEX(sel_mutex);
/* global data for booleans */
static struct dentry *bool_dir = NULL;
static int bool_num = 0;
+static char **bool_pending_names;
static int *bool_pending_values = NULL;
/* global data for classes */
static struct dentry *class_dir = NULL;
static unsigned long last_class_ino;
+/* global data for policy capabilities */
+static struct dentry *policycap_dir = NULL;
+
extern void selnl_notify_setenforce(int val);
/* Check whether a task is allowed to use a security operation. */
@@ -110,10 +124,11 @@ enum sel_inos {
static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
-#define SEL_INITCON_INO_OFFSET 0x01000000
-#define SEL_BOOL_INO_OFFSET 0x02000000
-#define SEL_CLASS_INO_OFFSET 0x04000000
-#define SEL_INO_MASK 0x00ffffff
+#define SEL_INITCON_INO_OFFSET 0x01000000
+#define SEL_BOOL_INO_OFFSET 0x02000000
+#define SEL_CLASS_INO_OFFSET 0x04000000
+#define SEL_POLICYCAP_INO_OFFSET 0x08000000
+#define SEL_INO_MASK 0x00ffffff
#define TMPBUFLEN 12
static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
@@ -157,9 +172,10 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf,
if (length)
goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
- "enforcing=%d old_enforcing=%d auid=%u", new_value,
- selinux_enforcing,
- audit_get_loginuid(current->audit_context));
+ "enforcing=%d old_enforcing=%d auid=%u ses=%u",
+ new_value, selinux_enforcing,
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
selinux_enforcing = new_value;
if (selinux_enforcing)
avc_ss_reset(0);
@@ -228,8 +244,9 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf,
if (length < 0)
goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
- "selinux=0 auid=%u",
- audit_get_loginuid(current->audit_context));
+ "selinux=0 auid=%u ses=%u",
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
}
length = count;
@@ -262,6 +279,7 @@ static const struct file_operations sel_policyvers_ops = {
/* declaration for sel_write_load */
static int sel_make_bools(void);
static int sel_make_classes(void);
+static int sel_make_policycap(void);
/* declaration for sel_make_class_dirs */
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
@@ -322,6 +340,12 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
}
ret = sel_make_classes();
+ if (ret) {
+ length = ret;
+ goto out1;
+ }
+
+ ret = sel_make_policycap();
if (ret)
length = ret;
else
@@ -334,8 +358,9 @@ out1:
(security_get_allow_unknown() ? "allow" : "deny")));
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
- "policy loaded auid=%u",
- audit_get_loginuid(current->audit_context));
+ "policy loaded auid=%u ses=%u",
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
out:
mutex_unlock(&sel_mutex);
vfree(data);
@@ -832,15 +857,16 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
ssize_t length;
ssize_t ret;
int cur_enforcing;
- struct inode *inode;
+ struct inode *inode = filep->f_path.dentry->d_inode;
+ unsigned index = inode->i_ino & SEL_INO_MASK;
+ const char *name = filep->f_path.dentry->d_name.name;
mutex_lock(&sel_mutex);
- ret = -EFAULT;
-
- /* check to see if this file has been deleted */
- if (!filep->f_op)
+ if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
+ ret = -EINVAL;
goto out;
+ }
if (count > PAGE_SIZE) {
ret = -EINVAL;
@@ -851,15 +877,13 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
goto out;
}
- inode = filep->f_path.dentry->d_inode;
- cur_enforcing = security_get_bool_value(inode->i_ino&SEL_INO_MASK);
+ cur_enforcing = security_get_bool_value(index);
if (cur_enforcing < 0) {
ret = cur_enforcing;
goto out;
}
-
length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
- bool_pending_values[inode->i_ino&SEL_INO_MASK]);
+ bool_pending_values[index]);
ret = simple_read_from_buffer(buf, count, ppos, page, length);
out:
mutex_unlock(&sel_mutex);
@@ -872,9 +896,11 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
size_t count, loff_t *ppos)
{
char *page = NULL;
- ssize_t length = -EFAULT;
+ ssize_t length;
int new_value;
- struct inode *inode;
+ struct inode *inode = filep->f_path.dentry->d_inode;
+ unsigned index = inode->i_ino & SEL_INO_MASK;
+ const char *name = filep->f_path.dentry->d_name.name;
mutex_lock(&sel_mutex);
@@ -882,16 +908,19 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (length)
goto out;
- /* check to see if this file has been deleted */
- if (!filep->f_op)
+ if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
+ length = -EINVAL;
goto out;
+ }
if (count >= PAGE_SIZE) {
length = -ENOMEM;
goto out;
}
+
if (*ppos != 0) {
/* No partial writes. */
+ length = -EINVAL;
goto out;
}
page = (char*)get_zeroed_page(GFP_KERNEL);
@@ -900,6 +929,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
goto out;
}
+ length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
@@ -910,8 +940,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (new_value)
new_value = 1;
- inode = filep->f_path.dentry->d_inode;
- bool_pending_values[inode->i_ino&SEL_INO_MASK] = new_value;
+ bool_pending_values[index] = new_value;
length = count;
out:
@@ -931,7 +960,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
size_t count, loff_t *ppos)
{
char *page = NULL;
- ssize_t length = -EFAULT;
+ ssize_t length;
int new_value;
mutex_lock(&sel_mutex);
@@ -940,10 +969,6 @@ static ssize_t sel_commit_bools_write(struct file *filep,
if (length)
goto out;
- /* check to see if this file has been deleted */
- if (!filep->f_op)
- goto out;
-
if (count >= PAGE_SIZE) {
length = -ENOMEM;
goto out;
@@ -958,6 +983,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
goto out;
}
+ length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
@@ -982,11 +1008,9 @@ static const struct file_operations sel_commit_bools_ops = {
.write = sel_commit_bools_write,
};
-/* partial revoke() from fs/proc/generic.c proc_kill_inodes */
static void sel_remove_entries(struct dentry *de)
{
- struct list_head *p, *node;
- struct super_block *sb = de->d_sb;
+ struct list_head *node;
spin_lock(&dcache_lock);
node = de->d_subdirs.next;
@@ -1006,18 +1030,6 @@ static void sel_remove_entries(struct dentry *de)
}
spin_unlock(&dcache_lock);
-
- file_list_lock();
- list_for_each(p, &sb->s_files) {
- struct file * filp = list_entry(p, struct file, f_u.fu_list);
- struct dentry * dentry = filp->f_path.dentry;
-
- if (dentry->d_parent != de) {
- continue;
- }
- filp->f_op = NULL;
- }
- file_list_unlock();
}
#define BOOL_DIR_NAME "booleans"
@@ -1036,7 +1048,9 @@ static int sel_make_bools(void)
u32 sid;
/* remove any existing files */
+ kfree(bool_pending_names);
kfree(bool_pending_values);
+ bool_pending_names = NULL;
bool_pending_values = NULL;
sel_remove_entries(dir);
@@ -1078,16 +1092,17 @@ static int sel_make_bools(void)
d_add(dentry, inode);
}
bool_num = num;
+ bool_pending_names = names;
bool_pending_values = values;
out:
free_page((unsigned long)page);
+ return ret;
+err:
if (names) {
for (i = 0; i < num; i++)
kfree(names[i]);
kfree(names);
}
- return ret;
-err:
kfree(values);
sel_remove_entries(dir);
ret = -ENOMEM;
@@ -1231,7 +1246,7 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v)
static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v)
{ }
-static struct seq_operations sel_avc_cache_stats_seq_ops = {
+static const struct seq_operations sel_avc_cache_stats_seq_ops = {
.start = sel_avc_stats_seq_start,
.next = sel_avc_stats_seq_next,
.show = sel_avc_stats_seq_show,
@@ -1408,6 +1423,24 @@ static const struct file_operations sel_perm_ops = {
.read = sel_read_perm,
};
+static ssize_t sel_read_policycap(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int value;
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+ unsigned long i_ino = file->f_path.dentry->d_inode->i_ino;
+
+ value = security_policycap_supported(i_ino & SEL_INO_MASK);
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value);
+
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static const struct file_operations sel_policycap_ops = {
+ .read = sel_read_policycap,
+};
+
static int sel_make_perm_files(char *objclass, int classvalue,
struct dentry *dir)
{
@@ -1554,6 +1587,36 @@ out:
return rc;
}
+static int sel_make_policycap(void)
+{
+ unsigned int iter;
+ struct dentry *dentry = NULL;
+ struct inode *inode = NULL;
+
+ sel_remove_entries(policycap_dir);
+
+ for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
+ if (iter < ARRAY_SIZE(policycap_names))
+ dentry = d_alloc_name(policycap_dir,
+ policycap_names[iter]);
+ else
+ dentry = d_alloc_name(policycap_dir, "unknown");
+
+ if (dentry == NULL)
+ return -ENOMEM;
+
+ inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO);
+ if (inode == NULL)
+ return -ENOMEM;
+
+ inode->i_fop = &sel_policycap_ops;
+ inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET;
+ d_add(dentry, inode);
+ }
+
+ return 0;
+}
+
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
unsigned long *ino)
{
@@ -1682,6 +1745,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
class_dir = dentry;
+ dentry = d_alloc_name(sb->s_root, "policy_capabilities");
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ policycap_dir = dentry;
+
out:
return ret;
err:
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 7551af1..cd10e27 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -280,7 +280,7 @@ int avtab_alloc(struct avtab *h, u32 nrules)
h->nel = 0;
h->nslot = nslot;
h->mask = mask;
- printk(KERN_DEBUG "SELinux:%d avtab hash slots allocated."
+ printk(KERN_DEBUG "SELinux:%d avtab hash slots allocated. "
"Num of rules:%d\n", h->nslot, nrules);
return 0;
}
@@ -325,7 +325,7 @@ static uint16_t spec_order[] = {
AVTAB_MEMBER
};
-int avtab_read_item(void *fp, u32 vers, struct avtab *a,
+int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
int (*insertf)(struct avtab *a, struct avtab_key *k,
struct avtab_datum *d, void *p),
void *p)
@@ -333,10 +333,11 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
__le16 buf16[4];
u16 enabled;
__le32 buf32[7];
- u32 items, items2, val;
+ u32 items, items2, val, vers = pol->policyvers;
struct avtab_key key;
struct avtab_datum datum;
int i, rc;
+ unsigned set;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -420,12 +421,35 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a,
key.target_class = le16_to_cpu(buf16[items++]);
key.specified = le16_to_cpu(buf16[items++]);
+ if (!policydb_type_isvalid(pol, key.source_type) ||
+ !policydb_type_isvalid(pol, key.target_type) ||
+ !policydb_class_isvalid(pol, key.target_class)) {
+ printk(KERN_WARNING "security: avtab: invalid type or class\n");
+ return -1;
+ }
+
+ set = 0;
+ for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
+ if (key.specified & spec_order[i])
+ set++;
+ }
+ if (!set || set > 1) {
+ printk(KERN_WARNING
+ "security: avtab: more than one specifier\n");
+ return -1;
+ }
+
rc = next_entry(buf32, fp, sizeof(u32));
if (rc < 0) {
printk("security: avtab: truncated entry\n");
return -1;
}
datum.data = le32_to_cpu(*buf32);
+ if ((key.specified & AVTAB_TYPE) &&
+ !policydb_type_isvalid(pol, datum.data)) {
+ printk(KERN_WARNING "security: avtab: invalid type\n");
+ return -1;
+ }
return insertf(a, &key, &datum, p);
}
@@ -435,7 +459,7 @@ static int avtab_insertf(struct avtab *a, struct avtab_key *k,
return avtab_insert(a, k, d);
}
-int avtab_read(struct avtab *a, void *fp, u32 vers)
+int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
{
int rc;
__le32 buf[1];
@@ -459,7 +483,7 @@ int avtab_read(struct avtab *a, void *fp, u32 vers)
goto bad;
for (i = 0; i < nel; i++) {
- rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL);
+ rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
if (rc) {
if (rc == -ENOMEM)
printk(KERN_ERR "security: avtab: out of memory\n");
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index d8edf8c..8da6a84 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -64,12 +64,13 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
void avtab_destroy(struct avtab *h);
void avtab_hash_eval(struct avtab *h, char *tag);
-int avtab_read_item(void *fp, uint32_t vers, struct avtab *a,
+struct policydb;
+int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
int (*insert)(struct avtab *a, struct avtab_key *k,
struct avtab_datum *d, void *p),
void *p);
-int avtab_read(struct avtab *a, void *fp, u32 vers);
+int avtab_read(struct avtab *a, void *fp, struct policydb *pol);
struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
struct avtab_datum *datum);
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 45b93a8..50ad85d 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -362,7 +362,8 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
data.head = NULL;
data.tail = NULL;
for (i = 0; i < len; i++) {
- rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data);
+ rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,
+ &data);
if (rc)
return rc;
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index c1a6b22..920b5e3 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -129,8 +129,8 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap,
cmap_sft = delta % NETLBL_CATMAP_MAPSIZE;
c_iter->bitmap[cmap_idx]
|= e_iter->maps[cmap_idx] << cmap_sft;
- e_iter = e_iter->next;
}
+ e_iter = e_iter->next;
}
return 0;
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 9a11dea..feaf0a5 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -157,49 +157,55 @@ void mls_sid_to_context(struct context *context,
return;
}
+int mls_level_isvalid(struct policydb *p, struct mls_level *l)
+{
+ struct level_datum *levdatum;
+ struct ebitmap_node *node;
+ int i;
+
+ if (!l->sens || l->sens > p->p_levels.nprim)
+ return 0;
+ levdatum = hashtab_search(p->p_levels.table,
+ p->p_sens_val_to_name[l->sens - 1]);
+ if (!levdatum)
+ return 0;
+
+ ebitmap_for_each_positive_bit(&l->cat, node, i) {
+ if (i > p->p_cats.nprim)
+ return 0;
+ if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
+ /*
+ * Category may not be associated with
+ * sensitivity.
+ */
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int mls_range_isvalid(struct policydb *p, struct mls_range *r)
+{
+ return (mls_level_isvalid(p, &r->level[0]) &&
+ mls_level_isvalid(p, &r->level[1]) &&
+ mls_level_dom(&r->level[1], &r->level[0]));
+}
+
/*
* Return 1 if the MLS fields in the security context
* structure `c' are valid. Return 0 otherwise.
*/
int mls_context_isvalid(struct policydb *p, struct context *c)
{
- struct level_datum *levdatum;
struct user_datum *usrdatum;
- struct ebitmap_node *node;
- int i, l;
if (!selinux_mls_enabled)
return 1;
- /*
- * MLS range validity checks: high must dominate low, low level must
- * be valid (category set <-> sensitivity check), and high level must
- * be valid (category set <-> sensitivity check)
- */
- if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
- /* High does not dominate low. */
+ if (!mls_range_isvalid(p, &c->range))
return 0;
- for (l = 0; l < 2; l++) {
- if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
- return 0;
- levdatum = hashtab_search(p->p_levels.table,
- p->p_sens_val_to_name[c->range.level[l].sens - 1]);
- if (!levdatum)
- return 0;
-
- ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
- if (i > p->p_cats.nprim)
- return 0;
- if (!ebitmap_get_bit(&levdatum->level->cat, i))
- /*
- * Category may not be associated with
- * sensitivity in low level.
- */
- return 0;
- }
- }
-
if (c->role == OBJECT_R_VAL)
return 1;
@@ -531,15 +537,8 @@ int mls_compute_sid(struct context *scontext,
/* Use the process effective MLS attributes. */
return mls_context_cpy_low(newcontext, scontext);
case AVTAB_MEMBER:
- /* Only polyinstantiate the MLS attributes if
- the type is being polyinstantiated */
- if (newcontext->type != tcontext->type) {
- /* Use the process effective MLS attributes. */
- return mls_context_cpy_low(newcontext, scontext);
- } else {
- /* Use the related object MLS attributes. */
- return mls_context_cpy(newcontext, tcontext);
- }
+ /* Use the process effective MLS attributes. */
+ return mls_context_cpy_low(newcontext, scontext);
default:
return -EINVAL;
}
@@ -563,7 +562,7 @@ void mls_export_netlbl_lvl(struct context *context,
if (!selinux_mls_enabled)
return;
- secattr->mls_lvl = context->range.level[0].sens - 1;
+ secattr->attr.mls.lvl = context->range.level[0].sens - 1;
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
}
@@ -583,7 +582,7 @@ void mls_import_netlbl_lvl(struct context *context,
if (!selinux_mls_enabled)
return;
- context->range.level[0].sens = secattr->mls_lvl + 1;
+ context->range.level[0].sens = secattr->attr.mls.lvl + 1;
context->range.level[1].sens = context->range.level[0].sens;
}
@@ -606,8 +605,8 @@ int mls_export_netlbl_cat(struct context *context,
return 0;
rc = ebitmap_netlbl_export(&context->range.level[0].cat,
- &secattr->mls_cat);
- if (rc == 0 && secattr->mls_cat != NULL)
+ &secattr->attr.mls.cat);
+ if (rc == 0 && secattr->attr.mls.cat != NULL)
secattr->flags |= NETLBL_SECATTR_MLS_CAT;
return rc;
@@ -634,7 +633,7 @@ int mls_import_netlbl_cat(struct context *context,
return 0;
rc = ebitmap_netlbl_import(&context->range.level[0].cat,
- secattr->mls_cat);
+ secattr->attr.mls.cat);
if (rc != 0)
goto import_netlbl_cat_failure;
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 096d1b4..ab53663 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -27,6 +27,8 @@
int mls_compute_context_len(struct context *context);
void mls_sid_to_context(struct context *context, char **scontext);
int mls_context_isvalid(struct policydb *p, struct context *c);
+int mls_range_isvalid(struct policydb *p, struct mls_range *r);
+int mls_level_isvalid(struct policydb *p, struct mls_level *l);
int mls_context_to_sid(char oldc,
char **scontext,
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 539828b..bd7d6a0 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -13,6 +13,11 @@
*
* Added conditional policy language extensions
*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for the policy capability bitmap
+ *
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* This program is free software; you can redistribute it and/or modify
@@ -102,6 +107,11 @@ static struct policydb_compat_info policydb_compat[] = {
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
+ {
+ .version = POLICYDB_VERSION_POLCAP,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ }
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -183,6 +193,8 @@ static int policydb_init(struct policydb *p)
if (rc)
goto out_free_symtab;
+ ebitmap_init(&p->policycaps);
+
out:
return rc;
@@ -673,8 +685,8 @@ void policydb_destroy(struct policydb *p)
ebitmap_destroy(&p->type_attr_map[i]);
}
kfree(p->type_attr_map);
-
kfree(p->undefined_perms);
+ ebitmap_destroy(&p->policycaps);
return;
}
@@ -713,6 +725,27 @@ out:
return rc;
}
+int policydb_class_isvalid(struct policydb *p, unsigned int class)
+{
+ if (!class || class > p->p_classes.nprim)
+ return 0;
+ return 1;
+}
+
+int policydb_role_isvalid(struct policydb *p, unsigned int role)
+{
+ if (!role || role > p->p_roles.nprim)
+ return 0;
+ return 1;
+}
+
+int policydb_type_isvalid(struct policydb *p, unsigned int type)
+{
+ if (!type || type > p->p_types.nprim)
+ return 0;
+ return 1;
+}
+
/*
* Return 1 if the fields in the security context
* structure `c' are valid. Return 0 otherwise.
@@ -1260,6 +1293,7 @@ static int mls_read_level(struct mls_level *lp, void *fp)
"categories\n");
goto bad;
}
+
return 0;
bad:
@@ -1532,6 +1566,10 @@ int policydb_read(struct policydb *p, void *fp)
p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
+ if (p->policyvers >= POLICYDB_VERSION_POLCAP &&
+ ebitmap_read(&p->policycaps, fp) != 0)
+ goto bad;
+
info = policydb_lookup_compat(p->policyvers);
if (!info) {
printk(KERN_ERR "security: unable to find policy compat info "
@@ -1563,7 +1601,7 @@ int policydb_read(struct policydb *p, void *fp)
p->symtab[i].nprim = nprim;
}
- rc = avtab_read(&p->te_avtab, fp, p->policyvers);
+ rc = avtab_read(&p->te_avtab, fp, p);
if (rc)
goto bad;
@@ -1595,6 +1633,12 @@ int policydb_read(struct policydb *p, void *fp)
tr->role = le32_to_cpu(buf[0]);
tr->type = le32_to_cpu(buf[1]);
tr->new_role = le32_to_cpu(buf[2]);
+ if (!policydb_role_isvalid(p, tr->role) ||
+ !policydb_type_isvalid(p, tr->type) ||
+ !policydb_role_isvalid(p, tr->new_role)) {
+ rc = -EINVAL;
+ goto bad;
+ }
ltr = tr;
}
@@ -1619,6 +1663,11 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
ra->role = le32_to_cpu(buf[0]);
ra->new_role = le32_to_cpu(buf[1]);
+ if (!policydb_role_isvalid(p, ra->role) ||
+ !policydb_role_isvalid(p, ra->new_role)) {
+ rc = -EINVAL;
+ goto bad;
+ }
lra = ra;
}
@@ -1872,9 +1921,19 @@ int policydb_read(struct policydb *p, void *fp)
rt->target_class = le32_to_cpu(buf[0]);
} else
rt->target_class = SECCLASS_PROCESS;
+ if (!policydb_type_isvalid(p, rt->source_type) ||
+ !policydb_type_isvalid(p, rt->target_type) ||
+ !policydb_class_isvalid(p, rt->target_class)) {
+ rc = -EINVAL;
+ goto bad;
+ }
rc = mls_read_range_helper(&rt->target_range, fp);
if (rc)
goto bad;
+ if (!mls_range_isvalid(p, &rt->target_range)) {
+ printk(KERN_WARNING "security: rangetrans: invalid range\n");
+ goto bad;
+ }
lrt = rt;
}
}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 844d310..c4ce996 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -241,6 +241,8 @@ struct policydb {
/* type -> attribute reverse mapping */
struct ebitmap *type_attr_map;
+ struct ebitmap policycaps;
+
unsigned int policyvers;
unsigned int reject_unknown : 1;
@@ -251,6 +253,9 @@ struct policydb {
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
+extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
+extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, void *fp);
#define PERM_SYMTAB_SIZE 32
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index d572dc9..fced6bc 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -16,12 +16,13 @@
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support for NetLabel
+ * Added support for the policy capability bitmap
*
* Updated: Chad Sellers <csellers@tresys.com>
*
* Added validation of kernel classes and permissions
*
- * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
@@ -59,6 +60,8 @@
extern void selnl_notify_policyload(u32 seqno);
unsigned int policydb_loaded_version;
+int selinux_policycap_netpeer;
+
/*
* This is declared in avc.c
*/
@@ -1299,6 +1302,12 @@ bad:
goto out;
}
+static void security_load_policycaps(void)
+{
+ selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
+ POLICYDB_CAPABILITY_NETPEER);
+}
+
extern void selinux_complete_init(void);
static int security_preserve_bools(struct policydb *p);
@@ -1346,6 +1355,7 @@ int security_load_policy(void *data, size_t len)
avtab_cache_destroy();
return -EINVAL;
}
+ security_load_policycaps();
policydb_loaded_version = policydb.policyvers;
ss_initialized = 1;
seqno = ++latest_granting;
@@ -1404,6 +1414,7 @@ int security_load_policy(void *data, size_t len)
POLICY_WRLOCK;
memcpy(&policydb, &newpolicydb, sizeof policydb);
sidtab_set(&sidtab, &newsidtab);
+ security_load_policycaps();
seqno = ++latest_granting;
policydb_loaded_version = policydb.policyvers;
POLICY_WRUNLOCK;
@@ -1478,11 +1489,8 @@ out:
* security_netif_sid - Obtain the SID for a network interface.
* @name: interface name
* @if_sid: interface SID
- * @msg_sid: default SID for received packets
*/
-int security_netif_sid(char *name,
- u32 *if_sid,
- u32 *msg_sid)
+int security_netif_sid(char *name, u32 *if_sid)
{
int rc = 0;
struct ocontext *c;
@@ -1510,11 +1518,8 @@ int security_netif_sid(char *name,
goto out;
}
*if_sid = c->sid[0];
- *msg_sid = c->sid[1];
- } else {
+ } else
*if_sid = SECINITSID_NETIF;
- *msg_sid = SECINITSID_NETMSG;
- }
out:
POLICY_RDUNLOCK;
@@ -1744,6 +1749,9 @@ int security_genfs_sid(const char *fstype,
struct ocontext *c;
int rc = 0, cmp = 0;
+ while (path[0] == '/' && path[1] == '/')
+ path++;
+
POLICY_RDLOCK;
for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
@@ -1897,11 +1905,12 @@ int security_set_bools(int len, int *values)
if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
audit_log(current->audit_context, GFP_ATOMIC,
AUDIT_MAC_CONFIG_CHANGE,
- "bool=%s val=%d old_val=%d auid=%u",
+ "bool=%s val=%d old_val=%d auid=%u ses=%u",
policydb.p_bool_val_to_name[i],
!!values[i],
policydb.bool_val_to_struct[i]->state,
- audit_get_loginuid(current->audit_context));
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
}
if (values[i]) {
policydb.bool_val_to_struct[i]->state = 1;
@@ -2046,6 +2055,91 @@ out:
return rc;
}
+/**
+ * security_net_peersid_resolve - Compare and resolve two network peer SIDs
+ * @nlbl_sid: NetLabel SID
+ * @nlbl_type: NetLabel labeling protocol type
+ * @xfrm_sid: XFRM SID
+ *
+ * Description:
+ * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be
+ * resolved into a single SID it is returned via @peer_sid and the function
+ * returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function
+ * returns a negative value. A table summarizing the behavior is below:
+ *
+ * | function return | @sid
+ * ------------------------------+-----------------+-----------------
+ * no peer labels | 0 | SECSID_NULL
+ * single peer label | 0 | <peer_label>
+ * multiple, consistent labels | 0 | <peer_label>
+ * multiple, inconsistent labels | -<errno> | SECSID_NULL
+ *
+ */
+int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+ u32 xfrm_sid,
+ u32 *peer_sid)
+{
+ int rc;
+ struct context *nlbl_ctx;
+ struct context *xfrm_ctx;
+
+ /* handle the common (which also happens to be the set of easy) cases
+ * right away, these two if statements catch everything involving a
+ * single or absent peer SID/label */
+ if (xfrm_sid == SECSID_NULL) {
+ *peer_sid = nlbl_sid;
+ return 0;
+ }
+ /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label
+ * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label
+ * is present */
+ if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) {
+ *peer_sid = xfrm_sid;
+ return 0;
+ }
+
+ /* we don't need to check ss_initialized here since the only way both
+ * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
+ * security server was initialized and ss_initialized was true */
+ if (!selinux_mls_enabled) {
+ *peer_sid = SECSID_NULL;
+ return 0;
+ }
+
+ POLICY_RDLOCK;
+
+ nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
+ if (!nlbl_ctx) {
+ printk(KERN_ERR
+ "security_sid_mls_cmp: unrecognized SID %d\n",
+ nlbl_sid);
+ rc = -EINVAL;
+ goto out_slowpath;
+ }
+ xfrm_ctx = sidtab_search(&sidtab, xfrm_sid);
+ if (!xfrm_ctx) {
+ printk(KERN_ERR
+ "security_sid_mls_cmp: unrecognized SID %d\n",
+ xfrm_sid);
+ rc = -EINVAL;
+ goto out_slowpath;
+ }
+ rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
+
+out_slowpath:
+ POLICY_RDUNLOCK;
+ if (rc == 0)
+ /* at present NetLabel SIDs/labels really only carry MLS
+ * information so if the MLS portion of the NetLabel SID
+ * matches the MLS portion of the labeled XFRM SID/label
+ * then pass along the XFRM SID as it is the most
+ * expressive */
+ *peer_sid = xfrm_sid;
+ else
+ *peer_sid = SECSID_NULL;
+ return rc;
+}
+
static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
@@ -2151,6 +2245,60 @@ int security_get_allow_unknown(void)
return policydb.allow_unknown;
}
+/**
+ * security_get_policycaps - Query the loaded policy for its capabilities
+ * @len: the number of capability bits
+ * @values: the capability bit array
+ *
+ * Description:
+ * Get an array of the policy capabilities in @values where each entry in
+ * @values is either true (1) or false (0) depending the policy's support of
+ * that feature. The policy capabilities are defined by the
+ * POLICYDB_CAPABILITY_* enums. The size of the array is stored in @len and it
+ * is up to the caller to free the array in @values. Returns zero on success,
+ * negative values on failure.
+ *
+ */
+int security_get_policycaps(int *len, int **values)
+{
+ int rc = -ENOMEM;
+ unsigned int iter;
+
+ POLICY_RDLOCK;
+
+ *values = kcalloc(POLICYDB_CAPABILITY_MAX, sizeof(int), GFP_ATOMIC);
+ if (*values == NULL)
+ goto out;
+ for (iter = 0; iter < POLICYDB_CAPABILITY_MAX; iter++)
+ (*values)[iter] = ebitmap_get_bit(&policydb.policycaps, iter);
+ *len = POLICYDB_CAPABILITY_MAX;
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+/**
+ * security_policycap_supported - Check for a specific policy capability
+ * @req_cap: capability
+ *
+ * Description:
+ * This function queries the currently loaded policy to see if it supports the
+ * capability specified by @req_cap. Returns true (1) if the capability is
+ * supported, false (0) if it isn't supported.
+ *
+ */
+int security_policycap_supported(unsigned int req_cap)
+{
+ int rc;
+
+ POLICY_RDLOCK;
+ rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
+ POLICY_RDUNLOCK;
+
+ return rc;
+}
+
struct selinux_audit_rule {
u32 au_seqno;
struct context au_ctxt;
@@ -2400,50 +2548,10 @@ void selinux_audit_set_callback(int (*callback)(void))
}
#ifdef CONFIG_NETLABEL
-/*
- * NetLabel cache structure
- */
-#define NETLBL_CACHE(x) ((struct selinux_netlbl_cache *)(x))
-#define NETLBL_CACHE_T_NONE 0
-#define NETLBL_CACHE_T_SID 1
-#define NETLBL_CACHE_T_MLS 2
-struct selinux_netlbl_cache {
- u32 type;
- union {
- u32 sid;
- struct mls_range mls_label;
- } data;
-};
-
-/**
- * security_netlbl_cache_free - Free the NetLabel cached data
- * @data: the data to free
- *
- * Description:
- * This function is intended to be used as the free() callback inside the
- * netlbl_lsm_cache structure.
- *
- */
-static void security_netlbl_cache_free(const void *data)
-{
- struct selinux_netlbl_cache *cache;
-
- if (data == NULL)
- return;
-
- cache = NETLBL_CACHE(data);
- switch (cache->type) {
- case NETLBL_CACHE_T_MLS:
- ebitmap_destroy(&cache->data.mls_label.level[0].cat);
- break;
- }
- kfree(data);
-}
-
/**
* security_netlbl_cache_add - Add an entry to the NetLabel cache
* @secattr: the NetLabel packet security attributes
- * @ctx: the SELinux context
+ * @sid: the SELinux SID
*
* Description:
* Attempt to cache the context in @ctx, which was derived from the packet in
@@ -2452,60 +2560,46 @@ static void security_netlbl_cache_free(const void *data)
*
*/
static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
- struct context *ctx)
+ u32 sid)
{
- struct selinux_netlbl_cache *cache = NULL;
+ u32 *sid_cache;
- secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
- if (secattr->cache == NULL)
- return;
-
- cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
- if (cache == NULL)
+ sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC);
+ if (sid_cache == NULL)
return;
-
- cache->type = NETLBL_CACHE_T_MLS;
- if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
- &ctx->range.level[0].cat) != 0) {
- kfree(cache);
+ secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
+ if (secattr->cache == NULL) {
+ kfree(sid_cache);
return;
}
- cache->data.mls_label.level[1].cat.highbit =
- cache->data.mls_label.level[0].cat.highbit;
- cache->data.mls_label.level[1].cat.node =
- cache->data.mls_label.level[0].cat.node;
- cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
- cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
- secattr->cache->free = security_netlbl_cache_free;
- secattr->cache->data = (void *)cache;
+ *sid_cache = sid;
+ secattr->cache->free = kfree;
+ secattr->cache->data = sid_cache;
secattr->flags |= NETLBL_SECATTR_CACHE;
}
/**
* security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
* @secattr: the NetLabel packet security attributes
- * @base_sid: the SELinux SID to use as a context for MLS only attributes
* @sid: the SELinux SID
*
* Description:
* Convert the given NetLabel security attributes in @secattr into a
* SELinux SID. If the @secattr field does not contain a full SELinux
- * SID/context then use the context in @base_sid as the foundation. If
- * possibile the 'cache' field of @secattr is set and the CACHE flag is set;
- * this is to allow the @secattr to be used by NetLabel to cache the secattr to
- * SID conversion for future lookups. Returns zero on success, negative
- * values on failure.
+ * SID/context then use SECINITSID_NETMSG as the foundation. If possibile the
+ * 'cache' field of @secattr is set and the CACHE flag is set; this is to
+ * allow the @secattr to be used by NetLabel to cache the secattr to SID
+ * conversion for future lookups. Returns zero on success, negative values on
+ * failure.
*
*/
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
- u32 base_sid,
u32 *sid)
{
int rc = -EIDRM;
struct context *ctx;
struct context ctx_new;
- struct selinux_netlbl_cache *cache;
if (!ss_initialized) {
*sid = SECSID_NULL;
@@ -2515,40 +2609,13 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
POLICY_RDLOCK;
if (secattr->flags & NETLBL_SECATTR_CACHE) {
- cache = NETLBL_CACHE(secattr->cache->data);
- switch (cache->type) {
- case NETLBL_CACHE_T_SID:
- *sid = cache->data.sid;
- rc = 0;
- break;
- case NETLBL_CACHE_T_MLS:
- ctx = sidtab_search(&sidtab, base_sid);
- if (ctx == NULL)
- goto netlbl_secattr_to_sid_return;
-
- ctx_new.user = ctx->user;
- ctx_new.role = ctx->role;
- ctx_new.type = ctx->type;
- ctx_new.range.level[0].sens =
- cache->data.mls_label.level[0].sens;
- ctx_new.range.level[0].cat.highbit =
- cache->data.mls_label.level[0].cat.highbit;
- ctx_new.range.level[0].cat.node =
- cache->data.mls_label.level[0].cat.node;
- ctx_new.range.level[1].sens =
- cache->data.mls_label.level[1].sens;
- ctx_new.range.level[1].cat.highbit =
- cache->data.mls_label.level[1].cat.highbit;
- ctx_new.range.level[1].cat.node =
- cache->data.mls_label.level[1].cat.node;
-
- rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
- break;
- default:
- goto netlbl_secattr_to_sid_return;
- }
+ *sid = *(u32 *)secattr->cache->data;
+ rc = 0;
+ } else if (secattr->flags & NETLBL_SECATTR_SECID) {
+ *sid = secattr->attr.secid;
+ rc = 0;
} else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
- ctx = sidtab_search(&sidtab, base_sid);
+ ctx = sidtab_search(&sidtab, SECINITSID_NETMSG);
if (ctx == NULL)
goto netlbl_secattr_to_sid_return;
@@ -2558,7 +2625,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
mls_import_netlbl_lvl(&ctx_new, secattr);
if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
- secattr->mls_cat) != 0)
+ secattr->attr.mls.cat) != 0)
goto netlbl_secattr_to_sid_return;
ctx_new.range.level[1].cat.highbit =
ctx_new.range.level[0].cat.highbit;
@@ -2575,7 +2642,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
if (rc != 0)
goto netlbl_secattr_to_sid_return_cleanup;
- security_netlbl_cache_add(secattr, &ctx_new);
+ security_netlbl_cache_add(secattr, *sid);
ebitmap_destroy(&ctx_new.range.level[0].cat);
} else {
@@ -2606,8 +2673,6 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
int rc = -ENOENT;
struct context *ctx;
- netlbl_secattr_init(secattr);
-
if (!ss_initialized)
return 0;
@@ -2628,7 +2693,6 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
netlbl_sid_to_secattr_failure:
POLICY_RDUNLOCK;
- netlbl_secattr_destroy(secattr);
return rc;
}
#endif /* CONFIG_NETLABEL */
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 36a191e..7e15820 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -46,11 +46,14 @@
#include <net/checksum.h>
#include <net/udp.h>
#include <asm/semaphore.h>
+#include <asm/atomic.h>
#include "avc.h"
#include "objsec.h"
#include "xfrm.h"
+/* Labeled XFRM instance counter */
+atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
/*
* Returns true if an LSM/SELinux context
@@ -211,26 +214,27 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
return -EINVAL;
- if (uctx->ctx_len >= PAGE_SIZE)
+ str_len = uctx->ctx_len;
+ if (str_len >= PAGE_SIZE)
return -ENOMEM;
*ctxp = ctx = kmalloc(sizeof(*ctx) +
- uctx->ctx_len + 1,
+ str_len + 1,
GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->ctx_doi = uctx->ctx_doi;
- ctx->ctx_len = uctx->ctx_len;
+ ctx->ctx_len = str_len;
ctx->ctx_alg = uctx->ctx_alg;
memcpy(ctx->ctx_str,
uctx+1,
- ctx->ctx_len);
- ctx->ctx_str[ctx->ctx_len] = 0;
+ str_len);
+ ctx->ctx_str[str_len] = 0;
rc = security_context_to_sid(ctx->ctx_str,
- ctx->ctx_len,
+ str_len,
&ctx->ctx_sid);
if (rc)
@@ -292,6 +296,9 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp,
BUG_ON(!uctx);
err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, 0);
+ if (err == 0)
+ atomic_inc(&selinux_xfrm_refcount);
+
return err;
}
@@ -339,10 +346,13 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
struct xfrm_sec_ctx *ctx = xp->security;
int rc = 0;
- if (ctx)
+ if (ctx) {
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
+ if (rc == 0)
+ atomic_dec(&selinux_xfrm_refcount);
+ }
return rc;
}
@@ -359,6 +369,8 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct
BUG_ON(!x);
err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
+ if (err == 0)
+ atomic_inc(&selinux_xfrm_refcount);
return err;
}
@@ -381,10 +393,13 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
struct xfrm_sec_ctx *ctx = x->security;
int rc = 0;
- if (ctx)
+ if (ctx) {
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
+ if (rc == 0)
+ atomic_dec(&selinux_xfrm_refcount);
+ }
return rc;
}
OpenPOWER on IntegriCloud