diff options
Diffstat (limited to 'security/selinux/ss')
| -rw-r--r-- | security/selinux/ss/Makefile | 9 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.c | 261 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.h | 43 | ||||
| -rw-r--r-- | security/selinux/ss/conditional.c | 295 | ||||
| -rw-r--r-- | security/selinux/ss/conditional.h | 11 | ||||
| -rw-r--r-- | security/selinux/ss/constraint.h | 1 | ||||
| -rw-r--r-- | security/selinux/ss/context.h | 78 | ||||
| -rw-r--r-- | security/selinux/ss/ebitmap.c | 430 | ||||
| -rw-r--r-- | security/selinux/ss/ebitmap.h | 119 | ||||
| -rw-r--r-- | security/selinux/ss/hashtab.c | 17 | ||||
| -rw-r--r-- | security/selinux/ss/hashtab.h | 14 | ||||
| -rw-r--r-- | security/selinux/ss/mls.c | 513 | ||||
| -rw-r--r-- | security/selinux/ss/mls.h | 57 | ||||
| -rw-r--r-- | security/selinux/ss/mls_types.h | 11 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 3009 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.h | 136 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 2577 | ||||
| -rw-r--r-- | security/selinux/ss/sidtab.c | 122 | ||||
| -rw-r--r-- | security/selinux/ss/sidtab.h | 9 | ||||
| -rw-r--r-- | security/selinux/ss/status.c | 126 | ||||
| -rw-r--r-- | security/selinux/ss/symtab.c | 11 |
21 files changed, 5971 insertions, 1878 deletions
diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile deleted file mode 100644 index bad78779b9b..00000000000 --- a/security/selinux/ss/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for building the SELinux security server as part of the kernel tree. -# - -EXTRA_CFLAGS += -Isecurity/selinux/include -obj-y := ss.o - -ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o - diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index d049c7acbc8..a3dd9faa19c 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -6,40 +6,40 @@ /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * * Copyright (C) 2003 Tresys Technology, LLC * 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 + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. + * + * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> + * Tuned number of hash slots for avtab to reduce memory usage */ #include <linux/kernel.h> #include <linux/slab.h> -#include <linux/vmalloc.h> #include <linux/errno.h> - #include "avtab.h" #include "policydb.h" -#define AVTAB_HASH(keyp) \ -((keyp->target_class + \ - (keyp->target_type << 2) + \ - (keyp->source_type << 9)) & \ - AVTAB_HASH_MASK) +static struct kmem_cache *avtab_node_cachep; -static kmem_cache_t *avtab_node_cachep; +static inline int avtab_hash(struct avtab_key *keyp, u16 mask) +{ + return ((keyp->target_class + (keyp->target_type << 2) + + (keyp->source_type << 9)) & mask); +} static struct avtab_node* avtab_insert_node(struct avtab *h, int hvalue, - struct avtab_node * prev, struct avtab_node * cur, + struct avtab_node *prev, struct avtab_node *cur, struct avtab_key *key, struct avtab_datum *datum) { - struct avtab_node * newnode; - newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL); + struct avtab_node *newnode; + newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); if (newnode == NULL) return NULL; - memset(newnode, 0, sizeof(struct avtab_node)); newnode->key = *key; newnode->datum = *datum; if (prev) { @@ -60,10 +60,10 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat struct avtab_node *prev, *cur, *newnode; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return -EINVAL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (prev = NULL, cur = h->htable[hvalue]; cur; prev = cur, cur = cur->next) { @@ -84,7 +84,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat } newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); - if(!newnode) + if (!newnode) return -ENOMEM; return 0; @@ -95,15 +95,15 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat * It also returns a pointer to the node inserted. */ struct avtab_node * -avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum) +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) + if (!h || !h->htable) return NULL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (prev = NULL, cur = h->htable[hvalue]; cur; prev = cur, cur = cur->next) { @@ -122,9 +122,7 @@ avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_da 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) @@ -133,10 +131,10 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) struct avtab_node *cur; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return NULL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (cur = h->htable[hvalue]; cur; cur = cur->next) { if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && @@ -168,10 +166,10 @@ avtab_search_node(struct avtab *h, struct avtab_key *key) struct avtab_node *cur; u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD); - if (!h) + if (!h || !h->htable) return NULL; - hvalue = AVTAB_HASH(key); + hvalue = avtab_hash(key, h->mask); for (cur = h->htable[hvalue]; cur; cur = cur->next) { if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && @@ -229,41 +227,72 @@ void avtab_destroy(struct avtab *h) if (!h || !h->htable) return; - for (i = 0; i < AVTAB_SIZE; i++) { + 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); } h->htable[i] = NULL; } - vfree(h->htable); + kfree(h->htable); h->htable = NULL; + h->nslot = 0; + h->mask = 0; } - int avtab_init(struct avtab *h) { - int i; + h->htable = NULL; + h->nel = 0; + return 0; +} - h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE); +int avtab_alloc(struct avtab *h, u32 nrules) +{ + u16 mask = 0; + u32 shift = 0; + u32 work = nrules; + u32 nslot = 0; + + if (nrules == 0) + goto avtab_alloc_out; + + while (work) { + work = work >> 1; + shift++; + } + if (shift > 2) + shift = shift - 2; + nslot = 1 << shift; + if (nslot > MAX_AVTAB_HASH_BUCKETS) + nslot = MAX_AVTAB_HASH_BUCKETS; + mask = nslot - 1; + + h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL); if (!h->htable) return -ENOMEM; - for (i = 0; i < AVTAB_SIZE; i++) - h->htable[i] = NULL; + + avtab_alloc_out: h->nel = 0; + h->nslot = nslot; + h->mask = mask; + printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n", + h->nslot, nrules); return 0; } void avtab_hash_eval(struct avtab *h, char *tag) { int i, chain_len, slots_used, max_chain_len; + unsigned long long chain2_len_sum; struct avtab_node *cur; slots_used = 0; max_chain_len = 0; - for (i = 0; i < AVTAB_SIZE; i++) { + chain2_len_sum = 0; + for (i = 0; i < h->nslot; i++) { cur = h->htable[i]; if (cur) { slots_used++; @@ -275,12 +304,14 @@ void avtab_hash_eval(struct avtab *h, char *tag) if (chain_len > max_chain_len) max_chain_len = chain_len; + chain2_len_sum += chain_len * chain_len; } } - printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " - "chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE, - max_chain_len); + printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " + "longest chain length %d sum of chain length^2 %llu\n", + tag, h->nel, slots_used, h->nslot, max_chain_len, + chain2_len_sum); } static uint16_t spec_order[] = { @@ -292,71 +323,72 @@ static uint16_t spec_order[] = { AVTAB_MEMBER }; -int avtab_read_item(void *fp, u32 vers, struct avtab *a, - int (*insertf)(struct avtab *a, struct avtab_key *k, +int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, + int (*insertf)(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *p), void *p) { __le16 buf16[4]; u16 enabled; __le32 buf32[7]; - u32 items, items2, val; + u32 items, items2, val, vers = pol->policyvers; struct avtab_key key; struct avtab_datum datum; int i, rc; + unsigned set; memset(&key, 0, sizeof(struct avtab_key)); memset(&datum, 0, sizeof(struct avtab_datum)); if (vers < POLICYDB_VERSION_AVTAB) { rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { - printk(KERN_ERR "security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } items2 = le32_to_cpu(buf32[0]); if (items2 > ARRAY_SIZE(buf32)) { - printk(KERN_ERR "security: avtab: entry overflow\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: entry overflow\n"); + return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32)*items2); - if (rc < 0) { - printk(KERN_ERR "security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } items = 0; val = le32_to_cpu(buf32[items++]); key.source_type = (u16)val; if (key.source_type != val) { - printk("security: avtab: truncated source type\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: truncated source type\n"); + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_type = (u16)val; if (key.target_type != val) { - printk("security: avtab: truncated target type\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: truncated target type\n"); + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_class = (u16)val; if (key.target_class != val) { - printk("security: avtab: truncated target class\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: truncated target class\n"); + return -EINVAL; } val = le32_to_cpu(buf32[items++]); enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0; if (!(val & (AVTAB_AV | AVTAB_TYPE))) { - printk("security: avtab: null entry\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: null entry\n"); + return -EINVAL; } if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) { - printk("security: avtab: entry has both access vectors and types\n"); - return -1; + printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(spec_order); i++) { @@ -364,21 +396,22 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a, key.specified = spec_order[i] | enabled; datum.data = le32_to_cpu(buf32[items++]); rc = insertf(a, &key, &datum, p); - if (rc) return rc; + if (rc) + return rc; } } if (items != items2) { - printk("security: avtab: entry only had %d items, expected %d\n", items2, items); - return -1; + printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); + return -EINVAL; } return 0; } rc = next_entry(buf16, fp, sizeof(u16)*4); - if (rc < 0) { - printk("security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } items = 0; @@ -387,12 +420,34 @@ int avtab_read_item(void *fp, u32 vers, struct avtab *a, key.target_class = le16_to_cpu(buf16[items++]); key.specified = le16_to_cpu(buf16[items++]); + if (!policydb_type_isvalid(pol, key.source_type) || + !policydb_type_isvalid(pol, key.target_type) || + !policydb_class_isvalid(pol, key.target_class)) { + printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); + return -EINVAL; + } + + set = 0; + for (i = 0; i < ARRAY_SIZE(spec_order); i++) { + if (key.specified & spec_order[i]) + set++; + } + if (!set || set > 1) { + printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); + return -EINVAL; + } + rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { - printk("security: avtab: truncated entry\n"); - return -1; + if (rc) { + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; } datum.data = le32_to_cpu(*buf32); + if ((key.specified & AVTAB_TYPE) && + !policydb_type_isvalid(pol, datum.data)) { + printk(KERN_ERR "SELinux: avtab: invalid type\n"); + return -EINVAL; + } return insertf(a, &key, &datum, p); } @@ -402,7 +457,7 @@ static int avtab_insertf(struct avtab *a, struct avtab_key *k, return avtab_insert(a, k, d); } -int avtab_read(struct avtab *a, void *fp, u32 vers) +int avtab_read(struct avtab *a, void *fp, struct policydb *pol) { int rc; __le32 buf[1]; @@ -411,24 +466,28 @@ int avtab_read(struct avtab *a, void *fp, u32 vers) rc = next_entry(buf, fp, sizeof(u32)); if (rc < 0) { - printk(KERN_ERR "security: avtab: truncated table\n"); + printk(KERN_ERR "SELinux: avtab: truncated table\n"); goto bad; } nel = le32_to_cpu(buf[0]); if (!nel) { - printk(KERN_ERR "security: avtab: table is empty\n"); + printk(KERN_ERR "SELinux: avtab: table is empty\n"); rc = -EINVAL; goto bad; } + + rc = avtab_alloc(a, nel); + if (rc) + goto bad; + for (i = 0; i < nel; i++) { - rc = avtab_read_item(fp,vers, a, avtab_insertf, NULL); + rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL); if (rc) { if (rc == -ENOMEM) - printk(KERN_ERR "security: avtab: out of memory\n"); + printk(KERN_ERR "SELinux: avtab: out of memory\n"); else if (rc == -EEXIST) - printk(KERN_ERR "security: avtab: duplicate entry\n"); - else - rc = -EINVAL; + printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); + goto bad; } } @@ -442,14 +501,56 @@ bad: goto out; } +int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) +{ + __le16 buf16[4]; + __le32 buf32[1]; + int rc; + + buf16[0] = cpu_to_le16(cur->key.source_type); + buf16[1] = cpu_to_le16(cur->key.target_type); + buf16[2] = cpu_to_le16(cur->key.target_class); + buf16[3] = cpu_to_le16(cur->key.specified); + 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 (rc) + return rc; + return 0; +} + +int avtab_write(struct policydb *p, struct avtab *a, void *fp) +{ + unsigned int i; + int rc = 0; + struct avtab_node *cur; + __le32 buf[1]; + + buf[0] = cpu_to_le32(a->nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (i = 0; i < a->nslot; i++) { + for (cur = a->htable[i]; cur; cur = cur->next) { + rc = avtab_write_item(p, cur, fp); + if (rc) + return rc; + } + } + + return rc; +} void avtab_cache_init(void) { avtab_node_cachep = kmem_cache_create("avtab_node", sizeof(struct avtab_node), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); } void avtab_cache_destroy(void) { - kmem_cache_destroy (avtab_node_cachep); + kmem_cache_destroy(avtab_node_cachep); } diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 0a90d939af9..63ce2f9e441 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -14,8 +14,11 @@ * * Copyright (C) 2003 Tresys Technology, LLC * 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 + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. + * + * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> + * Tuned number of hash slots for avtab to reduce memory usage */ #ifndef _SS_AVTAB_H_ #define _SS_AVTAB_H_ @@ -24,16 +27,16 @@ struct avtab_key { u16 source_type; /* source type */ u16 target_type; /* target type */ u16 target_class; /* target object class */ -#define AVTAB_ALLOWED 1 -#define AVTAB_AUDITALLOW 2 -#define AVTAB_AUDITDENY 4 -#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) -#define AVTAB_TRANSITION 16 -#define AVTAB_MEMBER 32 -#define AVTAB_CHANGE 64 -#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) -#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ -#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ +#define AVTAB_ALLOWED 0x0001 +#define AVTAB_AUDITALLOW 0x0002 +#define AVTAB_AUDITDENY 0x0004 +#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) +#define AVTAB_TRANSITION 0x0010 +#define AVTAB_MEMBER 0x0020 +#define AVTAB_CHANGE 0x0040 +#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) +#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ +#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ u16 specified; /* what field is specified */ }; @@ -50,19 +53,26 @@ struct avtab_node { struct avtab { struct avtab_node **htable; u32 nel; /* number of elements */ + u32 nslot; /* number of hash slots */ + u16 mask; /* mask to compute hash func */ + }; int avtab_init(struct avtab *); +int avtab_alloc(struct avtab *, u32); struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k); void avtab_destroy(struct avtab *h); void avtab_hash_eval(struct avtab *h, char *tag); -int avtab_read_item(void *fp, uint32_t vers, struct avtab *a, +struct policydb; +int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, int (*insert)(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *p), void *p); -int avtab_read(struct avtab *a, void *fp, u32 vers); +int avtab_read(struct avtab *a, void *fp, struct policydb *pol); +int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp); +int avtab_write(struct policydb *p, struct avtab *a, void *fp); struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum); @@ -74,11 +84,8 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified void avtab_cache_init(void); void avtab_cache_destroy(void); -#define AVTAB_HASH_BITS 15 -#define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS) -#define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1) - -#define AVTAB_SIZE AVTAB_HASH_BUCKETS +#define MAX_AVTAB_HASH_BITS 11 +#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS) #endif /* _SS_AVTAB_H_ */ diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index d2737edba54..377d148e715 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -1,9 +1,9 @@ /* Authors: Karl MacMillan <kmacmillan@tresys.com> - * Frank Mayer <mayerf@tresys.com> + * Frank Mayer <mayerf@tresys.com> * * Copyright (C) 2003 - 2004 Tresys Technology, LLC * 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 + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/spinlock.h> -#include <asm/semaphore.h> #include <linux/slab.h> #include "security.h" @@ -30,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)) @@ -90,29 +89,27 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) int evaluate_cond_node(struct policydb *p, struct cond_node *node) { int new_state; - struct cond_av_list* cur; + struct cond_av_list *cur; new_state = cond_evaluate_expr(p, node->expr); if (new_state != node->cur_state) { node->cur_state = new_state; if (new_state == -1) - printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n"); + 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) { - if (new_state <= 0) { + for (cur = node->true_list; cur; cur = cur->next) { + if (new_state <= 0) cur->node->key.specified &= ~AVTAB_ENABLED; - } else { + 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) { + if (new_state) cur->node->key.specified &= ~AVTAB_ENABLED; - } else { + else cur->node->key.specified |= AVTAB_ENABLED; - } } } return 0; @@ -120,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node) int cond_policydb_init(struct policydb *p) { + int rc; + p->bool_val_to_struct = NULL; p->cond_list = NULL; - if (avtab_init(&p->te_cond_avtab)) - return -1; + + rc = avtab_init(&p->te_cond_avtab); + if (rc) + return rc; return 0; } @@ -131,7 +132,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); @@ -142,7 +143,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); } @@ -158,7 +159,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); } @@ -174,10 +175,10 @@ void cond_policydb_destroy(struct policydb *p) int cond_init_bool_indexes(struct policydb *p) { kfree(p->bool_val_to_struct); - p->bool_val_to_struct = (struct cond_bool_datum**) - kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL); + p->bool_val_to_struct = + kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL); if (!p->bool_val_to_struct) - return -1; + return -ENOMEM; return 0; } @@ -192,6 +193,7 @@ int cond_index_bool(void *key, void *datum, void *datap) { struct policydb *p; struct cond_bool_datum *booldatum; + struct flex_array *fa; booldatum = datum; p = datap; @@ -199,8 +201,11 @@ int cond_index_bool(void *key, void *datum, void *datap) if (!booldatum->value || booldatum->value > p->p_bools.nprim) return -EINVAL; - p->p_bool_val_to_name[booldatum->value - 1] = key; - p->bool_val_to_struct[booldatum->value -1] = booldatum; + fa = p->sym_val_to_name[SYM_BOOLS]; + if (flex_array_put_ptr(fa, booldatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); + p->bool_val_to_struct[booldatum->value - 1] = booldatum; return 0; } @@ -222,38 +227,40 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); if (!booldatum) - return -1; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto err; booldatum->value = le32_to_cpu(buf[0]); booldatum->state = le32_to_cpu(buf[1]); + rc = -EINVAL; if (!bool_isvalid(booldatum)) goto err; len = le32_to_cpu(buf[2]); + rc = -ENOMEM; key = kmalloc(len + 1, GFP_KERNEL); if (!key) goto err; rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto err; - key[len] = 0; - if (hashtab_insert(h, key, booldatum)) + key[len] = '\0'; + rc = hashtab_insert(h, key, booldatum); + if (rc) goto err; return 0; err: cond_destroy_bool(key, booldatum, NULL); - return -1; + return rc; } -struct cond_insertf_data -{ +struct cond_insertf_data { struct policydb *p; struct cond_av_list *other; struct cond_av_list *head; @@ -267,7 +274,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum struct cond_av_list *other = data->other, *list, *cur; struct avtab_node *node_ptr; u8 found; - + int rc = -EINVAL; /* * For type rules we have to make certain there aren't any @@ -276,7 +283,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum */ if (k->specified & AVTAB_TYPE) { if (avtab_search(&p->te_avtab, k)) { - printk("security: type rule already exists outside of a conditional."); + printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n"); goto err; } /* @@ -291,24 +298,24 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum node_ptr = avtab_search_node(&p->te_cond_avtab, k); if (node_ptr) { if (avtab_search_node_next(node_ptr, k->specified)) { - printk("security: too many conflicting type rules."); + printk(KERN_ERR "SELinux: too many conflicting type rules.\n"); 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; } } if (!found) { - printk("security: conflicting type rules.\n"); + printk(KERN_ERR "SELinux: conflicting type rules.\n"); goto err; } } } else { if (avtab_search(&p->te_cond_avtab, k)) { - printk("security: conflicting type rules when adding type rule for true.\n"); + printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n"); goto err; } } @@ -316,13 +323,16 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); if (!node_ptr) { - printk("security: could not insert rule."); + printk(KERN_ERR "SELinux: could not insert rule.\n"); + rc = -ENOMEM; goto err; } list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); - if (!list) + if (!list) { + rc = -ENOMEM; goto err; + } list->node = node_ptr; if (!data->head) @@ -335,7 +345,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum err: cond_av_list_destroy(data->head); data->head = NULL; - return -1; + return rc; } static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) @@ -349,23 +359,22 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); - if (len == 0) { + if (len == 0) return 0; - } data.p = p; data.other = other; data.head = NULL; data.tail = NULL; for (i = 0; i < len; i++) { - rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, cond_insertf, &data); + rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf, + &data); if (rc) return rc; - } *ret_list = data.head; @@ -375,12 +384,12 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * static int expr_isvalid(struct policydb *p, struct cond_expr *expr) { if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { - printk("security: conditional expressions uses unknown operator.\n"); + printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n"); return 0; } if (expr->bool > p->p_bools.nprim) { - printk("security: conditional expressions uses unknown bool.\n"); + printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n"); return 0; } return 1; @@ -394,53 +403,55 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) struct cond_expr *expr = NULL, *last = NULL; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; node->cur_state = le32_to_cpu(buf[0]); len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; /* expr */ len = le32_to_cpu(buf[0]); - for (i = 0; i < len; i++ ) { + for (i = 0; i < len; i++) { rc = next_entry(buf, fp, sizeof(u32) * 2); - if (rc < 0) + if (rc) goto err; + rc = -ENOMEM; expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); - if (!expr) { + if (!expr) goto err; - } expr->expr_type = le32_to_cpu(buf[0]); expr->bool = le32_to_cpu(buf[1]); if (!expr_isvalid(p, expr)) { + rc = -EINVAL; kfree(expr); goto err; } - if (i == 0) { + if (i == 0) node->expr = expr; - } else { + else last->next = expr; - } last = expr; } - if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) + rc = cond_read_av_list(p, fp, &node->true_list, NULL); + if (rc) goto err; - if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) + rc = cond_read_av_list(p, fp, &node->false_list, node->true_list); + if (rc) goto err; return 0; err: cond_node_destroy(node); - return -1; + return rc; } int cond_read_list(struct policydb *p, void *fp) @@ -451,33 +462,161 @@ int cond_read_list(struct policydb *p, void *fp) int rc; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); + rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel); + if (rc) + goto err; + for (i = 0; i < len; i++) { + rc = -ENOMEM; node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); if (!node) goto err; - if (cond_read_node(p, node, fp) != 0) + rc = cond_read_node(p, node, fp); + if (rc) goto err; - if (i == 0) { + if (i == 0) p->cond_list = node; - } else { + else last->next = node; - } last = node; } return 0; err: cond_list_destroy(p->cond_list); p->cond_list = NULL; - return -1; + return rc; +} + +int cond_write_bool(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct cond_bool_datum *booldatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[3]; + u32 len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(booldatum->value); + buf[1] = cpu_to_le32(booldatum->state); + buf[2] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + return 0; +} + +/* + * cond_write_cond_av_list doesn't write out the av_list nodes. + * Instead it writes out the key/value pairs from the avtab. This + * is necessary because there is no way to uniquely identifying rules + * in the avtab so it is not possible to associate individual rules + * in the avtab with a conditional without saving them as part of + * the conditional. This means that the avtab with the conditional + * rules will not be saved but will be rebuilt on policy load. + */ +static int cond_write_av_list(struct policydb *p, + struct cond_av_list *list, struct policy_file *fp) +{ + __le32 buf[1]; + struct cond_av_list *cur_list; + u32 len; + int rc; + + len = 0; + for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) + len++; + + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + if (len == 0) + return 0; + + for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) { + rc = avtab_write_item(p, cur_list->node, fp); + if (rc) + return rc; + } + + return 0; } +static int cond_write_node(struct policydb *p, struct cond_node *node, + struct policy_file *fp) +{ + struct cond_expr *cur_expr; + __le32 buf[2]; + int rc; + u32 len = 0; + + buf[0] = cpu_to_le32(node->cur_state); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) + len++; + + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) { + buf[0] = cpu_to_le32(cur_expr->expr_type); + buf[1] = cpu_to_le32(cur_expr->bool); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + } + + rc = cond_write_av_list(p, node->true_list, fp); + if (rc) + return rc; + rc = cond_write_av_list(p, node->false_list, fp); + if (rc) + return rc; + + return 0; +} + +int cond_write_list(struct policydb *p, struct cond_node *list, void *fp) +{ + struct cond_node *cur; + u32 len; + __le32 buf[1]; + int rc; + + len = 0; + for (cur = list; cur != NULL; cur = cur->next) + len++; + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + for (cur = list; cur != NULL; cur = cur->next) { + rc = cond_write_node(p, cur, fp); + if (rc) + return rc; + } + + return 0; +} /* Determine whether additional permissions are granted by the conditional * av table, and if so, add them to the result */ @@ -485,24 +624,24 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi { struct avtab_node *node; - if(!ctab || !key || !avd) + 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))) + if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) avd->allowed |= node->datum.data; - if ( (u16) (AVTAB_AUDITDENY|AVTAB_ENABLED) == - (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) + if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) /* Since a '0' in an auditdeny mask represents a * permission we do NOT want to audit (dontaudit), we use * the '&' operand to ensure that all '0's in the mask * are retained (much unlike the allow and auditallow cases). */ avd->auditdeny &= node->datum.data; - if ( (u16) (AVTAB_AUDITALLOW|AVTAB_ENABLED) == - (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) + if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) avd->auditallow |= node->datum.data; } return; diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index f3a1fc6e5d6..4d1f8746650 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -13,6 +13,7 @@ #include "avtab.h" #include "symtab.h" #include "policydb.h" +#include "../include/conditional.h" #define COND_EXPR_MAXDEPTH 10 @@ -28,7 +29,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; @@ -59,16 +60,18 @@ struct cond_node { struct cond_node *next; }; -int cond_policydb_init(struct policydb* p); -void cond_policydb_destroy(struct policydb* p); +int cond_policydb_init(struct policydb *p); +void cond_policydb_destroy(struct policydb *p); -int cond_init_bool_indexes(struct policydb* p); +int cond_init_bool_indexes(struct policydb *p); int cond_destroy_bool(void *key, void *datum, void *p); int cond_index_bool(void *key, void *datum, void *datap); int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp); int cond_read_list(struct policydb *p, void *fp); +int cond_write_bool(void *key, void *datum, void *ptr); +int cond_write_list(struct policydb *p, struct cond_node *list, void *fp); void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h index 149dda731fd..96fd947c494 100644 --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -48,6 +48,7 @@ struct constraint_expr { u32 op; /* operator */ struct ebitmap names; /* names */ + struct type_set *type_names; struct constraint_expr *next; /* next expression */ }; diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 0562bacb7b9..212e3479a0d 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -27,7 +27,9 @@ struct context { u32 user; u32 role; u32 type; + u32 len; /* length of string in bytes */ struct mls_range range; + char *str; /* string representation if context cannot be mapped. */ }; static inline void mls_context_init(struct context *c) @@ -39,14 +41,51 @@ static inline int mls_context_cpy(struct context *dst, struct context *src) { int rc; - if (!selinux_mls_enabled) - return 0; + dst->range.level[0].sens = src->range.level[0].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); + if (rc) + goto out; + + dst->range.level[1].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + +/* + * Sets both levels in the MLS range of 'dst' to the low level of 'src'. + */ +static inline int mls_context_cpy_low(struct context *dst, struct context *src) +{ + int rc; dst->range.level[0].sens = src->range.level[0].sens; rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); if (rc) goto out; + dst->range.level[1].sens = src->range.level[0].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[0].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + +/* + * Sets both levels in the MLS range of 'dst' to the high level of 'src'. + */ +static inline int mls_context_cpy_high(struct context *dst, struct context *src) +{ + int rc; + + dst->range.level[0].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].cat); + if (rc) + goto out; + dst->range.level[1].sens = src->range.level[1].sens; rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); if (rc) @@ -57,20 +96,14 @@ out: static inline int mls_context_cmp(struct context *c1, struct context *c2) { - if (!selinux_mls_enabled) - return 1; - return ((c1->range.level[0].sens == c2->range.level[0].sens) && - ebitmap_cmp(&c1->range.level[0].cat,&c2->range.level[0].cat) && + ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) && (c1->range.level[1].sens == c2->range.level[1].sens) && - ebitmap_cmp(&c1->range.level[1].cat,&c2->range.level[1].cat)); + ebitmap_cmp(&c1->range.level[1].cat, &c2->range.level[1].cat)); } static inline void mls_context_destroy(struct context *c) { - if (!selinux_mls_enabled) - return; - ebitmap_destroy(&c->range.level[0].cat); ebitmap_destroy(&c->range.level[1].cat); mls_context_init(c); @@ -83,20 +116,43 @@ static inline void context_init(struct context *c) static inline int context_cpy(struct context *dst, struct context *src) { + int rc; + dst->user = src->user; dst->role = src->role; dst->type = src->type; - return mls_context_cpy(dst, src); + if (src->str) { + dst->str = kstrdup(src->str, GFP_ATOMIC); + if (!dst->str) + return -ENOMEM; + dst->len = src->len; + } else { + dst->str = NULL; + dst->len = 0; + } + rc = mls_context_cpy(dst, src); + if (rc) { + kfree(dst->str); + return rc; + } + return 0; } static inline void context_destroy(struct context *c) { c->user = c->role = c->type = 0; + kfree(c->str); + c->str = NULL; + c->len = 0; mls_context_destroy(c); } static inline int context_cmp(struct context *c1, struct context *c2) { + if (c1->len && c2->len) + return (c1->len == c2->len && !strcmp(c1->str, c2->str)); + if (c1->len || c2->len) + return 0; return ((c1->user == c2->user) && (c1->role == c2->role) && (c1->type == c2->type) && diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 47024a6e184..820313a04d4 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -3,12 +3,27 @@ * * Author : Stephen Smalley, <sds@epoch.ncsc.mil> */ +/* + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support to import/export the NetLabel category bitmap + * + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + */ +/* + * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com> + * Applied standard bit operations to improve bitmap scanning. + */ + #include <linux/kernel.h> #include <linux/slab.h> #include <linux/errno.h> +#include <net/netlabel.h> #include "ebitmap.h" #include "policydb.h" +#define BITS_PER_U64 (sizeof(u64) * 8) + int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) { struct ebitmap_node *n1, *n2; @@ -20,7 +35,7 @@ int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) n2 = e2->node; while (n1 && n2 && (n1->startbit == n2->startbit) && - (n1->map == n2->map)) { + !memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) { n1 = n1->next; n2 = n2->next; } @@ -45,7 +60,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return -ENOMEM; } new->startbit = n->startbit; - new->map = n->map; + memcpy(new->maps, n->maps, EBITMAP_SIZE / 8); new->next = NULL; if (prev) prev->next = new; @@ -59,22 +74,180 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return 0; } -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) +#ifdef CONFIG_NETLABEL +/** + * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap + * @ebmap: the ebitmap to export + * @catmap: the NetLabel category bitmap + * + * Description: + * Export a SELinux extensibile bitmap into a NetLabel category bitmap. + * Returns zero on success, negative values on error. + * + */ +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) +{ + struct ebitmap_node *e_iter = ebmap->node; + struct netlbl_lsm_secattr_catmap *c_iter; + u32 cmap_idx, cmap_sft; + int i; + + /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, + * however, it is not always compatible with an array of unsigned long + * in ebitmap_node. + * In addition, you should pay attention the following implementation + * assumes unsigned long has a width equal with or less than 64-bit. + */ + + if (e_iter == NULL) { + *catmap = NULL; + return 0; + } + + c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter == NULL) + return -ENOMEM; + *catmap = c_iter; + c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); + + while (e_iter) { + for (i = 0; i < EBITMAP_UNIT_NUMS; i++) { + unsigned int delta, e_startbit, c_endbit; + + e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE; + c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE; + if (e_startbit >= c_endbit) { + c_iter->next + = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter->next == NULL) + goto netlbl_export_failure; + c_iter = c_iter->next; + c_iter->startbit + = e_startbit & ~(NETLBL_CATMAP_SIZE - 1); + } + delta = e_startbit - c_iter->startbit; + cmap_idx = delta / NETLBL_CATMAP_MAPSIZE; + cmap_sft = delta % NETLBL_CATMAP_MAPSIZE; + c_iter->bitmap[cmap_idx] + |= e_iter->maps[i] << cmap_sft; + } + e_iter = e_iter->next; + } + + return 0; + +netlbl_export_failure: + netlbl_secattr_catmap_free(*catmap); + return -ENOMEM; +} + +/** + * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap + * @ebmap: the ebitmap to import + * @catmap: the NetLabel category bitmap + * + * Description: + * Import a NetLabel category bitmap into a SELinux extensibile bitmap. + * Returns zero on success, negative values on error. + * + */ +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) +{ + struct ebitmap_node *e_iter = NULL; + struct ebitmap_node *emap_prev = NULL; + struct netlbl_lsm_secattr_catmap *c_iter = catmap; + u32 c_idx, c_pos, e_idx, e_sft; + + /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, + * however, it is not always compatible with an array of unsigned long + * in ebitmap_node. + * In addition, you should pay attention the following implementation + * assumes unsigned long has a width equal with or less than 64-bit. + */ + + do { + for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { + unsigned int delta; + u64 map = c_iter->bitmap[c_idx]; + + if (!map) + continue; + + c_pos = c_iter->startbit + + c_idx * NETLBL_CATMAP_MAPSIZE; + if (!e_iter + || c_pos >= e_iter->startbit + EBITMAP_SIZE) { + e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + if (!e_iter) + goto netlbl_import_failure; + e_iter->startbit + = c_pos - (c_pos % EBITMAP_SIZE); + if (emap_prev == NULL) + ebmap->node = e_iter; + else + emap_prev->next = e_iter; + emap_prev = e_iter; + } + delta = c_pos - e_iter->startbit; + e_idx = delta / EBITMAP_UNIT_SIZE; + e_sft = delta % EBITMAP_UNIT_SIZE; + while (map) { + e_iter->maps[e_idx++] |= map & (-1UL); + map = EBITMAP_SHIFT_UNIT_SIZE(map); + } + } + c_iter = c_iter->next; + } while (c_iter); + if (e_iter != NULL) + ebmap->highbit = e_iter->startbit + EBITMAP_SIZE; + else + ebitmap_destroy(ebmap); + + return 0; + +netlbl_import_failure: + ebitmap_destroy(ebmap); + return -ENOMEM; +} +#endif /* CONFIG_NETLABEL */ + +/* + * Check to see if all the bits set in e2 are also set in e1. Optionally, + * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed + * last_e2bit. + */ +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit) { struct ebitmap_node *n1, *n2; + int i; if (e1->highbit < e2->highbit) return 0; n1 = e1->node; n2 = e2->node; + while (n1 && n2 && (n1->startbit <= n2->startbit)) { if (n1->startbit < n2->startbit) { n1 = n1->next; continue; } - if ((n1->map & n2->map) != n2->map) - return 0; + for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; ) + i--; /* Skip trailing NULL map entries */ + if (last_e2bit && (i >= 0)) { + u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE + + __fls(n2->maps[i]); + if (lastsetbit > last_e2bit) + return 0; + } + + while (i >= 0) { + if ((n1->maps[i] & n2->maps[i]) != n2->maps[i]) + return 0; + i--; + } n1 = n1->next; n2 = n2->next; @@ -95,12 +268,8 @@ int ebitmap_get_bit(struct ebitmap *e, unsigned long bit) n = e->node; while (n && (n->startbit <= bit)) { - if ((n->startbit + MAPSIZE) > bit) { - if (n->map & (MAPBIT << (bit - n->startbit))) - return 1; - else - return 0; - } + if ((n->startbit + EBITMAP_SIZE) > bit) + return ebitmap_node_get_bit(n, bit); n = n->next; } @@ -114,31 +283,35 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) prev = NULL; n = e->node; while (n && n->startbit <= bit) { - if ((n->startbit + MAPSIZE) > bit) { + if ((n->startbit + EBITMAP_SIZE) > bit) { if (value) { - n->map |= (MAPBIT << (bit - n->startbit)); + ebitmap_node_set_bit(n, bit); } else { - n->map &= ~(MAPBIT << (bit - n->startbit)); - if (!n->map) { - /* drop this node from the bitmap */ - - if (!n->next) { - /* - * this was the highest map - * within the bitmap - */ - if (prev) - e->highbit = prev->startbit + MAPSIZE; - else - e->highbit = 0; - } + unsigned int s; + + ebitmap_node_clr_bit(n, bit); + + s = find_first_bit(n->maps, EBITMAP_SIZE); + if (s < EBITMAP_SIZE) + return 0; + + /* drop this node from the bitmap */ + if (!n->next) { + /* + * this was the highest map + * within the bitmap + */ if (prev) - prev->next = n->next; + e->highbit = prev->startbit + + EBITMAP_SIZE; else - e->node = n->next; - - kfree(n); + e->highbit = 0; } + if (prev) + prev->next = n->next; + else + e->node = n->next; + kfree(n); } return 0; } @@ -153,12 +326,12 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) if (!new) return -ENOMEM; - new->startbit = bit & ~(MAPSIZE - 1); - new->map = (MAPBIT << (bit - new->startbit)); + new->startbit = bit - (bit % EBITMAP_SIZE); + ebitmap_node_set_bit(new, bit); if (!n) /* this node will be the highest map within the bitmap */ - e->highbit = new->startbit + MAPSIZE; + e->highbit = new->startbit + EBITMAP_SIZE; if (prev) { new->next = prev->next; @@ -192,11 +365,11 @@ void ebitmap_destroy(struct ebitmap *e) int ebitmap_read(struct ebitmap *e, void *fp) { - int rc; - struct ebitmap_node *n, *l; + struct ebitmap_node *n = NULL; + u32 mapunit, count, startbit, index; + u64 map; __le32 buf[3]; - u32 mapsize, count, i; - __le64 map; + int rc, i; ebitmap_init(e); @@ -204,88 +377,165 @@ int ebitmap_read(struct ebitmap *e, void *fp) if (rc < 0) goto out; - mapsize = le32_to_cpu(buf[0]); + mapunit = le32_to_cpu(buf[0]); e->highbit = le32_to_cpu(buf[1]); count = le32_to_cpu(buf[2]); - if (mapsize != MAPSIZE) { - printk(KERN_ERR "security: ebitmap: map size %u does not " - "match my size %Zd (high bit was %d)\n", mapsize, - MAPSIZE, e->highbit); + if (mapunit != BITS_PER_U64) { + printk(KERN_ERR "SELinux: ebitmap: map size %u does not " + "match my size %Zd (high bit was %d)\n", + mapunit, BITS_PER_U64, e->highbit); goto bad; } + + /* round up e->highbit */ + e->highbit += EBITMAP_SIZE - 1; + e->highbit -= (e->highbit % EBITMAP_SIZE); + if (!e->highbit) { e->node = NULL; goto ok; } - if (e->highbit & (MAPSIZE - 1)) { - printk(KERN_ERR "security: ebitmap: high bit (%d) is not a " - "multiple of the map size (%Zd)\n", e->highbit, MAPSIZE); - goto bad; - } - l = NULL; + for (i = 0; i < count; i++) { - rc = next_entry(buf, fp, sizeof(u32)); + rc = next_entry(&startbit, fp, sizeof(u32)); if (rc < 0) { - printk(KERN_ERR "security: ebitmap: truncated map\n"); + printk(KERN_ERR "SELinux: ebitmap: truncated map\n"); goto bad; } - n = kzalloc(sizeof(*n), GFP_KERNEL); - if (!n) { - printk(KERN_ERR "security: ebitmap: out of memory\n"); - rc = -ENOMEM; + startbit = le32_to_cpu(startbit); + + if (startbit & (mapunit - 1)) { + printk(KERN_ERR "SELinux: ebitmap start bit (%d) is " + "not a multiple of the map unit size (%u)\n", + startbit, mapunit); goto bad; } - - n->startbit = le32_to_cpu(buf[0]); - - if (n->startbit & (MAPSIZE - 1)) { - printk(KERN_ERR "security: ebitmap start bit (%d) is " - "not a multiple of the map size (%Zd)\n", - n->startbit, MAPSIZE); - goto bad_free; + if (startbit > e->highbit - mapunit) { + printk(KERN_ERR "SELinux: ebitmap start bit (%d) is " + "beyond the end of the bitmap (%u)\n", + startbit, (e->highbit - mapunit)); + goto bad; } - if (n->startbit > (e->highbit - MAPSIZE)) { - printk(KERN_ERR "security: ebitmap start bit (%d) is " - "beyond the end of the bitmap (%Zd)\n", - n->startbit, (e->highbit - MAPSIZE)); - goto bad_free; + + if (!n || startbit >= n->startbit + EBITMAP_SIZE) { + struct ebitmap_node *tmp; + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + printk(KERN_ERR + "SELinux: ebitmap: out of memory\n"); + rc = -ENOMEM; + goto bad; + } + /* round down */ + tmp->startbit = startbit - (startbit % EBITMAP_SIZE); + if (n) + n->next = tmp; + else + e->node = tmp; + n = tmp; + } else if (startbit <= n->startbit) { + printk(KERN_ERR "SELinux: ebitmap: start bit %d" + " comes after start bit %d\n", + startbit, n->startbit); + goto bad; } + rc = next_entry(&map, fp, sizeof(u64)); if (rc < 0) { - printk(KERN_ERR "security: ebitmap: truncated map\n"); - goto bad_free; + printk(KERN_ERR "SELinux: ebitmap: truncated map\n"); + goto bad; } - n->map = le64_to_cpu(map); + map = le64_to_cpu(map); - if (!n->map) { - printk(KERN_ERR "security: ebitmap: null map in " - "ebitmap (startbit %d)\n", n->startbit); - goto bad_free; + index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE; + while (map) { + n->maps[index++] = map & (-1UL); + map = EBITMAP_SHIFT_UNIT_SIZE(map); } - if (l) { - if (n->startbit <= l->startbit) { - printk(KERN_ERR "security: ebitmap: start " - "bit %d comes after start bit %d\n", - n->startbit, l->startbit); - goto bad_free; - } - l->next = n; - } else - e->node = n; - - l = n; } - ok: rc = 0; out: return rc; -bad_free: - kfree(n); bad: if (!rc) rc = -EINVAL; ebitmap_destroy(e); goto out; } + +int ebitmap_write(struct ebitmap *e, void *fp) +{ + struct ebitmap_node *n; + u32 count; + __le32 buf[3]; + u64 map; + int bit, last_bit, last_startbit, rc; + + buf[0] = cpu_to_le32(BITS_PER_U64); + + count = 0; + last_bit = 0; + last_startbit = -1; + ebitmap_for_each_positive_bit(e, n, bit) { + if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + count++; + last_startbit = rounddown(bit, BITS_PER_U64); + } + last_bit = roundup(bit + 1, BITS_PER_U64); + } + buf[1] = cpu_to_le32(last_bit); + buf[2] = cpu_to_le32(count); + + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + map = 0; + last_startbit = INT_MIN; + ebitmap_for_each_positive_bit(e, n, bit) { + if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + __le64 buf64[1]; + + /* this is the very first bit */ + if (!map) { + last_startbit = rounddown(bit, BITS_PER_U64); + map = (u64)1 << (bit - last_startbit); + continue; + } + + /* write the last node */ + buf[0] = cpu_to_le32(last_startbit); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + buf64[0] = cpu_to_le64(map); + rc = put_entry(buf64, sizeof(u64), 1, fp); + if (rc) + return rc; + + /* set up for the next node */ + map = 0; + last_startbit = rounddown(bit, BITS_PER_U64); + } + map |= (u64)1 << (bit - last_startbit); + } + /* write the last node */ + if (map) { + __le64 buf64[1]; + + /* write the last node */ + buf[0] = cpu_to_le32(last_startbit); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + buf64[0] = cpu_to_le64(map); + rc = put_entry(buf64, sizeof(u64), 1, fp); + if (rc) + return rc; + } + return 0; +} diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 8bf41055a6c..712c8a7b8e8 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -14,14 +14,26 @@ #ifndef _SS_EBITMAP_H_ #define _SS_EBITMAP_H_ -#define MAPTYPE u64 /* portion of bitmap in each node */ -#define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */ -#define MAPBIT 1ULL /* a bit in the node bitmap */ +#include <net/netlabel.h> + +#ifdef CONFIG_64BIT +#define EBITMAP_NODE_SIZE 64 +#else +#define EBITMAP_NODE_SIZE 32 +#endif + +#define EBITMAP_UNIT_NUMS ((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\ + / sizeof(unsigned long)) +#define EBITMAP_UNIT_SIZE BITS_PER_LONG +#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE) +#define EBITMAP_BIT 1ULL +#define EBITMAP_SHIFT_UNIT_SIZE(x) \ + (((x) >> EBITMAP_UNIT_SIZE / 2) >> EBITMAP_UNIT_SIZE / 2) struct ebitmap_node { - u32 startbit; /* starting position in the total bitmap */ - MAPTYPE map; /* this node's portion of the bitmap */ struct ebitmap_node *next; + unsigned long maps[EBITMAP_UNIT_NUMS]; + u32 startbit; }; struct ebitmap { @@ -30,13 +42,18 @@ struct ebitmap { }; #define ebitmap_length(e) ((e)->highbit) -#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0) -static inline unsigned int ebitmap_start(struct ebitmap *e, - struct ebitmap_node **n) +static inline unsigned int ebitmap_start_positive(struct ebitmap *e, + struct ebitmap_node **n) { - *n = e->node; - return ebitmap_startbit(e); + unsigned int ofs; + + for (*n = e->node; *n; *n = (*n)->next) { + ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); + if (ofs < EBITMAP_SIZE) + return (*n)->startbit + ofs; + } + return ebitmap_length(e); } static inline void ebitmap_init(struct ebitmap *e) @@ -44,35 +61,91 @@ static inline void ebitmap_init(struct ebitmap *e) memset(e, 0, sizeof(*e)); } -static inline unsigned int ebitmap_next(struct ebitmap_node **n, - unsigned int bit) +static inline unsigned int ebitmap_next_positive(struct ebitmap *e, + struct ebitmap_node **n, + unsigned int bit) { - if ((bit == ((*n)->startbit + MAPSIZE - 1)) && - (*n)->next) { - *n = (*n)->next; - return (*n)->startbit; - } + unsigned int ofs; + + ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1); + if (ofs < EBITMAP_SIZE) + return ofs + (*n)->startbit; - return (bit+1); + for (*n = (*n)->next; *n; *n = (*n)->next) { + ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); + if (ofs < EBITMAP_SIZE) + return ofs + (*n)->startbit; + } + return ebitmap_length(e); } -static inline int ebitmap_node_get_bit(struct ebitmap_node * n, +#define EBITMAP_NODE_INDEX(node, bit) \ + (((bit) - (node)->startbit) / EBITMAP_UNIT_SIZE) +#define EBITMAP_NODE_OFFSET(node, bit) \ + (((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE) + +static inline int ebitmap_node_get_bit(struct ebitmap_node *n, unsigned int bit) { - if (n->map & (MAPBIT << (bit - n->startbit))) + unsigned int index = EBITMAP_NODE_INDEX(n, bit); + unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + + BUG_ON(index >= EBITMAP_UNIT_NUMS); + if ((n->maps[index] & (EBITMAP_BIT << ofs))) return 1; return 0; } -#define ebitmap_for_each_bit(e, n, bit) \ - for (bit = ebitmap_start(e, &n); bit < ebitmap_length(e); bit = ebitmap_next(&n, bit)) \ +static inline void ebitmap_node_set_bit(struct ebitmap_node *n, + unsigned int bit) +{ + unsigned int index = EBITMAP_NODE_INDEX(n, bit); + unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + + BUG_ON(index >= EBITMAP_UNIT_NUMS); + n->maps[index] |= (EBITMAP_BIT << ofs); +} + +static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, + unsigned int bit) +{ + unsigned int index = EBITMAP_NODE_INDEX(n, bit); + unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + + BUG_ON(index >= EBITMAP_UNIT_NUMS); + n->maps[index] &= ~(EBITMAP_BIT << ofs); +} + +#define ebitmap_for_each_positive_bit(e, n, bit) \ + for (bit = ebitmap_start_positive(e, &n); \ + bit < ebitmap_length(e); \ + bit = ebitmap_next_positive(e, &n, bit)) \ int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit); int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); +int ebitmap_write(struct ebitmap *e, void *fp); + +#ifdef CONFIG_NETLABEL +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap); +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap); +#else +static inline int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) +{ + return -ENOMEM; +} +static inline int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) +{ + return -ENOMEM; +} +#endif #endif /* _SS_EBITMAP_H_ */ diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 24e5ec95763..2cc49614984 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -6,11 +6,12 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/errno.h> +#include <linux/sched.h> #include "hashtab.h" -struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), - int (*keycmp)(struct hashtab *h, void *key1, void *key2), - u32 size) +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), + u32 size) { struct hashtab *p; u32 i; @@ -40,6 +41,8 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) u32 hvalue; struct hashtab_node *prev, *cur, *newnode; + cond_resched(); + if (!h || h->nel == HASHTAB_MAX_NODES) return -EINVAL; @@ -71,7 +74,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) return 0; } -void *hashtab_search(struct hashtab *h, void *key) +void *hashtab_search(struct hashtab *h, const void *key) { u32 hvalue; struct hashtab_node *cur; @@ -81,7 +84,7 @@ void *hashtab_search(struct hashtab *h, 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 +103,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 +130,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/hashtab.h b/security/selinux/ss/hashtab.h index 4cc85816a71..953872cd84a 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -22,9 +22,9 @@ struct hashtab { struct hashtab_node **htable; /* hash table */ u32 size; /* number of slots in hash table */ u32 nel; /* number of elements in hash table */ - u32 (*hash_value)(struct hashtab *h, void *key); + u32 (*hash_value)(struct hashtab *h, const void *key); /* hash function */ - int (*keycmp)(struct hashtab *h, void *key1, void *key2); + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2); /* key comparison function */ }; @@ -39,9 +39,9 @@ struct hashtab_info { * Returns NULL if insufficent space is available or * the new hash table otherwise. */ -struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), - int (*keycmp)(struct hashtab *h, void *key1, void *key2), - u32 size); +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), + u32 size); /* * Inserts the specified (key, datum) pair into the specified hash table. @@ -49,7 +49,7 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), * Returns -ENOMEM on memory allocation error, * -EEXIST if there is already an entry with the same key, * -EINVAL for general errors or - * 0 otherwise. + 0 otherwise. */ int hashtab_insert(struct hashtab *h, void *k, void *d); @@ -59,7 +59,7 @@ int hashtab_insert(struct hashtab *h, void *k, void *d); * Returns NULL if no entry has the specified key or * the datum of the entry otherwise. */ -void *hashtab_search(struct hashtab *h, void *k); +void *hashtab_search(struct hashtab *h, const void *k); /* * Destroys the specified hash table. diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 640d0bfdbc6..d307b37ddc2 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -8,13 +8,21 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. + */ +/* + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support to import/export the MLS label from NetLabel + * + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <net/netlabel.h> #include "sidtab.h" #include "mls.h" #include "policydb.h" @@ -24,41 +32,45 @@ * Return the length in bytes for the MLS fields of the * security context string representation of `context'. */ -int mls_compute_context_len(struct context * context) +int mls_compute_context_len(struct context *context) { - int i, l, len, range; + int i, l, len, head, prev; + char *nm; + struct ebitmap *e; struct ebitmap_node *node; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; len = 1; /* for the beginning ":" */ for (l = 0; l < 2; l++) { - range = 0; - len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); - - ebitmap_for_each_bit(&context->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - if (range) { - range++; - continue; - } + int index_sens = context->range.level[l].sens; + len += strlen(sym_name(&policydb, SYM_LEVELS, index_sens - 1)); - len += strlen(policydb.p_cat_val_to_name[i]) + 1; - range++; - } else { - if (range > 1) - len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; - range = 0; + /* categories */ + head = -2; + prev = -2; + e = &context->range.level[l].cat; + ebitmap_for_each_positive_bit(e, node, i) { + if (i - prev > 1) { + /* one or more negative bits are skipped */ + if (head != prev) { + nm = sym_name(&policydb, SYM_CATS, prev); + len += strlen(nm) + 1; + } + nm = sym_name(&policydb, SYM_CATS, i); + len += strlen(nm) + 1; + head = i; } + prev = i; + } + if (prev != head) { + nm = sym_name(&policydb, SYM_CATS, prev); + len += strlen(nm) + 1; } - /* Handle case where last category is the end of range */ - if (range > 1) - len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; - if (l == 0) { if (mls_level_eq(&context->range.level[0], - &context->range.level[1])) + &context->range.level[1])) break; else len++; @@ -74,13 +86,14 @@ int mls_compute_context_len(struct context * context) * Update `*scontext' to point to the end of the MLS fields. */ void mls_sid_to_context(struct context *context, - char **scontext) + char **scontext) { - char *scontextp; - int i, l, range, wrote_sep; + char *scontextp, *nm; + int i, l, head, prev; + struct ebitmap *e; struct ebitmap_node *node; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return; scontextp = *scontext; @@ -89,61 +102,54 @@ void mls_sid_to_context(struct context *context, scontextp++; for (l = 0; l < 2; l++) { - range = 0; - wrote_sep = 0; - strcpy(scontextp, - policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); - scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); + strcpy(scontextp, sym_name(&policydb, SYM_LEVELS, + context->range.level[l].sens - 1)); + scontextp += strlen(scontextp); /* categories */ - ebitmap_for_each_bit(&context->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - if (range) { - range++; - continue; - } - - if (!wrote_sep) { - *scontextp++ = ':'; - wrote_sep = 1; - } else - *scontextp++ = ','; - strcpy(scontextp, policydb.p_cat_val_to_name[i]); - scontextp += strlen(policydb.p_cat_val_to_name[i]); - range++; - } else { - if (range > 1) { - if (range > 2) + head = -2; + prev = -2; + e = &context->range.level[l].cat; + ebitmap_for_each_positive_bit(e, node, i) { + if (i - prev > 1) { + /* one or more negative bits are skipped */ + if (prev != head) { + if (prev - head > 1) *scontextp++ = '.'; else *scontextp++ = ','; - - strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); - scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); + nm = sym_name(&policydb, SYM_CATS, prev); + strcpy(scontextp, nm); + scontextp += strlen(nm); } - range = 0; + if (prev < 0) + *scontextp++ = ':'; + else + *scontextp++ = ','; + nm = sym_name(&policydb, SYM_CATS, i); + strcpy(scontextp, nm); + scontextp += strlen(nm); + head = i; } + prev = i; } - /* Handle case where last category is the end of range */ - if (range > 1) { - if (range > 2) + if (prev != head) { + if (prev - head > 1) *scontextp++ = '.'; else *scontextp++ = ','; - - strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); - scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); + nm = sym_name(&policydb, SYM_CATS, prev); + strcpy(scontextp, nm); + scontextp += strlen(nm); } if (l == 0) { if (mls_level_eq(&context->range.level[0], - &context->range.level[1])) + &context->range.level[1])) break; - else { - *scontextp = '-'; - scontextp++; - } + else + *scontextp++ = '-'; } } @@ -151,51 +157,47 @@ void mls_sid_to_context(struct context *context, return; } +int mls_level_isvalid(struct policydb *p, struct mls_level *l) +{ + struct level_datum *levdatum; + + if (!l->sens || l->sens > p->p_levels.nprim) + return 0; + levdatum = hashtab_search(p->p_levels.table, + sym_name(p, SYM_LEVELS, l->sens - 1)); + if (!levdatum) + return 0; + + /* + * Return 1 iff all the bits set in l->cat are also be set in + * levdatum->level->cat and no bit in l->cat is larger than + * p->p_cats.nprim. + */ + return ebitmap_contains(&levdatum->level->cat, &l->cat, + p->p_cats.nprim); +} + +int mls_range_isvalid(struct policydb *p, struct mls_range *r) +{ + return (mls_level_isvalid(p, &r->level[0]) && + mls_level_isvalid(p, &r->level[1]) && + mls_level_dom(&r->level[1], &r->level[0])); +} + /* * Return 1 if the MLS fields in the security context * structure `c' are valid. Return 0 otherwise. */ int mls_context_isvalid(struct policydb *p, struct context *c) { - struct level_datum *levdatum; struct user_datum *usrdatum; - struct ebitmap_node *node; - int i, l; - if (!selinux_mls_enabled) + if (!p->mls_enabled) return 1; - /* - * MLS range validity checks: high must dominate low, low level must - * be valid (category set <-> sensitivity check), and high level must - * be valid (category set <-> sensitivity check) - */ - if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) - /* High does not dominate low. */ + if (!mls_range_isvalid(p, &c->range)) return 0; - for (l = 0; l < 2; l++) { - if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) - return 0; - levdatum = hashtab_search(p->p_levels.table, - p->p_sens_val_to_name[c->range.level[l].sens - 1]); - if (!levdatum) - return 0; - - ebitmap_for_each_bit(&c->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - if (i > p->p_cats.nprim) - return 0; - if (!ebitmap_get_bit(&levdatum->level->cat, i)) - /* - * Category may not be associated with - * sensitivity in low level. - */ - return 0; - } - } - } - if (c->role == OBJECT_R_VAL) return 1; @@ -212,26 +214,6 @@ int mls_context_isvalid(struct policydb *p, struct context *c) } /* - * Copies the MLS range from `src' into `dst'. - */ -static inline int mls_copy_context(struct context *dst, - struct context *src) -{ - int l, rc = 0; - - /* Copy the MLS range from the source context */ - for (l = 0; l < 2; l++) { - dst->range.level[l].sens = src->range.level[l].sens; - rc = ebitmap_cpy(&dst->range.level[l].cat, - &src->range.level[l].cat); - if (rc) - break; - } - - return rc; -} - -/* * Set the MLS fields in the security context structure * `context' based on the string representation in * the string `*scontext'. Update `*scontext' to @@ -249,7 +231,8 @@ static inline int mls_copy_context(struct context *dst, * Policy read-lock must be held for sidtab lookup. * */ -int mls_context_to_sid(char oldc, +int mls_context_to_sid(struct policydb *pol, + char oldc, char **scontext, struct context *context, struct sidtab *s, @@ -262,9 +245,9 @@ int mls_context_to_sid(char oldc, struct cat_datum *catdatum, *rngdatum; int l, rc = -EINVAL; - if (!selinux_mls_enabled) { + if (!pol->mls_enabled) { if (def_sid != SECSID_NULL && oldc) - *scontext += strlen(*scontext); + *scontext += strlen(*scontext) + 1; return 0; } @@ -282,7 +265,7 @@ int mls_context_to_sid(char oldc, if (!defcon) goto out; - rc = mls_copy_context(context, defcon); + rc = mls_context_cpy(context, defcon); goto out; } @@ -292,11 +275,11 @@ int mls_context_to_sid(char oldc, p++; delim = *p; - if (delim != 0) - *p++ = 0; + if (delim != '\0') + *p++ = '\0'; for (l = 0; l < 2; l++) { - levdatum = hashtab_search(policydb.p_levels.table, scontextp); + levdatum = hashtab_search(pol->p_levels.table, scontextp); if (!levdatum) { rc = -EINVAL; goto out; @@ -311,24 +294,25 @@ int mls_context_to_sid(char oldc, while (*p && *p != ',' && *p != '-') p++; delim = *p; - if (delim != 0) - *p++ = 0; + if (delim != '\0') + *p++ = '\0'; /* Separate into range if exists */ - if ((rngptr = strchr(scontextp, '.')) != NULL) { + rngptr = strchr(scontextp, '.'); + if (rngptr != NULL) { /* Remove '.' */ - *rngptr++ = 0; + *rngptr++ = '\0'; } - catdatum = hashtab_search(policydb.p_cats.table, - scontextp); + catdatum = hashtab_search(pol->p_cats.table, + scontextp); if (!catdatum) { rc = -EINVAL; goto out; } rc = ebitmap_set_bit(&context->range.level[l].cat, - catdatum->value - 1, 1); + catdatum->value - 1, 1); if (rc) goto out; @@ -336,7 +320,7 @@ int mls_context_to_sid(char oldc, if (rngptr) { int i; - rngdatum = hashtab_search(policydb.p_cats.table, rngptr); + rngdatum = hashtab_search(pol->p_cats.table, rngptr); if (!rngdatum) { rc = -EINVAL; goto out; @@ -365,8 +349,8 @@ int mls_context_to_sid(char oldc, p++; delim = *p; - if (delim != 0) - *p++ = 0; + if (delim != '\0') + *p++ = '\0'; } else break; } @@ -385,20 +369,28 @@ out: } /* - * Copies the effective MLS range from `src' into `dst'. + * Set the MLS fields in the security context structure + * `context' based on the string representation in + * the string `str'. This function will allocate temporary memory with the + * given constraints of gfp_mask. */ -static inline int mls_scopy_context(struct context *dst, - struct context *src) +int mls_from_string(char *str, struct context *context, gfp_t gfp_mask) { - int l, rc = 0; + char *tmpstr, *freestr; + int rc; - /* Copy the MLS range from the source context */ - for (l = 0; l < 2; l++) { - dst->range.level[l].sens = src->range.level[0].sens; - rc = ebitmap_cpy(&dst->range.level[l].cat, - &src->range.level[0].cat); - if (rc) - break; + if (!policydb.mls_enabled) + return -EINVAL; + + /* we need freestr because mls_context_to_sid will change + the value of tmpstr */ + tmpstr = freestr = kstrdup(str, gfp_mask); + if (!tmpstr) { + rc = -ENOMEM; + } else { + rc = mls_context_to_sid(&policydb, ':', &tmpstr, context, + NULL, SECSID_NULL); + kfree(freestr); } return rc; @@ -407,8 +399,8 @@ static inline int mls_scopy_context(struct context *dst, /* * Copies the MLS range `range' into `context'. */ -static inline int mls_range_set(struct context *context, - struct mls_range *range) +int mls_range_set(struct context *context, + struct mls_range *range) { int l, rc = 0; @@ -425,9 +417,9 @@ static inline int mls_range_set(struct context *context, } int mls_setup_user_range(struct context *fromcon, struct user_datum *user, - struct context *usercon) + struct context *usercon) { - if (selinux_mls_enabled) { + if (policydb.mls_enabled) { struct mls_level *fromcon_sen = &(fromcon->range.level[0]); struct mls_level *fromcon_clr = &(fromcon->range.level[1]); struct mls_level *user_low = &(user->range.level[0]); @@ -437,13 +429,13 @@ int mls_setup_user_range(struct context *fromcon, struct user_datum *user, struct mls_level *usercon_clr = &(usercon->range.level[1]); /* Honor the user's default level if we can */ - if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { + if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) *usercon_sen = *user_def; - } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { + else if (mls_level_between(fromcon_sen, user_def, user_clr)) *usercon_sen = *fromcon_sen; - } else if (mls_level_between(fromcon_clr, user_low, user_def)) { + else if (mls_level_between(fromcon_clr, user_low, user_def)) *usercon_sen = *user_low; - } else + else return -EINVAL; /* Lower the clearance of available contexts @@ -451,11 +443,11 @@ int mls_setup_user_range(struct context *fromcon, struct user_datum *user, that of the user's default clearance (but only if the "fromcon" clearance dominates the user's computed sensitivity level) */ - if (mls_level_dom(user_clr, fromcon_clr)) { + if (mls_level_dom(user_clr, fromcon_clr)) *usercon_clr = *fromcon_clr; - } else if (mls_level_dom(fromcon_clr, user_clr)) { + else if (mls_level_dom(fromcon_clr, user_clr)) *usercon_clr = *user_clr; - } else + else return -EINVAL; } @@ -477,30 +469,31 @@ int mls_convert_context(struct policydb *oldp, struct ebitmap_node *node; int l, i; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; for (l = 0; l < 2; l++) { levdatum = hashtab_search(newp->p_levels.table, - oldp->p_sens_val_to_name[c->range.level[l].sens - 1]); + sym_name(oldp, SYM_LEVELS, + c->range.level[l].sens - 1)); if (!levdatum) return -EINVAL; c->range.level[l].sens = levdatum->level->sens; ebitmap_init(&bitmap); - ebitmap_for_each_bit(&c->range.level[l].cat, node, i) { - if (ebitmap_node_get_bit(node, i)) { - int rc; - - catdatum = hashtab_search(newp->p_cats.table, - oldp->p_cat_val_to_name[i]); - if (!catdatum) - return -EINVAL; - rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); - if (rc) - return rc; - } + ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) { + int rc; + + catdatum = hashtab_search(newp->p_cats.table, + sym_name(oldp, SYM_CATS, i)); + if (!catdatum) + return -EINVAL; + rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); + if (rc) + return rc; + + cond_resched(); } ebitmap_destroy(&c->range.level[l].cat); c->range.level[l].cat = bitmap; @@ -513,47 +506,167 @@ int mls_compute_sid(struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, - struct context *newcontext) + struct context *newcontext, + bool sock) { - if (!selinux_mls_enabled) + struct range_trans rtr; + struct mls_range *r; + struct class_datum *cladatum; + int default_range = 0; + + if (!policydb.mls_enabled) return 0; switch (specified) { case AVTAB_TRANSITION: - if (tclass == SECCLASS_PROCESS) { - struct range_trans *rangetr; - /* Look for a range transition rule. */ - for (rangetr = policydb.range_tr; rangetr; - rangetr = rangetr->next) { - if (rangetr->dom == scontext->type && - rangetr->type == tcontext->type) { - /* Set the range from the rule */ - return mls_range_set(newcontext, - &rangetr->range); - } - } + /* Look for a range transition rule. */ + rtr.source_type = scontext->type; + rtr.target_type = tcontext->type; + rtr.target_class = tclass; + r = hashtab_search(policydb.range_tr, &rtr); + if (r) + return mls_range_set(newcontext, r); + + if (tclass && tclass <= policydb.p_classes.nprim) { + cladatum = policydb.class_val_to_struct[tclass - 1]; + if (cladatum) + default_range = cladatum->default_range; } + + switch (default_range) { + case DEFAULT_SOURCE_LOW: + return mls_context_cpy_low(newcontext, scontext); + case DEFAULT_SOURCE_HIGH: + return mls_context_cpy_high(newcontext, scontext); + case DEFAULT_SOURCE_LOW_HIGH: + return mls_context_cpy(newcontext, scontext); + case DEFAULT_TARGET_LOW: + return mls_context_cpy_low(newcontext, tcontext); + case DEFAULT_TARGET_HIGH: + return mls_context_cpy_high(newcontext, tcontext); + case DEFAULT_TARGET_LOW_HIGH: + return mls_context_cpy(newcontext, tcontext); + } + /* Fallthrough */ case AVTAB_CHANGE: - if (tclass == SECCLASS_PROCESS) + if ((tclass == policydb.process_class) || (sock == true)) /* Use the process MLS attributes. */ - return mls_copy_context(newcontext, scontext); + return mls_context_cpy(newcontext, scontext); else /* Use the process effective MLS attributes. */ - return mls_scopy_context(newcontext, scontext); + return mls_context_cpy_low(newcontext, scontext); case AVTAB_MEMBER: - /* Only polyinstantiate the MLS attributes if - the type is being polyinstantiated */ - if (newcontext->type != tcontext->type) { - /* Use the process effective MLS attributes. */ - return mls_scopy_context(newcontext, scontext); - } else { - /* Use the related object MLS attributes. */ - return mls_copy_context(newcontext, tcontext); - } - default: - return -EINVAL; + /* Use the process effective MLS attributes. */ + return mls_context_cpy_low(newcontext, scontext); + + /* fall through */ } return -EINVAL; } +#ifdef CONFIG_NETLABEL +/** + * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Given the security context copy the low MLS sensitivity level into the + * NetLabel MLS sensitivity level field. + * + */ +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + if (!policydb.mls_enabled) + return; + + secattr->attr.mls.lvl = context->range.level[0].sens - 1; + secattr->flags |= NETLBL_SECATTR_MLS_LVL; +} + +/** + * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Given the security context and the NetLabel security attributes, copy the + * NetLabel MLS sensitivity level into the context. + * + */ +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + if (!policydb.mls_enabled) + return; + + context->range.level[0].sens = secattr->attr.mls.lvl + 1; + context->range.level[1].sens = context->range.level[0].sens; +} + +/** + * mls_export_netlbl_cat - Export the MLS categories to NetLabel + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Given the security context copy the low MLS categories into the NetLabel + * MLS category field. Returns zero on success, negative values on failure. + * + */ +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + int rc; + + if (!policydb.mls_enabled) + return 0; + + rc = ebitmap_netlbl_export(&context->range.level[0].cat, + &secattr->attr.mls.cat); + if (rc == 0 && secattr->attr.mls.cat != NULL) + secattr->flags |= NETLBL_SECATTR_MLS_CAT; + + return rc; +} + +/** + * mls_import_netlbl_cat - Import the MLS categories from NetLabel + * @context: the security context + * @secattr: the NetLabel security attributes + * + * Description: + * Copy the NetLabel security attributes into the SELinux context; since the + * NetLabel security attribute only contains a single MLS category use it for + * both the low and high categories of the context. Returns zero on success, + * negative values on failure. + * + */ +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + int rc; + + if (!policydb.mls_enabled) + return 0; + + rc = ebitmap_netlbl_import(&context->range.level[0].cat, + secattr->attr.mls.cat); + if (rc != 0) + goto import_netlbl_cat_failure; + + rc = ebitmap_cpy(&context->range.level[1].cat, + &context->range.level[0].cat); + if (rc != 0) + goto import_netlbl_cat_failure; + + return 0; + +import_netlbl_cat_failure: + ebitmap_destroy(&context->range.level[0].cat); + ebitmap_destroy(&context->range.level[1].cat); + return rc; +} +#endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 03de697c805..e4369e3e636 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -8,7 +8,14 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. + */ +/* + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support to import/export the MLS label from NetLabel + * + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ #ifndef _SS_MLS_H_ @@ -20,13 +27,20 @@ int mls_compute_context_len(struct context *context); void mls_sid_to_context(struct context *context, char **scontext); int mls_context_isvalid(struct policydb *p, struct context *c); +int mls_range_isvalid(struct policydb *p, struct mls_range *r); +int mls_level_isvalid(struct policydb *p, struct mls_level *l); -int mls_context_to_sid(char oldc, - char **scontext, +int mls_context_to_sid(struct policydb *p, + char oldc, + char **scontext, struct context *context, struct sidtab *s, u32 def_sid); +int mls_from_string(char *str, struct context *context, gfp_t gfp_mask); + +int mls_range_set(struct context *context, struct mls_range *range); + int mls_convert_context(struct policydb *oldp, struct policydb *newp, struct context *context); @@ -35,10 +49,43 @@ int mls_compute_sid(struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, - struct context *newcontext); + struct context *newcontext, + bool sock); int mls_setup_user_range(struct context *fromcon, struct user_datum *user, - struct context *usercon); + struct context *usercon); + +#ifdef CONFIG_NETLABEL +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +#else +static inline void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +static inline int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +#endif #endif /* _SS_MLS_H */ diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index 0c692d58d48..e9364877413 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -15,6 +15,7 @@ #define _SS_MLS_TYPES_H_ #include "security.h" +#include "ebitmap.h" struct mls_level { u32 sens; /* sensitivity */ @@ -27,20 +28,14 @@ struct mls_range { static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2) { - if (!selinux_mls_enabled) - return 1; - return ((l1->sens == l2->sens) && - ebitmap_cmp(&l1->cat, &l2->cat)); + ebitmap_cmp(&l1->cat, &l2->cat)); } static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2) { - if (!selinux_mls_enabled) - return 1; - return ((l1->sens >= l2->sens) && - ebitmap_contains(&l1->cat, &l2->cat)); + ebitmap_contains(&l1->cat, &l2->cat, 0)); } #define mls_level_incomp(l1, l2) \ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 0111990ba83..9c5cdc2caae 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -11,29 +11,38 @@ * * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support for the policy capability bitmap + * + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> +#include <linux/flex_array.h> #include "security.h" #include "policydb.h" #include "conditional.h" #include "mls.h" +#include "services.h" #define _DEBUG_HASHES #ifdef DEBUG_HASHES -static char *symtab_name[SYM_NUM] = { +static const char *symtab_name[SYM_NUM] = { "common prefixes", "classes", "roles", @@ -45,8 +54,6 @@ static char *symtab_name[SYM_NUM] = { }; #endif -int selinux_mls_enabled = 0; - static unsigned int symtab_sizes[SYM_NUM] = { 2, 32, @@ -67,34 +74,79 @@ struct policydb_compat_info { /* These need to be updated if SYM_NUM or OCON_NUM changes */ static struct policydb_compat_info policydb_compat[] = { { - .version = POLICYDB_VERSION_BASE, - .sym_num = SYM_NUM - 3, - .ocon_num = OCON_NUM - 1, + .version = POLICYDB_VERSION_BASE, + .sym_num = SYM_NUM - 3, + .ocon_num = OCON_NUM - 1, + }, + { + .version = POLICYDB_VERSION_BOOL, + .sym_num = SYM_NUM - 2, + .ocon_num = OCON_NUM - 1, + }, + { + .version = POLICYDB_VERSION_IPV6, + .sym_num = SYM_NUM - 2, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_NLCLASS, + .sym_num = SYM_NUM - 2, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_BOOL, - .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM - 1, + .version = POLICYDB_VERSION_MLS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_IPV6, - .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_AVTAB, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_NLCLASS, - .sym_num = SYM_NUM - 2, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_RANGETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_MLS, - .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_POLCAP, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, { - .version = POLICYDB_VERSION_AVTAB, - .sym_num = SYM_NUM, - .ocon_num = OCON_NUM, + .version = POLICYDB_VERSION_PERMISSIVE, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_FILENAME_TRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_ROLETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_DEFAULT_TYPE, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_CONSTRAINT_NAMES, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, }, }; @@ -121,33 +173,92 @@ static int roles_init(struct policydb *p) int rc; struct role_datum *role; + rc = -ENOMEM; role = kzalloc(sizeof(*role), GFP_KERNEL); - if (!role) { - rc = -ENOMEM; + if (!role) goto out; - } + + rc = -EINVAL; role->value = ++p->p_roles.nprim; - if (role->value != OBJECT_R_VAL) { - rc = -EINVAL; - goto out_free_role; - } - key = kmalloc(strlen(OBJECT_R)+1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; - goto out_free_role; - } - strcpy(key, OBJECT_R); + if (role->value != OBJECT_R_VAL) + goto out; + + rc = -ENOMEM; + key = kstrdup(OBJECT_R, GFP_KERNEL); + if (!key) + goto out; + rc = hashtab_insert(p->p_roles.table, key, role); if (rc) - goto out_free_key; -out: - return rc; + goto out; -out_free_key: + return 0; +out: kfree(key); -out_free_role: kfree(role); - goto out; + return rc; +} + +static u32 filenametr_hash(struct hashtab *h, const void *k) +{ + const struct filename_trans *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->stype ^ ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash & (h->size - 1); +} + +static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct filename_trans *ft1 = k1; + const struct filename_trans *ft2 = k2; + int v; + + v = ft1->stype - ft2->stype; + if (v) + return v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); + +} + +static u32 rangetr_hash(struct hashtab *h, const void *k) +{ + const struct range_trans *key = k; + return (key->source_type + (key->target_type << 3) + + (key->target_class << 5)) & (h->size - 1); +} + +static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct range_trans *key1 = k1, *key2 = k2; + int v; + + v = key1->source_type - key2->source_type; + if (v) + return v; + + v = key1->target_type - key2->target_type; + if (v) + return v; + + v = key1->target_class - key2->target_class; + + return v; } /* @@ -162,31 +273,40 @@ static int policydb_init(struct policydb *p) for (i = 0; i < SYM_NUM; i++) { rc = symtab_init(&p->symtab[i], symtab_sizes[i]); if (rc) - goto out_free_symtab; + goto out; } rc = avtab_init(&p->te_avtab); if (rc) - goto out_free_symtab; + goto out; rc = roles_init(p); if (rc) - goto out_free_avtab; + goto out; rc = cond_policydb_init(p); if (rc) - goto out_free_avtab; + goto out; -out: - return rc; + p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); + if (!p->filename_trans) + goto out; -out_free_avtab: - avtab_destroy(&p->te_avtab); + p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); + if (!p->range_tr) + goto out; -out_free_symtab: + ebitmap_init(&p->filename_trans_ttypes); + ebitmap_init(&p->policycaps); + ebitmap_init(&p->permissive_map); + + return 0; +out: + hashtab_destroy(p->filename_trans); + hashtab_destroy(p->range_tr); for (i = 0; i < SYM_NUM; i++) hashtab_destroy(p->symtab[i].table); - goto out; + return rc; } /* @@ -203,12 +323,17 @@ static int common_index(void *key, void *datum, void *datap) { struct policydb *p; struct common_datum *comdatum; + struct flex_array *fa; comdatum = datum; p = datap; if (!comdatum->value || comdatum->value > p->p_commons.nprim) return -EINVAL; - p->p_common_val_to_name[comdatum->value - 1] = key; + + fa = p->sym_val_to_name[SYM_COMMONS]; + if (flex_array_put_ptr(fa, comdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); return 0; } @@ -216,12 +341,16 @@ static int class_index(void *key, void *datum, void *datap) { struct policydb *p; struct class_datum *cladatum; + struct flex_array *fa; cladatum = datum; p = datap; if (!cladatum->value || cladatum->value > p->p_classes.nprim) return -EINVAL; - p->p_class_val_to_name[cladatum->value - 1] = key; + fa = p->sym_val_to_name[SYM_CLASSES]; + if (flex_array_put_ptr(fa, cladatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); p->class_val_to_struct[cladatum->value - 1] = cladatum; return 0; } @@ -230,12 +359,19 @@ static int role_index(void *key, void *datum, void *datap) { struct policydb *p; struct role_datum *role; + struct flex_array *fa; 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; + + fa = p->sym_val_to_name[SYM_ROLES]; + if (flex_array_put_ptr(fa, role->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); p->role_val_to_struct[role->value - 1] = role; return 0; } @@ -244,14 +380,25 @@ static int type_index(void *key, void *datum, void *datap) { struct policydb *p; struct type_datum *typdatum; + struct flex_array *fa; typdatum = datum; 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; + fa = p->sym_val_to_name[SYM_TYPES]; + if (flex_array_put_ptr(fa, typdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); + + fa = p->type_val_to_struct_array; + if (flex_array_put_ptr(fa, typdatum->value - 1, typdatum, + GFP_KERNEL | __GFP_ZERO)) + BUG(); } return 0; @@ -261,12 +408,19 @@ static int user_index(void *key, void *datum, void *datap) { struct policydb *p; struct user_datum *usrdatum; + struct flex_array *fa; 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; + + fa = p->sym_val_to_name[SYM_USERS]; + if (flex_array_put_ptr(fa, usrdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); p->user_val_to_struct[usrdatum->value - 1] = usrdatum; return 0; } @@ -275,6 +429,7 @@ static int sens_index(void *key, void *datum, void *datap) { struct policydb *p; struct level_datum *levdatum; + struct flex_array *fa; levdatum = datum; p = datap; @@ -283,7 +438,10 @@ static int sens_index(void *key, void *datum, void *datap) if (!levdatum->level->sens || levdatum->level->sens > p->p_levels.nprim) return -EINVAL; - p->p_sens_val_to_name[levdatum->level->sens - 1] = key; + fa = p->sym_val_to_name[SYM_LEVELS]; + if (flex_array_put_ptr(fa, levdatum->level->sens - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); } return 0; @@ -293,6 +451,7 @@ static int cat_index(void *key, void *datum, void *datap) { struct policydb *p; struct cat_datum *catdatum; + struct flex_array *fa; catdatum = datum; p = datap; @@ -300,7 +459,10 @@ static int cat_index(void *key, void *datum, void *datap) if (!catdatum->isalias) { if (!catdatum->value || catdatum->value > p->p_cats.nprim) return -EINVAL; - p->p_cat_val_to_name[catdatum->value - 1] = key; + fa = p->sym_val_to_name[SYM_CATS]; + if (flex_array_put_ptr(fa, catdatum->value - 1, key, + GFP_KERNEL | __GFP_ZERO)) + BUG(); } return 0; @@ -318,61 +480,28 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_index, }; -/* - * Define the common val_to_name array and the class - * val_to_name and val_to_struct arrays in a policy - * database structure. - * - * Caller must clean up upon failure. - */ -static int policydb_index_classes(struct policydb *p) +#ifdef DEBUG_HASHES +static void hash_eval(struct hashtab *h, const char *hash_name) { - int rc; + struct hashtab_info info; - p->p_common_val_to_name = - kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL); - if (!p->p_common_val_to_name) { - rc = -ENOMEM; - goto out; - } - - rc = hashtab_map(p->p_commons.table, common_index, p); - if (rc) - goto out; - - p->class_val_to_struct = - kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL); - if (!p->class_val_to_struct) { - rc = -ENOMEM; - goto out; - } - - p->p_class_val_to_name = - kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL); - if (!p->p_class_val_to_name) { - rc = -ENOMEM; - goto out; - } - - rc = hashtab_map(p->p_classes.table, class_index, p); -out: - return rc; + hashtab_stat(h, &info); + printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " + "longest chain length %d\n", hash_name, h->nel, + info.slots_used, h->size, info.max_chain_len); } -#ifdef DEBUG_HASHES static void symtab_hash_eval(struct symtab *s) { int i; - for (i = 0; i < SYM_NUM; i++) { - struct hashtab *h = s[i].table; - struct hashtab_info info; + for (i = 0; i < SYM_NUM; i++) + hash_eval(s[i].table, symtab_name[i]); +} - hashtab_stat(h, &info); - printk(KERN_INFO "%s: %d entries and %d/%d buckets used, " - "longest chain length %d\n", symtab_name[i], h->nel, - info.slots_used, h->size, info.max_chain_len); - } +#else +static inline void hash_eval(struct hashtab *h, char *hash_name) +{ } #endif @@ -382,18 +511,18 @@ static void symtab_hash_eval(struct symtab *s) * * Caller must clean up on failure. */ -static int policydb_index_others(struct policydb *p) +static int policydb_index(struct policydb *p) { - int i, rc = 0; + int i, rc; - printk(KERN_INFO "security: %d users, %d roles, %d types, %d bools", + printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools", p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); - if (selinux_mls_enabled) + if (p->mls_enabled) printk(", %d sens, %d cats", p->p_levels.nprim, p->p_cats.nprim); printk("\n"); - printk(KERN_INFO "security: %d classes, %d rules\n", + printk(KERN_DEBUG "SELinux: %d classes, %d rules\n", p->p_classes.nprim, p->te_avtab.nel); #ifdef DEBUG_HASHES @@ -401,39 +530,63 @@ static int policydb_index_others(struct policydb *p) symtab_hash_eval(p->symtab); #endif + rc = -ENOMEM; + p->class_val_to_struct = + kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), + GFP_KERNEL); + if (!p->class_val_to_struct) + goto out; + + rc = -ENOMEM; p->role_val_to_struct = kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), - GFP_KERNEL); - if (!p->role_val_to_struct) { - rc = -ENOMEM; + GFP_KERNEL); + if (!p->role_val_to_struct) goto out; - } + rc = -ENOMEM; p->user_val_to_struct = kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), - GFP_KERNEL); - if (!p->user_val_to_struct) { - rc = -ENOMEM; + GFP_KERNEL); + if (!p->user_val_to_struct) goto out; - } - if (cond_init_bool_indexes(p)) { - rc = -ENOMEM; + /* Yes, I want the sizeof the pointer, not the structure */ + rc = -ENOMEM; + p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *), + p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->type_val_to_struct_array) goto out; - } - for (i = SYM_ROLES; i < SYM_NUM; i++) { - p->sym_val_to_name[i] = - kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL); - if (!p->sym_val_to_name[i]) { - rc = -ENOMEM; + rc = flex_array_prealloc(p->type_val_to_struct_array, 0, + p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); + if (rc) + goto out; + + rc = cond_init_bool_indexes(p); + if (rc) + goto out; + + for (i = 0; i < SYM_NUM; i++) { + rc = -ENOMEM; + p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *), + p->symtab[i].nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->sym_val_to_name[i]) goto out; - } + + rc = flex_array_prealloc(p->sym_val_to_name[i], + 0, p->symtab[i].nprim, + GFP_KERNEL | __GFP_ZERO); + if (rc) + goto out; + rc = hashtab_map(p->symtab[i].table, index_f[i], p); if (rc) goto out; } - + rc = 0; out: return rc; } @@ -456,52 +609,66 @@ static int common_destroy(void *key, void *datum, void *p) struct common_datum *comdatum; kfree(key); - comdatum = datum; - hashtab_map(comdatum->permissions.table, perm_destroy, NULL); - hashtab_destroy(comdatum->permissions.table); + if (datum) { + comdatum = datum; + hashtab_map(comdatum->permissions.table, perm_destroy, NULL); + hashtab_destroy(comdatum->permissions.table); + } kfree(datum); return 0; } -static int class_destroy(void *key, void *datum, void *p) +static void constraint_expr_destroy(struct constraint_expr *expr) +{ + if (expr) { + ebitmap_destroy(&expr->names); + if (expr->type_names) { + ebitmap_destroy(&expr->type_names->types); + ebitmap_destroy(&expr->type_names->negset); + kfree(expr->type_names); + } + kfree(expr); + } +} + +static int cls_destroy(void *key, void *datum, void *p) { struct class_datum *cladatum; struct constraint_node *constraint, *ctemp; struct constraint_expr *e, *etmp; kfree(key); - cladatum = datum; - hashtab_map(cladatum->permissions.table, perm_destroy, NULL); - hashtab_destroy(cladatum->permissions.table); - constraint = cladatum->constraints; - while (constraint) { - e = constraint->expr; - while (e) { - ebitmap_destroy(&e->names); - etmp = e; - e = e->next; - kfree(etmp); + if (datum) { + cladatum = datum; + hashtab_map(cladatum->permissions.table, perm_destroy, NULL); + hashtab_destroy(cladatum->permissions.table); + constraint = cladatum->constraints; + while (constraint) { + e = constraint->expr; + while (e) { + etmp = e; + e = e->next; + constraint_expr_destroy(etmp); + } + ctemp = constraint; + constraint = constraint->next; + kfree(ctemp); } - ctemp = constraint; - constraint = constraint->next; - kfree(ctemp); - } - - constraint = cladatum->validatetrans; - while (constraint) { - e = constraint->expr; - while (e) { - ebitmap_destroy(&e->names); - etmp = e; - e = e->next; - kfree(etmp); + + constraint = cladatum->validatetrans; + while (constraint) { + e = constraint->expr; + while (e) { + etmp = e; + e = e->next; + constraint_expr_destroy(etmp); + } + ctemp = constraint; + constraint = constraint->next; + kfree(ctemp); } - ctemp = constraint; - constraint = constraint->next; - kfree(ctemp); + kfree(cladatum->comkey); } - - kfree(cladatum->comkey); kfree(datum); return 0; } @@ -511,9 +678,11 @@ static int role_destroy(void *key, void *datum, void *p) struct role_datum *role; kfree(key); - role = datum; - ebitmap_destroy(&role->dominates); - ebitmap_destroy(&role->types); + if (datum) { + role = datum; + ebitmap_destroy(&role->dominates); + ebitmap_destroy(&role->types); + } kfree(datum); return 0; } @@ -530,11 +699,13 @@ static int user_destroy(void *key, void *datum, void *p) struct user_datum *usrdatum; kfree(key); - usrdatum = datum; - ebitmap_destroy(&usrdatum->roles); - ebitmap_destroy(&usrdatum->range.level[0].cat); - ebitmap_destroy(&usrdatum->range.level[1].cat); - ebitmap_destroy(&usrdatum->dfltlevel.cat); + if (datum) { + usrdatum = datum; + ebitmap_destroy(&usrdatum->roles); + ebitmap_destroy(&usrdatum->range.level[0].cat); + ebitmap_destroy(&usrdatum->range.level[1].cat); + ebitmap_destroy(&usrdatum->dfltlevel.cat); + } kfree(datum); return 0; } @@ -544,9 +715,11 @@ static int sens_destroy(void *key, void *datum, void *p) struct level_datum *levdatum; kfree(key); - levdatum = datum; - ebitmap_destroy(&levdatum->level->cat); - kfree(levdatum->level); + if (datum) { + levdatum = datum; + ebitmap_destroy(&levdatum->level->cat); + kfree(levdatum->level); + } kfree(datum); return 0; } @@ -561,7 +734,7 @@ static int cat_destroy(void *key, void *datum, void *p) static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = { common_destroy, - class_destroy, + cls_destroy, role_destroy, type_destroy, user_destroy, @@ -570,8 +743,32 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_destroy, }; +static int filenametr_destroy(void *key, void *datum, void *p) +{ + struct filename_trans *ft = key; + kfree(ft->name); + kfree(key); + kfree(datum); + cond_resched(); + return 0; +} + +static int range_tr_destroy(void *key, void *datum, void *p) +{ + struct mls_range *rt = datum; + kfree(key); + ebitmap_destroy(&rt->level[0].cat); + ebitmap_destroy(&rt->level[1].cat); + kfree(datum); + cond_resched(); + return 0; +} + static void ocontext_destroy(struct ocontext *c, int i) { + if (!c) + return; + context_destroy(&c->context[0]); context_destroy(&c->context[1]); if (i == OCON_ISID || i == OCON_FS || @@ -590,70 +787,90 @@ void policydb_destroy(struct policydb *p) int i; struct role_allow *ra, *lra = NULL; struct role_trans *tr, *ltr = NULL; - struct range_trans *rt, *lrt = NULL; for (i = 0; i < SYM_NUM; i++) { + cond_resched(); hashtab_map(p->symtab[i].table, destroy_f[i], NULL); hashtab_destroy(p->symtab[i].table); } - for (i = 0; i < SYM_NUM; i++) - kfree(p->sym_val_to_name[i]); + for (i = 0; i < SYM_NUM; i++) { + if (p->sym_val_to_name[i]) + flex_array_free(p->sym_val_to_name[i]); + } kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + if (p->type_val_to_struct_array) + flex_array_free(p->type_val_to_struct_array); avtab_destroy(&p->te_avtab); for (i = 0; i < OCON_NUM; i++) { + cond_resched(); c = p->ocontexts[i]; while (c) { ctmp = c; c = c->next; - ocontext_destroy(ctmp,i); + ocontext_destroy(ctmp, i); } + p->ocontexts[i] = NULL; } g = p->genfs; while (g) { + cond_resched(); kfree(g->fstype); c = g->head; while (c) { ctmp = c; c = c->next; - ocontext_destroy(ctmp,OCON_FSUSE); + ocontext_destroy(ctmp, OCON_FSUSE); } gtmp = g; g = g->next; kfree(gtmp); } + p->genfs = NULL; cond_policydb_destroy(p); for (tr = p->role_tr; tr; tr = tr->next) { + cond_resched(); kfree(ltr); ltr = tr; } kfree(ltr); - for (ra = p->role_allow; ra; ra = ra -> next) { + for (ra = p->role_allow; ra; ra = ra->next) { + cond_resched(); kfree(lra); lra = ra; } kfree(lra); - for (rt = p->range_tr; rt; rt = rt -> next) { - kfree(lrt); - lrt = rt; - } - kfree(lrt); + hashtab_map(p->filename_trans, filenametr_destroy, NULL); + hashtab_destroy(p->filename_trans); + + hashtab_map(p->range_tr, range_tr_destroy, NULL); + hashtab_destroy(p->range_tr); - if (p->type_attr_map) { - for (i = 0; i < p->p_types.nprim; i++) - ebitmap_destroy(&p->type_attr_map[i]); + if (p->type_attr_map_array) { + for (i = 0; i < p->p_types.nprim; i++) { + struct ebitmap *e; + + e = flex_array_get(p->type_attr_map_array, i); + if (!e) + continue; + ebitmap_destroy(e); + } + flex_array_free(p->type_attr_map_array); } - kfree(p->type_attr_map); + + ebitmap_destroy(&p->filename_trans_ttypes); + ebitmap_destroy(&p->policycaps); + ebitmap_destroy(&p->permissive_map); return; } @@ -669,29 +886,52 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s) rc = sidtab_init(s); if (rc) { - printk(KERN_ERR "security: out of memory on SID table init\n"); + printk(KERN_ERR "SELinux: out of memory on SID table init\n"); goto out; } head = p->ocontexts[OCON_ISID]; for (c = head; c; c = c->next) { + rc = -EINVAL; if (!c->context[0].user) { - printk(KERN_ERR "security: SID %s was never " - "defined.\n", c->u.name); - rc = -EINVAL; + printk(KERN_ERR "SELinux: SID %s was never defined.\n", + c->u.name); goto out; } - if (sidtab_insert(s, c->sid[0], &c->context[0])) { - printk(KERN_ERR "security: unable to load initial " - "SID %s.\n", c->u.name); - rc = -EINVAL; + + rc = sidtab_insert(s, c->sid[0], &c->context[0]); + if (rc) { + printk(KERN_ERR "SELinux: unable to load initial SID %s.\n", + c->u.name); goto out; } } + rc = 0; out: return rc; } +int policydb_class_isvalid(struct policydb *p, unsigned int class) +{ + if (!class || class > p->p_classes.nprim) + return 0; + return 1; +} + +int policydb_role_isvalid(struct policydb *p, unsigned int role) +{ + if (!role || role > p->p_roles.nprim) + return 0; + return 1; +} + +int policydb_type_isvalid(struct policydb *p, unsigned int type) +{ + if (!type || type > p->p_types.nprim) + return 0; + return 1; +} + /* * Return 1 if the fields in the security context * structure `c' are valid. Return 0 otherwise. @@ -715,8 +955,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c) * Role must be authorized for the type. */ role = p->role_val_to_struct[c->role - 1]; - if (!ebitmap_get_bit(&role->types, - c->type - 1)) + if (!ebitmap_get_bit(&role->types, c->type - 1)) /* role may not be associated with type */ return 0; @@ -727,8 +966,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c) if (!usrdatum) return 0; - if (!ebitmap_get_bit(&usrdatum->roles, - c->role - 1)) + if (!ebitmap_get_bit(&usrdatum->roles, c->role - 1)) /* user may not be associated with role */ return 0; } @@ -750,20 +988,22 @@ static int mls_read_range_helper(struct mls_range *r, void *fp) int rc; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto out; + rc = -EINVAL; items = le32_to_cpu(buf[0]); if (items > ARRAY_SIZE(buf)) { - printk(KERN_ERR "security: mls: range overflow\n"); - rc = -EINVAL; + printk(KERN_ERR "SELinux: mls: range overflow\n"); goto out; } + rc = next_entry(buf, fp, sizeof(u32) * items); - if (rc < 0) { - printk(KERN_ERR "security: mls: truncated range\n"); + if (rc) { + printk(KERN_ERR "SELinux: mls: truncated range\n"); goto out; } + r->level[0].sens = le32_to_cpu(buf[0]); if (items > 1) r->level[1].sens = le32_to_cpu(buf[1]); @@ -772,31 +1012,28 @@ static int mls_read_range_helper(struct mls_range *r, void *fp) rc = ebitmap_read(&r->level[0].cat, fp); if (rc) { - printk(KERN_ERR "security: mls: error reading low " - "categories\n"); + printk(KERN_ERR "SELinux: mls: error reading low categories\n"); goto out; } if (items > 1) { rc = ebitmap_read(&r->level[1].cat, fp); if (rc) { - printk(KERN_ERR "security: mls: error reading high " - "categories\n"); + printk(KERN_ERR "SELinux: mls: error reading high categories\n"); goto bad_high; } } else { rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat); if (rc) { - printk(KERN_ERR "security: mls: out of memory\n"); + printk(KERN_ERR "SELinux: mls: out of memory\n"); goto bad_high; } } - rc = 0; -out: - return rc; + return 0; bad_high: ebitmap_destroy(&r->level[0].cat); - goto out; +out: + return rc; } /* @@ -811,27 +1048,28 @@ static int context_read_and_validate(struct context *c, int rc; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) { - printk(KERN_ERR "security: context truncated\n"); + if (rc) { + printk(KERN_ERR "SELinux: context truncated\n"); goto out; } c->user = le32_to_cpu(buf[0]); c->role = le32_to_cpu(buf[1]); c->type = le32_to_cpu(buf[2]); if (p->policyvers >= POLICYDB_VERSION_MLS) { - if (mls_read_range_helper(&c->range, fp)) { - printk(KERN_ERR "security: error reading MLS range of " - "context\n"); - rc = -EINVAL; + rc = mls_read_range_helper(&c->range, fp); + if (rc) { + printk(KERN_ERR "SELinux: error reading MLS range of context\n"); goto out; } } + rc = -EINVAL; if (!policydb_context_isvalid(p, c)) { - printk(KERN_ERR "security: invalid security context\n"); + printk(KERN_ERR "SELinux: invalid security context\n"); context_destroy(c); - rc = -EINVAL; + goto out; } + rc = 0; out: return rc; } @@ -850,37 +1088,36 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; + rc = -ENOMEM; perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL); - if (!perdatum) { - rc = -ENOMEM; - goto out; - } + if (!perdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); perdatum->value = le32_to_cpu(buf[1]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = hashtab_insert(h, key, perdatum); if (rc) goto bad; -out: - return rc; + + return 0; bad: perm_destroy(key, perdatum, NULL); - goto out; + return rc; } static int common_read(struct policydb *p, struct hashtab *h, void *fp) @@ -891,14 +1128,13 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) u32 len, nel; int i, rc; + rc = -ENOMEM; comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL); - if (!comdatum) { - rc = -ENOMEM; - goto out; - } + if (!comdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); @@ -910,15 +1146,15 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) comdatum->permissions.nprim = le32_to_cpu(buf[2]); nel = le32_to_cpu(buf[3]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; for (i = 0; i < nel; i++) { rc = perm_read(p, comdatum->permissions.table, fp); @@ -929,15 +1165,40 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) rc = hashtab_insert(h, key, comdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: common_destroy(key, comdatum, NULL); - goto out; + return rc; } -static int read_cons_helper(struct constraint_node **nodep, int ncons, - int allowxtarget, void *fp) +static void type_set_init(struct type_set *t) +{ + ebitmap_init(&t->types); + ebitmap_init(&t->negset); +} + +static int type_set_read(struct type_set *t, void *fp) +{ + __le32 buf[1]; + int rc; + + if (ebitmap_read(&t->types, fp)) + return -EINVAL; + if (ebitmap_read(&t->negset, fp)) + return -EINVAL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + return -EINVAL; + t->flags = le32_to_cpu(buf[0]); + + return 0; +} + + +static int read_cons_helper(struct policydb *p, + struct constraint_node **nodep, + int ncons, int allowxtarget, void *fp) { struct constraint_node *c, *lc; struct constraint_expr *e, *le; @@ -951,14 +1212,13 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, if (!c) return -ENOMEM; - if (lc) { + if (lc) lc->next = c; - } else { + else *nodep = c; - } rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) + if (rc) return rc; c->permissions = le32_to_cpu(buf[0]); nexpr = le32_to_cpu(buf[1]); @@ -969,14 +1229,13 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, if (!e) return -ENOMEM; - if (le) { + if (le) le->next = e; - } else { + else c->expr = e; - } rc = next_entry(buf, fp, (sizeof(u32) * 3)); - if (rc < 0) + if (rc) return rc; e->expr_type = le32_to_cpu(buf[0]); e->attr = le32_to_cpu(buf[1]); @@ -1004,8 +1263,21 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, if (depth == (CEXPR_MAXDEPTH - 1)) return -EINVAL; depth++; - if (ebitmap_read(&e->names, fp)) - return -EINVAL; + rc = ebitmap_read(&e->names, fp); + if (rc) + return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + e->type_names = kzalloc(sizeof + (*e->type_names), + GFP_KERNEL); + if (!e->type_names) + return -ENOMEM; + type_set_init(e->type_names); + rc = type_set_read(e->type_names, fp); + if (rc) + return rc; + } break; default: return -EINVAL; @@ -1028,14 +1300,13 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) u32 len, len2, ncons, nel; int i, rc; + rc = -ENOMEM; cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL); - if (!cladatum) { - rc = -ENOMEM; - goto out; - } + if (!cladatum) + goto bad; rc = next_entry(buf, fp, sizeof(u32)*6); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); @@ -1050,33 +1321,30 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) ncons = le32_to_cpu(buf[5]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; if (len2) { - cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL); - if (!cladatum->comkey) { - rc = -ENOMEM; + rc = -ENOMEM; + cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL); + if (!cladatum->comkey) goto bad; - } rc = next_entry(cladatum->comkey, fp, len2); - if (rc < 0) + if (rc) goto bad; - cladatum->comkey[len2] = 0; + cladatum->comkey[len2] = '\0'; - cladatum->comdatum = hashtab_search(p->p_commons.table, - cladatum->comkey); + rc = -EINVAL; + cladatum->comdatum = hashtab_search(p->p_commons.table, cladatum->comkey); if (!cladatum->comdatum) { - printk(KERN_ERR "security: unknown common %s\n", - cladatum->comkey); - rc = -EINVAL; + printk(KERN_ERR "SELinux: unknown common %s\n", cladatum->comkey); goto bad; } } @@ -1086,63 +1354,83 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; } - rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) { /* grab the validatetrans rules */ rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto bad; ncons = le32_to_cpu(buf[0]); - rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); + rc = read_cons_helper(p, &cladatum->validatetrans, + ncons, 1, fp); + if (rc) + goto bad; + } + + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + rc = next_entry(buf, fp, sizeof(u32) * 3); + if (rc) + goto bad; + + cladatum->default_user = le32_to_cpu(buf[0]); + cladatum->default_role = le32_to_cpu(buf[1]); + cladatum->default_range = le32_to_cpu(buf[2]); + } + + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + rc = next_entry(buf, fp, sizeof(u32) * 1); if (rc) goto bad; + cladatum->default_type = le32_to_cpu(buf[0]); } rc = hashtab_insert(h, key, cladatum); if (rc) goto bad; - rc = 0; -out: - return rc; + return 0; bad: - class_destroy(key, cladatum, NULL); - goto out; + cls_destroy(key, cladatum, NULL); + return rc; } 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; + rc = -ENOMEM; role = kzalloc(sizeof(*role), GFP_KERNEL); - if (!role) { - rc = -ENOMEM; - goto out; - } + if (!role) + goto bad; - rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); + if (rc) 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) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } + rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = ebitmap_read(&role->dominates, fp); if (rc) @@ -1153,10 +1441,10 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; if (strcmp(key, OBJECT_R) == 0) { + rc = -EINVAL; if (role->value != OBJECT_R_VAL) { - printk(KERN_ERR "Role %s has wrong value %d\n", + printk(KERN_ERR "SELinux: Role %s has wrong value %d\n", OBJECT_R, role->value); - rc = -EINVAL; goto bad; } rc = 0; @@ -1166,53 +1454,63 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) rc = hashtab_insert(h, key, role); if (rc) goto bad; -out: - return rc; + return 0; bad: role_destroy(key, role, NULL); - goto out; + return rc; } 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); - if (!typdatum) { - rc = -ENOMEM; - return rc; - } + rc = -ENOMEM; + typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); + if (!typdatum) + goto bad; - rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); + if (rc) 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]); - key = kmalloc(len + 1,GFP_KERNEL); - if (!key) { - rc = -ENOMEM; - goto bad; + 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]); } + + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) + goto bad; rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = hashtab_insert(h, key, typdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: type_destroy(key, typdatum, NULL); - goto out; + return rc; } @@ -1228,53 +1526,53 @@ static int mls_read_level(struct mls_level *lp, void *fp) memset(lp, 0, sizeof(*lp)); rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) { - printk(KERN_ERR "security: mls: truncated level\n"); - goto bad; + if (rc) { + printk(KERN_ERR "SELinux: mls: truncated level\n"); + return rc; } lp->sens = le32_to_cpu(buf[0]); - if (ebitmap_read(&lp->cat, fp)) { - printk(KERN_ERR "security: mls: error reading level " - "categories\n"); - goto bad; + rc = ebitmap_read(&lp->cat, fp); + if (rc) { + printk(KERN_ERR "SELinux: mls: error reading level categories\n"); + return rc; } return 0; - -bad: - return -EINVAL; } 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; + rc = -ENOMEM; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); - if (!usrdatum) { - rc = -ENOMEM; - goto out; - } + if (!usrdatum) + goto bad; - rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); + if (rc) 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) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_KERNEL); + if (!key) goto bad; - } rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = ebitmap_read(&usrdatum->roles, fp); if (rc) @@ -1292,11 +1590,10 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) rc = hashtab_insert(h, key, usrdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: user_destroy(key, usrdatum, NULL); - goto out; + return rc; } static int sens_read(struct policydb *p, struct hashtab *h, void *fp) @@ -1307,47 +1604,43 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; + rc = -ENOMEM; levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC); - if (!levdatum) { - rc = -ENOMEM; - goto out; - } + if (!levdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); levdatum->isalias = le32_to_cpu(buf[1]); - key = kmalloc(len + 1,GFP_ATOMIC); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_ATOMIC); + if (!key) goto bad; - } rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; + rc = -ENOMEM; levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC); - if (!levdatum->level) { - rc = -ENOMEM; + if (!levdatum->level) goto bad; - } - if (mls_read_level(levdatum->level, fp)) { - rc = -EINVAL; + + rc = mls_read_level(levdatum->level, fp); + if (rc) goto bad; - } rc = hashtab_insert(h, key, levdatum); if (rc) goto bad; -out: - return rc; + return 0; bad: sens_destroy(key, levdatum, NULL); - goto out; + return rc; } static int cat_read(struct policydb *p, struct hashtab *h, void *fp) @@ -1358,39 +1651,35 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[3]; u32 len; + rc = -ENOMEM; catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC); - if (!catdatum) { - rc = -ENOMEM; - goto out; - } + if (!catdatum) + goto bad; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto bad; len = le32_to_cpu(buf[0]); catdatum->value = le32_to_cpu(buf[1]); catdatum->isalias = le32_to_cpu(buf[2]); - key = kmalloc(len + 1,GFP_ATOMIC); - if (!key) { - rc = -ENOMEM; + rc = -ENOMEM; + key = kmalloc(len + 1, GFP_ATOMIC); + if (!key) goto bad; - } rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto bad; - key[len] = 0; + key[len] = '\0'; rc = hashtab_insert(h, key, catdatum); if (rc) goto bad; -out: - return rc; - + return 0; bad: cat_destroy(key, catdatum, NULL); - goto out; + return rc; } static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) = @@ -1405,7 +1694,588 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; -extern int ss_initialized; +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", + sym_name(p, SYM_USERS, user->value - 1), + sym_name(p, SYM_ROLES, bit), + sym_name(p, SYM_USERS, 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", + sym_name(p, SYM_ROLES, role->value - 1), + sym_name(p, SYM_TYPES, bit), + sym_name(p, SYM_ROLES, upper->value - 1)); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper; + struct policydb *p = datap; + int depth = 0; + + upper = 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 = flex_array_get_ptr(p->type_val_to_struct_array, + upper->bounds - 1); + BUG_ON(!upper); + + if (upper->attribute) { + printk(KERN_ERR "SELinux: type %s: " + "bounded by attribute %s", + (char *) key, + sym_name(p, SYM_TYPES, 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; +} + +u16 string_to_security_class(struct policydb *p, const char *name) +{ + struct class_datum *cladatum; + + cladatum = hashtab_search(p->p_classes.table, name); + if (!cladatum) + return 0; + + return cladatum->value; +} + +u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) +{ + struct class_datum *cladatum; + struct perm_datum *perdatum = NULL; + struct common_datum *comdatum; + + if (!tclass || tclass > p->p_classes.nprim) + return 0; + + cladatum = p->class_val_to_struct[tclass-1]; + comdatum = cladatum->comdatum; + if (comdatum) + perdatum = hashtab_search(comdatum->permissions.table, + name); + if (!perdatum) + perdatum = hashtab_search(cladatum->permissions.table, + name); + if (!perdatum) + return 0; + + return 1U << (perdatum->value-1); +} + +static int range_read(struct policydb *p, void *fp) +{ + struct range_trans *rt = NULL; + struct mls_range *r = NULL; + int i, rc; + __le32 buf[2]; + u32 nel; + + if (p->policyvers < POLICYDB_VERSION_MLS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel = le32_to_cpu(buf[0]); + for (i = 0; i < nel; i++) { + rc = -ENOMEM; + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + goto out; + + rc = next_entry(buf, fp, (sizeof(u32) * 2)); + if (rc) + goto out; + + rt->source_type = le32_to_cpu(buf[0]); + rt->target_type = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + rt->target_class = le32_to_cpu(buf[0]); + } else + rt->target_class = p->process_class; + + rc = -EINVAL; + if (!policydb_type_isvalid(p, rt->source_type) || + !policydb_type_isvalid(p, rt->target_type) || + !policydb_class_isvalid(p, rt->target_class)) + goto out; + + rc = -ENOMEM; + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + goto out; + + rc = mls_read_range_helper(r, fp); + if (rc) + goto out; + + rc = -EINVAL; + if (!mls_range_isvalid(p, r)) { + printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); + goto out; + } + + rc = hashtab_insert(p->range_tr, rt, r); + if (rc) + goto out; + + rt = NULL; + r = NULL; + } + hash_eval(p->range_tr, "rangetr"); + rc = 0; +out: + kfree(rt); + kfree(r); + return rc; +} + +static int filename_trans_read(struct policydb *p, void *fp) +{ + struct filename_trans *ft; + struct filename_trans_datum *otype; + char *name; + u32 nel, len; + __le32 buf[4]; + int rc, i; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + return rc; + nel = le32_to_cpu(buf[0]); + + for (i = 0; i < nel; i++) { + ft = NULL; + otype = NULL; + name = NULL; + + rc = -ENOMEM; + ft = kzalloc(sizeof(*ft), GFP_KERNEL); + if (!ft) + goto out; + + rc = -ENOMEM; + otype = kmalloc(sizeof(*otype), GFP_KERNEL); + if (!otype) + goto out; + + /* length of the path component string */ + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) + goto out; + + ft->name = name; + + /* path component string */ + rc = next_entry(name, fp, len); + if (rc) + goto out; + name[len] = 0; + + rc = next_entry(buf, fp, sizeof(u32) * 4); + if (rc) + goto out; + + ft->stype = le32_to_cpu(buf[0]); + ft->ttype = le32_to_cpu(buf[1]); + ft->tclass = le32_to_cpu(buf[2]); + + otype->otype = le32_to_cpu(buf[3]); + + rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1); + if (rc) + goto out; + + rc = hashtab_insert(p->filename_trans, ft, otype); + if (rc) { + /* + * Do not return -EEXIST to the caller, or the system + * will not boot. + */ + if (rc != -EEXIST) + goto out; + /* But free memory to avoid memory leak. */ + kfree(ft); + kfree(name); + kfree(otype); + } + } + hash_eval(p->filename_trans, "filenametr"); + return 0; +out: + kfree(ft); + kfree(name); + kfree(otype); + + return rc; +} + +static int genfs_read(struct policydb *p, void *fp) +{ + int i, j, rc; + u32 nel, nel2, len, len2; + __le32 buf[1]; + struct ocontext *l, *c; + struct ocontext *newc = NULL; + struct genfs *genfs_p, *genfs; + struct genfs *newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + for (i = 0; i < nel; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); + if (!newgenfs) + goto out; + + rc = -ENOMEM; + newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); + if (!newgenfs->fstype) + goto out; + + rc = next_entry(newgenfs->fstype, fp, len); + if (rc) + goto out; + + newgenfs->fstype[len] = 0; + + for (genfs_p = NULL, genfs = p->genfs; genfs; + genfs_p = genfs, genfs = genfs->next) { + rc = -EINVAL; + if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { + printk(KERN_ERR "SELinux: dup genfs fstype %s\n", + newgenfs->fstype); + goto out; + } + if (strcmp(newgenfs->fstype, genfs->fstype) < 0) + break; + } + newgenfs->next = genfs; + if (genfs_p) + genfs_p->next = newgenfs; + else + p->genfs = newgenfs; + genfs = newgenfs; + newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel2 = le32_to_cpu(buf[0]); + for (j = 0; j < nel2; j++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newc = kzalloc(sizeof(*newc), GFP_KERNEL); + if (!newc) + goto out; + + rc = -ENOMEM; + newc->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!newc->u.name) + goto out; + + rc = next_entry(newc->u.name, fp, len); + if (rc) + goto out; + newc->u.name[len] = 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + newc->v.sclass = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&newc->context[0], p, fp); + if (rc) + goto out; + + for (l = NULL, c = genfs->head; c; + l = c, c = c->next) { + rc = -EINVAL; + if (!strcmp(newc->u.name, c->u.name) && + (!c->v.sclass || !newc->v.sclass || + newc->v.sclass == c->v.sclass)) { + printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n", + genfs->fstype, c->u.name); + goto out; + } + len = strlen(newc->u.name); + len2 = strlen(c->u.name); + if (len > len2) + break; + } + + newc->next = c; + if (l) + l->next = newc; + else + genfs->head = newc; + newc = NULL; + } + } + rc = 0; +out: + if (newgenfs) + kfree(newgenfs->fstype); + kfree(newgenfs); + ocontext_destroy(newc, OCON_FSUSE); + + return rc; +} + +static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, + void *fp) +{ + int i, j, rc; + u32 nel, len; + __le32 buf[3]; + struct ocontext *l, *c; + u32 nodebuf[8]; + + for (i = 0; i < info->ocon_num; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + l = NULL; + for (j = 0; j < nel; j++) { + rc = -ENOMEM; + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + goto out; + if (l) + l->next = c; + else + p->ocontexts[i] = c; + l = c; + + switch (i) { + case OCON_ISID: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + c->sid[0] = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FS: + case OCON_NETIF: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + rc = context_read_and_validate(&c->context[1], p, fp); + if (rc) + goto out; + break; + case OCON_PORT: + rc = next_entry(buf, fp, sizeof(u32)*3); + if (rc) + goto out; + c->u.port.protocol = le32_to_cpu(buf[0]); + c->u.port.low_port = le32_to_cpu(buf[1]); + c->u.port.high_port = le32_to_cpu(buf[2]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE: + rc = next_entry(nodebuf, fp, sizeof(u32) * 2); + if (rc) + goto out; + c->u.node.addr = nodebuf[0]; /* network order */ + c->u.node.mask = nodebuf[1]; /* network order */ + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FSUSE: + rc = next_entry(buf, fp, sizeof(u32)*2); + if (rc) + goto out; + + rc = -EINVAL; + c->v.behavior = le32_to_cpu(buf[0]); + /* Determined at runtime, not in policy DB. */ + if (c->v.behavior == SECURITY_FS_USE_MNTPOINT) + goto out; + if (c->v.behavior > SECURITY_FS_USE_MAX) + goto out; + + rc = -ENOMEM; + len = le32_to_cpu(buf[1]); + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE6: { + int k; + + rc = next_entry(nodebuf, fp, sizeof(u32) * 8); + if (rc) + goto out; + for (k = 0; k < 4; k++) + c->u.node6.addr[k] = nodebuf[k]; + for (k = 0; k < 4; k++) + c->u.node6.mask[k] = nodebuf[k+4]; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + } + } + } + } + rc = 0; +out: + return rc; +} /* * Read the configuration data from a policy database binary @@ -1415,56 +2285,58 @@ int policydb_read(struct policydb *p, void *fp) { struct role_allow *ra, *lra; struct role_trans *tr, *ltr; - struct ocontext *l, *c, *newc; - struct genfs *genfs_p, *genfs, *newgenfs; int i, j, rc; - __le32 buf[8]; - u32 len, len2, config, nprim, nel, nel2; + __le32 buf[4]; + u32 len, nprim, nel; + char *policydb_str; struct policydb_compat_info *info; - struct range_trans *rt, *lrt; - - config = 0; rc = policydb_init(p); if (rc) - goto out; + return rc; /* Read the magic number and string length. */ - rc = next_entry(buf, fp, sizeof(u32)* 2); - if (rc < 0) + rc = next_entry(buf, fp, sizeof(u32) * 2); + if (rc) goto bad; + rc = -EINVAL; if (le32_to_cpu(buf[0]) != POLICYDB_MAGIC) { - printk(KERN_ERR "security: policydb magic number 0x%x does " + printk(KERN_ERR "SELinux: policydb magic number 0x%x does " "not match expected magic number 0x%x\n", le32_to_cpu(buf[0]), POLICYDB_MAGIC); goto bad; } + rc = -EINVAL; len = le32_to_cpu(buf[1]); if (len != strlen(POLICYDB_STRING)) { - printk(KERN_ERR "security: policydb string length %d does not " + printk(KERN_ERR "SELinux: policydb string length %d does not " "match expected length %Zu\n", len, strlen(POLICYDB_STRING)); goto bad; } - policydb_str = kmalloc(len + 1,GFP_KERNEL); + + rc = -ENOMEM; + policydb_str = kmalloc(len + 1, GFP_KERNEL); if (!policydb_str) { - printk(KERN_ERR "security: unable to allocate memory for policydb " + printk(KERN_ERR "SELinux: unable to allocate memory for policydb " "string of length %d\n", len); - rc = -ENOMEM; goto bad; } + rc = next_entry(policydb_str, fp, len); - if (rc < 0) { - printk(KERN_ERR "security: truncated policydb string identifier\n"); + if (rc) { + printk(KERN_ERR "SELinux: truncated policydb string identifier\n"); kfree(policydb_str); goto bad; } - policydb_str[len] = 0; + + rc = -EINVAL; + policydb_str[len] = '\0'; if (strcmp(policydb_str, POLICYDB_STRING)) { - printk(KERN_ERR "security: policydb string %s does not match " + printk(KERN_ERR "SELinux: policydb string %s does not match " "my string %s\n", policydb_str, POLICYDB_STRING); kfree(policydb_str); goto bad; @@ -1473,52 +2345,59 @@ int policydb_read(struct policydb *p, void *fp) kfree(policydb_str); policydb_str = NULL; - /* Read the version, config, and table sizes. */ + /* Read the version and table sizes. */ rc = next_entry(buf, fp, sizeof(u32)*4); - if (rc < 0) + if (rc) goto bad; + rc = -EINVAL; p->policyvers = le32_to_cpu(buf[0]); if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX) { - printk(KERN_ERR "security: policydb version %d does not match " - "my version range %d-%d\n", - le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); - goto bad; + printk(KERN_ERR "SELinux: policydb version %d does not match " + "my version range %d-%d\n", + le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); + goto bad; } if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) { - if (ss_initialized && !selinux_mls_enabled) { - printk(KERN_ERR "Cannot switch between non-MLS and MLS " - "policies\n"); - goto bad; - } - selinux_mls_enabled = 1; - config |= POLICYDB_CONFIG_MLS; + p->mls_enabled = 1; + rc = -EINVAL; if (p->policyvers < POLICYDB_VERSION_MLS) { - printk(KERN_ERR "security policydb version %d (MLS) " - "not backwards compatible\n", p->policyvers); + printk(KERN_ERR "SELinux: security policydb version %d " + "(MLS) not backwards compatible\n", + p->policyvers); goto bad; } - } else { - if (ss_initialized && selinux_mls_enabled) { - printk(KERN_ERR "Cannot switch between MLS and non-MLS " - "policies\n"); + } + p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN); + p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN); + + if (p->policyvers >= POLICYDB_VERSION_POLCAP) { + rc = ebitmap_read(&p->policycaps, fp); + if (rc) + goto bad; + } + + if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE) { + rc = ebitmap_read(&p->permissive_map, fp); + if (rc) goto bad; - } } + rc = -EINVAL; info = policydb_lookup_compat(p->policyvers); if (!info) { - printk(KERN_ERR "security: unable to find policy compat info " + printk(KERN_ERR "SELinux: unable to find policy compat info " "for version %d\n", p->policyvers); goto bad; } + rc = -EINVAL; if (le32_to_cpu(buf[2]) != info->sym_num || le32_to_cpu(buf[3]) != info->ocon_num) { - printk(KERN_ERR "security: policydb table sizes (%d,%d) do " + printk(KERN_ERR "SELinux: policydb table sizes (%d,%d) do " "not match mine (%d,%d)\n", le32_to_cpu(buf[2]), le32_to_cpu(buf[3]), info->sym_num, info->ocon_num); @@ -1527,7 +2406,7 @@ int policydb_read(struct policydb *p, void *fp) for (i = 0; i < info->sym_num; i++) { rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) + if (rc) goto bad; nprim = le32_to_cpu(buf[0]); nel = le32_to_cpu(buf[1]); @@ -1540,7 +2419,12 @@ int policydb_read(struct policydb *p, void *fp) p->symtab[i].nprim = nprim; } - rc = avtab_read(&p->te_avtab, fp, p->policyvers); + rc = -EINVAL; + p->process_class = string_to_security_class(p, "process"); + if (!p->process_class) + goto bad; + + rc = avtab_read(&p->te_avtab, fp, p); if (rc) goto bad; @@ -1551,326 +2435,1081 @@ int policydb_read(struct policydb *p, void *fp) } rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto bad; nel = le32_to_cpu(buf[0]); ltr = NULL; for (i = 0; i < nel; i++) { + rc = -ENOMEM; tr = kzalloc(sizeof(*tr), GFP_KERNEL); - if (!tr) { - rc = -ENOMEM; + if (!tr) goto bad; - } - if (ltr) { + if (ltr) ltr->next = tr; - } else { + else p->role_tr = tr; - } rc = next_entry(buf, fp, sizeof(u32)*3); - if (rc < 0) + if (rc) goto bad; + + rc = -EINVAL; tr->role = le32_to_cpu(buf[0]); tr->type = le32_to_cpu(buf[1]); tr->new_role = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto bad; + tr->tclass = le32_to_cpu(buf[0]); + } else + tr->tclass = p->process_class; + + if (!policydb_role_isvalid(p, tr->role) || + !policydb_type_isvalid(p, tr->type) || + !policydb_class_isvalid(p, tr->tclass) || + !policydb_role_isvalid(p, tr->new_role)) + goto bad; ltr = tr; } rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc) goto bad; nel = le32_to_cpu(buf[0]); lra = NULL; for (i = 0; i < nel; i++) { + rc = -ENOMEM; ra = kzalloc(sizeof(*ra), GFP_KERNEL); - if (!ra) { - rc = -ENOMEM; + if (!ra) goto bad; - } - if (lra) { + if (lra) lra->next = ra; - } else { + else p->role_allow = ra; - } rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) + if (rc) goto bad; + + rc = -EINVAL; ra->role = le32_to_cpu(buf[0]); ra->new_role = le32_to_cpu(buf[1]); + if (!policydb_role_isvalid(p, ra->role) || + !policydb_role_isvalid(p, ra->new_role)) + goto bad; lra = ra; } - rc = policydb_index_classes(p); + rc = filename_trans_read(p, fp); if (rc) goto bad; - rc = policydb_index_others(p); + rc = policydb_index(p); if (rc) goto bad; - for (i = 0; i < info->ocon_num; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - l = NULL; - for (j = 0; j < nel; j++) { - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - rc = -ENOMEM; + rc = -EINVAL; + p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition"); + p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition"); + if (!p->process_trans_perms) + goto bad; + + rc = ocontext_read(p, info, fp); + if (rc) + goto bad; + + rc = genfs_read(p, fp); + if (rc) + goto bad; + + rc = range_read(p, fp); + if (rc) + goto bad; + + rc = -ENOMEM; + p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), + p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->type_attr_map_array) + goto bad; + + /* preallocate so we don't have to worry about the put ever failing */ + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (rc) + goto bad; + + for (i = 0; i < p->p_types.nprim; i++) { + struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); + + BUG_ON(!e); + ebitmap_init(e); + if (p->policyvers >= POLICYDB_VERSION_AVTAB) { + rc = ebitmap_read(e, fp); + if (rc) goto bad; + } + /* add the type itself as the degenerate case */ + rc = ebitmap_set_bit(e, i, 1); + if (rc) + goto bad; + } + + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + + rc = 0; +out: + return rc; +bad: + policydb_destroy(p); + goto out; +} + +/* + * Write a MLS level structure to a policydb binary + * representation file. + */ +static int mls_write_level(struct mls_level *l, void *fp) +{ + __le32 buf[1]; + int rc; + + buf[0] = cpu_to_le32(l->sens); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = ebitmap_write(&l->cat, fp); + if (rc) + return rc; + + return 0; +} + +/* + * Write a MLS range structure to a policydb binary + * representation file. + */ +static int mls_write_range_helper(struct mls_range *r, void *fp) +{ + __le32 buf[3]; + size_t items; + int rc, eq; + + eq = mls_level_eq(&r->level[1], &r->level[0]); + + if (eq) + items = 2; + else + items = 3; + buf[0] = cpu_to_le32(items-1); + buf[1] = cpu_to_le32(r->level[0].sens); + if (!eq) + buf[2] = cpu_to_le32(r->level[1].sens); + + BUG_ON(items > (sizeof(buf)/sizeof(buf[0]))); + + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = ebitmap_write(&r->level[0].cat, fp); + if (rc) + return rc; + if (!eq) { + rc = ebitmap_write(&r->level[1].cat, fp); + if (rc) + return rc; + } + + return 0; +} + +static int sens_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct level_datum *levdatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[2]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(levdatum->isalias); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = mls_write_level(levdatum->level, fp); + if (rc) + return rc; + + return 0; +} + +static int cat_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct cat_datum *catdatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[3]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(catdatum->value); + buf[2] = cpu_to_le32(catdatum->isalias); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + return 0; +} + +static int role_trans_write(struct policydb *p, void *fp) +{ + struct role_trans *r = p->role_tr; + struct role_trans *tr; + u32 buf[3]; + size_t nel; + int rc; + + nel = 0; + for (tr = r; tr; tr = tr->next) + nel++; + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (tr = r; tr; tr = tr->next) { + buf[0] = cpu_to_le32(tr->role); + buf[1] = cpu_to_le32(tr->type); + buf[2] = cpu_to_le32(tr->new_role); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + buf[0] = cpu_to_le32(tr->tclass); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + } + } + + return 0; +} + +static int role_allow_write(struct role_allow *r, void *fp) +{ + struct role_allow *ra; + u32 buf[2]; + size_t nel; + int rc; + + nel = 0; + for (ra = r; ra; ra = ra->next) + nel++; + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (ra = r; ra; ra = ra->next) { + buf[0] = cpu_to_le32(ra->role); + buf[1] = cpu_to_le32(ra->new_role); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + } + return 0; +} + +/* + * Write a security context structure + * to a policydb binary representation file. + */ +static int context_write(struct policydb *p, struct context *c, + void *fp) +{ + int rc; + __le32 buf[3]; + + buf[0] = cpu_to_le32(c->user); + buf[1] = cpu_to_le32(c->role); + buf[2] = cpu_to_le32(c->type); + + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + rc = mls_write_range_helper(&c->range, fp); + if (rc) + return rc; + + return 0; +} + +/* + * The following *_write functions are used to + * write the symbol data to a policy database + * binary representation file. + */ + +static int perm_write(void *vkey, void *datum, void *fp) +{ + char *key = vkey; + struct perm_datum *perdatum = datum; + __le32 buf[2]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(perdatum->value); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + return 0; +} + +static int common_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct common_datum *comdatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + __le32 buf[4]; + size_t len; + int rc; + + len = strlen(key); + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(comdatum->value); + buf[2] = cpu_to_le32(comdatum->permissions.nprim); + buf[3] = cpu_to_le32(comdatum->permissions.table->nel); + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = hashtab_map(comdatum->permissions.table, perm_write, fp); + if (rc) + return rc; + + return 0; +} + +static int type_set_write(struct type_set *t, void *fp) +{ + int rc; + __le32 buf[1]; + + if (ebitmap_write(&t->types, fp)) + return -EINVAL; + if (ebitmap_write(&t->negset, fp)) + return -EINVAL; + + buf[0] = cpu_to_le32(t->flags); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return -EINVAL; + + return 0; +} + +static int write_cons_helper(struct policydb *p, struct constraint_node *node, + void *fp) +{ + struct constraint_node *c; + struct constraint_expr *e; + __le32 buf[3]; + u32 nel; + int rc; + + for (c = node; c; c = c->next) { + nel = 0; + for (e = c->expr; e; e = e->next) + nel++; + buf[0] = cpu_to_le32(c->permissions); + buf[1] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + for (e = c->expr; e; e = e->next) { + buf[0] = cpu_to_le32(e->expr_type); + buf[1] = cpu_to_le32(e->attr); + buf[2] = cpu_to_le32(e->op); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + switch (e->expr_type) { + case CEXPR_NAMES: + rc = ebitmap_write(&e->names, fp); + if (rc) + return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + rc = type_set_write(e->type_names, fp); + if (rc) + return rc; + } + break; + default: + break; } - if (l) { - l->next = c; - } else { - p->ocontexts[i] = c; - } - l = c; - rc = -EINVAL; + } + } + + return 0; +} + +static int class_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct class_datum *cladatum = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + struct policydb *p = pd->p; + struct constraint_node *c; + __le32 buf[6]; + u32 ncons; + size_t len, len2; + int rc; + + len = strlen(key); + if (cladatum->comkey) + len2 = strlen(cladatum->comkey); + else + len2 = 0; + + ncons = 0; + for (c = cladatum->constraints; c; c = c->next) + ncons++; + + buf[0] = cpu_to_le32(len); + buf[1] = cpu_to_le32(len2); + buf[2] = cpu_to_le32(cladatum->value); + buf[3] = cpu_to_le32(cladatum->permissions.nprim); + if (cladatum->permissions.table) + buf[4] = cpu_to_le32(cladatum->permissions.table->nel); + else + buf[4] = 0; + buf[5] = cpu_to_le32(ncons); + rc = put_entry(buf, sizeof(u32), 6, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + if (cladatum->comkey) { + rc = put_entry(cladatum->comkey, 1, len2, fp); + if (rc) + return rc; + } + + rc = hashtab_map(cladatum->permissions.table, perm_write, fp); + if (rc) + return rc; + + rc = write_cons_helper(p, cladatum->constraints, fp); + if (rc) + return rc; + + /* write out the validatetrans rule */ + ncons = 0; + for (c = cladatum->validatetrans; c; c = c->next) + ncons++; + + buf[0] = cpu_to_le32(ncons); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = write_cons_helper(p, cladatum->validatetrans, fp); + if (rc) + return rc; + + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + buf[0] = cpu_to_le32(cladatum->default_user); + buf[1] = cpu_to_le32(cladatum->default_role); + buf[2] = cpu_to_le32(cladatum->default_range); + + rc = put_entry(buf, sizeof(uint32_t), 3, fp); + if (rc) + return rc; + } + + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + buf[0] = cpu_to_le32(cladatum->default_type); + rc = put_entry(buf, sizeof(uint32_t), 1, fp); + if (rc) + return rc; + } + + return 0; +} + +static int role_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct role_datum *role = datum; + struct policy_data *pd = ptr; + void *fp = pd->fp; + struct policydb *p = pd->p; + __le32 buf[3]; + size_t items, len; + int rc; + + len = strlen(key); + items = 0; + buf[items++] = cpu_to_le32(len); + buf[items++] = cpu_to_le32(role->value); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + buf[items++] = cpu_to_le32(role->bounds); + + BUG_ON(items > (sizeof(buf)/sizeof(buf[0]))); + + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = ebitmap_write(&role->dominates, fp); + if (rc) + return rc; + + rc = ebitmap_write(&role->types, fp); + if (rc) + return rc; + + return 0; +} + +static int type_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct type_datum *typdatum = datum; + struct policy_data *pd = ptr; + struct policydb *p = pd->p; + void *fp = pd->fp; + __le32 buf[4]; + int rc; + size_t items, len; + + len = strlen(key); + items = 0; + buf[items++] = cpu_to_le32(len); + buf[items++] = cpu_to_le32(typdatum->value); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 properties = 0; + + if (typdatum->primary) + properties |= TYPEDATUM_PROPERTY_PRIMARY; + + if (typdatum->attribute) + properties |= TYPEDATUM_PROPERTY_ATTRIBUTE; + + buf[items++] = cpu_to_le32(properties); + buf[items++] = cpu_to_le32(typdatum->bounds); + } else { + buf[items++] = cpu_to_le32(typdatum->primary); + } + BUG_ON(items > (sizeof(buf) / sizeof(buf[0]))); + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + return 0; +} + +static int user_write(void *vkey, void *datum, void *ptr) +{ + char *key = vkey; + struct user_datum *usrdatum = datum; + struct policy_data *pd = ptr; + struct policydb *p = pd->p; + void *fp = pd->fp; + __le32 buf[3]; + size_t items, len; + int rc; + + len = strlen(key); + items = 0; + buf[items++] = cpu_to_le32(len); + buf[items++] = cpu_to_le32(usrdatum->value); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + buf[items++] = cpu_to_le32(usrdatum->bounds); + BUG_ON(items > (sizeof(buf) / sizeof(buf[0]))); + rc = put_entry(buf, sizeof(u32), items, fp); + if (rc) + return rc; + + rc = put_entry(key, 1, len, fp); + if (rc) + return rc; + + rc = ebitmap_write(&usrdatum->roles, fp); + if (rc) + return rc; + + rc = mls_write_range_helper(&usrdatum->range, fp); + if (rc) + return rc; + + rc = mls_write_level(&usrdatum->dfltlevel, fp); + if (rc) + return rc; + + return 0; +} + +static int (*write_f[SYM_NUM]) (void *key, void *datum, + void *datap) = +{ + common_write, + class_write, + role_write, + type_write, + user_write, + cond_write_bool, + sens_write, + cat_write, +}; + +static int ocontext_write(struct policydb *p, struct policydb_compat_info *info, + void *fp) +{ + unsigned int i, j, rc; + size_t nel, len; + __le32 buf[3]; + u32 nodebuf[8]; + struct ocontext *c; + for (i = 0; i < info->ocon_num; i++) { + nel = 0; + for (c = p->ocontexts[i]; c; c = c->next) + nel++; + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (c = p->ocontexts[i]; c; c = c->next) { switch (i) { case OCON_ISID: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - c->sid[0] = le32_to_cpu(buf[0]); - rc = context_read_and_validate(&c->context[0], p, fp); + buf[0] = cpu_to_le32(c->sid[0]); + rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) - goto bad; + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; break; case OCON_FS: case OCON_NETIF: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - c->u.name = kmalloc(len + 1,GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); + len = strlen(c->u.name); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) - goto bad; - rc = context_read_and_validate(&c->context[1], p, fp); + return rc; + rc = put_entry(c->u.name, 1, len, fp); if (rc) - goto bad; + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; + rc = context_write(p, &c->context[1], fp); + if (rc) + return rc; break; case OCON_PORT: - rc = next_entry(buf, fp, sizeof(u32)*3); - if (rc < 0) - goto bad; - c->u.port.protocol = le32_to_cpu(buf[0]); - c->u.port.low_port = le32_to_cpu(buf[1]); - c->u.port.high_port = le32_to_cpu(buf[2]); - rc = context_read_and_validate(&c->context[0], p, fp); + buf[0] = cpu_to_le32(c->u.port.protocol); + buf[1] = cpu_to_le32(c->u.port.low_port); + buf[2] = cpu_to_le32(c->u.port.high_port); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); if (rc) - goto bad; + return rc; break; case OCON_NODE: - rc = next_entry(buf, fp, sizeof(u32)* 2); - if (rc < 0) - goto bad; - c->u.node.addr = le32_to_cpu(buf[0]); - c->u.node.mask = le32_to_cpu(buf[1]); - rc = context_read_and_validate(&c->context[0], p, fp); + nodebuf[0] = c->u.node.addr; /* network order */ + nodebuf[1] = c->u.node.mask; /* network order */ + rc = put_entry(nodebuf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); if (rc) - goto bad; + return rc; break; case OCON_FSUSE: - rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) - goto bad; - c->v.behavior = le32_to_cpu(buf[0]); - if (c->v.behavior > SECURITY_FS_USE_NONE) - goto bad; - len = le32_to_cpu(buf[1]); - c->u.name = kmalloc(len + 1,GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); + buf[0] = cpu_to_le32(c->v.behavior); + len = strlen(c->u.name); + buf[1] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 2, fp); if (rc) - goto bad; + return rc; + rc = put_entry(c->u.name, 1, len, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; break; - case OCON_NODE6: { - int k; - - rc = next_entry(buf, fp, sizeof(u32) * 8); - if (rc < 0) - goto bad; - for (k = 0; k < 4; k++) - c->u.node6.addr[k] = le32_to_cpu(buf[k]); - for (k = 0; k < 4; k++) - c->u.node6.mask[k] = le32_to_cpu(buf[k+4]); - if (context_read_and_validate(&c->context[0], p, fp)) - goto bad; + case OCON_NODE6: + for (j = 0; j < 4; j++) + nodebuf[j] = c->u.node6.addr[j]; /* network order */ + for (j = 0; j < 4; j++) + nodebuf[j + 4] = c->u.node6.mask[j]; /* network order */ + rc = put_entry(nodebuf, sizeof(u32), 8, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; break; } - } } } + return 0; +} - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - genfs_p = NULL; - rc = -EINVAL; - for (i = 0; i < nel; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); - if (!newgenfs) { - rc = -ENOMEM; - goto bad; - } +static int genfs_write(struct policydb *p, void *fp) +{ + struct genfs *genfs; + struct ocontext *c; + size_t len; + __le32 buf[1]; + int rc; - newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL); - if (!newgenfs->fstype) { - rc = -ENOMEM; - kfree(newgenfs); - goto bad; - } - rc = next_entry(newgenfs->fstype, fp, len); - if (rc < 0) { - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - newgenfs->fstype[len] = 0; - for (genfs_p = NULL, genfs = p->genfs; genfs; - genfs_p = genfs, genfs = genfs->next) { - if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { - printk(KERN_ERR "security: dup genfs " - "fstype %s\n", newgenfs->fstype); - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - if (strcmp(newgenfs->fstype, genfs->fstype) < 0) - break; + len = 0; + for (genfs = p->genfs; genfs; genfs = genfs->next) + len++; + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (genfs = p->genfs; genfs; genfs = genfs->next) { + len = strlen(genfs->fstype); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + rc = put_entry(genfs->fstype, 1, len, fp); + if (rc) + return rc; + len = 0; + for (c = genfs->head; c; c = c->next) + len++; + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + for (c = genfs->head; c; c = c->next) { + len = strlen(c->u.name); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + rc = put_entry(c->u.name, 1, len, fp); + if (rc) + return rc; + buf[0] = cpu_to_le32(c->v.sclass); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + rc = context_write(p, &c->context[0], fp); + if (rc) + return rc; } - newgenfs->next = genfs; - if (genfs_p) - genfs_p->next = newgenfs; - else - p->genfs = newgenfs; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel2 = le32_to_cpu(buf[0]); - for (j = 0; j < nel2; j++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); + } + return 0; +} - newc = kzalloc(sizeof(*newc), GFP_KERNEL); - if (!newc) { - rc = -ENOMEM; - goto bad; - } +static int hashtab_cnt(void *key, void *data, void *ptr) +{ + int *cnt = ptr; + *cnt = *cnt + 1; - newc->u.name = kmalloc(len + 1,GFP_KERNEL); - if (!newc->u.name) { - rc = -ENOMEM; - goto bad_newc; - } - rc = next_entry(newc->u.name, fp, len); - if (rc < 0) - goto bad_newc; - newc->u.name[len] = 0; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad_newc; - newc->v.sclass = le32_to_cpu(buf[0]); - if (context_read_and_validate(&newc->context[0], p, fp)) - goto bad_newc; - for (l = NULL, c = newgenfs->head; c; - l = c, c = c->next) { - if (!strcmp(newc->u.name, c->u.name) && - (!c->v.sclass || !newc->v.sclass || - newc->v.sclass == c->v.sclass)) { - printk(KERN_ERR "security: dup genfs " - "entry (%s,%s)\n", - newgenfs->fstype, c->u.name); - goto bad_newc; - } - len = strlen(newc->u.name); - len2 = strlen(c->u.name); - if (len > len2) - break; - } + return 0; +} - newc->next = c; - if (l) - l->next = newc; - else - newgenfs->head = newc; - } +static int range_write_helper(void *key, void *data, void *ptr) +{ + __le32 buf[2]; + struct range_trans *rt = key; + struct mls_range *r = data; + struct policy_data *pd = ptr; + void *fp = pd->fp; + struct policydb *p = pd->p; + int rc; + + buf[0] = cpu_to_le32(rt->source_type); + buf[1] = cpu_to_le32(rt->target_type); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { + buf[0] = cpu_to_le32(rt->target_class); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; } + rc = mls_write_range_helper(r, fp); + if (rc) + return rc; - if (p->policyvers >= POLICYDB_VERSION_MLS) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - lrt = NULL; - for (i = 0; i < nel; i++) { - rt = kzalloc(sizeof(*rt), GFP_KERNEL); - if (!rt) { - rc = -ENOMEM; - goto bad; - } - if (lrt) - lrt->next = rt; - else - p->range_tr = rt; - rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) - goto bad; - rt->dom = le32_to_cpu(buf[0]); - rt->type = le32_to_cpu(buf[1]); - rc = mls_read_range_helper(&rt->range, fp); - if (rc) - goto bad; - lrt = rt; - } + return 0; +} + +static int range_write(struct policydb *p, void *fp) +{ + __le32 buf[1]; + int rc, nel; + struct policy_data pd; + + pd.p = p; + pd.fp = fp; + + /* count the number of entries in the hashtab */ + nel = 0; + rc = hashtab_map(p->range_tr, hashtab_cnt, &nel); + if (rc) + return rc; + + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + /* actually write all of the entries */ + rc = hashtab_map(p->range_tr, range_write_helper, &pd); + if (rc) + return rc; + + return 0; +} + +static int filename_write_helper(void *key, void *data, void *ptr) +{ + __le32 buf[4]; + struct filename_trans *ft = key; + struct filename_trans_datum *otype = data; + void *fp = ptr; + int rc; + u32 len; + + len = strlen(ft->name); + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = put_entry(ft->name, sizeof(char), len, fp); + if (rc) + return rc; + + buf[0] = cpu_to_le32(ft->stype); + buf[1] = cpu_to_le32(ft->ttype); + buf[2] = cpu_to_le32(ft->tclass); + buf[3] = cpu_to_le32(otype->otype); + + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; + + return 0; +} + +static int filename_trans_write(struct policydb *p, void *fp) +{ + u32 nel; + __le32 buf[1]; + int rc; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + nel = 0; + rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel); + if (rc) + return rc; + + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = hashtab_map(p->filename_trans, filename_write_helper, fp); + if (rc) + return rc; + + return 0; +} + +/* + * Write the configuration data in a policy database + * structure to a policy database binary representation + * file. + */ +int policydb_write(struct policydb *p, void *fp) +{ + unsigned int i, num_syms; + int rc; + __le32 buf[4]; + u32 config; + size_t len; + struct policydb_compat_info *info; + + /* + * refuse to write policy older than compressed avtab + * to simplify the writer. There are other tests dropped + * since we assume this throughout the writer code. Be + * careful if you ever try to remove this restriction + */ + if (p->policyvers < POLICYDB_VERSION_AVTAB) { + printk(KERN_ERR "SELinux: refusing to write policy version %d." + " Because it is less than version %d\n", p->policyvers, + POLICYDB_VERSION_AVTAB); + return -EINVAL; } - p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL); - if (!p->type_attr_map) - goto bad; + config = 0; + if (p->mls_enabled) + config |= POLICYDB_CONFIG_MLS; + + if (p->reject_unknown) + config |= REJECT_UNKNOWN; + if (p->allow_unknown) + config |= ALLOW_UNKNOWN; + + /* Write the magic number and string identifiers. */ + buf[0] = cpu_to_le32(POLICYDB_MAGIC); + len = strlen(POLICYDB_STRING); + buf[1] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = put_entry(POLICYDB_STRING, 1, len, fp); + if (rc) + return rc; + + /* Write the version, config, and table sizes. */ + info = policydb_lookup_compat(p->policyvers); + if (!info) { + printk(KERN_ERR "SELinux: compatibility lookup failed for policy " + "version %d", p->policyvers); + return -EINVAL; + } + + buf[0] = cpu_to_le32(p->policyvers); + buf[1] = cpu_to_le32(config); + buf[2] = cpu_to_le32(info->sym_num); + buf[3] = cpu_to_le32(info->ocon_num); + + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; + + if (p->policyvers >= POLICYDB_VERSION_POLCAP) { + rc = ebitmap_write(&p->policycaps, fp); + if (rc) + return rc; + } + + if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE) { + rc = ebitmap_write(&p->permissive_map, fp); + if (rc) + return rc; + } + + num_syms = info->sym_num; + for (i = 0; i < num_syms; i++) { + struct policy_data pd; + + pd.fp = fp; + pd.p = p; + + buf[0] = cpu_to_le32(p->symtab[i].nprim); + buf[1] = cpu_to_le32(p->symtab[i].table->nel); + + rc = put_entry(buf, sizeof(u32), 2, fp); + if (rc) + return rc; + rc = hashtab_map(p->symtab[i].table, write_f[i], &pd); + if (rc) + return rc; + } + + rc = avtab_write(p, &p->te_avtab, fp); + if (rc) + return rc; + + rc = cond_write_list(p, p->cond_list, fp); + if (rc) + return rc; + + rc = role_trans_write(p, fp); + if (rc) + return rc; + + rc = role_allow_write(p->role_allow, fp); + if (rc) + return rc; + + rc = filename_trans_write(p, fp); + if (rc) + return rc; + + rc = ocontext_write(p, info, fp); + if (rc) + return rc; + + rc = genfs_write(p, fp); + if (rc) + return rc; + + rc = range_write(p, fp); + if (rc) + return rc; for (i = 0; i < p->p_types.nprim; i++) { - ebitmap_init(&p->type_attr_map[i]); - if (p->policyvers >= POLICYDB_VERSION_AVTAB) { - if (ebitmap_read(&p->type_attr_map[i], fp)) - goto bad; - } - /* add the type itself as the degenerate case */ - if (ebitmap_set_bit(&p->type_attr_map[i], i, 1)) - goto bad; + struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); + + BUG_ON(!e); + rc = ebitmap_write(e, fp); + if (rc) + return rc; } - rc = 0; -out: - return rc; -bad_newc: - ocontext_destroy(newc,OCON_FSUSE); -bad: - if (!rc) - rc = -EINVAL; - policydb_destroy(p); - goto out; + return 0; } diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index b1340711f72..725d5945a97 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -12,21 +12,25 @@ * * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #ifndef _SS_POLICYDB_H_ #define _SS_POLICYDB_H_ +#include <linux/flex_array.h> + #include "symtab.h" #include "avtab.h" #include "sidtab.h" +#include "ebitmap.h" +#include "mls_types.h" #include "context.h" #include "constraint.h" @@ -56,22 +60,49 @@ struct class_datum { struct symtab permissions; /* class-specific permission symbol table */ struct constraint_node *constraints; /* constraints on class permissions */ struct constraint_node *validatetrans; /* special transition rules */ +/* Options how a new object user, role, and type should be decided */ +#define DEFAULT_SOURCE 1 +#define DEFAULT_TARGET 2 + char default_user; + char default_role; + char default_type; +/* Options how a new object range should be decided */ +#define DEFAULT_SOURCE_LOW 1 +#define DEFAULT_SOURCE_HIGH 2 +#define DEFAULT_SOURCE_LOW_HIGH 3 +#define DEFAULT_TARGET_LOW 4 +#define DEFAULT_TARGET_HIGH 5 +#define DEFAULT_TARGET_LOW_HIGH 6 + char default_range; }; /* 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 */ }; struct role_trans { u32 role; /* current role */ - u32 type; /* program executable type */ + u32 type; /* program executable type, or new object type */ + u32 tclass; /* process class, or new object class */ u32 new_role; /* new role */ struct role_trans *next; }; +struct filename_trans { + u32 stype; /* current process */ + u32 ttype; /* parent dir context */ + u16 tclass; /* class of new object */ + const char *name; /* last path component */ +}; + +struct filename_trans_datum { + u32 otype; /* expected of new object */ +}; + struct role_allow { u32 role; /* current role */ u32 new_role; /* new role */ @@ -81,12 +112,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 */ @@ -106,10 +140,9 @@ struct cat_datum { }; struct range_trans { - u32 dom; /* current process domain */ - u32 type; /* program executable type */ - struct mls_range range; /* new range */ - struct range_trans *next; + u32 source_type; + u32 target_type; + u32 target_class; }; /* Boolean data type */ @@ -121,6 +154,17 @@ struct cond_bool_datum { struct cond_node; /* + * type set preserves data needed to determine constraint info from + * policy source. This is not used by the kernel policy but allows + * utilities such as audit2allow to determine constraint denials. + */ +struct type_set { + struct ebitmap types; + struct ebitmap negset; + u32 flags; +}; + +/* * The configuration data includes security contexts for * initial SIDs, unlabeled file systems, TCP and UDP port numbers, * network interfaces, and nodes. This structure stores the @@ -182,6 +226,8 @@ struct genfs { /* The policy database */ struct policydb { + int mls_enabled; + /* symbol tables */ struct symtab symtab[SYM_NUM]; #define p_commons symtab[SYM_COMMONS] @@ -194,20 +240,13 @@ struct policydb { #define p_cats symtab[SYM_CATS] /* symbol names indexed by (value - 1) */ - char **sym_val_to_name[SYM_NUM]; -#define p_common_val_to_name sym_val_to_name[SYM_COMMONS] -#define p_class_val_to_name sym_val_to_name[SYM_CLASSES] -#define p_role_val_to_name sym_val_to_name[SYM_ROLES] -#define p_type_val_to_name sym_val_to_name[SYM_TYPES] -#define p_user_val_to_name sym_val_to_name[SYM_USERS] -#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS] -#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS] -#define p_cat_val_to_name sym_val_to_name[SYM_CATS] + struct flex_array *sym_val_to_name[SYM_NUM]; /* class, role, and user attributes indexed by (value - 1) */ struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct flex_array *type_val_to_struct_array; /* type enforcement access vectors and transitions */ struct avtab te_avtab; @@ -215,12 +254,18 @@ struct policydb { /* role transitions */ struct role_trans *role_tr; + /* file transitions with the last path component */ + /* quickly exclude lookups when parent ttype has no rules */ + struct ebitmap filename_trans_ttypes; + /* actual set of filename_trans rules */ + struct hashtab *filename_trans; + /* bools indexed by (value - 1) */ struct cond_bool_datum **bool_val_to_struct; /* type enforcement conditional access vectors and transitions */ struct avtab te_cond_avtab; /* linked list indexing te_cond_avtab by conditional */ - struct cond_node* cond_list; + struct cond_node *cond_list; /* role allows */ struct role_allow *role_allow; @@ -229,29 +274,50 @@ struct policydb { TCP or UDP port numbers, network interfaces and nodes */ struct ocontext *ocontexts[OCON_NUM]; - /* security contexts for files in filesystems that cannot support + /* security contexts for files in filesystems that cannot support a persistent label mapping or use another fixed labeling behavior. */ - struct genfs *genfs; + struct genfs *genfs; - /* range transitions */ - struct range_trans *range_tr; + /* range transitions table (range_trans_key -> mls_range) */ + struct hashtab *range_tr; /* type -> attribute reverse mapping */ - struct ebitmap *type_attr_map; + struct flex_array *type_attr_map_array; + + struct ebitmap policycaps; + + struct ebitmap permissive_map; + + /* length of this policy when it was loaded */ + size_t len; unsigned int policyvers; + + unsigned int reject_unknown : 1; + unsigned int allow_unknown : 1; + + u16 process_class; + u32 process_trans_perms; }; extern void policydb_destroy(struct policydb *p); extern int policydb_load_isids(struct policydb *p, struct sidtab *s); extern int policydb_context_isvalid(struct policydb *p, struct context *c); +extern int policydb_class_isvalid(struct policydb *p, unsigned int class); +extern int policydb_type_isvalid(struct policydb *p, unsigned int type); +extern int policydb_role_isvalid(struct policydb *p, unsigned int role); extern int policydb_read(struct policydb *p, void *fp); +extern int policydb_write(struct policydb *p, void *fp); #define PERM_SYMTAB_SIZE 32 #define POLICYDB_CONFIG_MLS 1 +/* the config flags related to unknown classes/perms are bits 2 and 3 */ +#define REJECT_UNKNOWN 0x00000002 +#define ALLOW_UNKNOWN 0x00000004 + #define OBJECT_R "object_r" #define OBJECT_R_VAL 1 @@ -263,6 +329,11 @@ struct policy_file { size_t len; }; +struct policy_data { + struct policydb *p; + void *fp; +}; + static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes) { if (bytes > fp->len) @@ -274,5 +345,26 @@ static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes) return 0; } +static inline int put_entry(const void *buf, size_t bytes, int num, struct policy_file *fp) +{ + size_t len = bytes * num; + + memcpy(fp->data, buf, len); + fp->data += len; + fp->len -= len; + + return 0; +} + +static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr) +{ + struct flex_array *fa = p->sym_val_to_name[sym_num]; + + return flex_array_get_ptr(fa, element_nr); +} + +extern u16 string_to_security_class(struct policydb *p, const char *name); +extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name); + #endif /* _SS_POLICYDB_H_ */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8a764928ff4..4bca49414a4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2,32 +2,58 @@ * Implementation of the security services. * * Authors : Stephen Smalley, <sds@epoch.ncsc.mil> - * James Morris <jmorris@redhat.com> + * James Morris <jmorris@redhat.com> * * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> * * Support for enhanced MLS infrastructure. + * Support for context based audit filters. * * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> * - * Added conditional policy language extensions + * Added conditional policy language extensions * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. - * Copyright (C) 2003 - 2004 Tresys Technology, LLC + * Updated: Hewlett-Packard <paul@paul-moore.com> + * + * Added support for NetLabel + * Added support for the policy capability bitmap + * + * Updated: Chad Sellers <csellers@tresys.com> + * + * Added validation of kernel classes and permissions + * + * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * Added support for bounds domain and audit messaged on masked permissions + * + * Updated: Guido Trentalancia <guido@trentalancia.com> + * + * Added support for runtime switching of the policy type + * + * Copyright (C) 2008, 2009 NEC Corporation + * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. + * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * 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 + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/spinlock.h> +#include <linux/rcupdate.h> #include <linux/errno.h> #include <linux/in.h> #include <linux/sched.h> #include <linux/audit.h> -#include <asm/semaphore.h> +#include <linux/mutex.h> +#include <linux/selinux.h> +#include <linux/flex_array.h> +#include <linux/vmalloc.h> +#include <net/netlabel.h> + #include "flask.h" #include "avc.h" #include "avc_ss.h" @@ -38,23 +64,21 @@ #include "services.h" #include "conditional.h" #include "mls.h" +#include "objsec.h" +#include "netlabel.h" +#include "xfrm.h" +#include "ebitmap.h" +#include "audit.h" -extern void selnl_notify_policyload(u32 seqno); -unsigned int policydb_loaded_version; +int selinux_policycap_netpeer; +int selinux_policycap_openperm; +int selinux_policycap_alwaysnetwork; static DEFINE_RWLOCK(policy_rwlock); -#define POLICY_RDLOCK read_lock(&policy_rwlock) -#define POLICY_WRLOCK write_lock_irq(&policy_rwlock) -#define POLICY_RDUNLOCK read_unlock(&policy_rwlock) -#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock) - -static DECLARE_MUTEX(load_sem); -#define LOAD_LOCK down(&load_sem) -#define LOAD_UNLOCK up(&load_sem) static struct sidtab sidtab; struct policydb policydb; -int ss_initialized = 0; +int ss_initialized; /* * The largest sequence number that has been used when @@ -62,12 +86,177 @@ int ss_initialized = 0; * The sequence number only changes when a policy change * occurs. */ -static u32 latest_granting = 0; +static u32 latest_granting; /* Forward declaration. */ static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static void context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd); + +struct selinux_mapping { + u16 value; /* policy value */ + unsigned num_perms; + u32 perms[sizeof(u32) * 8]; +}; + +static struct selinux_mapping *current_mapping; +static u16 current_mapping_size; + +static int selinux_set_mapping(struct policydb *pol, + struct security_class_mapping *map, + struct selinux_mapping **out_map_p, + u16 *out_map_size) +{ + struct selinux_mapping *out_map = NULL; + size_t size = sizeof(struct selinux_mapping); + u16 i, j; + unsigned k; + bool print_unknown_handle = false; + + /* Find number of classes in the input mapping */ + if (!map) + return -EINVAL; + i = 0; + while (map[i].name) + i++; + + /* Allocate space for the class records, plus one for class zero */ + out_map = kcalloc(++i, size, GFP_ATOMIC); + if (!out_map) + return -ENOMEM; + + /* Store the raw class and permission values */ + j = 0; + while (map[j].name) { + struct security_class_mapping *p_in = map + (j++); + struct selinux_mapping *p_out = out_map + j; + + /* An empty class string skips ahead */ + if (!strcmp(p_in->name, "")) { + p_out->num_perms = 0; + continue; + } + + p_out->value = string_to_security_class(pol, p_in->name); + if (!p_out->value) { + printk(KERN_INFO + "SELinux: Class %s not defined in policy.\n", + p_in->name); + if (pol->reject_unknown) + goto err; + p_out->num_perms = 0; + print_unknown_handle = true; + continue; + } + + k = 0; + while (p_in->perms && p_in->perms[k]) { + /* An empty permission string skips ahead */ + if (!*p_in->perms[k]) { + k++; + continue; + } + p_out->perms[k] = string_to_av_perm(pol, p_out->value, + p_in->perms[k]); + if (!p_out->perms[k]) { + printk(KERN_INFO + "SELinux: Permission %s in class %s not defined in policy.\n", + p_in->perms[k], p_in->name); + if (pol->reject_unknown) + goto err; + print_unknown_handle = true; + } + + k++; + } + p_out->num_perms = k; + } + + if (print_unknown_handle) + printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n", + pol->allow_unknown ? "allowed" : "denied"); + + *out_map_p = out_map; + *out_map_size = i; + return 0; +err: + kfree(out_map); + return -EINVAL; +} + +/* + * Get real, policy values from mapped values + */ + +static u16 unmap_class(u16 tclass) +{ + if (tclass < current_mapping_size) + return current_mapping[tclass].value; + + return tclass; +} + +/* + * Get kernel value for class from its policy value + */ +static u16 map_class(u16 pol_value) +{ + u16 i; + + for (i = 1; i < current_mapping_size; i++) { + if (current_mapping[i].value == pol_value) + return i; + } + + return SECCLASS_NULL; +} + +static void map_decision(u16 tclass, struct av_decision *avd, + int allow_unknown) +{ + if (tclass < current_mapping_size) { + unsigned i, n = current_mapping[tclass].num_perms; + u32 result; + + for (i = 0, result = 0; i < n; i++) { + if (avd->allowed & current_mapping[tclass].perms[i]) + result |= 1<<i; + if (allow_unknown && !current_mapping[tclass].perms[i]) + result |= 1<<i; + } + avd->allowed = result; + + for (i = 0, result = 0; i < n; i++) + if (avd->auditallow & current_mapping[tclass].perms[i]) + result |= 1<<i; + avd->auditallow = result; + + for (i = 0, result = 0; i < n; i++) { + if (avd->auditdeny & current_mapping[tclass].perms[i]) + result |= 1<<i; + if (!allow_unknown && !current_mapping[tclass].perms[i]) + result |= 1<<i; + } + /* + * In case the kernel has a bug and requests a permission + * between num_perms and the maximum permission number, we + * should audit that denial + */ + for (; i < (sizeof(u32)*8); i++) + result |= 1<<i; + avd->auditdeny = result; + } +} + +int security_mls_enabled(void) +{ + return policydb.mls_enabled; +} + /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -101,15 +290,15 @@ static int constraint_expr_eval(struct context *scontext, case CEXPR_AND: BUG_ON(sp < 1); sp--; - s[sp] &= s[sp+1]; + s[sp] &= s[sp + 1]; break; case CEXPR_OR: BUG_ON(sp < 1); sp--; - s[sp] |= s[sp+1]; + s[sp] |= s[sp + 1]; break; case CEXPR_ATTR: - if (sp == (CEXPR_MAXDEPTH-1)) + if (sp == (CEXPR_MAXDEPTH - 1)) return 0; switch (e->attr) { case CEXPR_USER: @@ -135,10 +324,10 @@ static int constraint_expr_eval(struct context *scontext, val1 - 1); continue; case CEXPR_INCOMP: - s[++sp] = ( !ebitmap_get_bit(&r1->dominates, - val2 - 1) && - !ebitmap_get_bit(&r2->dominates, - val1 - 1) ); + s[++sp] = (!ebitmap_get_bit(&r1->dominates, + val2 - 1) && + !ebitmap_get_bit(&r2->dominates, + val1 - 1)); continue; default: break; @@ -254,15 +443,184 @@ mls_ops: } /* - * Compute access vectors based on a context structure pair for - * the permissions in a particular class. + * security_dump_masked_av - dumps masked permissions during + * security_compute_av due to RBAC, MLS/Constraint and Type bounds. + */ +static int dump_masked_av_helper(void *k, void *d, void *args) +{ + struct perm_datum *pdatum = d; + char **permission_names = args; + + BUG_ON(pdatum->value < 1 || pdatum->value > 32); + + permission_names[pdatum->value - 1] = (char *)k; + + return 0; +} + +static void security_dump_masked_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 permissions, + const char *reason) +{ + struct common_datum *common_dat; + struct class_datum *tclass_dat; + struct audit_buffer *ab; + char *tclass_name; + char *scontext_name = NULL; + char *tcontext_name = NULL; + char *permission_names[32]; + int index; + u32 length; + bool need_comma = false; + + if (!permissions) + return; + + tclass_name = sym_name(&policydb, SYM_CLASSES, tclass - 1); + tclass_dat = policydb.class_val_to_struct[tclass - 1]; + common_dat = tclass_dat->comdatum; + + /* init permission_names */ + if (common_dat && + hashtab_map(common_dat->permissions.table, + dump_masked_av_helper, permission_names) < 0) + goto out; + + if (hashtab_map(tclass_dat->permissions.table, + dump_masked_av_helper, permission_names) < 0) + goto out; + + /* get scontext/tcontext in text form */ + if (context_struct_to_string(scontext, + &scontext_name, &length) < 0) + goto out; + + if (context_struct_to_string(tcontext, + &tcontext_name, &length) < 0) + goto out; + + /* audit a message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + goto out; + + audit_log_format(ab, "op=security_compute_av reason=%s " + "scontext=%s tcontext=%s tclass=%s perms=", + reason, scontext_name, tcontext_name, tclass_name); + + for (index = 0; index < 32; index++) { + u32 mask = (1 << index); + + if ((mask & permissions) == 0) + continue; + + audit_log_format(ab, "%s%s", + need_comma ? "," : "", + permission_names[index] + ? permission_names[index] : "????"); + need_comma = true; + } + audit_log_end(ab); +out: + /* release scontext/tcontext */ + kfree(tcontext_name); + kfree(scontext_name); + + return; +} + +/* + * security_boundary_permission - drops violated permissions + * on boundary constraint. */ -static int context_struct_compute_av(struct context *scontext, +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; + struct type_datum *target; + u32 masked = 0; + + source = flex_array_get_ptr(policydb.type_val_to_struct_array, + scontext->type - 1); + BUG_ON(!source); + + target = flex_array_get_ptr(policydb.type_val_to_struct_array, + tcontext->type - 1); + BUG_ON(!target); + + 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, + &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, + &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, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* audit masked permissions */ + security_dump_masked_av(scontext, tcontext, + tclass, masked, "bounds"); + } +} + +/* + * Compute access vectors based on a context structure pair for + * the permissions in a particular class. + */ +static void context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd) +{ struct constraint_node *constraint; struct role_allow *ra; struct avtab_key avkey; @@ -272,32 +630,17 @@ static int context_struct_compute_av(struct context *scontext, struct ebitmap_node *snode, *tnode; unsigned int i, j; - /* - * Remap extended Netlink classes for old policy versions. - * Do this here rather than socket_type_to_security_class() - * in case a newer policy version is loaded, allowing sockets - * to remain in the correct class. - */ - if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && - tclass <= SECCLASS_NETLINK_DNRT_SOCKET) - tclass = SECCLASS_NETLINK_SOCKET; - - if (!tclass || tclass > policydb.p_classes.nprim) { - printk(KERN_ERR "security_compute_av: unrecognized class %d\n", - tclass); - return -EINVAL; - } - tclass_datum = policydb.class_val_to_struct[tclass - 1]; - - /* - * Initialize the access vectors to the default values. - */ avd->allowed = 0; - avd->decided = 0xffffffff; avd->auditallow = 0; avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; + + if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (printk_ratelimit()) + printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); + return; + } + + tclass_datum = policydb.class_val_to_struct[tclass - 1]; /* * If a specific type enforcement rule was defined for @@ -305,18 +648,16 @@ static int context_struct_compute_av(struct context *scontext, */ avkey.target_class = tclass; avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; - ebitmap_for_each_bit(sattr, snode, i) { - if (!ebitmap_node_get_bit(snode, i)) - continue; - ebitmap_for_each_bit(tattr, tnode, j) { - if (!ebitmap_node_get_bit(tnode, j)) - continue; + sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); + BUG_ON(!sattr); + tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); + BUG_ON(!tattr); + ebitmap_for_each_positive_bit(sattr, snode, i) { + ebitmap_for_each_positive_bit(tattr, tnode, j) { 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; @@ -341,7 +682,7 @@ static int context_struct_compute_av(struct context *scontext, if ((constraint->permissions & (avd->allowed)) && !constraint_expr_eval(scontext, tcontext, NULL, constraint->expr)) { - avd->allowed = (avd->allowed) & ~(constraint->permissions); + avd->allowed &= ~(constraint->permissions); } constraint = constraint->next; } @@ -351,8 +692,8 @@ static int context_struct_compute_av(struct context *scontext, * role is changing, then check the (current_role, new_role) * pair. */ - if (tclass == SECCLASS_PROCESS && - (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) && + if (tclass == policydb.process_class && + (avd->allowed & policydb.process_trans_perms) && scontext->role != tcontext->role) { for (ra = policydb.role_allow; ra; ra = ra->next) { if (scontext->role == ra->role && @@ -360,31 +701,36 @@ static int context_struct_compute_av(struct context *scontext, break; } if (!ra) - avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION | - PROCESS__DYNTRANSITION); + avd->allowed &= ~policydb.process_trans_perms; } - return 0; + /* + * 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, avd); } static int security_validtrans_handle_fail(struct context *ocontext, - struct context *ncontext, - struct context *tcontext, - u16 tclass) + struct context *ncontext, + struct context *tcontext, + u16 tclass) { char *o = NULL, *n = NULL, *t = NULL; u32 olen, nlen, tlen; - if (context_struct_to_string(ocontext, &o, &olen) < 0) + if (context_struct_to_string(ocontext, &o, &olen)) goto out; - if (context_struct_to_string(ncontext, &n, &nlen) < 0) + if (context_struct_to_string(ncontext, &n, &nlen)) goto out; - if (context_struct_to_string(tcontext, &t, &tlen) < 0) + if (context_struct_to_string(tcontext, &t, &tlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, - "security_validate_transition: denied for" - " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", - o, n, t, policydb.p_class_val_to_name[tclass-1]); + "security_validate_transition: denied for" + " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", + o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); out: kfree(o); kfree(n); @@ -396,34 +742,26 @@ out: } int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, - u16 tclass) + u16 orig_tclass) { struct context *ocontext; struct context *ncontext; struct context *tcontext; struct class_datum *tclass_datum; struct constraint_node *constraint; + u16 tclass; int rc = 0; if (!ss_initialized) return 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); - /* - * Remap extended Netlink classes for old policy versions. - * Do this here rather than socket_type_to_security_class() - * in case a newer policy version is loaded, allowing sockets - * to remain in the correct class. - */ - if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && - tclass <= SECCLASS_NETLINK_DNRT_SOCKET) - tclass = SECCLASS_NETLINK_SOCKET; + tclass = unmap_class(orig_tclass); if (!tclass || tclass > policydb.p_classes.nprim) { - printk(KERN_ERR "security_validate_transition: " - "unrecognized class %d\n", tclass); + printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", + __func__, tclass); rc = -EINVAL; goto out; } @@ -431,24 +769,24 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, ocontext = sidtab_search(&sidtab, oldsid); if (!ocontext) { - printk(KERN_ERR "security_validate_transition: " - " unrecognized SID %d\n", oldsid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, oldsid); rc = -EINVAL; goto out; } ncontext = sidtab_search(&sidtab, newsid); if (!ncontext) { - printk(KERN_ERR "security_validate_transition: " - " unrecognized SID %d\n", newsid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, newsid); rc = -EINVAL; goto out; } tcontext = sidtab_search(&sidtab, tasksid); if (!tcontext) { - printk(KERN_ERR "security_validate_transition: " - " unrecognized SID %d\n", tasksid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tasksid); rc = -EINVAL; goto out; } @@ -456,72 +794,212 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, constraint = tclass_datum->validatetrans; while (constraint) { if (!constraint_expr_eval(ocontext, ncontext, tcontext, - constraint->expr)) { + constraint->expr)) { rc = security_validtrans_handle_fail(ocontext, ncontext, - tcontext, tclass); + tcontext, tclass); goto out; } constraint = constraint->next; } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); + 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; + + read_lock(&policy_rwlock); + + rc = -EINVAL; + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + rc = -EINVAL; + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + rc = 0; + /* type/domain unchanged */ + if (old_context->type == new_context->type) + goto out; + + index = new_context->type; + while (true) { + type = flex_array_get_ptr(policydb.type_val_to_struct_array, + index - 1); + BUG_ON(!type); + + /* not bounded anymore */ + rc = -EPERM; + if (!type->bounds) + break; + + /* @newsid is bounded by @oldsid */ + rc = 0; + if (type->bounds == old_context->type) + break; + + index = type->bounds; + } + + if (rc) { + char *old_name = NULL; + char *new_name = NULL; + u32 length; + + if (!context_struct_to_string(old_context, + &old_name, &length) && + !context_struct_to_string(new_context, + &new_name, &length)) { + audit_log(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR, + "op=security_bounded_transition " + "result=denied " + "oldcontext=%s newcontext=%s", + old_name, new_name); + } + kfree(new_name); + kfree(old_name); + } +out: + read_unlock(&policy_rwlock); + return rc; } +static void avd_init(struct av_decision *avd) +{ + avd->allowed = 0; + avd->auditallow = 0; + avd->auditdeny = 0xffffffff; + avd->seqno = latest_granting; + avd->flags = 0; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class - * @requested: requested permissions * @avd: access vector decisions * * Compute a set of access vector decisions based on the * SID pair (@ssid, @tsid) for the permissions in @tclass. - * Return -%EINVAL if any of the parameters are invalid or %0 - * if the access vector decisions were computed successfully. */ -int security_compute_av(u32 ssid, - u32 tsid, - u16 tclass, - u32 requested, - struct av_decision *avd) +void security_compute_av(u32 ssid, + u32 tsid, + u16 orig_tclass, + struct av_decision *avd) { + u16 tclass; struct context *scontext = NULL, *tcontext = NULL; - int rc = 0; - if (!ss_initialized) { - avd->allowed = 0xffffffff; - avd->decided = 0xffffffff; - avd->auditallow = 0; - avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; - return 0; + read_lock(&policy_rwlock); + avd_init(avd); + if (!ss_initialized) + goto allow; + + scontext = sidtab_search(&sidtab, ssid); + if (!scontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); + goto out; + } + + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + + tcontext = sidtab_search(&sidtab, tsid); + if (!tcontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); + goto out; } - POLICY_RDLOCK; + tclass = unmap_class(orig_tclass); + if (unlikely(orig_tclass && !tclass)) { + if (policydb.allow_unknown) + goto allow; + goto out; + } + context_struct_compute_av(scontext, tcontext, tclass, avd); + map_decision(orig_tclass, avd, policydb.allow_unknown); +out: + read_unlock(&policy_rwlock); + return; +allow: + avd->allowed = 0xffffffff; + goto out; +} + +void security_compute_av_user(u32 ssid, + u32 tsid, + u16 tclass, + struct av_decision *avd) +{ + struct context *scontext = NULL, *tcontext = NULL; + + read_lock(&policy_rwlock); + avd_init(avd); + if (!ss_initialized) + goto allow; scontext = sidtab_search(&sidtab, ssid); if (!scontext) { - printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", - ssid); - rc = -EINVAL; + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); goto out; } + + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + tcontext = sidtab_search(&sidtab, tsid); if (!tcontext) { - printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", - tsid); - rc = -EINVAL; + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); goto out; } - rc = context_struct_compute_av(scontext, tcontext, tclass, - requested, avd); -out: - POLICY_RDUNLOCK; - return rc; + if (unlikely(!tclass)) { + if (policydb.allow_unknown) + goto allow; + goto out; + } + + context_struct_compute_av(scontext, tcontext, tclass, avd); + out: + read_unlock(&policy_rwlock); + return; +allow: + avd->allowed = 0xffffffff; + goto out; } /* @@ -535,32 +1013,45 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 { char *scontextp; - *scontext = NULL; + if (scontext) + *scontext = NULL; *scontext_len = 0; + if (context->len) { + *scontext_len = context->len; + if (scontext) { + *scontext = kstrdup(context->str, GFP_ATOMIC); + if (!(*scontext)) + return -ENOMEM; + } + return 0; + } + /* Compute the size of the context. */ - *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1; - *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1; - *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1; + *scontext_len += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + 1; + *scontext_len += strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + 1; + *scontext_len += strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)) + 1; *scontext_len += mls_compute_context_len(context); + if (!scontext) + return 0; + /* Allocate space for the context; caller must free this space. */ scontextp = kmalloc(*scontext_len, GFP_ATOMIC); - if (!scontextp) { + if (!scontextp) return -ENOMEM; - } *scontext = scontextp; /* * Copy the user name, role name and type name into the context. */ sprintf(scontextp, "%s:%s:%s", - policydb.p_user_val_to_name[context->user - 1], - policydb.p_role_val_to_name[context->role - 1], - policydb.p_type_val_to_name[context->type - 1]); - scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) + - 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) + - 1 + strlen(policydb.p_type_val_to_name[context->type - 1]); + sym_name(&policydb, SYM_USERS, context->user - 1), + sym_name(&policydb, SYM_ROLES, context->role - 1), + sym_name(&policydb, SYM_TYPES, context->type - 1)); + scontextp += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + + 1 + strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + + 1 + strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)); mls_sid_to_context(context, &scontextp); @@ -571,98 +1062,105 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 #include "initial_sid_to_string.h" -/** - * security_sid_to_context - Obtain a context for a given SID. - * @sid: security identifier, SID - * @scontext: security context - * @scontext_len: length in bytes - * - * Write the string representation of the context associated with @sid - * into a dynamically allocated string of the correct size. Set @scontext - * to point to this string and set @scontext_len to the length of the string. - */ -int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) +const char *security_get_initial_sid_context(u32 sid) +{ + if (unlikely(sid > SECINITSID_NUM)) + return NULL; + return initial_sid_to_string[sid]; +} + +static int security_sid_to_context_core(u32 sid, char **scontext, + u32 *scontext_len, int force) { struct context *context; int rc = 0; + if (scontext) + *scontext = NULL; + *scontext_len = 0; + if (!ss_initialized) { if (sid <= SECINITSID_NUM) { char *scontextp; *scontext_len = strlen(initial_sid_to_string[sid]) + 1; - scontextp = kmalloc(*scontext_len,GFP_ATOMIC); + if (!scontext) + goto out; + scontextp = kmalloc(*scontext_len, GFP_ATOMIC); + if (!scontextp) { + rc = -ENOMEM; + goto out; + } strcpy(scontextp, initial_sid_to_string[sid]); *scontext = scontextp; goto out; } - printk(KERN_ERR "security_sid_to_context: called before initial " - "load_policy on unknown SID %d\n", sid); + printk(KERN_ERR "SELinux: %s: called before initial " + "load_policy on unknown SID %d\n", __func__, sid); rc = -EINVAL; goto out; } - POLICY_RDLOCK; - context = sidtab_search(&sidtab, sid); + read_lock(&policy_rwlock); + if (force) + context = sidtab_search_force(&sidtab, sid); + else + context = sidtab_search(&sidtab, sid); if (!context) { - printk(KERN_ERR "security_sid_to_context: unrecognized SID " - "%d\n", sid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, sid); rc = -EINVAL; goto out_unlock; } rc = context_struct_to_string(context, scontext, scontext_len); out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); out: return rc; } -static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid) +/** + * security_sid_to_context - Obtain a context for a given SID. + * @sid: security identifier, SID + * @scontext: security context + * @scontext_len: length in bytes + * + * Write the string representation of the context associated with @sid + * into a dynamically allocated string of the correct size. Set @scontext + * to point to this string and set @scontext_len to the length of the string. + */ +int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(sid, scontext, scontext_len, 0); +} + +int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(sid, scontext, scontext_len, 1); +} + +/* + * Caveat: Mutates scontext. + */ +static int string_to_context_struct(struct policydb *pol, + struct sidtab *sidtabp, + char *scontext, + u32 scontext_len, + struct context *ctx, + u32 def_sid) { - char *scontext2; - struct context context; struct role_datum *role; struct type_datum *typdatum; struct user_datum *usrdatum; char *scontextp, *p, oldc; int rc = 0; - if (!ss_initialized) { - int i; - - for (i = 1; i < SECINITSID_NUM; i++) { - if (!strcmp(initial_sid_to_string[i], scontext)) { - *sid = i; - goto out; - } - } - *sid = SECINITSID_KERNEL; - goto out; - } - *sid = SECSID_NULL; - - /* Copy the string so that we can modify the copy as we parse it. - The string should already by null terminated, but we append a - null suffix to the copy to avoid problems with the existing - attr package, which doesn't view the null terminator as part - of the attribute value. */ - scontext2 = kmalloc(scontext_len+1,GFP_KERNEL); - if (!scontext2) { - rc = -ENOMEM; - goto out; - } - memcpy(scontext2, scontext, scontext_len); - scontext2[scontext_len] = 0; - - context_init(&context); - *sid = SECSID_NULL; - - POLICY_RDLOCK; + context_init(ctx); /* Parse the security context. */ rc = -EINVAL; - scontextp = (char *) scontext2; + scontextp = (char *) scontext; /* Extract the user. */ p = scontextp; @@ -670,15 +1168,15 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s p++; if (*p == 0) - goto out_unlock; + goto out; *p++ = 0; - usrdatum = hashtab_search(policydb.p_users.table, scontextp); + usrdatum = hashtab_search(pol->p_users.table, scontextp); if (!usrdatum) - goto out_unlock; + goto out; - context.user = usrdatum->value; + ctx->user = usrdatum->value; /* Extract role. */ scontextp = p; @@ -686,14 +1184,14 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s p++; if (*p == 0) - goto out_unlock; + goto out; *p++ = 0; - role = hashtab_search(policydb.p_roles.table, scontextp); + role = hashtab_search(pol->p_roles.table, scontextp); if (!role) - goto out_unlock; - context.role = role->value; + goto out; + ctx->role = role->value; /* Extract type. */ scontextp = p; @@ -702,33 +1200,87 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s oldc = *p; *p++ = 0; - typdatum = hashtab_search(policydb.p_types.table, scontextp); - if (!typdatum) - goto out_unlock; + typdatum = hashtab_search(pol->p_types.table, scontextp); + if (!typdatum || typdatum->attribute) + goto out; - context.type = typdatum->value; + ctx->type = typdatum->value; - rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid); + rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid); if (rc) - goto out_unlock; + goto out; - if ((p - scontext2) < scontext_len) { - rc = -EINVAL; - goto out_unlock; - } + rc = -EINVAL; + if ((p - scontext) < scontext_len) + goto out; /* Check the validity of the new context. */ - if (!policydb_context_isvalid(&policydb, &context)) { - rc = -EINVAL; - goto out_unlock; + if (!policydb_context_isvalid(pol, ctx)) + goto out; + rc = 0; +out: + if (rc) + context_destroy(ctx); + return rc; +} + +static int security_context_to_sid_core(const char *scontext, u32 scontext_len, + u32 *sid, u32 def_sid, gfp_t gfp_flags, + int force) +{ + char *scontext2, *str = NULL; + struct context context; + int rc = 0; + + /* An empty security context is never valid. */ + if (!scontext_len) + return -EINVAL; + + if (!ss_initialized) { + int i; + + for (i = 1; i < SECINITSID_NUM; i++) { + if (!strcmp(initial_sid_to_string[i], scontext)) { + *sid = i; + return 0; + } + } + *sid = SECINITSID_KERNEL; + return 0; + } + *sid = SECSID_NULL; + + /* Copy the string so that we can modify the copy as we parse it. */ + scontext2 = kmalloc(scontext_len + 1, gfp_flags); + if (!scontext2) + return -ENOMEM; + memcpy(scontext2, scontext, scontext_len); + scontext2[scontext_len] = 0; + + if (force) { + /* Save another copy for storing in uninterpreted form */ + rc = -ENOMEM; + str = kstrdup(scontext2, gfp_flags); + if (!str) + goto out; } - /* Obtain the new sid. */ + + read_lock(&policy_rwlock); + rc = string_to_context_struct(&policydb, &sidtab, scontext2, + scontext_len, &context, def_sid); + if (rc == -EINVAL && force) { + context.str = str; + context.len = scontext_len; + str = NULL; + } else if (rc) + goto out_unlock; rc = sidtab_context_to_sid(&sidtab, &context, sid); -out_unlock: - POLICY_RDUNLOCK; context_destroy(&context); - kfree(scontext2); +out_unlock: + read_unlock(&policy_rwlock); out: + kfree(scontext2); + kfree(str); return rc; } @@ -737,16 +1289,18 @@ out: * @scontext: security context * @scontext_len: length in bytes * @sid: security identifier, SID + * @gfp: context for the allocation * * Obtains a SID associated with the security context that * has the string representation specified by @scontext. * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient * memory is available, or 0 on success. */ -int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid) +int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, + gfp_t gfp) { return security_context_to_sid_core(scontext, scontext_len, - sid, SECSID_NULL); + sid, SECSID_NULL, gfp, 0); } /** @@ -756,20 +1310,29 @@ int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid) * @scontext: security context * @scontext_len: length in bytes * @sid: security identifier, SID - * @def_sid: default SID to assign on errror + * @def_sid: default SID to assign on error * * Obtains a SID associated with the security context that * has the string representation specified by @scontext. * The default SID is passed to the MLS layer to be used to allow * kernel labeling of the MLS field if the MLS field is not present * (for upgrading to MLS without full relabel). + * Implicitly forces adding of the context even if it cannot be mapped yet. * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient * memory is available, or 0 on success. */ -int security_context_to_sid_default(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid) +int security_context_to_sid_default(const char *scontext, u32 scontext_len, + u32 *sid, u32 def_sid, gfp_t gfp_flags) +{ + return security_context_to_sid_core(scontext, scontext_len, + sid, def_sid, gfp_flags, 1); +} + +int security_context_to_sid_force(const char *scontext, u32 scontext_len, + u32 *sid) { return security_context_to_sid_core(scontext, scontext_len, - sid, def_sid); + sid, SECSID_NULL, GFP_KERNEL, 1); } static int compute_sid_handle_invalid_context( @@ -781,18 +1344,18 @@ static int compute_sid_handle_invalid_context( char *s = NULL, *t = NULL, *n = NULL; u32 slen, tlen, nlen; - if (context_struct_to_string(scontext, &s, &slen) < 0) + if (context_struct_to_string(scontext, &s, &slen)) goto out; - if (context_struct_to_string(tcontext, &t, &tlen) < 0) + if (context_struct_to_string(tcontext, &t, &tlen)) goto out; - if (context_struct_to_string(newcontext, &n, &nlen) < 0) + if (context_struct_to_string(newcontext, &n, &nlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, "security_compute_sid: invalid context %s" " for scontext=%s" " tcontext=%s" " tclass=%s", - n, s, t, policydb.p_class_val_to_name[tclass-1]); + n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); out: kfree(s); kfree(t); @@ -802,22 +1365,52 @@ out: return -EACCES; } +static void filename_compute_type(struct policydb *p, struct context *newcontext, + u32 stype, u32 ttype, u16 tclass, + const char *objname) +{ + struct filename_trans ft; + struct filename_trans_datum *otype; + + /* + * Most filename trans rules are going to live in specific directories + * like /dev or /var/run. This bitmap will quickly skip rule searches + * if the ttype does not contain any rules. + */ + if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype)) + return; + + ft.stype = stype; + ft.ttype = ttype; + ft.tclass = tclass; + ft.name = objname; + + otype = hashtab_search(p->filename_trans, &ft); + if (otype) + newcontext->type = otype->otype; +} + static int security_compute_sid(u32 ssid, u32 tsid, - u16 tclass, + u16 orig_tclass, u32 specified, - u32 *out_sid) + const char *objname, + u32 *out_sid, + bool kern) { + struct class_datum *cladatum = NULL; struct context *scontext = NULL, *tcontext = NULL, newcontext; struct role_trans *roletr = NULL; struct avtab_key avkey; struct avtab_datum *avdatum; struct avtab_node *node; + u16 tclass; int rc = 0; + bool sock; if (!ss_initialized) { - switch (tclass) { - case SECCLASS_PROCESS: + switch (orig_tclass) { + case SECCLASS_PROCESS: /* kernel value */ *out_sid = ssid; break; default: @@ -827,31 +1420,47 @@ static int security_compute_sid(u32 ssid, goto out; } - POLICY_RDLOCK; + context_init(&newcontext); + + read_lock(&policy_rwlock); + + if (kern) { + tclass = unmap_class(orig_tclass); + sock = security_is_socket_class(orig_tclass); + } else { + tclass = orig_tclass; + sock = security_is_socket_class(map_class(tclass)); + } scontext = sidtab_search(&sidtab, ssid); if (!scontext) { - printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", - ssid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); rc = -EINVAL; goto out_unlock; } tcontext = sidtab_search(&sidtab, tsid); if (!tcontext) { - printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", - tsid); + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); rc = -EINVAL; goto out_unlock; } - context_init(&newcontext); + if (tclass && tclass <= policydb.p_classes.nprim) + cladatum = policydb.class_val_to_struct[tclass - 1]; /* Set the user identity. */ switch (specified) { case AVTAB_TRANSITION: case AVTAB_CHANGE: - /* Use the process user identity. */ - newcontext.user = scontext->user; + if (cladatum && cladatum->default_user == DEFAULT_TARGET) { + newcontext.user = tcontext->user; + } else { + /* notice this gets both DEFAULT_SOURCE and unset */ + /* Use the process user identity. */ + newcontext.user = scontext->user; + } break; case AVTAB_MEMBER: /* Use the related object owner. */ @@ -859,18 +1468,31 @@ static int security_compute_sid(u32 ssid, break; } - /* Set the role and type to default values. */ - switch (tclass) { - case SECCLASS_PROCESS: - /* Use the current role and type of process. */ + /* Set the role to default values. */ + if (cladatum && cladatum->default_role == DEFAULT_SOURCE) { newcontext.role = scontext->role; + } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { + newcontext.role = tcontext->role; + } else { + if ((tclass == policydb.process_class) || (sock == true)) + newcontext.role = scontext->role; + else + newcontext.role = OBJECT_R_VAL; + } + + /* Set the type to default values. */ + if (cladatum && cladatum->default_type == DEFAULT_SOURCE) { newcontext.type = scontext->type; - break; - default: - /* Use the well-defined object role. */ - newcontext.role = OBJECT_R_VAL; - /* Use the type of the related object. */ + } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { newcontext.type = tcontext->type; + } else { + if ((tclass == policydb.process_class) || (sock == true)) { + /* Use the type of process. */ + newcontext.type = scontext->type; + } else { + /* Use the type of the related object. */ + newcontext.type = tcontext->type; + } } /* Look for a type transition/member/change rule. */ @@ -881,9 +1503,9 @@ static int security_compute_sid(u32 ssid, avdatum = avtab_search(&policydb.te_avtab, &avkey); /* If no permanent rule, also check for enabled conditional rules */ - if(!avdatum) { + 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; @@ -896,29 +1518,29 @@ static int security_compute_sid(u32 ssid, newcontext.type = avdatum->data; } + /* if we have a objname this is a file trans check so check those rules */ + if (objname) + filename_compute_type(&policydb, &newcontext, scontext->type, + tcontext->type, tclass, objname); + /* Check for class-specific changes. */ - switch (tclass) { - case SECCLASS_PROCESS: - if (specified & AVTAB_TRANSITION) { - /* Look for a role transition rule. */ - for (roletr = policydb.role_tr; roletr; - roletr = roletr->next) { - if (roletr->role == scontext->role && - roletr->type == tcontext->type) { - /* Use the role transition rule. */ - newcontext.role = roletr->new_role; - break; - } + if (specified & AVTAB_TRANSITION) { + /* Look for a role transition rule. */ + for (roletr = policydb.role_tr; roletr; roletr = roletr->next) { + if ((roletr->role == scontext->role) && + (roletr->type == tcontext->type) && + (roletr->tclass == tclass)) { + /* Use the role transition rule. */ + newcontext.role = roletr->new_role; + break; } } - break; - default: - break; } /* Set the MLS attributes. This is done last because it may allocate memory. */ - rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext); + rc = mls_compute_sid(scontext, tcontext, tclass, specified, + &newcontext, sock); if (rc) goto out_unlock; @@ -934,7 +1556,7 @@ static int security_compute_sid(u32 ssid, /* Obtain the sid for the context. */ rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid); out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); context_destroy(&newcontext); out: return rc; @@ -953,12 +1575,18 @@ out: * if insufficient memory is available, or %0 if the new SID was * computed successfully. */ -int security_transition_sid(u32 ssid, - u32 tsid, - u16 tclass, - u32 *out_sid) +int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, + const struct qstr *qstr, u32 *out_sid) +{ + return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + qstr ? qstr->name : NULL, out_sid, true); +} + +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid); + return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + objname, out_sid, false); } /** @@ -979,7 +1607,8 @@ int security_member_sid(u32 ssid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid); + return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, NULL, + out_sid, false); } /** @@ -1000,90 +1629,8 @@ int security_change_sid(u32 ssid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); -} - -/* - * Verify that each permission that is defined under the - * existing policy is still defined with the same value - * in the new policy. - */ -static int validate_perm(void *key, void *datum, void *p) -{ - struct hashtab *h; - struct perm_datum *perdatum, *perdatum2; - int rc = 0; - - - h = p; - perdatum = datum; - - perdatum2 = hashtab_search(h, key); - if (!perdatum2) { - printk(KERN_ERR "security: permission %s disappeared", - (char *)key); - rc = -ENOENT; - goto out; - } - if (perdatum->value != perdatum2->value) { - printk(KERN_ERR "security: the value of permission %s changed", - (char *)key); - rc = -EINVAL; - } -out: - return rc; -} - -/* - * Verify that each class that is defined under the - * existing policy is still defined with the same - * attributes in the new policy. - */ -static int validate_class(void *key, void *datum, void *p) -{ - struct policydb *newp; - struct class_datum *cladatum, *cladatum2; - int rc; - - newp = p; - cladatum = datum; - - cladatum2 = hashtab_search(newp->p_classes.table, key); - if (!cladatum2) { - printk(KERN_ERR "security: class %s disappeared\n", - (char *)key); - rc = -ENOENT; - goto out; - } - if (cladatum->value != cladatum2->value) { - printk(KERN_ERR "security: the value of class %s changed\n", - (char *)key); - rc = -EINVAL; - goto out; - } - if ((cladatum->comdatum && !cladatum2->comdatum) || - (!cladatum->comdatum && cladatum2->comdatum)) { - printk(KERN_ERR "security: the inherits clause for the access " - "vector definition for class %s changed\n", (char *)key); - rc = -EINVAL; - goto out; - } - if (cladatum->comdatum) { - rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, - cladatum2->comdatum->permissions.table); - if (rc) { - printk(" in the access vector definition for class " - "%s\n", (char *)key); - goto out; - } - } - rc = hashtab_map(cladatum->permissions.table, validate_perm, - cladatum2->permissions.table); - if (rc) - printk(" in access vector definition for class %s\n", - (char *)key); -out: - return rc; + return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, NULL, + out_sid, false); } /* Clone the SID into the new SID table. */ @@ -1093,24 +1640,25 @@ static int clone_sid(u32 sid, { struct sidtab *s = arg; - return sidtab_insert(s, sid, context); + if (sid > SECINITSID_NUM) + return sidtab_insert(s, sid, context); + else + return 0; } static inline int convert_context_handle_invalid_context(struct context *context) { - int rc = 0; + char *s; + u32 len; - if (selinux_enforcing) { - rc = -EINVAL; - } else { - char *s; - u32 len; + if (selinux_enforcing) + return -EINVAL; - context_struct_to_string(context, &s, &len); - printk(KERN_ERR "security: context %s is invalid\n", s); + if (!context_struct_to_string(context, &s, &len)) { + printk(KERN_WARNING "SELinux: Context %s would be invalid if enforcing\n", s); kfree(s); } - return rc; + return 0; } struct convert_context_args { @@ -1131,48 +1679,112 @@ static int convert_context(u32 key, { struct convert_context_args *args; struct context oldc; + struct ocontext *oc; + struct mls_range *range; struct role_datum *role; struct type_datum *typdatum; struct user_datum *usrdatum; char *s; u32 len; - int rc; + int rc = 0; + + if (key <= SECINITSID_NUM) + goto out; args = p; + if (c->str) { + struct context ctx; + + rc = -ENOMEM; + s = kstrdup(c->str, GFP_KERNEL); + if (!s) + goto out; + + rc = string_to_context_struct(args->newp, NULL, s, + c->len, &ctx, SECSID_NULL); + kfree(s); + if (!rc) { + printk(KERN_INFO "SELinux: Context %s became valid (mapped).\n", + c->str); + /* Replace string with mapped representation. */ + kfree(c->str); + memcpy(c, &ctx, sizeof(*c)); + goto out; + } else if (rc == -EINVAL) { + /* Retain string representation for later mapping. */ + rc = 0; + goto out; + } else { + /* Other error condition, e.g. ENOMEM. */ + printk(KERN_ERR "SELinux: Unable to map context %s, rc = %d.\n", + c->str, -rc); + goto out; + } + } + rc = context_cpy(&oldc, c); if (rc) goto out; - rc = -EINVAL; - /* Convert the user. */ + rc = -EINVAL; usrdatum = hashtab_search(args->newp->p_users.table, - args->oldp->p_user_val_to_name[c->user - 1]); - if (!usrdatum) { + sym_name(args->oldp, SYM_USERS, c->user - 1)); + if (!usrdatum) goto bad; - } c->user = usrdatum->value; /* Convert the role. */ + rc = -EINVAL; role = hashtab_search(args->newp->p_roles.table, - args->oldp->p_role_val_to_name[c->role - 1]); - if (!role) { + sym_name(args->oldp, SYM_ROLES, c->role - 1)); + if (!role) goto bad; - } c->role = role->value; /* Convert the type. */ + rc = -EINVAL; typdatum = hashtab_search(args->newp->p_types.table, - args->oldp->p_type_val_to_name[c->type - 1]); - if (!typdatum) { + sym_name(args->oldp, SYM_TYPES, c->type - 1)); + if (!typdatum) goto bad; - } c->type = typdatum->value; - rc = mls_convert_context(args->oldp, args->newp, c); - if (rc) - goto bad; + /* Convert the MLS fields if dealing with MLS policies */ + if (args->oldp->mls_enabled && args->newp->mls_enabled) { + rc = mls_convert_context(args->oldp, args->newp, c); + if (rc) + goto bad; + } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) { + /* + * Switching between MLS and non-MLS policy: + * free any storage used by the MLS fields in the + * context for all existing entries in the sidtab. + */ + mls_context_destroy(c); + } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { + /* + * Switching between non-MLS and MLS policy: + * ensure that the MLS fields of the context for all + * existing entries in the sidtab are filled in with a + * suitable default value, likely taken from one of the + * initial SIDs. + */ + oc = args->newp->ocontexts[OCON_ISID]; + while (oc && oc->sid[0] != SECINITSID_UNLABELED) + oc = oc->next; + rc = -EINVAL; + if (!oc) { + printk(KERN_ERR "SELinux: unable to look up" + " the initial SIDs list\n"); + goto bad; + } + range = &oc->context[0].range; + rc = mls_range_set(c, range); + if (rc) + goto bad; + } /* Check the validity of the new context. */ if (!policydb_context_isvalid(args->newp, c)) { @@ -1182,17 +1794,36 @@ static int convert_context(u32 key, } context_destroy(&oldc); + + rc = 0; out: return rc; bad: - context_struct_to_string(&oldc, &s, &len); + /* Map old representation to string and save it. */ + rc = context_struct_to_string(&oldc, &s, &len); + if (rc) + return rc; context_destroy(&oldc); - printk(KERN_ERR "security: invalidating context %s\n", s); - kfree(s); + context_destroy(c); + c->str = s; + c->len = len; + printk(KERN_INFO "SELinux: Context %s became invalid (unmapped).\n", + c->str); + rc = 0; goto out; } -extern void selinux_complete_init(void); +static void security_load_policycaps(void) +{ + selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_NETPEER); + selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_OPENPERM); + selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_ALWAYSNETWORK); +} + +static int security_preserve_bools(struct policydb *p); /** * security_load_policy - Load a security policy configuration. @@ -1206,118 +1837,174 @@ extern void selinux_complete_init(void); */ int security_load_policy(void *data, size_t len) { - struct policydb oldpolicydb, newpolicydb; + struct policydb *oldpolicydb, *newpolicydb; struct sidtab oldsidtab, newsidtab; + struct selinux_mapping *oldmap, *map = NULL; struct convert_context_args args; u32 seqno; + u16 map_size; int rc = 0; struct policy_file file = { data, len }, *fp = &file; - LOAD_LOCK; + oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); + if (!oldpolicydb) { + rc = -ENOMEM; + goto out; + } + newpolicydb = oldpolicydb + 1; if (!ss_initialized) { avtab_cache_init(); - if (policydb_read(&policydb, fp)) { - LOAD_UNLOCK; + rc = policydb_read(&policydb, fp); + if (rc) { avtab_cache_destroy(); - return -EINVAL; + goto out; } - if (policydb_load_isids(&policydb, &sidtab)) { - LOAD_UNLOCK; + + policydb.len = len; + rc = selinux_set_mapping(&policydb, secclass_map, + ¤t_mapping, + ¤t_mapping_size); + if (rc) { policydb_destroy(&policydb); avtab_cache_destroy(); - return -EINVAL; + goto out; + } + + rc = policydb_load_isids(&policydb, &sidtab); + if (rc) { + policydb_destroy(&policydb); + avtab_cache_destroy(); + goto out; } - policydb_loaded_version = policydb.policyvers; + + security_load_policycaps(); ss_initialized = 1; seqno = ++latest_granting; - LOAD_UNLOCK; selinux_complete_init(); avc_ss_reset(seqno); selnl_notify_policyload(seqno); - return 0; + selinux_status_update_policyload(seqno); + selinux_netlbl_cache_invalidate(); + selinux_xfrm_notify_policyload(); + goto out; } #if 0 sidtab_hash_eval(&sidtab, "sids"); #endif - if (policydb_read(&newpolicydb, fp)) { - LOAD_UNLOCK; - return -EINVAL; + rc = policydb_read(newpolicydb, fp); + if (rc) + goto out; + + newpolicydb->len = len; + /* If switching between different policy types, log MLS status */ + if (policydb.mls_enabled && !newpolicydb->mls_enabled) + printk(KERN_INFO "SELinux: Disabling MLS support...\n"); + else if (!policydb.mls_enabled && newpolicydb->mls_enabled) + printk(KERN_INFO "SELinux: Enabling MLS support...\n"); + + rc = policydb_load_isids(newpolicydb, &newsidtab); + if (rc) { + printk(KERN_ERR "SELinux: unable to load the initial SIDs\n"); + policydb_destroy(newpolicydb); + goto out; } - sidtab_init(&newsidtab); + rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size); + if (rc) + goto err; - /* Verify that the existing classes did not change. */ - if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { - printk(KERN_ERR "security: the definition of an existing " - "class changed\n"); - rc = -EINVAL; + rc = security_preserve_bools(newpolicydb); + if (rc) { + printk(KERN_ERR "SELinux: unable to preserve booleans\n"); goto err; } /* Clone the SID table. */ sidtab_shutdown(&sidtab); - if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { - rc = -ENOMEM; + + rc = sidtab_map(&sidtab, clone_sid, &newsidtab); + if (rc) goto err; - } - /* Convert the internal representations of contexts - in the new SID table and remove invalid SIDs. */ + /* + * Convert the internal representations of contexts + * in the new SID table. + */ args.oldp = &policydb; - args.newp = &newpolicydb; - sidtab_map_remove_on_error(&newsidtab, convert_context, &args); + args.newp = newpolicydb; + rc = sidtab_map(&newsidtab, convert_context, &args); + if (rc) { + printk(KERN_ERR "SELinux: unable to convert the internal" + " representation of contexts in the new SID" + " table\n"); + goto err; + } /* Save the old policydb and SID table to free later. */ - memcpy(&oldpolicydb, &policydb, sizeof policydb); + memcpy(oldpolicydb, &policydb, sizeof(policydb)); sidtab_set(&oldsidtab, &sidtab); /* Install the new policydb and SID table. */ - POLICY_WRLOCK; - memcpy(&policydb, &newpolicydb, sizeof policydb); + write_lock_irq(&policy_rwlock); + memcpy(&policydb, newpolicydb, sizeof(policydb)); sidtab_set(&sidtab, &newsidtab); + security_load_policycaps(); + oldmap = current_mapping; + current_mapping = map; + current_mapping_size = map_size; seqno = ++latest_granting; - policydb_loaded_version = policydb.policyvers; - POLICY_WRUNLOCK; - LOAD_UNLOCK; + write_unlock_irq(&policy_rwlock); /* Free the old policydb and SID table. */ - policydb_destroy(&oldpolicydb); + policydb_destroy(oldpolicydb); sidtab_destroy(&oldsidtab); + kfree(oldmap); avc_ss_reset(seqno); selnl_notify_policyload(seqno); + selinux_status_update_policyload(seqno); + selinux_netlbl_cache_invalidate(); + selinux_xfrm_notify_policyload(); - return 0; + rc = 0; + goto out; err: - LOAD_UNLOCK; + kfree(map); sidtab_destroy(&newsidtab); - policydb_destroy(&newpolicydb); + policydb_destroy(newpolicydb); + +out: + kfree(oldpolicydb); return rc; +} + +size_t security_policydb_len(void) +{ + size_t len; + + read_lock(&policy_rwlock); + len = policydb.len; + read_unlock(&policy_rwlock); + return len; } /** * security_port_sid - Obtain the SID for a port. - * @domain: communication domain aka address family - * @type: socket type * @protocol: protocol number * @port: port number * @out_sid: security identifier */ -int security_port_sid(u16 domain, - u16 type, - u8 protocol, - u16 port, - u32 *out_sid) +int security_port_sid(u8 protocol, u16 port, u32 *out_sid) { struct ocontext *c; int rc = 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_PORT]; while (c) { @@ -1342,7 +2029,7 @@ int security_port_sid(u16 domain, } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1350,16 +2037,13 @@ out: * security_netif_sid - Obtain the SID for a network interface. * @name: interface name * @if_sid: interface SID - * @msg_sid: default SID for received packets */ -int security_netif_sid(char *name, - u32 *if_sid, - u32 *msg_sid) +int security_netif_sid(char *name, u32 *if_sid) { int rc = 0; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_NETIF]; while (c) { @@ -1382,14 +2066,11 @@ int security_netif_sid(char *name, goto out; } *if_sid = c->sid[0]; - *msg_sid = c->sid[1]; - } else { + } else *if_sid = SECINITSID_NETIF; - *msg_sid = SECINITSID_NETMSG; - } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1397,8 +2078,8 @@ static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask) { int i, fail = 0; - for(i = 0; i < 4; i++) - if(addr[i] != (input[i] & mask[i])) { + for (i = 0; i < 4; i++) + if (addr[i] != (input[i] & mask[i])) { fail = 1; break; } @@ -1418,19 +2099,18 @@ int security_node_sid(u16 domain, u32 addrlen, u32 *out_sid) { - int rc = 0; + int rc; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); switch (domain) { case AF_INET: { u32 addr; - if (addrlen != sizeof(u32)) { - rc = -EINVAL; + rc = -EINVAL; + if (addrlen != sizeof(u32)) goto out; - } addr = *((u32 *)addrp); @@ -1444,10 +2124,9 @@ int security_node_sid(u16 domain, } case AF_INET6: - if (addrlen != sizeof(u64) * 2) { - rc = -EINVAL; + rc = -EINVAL; + if (addrlen != sizeof(u64) * 2) goto out; - } c = policydb.ocontexts[OCON_NODE6]; while (c) { if (match_ipv6_addrmask(addrp, c->u.node6.addr, @@ -1458,6 +2137,7 @@ int security_node_sid(u16 domain, break; default: + rc = 0; *out_sid = SECINITSID_NODE; goto out; } @@ -1475,8 +2155,9 @@ int security_node_sid(u16 domain, *out_sid = SECINITSID_NODE; } + rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1497,80 +2178,65 @@ out: */ int security_get_user_sids(u32 fromsid, - char *username, + char *username, u32 **sids, u32 *nel) { struct context *fromcon, usercon; - u32 *mysids, *mysids2, sid; + u32 *mysids = NULL, *mysids2, sid; u32 mynel = 0, maxnel = SIDS_NEL; struct user_datum *user; struct role_datum *role; - struct av_decision avd; struct ebitmap_node *rnode, *tnode; int rc = 0, i, j; - if (!ss_initialized) { - *sids = NULL; - *nel = 0; + *sids = NULL; + *nel = 0; + + if (!ss_initialized) goto out; - } - POLICY_RDLOCK; + read_lock(&policy_rwlock); + context_init(&usercon); + + rc = -EINVAL; fromcon = sidtab_search(&sidtab, fromsid); - if (!fromcon) { - rc = -EINVAL; + if (!fromcon) goto out_unlock; - } + rc = -EINVAL; user = hashtab_search(policydb.p_users.table, username); - if (!user) { - rc = -EINVAL; + if (!user) goto out_unlock; - } + usercon.user = user->value; + rc = -ENOMEM; mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC); - if (!mysids) { - rc = -ENOMEM; + if (!mysids) goto out_unlock; - } - ebitmap_for_each_bit(&user->roles, rnode, i) { - if (!ebitmap_node_get_bit(rnode, i)) - continue; + ebitmap_for_each_positive_bit(&user->roles, rnode, i) { role = policydb.role_val_to_struct[i]; - usercon.role = i+1; - ebitmap_for_each_bit(&role->types, tnode, j) { - if (!ebitmap_node_get_bit(tnode, j)) - continue; - usercon.type = j+1; + usercon.role = i + 1; + ebitmap_for_each_positive_bit(&role->types, tnode, j) { + usercon.type = j + 1; if (mls_setup_user_range(fromcon, user, &usercon)) continue; - rc = context_struct_compute_av(fromcon, &usercon, - SECCLASS_PROCESS, - PROCESS__TRANSITION, - &avd); - if (rc || !(avd.allowed & PROCESS__TRANSITION)) - continue; rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); - if (rc) { - kfree(mysids); + if (rc) goto out_unlock; - } if (mynel < maxnel) { mysids[mynel++] = sid; } else { + rc = -ENOMEM; maxnel += SIDS_NEL; mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); - if (!mysids2) { - rc = -ENOMEM; - kfree(mysids); + if (!mysids2) goto out_unlock; - } memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); kfree(mysids); mysids = mysids2; @@ -1578,12 +2244,34 @@ int security_get_user_sids(u32 fromsid, } } } - - *sids = mysids; - *nel = mynel; - + rc = 0; out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); + if (rc || !mynel) { + kfree(mysids); + goto out; + } + + rc = -ENOMEM; + mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); + if (!mysids2) { + kfree(mysids); + goto out; + } + for (i = 0, j = 0; i < mynel; i++) { + struct av_decision dummy_avd; + rc = avc_has_perm_noaudit(fromsid, mysids[i], + SECCLASS_PROCESS, /* kernel value */ + PROCESS__TRANSITION, AVC_STRICT, + &dummy_avd); + if (!rc) + mysids2[j++] = mysids[i]; + cond_resched(); + } + rc = 0; + kfree(mysids); + *sids = mysids2; + *nel = j; out: return rc; } @@ -1600,16 +2288,23 @@ out: * transition SIDs or task SIDs. */ int security_genfs_sid(const char *fstype, - char *path, - u16 sclass, + char *path, + u16 orig_sclass, u32 *sid) { int len; + u16 sclass; struct genfs *genfs; struct ocontext *c; - int rc = 0, cmp = 0; + int rc, cmp = 0; + + while (path[0] == '/' && path[1] == '/') + path++; - POLICY_RDLOCK; + read_lock(&policy_rwlock); + + sclass = unmap_class(orig_sclass); + *sid = SECINITSID_UNLABELED; for (genfs = policydb.genfs; genfs; genfs = genfs->next) { cmp = strcmp(fstype, genfs->fstype); @@ -1617,11 +2312,9 @@ int security_genfs_sid(const char *fstype, break; } - if (!genfs || cmp) { - *sid = SECINITSID_UNLABELED; - rc = -ENOENT; + rc = -ENOENT; + if (!genfs || cmp) goto out; - } for (c = genfs->head; c; c = c->next) { len = strlen(c->u.name); @@ -1630,41 +2323,35 @@ int security_genfs_sid(const char *fstype, break; } - if (!c) { - *sid = SECINITSID_UNLABELED; - rc = -ENOENT; + rc = -ENOENT; + if (!c) goto out; - } if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, - &c->context[0], - &c->sid[0]); + rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; } *sid = c->sid[0]; + rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } /** * security_fs_use - Determine how to handle labeling for a filesystem. - * @fstype: filesystem type - * @behavior: labeling behavior - * @sid: SID for filesystem (superblock) + * @sb: superblock in question */ -int security_fs_use( - const char *fstype, - unsigned int *behavior, - u32 *sid) +int security_fs_use(struct super_block *sb) { int rc = 0; struct ocontext *c; + struct superblock_security_struct *sbsec = sb->s_security; + const char *fstype = sb->s_type->name; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_FSUSE]; while (c) { @@ -1674,65 +2361,69 @@ int security_fs_use( } if (c) { - *behavior = c->v.behavior; + sbsec->behavior = c->v.behavior; if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, - &c->context[0], + rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; } - *sid = c->sid[0]; + sbsec->sid = c->sid[0]; } else { - rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); + rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid); if (rc) { - *behavior = SECURITY_FS_USE_NONE; + sbsec->behavior = SECURITY_FS_USE_NONE; rc = 0; } else { - *behavior = SECURITY_FS_USE_GENFS; + sbsec->behavior = SECURITY_FS_USE_GENFS; } } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } int security_get_bools(int *len, char ***names, int **values) { - int i, rc = -ENOMEM; + int i, rc; - POLICY_RDLOCK; + read_lock(&policy_rwlock); *names = NULL; *values = NULL; + rc = 0; *len = policydb.p_bools.nprim; - if (!*len) { - rc = 0; + if (!*len) goto out; - } - *names = kcalloc(*len, sizeof(char*), GFP_ATOMIC); + rc = -ENOMEM; + *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC); if (!*names) goto err; - *values = kcalloc(*len, sizeof(int), GFP_ATOMIC); + rc = -ENOMEM; + *values = kcalloc(*len, sizeof(int), GFP_ATOMIC); if (!*values) goto err; for (i = 0; i < *len; i++) { size_t name_len; + (*values)[i] = policydb.bool_val_to_struct[i]->state; - name_len = strlen(policydb.p_bool_val_to_name[i]) + 1; - (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC); + name_len = strlen(sym_name(&policydb, SYM_BOOLS, i)) + 1; + + rc = -ENOMEM; + (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC); if (!(*names)[i]) goto err; - strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len); + + strncpy((*names)[i], sym_name(&policydb, SYM_BOOLS, i), name_len); (*names)[i][name_len - 1] = 0; } rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; err: if (*names) { @@ -1746,64 +2437,826 @@ err: int security_set_bools(int len, int *values) { - int i, rc = 0; + int i, rc; int lenp, seqno = 0; struct cond_node *cur; - POLICY_WRLOCK; + write_lock_irq(&policy_rwlock); + rc = -EFAULT; lenp = policydb.p_bools.nprim; - if (len != lenp) { - rc = -EFAULT; + if (len != lenp) goto out; - } - printk(KERN_INFO "security: committed booleans { "); for (i = 0; i < len; i++) { - if (values[i]) { + if (!!values[i] != policydb.bool_val_to_struct[i]->state) { + audit_log(current->audit_context, GFP_ATOMIC, + AUDIT_MAC_CONFIG_CHANGE, + "bool=%s val=%d old_val=%d auid=%u ses=%u", + sym_name(&policydb, SYM_BOOLS, i), + !!values[i], + policydb.bool_val_to_struct[i]->state, + from_kuid(&init_user_ns, audit_get_loginuid(current)), + audit_get_sessionid(current)); + } + if (values[i]) policydb.bool_val_to_struct[i]->state = 1; - } else { + else policydb.bool_val_to_struct[i]->state = 0; - } - if (i != 0) - printk(", "); - printk("%s:%d", policydb.p_bool_val_to_name[i], - policydb.bool_val_to_struct[i]->state); } - printk(" }\n"); - 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; } seqno = ++latest_granting; - + rc = 0; out: - POLICY_WRUNLOCK; + write_unlock_irq(&policy_rwlock); if (!rc) { avc_ss_reset(seqno); selnl_notify_policyload(seqno); + selinux_status_update_policyload(seqno); + selinux_xfrm_notify_policyload(); } return rc; } int security_get_bool_value(int bool) { - int rc = 0; + int rc; int len; - POLICY_RDLOCK; + read_lock(&policy_rwlock); + rc = -EFAULT; len = policydb.p_bools.nprim; - if (bool >= len) { - rc = -EFAULT; + if (bool >= len) goto out; - } rc = policydb.bool_val_to_struct[bool]->state; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); + return rc; +} + +static int security_preserve_bools(struct policydb *p) +{ + int rc, nbools = 0, *bvalues = NULL, i; + char **bnames = NULL; + struct cond_bool_datum *booldatum; + struct cond_node *cur; + + rc = security_get_bools(&nbools, &bnames, &bvalues); + if (rc) + goto out; + for (i = 0; i < nbools; i++) { + booldatum = hashtab_search(p->p_bools.table, bnames[i]); + if (booldatum) + booldatum->state = bvalues[i]; + } + for (cur = p->cond_list; cur; cur = cur->next) { + rc = evaluate_cond_node(p, cur); + if (rc) + goto out; + } + +out: + if (bnames) { + for (i = 0; i < nbools; i++) + kfree(bnames[i]); + } + kfree(bnames); + kfree(bvalues); + return rc; +} + +/* + * security_sid_mls_copy() - computes a new sid based on the given + * sid and the mls portion of mls_sid. + */ +int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) +{ + struct context *context1; + struct context *context2; + struct context newcon; + char *s; + u32 len; + int rc; + + rc = 0; + if (!ss_initialized || !policydb.mls_enabled) { + *new_sid = sid; + goto out; + } + + context_init(&newcon); + + read_lock(&policy_rwlock); + + rc = -EINVAL; + context1 = sidtab_search(&sidtab, sid); + if (!context1) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, sid); + goto out_unlock; + } + + rc = -EINVAL; + context2 = sidtab_search(&sidtab, mls_sid); + if (!context2) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, mls_sid); + goto out_unlock; + } + + newcon.user = context1->user; + newcon.role = context1->role; + newcon.type = context1->type; + rc = mls_context_cpy(&newcon, context2); + if (rc) + goto out_unlock; + + /* Check the validity of the new context. */ + if (!policydb_context_isvalid(&policydb, &newcon)) { + rc = convert_context_handle_invalid_context(&newcon); + if (rc) { + if (!context_struct_to_string(&newcon, &s, &len)) { + audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, + "security_sid_mls_copy: invalid context %s", s); + kfree(s); + } + goto out_unlock; + } + } + + rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid); +out_unlock: + read_unlock(&policy_rwlock); + context_destroy(&newcon); +out: + return rc; +} + +/** + * security_net_peersid_resolve - Compare and resolve two network peer SIDs + * @nlbl_sid: NetLabel SID + * @nlbl_type: NetLabel labeling protocol type + * @xfrm_sid: XFRM SID + * + * Description: + * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be + * resolved into a single SID it is returned via @peer_sid and the function + * returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function + * returns a negative value. A table summarizing the behavior is below: + * + * | function return | @sid + * ------------------------------+-----------------+----------------- + * no peer labels | 0 | SECSID_NULL + * single peer label | 0 | <peer_label> + * multiple, consistent labels | 0 | <peer_label> + * multiple, inconsistent labels | -<errno> | SECSID_NULL + * + */ +int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, + u32 xfrm_sid, + u32 *peer_sid) +{ + int rc; + struct context *nlbl_ctx; + struct context *xfrm_ctx; + + *peer_sid = SECSID_NULL; + + /* handle the common (which also happens to be the set of easy) cases + * right away, these two if statements catch everything involving a + * single or absent peer SID/label */ + if (xfrm_sid == SECSID_NULL) { + *peer_sid = nlbl_sid; + return 0; + } + /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label + * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label + * is present */ + if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) { + *peer_sid = xfrm_sid; + return 0; + } + + /* we don't need to check ss_initialized here since the only way both + * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the + * security server was initialized and ss_initialized was true */ + if (!policydb.mls_enabled) + return 0; + + read_lock(&policy_rwlock); + + rc = -EINVAL; + nlbl_ctx = sidtab_search(&sidtab, nlbl_sid); + if (!nlbl_ctx) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, nlbl_sid); + goto out; + } + rc = -EINVAL; + xfrm_ctx = sidtab_search(&sidtab, xfrm_sid); + if (!xfrm_ctx) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, xfrm_sid); + goto out; + } + rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES); + if (rc) + goto out; + + /* at present NetLabel SIDs/labels really only carry MLS + * information so if the MLS portion of the NetLabel SID + * matches the MLS portion of the labeled XFRM SID/label + * then pass along the XFRM SID as it is the most + * expressive */ + *peer_sid = xfrm_sid; +out: + read_unlock(&policy_rwlock); + return rc; +} + +static int get_classes_callback(void *k, void *d, void *args) +{ + struct class_datum *datum = d; + char *name = k, **classes = args; + int value = datum->value - 1; + + classes[value] = kstrdup(name, GFP_ATOMIC); + if (!classes[value]) + return -ENOMEM; + + return 0; +} + +int security_get_classes(char ***classes, int *nclasses) +{ + int rc; + + read_lock(&policy_rwlock); + + rc = -ENOMEM; + *nclasses = policydb.p_classes.nprim; + *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC); + if (!*classes) + goto out; + + rc = hashtab_map(policydb.p_classes.table, get_classes_callback, + *classes); + if (rc) { + int i; + for (i = 0; i < *nclasses; i++) + kfree((*classes)[i]); + kfree(*classes); + } + +out: + read_unlock(&policy_rwlock); return rc; } + +static int get_permissions_callback(void *k, void *d, void *args) +{ + struct perm_datum *datum = d; + char *name = k, **perms = args; + int value = datum->value - 1; + + perms[value] = kstrdup(name, GFP_ATOMIC); + if (!perms[value]) + return -ENOMEM; + + return 0; +} + +int security_get_permissions(char *class, char ***perms, int *nperms) +{ + int rc, i; + struct class_datum *match; + + read_lock(&policy_rwlock); + + rc = -EINVAL; + match = hashtab_search(policydb.p_classes.table, class); + if (!match) { + printk(KERN_ERR "SELinux: %s: unrecognized class %s\n", + __func__, class); + goto out; + } + + rc = -ENOMEM; + *nperms = match->permissions.nprim; + *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC); + if (!*perms) + goto out; + + if (match->comdatum) { + rc = hashtab_map(match->comdatum->permissions.table, + get_permissions_callback, *perms); + if (rc) + goto err; + } + + rc = hashtab_map(match->permissions.table, get_permissions_callback, + *perms); + if (rc) + goto err; + +out: + read_unlock(&policy_rwlock); + return rc; + +err: + read_unlock(&policy_rwlock); + for (i = 0; i < *nperms; i++) + kfree((*perms)[i]); + kfree(*perms); + return rc; +} + +int security_get_reject_unknown(void) +{ + return policydb.reject_unknown; +} + +int security_get_allow_unknown(void) +{ + return policydb.allow_unknown; +} + +/** + * security_policycap_supported - Check for a specific policy capability + * @req_cap: capability + * + * Description: + * This function queries the currently loaded policy to see if it supports the + * capability specified by @req_cap. Returns true (1) if the capability is + * supported, false (0) if it isn't supported. + * + */ +int security_policycap_supported(unsigned int req_cap) +{ + int rc; + + read_lock(&policy_rwlock); + rc = ebitmap_get_bit(&policydb.policycaps, req_cap); + read_unlock(&policy_rwlock); + + return rc; +} + +struct selinux_audit_rule { + u32 au_seqno; + struct context au_ctxt; +}; + +void selinux_audit_rule_free(void *vrule) +{ + struct selinux_audit_rule *rule = vrule; + + if (rule) { + context_destroy(&rule->au_ctxt); + kfree(rule); + } +} + +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) +{ + struct selinux_audit_rule *tmprule; + struct role_datum *roledatum; + struct type_datum *typedatum; + struct user_datum *userdatum; + struct selinux_audit_rule **rule = (struct selinux_audit_rule **)vrule; + int rc = 0; + + *rule = NULL; + + if (!ss_initialized) + return -EOPNOTSUPP; + + switch (field) { + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + /* only 'equals' and 'not equals' fit user, role, and type */ + if (op != Audit_equal && op != Audit_not_equal) + return -EINVAL; + break; + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + /* we do not allow a range, indicated by the presence of '-' */ + if (strchr(rulestr, '-')) + return -EINVAL; + break; + default: + /* only the above fields are valid */ + return -EINVAL; + } + + tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL); + if (!tmprule) + return -ENOMEM; + + context_init(&tmprule->au_ctxt); + + read_lock(&policy_rwlock); + + tmprule->au_seqno = latest_granting; + + switch (field) { + case AUDIT_SUBJ_USER: + case AUDIT_OBJ_USER: + rc = -EINVAL; + userdatum = hashtab_search(policydb.p_users.table, rulestr); + if (!userdatum) + goto out; + tmprule->au_ctxt.user = userdatum->value; + break; + case AUDIT_SUBJ_ROLE: + case AUDIT_OBJ_ROLE: + rc = -EINVAL; + roledatum = hashtab_search(policydb.p_roles.table, rulestr); + if (!roledatum) + goto out; + tmprule->au_ctxt.role = roledatum->value; + break; + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_TYPE: + rc = -EINVAL; + typedatum = hashtab_search(policydb.p_types.table, rulestr); + if (!typedatum) + goto out; + tmprule->au_ctxt.type = typedatum->value; + break; + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC); + if (rc) + goto out; + break; + } + rc = 0; +out: + read_unlock(&policy_rwlock); + + if (rc) { + selinux_audit_rule_free(tmprule); + tmprule = NULL; + } + + *rule = tmprule; + + return rc; +} + +/* Check to see if the rule contains any selinux fields */ +int selinux_audit_rule_known(struct audit_krule *rule) +{ + int i; + + for (i = 0; i < rule->field_count; i++) { + struct audit_field *f = &rule->fields[i]; + switch (f->type) { + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + return 1; + } + } + + return 0; +} + +int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, + struct audit_context *actx) +{ + struct context *ctxt; + struct mls_level *level; + struct selinux_audit_rule *rule = vrule; + int match = 0; + + if (unlikely(!rule)) { + WARN_ONCE(1, "selinux_audit_rule_match: missing rule\n"); + return -ENOENT; + } + + read_lock(&policy_rwlock); + + if (rule->au_seqno < latest_granting) { + match = -ESTALE; + goto out; + } + + ctxt = sidtab_search(&sidtab, sid); + if (unlikely(!ctxt)) { + WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", + sid); + match = -ENOENT; + goto out; + } + + /* a field/op pair that is not caught here will simply fall through + without a match */ + switch (field) { + case AUDIT_SUBJ_USER: + case AUDIT_OBJ_USER: + switch (op) { + case Audit_equal: + match = (ctxt->user == rule->au_ctxt.user); + break; + case Audit_not_equal: + match = (ctxt->user != rule->au_ctxt.user); + break; + } + break; + case AUDIT_SUBJ_ROLE: + case AUDIT_OBJ_ROLE: + switch (op) { + case Audit_equal: + match = (ctxt->role == rule->au_ctxt.role); + break; + case Audit_not_equal: + match = (ctxt->role != rule->au_ctxt.role); + break; + } + break; + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_TYPE: + switch (op) { + case Audit_equal: + match = (ctxt->type == rule->au_ctxt.type); + break; + case Audit_not_equal: + match = (ctxt->type != rule->au_ctxt.type); + break; + } + break; + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + level = ((field == AUDIT_SUBJ_SEN || + field == AUDIT_OBJ_LEV_LOW) ? + &ctxt->range.level[0] : &ctxt->range.level[1]); + switch (op) { + case Audit_equal: + match = mls_level_eq(&rule->au_ctxt.range.level[0], + level); + break; + case Audit_not_equal: + match = !mls_level_eq(&rule->au_ctxt.range.level[0], + level); + break; + case Audit_lt: + match = (mls_level_dom(&rule->au_ctxt.range.level[0], + level) && + !mls_level_eq(&rule->au_ctxt.range.level[0], + level)); + break; + case Audit_le: + match = mls_level_dom(&rule->au_ctxt.range.level[0], + level); + break; + case Audit_gt: + match = (mls_level_dom(level, + &rule->au_ctxt.range.level[0]) && + !mls_level_eq(level, + &rule->au_ctxt.range.level[0])); + break; + case Audit_ge: + match = mls_level_dom(level, + &rule->au_ctxt.range.level[0]); + break; + } + } + +out: + read_unlock(&policy_rwlock); + return match; +} + +static int (*aurule_callback)(void) = audit_update_lsm_rules; + +static int aurule_avc_callback(u32 event) +{ + int err = 0; + + if (event == AVC_CALLBACK_RESET && aurule_callback) + err = aurule_callback(); + return err; +} + +static int __init aurule_init(void) +{ + int err; + + err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET); + if (err) + panic("avc_add_callback() failed, error %d\n", err); + + return err; +} +__initcall(aurule_init); + +#ifdef CONFIG_NETLABEL +/** + * security_netlbl_cache_add - Add an entry to the NetLabel cache + * @secattr: the NetLabel packet security attributes + * @sid: the SELinux SID + * + * Description: + * Attempt to cache the context in @ctx, which was derived from the packet in + * @skb, in the NetLabel subsystem cache. This function assumes @secattr has + * already been initialized. + * + */ +static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr, + u32 sid) +{ + u32 *sid_cache; + + sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC); + if (sid_cache == NULL) + return; + secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); + if (secattr->cache == NULL) { + kfree(sid_cache); + return; + } + + *sid_cache = sid; + secattr->cache->free = kfree; + secattr->cache->data = sid_cache; + secattr->flags |= NETLBL_SECATTR_CACHE; +} + +/** + * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID + * @secattr: the NetLabel packet security attributes + * @sid: the SELinux SID + * + * Description: + * Convert the given NetLabel security attributes in @secattr into a + * SELinux SID. If the @secattr field does not contain a full SELinux + * SID/context then use SECINITSID_NETMSG as the foundation. If possible the + * 'cache' field of @secattr is set and the CACHE flag is set; this is to + * allow the @secattr to be used by NetLabel to cache the secattr to SID + * conversion for future lookups. Returns zero on success, negative values on + * failure. + * + */ +int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, + u32 *sid) +{ + int rc; + struct context *ctx; + struct context ctx_new; + + if (!ss_initialized) { + *sid = SECSID_NULL; + return 0; + } + + read_lock(&policy_rwlock); + + if (secattr->flags & NETLBL_SECATTR_CACHE) + *sid = *(u32 *)secattr->cache->data; + else if (secattr->flags & NETLBL_SECATTR_SECID) + *sid = secattr->attr.secid; + else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { + rc = -EIDRM; + ctx = sidtab_search(&sidtab, SECINITSID_NETMSG); + if (ctx == NULL) + goto out; + + context_init(&ctx_new); + ctx_new.user = ctx->user; + ctx_new.role = ctx->role; + ctx_new.type = ctx->type; + mls_import_netlbl_lvl(&ctx_new, secattr); + if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { + rc = ebitmap_netlbl_import(&ctx_new.range.level[0].cat, + secattr->attr.mls.cat); + if (rc) + goto out; + memcpy(&ctx_new.range.level[1].cat, + &ctx_new.range.level[0].cat, + sizeof(ctx_new.range.level[0].cat)); + } + rc = -EIDRM; + if (!mls_context_isvalid(&policydb, &ctx_new)) + goto out_free; + + rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid); + if (rc) + goto out_free; + + security_netlbl_cache_add(secattr, *sid); + + ebitmap_destroy(&ctx_new.range.level[0].cat); + } else + *sid = SECSID_NULL; + + read_unlock(&policy_rwlock); + return 0; +out_free: + ebitmap_destroy(&ctx_new.range.level[0].cat); +out: + read_unlock(&policy_rwlock); + return rc; +} + +/** + * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr + * @sid: the SELinux SID + * @secattr: the NetLabel packet security attributes + * + * Description: + * Convert the given SELinux SID in @sid into a NetLabel security attribute. + * Returns zero on success, negative values on failure. + * + */ +int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) +{ + int rc; + struct context *ctx; + + if (!ss_initialized) + return 0; + + read_lock(&policy_rwlock); + + rc = -ENOENT; + ctx = sidtab_search(&sidtab, sid); + if (ctx == NULL) + goto out; + + rc = -ENOMEM; + secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1), + GFP_ATOMIC); + if (secattr->domain == NULL) + goto out; + + 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); +out: + read_unlock(&policy_rwlock); + return rc; +} +#endif /* CONFIG_NETLABEL */ + +/** + * security_read_policy - read the policy. + * @data: binary policy data + * @len: length of data in bytes + * + */ +int security_read_policy(void **data, size_t *len) +{ + int rc; + struct policy_file fp; + + if (!ss_initialized) + return -EINVAL; + + *len = security_policydb_len(); + + *data = vmalloc_user(*len); + if (!*data) + return -ENOMEM; + + fp.data = *data; + fp.len = *len; + + read_lock(&policy_rwlock); + rc = policydb_write(&policydb, &fp); + read_unlock(&policy_rwlock); + + if (rc) + return rc; + + *len = (unsigned long)fp.data - (unsigned long)*data; + return 0; + +} diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 871c33bd074..5840a35155f 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -7,7 +7,6 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/errno.h> -#include <linux/sched.h> #include "flask.h" #include "security.h" #include "sidtab.h" @@ -15,10 +14,6 @@ #define SIDTAB_HASH(sid) \ (sid & SIDTAB_HASH_MASK) -#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock) -#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x) -#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x) - int sidtab_init(struct sidtab *s) { int i; @@ -31,7 +26,7 @@ int sidtab_init(struct sidtab *s) s->nel = 0; s->next_sid = 1; s->shutdown = 0; - INIT_SIDTAB_LOCK(s); + spin_lock_init(&s->lock); return 0; } @@ -48,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; } @@ -87,7 +82,7 @@ out: return rc; } -struct context *sidtab_search(struct sidtab *s, u32 sid) +static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) { int hvalue; struct sidtab_node *cur; @@ -97,15 +92,18 @@ struct context *sidtab_search(struct sidtab *s, u32 sid) hvalue = SIDTAB_HASH(sid); cur = s->htable[hvalue]; - while (cur != NULL && sid > cur->sid) + while (cur && sid > cur->sid) cur = cur->next; - if (cur == NULL || sid != cur->sid) { + if (force && cur && sid == cur->sid && cur->context.len) + return &cur->context; + + if (cur == NULL || sid != cur->sid || cur->context.len) { /* Remap invalid SIDs to the unlabeled SID. */ 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; @@ -114,6 +112,16 @@ struct context *sidtab_search(struct sidtab *s, u32 sid) return &cur->context; } +struct context *sidtab_search(struct sidtab *s, u32 sid) +{ + return sidtab_search_core(s, sid, 0); +} + +struct context *sidtab_search_force(struct sidtab *s, u32 sid) +{ + return sidtab_search_core(s, sid, 1); +} + int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, struct context *context, @@ -128,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; @@ -139,43 +147,15 @@ out: return rc; } -void sidtab_map_remove_on_error(struct sidtab *s, - int (*apply) (u32 sid, - struct context *context, - void *args), - void *args) +static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc) { - int i, ret; - struct sidtab_node *last, *cur, *temp; + BUG_ON(loc >= SIDTAB_CACHE_LEN); - if (!s) - return; - - for (i = 0; i < SIDTAB_SIZE; i++) { - last = NULL; - cur = s->htable[i]; - while (cur != NULL) { - ret = apply(cur->sid, &cur->context, args); - if (ret) { - if (last) { - last->next = cur->next; - } else { - s->htable[i] = cur->next; - } - - temp = cur; - cur = cur->next; - context_destroy(&temp->context); - kfree(temp); - s->nel--; - } else { - last = cur; - cur = cur->next; - } - } + while (loc > 0) { + s->cache[loc] = s->cache[loc - 1]; + loc--; } - - return; + s->cache[0] = n; } static inline u32 sidtab_search_context(struct sidtab *s, @@ -186,15 +166,34 @@ static inline u32 sidtab_search_context(struct sidtab *s, for (i = 0; i < SIDTAB_SIZE; i++) { cur = s->htable[i]; - while (cur != NULL) { - if (context_cmp(&cur->context, context)) + while (cur) { + if (context_cmp(&cur->context, context)) { + sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1); return cur->sid; + } cur = cur->next; } } return 0; } +static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context) +{ + int i; + struct sidtab_node *node; + + for (i = 0; i < SIDTAB_CACHE_LEN; i++) { + node = s->cache[i]; + if (unlikely(!node)) + return 0; + if (context_cmp(&node->context, context)) { + sidtab_update_cache(s, node, i); + return node->sid; + } + } + return 0; +} + int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *out_sid) @@ -205,9 +204,11 @@ int sidtab_context_to_sid(struct sidtab *s, *out_sid = SECSID_NULL; - sid = sidtab_search_context(s, context); + sid = sidtab_search_cache(s, context); + if (!sid) + sid = sidtab_search_context(s, context); if (!sid) { - SIDTAB_LOCK(s, flags); + spin_lock_irqsave(&s->lock, flags); /* Rescan now that we hold the lock. */ sid = sidtab_search_context(s, context); if (sid) @@ -218,11 +219,15 @@ int sidtab_context_to_sid(struct sidtab *s, goto unlock_out; } sid = s->next_sid++; + if (context->len) + printk(KERN_INFO + "SELinux: Context %s is not valid (left unmapped).\n", + context->str); ret = sidtab_insert(s, sid, context); if (ret) s->next_sid--; unlock_out: - SIDTAB_UNLOCK(s, flags); + spin_unlock_irqrestore(&s->lock, flags); } if (ret) @@ -254,7 +259,7 @@ void sidtab_hash_eval(struct sidtab *h, char *tag) } } - printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " + printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest " "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, max_chain_len); } @@ -269,7 +274,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); @@ -286,20 +291,23 @@ void sidtab_destroy(struct sidtab *s) void sidtab_set(struct sidtab *dst, struct sidtab *src) { unsigned long flags; + int i; - SIDTAB_LOCK(src, flags); + spin_lock_irqsave(&src->lock, flags); dst->htable = src->htable; dst->nel = src->nel; dst->next_sid = src->next_sid; dst->shutdown = 0; - SIDTAB_UNLOCK(src, flags); + for (i = 0; i < SIDTAB_CACHE_LEN; i++) + dst->cache[i] = NULL; + spin_unlock_irqrestore(&src->lock, flags); } void sidtab_shutdown(struct sidtab *s) { unsigned long flags; - SIDTAB_LOCK(s, flags); + spin_lock_irqsave(&s->lock, flags); s->shutdown = 1; - SIDTAB_UNLOCK(s, flags); + spin_unlock_irqrestore(&s->lock, flags); } diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 2fe9dfa3eb3..84dc154d938 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -26,12 +26,15 @@ struct sidtab { unsigned int nel; /* number of elements */ unsigned int next_sid; /* next SID to allocate */ unsigned char shutdown; +#define SIDTAB_CACHE_LEN 3 + struct sidtab_node *cache[SIDTAB_CACHE_LEN]; spinlock_t lock; }; int sidtab_init(struct sidtab *s); int sidtab_insert(struct sidtab *s, u32 sid, struct context *context); struct context *sidtab_search(struct sidtab *s, u32 sid); +struct context *sidtab_search_force(struct sidtab *s, u32 sid); int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, @@ -39,12 +42,6 @@ int sidtab_map(struct sidtab *s, void *args), void *args); -void sidtab_map_remove_on_error(struct sidtab *s, - int (*apply) (u32 sid, - struct context *context, - void *args), - void *args); - int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c new file mode 100644 index 00000000000..d982365f9d1 --- /dev/null +++ b/security/selinux/ss/status.c @@ -0,0 +1,126 @@ +/* + * mmap based event notifications for SELinux + * + * Author: KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * Copyright (C) 2010 NEC corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include "avc.h" +#include "services.h" + +/* + * The selinux_status_page shall be exposed to userspace applications + * using mmap interface on /selinux/status. + * It enables to notify applications a few events that will cause reset + * of userspace access vector without context switching. + * + * The selinux_kernel_status structure on the head of status page is + * protected from concurrent accesses using seqlock logic, so userspace + * application should reference the status page according to the seqlock + * logic. + * + * Typically, application checks status->sequence at the head of access + * control routine. If it is odd-number, kernel is updating the status, + * so please wait for a moment. If it is changed from the last sequence + * number, it means something happen, so application will reset userspace + * avc, if needed. + * In most cases, application shall confirm the kernel status is not + * changed without any system call invocations. + */ +static struct page *selinux_status_page; +static DEFINE_MUTEX(selinux_status_lock); + +/* + * selinux_kernel_status_page + * + * It returns a reference to selinux_status_page. If the status page is + * not allocated yet, it also tries to allocate it at the first time. + */ +struct page *selinux_kernel_status_page(void) +{ + struct selinux_kernel_status *status; + struct page *result = NULL; + + mutex_lock(&selinux_status_lock); + if (!selinux_status_page) { + selinux_status_page = alloc_page(GFP_KERNEL|__GFP_ZERO); + + if (selinux_status_page) { + status = page_address(selinux_status_page); + + status->version = SELINUX_KERNEL_STATUS_VERSION; + status->sequence = 0; + status->enforcing = selinux_enforcing; + /* + * NOTE: the next policyload event shall set + * a positive value on the status->policyload, + * although it may not be 1, but never zero. + * So, application can know it was updated. + */ + status->policyload = 0; + status->deny_unknown = !security_get_allow_unknown(); + } + } + result = selinux_status_page; + mutex_unlock(&selinux_status_lock); + + return result; +} + +/* + * selinux_status_update_setenforce + * + * It updates status of the current enforcing/permissive mode. + */ +void selinux_status_update_setenforce(int enforcing) +{ + struct selinux_kernel_status *status; + + mutex_lock(&selinux_status_lock); + if (selinux_status_page) { + status = page_address(selinux_status_page); + + status->sequence++; + smp_wmb(); + + status->enforcing = enforcing; + + smp_wmb(); + status->sequence++; + } + mutex_unlock(&selinux_status_lock); +} + +/* + * selinux_status_update_policyload + * + * It updates status of the times of policy reloaded, and current + * setting of deny_unknown. + */ +void selinux_status_update_policyload(int seqno) +{ + struct selinux_kernel_status *status; + + mutex_lock(&selinux_status_lock); + if (selinux_status_page) { + status = page_address(selinux_status_page); + + status->sequence++; + smp_wmb(); + + status->policyload = seqno; + status->deny_unknown = !security_get_allow_unknown(); + + smp_wmb(); + status->sequence++; + } + mutex_unlock(&selinux_status_lock); +} diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index 24a10d36d3b..160326ee99e 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -4,14 +4,13 @@ * Author : Stephen Smalley, <sds@epoch.ncsc.mil> */ #include <linux/kernel.h> -#include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> #include "symtab.h" -static unsigned int symhash(struct hashtab *h, void *key) +static unsigned int symhash(struct hashtab *h, const void *key) { - char *p, *keyp; + const char *p, *keyp; unsigned int size; unsigned int val; @@ -23,9 +22,9 @@ static unsigned int symhash(struct hashtab *h, void *key) return val & (h->size - 1); } -static int symcmp(struct hashtab *h, void *key1, void *key2) +static int symcmp(struct hashtab *h, const void *key1, const void *key2) { - char *keyp1, *keyp2; + const char *keyp1, *keyp2; keyp1 = key1; keyp2 = key2; @@ -37,7 +36,7 @@ int symtab_init(struct symtab *s, unsigned int size) { s->table = hashtab_create(symhash, symcmp, size); if (!s->table) - return -1; + return -ENOMEM; s->nprim = 0; return 0; } |
