summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-10-28 16:26:12 +0100
committerIngo Molnar <mingo@elte.hu>2008-10-28 16:26:12 +0100
commit7a9787e1eba95a166265e6a260cf30af04ef0a99 (patch)
treee730a4565e0318140d2fbd2f0415d18a339d7336 /security
parent41b9eb264c8407655db57b60b4457fe1b2ec9977 (diff)
parent0173a3265b228da319ceb9c1ec6a5682fd1b2d92 (diff)
downloadblackbird-op-linux-7a9787e1eba95a166265e6a260cf30af04ef0a99.tar.gz
blackbird-op-linux-7a9787e1eba95a166265e6a260cf30af04ef0a99.zip
Merge commit 'v2.6.28-rc2' into x86/pci-ioapic-boot-irq-quirks
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig11
-rw-r--r--security/Makefile3
-rw-r--r--security/capability.c6
-rw-r--r--security/commoncap.c134
-rw-r--r--security/device_cgroup.c216
-rw-r--r--security/inode.c36
-rw-r--r--security/root_plug.c3
-rw-r--r--security/security.c23
-rw-r--r--security/selinux/Kconfig3
-rw-r--r--security/selinux/avc.c2
-rw-r--r--security/selinux/hooks.c390
-rw-r--r--security/selinux/include/avc.h4
-rw-r--r--security/selinux/include/netlabel.h44
-rw-r--r--security/selinux/include/objsec.h9
-rw-r--r--security/selinux/include/security.h15
-rw-r--r--security/selinux/netlabel.c280
-rw-r--r--security/selinux/ss/avtab.c8
-rw-r--r--security/selinux/ss/conditional.c18
-rw-r--r--security/selinux/ss/conditional.h2
-rw-r--r--security/selinux/ss/ebitmap.c4
-rw-r--r--security/selinux/ss/hashtab.c6
-rw-r--r--security/selinux/ss/mls.c14
-rw-r--r--security/selinux/ss/policydb.c225
-rw-r--r--security/selinux/ss/policydb.h5
-rw-r--r--security/selinux/ss/services.c210
-rw-r--r--security/selinux/ss/sidtab.c12
-rw-r--r--security/smack/smack.h1
-rw-r--r--security/smack/smack_access.c10
-rw-r--r--security/smack/smack_lsm.c57
-rw-r--r--security/smack/smackfs.c96
30 files changed, 1332 insertions, 515 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 62ed4717d334..d9f47ce7e207 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -51,6 +51,14 @@ config SECURITY
If you are unsure how to answer this question, answer N.
+config SECURITYFS
+ bool "Enable the securityfs filesystem"
+ help
+ This will build the securityfs filesystem. It is currently used by
+ the TPM bios character driver. It is not used by SELinux or SMACK.
+
+ If you are unsure how to answer this question, answer N.
+
config SECURITY_NETWORK
bool "Socket and Networking Security Hooks"
depends on SECURITY
@@ -74,8 +82,7 @@ config SECURITY_NETWORK_XFRM
If you are unsure how to answer this question, answer N.
config SECURITY_FILE_CAPABILITIES
- bool "File POSIX Capabilities (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ bool "File POSIX Capabilities"
default n
help
This enables filesystem capabilities, allowing you to give
diff --git a/security/Makefile b/security/Makefile
index f65426099aa6..c05c127fff9a 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -10,7 +10,8 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
obj-y += commoncap.o
# Object file lists
-obj-$(CONFIG_SECURITY) += security.o capability.o inode.o
+obj-$(CONFIG_SECURITY) += security.o capability.o
+obj-$(CONFIG_SECURITYFS) += inode.o
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
diff --git a/security/capability.c b/security/capability.c
index 5b01c0b02422..245874819036 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -211,8 +211,7 @@ static int cap_inode_follow_link(struct dentry *dentry,
return 0;
}
-static int cap_inode_permission(struct inode *inode, int mask,
- struct nameidata *nd)
+static int cap_inode_permission(struct inode *inode, int mask)
{
return 0;
}
@@ -812,7 +811,8 @@ struct security_operations default_security_ops = {
void security_fixup_ops(struct security_operations *ops)
{
- set_to_cap_if_null(ops, ptrace);
+ set_to_cap_if_null(ops, ptrace_may_access);
+ set_to_cap_if_null(ops, ptrace_traceme);
set_to_cap_if_null(ops, capget);
set_to_cap_if_null(ops, capset_check);
set_to_cap_if_null(ops, capset_set);
diff --git a/security/commoncap.c b/security/commoncap.c
index 0b6537a3672d..399bfdb9e2da 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -63,14 +63,24 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
return 0;
}
-int cap_ptrace (struct task_struct *parent, struct task_struct *child,
- unsigned int mode)
+int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
{
/* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
- if (!cap_issubset(child->cap_permitted, parent->cap_permitted) &&
- !__capable(parent, CAP_SYS_PTRACE))
- return -EPERM;
- return 0;
+ if (cap_issubset(child->cap_permitted, current->cap_permitted))
+ return 0;
+ if (capable(CAP_SYS_PTRACE))
+ return 0;
+ return -EPERM;
+}
+
+int cap_ptrace_traceme(struct task_struct *parent)
+{
+ /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
+ if (cap_issubset(current->cap_permitted, parent->cap_permitted))
+ return 0;
+ if (has_capability(parent, CAP_SYS_PTRACE))
+ return 0;
+ return -EPERM;
}
int cap_capget (struct task_struct *target, kernel_cap_t *effective,
@@ -162,8 +172,7 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
static inline void bprm_clear_caps(struct linux_binprm *bprm)
{
- cap_clear(bprm->cap_inheritable);
- cap_clear(bprm->cap_permitted);
+ cap_clear(bprm->cap_post_exec_permitted);
bprm->cap_effective = false;
}
@@ -198,6 +207,7 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
{
__u32 magic_etc;
unsigned tocopy, i;
+ int ret;
if (size < sizeof(magic_etc))
return -EINVAL;
@@ -225,19 +235,40 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
bprm->cap_effective = false;
}
- for (i = 0; i < tocopy; ++i) {
- bprm->cap_permitted.cap[i] =
- le32_to_cpu(caps->data[i].permitted);
- bprm->cap_inheritable.cap[i] =
- le32_to_cpu(caps->data[i].inheritable);
- }
- while (i < VFS_CAP_U32) {
- bprm->cap_permitted.cap[i] = 0;
- bprm->cap_inheritable.cap[i] = 0;
- i++;
+ ret = 0;
+
+ CAP_FOR_EACH_U32(i) {
+ __u32 value_cpu;
+
+ if (i >= tocopy) {
+ /*
+ * Legacy capability sets have no upper bits
+ */
+ bprm->cap_post_exec_permitted.cap[i] = 0;
+ continue;
+ }
+ /*
+ * pP' = (X & fP) | (pI & fI)
+ */
+ value_cpu = le32_to_cpu(caps->data[i].permitted);
+ bprm->cap_post_exec_permitted.cap[i] =
+ (current->cap_bset.cap[i] & value_cpu) |
+ (current->cap_inheritable.cap[i] &
+ le32_to_cpu(caps->data[i].inheritable));
+ if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
+ /*
+ * insufficient to execute correctly
+ */
+ ret = -EPERM;
+ }
}
- return 0;
+ /*
+ * For legacy apps, with no internal support for recognizing they
+ * do not have enough capabilities, we return an error if they are
+ * missing some "forced" (aka file-permitted) capabilities.
+ */
+ return bprm->cap_effective ? ret : 0;
}
/* Locate any VFS capabilities: */
@@ -269,9 +300,9 @@ static int get_file_caps(struct linux_binprm *bprm)
goto out;
rc = cap_from_disk(&vcaps, bprm, rc);
- if (rc)
+ if (rc == -EINVAL)
printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
- __func__, rc, bprm->filename);
+ __func__, rc, bprm->filename);
out:
dput(dentry);
@@ -304,25 +335,24 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
int ret;
ret = get_file_caps(bprm);
- if (ret)
- printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n",
- __func__, ret, bprm->filename);
-
- /* To support inheritance of root-permissions and suid-root
- * executables under compatibility mode, we raise all three
- * capability sets for the file.
- *
- * If only the real uid is 0, we only raise the inheritable
- * and permitted sets of the executable file.
- */
- if (!issecure (SECURE_NOROOT)) {
+ if (!issecure(SECURE_NOROOT)) {
+ /*
+ * To support inheritance of root-permissions and suid-root
+ * executables under compatibility mode, we override the
+ * capability sets for the file.
+ *
+ * If only the real uid is 0, we do not set the effective
+ * bit.
+ */
if (bprm->e_uid == 0 || current->uid == 0) {
- cap_set_full (bprm->cap_inheritable);
- cap_set_full (bprm->cap_permitted);
+ /* pP' = (cap_bset & ~0) | (pI & ~0) */
+ bprm->cap_post_exec_permitted = cap_combine(
+ current->cap_bset, current->cap_inheritable
+ );
+ bprm->cap_effective = (bprm->e_uid == 0);
+ ret = 0;
}
- if (bprm->e_uid == 0)
- bprm->cap_effective = true;
}
return ret;
@@ -330,17 +360,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
{
- /* Derived from fs/exec.c:compute_creds. */
- kernel_cap_t new_permitted, working;
-
- new_permitted = cap_intersect(bprm->cap_permitted,
- current->cap_bset);
- working = cap_intersect(bprm->cap_inheritable,
- current->cap_inheritable);
- new_permitted = cap_combine(new_permitted, working);
-
if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
- !cap_issubset (new_permitted, current->cap_permitted)) {
+ !cap_issubset(bprm->cap_post_exec_permitted,
+ current->cap_permitted)) {
set_dumpable(current->mm, suid_dumpable);
current->pdeath_signal = 0;
@@ -350,9 +372,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
bprm->e_gid = current->gid;
}
if (cap_limit_ptraced_target()) {
- new_permitted =
- cap_intersect(new_permitted,
- current->cap_permitted);
+ bprm->cap_post_exec_permitted = cap_intersect(
+ bprm->cap_post_exec_permitted,
+ current->cap_permitted);
}
}
}
@@ -364,9 +386,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
* in the init_task struct. Thus we skip the usual
* capability rules */
if (!is_global_init(current)) {
- current->cap_permitted = new_permitted;
+ current->cap_permitted = bprm->cap_post_exec_permitted;
if (bprm->cap_effective)
- current->cap_effective = new_permitted;
+ current->cap_effective = bprm->cap_post_exec_permitted;
else
cap_clear(current->cap_effective);
}
@@ -381,9 +403,7 @@ int cap_bprm_secureexec (struct linux_binprm *bprm)
if (current->uid != 0) {
if (bprm->cap_effective)
return 1;
- if (!cap_isclear(bprm->cap_permitted))
- return 1;
- if (!cap_isclear(bprm->cap_inheritable))
+ if (!cap_isclear(bprm->cap_post_exec_permitted))
return 1;
}
@@ -521,10 +541,10 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
* yet with increased caps.
* So we check for increased caps on the target process.
*/
-static inline int cap_safe_nice(struct task_struct *p)
+static int cap_safe_nice(struct task_struct *p)
{
if (!cap_issubset(p->cap_permitted, current->cap_permitted) &&
- !__capable(current, CAP_SYS_NICE))
+ !capable(CAP_SYS_NICE))
return -EPERM;
return 0;
}
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index ddd92cec78ed..5ba78701adc3 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -1,5 +1,5 @@
/*
- * dev_cgroup.c - device cgroup subsystem
+ * device_cgroup.c - device cgroup subsystem
*
* Copyright 2007 IBM Corp
*/
@@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
+#include <linux/rcupdate.h>
#define ACC_MKNOD 1
#define ACC_READ 2
@@ -22,18 +23,8 @@
/*
* whitelist locking rules:
- * cgroup_lock() cannot be taken under dev_cgroup->lock.
- * dev_cgroup->lock can be taken with or without cgroup_lock().
- *
- * modifications always require cgroup_lock
- * modifications to a list which is visible require the
- * dev_cgroup->lock *and* cgroup_lock()
- * walking the list requires dev_cgroup->lock or cgroup_lock().
- *
- * reasoning: dev_whitelist_copy() needs to kmalloc, so needs
- * a mutex, which the cgroup_lock() is. Since modifying
- * a visible list requires both locks, either lock can be
- * taken for walking the list.
+ * hold cgroup_lock() for update/read.
+ * hold rcu_read_lock() for read.
*/
struct dev_whitelist_item {
@@ -41,12 +32,12 @@ struct dev_whitelist_item {
short type;
short access;
struct list_head list;
+ struct rcu_head rcu;
};
struct dev_cgroup {
struct cgroup_subsys_state css;
struct list_head whitelist;
- spinlock_t lock;
};
static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
@@ -59,6 +50,11 @@ static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id));
}
+static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
+{
+ return css_to_devcgroup(task_subsys_state(task, devices_subsys_id));
+}
+
struct cgroup_subsys devices_subsys;
static int devcgroup_can_attach(struct cgroup_subsys *ss,
@@ -78,13 +74,9 @@ static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
struct dev_whitelist_item *wh, *tmp, *new;
list_for_each_entry(wh, orig, list) {
- new = kmalloc(sizeof(*wh), GFP_KERNEL);
+ new = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
if (!new)
goto free_and_exit;
- new->major = wh->major;
- new->minor = wh->minor;
- new->type = wh->type;
- new->access = wh->access;
list_add_tail(&new->list, dest);
}
@@ -101,19 +93,16 @@ free_and_exit:
/* Stupid prototype - don't bother combining existing entries */
/*
* called under cgroup_lock()
- * since the list is visible to other tasks, we need the spinlock also
*/
static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
struct dev_whitelist_item *wh)
{
struct dev_whitelist_item *whcopy, *walk;
- whcopy = kmalloc(sizeof(*whcopy), GFP_KERNEL);
+ whcopy = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
if (!whcopy)
return -ENOMEM;
- memcpy(whcopy, wh, sizeof(*whcopy));
- spin_lock(&dev_cgroup->lock);
list_for_each_entry(walk, &dev_cgroup->whitelist, list) {
if (walk->type != wh->type)
continue;
@@ -128,21 +117,26 @@ static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
}
if (whcopy != NULL)
- list_add_tail(&whcopy->list, &dev_cgroup->whitelist);
- spin_unlock(&dev_cgroup->lock);
+ list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist);
return 0;
}
+static void whitelist_item_free(struct rcu_head *rcu)
+{
+ struct dev_whitelist_item *item;
+
+ item = container_of(rcu, struct dev_whitelist_item, rcu);
+ kfree(item);
+}
+
/*
* called under cgroup_lock()
- * since the list is visible to other tasks, we need the spinlock also
*/
static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
struct dev_whitelist_item *wh)
{
struct dev_whitelist_item *walk, *tmp;
- spin_lock(&dev_cgroup->lock);
list_for_each_entry_safe(walk, tmp, &dev_cgroup->whitelist, list) {
if (walk->type == DEV_ALL)
goto remove;
@@ -156,11 +150,10 @@ static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
remove:
walk->access &= ~wh->access;
if (!walk->access) {
- list_del(&walk->list);
- kfree(walk);
+ list_del_rcu(&walk->list);
+ call_rcu(&walk->rcu, whitelist_item_free);
}
}
- spin_unlock(&dev_cgroup->lock);
}
/*
@@ -188,7 +181,7 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
}
wh->minor = wh->major = ~0;
wh->type = DEV_ALL;
- wh->access = ACC_MKNOD | ACC_READ | ACC_WRITE;
+ wh->access = ACC_MASK;
list_add(&wh->list, &dev_cgroup->whitelist);
} else {
parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
@@ -200,7 +193,6 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
}
}
- spin_lock_init(&dev_cgroup->lock);
return &dev_cgroup->css;
}
@@ -250,11 +242,10 @@ static char type_to_char(short type)
static void set_majmin(char *str, unsigned m)
{
- memset(str, 0, MAJMINLEN);
if (m == ~0)
- sprintf(str, "*");
+ strcpy(str, "*");
else
- snprintf(str, MAJMINLEN, "%u", m);
+ sprintf(str, "%u", m);
}
static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
@@ -264,15 +255,15 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
struct dev_whitelist_item *wh;
char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
- spin_lock(&devcgroup->lock);
- list_for_each_entry(wh, &devcgroup->whitelist, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) {
set_access(acc, wh->access);
set_majmin(maj, wh->major);
set_majmin(min, wh->minor);
seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
maj, min, acc);
}
- spin_unlock(&devcgroup->lock);
+ rcu_read_unlock();
return 0;
}
@@ -312,20 +303,16 @@ static int may_access_whitelist(struct dev_cgroup *c,
* when adding a new allow rule to a device whitelist, the rule
* must be allowed in the parent device
*/
-static int parent_has_perm(struct cgroup *childcg,
+static int parent_has_perm(struct dev_cgroup *childcg,
struct dev_whitelist_item *wh)
{
- struct cgroup *pcg = childcg->parent;
+ struct cgroup *pcg = childcg->css.cgroup->parent;
struct dev_cgroup *parent;
- int ret;
if (!pcg)
return 1;
parent = cgroup_to_devcgroup(pcg);
- spin_lock(&parent->lock);
- ret = may_access_whitelist(parent, wh);
- spin_unlock(&parent->lock);
- return ret;
+ return may_access_whitelist(parent, wh);
}
/*
@@ -341,40 +328,17 @@ static int parent_has_perm(struct cgroup *childcg,
* new access is only allowed if you're in the top-level cgroup, or your
* parent cgroup has the access you're asking for.
*/
-static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
- struct file *file, const char __user *userbuf,
- size_t nbytes, loff_t *ppos)
+static int devcgroup_update_access(struct dev_cgroup *devcgroup,
+ int filetype, const char *buffer)
{
- struct cgroup *cur_cgroup;
- struct dev_cgroup *devcgroup, *cur_devcgroup;
- int filetype = cft->private;
- char *buffer, *b;
- int retval = 0, count;
+ const char *b;
+ char *endp;
+ int count;
struct dev_whitelist_item wh;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- devcgroup = cgroup_to_devcgroup(cgroup);
- cur_cgroup = task_cgroup(current, devices_subsys.subsys_id);
- cur_devcgroup = cgroup_to_devcgroup(cur_cgroup);
-
- buffer = kmalloc(nbytes+1, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- if (copy_from_user(buffer, userbuf, nbytes)) {
- retval = -EFAULT;
- goto out1;
- }
- buffer[nbytes] = 0; /* nul-terminate */
-
- cgroup_lock();
- if (cgroup_is_removed(cgroup)) {
- retval = -ENODEV;
- goto out2;
- }
-
memset(&wh, 0, sizeof(wh));
b = buffer;
@@ -392,32 +356,23 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
wh.type = DEV_CHAR;
break;
default:
- retval = -EINVAL;
- goto out2;
+ return -EINVAL;
}
b++;
- if (!isspace(*b)) {
- retval = -EINVAL;
- goto out2;
- }
+ if (!isspace(*b))
+ return -EINVAL;
b++;
if (*b == '*') {
wh.major = ~0;
b++;
} else if (isdigit(*b)) {
- wh.major = 0;
- while (isdigit(*b)) {
- wh.major = wh.major*10+(*b-'0');
- b++;
- }
+ wh.major = simple_strtoul(b, &endp, 10);
+ b = endp;
} else {
- retval = -EINVAL;
- goto out2;
- }
- if (*b != ':') {
- retval = -EINVAL;
- goto out2;
+ return -EINVAL;
}
+ if (*b != ':')
+ return -EINVAL;
b++;
/* read minor */
@@ -425,19 +380,13 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
wh.minor = ~0;
b++;
} else if (isdigit(*b)) {
- wh.minor = 0;
- while (isdigit(*b)) {
- wh.minor = wh.minor*10+(*b-'0');
- b++;
- }
+ wh.minor = simple_strtoul(b, &endp, 10);
+ b = endp;
} else {
- retval = -EINVAL;
- goto out2;
- }
- if (!isspace(*b)) {
- retval = -EINVAL;
- goto out2;
+ return -EINVAL;
}
+ if (!isspace(*b))
+ return -EINVAL;
for (b++, count = 0; count < 3; count++, b++) {
switch (*b) {
case 'r':
@@ -454,47 +403,46 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft,
count = 3;
break;
default:
- retval = -EINVAL;
- goto out2;
+ return -EINVAL;
}
}
handle:
- retval = 0;
switch (filetype) {
case DEVCG_ALLOW:
- if (!parent_has_perm(cgroup, &wh))
- retval = -EPERM;
- else
- retval = dev_whitelist_add(devcgroup, &wh);
- break;
+ if (!parent_has_perm(devcgroup, &wh))
+ return -EPERM;
+ return dev_whitelist_add(devcgroup, &wh);
case DEVCG_DENY:
dev_whitelist_rm(devcgroup, &wh);
break;
default:
- retval = -EINVAL;
- goto out2;
+ return -EINVAL;
}
+ return 0;
+}
- if (retval == 0)
- retval = nbytes;
-
-out2:
+static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
+ const char *buffer)
+{
+ int retval;
+ if (!cgroup_lock_live_group(cgrp))
+ return -ENODEV;
+ retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
+ cft->private, buffer);
cgroup_unlock();
-out1:
- kfree(buffer);
return retval;
}
static struct cftype dev_cgroup_files[] = {
{
.name = "allow",
- .write = devcgroup_access_write,
+ .write_string = devcgroup_access_write,
.private = DEVCG_ALLOW,
},
{
.name = "deny",
- .write = devcgroup_access_write,
+ .write_string = devcgroup_access_write,
.private = DEVCG_DENY,
},
{
@@ -530,13 +478,12 @@ int devcgroup_inode_permission(struct inode *inode, int mask)
return 0;
if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
return 0;
- dev_cgroup = css_to_devcgroup(task_subsys_state(current,
- devices_subsys_id));
- if (!dev_cgroup)
- return 0;
- spin_lock(&dev_cgroup->lock);
- list_for_each_entry(wh, &dev_cgroup->whitelist, list) {
+ rcu_read_lock();
+
+ dev_cgroup = task_devcgroup(current);
+
+ list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
if (wh->type & DEV_ALL)
goto acc_check;
if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
@@ -552,10 +499,11 @@ acc_check:
continue;
if ((mask & MAY_READ) && !(wh->access & ACC_READ))
continue;
- spin_unlock(&dev_cgroup->lock);
+ rcu_read_unlock();
return 0;
}
- spin_unlock(&dev_cgroup->lock);
+
+ rcu_read_unlock();
return -EPERM;
}
@@ -565,12 +513,10 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
struct dev_cgroup *dev_cgroup;
struct dev_whitelist_item *wh;
- dev_cgroup = css_to_devcgroup(task_subsys_state(current,
- devices_subsys_id));
- if (!dev_cgroup)
- return 0;
+ rcu_read_lock();
+
+ dev_cgroup = task_devcgroup(current);
- spin_lock(&dev_cgroup->lock);
list_for_each_entry(wh, &dev_cgroup->whitelist, list) {
if (wh->type & DEV_ALL)
goto acc_check;
@@ -585,9 +531,11 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
acc_check:
if (!(wh->access & ACC_MKNOD))
continue;
- spin_unlock(&dev_cgroup->lock);
+ rcu_read_unlock();
return 0;
}
- spin_unlock(&dev_cgroup->lock);
+
+ rcu_read_unlock();
+
return -EPERM;
}
diff --git a/security/inode.c b/security/inode.c
index acc6cf0d7900..efea5a605466 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -20,8 +20,7 @@
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/security.h>
-
-#define SECURITYFS_MAGIC 0x73636673
+#include <linux/magic.h>
static struct vfsmount *mount;
static int mount_count;
@@ -190,7 +189,7 @@ static int create_by_name(const char *name, mode_t mode,
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
- * directory dentry if set. If this paramater is NULL, then the
+ * directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the securityfs filesystem.
* @data: a pointer to something that the caller will want to get to later
* on. The inode.i_private pointer will point to this value on
@@ -199,18 +198,18 @@ static int create_by_name(const char *name, mode_t mode,
* this file.
*
* This is the basic "create a file" function for securityfs. It allows for a
- * wide range of flexibility in createing a file, or a directory (if you
+ * wide range of flexibility in creating a file, or a directory (if you
* want to create a directory, the securityfs_create_dir() function is
- * recommended to be used instead.)
+ * recommended to be used instead).
*
- * This function will return a pointer to a dentry if it succeeds. This
+ * This function returns a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here.) If an error occurs, NULL will be returned.
+ * you are responsible here). If an error occurs, %NULL is returned.
*
- * If securityfs is not enabled in the kernel, the value -ENODEV will be
+ * If securityfs is not enabled in the kernel, the value %-ENODEV is
* returned. It is not wise to check for this value, but rather, check for
- * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *securityfs_create_file(const char *name, mode_t mode,
@@ -252,19 +251,19 @@ EXPORT_SYMBOL_GPL(securityfs_create_file);
* @name: a pointer to a string containing the name of the directory to
* create.
* @parent: a pointer to the parent dentry for this file. This should be a
- * directory dentry if set. If this paramater is NULL, then the
+ * directory dentry if set. If this parameter is %NULL, then the
* directory will be created in the root of the securityfs filesystem.
*
- * This function creates a directory in securityfs with the given name.
+ * This function creates a directory in securityfs with the given @name.
*
- * This function will return a pointer to a dentry if it succeeds. This
+ * This function returns a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here.) If an error occurs, NULL will be returned.
+ * you are responsible here). If an error occurs, %NULL will be returned.
*
- * If securityfs is not enabled in the kernel, the value -ENODEV will be
+ * If securityfs is not enabled in the kernel, the value %-ENODEV is
* returned. It is not wise to check for this value, but rather, check for
- * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *securityfs_create_dir(const char *name, struct dentry *parent)
@@ -278,16 +277,15 @@ EXPORT_SYMBOL_GPL(securityfs_create_dir);
/**
* securityfs_remove - removes a file or directory from the securityfs filesystem
*
- * @dentry: a pointer to a the dentry of the file or directory to be
- * removed.
+ * @dentry: a pointer to a the dentry of the file or directory to be removed.
*
* This function removes a file or directory in securityfs that was previously
* created with a call to another securityfs function (like
* securityfs_create_file() or variants thereof.)
*
* This function is required to be called in order for the file to be
- * removed, no automatic cleanup of files will happen when a module is
- * removed, you are responsible here.
+ * removed. No automatic cleanup of files will happen when a module is
+ * removed; you are responsible here.
*/
void securityfs_remove(struct dentry *dentry)
{
diff --git a/security/root_plug.c b/security/root_plug.c
index be0ebec2580b..c3f68b5b372d 100644
--- a/security/root_plug.c
+++ b/security/root_plug.c
@@ -72,7 +72,8 @@ static int rootplug_bprm_check_security (struct linux_binprm *bprm)
static struct security_operations rootplug_security_ops = {
/* Use the capability functions for some of the hooks */
- .ptrace = cap_ptrace,
+ .ptrace_may_access = cap_ptrace_may_access,
+ .ptrace_traceme = cap_ptrace_traceme,
.capget = cap_capget,
.capset_check = cap_capset_check,
.capset_set = cap_capset_set,
diff --git a/security/security.c b/security/security.c
index 59f23b5918b3..255b08559b2b 100644
--- a/security/security.c
+++ b/security/security.c
@@ -82,8 +82,8 @@ __setup("security=", choose_lsm);
*
* Return true if:
* -The passed LSM is the one chosen by user at boot time,
- * -or user didsn't specify a specific LSM and we're the first to ask
- * for registeration permissoin,
+ * -or user didn't specify a specific LSM and we're the first to ask
+ * for registration permission,
* -or the passed LSM is currently loaded.
* Otherwise, return false.
*/
@@ -101,13 +101,13 @@ int __init security_module_enable(struct security_operations *ops)
* register_security - registers a security framework with the kernel
* @ops: a pointer to the struct security_options that is to be registered
*
- * This function is to allow a security module to register itself with the
+ * This function allows a security module to register itself with the
* kernel security subsystem. Some rudimentary checking is done on the @ops
* value passed to this function. You'll need to check first if your LSM
* is allowed to register its @ops by calling security_module_enable(@ops).
*
* If there is already a security module registered with the kernel,
- * an error will be returned. Otherwise 0 is returned on success.
+ * an error will be returned. Otherwise %0 is returned on success.
*/
int register_security(struct security_operations *ops)
{
@@ -127,10 +127,14 @@ int register_security(struct security_operations *ops)
/* Security operations */
-int security_ptrace(struct task_struct *parent, struct task_struct *child,
- unsigned int mode)
+int security_ptrace_may_access(struct task_struct *child, unsigned int mode)
{
- return security_ops->ptrace(parent, child, mode);
+ return security_ops->ptrace_may_access(child, mode);
+}
+
+int security_ptrace_traceme(struct task_struct *parent)
+{
+ return security_ops->ptrace_traceme(parent);
}
int security_capget(struct task_struct *target,
@@ -429,11 +433,11 @@ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
return security_ops->inode_follow_link(dentry, nd);
}
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+int security_inode_permission(struct inode *inode, int mask)
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return security_ops->inode_permission(inode, mask, nd);
+ return security_ops->inode_permission(inode, mask);
}
int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
@@ -442,6 +446,7 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
return 0;
return security_ops->inode_setattr(dentry, attr);
}
+EXPORT_SYMBOL_GPL(security_inode_setattr);
int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index a436d1cfa88b..26301dd651d3 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -6,9 +6,6 @@ config SECURITY_SELINUX
help
This selects NSA Security-Enhanced Linux (SELinux).
You will also need a policy configuration and a labeled filesystem.
- You can obtain the policy compiler (checkpolicy), the utility for
- labeling filesystems (setfiles), and an example policy configuration
- from <http://www.nsa.gov/selinux/>.
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_BOOTPARAM
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 114b4b4c97b2..cb30c7e350b3 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
* @tclass: target security class
* @av: access vector
*/
-static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
{
const char **common_pts = NULL;
u32 common_base = 0;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 63f131fc42e4..3e3fde7c1d2b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -25,7 +25,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/security.h>
@@ -75,6 +75,7 @@
#include <linux/string.h>
#include <linux/selinux.h>
#include <linux/mutex.h>
+#include <linux/posix-timers.h>
#include "avc.h"
#include "objsec.h"
@@ -291,6 +292,7 @@ static void sk_free_security(struct sock *sk)
struct sk_security_struct *ssec = sk->sk_security;
sk->sk_security = NULL;
+ selinux_netlbl_sk_security_free(ssec);
kfree(ssec);
}
@@ -324,7 +326,7 @@ enum {
Opt_rootcontext = 4,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_context, CONTEXT_STR "%s"},
{Opt_fscontext, FSCONTEXT_STR "%s"},
{Opt_defcontext, DEFCONTEXT_STR "%s"},
@@ -957,7 +959,8 @@ out_err:
return rc;
}
-void selinux_write_opts(struct seq_file *m, struct security_mnt_opts *opts)
+static void selinux_write_opts(struct seq_file *m,
+ struct security_mnt_opts *opts)
{
int i;
char *prefix;
@@ -998,8 +1001,12 @@ static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
int rc;
rc = selinux_get_mnt_opts(sb, &opts);
- if (rc)
+ if (rc) {
+ /* before policy load we may get EINVAL, don't show anything */
+ if (rc == -EINVAL)
+ rc = 0;
return rc;
+ }
selinux_write_opts(m, &opts);
@@ -1286,7 +1293,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/* Default to the fs superblock SID. */
isec->sid = sbsec->sid;
- if (sbsec->proc) {
+ if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
struct proc_inode *proci = PROC_I(inode);
if (proci->pde) {
isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1734,24 +1741,34 @@ static inline u32 file_to_av(struct file *file)
/* Hook functions begin here. */
-static int selinux_ptrace(struct task_struct *parent,
- struct task_struct *child,
- unsigned int mode)
+static int selinux_ptrace_may_access(struct task_struct *child,
+ unsigned int mode)
{
int rc;
- rc = secondary_ops->ptrace(parent, child, mode);
+ rc = secondary_ops->ptrace_may_access(child, mode);
if (rc)
return rc;
if (mode == PTRACE_MODE_READ) {
- struct task_security_struct *tsec = parent->security;
+ struct task_security_struct *tsec = current->security;
struct task_security_struct *csec = child->security;
return avc_has_perm(tsec->sid, csec->sid,
SECCLASS_FILE, FILE__READ, NULL);
}
- return task_has_perm(parent, child, PROCESS__PTRACE);
+ return task_has_perm(current, child, PROCESS__PTRACE);
+}
+
+static int selinux_ptrace_traceme(struct task_struct *parent)
+{
+ int rc;
+
+ rc = secondary_ops->ptrace_traceme(parent);
+ if (rc)
+ return rc;
+
+ return task_has_perm(parent, current, PROCESS__PTRACE);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
@@ -1971,22 +1988,6 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
-/**
- * task_tracer_task - return the task that is tracing the given task
- * @task: task to consider
- *
- * Returns NULL if noone is tracing @task, or the &struct task_struct
- * pointer to its tracer.
- *
- * Must be called under rcu_read_lock().
- */
-static struct task_struct *task_tracer_task(struct task_struct *task)
-{
- if (task->ptrace & PT_PTRACED)
- return rcu_dereference(task->parent);
- return NULL;
-}
-
/* binprm security operations */
static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
@@ -2122,7 +2123,6 @@ static inline void flush_unauthorized_files(struct files_struct *files)
long j = -1;
int drop_tty = 0;
- mutex_lock(&tty_mutex);
tty = get_current_tty();
if (tty) {
file_list_lock();
@@ -2140,8 +2140,8 @@ static inline void flush_unauthorized_files(struct files_struct *files)
}
}
file_list_unlock();
+ tty_kref_put(tty);
}
- mutex_unlock(&tty_mutex);
/* Reset controlling tty. */
if (drop_tty)
no_tty();
@@ -2238,7 +2238,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
u32 ptsid = 0;
rcu_read_lock();
- tracer = task_tracer_task(current);
+ tracer = tracehook_tracer_task(current);
if (likely(tracer != NULL)) {
sec = tracer->security;
ptsid = sec->sid;
@@ -2323,13 +2323,7 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
initrlim = init_task.signal->rlim+i;
rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
}
- if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
- /*
- * This will cause RLIMIT_CPU calculations
- * to be refigured.
- */
- current->it_prof_expires = jiffies_to_cputime(1);
- }
+ update_rlimit_cpu(rlim->rlim_cur);
}
/* Wake up the parent if it is waiting so that it can
@@ -2640,12 +2634,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na
return dentry_has_perm(current, NULL, dentry, FILE__READ);
}
-static int selinux_inode_permission(struct inode *inode, int mask,
- struct nameidata *nd)
+static int selinux_inode_permission(struct inode *inode, int mask)
{
int rc;
- rc = secondary_ops->inode_permission(inode, mask, nd);
+ rc = secondary_ops->inode_permission(inode, mask);
if (rc)
return rc;
@@ -3551,38 +3544,44 @@ out:
#endif /* IPV6 */
static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
- char **addrp, int src, u8 *proto)
+ char **_addrp, int src, u8 *proto)
{
- int ret = 0;
+ char *addrp;
+ int ret;
switch (ad->u.net.family) {
case PF_INET:
ret = selinux_parse_skb_ipv4(skb, ad, proto);
- if (ret || !addrp)
- break;
- *addrp = (char *)(src ? &ad->u.net.v4info.saddr :
- &ad->u.net.v4info.daddr);
- break;
+ if (ret)
+ goto parse_error;
+ addrp = (char *)(src ? &ad->u.net.v4info.saddr :
+ &ad->u.net.v4info.daddr);
+ goto okay;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case PF_INET6:
ret = selinux_parse_skb_ipv6(skb, ad, proto);
- if (ret || !addrp)
- break;
- *addrp = (char *)(src ? &ad->u.net.v6info.saddr :
- &ad->u.net.v6info.daddr);
- break;
+ if (ret)
+ goto parse_error;
+ addrp = (char *)(src ? &ad->u.net.v6info.saddr :
+ &ad->u.net.v6info.daddr);
+ goto okay;
#endif /* IPV6 */
default:
- break;
+ addrp = NULL;
+ goto okay;
}
- if (unlikely(ret))
- printk(KERN_WARNING
- "SELinux: failure in selinux_parse_skb(),"
- " unable to parse packet\n");
-
+parse_error:
+ printk(KERN_WARNING
+ "SELinux: failure in selinux_parse_skb(),"
+ " unable to parse packet\n");
return ret;
+
+okay:
+ if (_addrp)
+ *_addrp = addrp;
+ return 0;
}
/**
@@ -3797,6 +3796,7 @@ out:
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
+ struct sock *sk = sock->sk;
struct inode_security_struct *isec;
int err;
@@ -3810,7 +3810,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
isec = SOCK_INODE(sock)->i_security;
if (isec->sclass == SECCLASS_TCP_SOCKET ||
isec->sclass == SECCLASS_DCCP_SOCKET) {
- struct sock *sk = sock->sk;
struct avc_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
@@ -3844,6 +3843,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
goto out;
}
+ err = selinux_netlbl_socket_connect(sk, address);
+
out:
return err;
}
@@ -4073,20 +4074,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
}
static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
- struct avc_audit_data *ad,
- u16 family, char *addrp)
+ u16 family)
{
int err;
struct sk_security_struct *sksec = sk->sk_security;
u32 peer_sid;
u32 sk_sid = sksec->sid;
+ struct avc_audit_data ad;
+ char *addrp;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = skb->iif;
+ ad.u.net.family = family;
+ err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
+ if (err)
+ return err;
if (selinux_compat_net)
- err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
+ 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);
+ PACKET__RECV, &ad);
if (err)
return err;
@@ -4095,12 +4104,14 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
if (err)
return err;
err = avc_has_perm(sk_sid, peer_sid,
- SECCLASS_PEER, PEER__RECV, ad);
+ SECCLASS_PEER, PEER__RECV, &ad);
+ if (err)
+ selinux_netlbl_err(skb, err, 0);
} else {
- err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
+ err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
if (err)
return err;
- err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
+ err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
}
return err;
@@ -4114,6 +4125,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
u32 sk_sid = sksec->sid;
struct avc_audit_data ad;
char *addrp;
+ u8 secmark_active;
+ u8 peerlbl_active;
if (family != PF_INET && family != PF_INET6)
return 0;
@@ -4122,6 +4135,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
+ /* 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, family);
+
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return 0;
+
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
@@ -4129,15 +4154,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err)
return err;
- /* 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);
-
- if (netlbl_enabled() || selinux_xfrm_enabled()) {
+ if (peerlbl_active) {
u32 peer_sid;
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
@@ -4145,13 +4162,17 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
return err;
err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
peer_sid, &ad);
- if (err)
+ if (err) {
+ selinux_netlbl_err(skb, err, 0);
return err;
+ }
err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
+ if (err)
+ selinux_netlbl_err(skb, err, 0);
}
- if (selinux_secmark_enabled()) {
+ if (secmark_active) {
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
@@ -4210,10 +4231,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
u32 peer_secid = SECSID_NULL;
u16 family;
- if (sock)
+ if (skb && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+ else if (skb && skb->protocol == htons(ETH_P_IPV6))
+ family = PF_INET6;
+ else if (sock)
family = sock->sk->sk_family;
- else if (skb && skb->sk)
- family = skb->sk->sk_family;
else
goto out;
@@ -4271,8 +4294,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
sk->sk_family == PF_UNIX)
isec->sid = sksec->sid;
sksec->sclass = isec->sclass;
-
- selinux_netlbl_sock_graft(sk, parent);
}
static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
@@ -4280,10 +4301,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
{
struct sk_security_struct *sksec = sk->sk_security;
int err;
+ u16 family = sk->sk_family;
u32 newsid;
u32 peersid;
- err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ err = selinux_skb_peerlbl_sid(skb, family, &peersid);
if (err)
return err;
if (peersid == SECSID_NULL) {
@@ -4318,12 +4344,18 @@ static void selinux_inet_csk_clone(struct sock *newsk,
selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
}
-static void selinux_inet_conn_established(struct sock *sk,
- struct sk_buff *skb)
+static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
{
+ u16 family = sk->sk_family;
struct sk_security_struct *sksec = sk->sk_security;
- selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
+
+ selinux_netlbl_inet_conn_established(sk, family);
}
static void selinux_req_classify_flow(const struct request_sock *req,
@@ -4373,39 +4405,54 @@ out:
static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
u16 family)
{
+ int err;
char *addrp;
u32 peer_sid;
struct avc_audit_data ad;
u8 secmark_active;
+ u8 netlbl_active;
u8 peerlbl_active;
if (!selinux_policycap_netpeer)
return NF_ACCEPT;
secmark_active = selinux_secmark_enabled();
- peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ netlbl_active = netlbl_enabled();
+ peerlbl_active = netlbl_active || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+ return NF_DROP;
+
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)
+ if (peerlbl_active) {
+ err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+ peer_sid, &ad);
+ if (err) {
+ selinux_netlbl_err(skb, err, 1);
return NF_DROP;
+ }
+ }
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
+ if (netlbl_active)
+ /* we do this in the FORWARD path and not the POST_ROUTING
+ * path because we want to make sure we apply the necessary
+ * labeling before IPsec is applied so we can leverage AH
+ * protection */
+ if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0)
+ return NF_DROP;
+
return NF_ACCEPT;
}
@@ -4429,6 +4476,37 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,
}
#endif /* IPV6 */
+static unsigned int selinux_ip_output(struct sk_buff *skb,
+ u16 family)
+{
+ u32 sid;
+
+ if (!netlbl_enabled())
+ return NF_ACCEPT;
+
+ /* we do this in the LOCAL_OUT path and not the POST_ROUTING path
+ * because we want to make sure we apply the necessary labeling
+ * before IPsec is applied so we can leverage AH protection */
+ if (skb->sk) {
+ struct sk_security_struct *sksec = skb->sk->sk_security;
+ sid = sksec->sid;
+ } else
+ sid = SECINITSID_KERNEL;
+ if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_output(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_output(skb, PF_INET);
+}
+
static int selinux_ip_postroute_iptables_compat(struct sock *sk,
int ifindex,
struct avc_audit_data *ad,
@@ -4496,30 +4574,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk,
static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
- struct avc_audit_data *ad,
- u16 family,
- char *addrp,
- u8 proto)
+ u16 family)
{
struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
+ struct avc_audit_data ad;
+ char *addrp;
+ u8 proto;
if (sk == NULL)
return NF_ACCEPT;
sksec = sk->sk_security;
+ 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 (selinux_compat_net) {
if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
- ad, family, addrp))
+ &ad, family, addrp))
return NF_DROP;
} else {
if (avc_has_perm(sksec->sid, skb->secmark,
- SECCLASS_PACKET, PACKET__SEND, ad))
+ SECCLASS_PACKET, PACKET__SEND, &ad))
return NF_DROP;
}
if (selinux_policycap_netpeer)
- if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
+ if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
return NF_DROP;
return NF_ACCEPT;
@@ -4533,23 +4617,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
struct sock *sk;
struct avc_audit_data ad;
char *addrp;
- u8 proto;
u8 secmark_active;
u8 peerlbl_active;
- 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);
+ return selinux_ip_postroute_compat(skb, ifindex, family);
/* 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
@@ -4565,21 +4641,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
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 */
+ /* if the packet is being forwarded then get the peer label from the
+ * packet itself; otherwise check to see if it is from a local
+ * application or the kernel, if from an application get the peer label
+ * from the sending socket, otherwise use the kernel's sid */
sk = skb->sk;
- if (sk) {
+ if (sk == NULL) {
+ switch (family) {
+ case PF_INET:
+ if (IPCB(skb)->flags & IPSKB_FORWARDED)
+ secmark_perm = PACKET__FORWARD_OUT;
+ else
+ secmark_perm = PACKET__SEND;
+ break;
+ case PF_INET6:
+ if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
+ secmark_perm = PACKET__FORWARD_OUT;
+ else
+ secmark_perm = PACKET__SEND;
+ break;
+ default:
+ return NF_DROP;
+ }
+ if (secmark_perm == PACKET__FORWARD_OUT) {
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+ return NF_DROP;
+ } else
+ peer_sid = SECINITSID_KERNEL;
+ } else {
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;
}
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
+ return NF_DROP;
+
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, secmark_perm, &ad))
@@ -5222,8 +5322,12 @@ static int selinux_setprocattr(struct task_struct *p,
if (sid == 0)
return -EINVAL;
-
- /* Only allow single threaded processes to change context */
+ /*
+ * SELinux allows to change context in the following case only.
+ * - Single threaded processes.
+ * - Multi threaded processes intend to change its context into
+ * more restricted domain (defined by TYPEBOUNDS statement).
+ */
if (atomic_read(&p->mm->mm_users) != 1) {
struct task_struct *g, *t;
struct mm_struct *mm = p->mm;
@@ -5231,11 +5335,16 @@ static int selinux_setprocattr(struct task_struct *p,
do_each_thread(g, t) {
if (t->mm == mm && t != p) {
read_unlock(&tasklist_lock);
- return -EPERM;
+ error = security_bounded_transition(tsec->sid, sid);
+ if (!error)
+ goto boundary_ok;
+
+ return error;
}
} while_each_thread(g, t);
read_unlock(&tasklist_lock);
}
+boundary_ok:
/* Check permissions for the transition. */
error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
@@ -5247,7 +5356,7 @@ static int selinux_setprocattr(struct task_struct *p,
Otherwise, leave SID unchanged and fail. */
task_lock(p);
rcu_read_lock();
- tracer = task_tracer_task(p);
+ tracer = tracehook_tracer_task(p);
if (tracer != NULL) {
struct task_security_struct *ptsec = tracer->security;
u32 ptsid = ptsec->sid;
@@ -5359,7 +5468,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
static struct security_operations selinux_ops = {
.name = "selinux",
- .ptrace = selinux_ptrace,
+ .ptrace_may_access = selinux_ptrace_may_access,
+ .ptrace_traceme = selinux_ptrace_traceme,
.capget = selinux_capget,
.capset_check = selinux_capset_check,
.capset_set = selinux_capset_set,
@@ -5643,6 +5753,13 @@ static struct nf_hook_ops selinux_ipv4_ops[] = {
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SELINUX_FIRST,
+ },
+ {
+ .hook = selinux_ipv4_output,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_LOCAL_OUT,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
}
};
@@ -5670,27 +5787,20 @@ static struct nf_hook_ops selinux_ipv6_ops[] = {
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");
- 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);
- }
+ err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+ if (err)
+ panic("SELinux: nf_register_hooks for IPv4: error %d\n", err);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- 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);
- }
+ err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+ if (err)
+ panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
#endif /* IPV6 */
out:
@@ -5702,15 +5812,11 @@ __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");
- for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
- nf_unregister_hook(&selinux_ipv4_ops[iter]);
+ nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
- nf_unregister_hook(&selinux_ipv6_ops[iter]);
+ nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
#endif /* IPV6 */
}
#endif
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 7b9769f5e775..d12ff1a9c0aa 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -12,6 +12,7 @@
#include <linux/kdev_t.h>
#include <linux/spinlock.h>
#include <linux/init.h>
+#include <linux/audit.h>
#include <linux/in6.h>
#include <linux/path.h>
#include <asm/system.h>
@@ -126,6 +127,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
u32 events, u32 ssid, u32 tsid,
u16 tclass, u32 perms);
+/* Shows permission in human readable form */
+void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
+
/* Exported to selinuxfs */
int avc_get_hash_stats(char *page);
extern unsigned int avc_cache_threshold;
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 487a7d81fe20..b913c8d06038 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -39,6 +39,9 @@
#ifdef CONFIG_NETLABEL
void selinux_netlbl_cache_invalidate(void);
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
+
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
int family);
@@ -46,8 +49,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u16 family,
u32 *type,
u32 *sid);
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid);
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
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,
@@ -57,12 +63,27 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
+
#else
static inline void selinux_netlbl_cache_invalidate(void)
{
return;
}
+static inline void selinux_netlbl_err(struct sk_buff *skb,
+ int error,
+ int gateway)
+{
+ return;
+}
+
+static inline void selinux_netlbl_sk_security_free(
+ struct sk_security_struct *ssec)
+{
+ return;
+}
+
static inline void selinux_netlbl_sk_security_reset(
struct sk_security_struct *ssec,
int family)
@@ -79,9 +100,21 @@ static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
*sid = SECSID_NULL;
return 0;
}
+static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid)
+{
+ return 0;
+}
-static inline void selinux_netlbl_sock_graft(struct sock *sk,
- struct socket *sock)
+static inline int selinux_netlbl_conn_setsid(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
+
+static inline void selinux_netlbl_inet_conn_established(struct sock *sk,
+ u16 family)
{
return;
}
@@ -107,6 +140,11 @@ static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
{
return 0;
}
+static inline int selinux_netlbl_socket_connect(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
#endif /* CONFIG_NETLABEL */
#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 91070ab874ce..f8be8d7fa26d 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -109,16 +109,19 @@ struct netport_security_struct {
};
struct sk_security_struct {
- u32 sid; /* SID of this object */
- u32 peer_sid; /* SID of peer */
- u16 sclass; /* sock security class */
#ifdef CONFIG_NETLABEL
enum { /* NetLabel state */
NLBL_UNSET = 0,
NLBL_REQUIRE,
NLBL_LABELED,
+ NLBL_REQSKB,
+ NLBL_CONNLABELED,
} nlbl_state;
+ struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */
#endif
+ u32 sid; /* SID of this object */
+ u32 peer_sid; /* SID of peer */
+ u16 sclass; /* sock security class */
};
struct key_security_struct {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 7c543003d653..72447370bc95 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -27,13 +27,14 @@
#define POLICYDB_VERSION_RANGETRANS 21
#define POLICYDB_VERSION_POLCAP 22
#define POLICYDB_VERSION_PERMISSIVE 23
+#define POLICYDB_VERSION_BOUNDARY 24
/* 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_PERMISSIVE
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY
#endif
#define CONTEXT_MNT 0x01
@@ -62,6 +63,16 @@ enum {
extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm;
+/*
+ * type_datum properties
+ * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY
+ */
+#define TYPEDATUM_PROPERTY_PRIMARY 0x0001
+#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002
+
+/* limitation of boundary depth */
+#define POLICYDB_BOUNDS_MAXDEPTH 4
+
int security_load_policy(void *data, size_t len);
int security_policycap_supported(unsigned int req_cap);
@@ -117,6 +128,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen,
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
+int security_bounded_transition(u32 oldsid, u32 newsid);
+
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 89b418392f11..f58701a7b728 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -9,7 +9,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,8 +29,12 @@
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <net/sock.h>
#include <net/netlabel.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
#include "objsec.h"
#include "security.h"
@@ -64,32 +68,69 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
}
/**
+ * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
+ * @sk: the socket
+ *
+ * Description:
+ * Generate the NetLabel security attributes for a socket, making full use of
+ * the socket's attribute cache. Returns a pointer to the security attributes
+ * on success, NULL on failure.
+ *
+ */
+static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_secattr != NULL)
+ return sksec->nlbl_secattr;
+
+ secattr = netlbl_secattr_alloc(GFP_ATOMIC);
+ if (secattr == NULL)
+ return NULL;
+ rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
+ if (rc != 0) {
+ netlbl_secattr_free(secattr);
+ return NULL;
+ }
+ sksec->nlbl_secattr = secattr;
+
+ return secattr;
+}
+
+/**
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
* @sk: the socket to label
- * @sid: the SID to use
*
* Description:
- * Attempt to label a socket using the NetLabel mechanism using the given
- * SID. Returns zero values on success, negative values on failure.
+ * Attempt to label a socket using the NetLabel mechanism. Returns zero values
+ * on success, negative values on failure.
*
*/
-static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
+static int selinux_netlbl_sock_setsid(struct sock *sk)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
+ struct netlbl_lsm_secattr *secattr;
- netlbl_secattr_init(&secattr);
+ if (sksec->nlbl_state != NLBL_REQUIRE)
+ return 0;
- rc = security_netlbl_sid_to_secattr(sid, &secattr);
- if (rc != 0)
- goto sock_setsid_return;
- rc = netlbl_sock_setattr(sk, &secattr);
- if (rc == 0)
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return -ENOMEM;
+ rc = netlbl_sock_setattr(sk, secattr);
+ switch (rc) {
+ case 0:
sksec->nlbl_state = NLBL_LABELED;
+ break;
+ case -EDESTADDRREQ:
+ sksec->nlbl_state = NLBL_REQSKB;
+ rc = 0;
+ break;
+ }
-sock_setsid_return:
- netlbl_secattr_destroy(&secattr);
return rc;
}
@@ -106,6 +147,38 @@ void selinux_netlbl_cache_invalidate(void)
}
/**
+ * selinux_netlbl_err - Handle a NetLabel packet error
+ * @skb: the packet
+ * @error: the error code
+ * @gateway: true if host is acting as a gateway, false otherwise
+ *
+ * Description:
+ * When a packet is dropped due to a call to avc_has_perm() pass the error
+ * code to the NetLabel subsystem so any protocol specific processing can be
+ * done. This is safe to call even if you are unsure if NetLabel labeling is
+ * present on the packet, NetLabel is smart enough to only act when it should.
+ *
+ */
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
+{
+ netlbl_skbuff_err(skb, error, gateway);
+}
+
+/**
+ * selinux_netlbl_sk_security_free - Free the NetLabel fields
+ * @sssec: the sk_security_struct
+ *
+ * Description:
+ * Free all of the memory in the NetLabel fields of a sk_security_struct.
+ *
+ */
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
+{
+ if (ssec->nlbl_secattr != NULL)
+ netlbl_secattr_free(ssec->nlbl_secattr);
+}
+
+/**
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
* @ssec: the sk_security_struct
* @family: the socket family
@@ -163,35 +236,118 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
}
/**
- * selinux_netlbl_sock_graft - Netlabel the new socket
+ * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
+ * @skb: the packet
+ * @family: protocol family
+ * @sid: the SID
+ *
+ * Description
+ * Call the NetLabel mechanism to set the label of a packet using @sid.
+ * Returns zero on auccess, negative values on failure.
+ *
+ */
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid)
+{
+ int rc;
+ struct netlbl_lsm_secattr secattr_storage;
+ struct netlbl_lsm_secattr *secattr = NULL;
+ struct sock *sk;
+
+ /* if this is a locally generated packet check to see if it is already
+ * being labeled by it's parent socket, if it is just exit */
+ sk = skb->sk;
+ if (sk != NULL) {
+ struct sk_security_struct *sksec = sk->sk_security;
+ if (sksec->nlbl_state != NLBL_REQSKB)
+ return 0;
+ secattr = sksec->nlbl_secattr;
+ }
+ if (secattr == NULL) {
+ secattr = &secattr_storage;
+ netlbl_secattr_init(secattr);
+ rc = security_netlbl_sid_to_secattr(sid, secattr);
+ if (rc != 0)
+ goto skbuff_setsid_return;
+ }
+
+ rc = netlbl_skbuff_setattr(skb, family, secattr);
+
+skbuff_setsid_return:
+ if (secattr == &secattr_storage)
+ netlbl_secattr_destroy(secattr);
+ return rc;
+}
+
+/**
+ * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection
* @sk: the new connection
- * @sock: the new socket
*
* Description:
- * The connection represented by @sk is being grafted onto @sock so set the
- * socket's NetLabel to match the SID of @sk.
+ * A new connection has been established on @sk so make sure it is labeled
+ * correctly with the NetLabel susbsystem.
*
*/
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
{
+ int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
- u32 nlbl_peer_sid;
+ struct netlbl_lsm_secattr *secattr;
+ struct inet_sock *sk_inet = inet_sk(sk);
+ struct sockaddr_in addr;
if (sksec->nlbl_state != NLBL_REQUIRE)
return;
- netlbl_secattr_init(&secattr);
- if (netlbl_sock_getattr(sk, &secattr) == 0 &&
- secattr.flags != NETLBL_SECATTR_NONE &&
- security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
- sksec->peer_sid = nlbl_peer_sid;
- netlbl_secattr_destroy(&secattr);
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return;
- /* Try to set the NetLabel on the socket to save time later, if we fail
- * here we will pick up the pieces in later calls to
- * selinux_netlbl_inode_permission(). */
- selinux_netlbl_sock_setsid(sk, sksec->sid);
+ rc = netlbl_sock_setattr(sk, secattr);
+ switch (rc) {
+ case 0:
+ sksec->nlbl_state = NLBL_LABELED;
+ break;
+ case -EDESTADDRREQ:
+ /* no PF_INET6 support yet because we don't support any IPv6
+ * labeling protocols */
+ if (family != PF_INET) {
+ sksec->nlbl_state = NLBL_UNSET;
+ return;
+ }
+
+ addr.sin_family = family;
+ addr.sin_addr.s_addr = sk_inet->daddr;
+ if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
+ secattr) != 0) {
+ /* we failed to label the connected socket (could be
+ * for a variety of reasons, the actual "why" isn't
+ * important here) so we have to go to our backup plan,
+ * labeling the packets individually in the netfilter
+ * local output hook. this is okay but we need to
+ * adjust the MSS of the connection to take into
+ * account any labeling overhead, since we don't know
+ * the exact overhead at this point we'll use the worst
+ * case value which is 40 bytes for IPv4 */
+ struct inet_connection_sock *sk_conn = inet_csk(sk);
+ sk_conn->icsk_ext_hdr_len += 40 -
+ (sk_inet->opt ? sk_inet->opt->optlen : 0);
+ sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
+
+ sksec->nlbl_state = NLBL_REQSKB;
+ } else
+ sksec->nlbl_state = NLBL_CONNLABELED;
+ break;
+ default:
+ /* note that we are failing to label the socket which could be
+ * a bad thing since it means traffic could leave the system
+ * without the desired labeling, however, all is not lost as
+ * we have a check in selinux_netlbl_inode_permission() to
+ * pick up the pieces that we might drop here because we can't
+ * return an error code */
+ break;
+ }
}
/**
@@ -205,13 +361,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
*/
int selinux_netlbl_socket_post_create(struct socket *sock)
{
- struct sock *sk = sock->sk;
- struct sk_security_struct *sksec = sk->sk_security;
-
- if (sksec->nlbl_state != NLBL_REQUIRE)
- return 0;
-
- return selinux_netlbl_sock_setsid(sk, sksec->sid);
+ return selinux_netlbl_sock_setsid(sock->sk);
}
/**
@@ -246,7 +396,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
local_bh_disable();
bh_lock_sock_nested(sk);
if (likely(sksec->nlbl_state == NLBL_REQUIRE))
- rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
+ rc = selinux_netlbl_sock_setsid(sk);
else
rc = 0;
bh_unlock_sock(sk);
@@ -307,7 +457,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
return 0;
if (nlbl_sid != SECINITSID_UNLABELED)
- netlbl_skbuff_err(skb, rc);
+ netlbl_skbuff_err(skb, rc, 0);
return rc;
}
@@ -334,7 +484,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
struct netlbl_lsm_secattr secattr;
if (level == IPPROTO_IP && optname == IP_OPTIONS &&
- sksec->nlbl_state == NLBL_LABELED) {
+ (sksec->nlbl_state == NLBL_LABELED ||
+ sksec->nlbl_state == NLBL_CONNLABELED)) {
netlbl_secattr_init(&secattr);
lock_sock(sk);
rc = netlbl_sock_getattr(sk, &secattr);
@@ -346,3 +497,50 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
return rc;
}
+
+/**
+ * selinux_netlbl_socket_connect - Label a client-side socket on connect
+ * @sk: the socket to label
+ * @addr: the destination address
+ *
+ * Description:
+ * Attempt to label a connected socket with NetLabel using the given address.
+ * Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_state != NLBL_REQSKB &&
+ sksec->nlbl_state != NLBL_CONNLABELED)
+ return 0;
+
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+
+ /* connected sockets are allowed to disconnect when the address family
+ * is set to AF_UNSPEC, if that is what is happening we want to reset
+ * the socket */
+ if (addr->sa_family == AF_UNSPEC) {
+ netlbl_sock_delattr(sk);
+ sksec->nlbl_state = NLBL_REQSKB;
+ rc = 0;
+ goto socket_connect_return;
+ }
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL) {
+ rc = -ENOMEM;
+ goto socket_connect_return;
+ }
+ rc = netlbl_conn_setattr(sk, addr, secattr);
+ if (rc == 0)
+ sksec->nlbl_state = NLBL_CONNLABELED;
+
+socket_connect_return:
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ return rc;
+}
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index a1be97f8beea..1215b8e47dba 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -98,7 +98,7 @@ struct avtab_node *
avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
{
int hvalue;
- struct avtab_node *prev, *cur, *newnode;
+ struct avtab_node *prev, *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
if (!h || !h->htable)
@@ -122,9 +122,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu
key->target_class < cur->key.target_class)
break;
}
- newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
-
- return newnode;
+ return avtab_insert_node(h, hvalue, prev, cur, key, datum);
}
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
@@ -231,7 +229,7 @@ void avtab_destroy(struct avtab *h)
for (i = 0; i < h->nslot; i++) {
cur = h->htable[i];
- while (cur != NULL) {
+ while (cur) {
temp = cur;
cur = cur->next;
kmem_cache_free(avtab_node_cachep, temp);
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index fb4efe4f4bc8..4a4e35cac22b 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -29,7 +29,7 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
int s[COND_EXPR_MAXDEPTH];
int sp = -1;
- for (cur = expr; cur != NULL; cur = cur->next) {
+ for (cur = expr; cur; cur = cur->next) {
switch (cur->expr_type) {
case COND_BOOL:
if (sp == (COND_EXPR_MAXDEPTH - 1))
@@ -97,14 +97,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node)
if (new_state == -1)
printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n");
/* turn the rules on or off */
- for (cur = node->true_list; cur != NULL; cur = cur->next) {
+ for (cur = node->true_list; cur; cur = cur->next) {
if (new_state <= 0)
cur->node->key.specified &= ~AVTAB_ENABLED;
else
cur->node->key.specified |= AVTAB_ENABLED;
}
- for (cur = node->false_list; cur != NULL; cur = cur->next) {
+ for (cur = node->false_list; cur; cur = cur->next) {
/* -1 or 1 */
if (new_state)
cur->node->key.specified &= ~AVTAB_ENABLED;
@@ -128,7 +128,7 @@ int cond_policydb_init(struct policydb *p)
static void cond_av_list_destroy(struct cond_av_list *list)
{
struct cond_av_list *cur, *next;
- for (cur = list; cur != NULL; cur = next) {
+ for (cur = list; cur; cur = next) {
next = cur->next;
/* the avtab_ptr_t node is destroy by the avtab */
kfree(cur);
@@ -139,7 +139,7 @@ static void cond_node_destroy(struct cond_node *node)
{
struct cond_expr *cur_expr, *next_expr;
- for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) {
+ for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) {
next_expr = cur_expr->next;
kfree(cur_expr);
}
@@ -155,7 +155,7 @@ static void cond_list_destroy(struct cond_node *list)
if (list == NULL)
return;
- for (cur = list; cur != NULL; cur = next) {
+ for (cur = list; cur; cur = next) {
next = cur->next;
cond_node_destroy(cur);
}
@@ -239,7 +239,7 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto err;
- key[len] = 0;
+ key[len] = '\0';
if (hashtab_insert(h, key, booldatum))
goto err;
@@ -291,7 +291,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
goto err;
}
found = 0;
- for (cur = other; cur != NULL; cur = cur->next) {
+ for (cur = other; cur; cur = cur->next) {
if (cur->node == node_ptr) {
found = 1;
break;
@@ -485,7 +485,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
if (!ctab || !key || !avd)
return;
- for (node = avtab_search_node(ctab, key); node != NULL;
+ for (node = avtab_search_node(ctab, key); node;
node = avtab_search_node_next(node, key->specified)) {
if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
index 65b9f8366e9c..53ddb013ae57 100644
--- a/security/selinux/ss/conditional.h
+++ b/security/selinux/ss/conditional.h
@@ -28,7 +28,7 @@ struct cond_expr {
#define COND_XOR 5 /* bool ^ bool */
#define COND_EQ 6 /* bool == bool */
#define COND_NEQ 7 /* bool != bool */
-#define COND_LAST 8
+#define COND_LAST COND_NEQ
__u32 expr_type;
__u32 bool;
struct cond_expr *next;
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index ddc275490af8..68c7348d1acc 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -109,7 +109,7 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap,
*catmap = c_iter;
c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1);
- while (e_iter != NULL) {
+ while (e_iter) {
for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
unsigned int delta, e_startbit, c_endbit;
@@ -197,7 +197,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
}
}
c_iter = c_iter->next;
- } while (c_iter != NULL);
+ } while (c_iter);
if (e_iter != NULL)
ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
else
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
index 2e7788e13213..933e735bb185 100644
--- a/security/selinux/ss/hashtab.c
+++ b/security/selinux/ss/hashtab.c
@@ -81,7 +81,7 @@ void *hashtab_search(struct hashtab *h, const void *key)
hvalue = h->hash_value(h, key);
cur = h->htable[hvalue];
- while (cur != NULL && h->keycmp(h, key, cur->key) > 0)
+ while (cur && h->keycmp(h, key, cur->key) > 0)
cur = cur->next;
if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
@@ -100,7 +100,7 @@ void hashtab_destroy(struct hashtab *h)
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
- while (cur != NULL) {
+ while (cur) {
temp = cur;
cur = cur->next;
kfree(temp);
@@ -127,7 +127,7 @@ int hashtab_map(struct hashtab *h,
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
- while (cur != NULL) {
+ while (cur) {
ret = apply(cur->key, cur->datum, args);
if (ret)
return ret;
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 77d745da48bb..b5407f16c2a4 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -283,8 +283,8 @@ int mls_context_to_sid(struct policydb *pol,
p++;
delim = *p;
- if (delim != 0)
- *p++ = 0;
+ if (delim != '\0')
+ *p++ = '\0';
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(pol->p_levels.table, scontextp);
@@ -302,14 +302,14 @@ int mls_context_to_sid(struct policydb *pol,
while (*p && *p != ',' && *p != '-')
p++;
delim = *p;
- if (delim != 0)
- *p++ = 0;
+ if (delim != '\0')
+ *p++ = '\0';
/* Separate into range if exists */
rngptr = strchr(scontextp, '.');
if (rngptr != NULL) {
/* Remove '.' */
- *rngptr++ = 0;
+ *rngptr++ = '\0';
}
catdatum = hashtab_search(pol->p_cats.table,
@@ -357,8 +357,8 @@ int mls_context_to_sid(struct policydb *pol,
p++;
delim = *p;
- if (delim != 0)
- *p++ = 0;
+ if (delim != '\0')
+ *p++ = '\0';
} else
break;
}
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 2391761ae422..72e4a54973aa 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
+#include <linux/audit.h>
#include "security.h"
#include "policydb.h"
@@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = {
.version = POLICYDB_VERSION_PERMISSIVE,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
- }
+ },
+ {
+ .version = POLICYDB_VERSION_BOUNDARY,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap)
role = datum;
p = datap;
- if (!role->value || role->value > p->p_roles.nprim)
+ if (!role->value
+ || role->value > p->p_roles.nprim
+ || role->bounds > p->p_roles.nprim)
return -EINVAL;
p->p_role_val_to_name[role->value - 1] = key;
p->role_val_to_struct[role->value - 1] = role;
@@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap)
p = datap;
if (typdatum->primary) {
- if (!typdatum->value || typdatum->value > p->p_types.nprim)
+ if (!typdatum->value
+ || typdatum->value > p->p_types.nprim
+ || typdatum->bounds > p->p_types.nprim)
return -EINVAL;
p->p_type_val_to_name[typdatum->value - 1] = key;
+ p->type_val_to_struct[typdatum->value - 1] = typdatum;
}
return 0;
@@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap)
usrdatum = datum;
p = datap;
- if (!usrdatum->value || usrdatum->value > p->p_users.nprim)
+ if (!usrdatum->value
+ || usrdatum->value > p->p_users.nprim
+ || usrdatum->bounds > p->p_users.nprim)
return -EINVAL;
p->p_user_val_to_name[usrdatum->value - 1] = key;
p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
@@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p)
goto out;
}
+ p->type_val_to_struct =
+ kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->type_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
if (cond_init_bool_indexes(p)) {
rc = -ENOMEM;
goto out;
@@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p)
kfree(p->class_val_to_struct);
kfree(p->role_val_to_struct);
kfree(p->user_val_to_struct);
+ kfree(p->type_val_to_struct);
avtab_destroy(&p->te_avtab);
@@ -932,7 +954,7 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
rc = hashtab_insert(h, key, perdatum);
if (rc)
@@ -979,7 +1001,7 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
for (i = 0; i < nel; i++) {
rc = perm_read(p, comdatum->permissions.table, fp);
@@ -1117,7 +1139,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
if (len2) {
cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
@@ -1128,7 +1150,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(cladatum->comkey, fp, len2);
if (rc < 0)
goto bad;
- cladatum->comkey[len2] = 0;
+ cladatum->comkey[len2] = '\0';
cladatum->comdatum = hashtab_search(p->p_commons.table,
cladatum->comkey);
@@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct role_datum *role;
- int rc;
- __le32 buf[2];
+ int rc, to_read = 2;
+ __le32 buf[3];
u32 len;
role = kzalloc(sizeof(*role), GFP_KERNEL);
@@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
goto out;
}
- rc = next_entry(buf, fp, sizeof buf);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 3;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
goto bad;
len = le32_to_cpu(buf[0]);
role->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ role->bounds = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
@@ -1201,7 +1228,7 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
rc = ebitmap_read(&role->dominates, fp);
if (rc)
@@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct type_datum *typdatum;
- int rc;
- __le32 buf[3];
+ int rc, to_read = 3;
+ __le32 buf[4];
u32 len;
typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
@@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
return rc;
}
- rc = next_entry(buf, fp, sizeof buf);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 4;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
goto bad;
len = le32_to_cpu(buf[0]);
typdatum->value = le32_to_cpu(buf[1]);
- typdatum->primary = le32_to_cpu(buf[2]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) {
+ u32 prop = le32_to_cpu(buf[2]);
+
+ if (prop & TYPEDATUM_PROPERTY_PRIMARY)
+ typdatum->primary = 1;
+ if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE)
+ typdatum->attribute = 1;
+
+ typdatum->bounds = le32_to_cpu(buf[3]);
+ } else {
+ typdatum->primary = le32_to_cpu(buf[2]);
+ }
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
@@ -1262,7 +1303,7 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
rc = hashtab_insert(h, key, typdatum);
if (rc)
@@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct user_datum *usrdatum;
- int rc;
- __le32 buf[2];
+ int rc, to_read = 2;
+ __le32 buf[3];
u32 len;
usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
@@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
goto out;
}
- rc = next_entry(buf, fp, sizeof buf);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 3;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
goto bad;
len = le32_to_cpu(buf[0]);
usrdatum->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ usrdatum->bounds = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
@@ -1334,7 +1380,7 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
rc = ebitmap_read(&usrdatum->roles, fp);
if (rc)
@@ -1388,7 +1434,7 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
if (!levdatum->level) {
@@ -1440,7 +1486,7 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
rc = next_entry(key, fp, len);
if (rc < 0)
goto bad;
- key[len] = 0;
+ key[len] = '\0';
rc = hashtab_insert(h, key, catdatum);
if (rc)
@@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp)
cat_read,
};
+static int user_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct user_datum *upper, *user;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = user = datum;
+ while (upper->bounds) {
+ struct ebitmap_node *node;
+ unsigned long bit;
+
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: user %s: "
+ "too deep or looped boundary",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->user_val_to_struct[upper->bounds - 1];
+ ebitmap_for_each_positive_bit(&user->roles, node, bit) {
+ if (ebitmap_get_bit(&upper->roles, bit))
+ continue;
+
+ printk(KERN_ERR
+ "SELinux: boundary violated policy: "
+ "user=%s role=%s bounds=%s\n",
+ p->p_user_val_to_name[user->value - 1],
+ p->p_role_val_to_name[bit],
+ p->p_user_val_to_name[upper->value - 1]);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int role_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct role_datum *upper, *role;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = role = datum;
+ while (upper->bounds) {
+ struct ebitmap_node *node;
+ unsigned long bit;
+
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: role %s: "
+ "too deep or looped bounds\n",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->role_val_to_struct[upper->bounds - 1];
+ ebitmap_for_each_positive_bit(&role->types, node, bit) {
+ if (ebitmap_get_bit(&upper->types, bit))
+ continue;
+
+ printk(KERN_ERR
+ "SELinux: boundary violated policy: "
+ "role=%s type=%s bounds=%s\n",
+ p->p_role_val_to_name[role->value - 1],
+ p->p_type_val_to_name[bit],
+ p->p_role_val_to_name[upper->value - 1]);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int type_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct type_datum *upper, *type;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = type = datum;
+ while (upper->bounds) {
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: type %s: "
+ "too deep or looped boundary\n",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->type_val_to_struct[upper->bounds - 1];
+ if (upper->attribute) {
+ printk(KERN_ERR "SELinux: type %s: "
+ "bounded by attribute %s",
+ (char *) key,
+ p->p_type_val_to_name[upper->value - 1]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int policydb_bounds_sanity_check(struct policydb *p)
+{
+ int rc;
+
+ if (p->policyvers < POLICYDB_VERSION_BOUNDARY)
+ return 0;
+
+ rc = hashtab_map(p->p_users.table,
+ user_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ rc = hashtab_map(p->p_roles.table,
+ role_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ rc = hashtab_map(p->p_types.table,
+ type_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
extern int ss_initialized;
/*
@@ -1523,7 +1696,7 @@ int policydb_read(struct policydb *p, void *fp)
kfree(policydb_str);
goto bad;
}
- policydb_str[len] = 0;
+ policydb_str[len] = '\0';
if (strcmp(policydb_str, POLICYDB_STRING)) {
printk(KERN_ERR "SELinux: policydb string %s does not match "
"my string %s\n", policydb_str, POLICYDB_STRING);
@@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
+ rc = policydb_bounds_sanity_check(p);
+ if (rc)
+ goto bad;
+
rc = 0;
out:
return rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 4253370fda6a..55152d498b53 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -61,6 +61,7 @@ struct class_datum {
/* Role attributes */
struct role_datum {
u32 value; /* internal role value */
+ u32 bounds; /* boundary of role */
struct ebitmap dominates; /* set of roles dominated by this role */
struct ebitmap types; /* set of authorized types for role */
};
@@ -81,12 +82,15 @@ struct role_allow {
/* Type attributes */
struct type_datum {
u32 value; /* internal type value */
+ u32 bounds; /* boundary of type */
unsigned char primary; /* primary name? */
+ unsigned char attribute;/* attribute ?*/
};
/* User attributes */
struct user_datum {
u32 value; /* internal user value */
+ u32 bounds; /* bounds of user */
struct ebitmap roles; /* set of authorized roles for user */
struct mls_range range; /* MLS range (min - max) for user */
struct mls_level dfltlevel; /* default login MLS level for user */
@@ -209,6 +213,7 @@ struct policydb {
struct class_datum **class_val_to_struct;
struct role_datum **role_val_to_struct;
struct user_datum **user_val_to_struct;
+ struct type_datum **type_val_to_struct;
/* type enforcement access vectors and transitions */
struct avtab te_avtab;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index b52f923ce680..343c8ab14af0 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -88,6 +88,11 @@ static u32 latest_granting;
static int context_struct_to_string(struct context *context, char **scontext,
u32 *scontext_len);
+static int context_struct_compute_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd);
/*
* Return the boolean value of a constraint expression
* when it is applied to the specified source and target
@@ -274,6 +279,100 @@ mls_ops:
}
/*
+ * security_boundary_permission - drops violated permissions
+ * on boundary constraint.
+ */
+static void type_attribute_bounds_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct context lo_scontext;
+ struct context lo_tcontext;
+ struct av_decision lo_avd;
+ struct type_datum *source
+ = policydb.type_val_to_struct[scontext->type - 1];
+ struct type_datum *target
+ = policydb.type_val_to_struct[tcontext->type - 1];
+ u32 masked = 0;
+
+ if (source->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+
+ memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+ lo_scontext.type = source->bounds;
+
+ context_struct_compute_av(&lo_scontext,
+ tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (target->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+
+ memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
+ lo_tcontext.type = target->bounds;
+
+ context_struct_compute_av(scontext,
+ &lo_tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (source->bounds && target->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+ /*
+ * lo_scontext and lo_tcontext are already
+ * set up.
+ */
+
+ context_struct_compute_av(&lo_scontext,
+ &lo_tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (masked) {
+ struct audit_buffer *ab;
+ char *stype_name
+ = policydb.p_type_val_to_name[source->value - 1];
+ char *ttype_name
+ = policydb.p_type_val_to_name[target->value - 1];
+ char *tclass_name
+ = policydb.p_class_val_to_name[tclass - 1];
+
+ /* mask violated permissions */
+ avd->allowed &= ~masked;
+
+ /* notice to userspace via audit message */
+ ab = audit_log_start(current->audit_context,
+ GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ if (!ab)
+ return;
+
+ audit_log_format(ab, "av boundary violation: "
+ "source=%s target=%s tclass=%s",
+ stype_name, ttype_name, tclass_name);
+ avc_dump_av(ab, tclass, masked);
+ audit_log_end(ab);
+ }
+}
+
+/*
* Compute access vectors based on a context structure pair for
* the permissions in a particular class.
*/
@@ -356,7 +455,7 @@ static int context_struct_compute_av(struct context *scontext,
avkey.source_type = i + 1;
avkey.target_type = j + 1;
for (node = avtab_search_node(&policydb.te_avtab, &avkey);
- node != NULL;
+ node;
node = avtab_search_node_next(node, avkey.specified)) {
if (node->key.specified == AVTAB_ALLOWED)
avd->allowed |= node->datum.data;
@@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext,
PROCESS__DYNTRANSITION);
}
+ /*
+ * If the given source and target types have boundary
+ * constraint, lazy checks have to mask any violated
+ * permission and notice it to userspace via audit.
+ */
+ type_attribute_bounds_av(scontext, tcontext,
+ tclass, requested, avd);
+
return 0;
inval_class:
@@ -549,6 +656,69 @@ out:
return rc;
}
+/*
+ * security_bounded_transition - check whether the given
+ * transition is directed to bounded, or not.
+ * It returns 0, if @newsid is bounded by @oldsid.
+ * Otherwise, it returns error code.
+ *
+ * @oldsid : current security identifier
+ * @newsid : destinated security identifier
+ */
+int security_bounded_transition(u32 old_sid, u32 new_sid)
+{
+ struct context *old_context, *new_context;
+ struct type_datum *type;
+ int index;
+ int rc = -EINVAL;
+
+ read_lock(&policy_rwlock);
+
+ old_context = sidtab_search(&sidtab, old_sid);
+ if (!old_context) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+ __func__, old_sid);
+ goto out;
+ }
+
+ new_context = sidtab_search(&sidtab, new_sid);
+ if (!new_context) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+ __func__, new_sid);
+ goto out;
+ }
+
+ /* type/domain unchaned */
+ if (old_context->type == new_context->type) {
+ rc = 0;
+ goto out;
+ }
+
+ index = new_context->type;
+ while (true) {
+ type = policydb.type_val_to_struct[index - 1];
+ BUG_ON(!type);
+
+ /* not bounded anymore */
+ if (!type->bounds) {
+ rc = -EPERM;
+ break;
+ }
+
+ /* @newsid is bounded by @oldsid */
+ if (type->bounds == old_context->type) {
+ rc = 0;
+ break;
+ }
+ index = type->bounds;
+ }
+out:
+ read_unlock(&policy_rwlock);
+
+ return rc;
+}
+
+
/**
* security_compute_av - Compute access vector decisions.
* @ssid: source security identifier
@@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol,
*p++ = 0;
typdatum = hashtab_search(pol->p_types.table, scontextp);
- if (!typdatum)
+ if (!typdatum || typdatum->attribute)
goto out;
ctx->type = typdatum->value;
@@ -811,11 +981,12 @@ static int string_to_context_struct(struct policydb *pol,
/* Check the validity of the new context. */
if (!policydb_context_isvalid(pol, ctx)) {
rc = -EINVAL;
- context_destroy(ctx);
goto out;
}
rc = 0;
out:
+ if (rc)
+ context_destroy(ctx);
return rc;
}
@@ -868,8 +1039,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
} else if (rc)
goto out;
rc = sidtab_context_to_sid(&sidtab, &context, sid);
- if (rc)
- context_destroy(&context);
+ context_destroy(&context);
out:
read_unlock(&policy_rwlock);
kfree(scontext2);
@@ -1037,7 +1207,7 @@ static int security_compute_sid(u32 ssid,
/* If no permanent rule, also check for enabled conditional rules */
if (!avdatum) {
node = avtab_search_node(&policydb.te_cond_avtab, &avkey);
- for (; node != NULL; node = avtab_search_node_next(node, specified)) {
+ for (; node; node = avtab_search_node_next(node, specified)) {
if (node->key.specified & AVTAB_ENABLED) {
avdatum = &node->datum;
break;
@@ -2050,7 +2220,7 @@ int security_set_bools(int len, int *values)
policydb.bool_val_to_struct[i]->state = 0;
}
- for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
+ for (cur = policydb.cond_list; cur; cur = cur->next) {
rc = evaluate_cond_node(&policydb, cur);
if (rc)
goto out;
@@ -2102,7 +2272,7 @@ static int security_preserve_bools(struct policydb *p)
if (booldatum)
booldatum->state = bvalues[i];
}
- for (cur = p->cond_list; cur != NULL; cur = cur->next) {
+ for (cur = p->cond_list; cur; cur = cur->next) {
rc = evaluate_cond_node(p, cur);
if (rc)
goto out;
@@ -2737,6 +2907,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
if (ctx == NULL)
goto netlbl_secattr_to_sid_return;
+ context_init(&ctx_new);
ctx_new.user = ctx->user;
ctx_new.role = ctx->role;
ctx_new.type = ctx->type;
@@ -2745,13 +2916,9 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
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;
- ctx_new.range.level[1].cat.node =
- ctx_new.range.level[0].cat.node;
- } else {
- ebitmap_init(&ctx_new.range.level[0].cat);
- ebitmap_init(&ctx_new.range.level[1].cat);
+ memcpy(&ctx_new.range.level[1].cat,
+ &ctx_new.range.level[0].cat,
+ sizeof(ctx_new.range.level[0].cat));
}
if (mls_context_isvalid(&policydb, &ctx_new) != 1)
goto netlbl_secattr_to_sid_return_cleanup;
@@ -2788,7 +2955,7 @@ netlbl_secattr_to_sid_return_cleanup:
*/
int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
{
- int rc = -ENOENT;
+ int rc;
struct context *ctx;
if (!ss_initialized)
@@ -2796,11 +2963,18 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
read_lock(&policy_rwlock);
ctx = sidtab_search(&sidtab, sid);
- if (ctx == NULL)
+ if (ctx == NULL) {
+ rc = -ENOENT;
goto netlbl_sid_to_secattr_failure;
+ }
secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
GFP_ATOMIC);
- secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY;
+ if (secattr->domain == NULL) {
+ rc = -ENOMEM;
+ goto netlbl_sid_to_secattr_failure;
+ }
+ secattr->attr.secid = sid;
+ secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
mls_export_netlbl_lvl(ctx, secattr);
rc = mls_export_netlbl_cat(ctx, secattr);
if (rc != 0)
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index a81ded104129..e817989764cd 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -43,7 +43,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
hvalue = SIDTAB_HASH(sid);
prev = NULL;
cur = s->htable[hvalue];
- while (cur != NULL && sid > cur->sid) {
+ while (cur && sid > cur->sid) {
prev = cur;
cur = cur->next;
}
@@ -92,7 +92,7 @@ static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
hvalue = SIDTAB_HASH(sid);
cur = s->htable[hvalue];
- while (cur != NULL && sid > cur->sid)
+ while (cur && sid > cur->sid)
cur = cur->next;
if (force && cur && sid == cur->sid && cur->context.len)
@@ -103,7 +103,7 @@ static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
sid = SECINITSID_UNLABELED;
hvalue = SIDTAB_HASH(sid);
cur = s->htable[hvalue];
- while (cur != NULL && sid > cur->sid)
+ while (cur && sid > cur->sid)
cur = cur->next;
if (!cur || sid != cur->sid)
return NULL;
@@ -136,7 +136,7 @@ int sidtab_map(struct sidtab *s,
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
- while (cur != NULL) {
+ while (cur) {
rc = apply(cur->sid, &cur->context, args);
if (rc)
goto out;
@@ -155,7 +155,7 @@ static inline u32 sidtab_search_context(struct sidtab *s,
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
- while (cur != NULL) {
+ while (cur) {
if (context_cmp(&cur->context, context))
return cur->sid;
cur = cur->next;
@@ -242,7 +242,7 @@ void sidtab_destroy(struct sidtab *s)
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
- while (cur != NULL) {
+ while (cur) {
temp = cur;
cur = cur->next;
context_destroy(&temp->context);
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 4a4477f5afdc..31dce559595a 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -178,6 +178,7 @@ u32 smack_to_secid(const char *);
extern int smack_cipso_direct;
extern int smack_net_nltype;
extern char *smack_net_ambient;
+extern char *smack_onlycap;
extern struct smack_known *smack_known;
extern struct smack_known smack_known_floor;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index f6b5f6eed6dd..79ff21ed4c3b 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -157,7 +157,7 @@ int smk_access(char *subject_label, char *object_label, int request)
*
* This function checks the current subject label/object label pair
* in the access rule list and returns 0 if the access is permitted,
- * non zero otherwise. It allows that current my have the capability
+ * non zero otherwise. It allows that current may have the capability
* to override the rules.
*/
int smk_curacc(char *obj_label, u32 mode)
@@ -168,6 +168,14 @@ int smk_curacc(char *obj_label, u32 mode)
if (rc == 0)
return 0;
+ /*
+ * Return if a specific label has been designated as the
+ * only one that gets privilege and current does not
+ * have that label.
+ */
+ if (smack_onlycap != NULL && smack_onlycap != current->security)
+ return rc;
+
if (capable(CAP_MAC_OVERRIDE))
return 0;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ee5a51cbc5eb..6e2dc0bab70d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -87,27 +87,46 @@ struct inode_smack *new_inode_smack(char *smack)
*/
/**
- * smack_ptrace - Smack approval on ptrace
- * @ptp: parent task pointer
+ * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
* @ctp: child task pointer
*
* Returns 0 if access is OK, an error code otherwise
*
* Do the capability checks, and require read and write.
*/
-static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp,
- unsigned int mode)
+static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
{
int rc;
- rc = cap_ptrace(ptp, ctp, mode);
+ rc = cap_ptrace_may_access(ctp, mode);
if (rc != 0)
return rc;
- rc = smk_access(ptp->security, ctp->security, MAY_READWRITE);
- if (rc != 0 && __capable(ptp, CAP_MAC_OVERRIDE))
+ rc = smk_access(current->security, ctp->security, MAY_READWRITE);
+ if (rc != 0 && capable(CAP_MAC_OVERRIDE))
return 0;
+ return rc;
+}
+
+/**
+ * smack_ptrace_traceme - Smack approval on PTRACE_TRACEME
+ * @ptp: parent task pointer
+ *
+ * Returns 0 if access is OK, an error code otherwise
+ *
+ * Do the capability checks, and require read and write.
+ */
+static int smack_ptrace_traceme(struct task_struct *ptp)
+{
+ int rc;
+ rc = cap_ptrace_traceme(ptp);
+ if (rc != 0)
+ return rc;
+
+ rc = smk_access(ptp->security, current->security, MAY_READWRITE);
+ if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
+ return 0;
return rc;
}
@@ -522,8 +541,7 @@ static int smack_inode_rename(struct inode *old_inode,
*
* Returns 0 if access is permitted, -EACCES otherwise
*/
-static int smack_inode_permission(struct inode *inode, int mask,
- struct nameidata *nd)
+static int smack_inode_permission(struct inode *inode, int mask)
{
/*
* No permission to check. Existence test. Yup, it's there.
@@ -924,7 +942,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
*/
file = container_of(fown, struct file, f_owner);
rc = smk_access(file->f_security, tsk->security, MAY_WRITE);
- if (rc != 0 && __capable(tsk, CAP_MAC_OVERRIDE))
+ if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
return 0;
return rc;
}
@@ -1165,12 +1183,12 @@ static int smack_task_wait(struct task_struct *p)
* account for the smack labels having gotten to
* be different in the first place.
*
- * This breaks the strict subjet/object access
+ * This breaks the strict subject/object access
* control ideal, taking the object's privilege
* state into account in the decision as well as
* the smack value.
*/
- if (capable(CAP_MAC_OVERRIDE) || __capable(p, CAP_MAC_OVERRIDE))
+ if (capable(CAP_MAC_OVERRIDE) || has_capability(p, CAP_MAC_OVERRIDE))
return 0;
return rc;
@@ -2017,9 +2035,6 @@ static int smack_setprocattr(struct task_struct *p, char *name,
{
char *newsmack;
- if (!__capable(p, CAP_MAC_ADMIN))
- return -EPERM;
-
/*
* Changing another process' Smack value is too dangerous
* and supports no sane use case.
@@ -2027,6 +2042,9 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (p != current)
return -EPERM;
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
if (value == NULL || size == 0 || size >= SMK_LABELLEN)
return -EINVAL;
@@ -2161,7 +2179,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
* This is the simplist possible security model
* for networking.
*/
- return smk_access(smack, ssp->smk_in, MAY_WRITE);
+ rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+ if (rc != 0)
+ netlbl_skbuff_err(skb, rc, 0);
+ return rc;
}
/**
@@ -2553,7 +2574,8 @@ static void smack_release_secctx(char *secdata, u32 seclen)
struct security_operations smack_ops = {
.name = "smack",
- .ptrace = smack_ptrace,
+ .ptrace_may_access = smack_ptrace_may_access,
+ .ptrace_traceme = smack_ptrace_traceme,
.capget = cap_capget,
.capset_check = cap_capset_check,
.capset_set = cap_capset_set,
@@ -2730,4 +2752,3 @@ static __init int smack_init(void)
* all processes and objects when they are created.
*/
security_initcall(smack_init);
-
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 271a835fbbe3..c21d8c8bf0c7 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -39,6 +39,7 @@ enum smk_inos {
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
SMK_AMBIENT = 7, /* internet ambient label */
SMK_NLTYPE = 8, /* label scheme to use by default */
+ SMK_ONLYCAP = 9, /* the only "capable" label */
};
/*
@@ -68,6 +69,16 @@ int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
*/
int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
+/*
+ * Unless a process is running with this label even
+ * having CAP_MAC_OVERRIDE isn't enough to grant
+ * privilege to violate MAC policy. If no label is
+ * designated (the NULL case) capabilities apply to
+ * everyone. It is expected that the hat (^) label
+ * will be used if any label is used.
+ */
+char *smack_onlycap;
+
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
struct smk_list_entry *smack_list;
@@ -343,9 +354,11 @@ static void smk_cipso_doi(void)
doip->tags[rc] = CIPSO_V4_TAG_INVALID;
rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
- if (rc != 0)
+ if (rc != 0) {
printk(KERN_WARNING "%s:%d add rc = %d\n",
__func__, __LINE__, rc);
+ kfree(doip);
+ }
}
/**
@@ -787,6 +800,85 @@ static const struct file_operations smk_ambient_ops = {
.write = smk_write_ambient,
};
+/**
+ * smk_read_onlycap - read() for /smack/onlycap
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @cn: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
+ size_t cn, loff_t *ppos)
+{
+ char *smack = "";
+ ssize_t rc = -EINVAL;
+ int asize;
+
+ if (*ppos != 0)
+ return 0;
+
+ if (smack_onlycap != NULL)
+ smack = smack_onlycap;
+
+ asize = strlen(smack) + 1;
+
+ if (cn >= asize)
+ rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
+
+ return rc;
+}
+
+/**
+ * smk_write_onlycap - write() for /smack/onlycap
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char in[SMK_LABELLEN];
+ char *sp = current->security;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ /*
+ * This can be done using smk_access() but is done
+ * explicitly for clarity. The smk_access() implementation
+ * would use smk_access(smack_onlycap, MAY_WRITE)
+ */
+ if (smack_onlycap != NULL && smack_onlycap != sp)
+ return -EPERM;
+
+ if (count >= SMK_LABELLEN)
+ return -EINVAL;
+
+ if (copy_from_user(in, buf, count) != 0)
+ return -EFAULT;
+
+ /*
+ * Should the null string be passed in unset the onlycap value.
+ * This seems like something to be careful with as usually
+ * smk_import only expects to return NULL for errors. It
+ * is usually the case that a nullstring or "\n" would be
+ * bad to pass to smk_import but in fact this is useful here.
+ */
+ smack_onlycap = smk_import(in, count);
+
+ return count;
+}
+
+static const struct file_operations smk_onlycap_ops = {
+ .read = smk_read_onlycap,
+ .write = smk_write_onlycap,
+};
+
struct option_names {
int o_number;
char *o_name;
@@ -919,6 +1011,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
{"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
[SMK_NLTYPE] =
{"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
+ [SMK_ONLYCAP] =
+ {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
/* last one */ {""}
};
OpenPOWER on IntegriCloud