aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/ss/avtab.c
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2015-04-08 11:27:46 -0700
committerrogersb11 <brettrogers11@gmail.com>2015-11-09 11:40:43 -0500
commitd177fbc2f0c263b06c18bda2eb46200a31bcbebd (patch)
tree70ccc31cdb4529be10956bd4228f9d731146e8ca /security/selinux/ss/avtab.c
parent90b542df96386ea6d875d51195c4b331fdc21d06 (diff)
downloadkernel_samsung_smdk4412-d177fbc2f0c263b06c18bda2eb46200a31bcbebd.zip
kernel_samsung_smdk4412-d177fbc2f0c263b06c18bda2eb46200a31bcbebd.tar.gz
kernel_samsung_smdk4412-d177fbc2f0c263b06c18bda2eb46200a31bcbebd.tar.bz2
SELinux: per-command whitelisting of ioctls
note that this patch depends on a prior patch that is already in android-3.4 but has not apparently found its way into the msm 3.4 branches (but is included in exynos and tegra), https://android-review.googlesource.com/#/c/92962/ Extend the generic ioctl permission check with support for per-command filtering. Source/target/class sets including the ioctl permission may additionally include a set of commands. Example: allow <source> <target>:<class> { 0x8910-0x8926 0x892A-0x8935 } auditallow <source> <target>:<class> 0x892A When ioctl commands are omitted only the permissions are checked. This feature is intended to provide finer granularity for the ioctl permission which may be too imprecise in some circumstances. For example, the same driver may use ioctls to provide important and benign functionality such as driver version or socket type as well as dangerous capabilities such as debugging features, read/write/execute to physical memory or access to sensitive data. Per-command filtering provides a mechanism to reduce the attack surface of the kernel, and limit applications to the subset of commands required. The format of the policy binary has been modified to include ioctl commands, and the policy version number has been incremented to POLICYDB_VERSION_IOCTL_OPERATIONS=30 to account for the format change. Bug: 18087110 Signed-off-by: Jeff Vander Stoep <jeffv@google.com> Change-Id: Ibf0e36728f6f3f0d5af56ccdeddee40800af689d SELinux: use deletion-safe iterator to free list This code is not exercised by policy version 26, but will be upon upgrade to policy version 30. Bug: 18087110 Signed-off-by: Jeff Vander Stoep <jeffv@google.com> Change-Id: I07c6f34607713294a6a12c43a64d9936f0602200 SELinux: ss: Fix policy write for ioctl operations Security server omits the type field when writing out the contents of the avtab from /sys/fs/selinux/policy. This leads to a corrupt output. No impact on the running kernel or its loaded policy. Impacts CTS neverallow tests. Bug: 20665861 Signed-off-by: Jeff Vander Stoep <jeffv@google.com> (cherry picked from commit 8cdfb356b51e29494ca0b9e4e86727d6f841a52d) Change-Id: I657e18013dd5a1f40052bc2b02dd8e0afee9bcfb selinux: correctly label /proc inodes in use before the policy is loaded commit f64410ec665479d7b4b77b7519e814253ed0f686 upstream. This patch is based on an earlier patch by Eric Paris, he describes the problem below: "If an inode is accessed before policy load it will get placed on a list of inodes to be initialized after policy load. After policy load we call inode_doinit() which calls inode_doinit_with_dentry() on all inodes accessed before policy load. In the case of inodes in procfs that means we'll end up at the bottom where it does: /* Default to the fs superblock SID. */ isec->sid = sbsec->sid; if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { if (opt_dentry) { isec->sclass = inode_mode_to_security_class(...) rc = selinux_proc_get_sid(opt_dentry, isec->sclass, &sid); if (rc) goto out_unlock; isec->sid = sid; } } Since opt_dentry is null, we'll never call selinux_proc_get_sid() and will leave the inode labeled with the label on the superblock. I believe a fix would be to mimic the behavior of xattrs. Look for an alias of the inode. If it can't be found, just leave the inode uninitialized (and pick it up later) if it can be found, we should be able to call selinux_proc_get_sid() ..." On a system exhibiting this problem, you will notice a lot of files in /proc with the generic "proc_t" type (at least the ones that were accessed early in the boot), for example: # ls -Z /proc/sys/kernel/shmmax | awk '{ print $4 " " $5 }' system_u:object_r:proc_t:s0 /proc/sys/kernel/shmmax However, with this patch in place we see the expected result: # ls -Z /proc/sys/kernel/shmmax | awk '{ print $4 " " $5 }' system_u:object_r:sysctl_kernel_t:s0 /proc/sys/kernel/shmmax Cc: Eric Paris <eparis@redhat.com> Signed-off-by: Paul Moore <pmoore@redhat.com> Acked-by: Eric Paris <eparis@redhat.com> Change-Id: I7742b4b7e53b45e4dd13d99c39553a927aa4a7e9
Diffstat (limited to 'security/selinux/ss/avtab.c')
-rw-r--r--security/selinux/ss/avtab.c91
1 files changed, 76 insertions, 15 deletions
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index a3dd9fa..2e4ff00 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -24,6 +24,7 @@
#include "policydb.h"
static struct kmem_cache *avtab_node_cachep;
+static struct kmem_cache *avtab_operation_cachep;
static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
{
@@ -37,11 +38,24 @@ avtab_insert_node(struct avtab *h, int hvalue,
struct avtab_key *key, struct avtab_datum *datum)
{
struct avtab_node *newnode;
+ struct avtab_operation *ops;
newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
if (newnode == NULL)
return NULL;
newnode->key = *key;
- newnode->datum = *datum;
+
+ if (key->specified & AVTAB_OP) {
+ ops = kmem_cache_zalloc(avtab_operation_cachep, GFP_KERNEL);
+ if (ops == NULL) {
+ kmem_cache_free(avtab_node_cachep, newnode);
+ return NULL;
+ }
+ *ops = *(datum->u.ops);
+ newnode->datum.u.ops = ops;
+ } else {
+ newnode->datum.u.data = datum->u.data;
+ }
+
if (prev) {
newnode->next = prev->next;
prev->next = newnode;
@@ -70,8 +84,11 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class == cur->key.target_class &&
- (specified & cur->key.specified))
+ (specified & cur->key.specified)) {
+ if (specified & AVTAB_OPNUM)
+ break;
return -EEXIST;
+ }
if (key->source_type < cur->key.source_type)
break;
if (key->source_type == cur->key.source_type &&
@@ -232,6 +249,9 @@ void avtab_destroy(struct avtab *h)
while (cur) {
temp = cur;
cur = cur->next;
+ if (temp->key.specified & AVTAB_OP)
+ kmem_cache_free(avtab_operation_cachep,
+ temp->datum.u.ops);
kmem_cache_free(avtab_node_cachep, temp);
}
h->htable[i] = NULL;
@@ -320,7 +340,13 @@ static uint16_t spec_order[] = {
AVTAB_AUDITALLOW,
AVTAB_TRANSITION,
AVTAB_CHANGE,
- AVTAB_MEMBER
+ AVTAB_MEMBER,
+ AVTAB_OPNUM_ALLOWED,
+ AVTAB_OPNUM_AUDITALLOW,
+ AVTAB_OPNUM_DONTAUDIT,
+ AVTAB_OPTYPE_ALLOWED,
+ AVTAB_OPTYPE_AUDITALLOW,
+ AVTAB_OPTYPE_DONTAUDIT
};
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
@@ -330,10 +356,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
{
__le16 buf16[4];
u16 enabled;
- __le32 buf32[7];
u32 items, items2, val, vers = pol->policyvers;
struct avtab_key key;
struct avtab_datum datum;
+ struct avtab_operation ops;
+ __le32 buf32[ARRAY_SIZE(ops.op.perms)];
int i, rc;
unsigned set;
@@ -390,11 +417,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
return -EINVAL;
}
+ if (val & AVTAB_OP) {
+ printk(KERN_ERR "SELinux: avtab: entry has operations\n");
+ return -EINVAL;
+ }
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
key.specified = spec_order[i] | enabled;
- datum.data = le32_to_cpu(buf32[items++]);
+ datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
if (rc)
return rc;
@@ -413,7 +444,6 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
return rc;
}
-
items = 0;
key.source_type = le16_to_cpu(buf16[items++]);
key.target_type = le16_to_cpu(buf16[items++]);
@@ -437,14 +467,32 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
return -EINVAL;
}
- rc = next_entry(buf32, fp, sizeof(u32));
- if (rc) {
- printk(KERN_ERR "SELinux: avtab: truncated entry\n");
- return rc;
+ if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS)
+ || !(key.specified & AVTAB_OP)) {
+ rc = next_entry(buf32, fp, sizeof(u32));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ datum.u.data = le32_to_cpu(*buf32);
+ } else {
+ memset(&ops, 0, sizeof(struct avtab_operation));
+ rc = next_entry(&ops.type, fp, sizeof(u8));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(ops.op.perms));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ for (i = 0; i < ARRAY_SIZE(ops.op.perms); i++)
+ ops.op.perms[i] = le32_to_cpu(buf32[i]);
+ datum.u.ops = &ops;
}
- datum.data = le32_to_cpu(*buf32);
if ((key.specified & AVTAB_TYPE) &&
- !policydb_type_isvalid(pol, datum.data)) {
+ !policydb_type_isvalid(pol, datum.u.data)) {
printk(KERN_ERR "SELinux: avtab: invalid type\n");
return -EINVAL;
}
@@ -504,8 +552,9 @@ bad:
int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
{
__le16 buf16[4];
- __le32 buf32[1];
+ __le32 buf32[ARRAY_SIZE(cur->datum.u.ops->op.perms)];
int rc;
+ unsigned int i;
buf16[0] = cpu_to_le16(cur->key.source_type);
buf16[1] = cpu_to_le16(cur->key.target_type);
@@ -514,8 +563,16 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
rc = put_entry(buf16, sizeof(u16), 4, fp);
if (rc)
return rc;
- buf32[0] = cpu_to_le32(cur->datum.data);
- rc = put_entry(buf32, sizeof(u32), 1, fp);
+
+ if (cur->key.specified & AVTAB_OP) {
+ for (i = 0; i < ARRAY_SIZE(cur->datum.u.ops->op.perms); i++)
+ buf32[i] = cpu_to_le32(cur->datum.u.ops->op.perms[i]);
+ rc = put_entry(buf32, sizeof(u32),
+ ARRAY_SIZE(cur->datum.u.ops->op.perms), fp);
+ } else {
+ buf32[0] = cpu_to_le32(cur->datum.u.data);
+ rc = put_entry(buf32, sizeof(u32), 1, fp);
+ }
if (rc)
return rc;
return 0;
@@ -548,9 +605,13 @@ void avtab_cache_init(void)
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
0, SLAB_PANIC, NULL);
+ avtab_operation_cachep = kmem_cache_create("avtab_operation",
+ sizeof(struct avtab_operation),
+ 0, SLAB_PANIC, NULL);
}
void avtab_cache_destroy(void)
{
kmem_cache_destroy(avtab_node_cachep);
+ kmem_cache_destroy(avtab_operation_cachep);
}