Stephen Smalley
2009-Aug-18 14:14 UTC
[Xen-devel] [PATCH] xen/xsm/flask: Update to policy.24
Update Xen Flask module to policy.24. This is a back-port of the latest SELinux code to Xen, adjusted for Xen coding style and interfaces. Unneeded functionality such as most object context config data, handle_unknown, MLS field defaulting, etc has been omitted. Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov> Signed-off-by: George S. Coker, II <gscoker@alpha.ncsc.mil> --- tools/flask/policy/Makefile | 2 xen/xsm/flask/avc.c | 234 ++++++++------- xen/xsm/flask/flask_op.c | 2 xen/xsm/flask/include/avc_ss.h | 23 + xen/xsm/flask/include/security.h | 14 xen/xsm/flask/ss/avtab.c | 142 +++++++-- xen/xsm/flask/ss/avtab.h | 20 - xen/xsm/flask/ss/conditional.c | 23 - xen/xsm/flask/ss/conditional.h | 2 xen/xsm/flask/ss/context.h | 33 +- xen/xsm/flask/ss/ebitmap.c | 209 +++++++------- xen/xsm/flask/ss/ebitmap.h | 94 ++++-- xen/xsm/flask/ss/hashtab.c | 10 xen/xsm/flask/ss/hashtab.h | 12 xen/xsm/flask/ss/mls.c | 321 ++++++++------------- xen/xsm/flask/ss/mls.h | 6 xen/xsm/flask/ss/policydb.c | 411 +++++++++++++++++++++++---- xen/xsm/flask/ss/policydb.h | 29 + xen/xsm/flask/ss/services.c | 574 +++++++++++++++++++++++++++------------ xen/xsm/flask/ss/sidtab.c | 2 xen/xsm/flask/ss/symtab.c | 8 21 files changed, 1438 insertions(+), 733 deletions(-) diff --git a/tools/flask/policy/Makefile b/tools/flask/policy/Makefile --- a/tools/flask/policy/Makefile +++ b/tools/flask/policy/Makefile @@ -20,7 +20,7 @@ # By default, checkpolicy will create the highest # version policy it supports. Setting this will # override the version. -OUTPUT_POLICY = 20 +#OUTPUT_POLICY = 20 # Policy Type # strict, targeted, diff --git a/xen/xsm/flask/avc.c b/xen/xsm/flask/avc.c --- a/xen/xsm/flask/avc.c +++ b/xen/xsm/flask/avc.c @@ -32,12 +32,7 @@ #include "avc.h" #include "avc_ss.h" -static const struct av_perm_to_string -{ - u16 tclass; - u32 value; - const char *name; -} av_perm_to_string[] = { +static const struct av_perm_to_string av_perm_to_string[] = { #define S_(c, v, s) { c, v, s }, #include "av_perm_to_string.h" #undef S_ @@ -57,17 +52,22 @@ #undef TE_ #undef S_ -static const struct av_inherit -{ - u16 tclass; - const char **common_pts; - u32 common_base; -} av_inherit[] = { -#define S_(c, i, b) { c, common_##i##_perm_to_string, b }, +static const struct av_inherit av_inherit[] = { +#define S_(c, i, b) { .tclass = c, .common_pts = common_##i##_perm_to_string, \ + .common_base = b }, #include "av_inherit.h" #undef S_ }; +const struct selinux_class_perm selinux_class_perm = { + .av_perm_to_string = av_perm_to_string, + .av_pts_len = ARRAY_SIZE(av_perm_to_string), + .class_to_string = class_to_string, + .cts_len = ARRAY_SIZE(class_to_string), + .av_inherit = av_inherit, + .av_inherit_len = ARRAY_SIZE(av_inherit) +}; + #define AVC_CACHE_SLOTS 512 #define AVC_DEF_CACHE_THRESHOLD 512 #define AVC_CACHE_RECLAIM 16 @@ -86,17 +86,16 @@ u32 tsid; u16 tclass; struct av_decision avd; - atomic_t used; /* used recently */ }; struct avc_node { struct avc_entry ae; - struct list_head list; + struct hlist_node list; /* anchored in avc_cache->slots[i] */ struct rcu_head rhead; }; struct avc_cache { - struct list_head slots[AVC_CACHE_SLOTS]; + struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ atomic_t lru_hint; /* LRU hint for reclaim scan */ atomic_t active_nodes; @@ -241,7 +240,7 @@ for ( i = 0; i < AVC_CACHE_SLOTS; i++ ) { - INIT_LIST_HEAD(&avc_cache.slots[i]); + INIT_HLIST_HEAD(&avc_cache.slots[i]); spin_lock_init(&avc_cache.slots_lock[i]); } atomic_set(&avc_cache.active_nodes, 0); @@ -254,6 +253,7 @@ { int i, chain_len, max_chain_len, slots_used; struct avc_node *node; + struct hlist_head *head; rcu_read_lock(); @@ -261,11 +261,14 @@ max_chain_len = 0; for ( i = 0; i < AVC_CACHE_SLOTS; i++ ) { - if ( !list_empty(&avc_cache.slots[i]) ) + head = &avc_cache.slots[i]; + if ( !hlist_empty(head) ) { + struct hlist_node *next; + slots_used++; chain_len = 0; - list_for_each_entry_rcu(node, &avc_cache.slots[i], list) + hlist_for_each_entry_rcu(node, next, head, list) chain_len++; if ( chain_len > max_chain_len ) max_chain_len = chain_len; @@ -289,7 +292,7 @@ static void avc_node_delete(struct avc_node *node) { - list_del_rcu(&node->list); + hlist_del_rcu(&node->list); call_rcu(&node->rhead, avc_node_free); atomic_dec(&avc_cache.active_nodes); } @@ -303,7 +306,7 @@ static void avc_node_replace(struct avc_node *new, struct avc_node *old) { - list_replace_rcu(&old->list, &new->list); + hlist_replace_rcu(&old->list, &new->list); call_rcu(&old->rhead, avc_node_free); atomic_dec(&avc_cache.active_nodes); } @@ -312,31 +315,34 @@ { struct avc_node *node; int hvalue, try, ecx; - unsigned long flags; + unsigned long flags; + struct hlist_head *head; + struct hlist_node *next; + spinlock_t *lock; for ( try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) { atomic_inc(&avc_cache.lru_hint); hvalue = atomic_read(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1); + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; - spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flags); - - list_for_each_entry(node, &avc_cache.slots[hvalue], list) + spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flags); + rcu_read_lock(); + hlist_for_each_entry(node, next, head, list) { - if ( atomic_dec_and_test(&node->ae.used) ) - { - /* Recently Unused */ avc_node_delete(node); avc_cache_stats_incr(reclaims); ecx++; if ( ecx >= AVC_CACHE_RECLAIM ) { - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); - goto out; + rcu_read_unlock(); + spin_unlock_irqrestore(lock, flags); + goto out; } - } } - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); + rcu_read_unlock(); + spin_unlock_irqrestore(lock, flags); } out: return ecx; @@ -352,8 +358,7 @@ memset(node, 0, sizeof(*node)); INIT_RCU_HEAD(&node->rhead); - INIT_LIST_HEAD(&node->list); - atomic_set(&node->ae.used, 1); + INIT_HLIST_NODE(&node->list); avc_cache_stats_incr(allocations); atomic_inc(&avc_cache.active_nodes); @@ -364,40 +369,35 @@ return node; } -static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) +static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd) { node->ae.ssid = ssid; node->ae.tsid = tsid; node->ae.tclass = tclass; - memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd)); + memcpy(&node->ae.avd, avd, sizeof(node->ae.avd)); } static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) { struct avc_node *node, *ret = NULL; int hvalue; + struct hlist_head *head; + struct hlist_node *next; hvalue = avc_hash(ssid, tsid, tclass); - list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) + head = &avc_cache.slots[hvalue]; + hlist_for_each_entry_rcu(node, next, head, list) { - if ( ssid == node->ae.ssid && tclass == node->ae.tclass && - tsid == node->ae.tsid ) + if ( ssid == node->ae.ssid && + tclass == node->ae.tclass && + tsid == node->ae.tsid ) { ret = node; break; } } - if ( ret == NULL ) - { - /* cache miss */ - goto out; - } - - /* cache hit */ - if ( atomic_read(&ret->ae.used) != 1 ) - atomic_set(&ret->ae.used, 1); -out: return ret; } @@ -415,22 +415,18 @@ * then this function return the avc_node. * Otherwise, this function returns NULL. */ -static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested) +static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass) { struct avc_node *node; avc_cache_stats_incr(lookups); node = avc_search_node(ssid, tsid, tclass); - if ( node && ((node->ae.avd.decided & requested) == requested) ) - { + if ( node ) avc_cache_stats_incr(hits); - goto out; - } + else + avc_cache_stats_incr(misses); - node = NULL; - avc_cache_stats_incr(misses); -out: return node; } @@ -477,34 +473,43 @@ * the access vectors into a cache entry, returns * avc_node inserted. Otherwise, this function returns NULL. */ -static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) +static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, + struct av_decision *avd) { struct avc_node *pos, *node = NULL; int hvalue; - unsigned long flag; + unsigned long flag; - if ( avc_latest_notif_update(ae->avd.seqno, 1) ) + if ( avc_latest_notif_update(avd->seqno, 1) ) goto out; node = avc_alloc_node(); if ( node ) { + struct hlist_head *head; + struct hlist_node *next; + spinlock_t *lock; + hvalue = avc_hash(ssid, tsid, tclass); - avc_node_populate(node, ssid, tsid, tclass, ae); + avc_node_populate(node, ssid, tsid, tclass, avd); - spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); - list_for_each_entry(pos, &avc_cache.slots[hvalue], list) + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; + + spin_lock_irqsave(lock, flag); + hlist_for_each_entry(pos, next, head, list) { - if ( pos->ae.ssid == ssid && pos->ae.tsid == tsid && - pos->ae.tclass == tclass ) + if ( pos->ae.ssid == ssid && + pos->ae.tsid == tsid && + pos->ae.tclass == tclass ) { avc_node_replace(node, pos); goto found; } } - list_add_rcu(&node->list, &avc_cache.slots[hvalue]); + hlist_add_head_rcu(&node->list, head); found: - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); + spin_unlock_irqrestore(lock, flag); } out: return node; @@ -622,11 +627,15 @@ * otherwise, this function update the AVC entry. The original AVC-entry object * will release later by RCU. */ -static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass) +static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, + u32 seqno) { int hvalue, rc = 0; - unsigned long flag; + unsigned long flag; struct avc_node *pos, *node, *orig = NULL; + struct hlist_head *head; + struct hlist_node *next; + spinlock_t *lock; node = avc_alloc_node(); if ( !node ) @@ -636,12 +645,18 @@ } hvalue = avc_hash(ssid, tsid, tclass); - spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); - list_for_each_entry(pos, &avc_cache.slots[hvalue], list) + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; + + spin_lock_irqsave(lock, flag); + + hlist_for_each_entry(pos, next, head, list) { - if ( ssid==pos->ae.ssid && tsid==pos->ae.tsid && - tclass==pos->ae.tclass ) + if ( ssid == pos->ae.ssid && + tsid == pos->ae.tsid && + tclass == pos->ae.tclass && + seqno == pos->ae.avd.seqno ) { orig = pos; break; @@ -659,7 +674,7 @@ * Copy and replace original node. */ - avc_node_populate(node, ssid, tsid, tclass, &orig->ae); + avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); switch ( event ) { @@ -685,7 +700,7 @@ } avc_node_replace(node, orig); out_unlock: - spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); + spin_unlock_irqrestore(lock, flag); out: return rc; } @@ -697,31 +712,40 @@ int avc_ss_reset(u32 seqno) { struct avc_callback_node *c; - int i, rc = 0; - unsigned long flag; + int i, rc = 0, tmprc; + unsigned long flag; struct avc_node *node; + struct hlist_head *head; + struct hlist_node *next; + spinlock_t *lock; for ( i = 0; i < AVC_CACHE_SLOTS; i++ ) { - spin_lock_irqsave(&avc_cache.slots_lock[i], flag); - list_for_each_entry(node, &avc_cache.slots[i], list) + head = &avc_cache.slots[i]; + lock = &avc_cache.slots_lock[i]; + + spin_lock_irqsave(lock, flag); + rcu_read_lock(); + hlist_for_each_entry(node, next, head, list) avc_node_delete(node); - spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag); + rcu_read_unlock(); + spin_unlock_irqrestore(lock, flag); } for ( c = avc_callbacks; c; c = c->next ) { if ( c->events & AVC_CALLBACK_RESET ) { - rc = c->callback(AVC_CALLBACK_RESET, - 0, 0, 0, 0, NULL); - if ( rc ) - goto out; + tmprc = c->callback(AVC_CALLBACK_RESET, + 0, 0, 0, 0, NULL); + /* save the first error encountered for the return + value and continue processing the callbacks */ + if ( !rc ) + rc = tmprc; } } avc_latest_notif_update(seqno, 0); -out: return rc; } @@ -745,41 +769,47 @@ * should be released for the auditing. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested, - struct av_decision *avd) + struct av_decision *in_avd) { struct avc_node *node; - struct avc_entry entry, *p_ae; + struct av_decision avd_entry, *avd; int rc = 0; u32 denied; + BUG_ON(!requested); + rcu_read_lock(); - node = avc_lookup(ssid, tsid, tclass, requested); + node = avc_lookup(ssid, tsid, tclass); if ( !node ) { rcu_read_unlock(); - rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd); + + if ( in_avd ) + avd = in_avd; + else + avd = &avd_entry; + + rc = security_compute_av(ssid,tsid,tclass,requested,avd); if ( rc ) goto out; rcu_read_lock(); - node = avc_insert(ssid,tsid,tclass,&entry); + node = avc_insert(ssid,tsid,tclass,avd); + } else { + if ( in_avd ) + memcpy(in_avd, &node->ae.avd, sizeof(*in_avd)); + avd = &node->ae.avd; } - p_ae = node ? &node->ae : &entry; + denied = requested & ~(avd->allowed); - if ( avd ) - memcpy(avd, &p_ae->avd, sizeof(*avd)); - - denied = requested & ~(p_ae->avd.allowed); - - if ( !requested || denied ) + if ( denied ) { - if ( flask_enforcing ) + if ( !flask_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE) ) + avc_update_node(AVC_CALLBACK_GRANT,requested, + ssid,tsid,tclass,avd->seqno); + else rc = -EACCES; - else - if ( node ) - avc_update_node(AVC_CALLBACK_GRANT,requested, - ssid,tsid,tclass); } rcu_read_unlock(); diff --git a/xen/xsm/flask/flask_op.c b/xen/xsm/flask/flask_op.c --- a/xen/xsm/flask/flask_op.c +++ b/xen/xsm/flask/flask_op.c @@ -346,7 +346,7 @@ memset(buf, 0, size); length = snprintf(buf, size, "%x %x %x %x %u", - avd.allowed, avd.decided, + avd.allowed, 0xffffffff, avd.auditallow, avd.auditdeny, avd.seqno); diff --git a/xen/xsm/flask/include/avc_ss.h b/xen/xsm/flask/include/avc_ss.h --- a/xen/xsm/flask/include/avc_ss.h +++ b/xen/xsm/flask/include/avc_ss.h @@ -10,5 +10,28 @@ int avc_ss_reset(u32 seqno); +struct av_perm_to_string { + u16 tclass; + u32 value; + const char *name; +}; + +struct av_inherit { + const char **common_pts; + u32 common_base; + u16 tclass; +}; + +struct selinux_class_perm { + const struct av_perm_to_string *av_perm_to_string; + u32 av_pts_len; + u32 cts_len; + const char **class_to_string; + const struct av_inherit *av_inherit; + u32 av_inherit_len; +}; + +extern const struct selinux_class_perm selinux_class_perm; + #endif /* _FLASK_AVC_SS_H_ */ diff --git a/xen/xsm/flask/include/security.h b/xen/xsm/flask/include/security.h --- a/xen/xsm/flask/include/security.h +++ b/xen/xsm/flask/include/security.h @@ -26,10 +26,14 @@ #define POLICYDB_VERSION_VALIDATETRANS 19 #define POLICYDB_VERSION_MLS 19 #define POLICYDB_VERSION_AVTAB 20 +#define POLICYDB_VERSION_RANGETRANS 21 +#define POLICYDB_VERSION_POLCAP 22 +#define POLICYDB_VERSION_PERMISSIVE 23 +#define POLICYDB_VERSION_BOUNDARY 24 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_AVTAB +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY #ifdef FLASK_BOOTPARAM extern int flask_enabled; @@ -43,12 +47,15 @@ struct av_decision { u32 allowed; - u32 decided; u32 auditallow; u32 auditdeny; u32 seqno; + u32 flags; }; +/* definitions of av_decision.flags */ +#define AVD_FLAGS_PERMISSIVE 0x0001 + int security_compute_av(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct av_decision *avd); @@ -62,9 +69,6 @@ int security_context_to_sid(char *scontext, u32 scontext_len, u32 *out_sid); -int security_context_to_sid_default(char *scontext, u32 scontext_len, - u32 *out_sid, u32 def_sid); - int security_get_user_sids(u32 callsid, char *username, u32 **sids, u32 *nel); int security_pirq_sid(int pirq, u32 *out_sid); diff --git a/xen/xsm/flask/ss/avtab.c b/xen/xsm/flask/ss/avtab.c --- a/xen/xsm/flask/ss/avtab.c +++ b/xen/xsm/flask/ss/avtab.c @@ -12,6 +12,9 @@ * 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 * the Free Software Foundation, version 2. + * + * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> + * Tuned number of hash slots for avtab to reduce memory usage */ /* Ported to Xen 3.0, George Coker, <gscoker@alpha.ncsc.mil> */ @@ -25,11 +28,11 @@ #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 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_key *key, @@ -64,10 +67,10 @@ 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) { @@ -105,9 +108,9 @@ struct avtab_node *prev, *cur, *newnode; 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 ) { @@ -137,10 +140,10 @@ 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 && @@ -172,10 +175,10 @@ 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 && @@ -235,7 +238,7 @@ 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 ) @@ -248,19 +251,52 @@ } xfree(h->htable); h->htable = NULL; + h->nslot = 0; + h->mask = 0; } - int avtab_init(struct avtab *h) { + h->htable = NULL; + h->nel = 0; + return 0; +} + +int avtab_alloc(struct avtab *h, u32 nrules) +{ + u16 mask = 0; + u32 shift = 0; + u32 work = nrules; + u32 nslot = 0; int i; - h->htable = (void *)xmalloc_array(struct avtab_node, AVTAB_SIZE); + 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_SIZE ) + nslot = MAX_AVTAB_SIZE; + mask = nslot - 1; + + h->htable = xmalloc_array(struct avtab_node *, nslot); if ( !h->htable ) return -ENOMEM; - for ( i = 0; i < AVTAB_SIZE; i++ ) + for ( i = 0; i < nslot; i++ ) h->htable[i] = NULL; + +avtab_alloc_out: h->nel = 0; + h->nslot = nslot; + h->mask = mask; + printk(KERN_DEBUG "Flask: %d avtab hash slots, %d rules.\n", + h->nslot, nrules); return 0; } @@ -271,7 +307,7 @@ slots_used = 0; max_chain_len = 0; - for ( i = 0; i < AVTAB_SIZE; i++ ) + for ( i = 0; i < h->nslot; i++ ) { cur = h->htable[i]; if ( cur ) @@ -290,7 +326,7 @@ } printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " - "chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE, + "chain length %d\n", tag, h->nel, slots_used, h->nslot, max_chain_len); } @@ -303,17 +339,18 @@ AVTAB_MEMBER }; -int avtab_read_item(void *fp, u32 vers, struct avtab *a, +int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, int (*insertf)(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *p), void *p) { __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)); @@ -323,20 +360,20 @@ rc = next_entry(buf32, fp, sizeof(u32)); if ( rc < 0 ) { - printk(KERN_ERR "security: avtab: truncated entry\n"); + printk(KERN_ERR "Flask: avtab: truncated entry\n"); return -1; } items2 = le32_to_cpu(buf32[0]); if ( items2 > ARRAY_SIZE(buf32) ) { - printk(KERN_ERR "security: avtab: entry overflow\n"); + printk(KERN_ERR "Flask: avtab: entry overflow\n"); return -1; } rc = next_entry(buf32, fp, sizeof(u32)*items2); if ( rc < 0 ) { - printk(KERN_ERR "security: avtab: truncated entry\n"); + printk(KERN_ERR "Flask: avtab: truncated entry\n"); return -1; } items = 0; @@ -345,21 +382,21 @@ key.source_type = (u16)val; if ( key.source_type != val ) { - printk("security: avtab: truncated source type\n"); + printk("Flask: avtab: truncated source type\n"); return -1; } val = le32_to_cpu(buf32[items++]); key.target_type = (u16)val; if ( key.target_type != val ) { - printk("security: avtab: truncated target type\n"); + printk("Flask: avtab: truncated target type\n"); return -1; } val = le32_to_cpu(buf32[items++]); key.target_class = (u16)val; if ( key.target_class != val ) { - printk("security: avtab: truncated target class\n"); + printk("Flask: avtab: truncated target class\n"); return -1; } @@ -368,12 +405,12 @@ if ( !(val & (AVTAB_AV | AVTAB_TYPE)) ) { - printk("security: avtab: null entry\n"); + printk("Flask: avtab: null entry\n"); return -1; } if ( (val & AVTAB_AV) && (val & AVTAB_TYPE) ) { - printk("security: avtab: entry has both access vectors and types\n"); + printk("Flask: avtab: entry has both access vectors and types\n"); return -1; } @@ -390,7 +427,7 @@ } if ( items != items2 ) { - printk("security: avtab: entry only had %d items, expected %d\n", + printk("Flask: avtab: entry only had %d items, expected %d\n", items2, items); return -1; } @@ -400,7 +437,7 @@ rc = next_entry(buf16, fp, sizeof(u16)*4); if ( rc < 0 ) { - printk("security: avtab: truncated entry\n"); + printk("Flask: avtab: truncated entry\n"); return -1; } @@ -410,13 +447,39 @@ 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 "Flask: avtab: invalid type or class\n"); + return -1; + } + + set = 0; + for ( i = 0; i < ARRAY_SIZE(spec_order); i++ ) + { + if ( key.specified & spec_order[i] ) + set++; + } + if ( !set || set > 1 ) + { + printk(KERN_ERR "Flask: avtab: more than one specifier\n"); + return -1; + } + rc = next_entry(buf32, fp, sizeof(u32)); if ( rc < 0 ) { - printk("security: avtab: truncated entry\n"); + printk("Flask: avtab: truncated entry\n"); return -1; } datum.data = le32_to_cpu(*buf32); + if ( (key.specified & AVTAB_TYPE) && + !policydb_type_isvalid(pol, datum.data) ) + { + printk(KERN_ERR "Flask: avtab: invalid type\n"); + return -1; + } return insertf(a, &key, &datum, p); } @@ -426,7 +489,7 @@ 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]; @@ -435,25 +498,28 @@ rc = next_entry(buf, fp, sizeof(u32)); if ( rc < 0 ) { - printk(KERN_ERR "security: avtab: truncated table\n"); + printk(KERN_ERR "Flask: 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 "Flask: 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 "Flask: avtab: out of memory\n"); else if ( rc == -EEXIST ) - printk(KERN_ERR "security: avtab: duplicate entry\n"); + printk(KERN_ERR "Flask: avtab: duplicate entry\n"); else rc = -EINVAL; goto bad; diff --git a/xen/xsm/flask/ss/avtab.h b/xen/xsm/flask/ss/avtab.h --- a/xen/xsm/flask/ss/avtab.h +++ b/xen/xsm/flask/ss/avtab.h @@ -16,6 +16,9 @@ * 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 * the Free Software Foundation, version 2. + * + * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp> + * Tuned number of hash slots for avtab to reduce memory usage */ /* Ported to Xen 3.0, George Coker, <gscoker@alpha.ncsc.mil> */ @@ -53,19 +56,23 @@ 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); struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum); @@ -75,11 +82,10 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified); -#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 13 +#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS) +#define MAX_AVTAB_HASH_MASK (MAX_AVTAB_HASH_BUCKETS-1) +#define MAX_AVTAB_SIZE MAX_AVTAB_HASH_BUCKETS #endif /* _SS_AVTAB_H_ */ diff --git a/xen/xsm/flask/ss/conditional.c b/xen/xsm/flask/ss/conditional.c --- a/xen/xsm/flask/ss/conditional.c +++ b/xen/xsm/flask/ss/conditional.c @@ -101,7 +101,7 @@ { node->cur_state = new_state; if ( new_state == -1 ) - printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n"); + printk(KERN_ERR "Flask: expression result was undefined - disabling all rules.\n"); /* turn the rules on or off */ for ( cur = node->true_list; cur != NULL; cur = cur->next ) { @@ -287,7 +287,7 @@ { if ( avtab_search(&p->te_avtab, k) ) { - printk("security: type rule already exists outside of a " + printk("Flask: type rule already exists outside of a " "conditional."); goto err; } @@ -306,7 +306,7 @@ { if ( avtab_search_node_next(node_ptr, k->specified) ) { - printk("security: too many conflicting type rules."); + printk("Flask: too many conflicting type rules."); goto err; } found = 0; @@ -320,7 +320,7 @@ } if ( !found ) { - printk("security: conflicting type rules.\n"); + printk("Flask: conflicting type rules.\n"); goto err; } } @@ -329,7 +329,7 @@ { if ( avtab_search(&p->te_cond_avtab, k) ) { - printk("security: conflicting type rules when adding type rule " + printk("Flask: conflicting type rules when adding type rule " "for true.\n"); goto err; } @@ -339,7 +339,7 @@ node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); if ( !node_ptr ) { - printk("security: could not insert rule."); + printk("Flask: could not insert rule."); goto err; } @@ -389,8 +389,7 @@ 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; } @@ -403,13 +402,13 @@ { if ( expr->expr_type <= 0 || expr->expr_type > COND_LAST ) { - printk("security: conditional expressions uses unknown operator.\n"); + printk("Flask: conditional expressions uses unknown operator.\n"); return 0; } if ( expr->bool > p->p_bools.nprim ) { - printk("security: conditional expressions uses unknown bool.\n"); + printk("Flask: conditional expressions uses unknown bool.\n"); return 0; } return 1; @@ -489,6 +488,10 @@ 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++ ) { node = xmalloc(struct cond_node); diff --git a/xen/xsm/flask/ss/conditional.h b/xen/xsm/flask/ss/conditional.h --- a/xen/xsm/flask/ss/conditional.h +++ b/xen/xsm/flask/ss/conditional.h @@ -28,7 +28,7 @@ #define COND_XOR 5 /* bool ^ bool */ #define COND_EQ 6 /* bool == bool */ #define COND_NEQ 7 /* bool != bool */ -#define COND_LAST 8 +#define COND_LAST COND_NEQ __u32 expr_type; __u32 bool; struct cond_expr *next; diff --git a/xen/xsm/flask/ss/context.h b/xen/xsm/flask/ss/context.h --- a/xen/xsm/flask/ss/context.h +++ b/xen/xsm/flask/ss/context.h @@ -42,17 +42,40 @@ { int rc; - if (!flask_mls_enabled) + if ( !flask_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) + 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) + 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; + + if ( !flask_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[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; @@ -60,7 +83,7 @@ static inline int mls_context_cmp(struct context *c1, struct context *c2) { - if (!flask_mls_enabled) + if ( !flask_mls_enabled ) return 1; return ((c1->range.level[0].sens == c2->range.level[0].sens) && @@ -71,7 +94,7 @@ static inline void mls_context_destroy(struct context *c) { - if (!flask_mls_enabled) + if ( !flask_mls_enabled ) return; ebitmap_destroy(&c->range.level[0].cat); diff --git a/xen/xsm/flask/ss/ebitmap.c b/xen/xsm/flask/ss/ebitmap.c --- a/xen/xsm/flask/ss/ebitmap.c +++ b/xen/xsm/flask/ss/ebitmap.c @@ -3,6 +3,10 @@ * * Author : Stephen Smalley, <sds@epoch.ncsc.mil> */ +/* + * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com> + * Applied standard bit operations to improve bitmap scanning. + */ /* Ported to Xen 3.0, George Coker, <gscoker@alpha.ncsc.mil> */ @@ -11,6 +15,7 @@ #include <xen/xmalloc.h> #include <xen/errno.h> #include <xen/spinlock.h> +#include <xen/bitmap.h> #include "ebitmap.h" #include "policydb.h" @@ -23,7 +28,8 @@ n1 = e1->node; n2 = e2->node; - while ( n1 && n2 && (n1->startbit == n2->startbit) && (n1->map == n2->map) ) + while ( n1 && n2 && (n1->startbit == n2->startbit) && + !memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) { n1 = n1->next; n2 = n2->next; @@ -52,7 +58,7 @@ } memset(new, 0, sizeof(*new)); new->startbit = n->startbit; - new->map = n->map; + memcpy(new->maps, n->maps, EBITMAP_SIZE / 8); new->next = NULL; if ( prev ) prev->next = new; @@ -69,6 +75,7 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) { struct ebitmap_node *n1, *n2; + int i; if ( e1->highbit < e2->highbit ) return 0; @@ -82,8 +89,11 @@ n1 = n1->next; continue; } - if ( (n1->map & n2->map) != n2->map ) - return 0; + for ( i = 0; i < EBITMAP_UNIT_NUMS; i++ ) + { + if ( (n1->maps[i] & n2->maps[i]) != n2->maps[i] ) + return 0; + } n1 = n1->next; n2 = n2->next; @@ -105,13 +115,8 @@ 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; } @@ -126,37 +131,41 @@ 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 ) + 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 ) { - /* drop this node from the bitmap */ + /* + * this was the highest map + * within the bitmap + */ + if ( prev ) + e->highbit = prev->startbit + EBITMAP_SIZE; + else + e->highbit = 0; + } + if ( prev ) + prev->next = n->next; + else + e->node = n->next; - if ( !n->next ) - { - /* - * this was the highest map - * within the bitmap - */ - if ( prev ) - e->highbit = prev->startbit + MAPSIZE; - else - e->highbit = 0; - } - if ( prev ) - prev->next = n->next; - else - e->node = n->next; - - xfree(n); - } + xfree(n); } return 0; } @@ -172,12 +181,12 @@ return -ENOMEM; memset(new, 0, sizeof(*new)); - 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 ) { @@ -215,11 +224,11 @@ 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); @@ -227,99 +236,99 @@ 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 ) + if ( mapunit != sizeof(u64) * 8 ) { - printk(KERN_ERR "security: ebitmap: map size %u does not " - "match my size %Zd (high bit was %d)\n", mapsize, - MAPSIZE, e->highbit); + printk(KERN_ERR "Flask: ebitmap: map size %u does not " + "match my size %Zd (high bit was %d)\n", mapunit, + sizeof(u64) * 8, 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 "Flask: ebitmap: truncated map\n"); goto bad; } - n = xmalloc(struct ebitmap_node); - if ( !n ) + startbit = le32_to_cpu(startbit); + if ( startbit & (mapunit - 1) ) { - printk(KERN_ERR "security: ebitmap: out of memory\n"); - rc = -ENOMEM; + printk(KERN_ERR "Flask: ebitmap start bit (%d) is " + "not a multiple of the map unit size (%u)\n", + startbit, mapunit); goto bad; } - memset(n, 0, sizeof(*n)); + if ( startbit > e->highbit - mapunit ) + { + printk(KERN_ERR "Flask: ebitmap start bit (%d) is " + "beyond the end of the bitmap (%u)\n", + startbit, (e->highbit - mapunit)); + goto bad; + } - n->startbit = le32_to_cpu(buf[0]); + if ( !n || startbit >= n->startbit + EBITMAP_SIZE ) + { + struct ebitmap_node *tmp; + tmp = xmalloc(struct ebitmap_node); + if ( !tmp ) + { + printk(KERN_ERR + "Flask: ebitmap: out of memory\n"); + rc = -ENOMEM; + goto bad; + } + memset(tmp, 0, sizeof(*tmp)); + /* 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 "Flask: ebitmap: start bit %d" + " comes after start bit %d\n", + startbit, n->startbit); + goto bad; + } - 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 ( 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; - } rc = next_entry(&map, fp, sizeof(u64)); if ( rc < 0 ) { - printk(KERN_ERR "security: ebitmap: truncated map\n"); - goto bad_free; + printk(KERN_ERR "Flask: ebitmap: truncated map\n"); + goto bad; } - n->map = le64_to_cpu(map); + map = le64_to_cpu(map); - if ( !n->map ) + index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE; + while ( map ) { - printk(KERN_ERR "security: ebitmap: null map in " - "ebitmap (startbit %d)\n", n->startbit); - goto bad_free; + 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: - xfree(n); bad: if ( !rc ) rc = -EINVAL; diff --git a/xen/xsm/flask/ss/ebitmap.h b/xen/xsm/flask/ss/ebitmap.h --- a/xen/xsm/flask/ss/ebitmap.h +++ b/xen/xsm/flask/ss/ebitmap.h @@ -14,14 +14,20 @@ #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 <xen/bitmap.h> + +#define EBITMAP_UNIT_NUMS ((32 - 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 { @@ -32,11 +38,18 @@ #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,29 +57,66 @@ 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 ) + unsigned int ofs; + + ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1); + if ( ofs < EBITMAP_SIZE ) + return ofs + (*n)->startbit; + + for ( *n = (*n)->next; *n; *n = (*n)->next ) { - *n = (*n)->next; - return (*n)->startbit; + ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); + if ( ofs < EBITMAP_SIZE ) + return ofs + (*n)->startbit; } - - return (bit+1); + return ebitmap_length(e); } -static inline int ebitmap_node_get_bit(struct ebitmap_node * n, - unsigned int bit) +#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); diff --git a/xen/xsm/flask/ss/hashtab.c b/xen/xsm/flask/ss/hashtab.c --- a/xen/xsm/flask/ss/hashtab.c +++ b/xen/xsm/flask/ss/hashtab.c @@ -11,8 +11,10 @@ #include <xen/errno.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; @@ -26,7 +28,7 @@ p->nel = 0; p->hash_value = hash_value; p->keycmp = keycmp; - p->htable = (void *)xmalloc_array(struct hashtab_node, size); + p->htable = xmalloc_array(struct hashtab_node *, size); if ( p->htable == NULL ) { xfree(p); @@ -80,7 +82,7 @@ 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; diff --git a/xen/xsm/flask/ss/hashtab.h b/xen/xsm/flask/ss/hashtab.h --- a/xen/xsm/flask/ss/hashtab.h +++ b/xen/xsm/flask/ss/hashtab.h @@ -22,9 +22,9 @@ 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,8 +39,10 @@ * 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. @@ -58,7 +60,7 @@ * 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/xen/xsm/flask/ss/mls.c b/xen/xsm/flask/ss/mls.c --- a/xen/xsm/flask/ss/mls.c +++ b/xen/xsm/flask/ss/mls.c @@ -29,46 +29,49 @@ */ 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 (!flask_mls_enabled) + if ( !flask_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]); + int index_sens = context->range.level[l].sens; + len += strlen(policydb.p_sens_val_to_name[index_sens - 1]); - ebitmap_for_each_bit(&context->range.level[l].cat, node, i) + /* categories */ + head = -2; + prev = -2; + e = &context->range.level[l].cat; + ebitmap_for_each_positive_bit(e, node, i) { - if ( ebitmap_node_get_bit(node, i) ) + if ( i - prev > 1 ) { - if ( range ) + /* one or more negative bits are skipped */ + if ( head != prev ) { - range++; - continue; + nm = policydb.p_cat_val_to_name[prev]; + len += strlen(nm) + 1; } - - len += strlen(policydb.p_cat_val_to_name[i]) + 1; - range++; + nm = policydb.p_cat_val_to_name[i]; + len += strlen(nm) + 1; + head = i; } - else - { - if ( range > 1 ) - len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; - range = 0; - } + prev = i; } - /* 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 ( prev != head ) + { + nm = policydb.p_cat_val_to_name[prev]; + len += strlen(nm) + 1; + } if ( l == 0 ) { if ( mls_level_eq(&context->range.level[0], - &context->range.level[1]) ) + &context->range.level[1]) ) break; else len++; @@ -85,8 +88,9 @@ */ void mls_sid_to_context(struct context *context, 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 ( !flask_mls_enabled ) @@ -99,64 +103,51 @@ for ( l = 0; l < 2; l++ ) { - range = 0; - wrote_sep = 0; - strlcpy(scontextp, + memcpy(scontextp, policydb.p_sens_val_to_name[context->range.level[l].sens - 1], strlen(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]); + scontextp += strlen(scontextp); /* categories */ - ebitmap_for_each_bit(&context->range.level[l].cat, node, i) + head = -2; + prev = -2; + e = &context->range.level[l].cat; + ebitmap_for_each_positive_bit(e, node, i) { - if ( ebitmap_node_get_bit(node, i) ) + if ( i - prev > 1 ) { - if ( range ) + /* one or more negative bits are skipped */ + if ( prev != head ) { - range++; - continue; - } - - if ( !wrote_sep ) - { - *scontextp++ = '':''; - wrote_sep = 1; - } - else - *scontextp++ = '',''; - strlcpy(scontextp, policydb.p_cat_val_to_name[i], - strlen(policydb.p_cat_val_to_name[i])); - scontextp += strlen(policydb.p_cat_val_to_name[i]); - range++; - } - else - { - if ( range > 1 ) - { - if ( range > 2 ) + if ( prev - head > 1 ) *scontextp++ = ''.''; else *scontextp++ = '',''; - - strlcpy(scontextp, policydb.p_cat_val_to_name[i - 1], - strlen(policydb.p_cat_val_to_name[i - 1])); - scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); + nm = policydb.p_cat_val_to_name[prev]; + memcpy(scontextp, nm, strlen(nm)); + scontextp += strlen(nm); } - range = 0; + if ( prev < 0 ) + *scontextp++ = '':''; + else + *scontextp++ = '',''; + nm = policydb.p_cat_val_to_name[i]; + memcpy(scontextp, nm, strlen(nm)); + scontextp += strlen(nm); + head = i; } + prev = i; } - /* Handle case where last category is the end of range */ - if ( range > 1 ) + if ( prev != head ) { - if ( range > 2 ) + if ( prev - head > 1 ) *scontextp++ = ''.''; else *scontextp++ = '',''; - - strlcpy(scontextp, policydb.p_cat_val_to_name[i - 1], - strlen(policydb.p_cat_val_to_name[i - 1])); - scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); + nm = policydb.p_cat_val_to_name[prev]; + memcpy(scontextp, nm, strlen(nm)); + scontextp += strlen(nm); } if ( l == 0 ) @@ -176,55 +167,57 @@ return; } +int mls_level_isvalid(struct policydb *p, struct mls_level *l) +{ + struct level_datum *levdatum; + struct ebitmap_node *node; + int i; + + if ( !l->sens || l->sens > p->p_levels.nprim ) + return 0; + levdatum = hashtab_search(p->p_levels.table, + p->p_sens_val_to_name[l->sens - 1]); + if ( !levdatum ) + return 0; + + ebitmap_for_each_positive_bit(&l->cat, node, i) + { + if ( i > p->p_cats.nprim ) + return 0; + if ( !ebitmap_get_bit(&levdatum->level->cat, i) ) + { + /* + * Category may not be associated with + * sensitivity. + */ + return 0; + } + } + + return 1; +} + +int mls_range_isvalid(struct policydb *p, struct mls_range *r) +{ + return ( mls_level_isvalid(p, &r->level[0]) && + mls_level_isvalid(p, &r->level[1]) && + mls_level_dom(&r->level[1], &r->level[0])); +} + /* * Return 1 if the MLS fields in the security context * structure `c'' are valid. Return 0 otherwise. */ int mls_context_isvalid(struct policydb *p, struct context *c) { - struct level_datum *levdatum; struct user_datum *usrdatum; - struct ebitmap_node *node; - int i, l; if ( !flask_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; @@ -241,26 +234,6 @@ } /* - * 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 @@ -270,16 +243,11 @@ * This function modifies the string in place, inserting * NULL characters to terminate the MLS fields. * - * If a def_sid is provided and no MLS field is present, - * copy the MLS field of the associated default context. - * Used for upgraded to MLS systems where objects may lack - * MLS fields. - * * Policy read-lock must be held for sidtab lookup. * */ -int mls_context_to_sid(char oldc, char **scontext, struct context *context, - struct sidtab *s, u32 def_sid) +int mls_context_to_sid(char oldc, char **scontext, + struct context *context, struct sidtab *s) { char delim; @@ -292,23 +260,10 @@ return 0; /* - * No MLS component to the security context, try and map to - * default if provided. + * No MLS component to the security context -> error. */ if ( !oldc ) - { - struct context *defcon; - - if ( def_sid == SECSID_NULL ) - goto out; - - defcon = sidtab_search(s, def_sid); - if ( !defcon ) - goto out; - - rc = mls_copy_context(context, defcon); goto out; - } /* Extract low sensitivity. */ scontextp = p = *scontext; @@ -421,26 +376,6 @@ } /* - * Copies the effective MLS range from `src'' into `dst''. - */ -static inline int mls_scopy_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[0].sens; - rc = ebitmap_cpy(&dst->range.level[l].cat, - &src->range.level[0].cat); - if ( rc ) - break; - } - - return rc; -} - -/* * Copies the MLS range `range'' into `context''. */ static inline int mls_range_set(struct context *context, @@ -537,20 +472,17 @@ c->range.level[l].sens = levdatum->level->sens; ebitmap_init(&bitmap); - ebitmap_for_each_bit(&c->range.level[l].cat, node, i) + ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) { - if ( ebitmap_node_get_bit(node, i) ) - { - int rc; + 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; - } + 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_destroy(&c->range.level[l].cat); c->range.level[l].cat = bitmap; @@ -562,48 +494,37 @@ int mls_compute_sid(struct context *scontext, struct context *tcontext, u16 tclass, u32 specified, struct context *newcontext) { + struct range_trans *rtr; + if ( !flask_mls_enabled ) return 0; switch ( specified ) { case AVTAB_TRANSITION: - if ( tclass == SECCLASS_DOMAIN ) + /* Look for a range transition rule. */ + for (rtr = policydb.range_tr; rtr; rtr = rtr->next) { - struct range_trans *rangetr; - /* Look for a range transition rule. */ - for ( rangetr = policydb.range_tr; rangetr; - rangetr = rangetr->next) + if (rtr->source_type == scontext->type && + rtr->target_type == tcontext->type && + rtr->target_class == tclass) { - if ( rangetr->dom == scontext->type && - rangetr->type == tcontext->type) - { - /* Set the range from the rule */ - return mls_range_set(newcontext, &rangetr->range); - } + /* Set the range from the rule */ + return mls_range_set(newcontext, + &rtr->target_range); } } /* Fallthrough */ case AVTAB_CHANGE: if ( tclass == SECCLASS_DOMAIN ) /* 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_context_cpy_low(newcontext, scontext); + case AVTAB_MEMBER: /* Use the process effective MLS attributes. */ - return mls_scopy_context(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); - } + return mls_context_cpy_low(newcontext, scontext); default: return -EINVAL; } diff --git a/xen/xsm/flask/ss/mls.h b/xen/xsm/flask/ss/mls.h --- a/xen/xsm/flask/ss/mls.h +++ b/xen/xsm/flask/ss/mls.h @@ -8,7 +8,7 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. */ #ifndef _SS_MLS_H_ @@ -20,9 +20,11 @@ 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, struct context *context, - struct sidtab *s, u32 def_sid); + struct sidtab *s); int mls_convert_context(struct policydb *oldp, struct policydb *newp, struct context *context); diff --git a/xen/xsm/flask/ss/policydb.c b/xen/xsm/flask/ss/policydb.c --- a/xen/xsm/flask/ss/policydb.c +++ b/xen/xsm/flask/ss/policydb.c @@ -100,6 +100,26 @@ .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_RANGETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, + { + .version = POLICYDB_VERSION_POLCAP, + .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, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -188,6 +208,9 @@ if ( rc ) goto out_free_avtab; + ebitmap_init(&p->policycaps); + ebitmap_init(&p->permissive_map); + out: return rc; @@ -244,7 +267,9 @@ role = datum; p = datap; - if ( !role->value || role->value > p->p_roles.nprim ) + if ( !role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim ) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -261,9 +286,12 @@ if ( typdatum->primary ) { - if ( !typdatum->value || typdatum->value > p->p_types.nprim ) + if ( !typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim ) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -276,7 +304,9 @@ usrdatum = datum; p = datap; - if ( !usrdatum->value || usrdatum->value > p->p_users.nprim ) + if ( !usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim ) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -356,7 +386,7 @@ goto out; p->class_val_to_struct - (void *)xmalloc_array(struct class_datum, p->p_classes.nprim); + xmalloc_array(struct class_datum *, p->p_classes.nprim); if ( !p->class_val_to_struct ) { rc = -ENOMEM; @@ -404,14 +434,14 @@ { int i, rc = 0; - printk(KERN_INFO "security: %d users, %d roles, %d types, %d bools", + printk(KERN_INFO "Flask: %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 ( flask_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_INFO "Flask: %d classes, %d rules\n", p->p_classes.nprim, p->te_avtab.nel); #ifdef DEBUG_HASHES @@ -420,7 +450,7 @@ #endif p->role_val_to_struct - (void *)xmalloc_array(struct role_datum, p->p_roles.nprim); + xmalloc_array(struct role_datum *, p->p_roles.nprim); if ( !p->role_val_to_struct ) { rc = -ENOMEM; @@ -428,13 +458,21 @@ } p->user_val_to_struct - (void *)xmalloc_array(struct user_datum, p->p_users.nprim); + xmalloc_array(struct user_datum *, p->p_users.nprim); if ( !p->user_val_to_struct ) { rc = -ENOMEM; goto out; } + p->type_val_to_struct + xmalloc_array(struct type_datum *, p->p_types.nprim); + if ( !p->type_val_to_struct ) + { + rc = -ENOMEM; + goto out; + } + if ( cond_init_bool_indexes(p) ) { rc = -ENOMEM; @@ -627,6 +665,7 @@ xfree(p->class_val_to_struct); xfree(p->role_val_to_struct); xfree(p->user_val_to_struct); + xfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -639,6 +678,7 @@ c = c->next; ocontext_destroy(ctmp,i); } + p->ocontexts[i] = NULL; } cond_policydb_destroy(p); @@ -659,15 +699,29 @@ for ( rt = p->range_tr; rt; rt = rt -> next ) { - if ( lrt ) xfree(lrt); + if ( lrt ) + { + ebitmap_destroy(&lrt->target_range.level[0].cat); + ebitmap_destroy(&lrt->target_range.level[1].cat); + xfree(lrt); + } lrt = rt; } - if ( lrt ) xfree(lrt); + if ( lrt ) + { + ebitmap_destroy(&lrt->target_range.level[0].cat); + ebitmap_destroy(&lrt->target_range.level[1].cat); + xfree(lrt); + } - for ( i = 0; i < p->p_types.nprim; i++ ) - ebitmap_destroy(&p->type_attr_map[i]); + if ( p->type_attr_map ) + for ( i = 0; i < p->p_types.nprim; i++ ) + ebitmap_destroy(&p->type_attr_map[i]); xfree(p->type_attr_map); + ebitmap_destroy(&p->policycaps); + ebitmap_destroy(&p->permissive_map); + return; } @@ -683,7 +737,7 @@ rc = sidtab_init(s); if ( rc ) { - printk(KERN_ERR "security: out of memory on SID table init\n"); + printk(KERN_ERR "Flask: out of memory on SID table init\n"); goto out; } @@ -692,14 +746,14 @@ { if ( !c->context[0].user ) { - printk(KERN_ERR "security: SID %s was never " + printk(KERN_ERR "Flask: SID %s was never " "defined.\n", c->u.name); rc = -EINVAL; goto out; } if ( sidtab_insert(s, c->sid[0], &c->context[0]) ) { - printk(KERN_ERR "security: unable to load initial " + printk(KERN_ERR "Flask: unable to load initial " "SID %s.\n", c->u.name); rc = -EINVAL; goto out; @@ -709,6 +763,27 @@ 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. @@ -772,14 +847,14 @@ items = le32_to_cpu(buf[0]); if ( items > ARRAY_SIZE(buf) ) { - printk(KERN_ERR "security: mls: range overflow\n"); + printk(KERN_ERR "Flask: mls: range overflow\n"); rc = -EINVAL; goto out; } rc = next_entry(buf, fp, sizeof(u32) * items); if ( rc < 0 ) { - printk(KERN_ERR "security: mls: truncated range\n"); + printk(KERN_ERR "Flask: mls: truncated range\n"); goto out; } r->level[0].sens = le32_to_cpu(buf[0]); @@ -791,7 +866,7 @@ rc = ebitmap_read(&r->level[0].cat, fp); if ( rc ) { - printk(KERN_ERR "security: mls: error reading low " + printk(KERN_ERR "Flask: mls: error reading low " "categories\n"); goto out; } @@ -800,7 +875,7 @@ rc = ebitmap_read(&r->level[1].cat, fp); if ( rc ) { - printk(KERN_ERR "security: mls: error reading high " + printk(KERN_ERR "Flask: mls: error reading high " "categories\n"); goto bad_high; } @@ -810,7 +885,7 @@ 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 "Flask: mls: out of memory\n"); goto bad_high; } } @@ -836,7 +911,7 @@ rc = next_entry(buf, fp, sizeof buf); if ( rc < 0 ) { - printk(KERN_ERR "security: context truncated\n"); + printk(KERN_ERR "Flask: context truncated\n"); goto out; } c->user = le32_to_cpu(buf[0]); @@ -846,7 +921,7 @@ { if ( mls_read_range_helper(&c->range, fp) ) { - printk(KERN_ERR "security: error reading MLS range of " + printk(KERN_ERR "Flask: error reading MLS range of " "context\n"); rc = -EINVAL; goto out; @@ -855,7 +930,7 @@ if ( !policydb_context_isvalid(p, c) ) { - printk(KERN_ERR "security: invalid security context\n"); + printk(KERN_ERR "Flask: invalid security context\n"); context_destroy(c); rc = -EINVAL; } @@ -1121,7 +1196,7 @@ cladatum->comkey); if ( !cladatum->comdatum ) { - printk(KERN_ERR "security: unknown common %s\n", + printk(KERN_ERR "Flask: unknown common %s\n", cladatum->comkey); rc = -EINVAL; goto bad; @@ -1166,8 +1241,8 @@ { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = xmalloc(struct role_datum); @@ -1178,12 +1253,17 @@ } memset(role, 0, sizeof(*role)); - rc = next_entry(buf, fp, sizeof buf); + if ( p->policyvers >= POLICYDB_VERSION_BOUNDARY ) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if ( rc < 0 ) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if ( p->policyvers >= POLICYDB_VERSION_BOUNDARY ) + role->bounds = le32_to_cpu(buf[2]); key = xmalloc_array(char, len + 1); if ( !key ) @@ -1231,8 +1311,8 @@ { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = xmalloc(struct type_datum); @@ -1243,13 +1323,30 @@ } memset(typdatum, 0, sizeof(*typdatum)); - rc = next_entry(buf, fp, sizeof buf); + if ( p->policyvers >= POLICYDB_VERSION_BOUNDARY ) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if ( rc < 0 ) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if ( p->policyvers >= POLICYDB_VERSION_BOUNDARY ) + { + u32 prop = le32_to_cpu(buf[2]); + + if ( prop & TYPEDATUM_PROPERTY_PRIMARY ) + typdatum->primary = 1; + if ( prop & TYPEDATUM_PROPERTY_ATTRIBUTE ) + typdatum->attribute = 1; + + typdatum->bounds = le32_to_cpu(buf[3]); + } + else + { + typdatum->primary = le32_to_cpu(buf[2]); + } key = xmalloc_array(char, len + 1); if ( !key ) @@ -1287,14 +1384,14 @@ rc = next_entry(buf, fp, sizeof buf); if ( rc < 0 ) { - printk(KERN_ERR "security: mls: truncated level\n"); + printk(KERN_ERR "Flask: mls: truncated level\n"); goto bad; } lp->sens = le32_to_cpu(buf[0]); if ( ebitmap_read(&lp->cat, fp) ) { - printk(KERN_ERR "security: mls: error reading level categories\n"); + printk(KERN_ERR "Flask: mls: error reading level categories\n"); goto bad; } return 0; @@ -1307,8 +1404,8 @@ { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = xmalloc(struct user_datum); @@ -1319,12 +1416,17 @@ } memset(usrdatum, 0, sizeof(*usrdatum)); - rc = next_entry(buf, fp, sizeof buf); + if ( p->policyvers >= POLICYDB_VERSION_BOUNDARY ) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if ( rc < 0 ) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if ( p->policyvers >= POLICYDB_VERSION_BOUNDARY ) + usrdatum->bounds = le32_to_cpu(buf[2]); key = xmalloc_array(char, len + 1); if ( !key ) @@ -1475,6 +1577,142 @@ cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) + { + struct ebitmap_node *node; + unsigned long bit; + + if ( ++depth == POLICYDB_BOUNDS_MAXDEPTH ) + { + printk(KERN_ERR "Flask: 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 + "Flask: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) + { + struct ebitmap_node *node; + unsigned long bit; + + if ( ++depth == POLICYDB_BOUNDS_MAXDEPTH ) + { + printk(KERN_ERR "Flask: 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 + "Flask: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper, *type; + struct policydb *p = datap; + int depth = 0; + + upper = type = datum; + while (upper->bounds) + { + if ( ++depth == POLICYDB_BOUNDS_MAXDEPTH ) + { + printk(KERN_ERR "Flask: type %s: " + "too deep or looped boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if ( upper->attribute ) + { + printk(KERN_ERR "Flask: type %s: " + "bounded by attribute %s", + (char *) key, + p->p_type_val_to_name[upper->value - 1]); + return -EINVAL; + } + } + + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if ( p->policyvers < POLICYDB_VERSION_BOUNDARY ) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if ( rc ) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if ( rc ) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if ( rc ) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1505,7 +1743,7 @@ if ( le32_to_cpu(buf[0]) != POLICYDB_MAGIC ) { - printk(KERN_ERR "security: policydb magic number 0x%x does " + printk(KERN_ERR "Flask: policydb magic number 0x%x does " "not match expected magic number 0x%x\n", le32_to_cpu(buf[0]), POLICYDB_MAGIC); goto bad; @@ -1514,7 +1752,7 @@ len = le32_to_cpu(buf[1]); if ( len != strlen(POLICYDB_STRING) ) { - printk(KERN_ERR "security: policydb string length %d does not " + printk(KERN_ERR "Flask: policydb string length %d does not " "match expected length %lu\n", len, strlen(POLICYDB_STRING)); goto bad; @@ -1522,7 +1760,7 @@ policydb_str = xmalloc_array(char, len + 1); if ( !policydb_str ) { - printk(KERN_ERR "security: unable to allocate memory for policydb " + printk(KERN_ERR "Flask: unable to allocate memory for policydb " "string of length %d\n", len); rc = -ENOMEM; goto bad; @@ -1530,14 +1768,14 @@ rc = next_entry(policydb_str, fp, len); if ( rc < 0 ) { - printk(KERN_ERR "security: truncated policydb string identifier\n"); + printk(KERN_ERR "Flask: truncated policydb string identifier\n"); xfree(policydb_str); goto bad; } policydb_str[len] = 0; if ( strcmp(policydb_str, POLICYDB_STRING) ) { - printk(KERN_ERR "security: policydb string %s does not match " + printk(KERN_ERR "Flask: policydb string %s does not match " "my string %s\n", policydb_str, POLICYDB_STRING); xfree(policydb_str); goto bad; @@ -1555,7 +1793,7 @@ if ( p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX ) { - printk(KERN_ERR "security: policydb version %d does not match " + printk(KERN_ERR "Flask: 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; @@ -1589,20 +1827,28 @@ } } + if ( p->policyvers >= POLICYDB_VERSION_POLCAP && + ebitmap_read(&p->policycaps, fp) != 0 ) + goto bad; + + if ( p->policyvers >= POLICYDB_VERSION_PERMISSIVE && + ebitmap_read(&p->permissive_map, fp) != 0 ) + goto bad; + info = policydb_lookup_compat(p->policyvers); if ( !info ) { - printk(KERN_ERR "security: unable to find policy compat info " + printk(KERN_ERR "Flask: unable to find policy compat info " "for version %d\n", p->policyvers); goto bad; } if ( le32_to_cpu(buf[2]) != info->sym_num || - le32_to_cpu(buf[3]) != info->ocon_num ) + le32_to_cpu(buf[3]) != info->ocon_num ) { - printk(KERN_ERR "security: policydb table sizes (%d,%d) do " + printk(KERN_ERR "Flask: policydb table sizes (%d,%d) do " "not match mine (%d,%d)\n", le32_to_cpu(buf[2]), - le32_to_cpu(buf[3]), + le32_to_cpu(buf[3]), info->sym_num, info->ocon_num); goto bad; } @@ -1624,7 +1870,7 @@ p->symtab[i].nprim = nprim; } - rc = avtab_read(&p->te_avtab, fp, p->policyvers); + rc = avtab_read(&p->te_avtab, fp, p); if ( rc ) goto bad; @@ -1659,6 +1905,13 @@ tr->role = le32_to_cpu(buf[0]); tr->type = le32_to_cpu(buf[1]); tr->new_role = le32_to_cpu(buf[2]); + if ( !policydb_role_isvalid(p, tr->role) || + !policydb_type_isvalid(p, tr->type) || + !policydb_role_isvalid(p, tr->new_role) ) + { + rc = -EINVAL; + goto bad; + } ltr = tr; } @@ -1685,6 +1938,12 @@ goto bad; ra->role = le32_to_cpu(buf[0]); ra->new_role = le32_to_cpu(buf[1]); + if ( !policydb_role_isvalid(p, ra->role) || + !policydb_role_isvalid(p, ra->new_role) ) + { + rc = -EINVAL; + goto bad; + } lra = ra; } @@ -1720,15 +1979,20 @@ rc = -EINVAL; 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); - if ( rc ) - goto bad; + 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); + if ( rc ) + goto bad; break; + default: + printk(KERN_ERR + "Flask: unsupported object context config data\n"); + rc = -EINVAL; + goto bad; } } } @@ -1737,9 +2001,16 @@ if ( rc < 0 ) goto bad; nel = le32_to_cpu(buf[0]); + if ( nel ) + { + printk(KERN_ERR "Flask: unsupported genfs config data\n"); + rc = -EINVAL; + goto bad; + } if ( p->policyvers >= POLICYDB_VERSION_MLS ) { + int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS; rc = next_entry(buf, fp, sizeof(u32)); if ( rc < 0 ) goto bad; @@ -1761,11 +2032,31 @@ 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); + rt->source_type = le32_to_cpu(buf[0]); + rt->target_type = le32_to_cpu(buf[1]); + if ( new_rangetr ) + { + rc = next_entry(buf, fp, sizeof(u32)); + if ( rc < 0 ) + goto bad; + rt->target_class = le32_to_cpu(buf[0]); + } else + rt->target_class = SECCLASS_DOMAIN; + if ( !policydb_type_isvalid(p, rt->source_type) || + !policydb_type_isvalid(p, rt->target_type) || + !policydb_class_isvalid(p, rt->target_class) ) + { + rc = -EINVAL; + goto bad; + } + rc = mls_read_range_helper(&rt->target_range, fp); if ( rc ) goto bad; + if ( !mls_range_isvalid(p, &rt->target_range) ) + { + printk(KERN_WARNING "Flask: rangetrans: invalid range\n"); + goto bad; + } lrt = rt; } } @@ -1787,6 +2078,10 @@ goto bad; } + rc = policydb_bounds_sanity_check(p); + if ( rc ) + goto bad; + rc = 0; out: return rc; diff --git a/xen/xsm/flask/ss/policydb.h b/xen/xsm/flask/ss/policydb.h --- a/xen/xsm/flask/ss/policydb.h +++ b/xen/xsm/flask/ss/policydb.h @@ -63,6 +63,7 @@ /* 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 */ }; @@ -83,12 +84,25 @@ /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; +/* + * type_datum properties + * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY + */ +#define TYPEDATUM_PROPERTY_PRIMARY 0x0001 +#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 + +/* limitation of boundary depth */ +#define POLICYDB_BOUNDS_MAXDEPTH 4 + /* 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 */ @@ -108,9 +122,10 @@ }; struct range_trans { - u32 dom; /* current process domain */ - u32 type; /* program executable type */ - struct mls_range range; /* new range */ + u32 source_type; + u32 target_type; + u32 target_class; + struct mls_range target_range; struct range_trans *next; }; @@ -191,6 +206,7 @@ struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; @@ -218,12 +234,19 @@ /* type -> attribute reverse mapping */ struct ebitmap *type_attr_map; + struct ebitmap policycaps; + + struct ebitmap permissive_map; + unsigned int policyvers; }; extern void policydb_destroy(struct policydb *p); extern int policydb_load_isids(struct policydb *p, struct sidtab *s); extern int policydb_context_isvalid(struct policydb *p, struct context *c); +extern int policydb_class_isvalid(struct policydb *p, unsigned int class); +extern int policydb_type_isvalid(struct policydb *p, unsigned int type); +extern int policydb_role_isvalid(struct policydb *p, unsigned int role); extern int policydb_read(struct policydb *p, void *fp); #define PERM_SYMTAB_SIZE 32 diff --git a/xen/xsm/flask/ss/services.c b/xen/xsm/flask/ss/services.c --- a/xen/xsm/flask/ss/services.c +++ b/xen/xsm/flask/ss/services.c @@ -12,8 +12,22 @@ * * Added conditional policy language extensions * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. - * Copyright (C) 2003 - 2004 Tresys Technology, LLC + * Updated: Hewlett-Packard <paul.moore@hp.com> + * + * 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 + * + * 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 @@ -42,9 +56,9 @@ static DEFINE_RWLOCK(policy_rwlock); #define POLICY_RDLOCK read_lock(&policy_rwlock) -#define POLICY_WRLOCK write_lock_irq(&policy_rwlock) +#define POLICY_WRLOCK write_lock(&policy_rwlock) #define POLICY_RDUNLOCK read_unlock(&policy_rwlock) -#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock) +#define POLICY_WRUNLOCK write_unlock(&policy_rwlock) static DEFINE_SPINLOCK(load_sem); #define LOAD_LOCK spin_lock(&load_sem) @@ -66,6 +80,12 @@ static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static int context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd); + /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -259,12 +279,180 @@ } /* + * 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; + char *tclass_name; + char *scontext_name = NULL; + char *tcontext_name = NULL; + char *permission_names[32]; + int index; + u32 length; + unsigned char need_comma = 0; + + if ( !permissions ) + return; + + tclass_name = policydb.p_class_val_to_name[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; + + printk("Flask: 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; + + printk("%s%s", + need_comma ? "," : "", + permission_names[index] + ? permission_names[index] : "????"); + need_comma = 1; + } + printk("\n"); +out: + /* release scontext/tcontext */ + xfree(tcontext_name); + xfree(scontext_name); + + return; +} + +/* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void type_attribute_bounds_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd) +{ + struct context lo_scontext; + struct context lo_tcontext; + struct av_decision lo_avd; + struct type_datum *source + = policydb.type_val_to_struct[scontext->type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[tcontext->type - 1]; + u32 masked = 0; + + if ( source->bounds ) + { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); + lo_scontext.type = source->bounds; + + context_struct_compute_av(&lo_scontext, + tcontext, + tclass, + requested, + &lo_avd); + if ( (lo_avd.allowed & avd->allowed) == avd->allowed ) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if ( target->bounds ) + { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); + lo_tcontext.type = target->bounds; + + context_struct_compute_av(scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ( (lo_avd.allowed & avd->allowed) == avd->allowed ) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if ( source->bounds && target->bounds ) + { + memset(&lo_avd, 0, sizeof(lo_avd)); + /* + * lo_scontext and lo_tcontext are already + * set up. + */ + + context_struct_compute_av(&lo_scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ( (lo_avd.allowed & avd->allowed) == avd->allowed ) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if ( masked ) + { + /* 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 int context_struct_compute_av(struct context *scontext, - struct context *tcontext, u16 tclass, u32 requested, - struct av_decision *avd) + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd) { struct constraint_node *constraint; struct role_allow *ra; @@ -275,22 +463,22 @@ struct ebitmap_node *snode, *tnode; unsigned int i, j; - 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; + avd->flags = 0; + + /* + * We do not presently support policydb.handle_unknown == allow in Xen. + */ + if ( !tclass || tclass > policydb.p_classes.nprim ) + return -EINVAL; + + tclass_datum = policydb.class_val_to_struct[tclass - 1]; /* * If a specific type enforcement rule was defined for @@ -300,14 +488,10 @@ 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) + ebitmap_for_each_positive_bit(sattr, snode, i) { - if ( !ebitmap_node_get_bit(snode, i) ) - continue; - ebitmap_for_each_bit(tattr, tnode, j) + ebitmap_for_each_positive_bit(tattr, tnode, j) { - if ( !ebitmap_node_get_bit(tnode, j) ) - continue; avkey.source_type = i + 1; avkey.target_type = j + 1; for ( node = avtab_search_node(&policydb.te_avtab, &avkey); @@ -338,7 +522,7 @@ 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; } @@ -349,23 +533,25 @@ * pair. */ if ( tclass == SECCLASS_DOMAIN && -/* removed until future dynamic domain capability - (avd->allowed & (DOMAIN__TRANSITION | DOMAIN__DYNTRANSITION)) && -*/ - scontext->role != tcontext->role ) - { + (avd->allowed & DOMAIN__TRANSITION) && + scontext->role != tcontext->role ) + { for ( ra = policydb.role_allow; ra; ra = ra->next ) { if ( scontext->role == ra->role && tcontext->role == ra->new_role ) break; } -/* removed until future dynamic domain capability if (!ra) - avd->allowed = (avd->allowed) & ~(DOMAIN__TRANSITION | - DOMAIN__DYNTRANSITION); -*/ + avd->allowed &= ~DOMAIN__TRANSITION; } + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(scontext, tcontext, + tclass, requested, avd); return 0; } @@ -477,7 +663,7 @@ * if the access vector decisions were computed successfully. */ int security_compute_av(u32 ssid, u32 tsid, u16 tclass, u32 requested, - struct av_decision *avd) + struct av_decision *avd) { struct context *scontext = NULL, *tcontext = NULL; int rc = 0; @@ -485,7 +671,6 @@ if ( !ss_initialized ) { avd->allowed = 0xffffffff; - avd->decided = 0xffffffff; avd->auditallow = 0; avd->auditdeny = 0xffffffff; avd->seqno = latest_granting; @@ -510,6 +695,10 @@ } rc = context_struct_compute_av(scontext, tcontext, tclass, requested, avd); + + /* permissive domain? */ + if ( ebitmap_get_bit(&policydb.permissive_map, scontext->type) ) + avd->flags |= AVD_FLAGS_PERMISSIVE; out: POLICY_RDUNLOCK; return rc; @@ -611,7 +800,18 @@ } -static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid) +/** + * security_context_to_sid - Obtain a SID for a given security context. + * @scontext: security context + * @scontext_len: length in bytes + * @sid: security identifier, SID + * + * 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) { char *scontext2; struct context context; @@ -701,12 +901,12 @@ *p++ = 0; typdatum = hashtab_search(policydb.p_types.table, scontextp); - if ( !typdatum ) + if ( !typdatum || typdatum->attribute ) goto out_unlock; context.type = typdatum->value; - rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid); + rc = mls_context_to_sid(oldc, &p, &context, &sidtab); if ( rc ) goto out_unlock; @@ -732,45 +932,6 @@ return rc; } -/** - * security_context_to_sid - Obtain a SID for a given security context. - * @scontext: security context - * @scontext_len: length in bytes - * @sid: security identifier, SID - * - * 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) -{ - return security_context_to_sid_core(scontext, scontext_len, - sid, SECSID_NULL); -} - -/** - * security_context_to_sid_default - Obtain a SID for a given security context, - * falling back to specified default if needed. - * - * @scontext: security context - * @scontext_len: length in bytes - * @sid: security identifier, SID - * @def_sid: default SID to assign on errror - * - * 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). - * 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) -{ - return security_context_to_sid_core(scontext, scontext_len, sid, def_sid); -} - static int compute_sid_handle_invalid_context( struct context *scontext, struct context *tcontext, u16 tclass, struct context *newcontext) @@ -1001,88 +1162,128 @@ } /* - * Verify that each permission that is defined under the - * existing policy is still defined with the same value - * in the new policy. + * Verify that each kernel class that is defined in the + * policy is correct */ -static int validate_perm(void *key, void *datum, void *p) +static int validate_classes(struct policydb *p) { - struct hashtab *h; - struct perm_datum *perdatum, *perdatum2; - int rc = 0; + int i, j; + struct class_datum *cladatum; + struct perm_datum *perdatum; + u32 nprim, tmp, common_pts_len, perm_val, pol_val; + u16 class_val; + const struct selinux_class_perm *kdefs = &selinux_class_perm; + const char *def_class, *def_perm, *pol_class; + struct symtab *perms; - h = p; - perdatum = datum; - - perdatum2 = hashtab_search(h, key); - if ( !perdatum2 ) + for ( i = 1; i < kdefs->cts_len; i++ ) { - 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 ) + def_class = kdefs->class_to_string[i]; + if ( !def_class ) + continue; + if ( i > p->p_classes.nprim ) { - printk(" in the access vector definition for class %s\n", - (char *)key); - goto out; + printk(KERN_INFO + "Flask: class %s not defined in policy\n", + def_class); + return -EINVAL; + } + pol_class = p->p_class_val_to_name[i-1]; + if ( strcmp(pol_class, def_class) ) + { + printk(KERN_ERR + "Flask: class %d is incorrect, found %s but should be %s\n", + i, pol_class, def_class); + return -EINVAL; } } - 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; + for ( i = 0; i < kdefs->av_pts_len; i++ ) + { + class_val = kdefs->av_perm_to_string[i].tclass; + perm_val = kdefs->av_perm_to_string[i].value; + def_perm = kdefs->av_perm_to_string[i].name; + if ( class_val > p->p_classes.nprim ) + continue; + pol_class = p->p_class_val_to_name[class_val-1]; + cladatum = hashtab_search(p->p_classes.table, pol_class); + BUG_ON( !cladatum ); + perms = &cladatum->permissions; + nprim = 1 << (perms->nprim - 1); + if ( perm_val > nprim ) + { + printk(KERN_INFO + "Flask: permission %s in class %s not defined in policy\n", + def_perm, pol_class); + return -EINVAL; + } + perdatum = hashtab_search(perms->table, def_perm); + if ( perdatum == NULL ) + { + printk(KERN_ERR + "Flask: permission %s in class %s not found in policy\n", + def_perm, pol_class); + return -EINVAL; + } + pol_val = 1 << (perdatum->value - 1); + if ( pol_val != perm_val ) + { + printk(KERN_ERR + "Flask: permission %s in class %s has incorrect value\n", + def_perm, pol_class); + return -EINVAL; + } + } + for ( i = 0; i < kdefs->av_inherit_len; i++ ) + { + class_val = kdefs->av_inherit[i].tclass; + if ( class_val > p->p_classes.nprim ) + continue; + pol_class = p->p_class_val_to_name[class_val-1]; + cladatum = hashtab_search(p->p_classes.table, pol_class); + BUG_ON( !cladatum ); + if ( !cladatum->comdatum ) + { + printk(KERN_ERR + "Flask: class %s should have an inherits clause but does not\n", + pol_class); + return -EINVAL; + } + tmp = kdefs->av_inherit[i].common_base; + common_pts_len = 0; + while ( !(tmp & 0x01) ) + { + common_pts_len++; + tmp >>= 1; + } + perms = &cladatum->comdatum->permissions; + for ( j = 0; j < common_pts_len; j++ ) + { + def_perm = kdefs->av_inherit[i].common_pts[j]; + if ( j >= perms->nprim ) + { + printk(KERN_INFO + "Flask: permission %s in class %s not defined in policy\n", + def_perm, pol_class); + return -EINVAL; + } + perdatum = hashtab_search(perms->table, def_perm); + if ( perdatum == NULL ) + { + printk(KERN_ERR + "Flask: permission %s in class %s not found in policy\n", + def_perm, pol_class); + return -EINVAL; + } + if ( perdatum->value != j + 1 ) + { + printk(KERN_ERR + "Flask: permission %s in class %s has incorrect value\n", + def_perm, pol_class); + return -EINVAL; + } + } + } + return 0; } /* Clone the SID into the new SID table. */ @@ -1105,7 +1306,7 @@ u32 len; context_struct_to_string(context, &s, &len); - printk(KERN_ERR "security: context %s is invalid\n", s); + printk(KERN_ERR "Flask: context %s is invalid\n", s); xfree(s); } return rc; @@ -1184,12 +1385,12 @@ bad: context_struct_to_string(&oldc, &s, &len); context_destroy(&oldc); - printk(KERN_ERR "security: invalidating context %s\n", s); + printk(KERN_ERR "Flask: invalidating context %s\n", s); xfree(s); goto out; } -extern void flask_complete_init(void); +static int security_preserve_bools(struct policydb *p); /** * security_load_policy - Load a security policy configuration. @@ -1225,6 +1426,14 @@ policydb_destroy(&policydb); return -EINVAL; } + if ( validate_classes(&policydb) ) + { + printk(KERN_ERR + "Flask: the definition of a class is incorrect\n"); + sidtab_destroy(&sidtab); + policydb_destroy(&policydb); + return -EINVAL; + } policydb_loaded_version = policydb.policyvers; ss_initialized = 1; seqno = ++latest_granting; @@ -1245,15 +1454,22 @@ sidtab_init(&newsidtab); - /* Verify that the existing classes did not change. */ - if ( hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb) ) + /* Verify that the kernel defined classes are correct. */ + if ( validate_classes(&newpolicydb) ) { - printk(KERN_ERR "security: the definition of an existing " - "class changed\n"); + printk(KERN_ERR + "Flask: the definition of a class is incorrect\n"); rc = -EINVAL; goto err; } + rc = security_preserve_bools(&newpolicydb); + if ( rc ) + { + printk(KERN_ERR "Flask: unable to preserve booleans\n"); + goto err; + } + /* Clone the SID table. */ sidtab_shutdown(&sidtab); if ( sidtab_map(&sidtab, clone_sid, &newsidtab) ) @@ -1517,15 +1733,11 @@ } memset(mysids, 0, maxnel*sizeof(*mysids)); - ebitmap_for_each_bit(&user->roles, rnode, i) + ebitmap_for_each_positive_bit(&user->roles, rnode, i) { - if ( !ebitmap_node_get_bit(rnode, i) ) - continue; 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; + ebitmap_for_each_positive_bit(&role->types, tnode, j) { usercon.type = j+1; if ( mls_setup_user_range(fromcon, user, &usercon) ) @@ -1640,7 +1852,7 @@ goto out; } - printk(KERN_INFO "security: committed booleans { "); + printk(KERN_INFO "Flask: committed booleans { "); for ( i = 0; i < len; i++ ) { if ( values[i] ) @@ -1695,3 +1907,37 @@ POLICY_RDUNLOCK; 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++ ) + xfree(bnames[i]); + } + xfree(bnames); + xfree(bvalues); + return rc; +} diff --git a/xen/xsm/flask/ss/sidtab.c b/xen/xsm/flask/ss/sidtab.c --- a/xen/xsm/flask/ss/sidtab.c +++ b/xen/xsm/flask/ss/sidtab.c @@ -24,7 +24,7 @@ { int i; - s->htable = (void *)xmalloc_array(struct sidtab_node, SIDTAB_SIZE); + s->htable = xmalloc_array(struct sidtab_node *, SIDTAB_SIZE); if ( !s->htable ) return -ENOMEM; for ( i = 0; i < SIDTAB_SIZE; i++ ) diff --git a/xen/xsm/flask/ss/symtab.c b/xen/xsm/flask/ss/symtab.c --- a/xen/xsm/flask/ss/symtab.c +++ b/xen/xsm/flask/ss/symtab.c @@ -12,9 +12,9 @@ #include <xen/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; @@ -26,9 +26,9 @@ 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; -- Stephen Smalley National Security Agency _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2009-Aug-19 12:10 UTC
Re: [Xen-devel] [PATCH] xen/xsm/flask: Update to policy.24
On 18/08/2009 15:14, "Stephen Smalley" <sds@tycho.nsa.gov> wrote:> Update Xen Flask module to policy.24. > This is a back-port of the latest SELinux code to Xen, adjusted > for Xen coding style and interfaces. Unneeded functionality such > as most object context config data, handle_unknown, MLS field defaulting, > etc has been omitted. > > Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov> > Signed-off-by: George S. Coker, II <gscoker@alpha.ncsc.mil>The patch didn''t apply. Given that the affected source files have a thick layer of dust on them, it is probably because the patch got mangled somehow. Try re-sending as an attachment. -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel