From owner-trustedbsd-cvs@FreeBSD.ORG Tue Oct 31 14:28:13 2006 Return-Path: X-Original-To: trustedbsd-cvs@freebsd.org Delivered-To: trustedbsd-cvs@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 8345716A492 for ; Tue, 31 Oct 2006 14:28:13 +0000 (UTC) (envelope-from owner-perforce@freebsd.org) Received: from cyrus.watson.org (cyrus.watson.org [209.31.154.42]) by mx1.FreeBSD.org (Postfix) with ESMTP id CCBFC43D46 for ; Tue, 31 Oct 2006 14:28:02 +0000 (GMT) (envelope-from owner-perforce@freebsd.org) Received: from mx2.freebsd.org (mx2.freebsd.org [216.136.204.119]) by cyrus.watson.org (Postfix) with ESMTP id 6E7EE46D96 for ; Tue, 31 Oct 2006 09:28:01 -0500 (EST) Received: from hub.freebsd.org (hub.freebsd.org [216.136.204.18]) by mx2.freebsd.org (Postfix) with ESMTP id 94CFA58953; Tue, 31 Oct 2006 14:28:00 +0000 (GMT) (envelope-from owner-perforce@freebsd.org) Received: by hub.freebsd.org (Postfix, from userid 32767) id 85B4716A50C; Tue, 31 Oct 2006 14:28:00 +0000 (UTC) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 5E58716A47E for ; Tue, 31 Oct 2006 14:28:00 +0000 (UTC) (envelope-from millert@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id A072C43D55 for ; Tue, 31 Oct 2006 14:27:47 +0000 (GMT) (envelope-from millert@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id k9VERebr015425 for ; Tue, 31 Oct 2006 14:27:40 GMT (envelope-from millert@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id k9VERdtX015416 for perforce@freebsd.org; Tue, 31 Oct 2006 14:27:39 GMT (envelope-from millert@freebsd.org) Date: Tue, 31 Oct 2006 14:27:39 GMT Message-Id: <200610311427.k9VERdtX015416@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to millert@freebsd.org using -f From: Todd Miller To: Perforce Change Reviews Cc: Subject: PERFORCE change 108822 for review X-BeenThere: trustedbsd-cvs@FreeBSD.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: TrustedBSD CVS and Perforce commit message list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 31 Oct 2006 14:28:13 -0000 http://perforce.freebsd.org/chv.cgi?CH=108822 Change 108822 by millert@millert_macbook on 2006/10/31 14:27:28 Update to latest selinux kernel code, binary policy version 21. Adds support for range transitions on object classes. We don't compile linux-specific netlabel or audit code. Based on kernel/git/jmorris/selinux-2.6.20.git as of 26 Oct. 2006. Affected files ... .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/avc/avc-selinux.c#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/avc/avc.c#5 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/linux-compat.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/sebsd_alloc.c#3 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/sebsd_alloc.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/context.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/ebitmap.c#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/ebitmap.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/init.c#3 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/mls.c#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/mls.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/policydb.c#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/policydb.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/security.h#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/services.c#2 edit .. //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/ss/services.h#3 edit Differences ... ==== //depot/projects/trustedbsd/sedarwin8/policies/sedarwin/sedarwin/avc/avc-selinux.c#2 (text+ko) ==== @@ -4,6 +4,9 @@ * Authors: Stephen Smalley, * James Morris * + * Update: KaiGai, Kohei + * Replaced the avc_lock spinlock by RCU. + * * Copyright (C) 2003 Red Hat, Inc., James Morris * * This program is free software; you can redistribute it and/or modify @@ -18,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -27,35 +31,77 @@ #include #include "avc.h" #include "avc_ss.h" -#ifdef CONFIG_AUDIT + +static const struct av_perm_to_string +{ + u16 tclass; + u32 value; + const char *name; +} av_perm_to_string[] = { +#define S_(c, v, s) { c, v, s }, +#include "av_perm_to_string.h" +#undef S_ +}; + +static const char *class_to_string[] = { +#define S_(s) s, #include "class_to_string.h" -#endif +#undef S_ +}; + +#define TB_(s) static const char * s [] = { +#define TE_(s) }; +#define S_(s) s, #include "common_perm_to_string.h" +#undef TB_ +#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 }, #include "av_inherit.h" -#include "av_perm_to_string.h" -#include "objsec.h" +#undef S_ +}; + +#define AVC_CACHE_SLOTS 512 +#define AVC_DEF_CACHE_THRESHOLD 512 +#define AVC_CACHE_RECLAIM 16 -#define AVC_CACHE_SLOTS 512 -#define AVC_CACHE_MAXNODES 410 +#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS +#define avc_cache_stats_incr(field) \ +do { \ + per_cpu(avc_cache_stats, get_cpu()).field++; \ + put_cpu(); \ +} while (0) +#else +#define avc_cache_stats_incr(field) do {} while (0) +#endif struct avc_entry { u32 ssid; u32 tsid; u16 tclass; struct av_decision avd; - int used; /* used recently */ + atomic_t used; /* used recently */ }; struct avc_node { struct avc_entry ae; - struct avc_node *next; + struct list_head list; + struct rcu_head rhead; }; struct avc_cache { - struct avc_node *slots[AVC_CACHE_SLOTS]; - u32 lru_hint; /* LRU hint for reclaim scan */ - u32 active_nodes; - u32 latest_notif; /* latest revocation notification */ + struct list_head slots[AVC_CACHE_SLOTS]; + spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ + atomic_t lru_hint; /* LRU hint for reclaim scan */ + atomic_t active_nodes; + u32 latest_notif; /* latest revocation notification */ }; struct avc_callback_node { @@ -70,43 +116,30 @@ struct avc_callback_node *next; }; -static spinlock_t avc_lock = SPIN_LOCK_UNLOCKED; -static struct avc_node *avc_node_freelist; +/* Exported via selinufs */ +unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD; + +#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS +DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; +#endif + static struct avc_cache avc_cache; -static unsigned avc_cache_stats[AVC_NSTATS]; static struct avc_callback_node *avc_callbacks; +static kmem_cache_t *avc_node_cachep; static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) { return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1); } -#ifdef AVC_CACHE_STATS -static inline void avc_cache_stats_incr(int type) -{ - avc_cache_stats[type]++; -} - -static inline void avc_cache_stats_add(int type, unsigned val) -{ - avc_cache_stats[type] += val; -} -#else -static inline void avc_cache_stats_incr(int type) -{ } - -static inline void avc_cache_stats_add(int type, unsigned val) -{ } -#endif - /** * avc_dump_av - Display an access vector in human-readable form. * @tclass: target security class * @av: access vector */ -void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) +static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) { - char **common_pts = NULL; + const char **common_pts = NULL; u32 common_base = 0; int i, i2, perm; @@ -127,8 +160,10 @@ i = 0; perm = 1; while (perm < common_base) { - if (perm & av) + if (perm & av) { audit_log_format(ab, " %s", common_pts[i]); + av &= ~perm; + } i++; perm <<= 1; } @@ -140,14 +175,19 @@ (av_perm_to_string[i2].value == perm)) break; } - if (i2 < ARRAY_SIZE(av_perm_to_string)) + if (i2 < ARRAY_SIZE(av_perm_to_string)) { audit_log_format(ab, " %s", av_perm_to_string[i2].name); + av &= ~perm; + } } i++; perm <<= 1; } + if (av) + audit_log_format(ab, " 0x%x", av); + audit_log_format(ab, " }"); } @@ -157,7 +197,7 @@ * @tsid: target security identifier * @tclass: target security class */ -void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass) +static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass) { int rc; char *scontext; @@ -188,155 +228,161 @@ */ void __init avc_init(void) { - struct avc_node *new; int i; - for (i = 0; i < AVC_CACHE_MAXNODES; i++) { - new = kmalloc(sizeof(*new), GFP_ATOMIC); - if (!new) { - printk(KERN_WARNING "avc: only able to allocate " - "%d entries\n", i); - break; - } - memset(new, 0, sizeof(*new)); - new->next = avc_node_freelist; - avc_node_freelist = new; + for (i = 0; i < AVC_CACHE_SLOTS; i++) { + INIT_LIST_HEAD(&avc_cache.slots[i]); + spin_lock_init(&avc_cache.slots_lock[i]); } + atomic_set(&avc_cache.active_nodes, 0); + atomic_set(&avc_cache.lru_hint, 0); - audit_log(current->audit_context, "AVC INITIALIZED\n"); + avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), + 0, SLAB_PANIC, NULL, NULL); + + audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); } -#if 0 -static void avc_hash_eval(char *tag) +int avc_get_hash_stats(char *page) { int i, chain_len, max_chain_len, slots_used; struct avc_node *node; - unsigned long flags; - spin_lock_irqsave(&avc_lock,flags); + rcu_read_lock(); slots_used = 0; max_chain_len = 0; for (i = 0; i < AVC_CACHE_SLOTS; i++) { - node = avc_cache.slots[i]; - if (node) { + if (!list_empty(&avc_cache.slots[i])) { slots_used++; chain_len = 0; - while (node) { + list_for_each_entry_rcu(node, &avc_cache.slots[i], list) chain_len++; - node = node->next; - } if (chain_len > max_chain_len) max_chain_len = chain_len; } } - spin_unlock_irqrestore(&avc_lock,flags); + rcu_read_unlock(); - printk(KERN_INFO "\n"); - printk(KERN_INFO "%s avc: %d entries and %d/%d buckets used, longest " - "chain length %d\n", tag, avc_cache.active_nodes, slots_used, - AVC_CACHE_SLOTS, max_chain_len); + return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n" + "longest chain: %d\n", + atomic_read(&avc_cache.active_nodes), + slots_used, AVC_CACHE_SLOTS, max_chain_len); } -#else -static inline void avc_hash_eval(char *tag) -{ } -#endif -static inline struct avc_node *avc_reclaim_node(void) +static void avc_node_free(struct rcu_head *rhead) { - struct avc_node *prev, *cur; - int hvalue, try; + struct avc_node *node = container_of(rhead, struct avc_node, rhead); + kmem_cache_free(avc_node_cachep, node); + avc_cache_stats_incr(frees); +} - hvalue = avc_cache.lru_hint; - for (try = 0; try < 2; try++) { - do { - prev = NULL; - cur = avc_cache.slots[hvalue]; - while (cur) { - if (!cur->ae.used) - goto found; +static void avc_node_delete(struct avc_node *node) +{ + list_del_rcu(&node->list); + call_rcu(&node->rhead, avc_node_free); + atomic_dec(&avc_cache.active_nodes); +} - cur->ae.used = 0; +static void avc_node_kill(struct avc_node *node) +{ + kmem_cache_free(avc_node_cachep, node); + avc_cache_stats_incr(frees); + atomic_dec(&avc_cache.active_nodes); +} - prev = cur; - cur = cur->next; - } - hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1); - } while (hvalue != avc_cache.lru_hint); - } +static void avc_node_replace(struct avc_node *new, struct avc_node *old) +{ + list_replace_rcu(&old->list, &new->list); + call_rcu(&old->rhead, avc_node_free); + atomic_dec(&avc_cache.active_nodes); +} - panic("avc_reclaim_node"); +static inline int avc_reclaim_node(void) +{ + struct avc_node *node; + int hvalue, try, ecx; + unsigned long flags; -found: - avc_cache.lru_hint = hvalue; + for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) { + hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1); - if (prev == NULL) - avc_cache.slots[hvalue] = cur->next; - else - prev->next = cur->next; + if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags)) + continue; - return cur; + list_for_each_entry(node, &avc_cache.slots[hvalue], 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; + } + } + } + spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); + } +out: + return ecx; } -static inline struct avc_node *avc_claim_node(u32 ssid, - u32 tsid, u16 tclass) +static struct avc_node *avc_alloc_node(void) { - struct avc_node *new; - int hvalue; + struct avc_node *node; + + node = kmem_cache_alloc(avc_node_cachep, SLAB_ATOMIC); + if (!node) + goto out; - hvalue = avc_hash(ssid, tsid, tclass); - if (avc_node_freelist) { - new = avc_node_freelist; - avc_node_freelist = avc_node_freelist->next; - avc_cache.active_nodes++; - } else { - new = avc_reclaim_node(); - if (!new) - goto out; - } + memset(node, 0, sizeof(*node)); + INIT_RCU_HEAD(&node->rhead); + INIT_LIST_HEAD(&node->list); + atomic_set(&node->ae.used, 1); + avc_cache_stats_incr(allocations); - new->ae.used = 1; - new->ae.ssid = ssid; - new->ae.tsid = tsid; - new->ae.tclass = tclass; - new->next = avc_cache.slots[hvalue]; - avc_cache.slots[hvalue] = new; + if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold) + avc_reclaim_node(); out: - return new; + return node; +} + +static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) +{ + node->ae.ssid = ssid; + node->ae.tsid = tsid; + node->ae.tclass = tclass; + memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd)); } -static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, - u16 tclass, int *probes) +static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) { - struct avc_node *cur; + struct avc_node *node, *ret = NULL; int hvalue; - int tprobes = 1; hvalue = avc_hash(ssid, tsid, tclass); - cur = avc_cache.slots[hvalue]; - while (cur != NULL && - (ssid != cur->ae.ssid || - tclass != cur->ae.tclass || - tsid != cur->ae.tsid)) { - tprobes++; - cur = cur->next; + list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) { + if (ssid == node->ae.ssid && + tclass == node->ae.tclass && + tsid == node->ae.tsid) { + ret = node; + break; + } } - if (cur == NULL) { + if (ret == NULL) { /* cache miss */ goto out; } /* cache hit */ - if (probes) - *probes = tprobes; - - cur->ae.used = 1; - + if (atomic_read(&ret->ae.used) != 1) + atomic_set(&ret->ae.used, 1); out: - return cur; + return ret; } /** @@ -345,36 +391,52 @@ * @tsid: target security identifier * @tclass: target security class * @requested: requested permissions, interpreted based on @tclass - * @aeref: AVC entry reference * * Look up an AVC entry that is valid for the * @requested permissions between the SID pair * (@ssid, @tsid), interpreting the permissions * based on @tclass. If a valid AVC entry exists, - * then this function updates @aeref to refer to the - * entry and returns %0. Otherwise, this function - * returns -%ENOENT. + * then this function return the avc_node. + * Otherwise, this function returns NULL. */ -int avc_lookup(u32 ssid, u32 tsid, u16 tclass, - u32 requested, struct avc_entry_ref *aeref) +static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested) { struct avc_node *node; - int probes, rc = 0; - avc_cache_stats_incr(AVC_CAV_LOOKUPS); - node = avc_search_node(ssid, tsid, tclass,&probes); + avc_cache_stats_incr(lookups); + node = avc_search_node(ssid, tsid, tclass); if (node && ((node->ae.avd.decided & requested) == requested)) { - avc_cache_stats_incr(AVC_CAV_HITS); - avc_cache_stats_add(AVC_CAV_PROBES,probes); - aeref->ae = &node->ae; + avc_cache_stats_incr(hits); goto out; } - avc_cache_stats_incr(AVC_CAV_MISSES); - rc = -ENOENT; + node = NULL; + avc_cache_stats_incr(misses); out: - return rc; + return node; +} + +static int avc_latest_notif_update(int seqno, int is_insert) +{ + int ret = 0; + static DEFINE_SPINLOCK(notif_lock); + unsigned long flag; + + spin_lock_irqsave(¬if_lock, flag); + if (is_insert) { + if (seqno < avc_cache.latest_notif) { + printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n", + seqno, avc_cache.latest_notif); + ret = -EAGAIN; + } + } else { + if (seqno > avc_cache.latest_notif) + avc_cache.latest_notif = seqno; + } + spin_unlock_irqrestore(¬if_lock, flag); + + return ret; } /** @@ -383,7 +445,6 @@ * @tsid: target security identifier * @tclass: target security class * @ae: AVC entry - * @aeref: AVC entry reference * * Insert an AVC entry for the SID pair * (@ssid, @tsid) and class @tclass. @@ -392,55 +453,55 @@ * response to a security_compute_av() call. If the * sequence number @ae->avd.seqno is not less than the latest * revocation notification, then the function copies - * the access vectors into a cache entry, updates - * @aeref to refer to the entry, and returns %0. - * Otherwise, this function returns -%EAGAIN. + * the access vectors into a cache entry, returns + * avc_node inserted. Otherwise, this function returns NULL. */ -int avc_insert(u32 ssid, u32 tsid, u16 tclass, - struct avc_entry *ae, struct avc_entry_ref *aeref) +static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) { - struct avc_node *node; - int rc = 0; + struct avc_node *pos, *node = NULL; + int hvalue; + unsigned long flag; - if (ae->avd.seqno < avc_cache.latest_notif) { - printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n", - ae->avd.seqno, avc_cache.latest_notif); - rc = -EAGAIN; + if (avc_latest_notif_update(ae->avd.seqno, 1)) goto out; - } + + node = avc_alloc_node(); + if (node) { + hvalue = avc_hash(ssid, tsid, tclass); + avc_node_populate(node, ssid, tsid, tclass, ae); - node = avc_claim_node(ssid, tsid, tclass); - if (!node) { - rc = -ENOMEM; - goto out; + spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); + list_for_each_entry(pos, &avc_cache.slots[hvalue], list) { + 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]); +found: + spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); } - - node->ae.avd.allowed = ae->avd.allowed; - node->ae.avd.decided = ae->avd.decided; - node->ae.avd.auditallow = ae->avd.auditallow; - node->ae.avd.auditdeny = ae->avd.auditdeny; - node->ae.avd.seqno = ae->avd.seqno; - aeref->ae = &node->ae; out: - return rc; + return node; } static inline void avc_print_ipv6_addr(struct audit_buffer *ab, - struct in6_addr *addr, u16 port, + struct in6_addr *addr, __be16 port, char *name1, char *name2) { if (!ipv6_addr_any(addr)) - audit_log_format(ab, " %s=%04x:%04x:%04x:%04x:%04x:" - "%04x:%04x:%04x", name1, NIP6(*addr)); + audit_log_format(ab, " %s=" NIP6_FMT, name1, NIP6(*addr)); if (port) audit_log_format(ab, " %s=%d", name2, ntohs(port)); } static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr, - u16 port, char *name1, char *name2) + __be16 port, char *name1, char *name2) { if (addr) - audit_log_format(ab, " %s=%d.%d.%d.%d", name1, NIPQUAD(addr)); + audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr)); if (port) audit_log_format(ab, " %s=%d", name2, ntohs(port)); } @@ -486,7 +547,7 @@ return; } - ab = audit_log_start(current->audit_context); + ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); @@ -495,33 +556,8 @@ if (a && a->tsk) tsk = a->tsk; if (tsk && tsk->pid) { - struct mm_struct *mm; - struct vm_area_struct *vma; - audit_log_format(ab, " pid=%d", tsk->pid); - if (tsk == current) - mm = current->mm; - else - mm = get_task_mm(tsk); - if (mm) { - if (down_read_trylock(&mm->mmap_sem)) { - vma = mm->mmap; - while (vma) { - if ((vma->vm_flags & VM_EXECUTABLE) && - vma->vm_file) { - audit_log_d_path(ab, "exe=", - vma->vm_file->f_dentry, - vma->vm_file->f_vfsmnt); - break; - } - vma = vma->vm_next; - } - up_read(&mm->mmap_sem); - } - if (tsk != current) - mmput(mm); - } else { - audit_log_format(ab, " comm=%s", tsk->comm); - } + audit_log_format(ab, " pid=%d comm=", tsk->pid); + audit_log_untrustedstring(ab, tsk->comm); } if (a) { switch (a->type) { @@ -534,21 +570,18 @@ case AVC_AUDIT_DATA_FS: if (a->u.fs.dentry) { struct dentry *dentry = a->u.fs.dentry; - if (a->u.fs.mnt) { - audit_log_d_path(ab, "path=", dentry, - a->u.fs.mnt); - } else { - audit_log_format(ab, " name=%s", - dentry->d_name.name); - } + if (a->u.fs.mnt) + audit_avc_path(dentry, a->u.fs.mnt); + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, dentry->d_name.name); inode = dentry->d_inode; } else if (a->u.fs.inode) { struct dentry *dentry; inode = a->u.fs.inode; dentry = d_find_alias(inode); if (dentry) { - audit_log_format(ab, " name=%s", - dentry->d_name.name); + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, dentry->d_name.name); dput(dentry); } } @@ -566,7 +599,7 @@ switch (sk->sk_family) { case AF_INET: { - struct inet_opt *inet = inet_sk(sk); + struct inet_sock *inet = inet_sk(sk); avc_print_ipv4_addr(ab, inet->rcv_saddr, inet->sport, @@ -577,7 +610,7 @@ break; } case AF_INET6: { - struct inet_opt *inet = inet_sk(sk); + struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *inet6 = inet6_sk(sk); avc_print_ipv6_addr(ab, &inet6->rcv_saddr, @@ -591,22 +624,20 @@ case AF_UNIX: u = unix_sk(sk); if (u->dentry) { - audit_log_d_path(ab, "path=", - u->dentry, u->mnt); + audit_avc_path(u->dentry, u->mnt); + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, u->dentry->d_name.name); break; } if (!u->addr) break; len = u->addr->len-sizeof(short); p = &u->addr->name->sun_path[0]; + audit_log_format(ab, " path="); if (*p) - audit_log_format(ab, - "path=%*.*s", len, - len, p); + audit_log_untrustedstring(ab, p); else - audit_log_format(ab, - "path=@%*.*s", len-1, - len-1, p+1); + audit_log_hex(ab, p, len); break; } } @@ -686,8 +717,54 @@ return (x == y || x == SECSID_WILD || y == SECSID_WILD); } -static inline void avc_update_node(u32 event, struct avc_node *node, u32 perms) +/** + * avc_update_node Update an AVC entry + * @event : Updating event + * @perms : Permission mask bits + * @ssid,@tsid,@tclass : identifier of an AVC entry + * + * if a valid AVC entry doesn't exist,this function returns -ENOENT. + * if kmalloc() called internal returns NULL, this function returns -ENOMEM. + * 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) { + int hvalue, rc = 0; + unsigned long flag; + struct avc_node *pos, *node, *orig = NULL; + + node = avc_alloc_node(); + if (!node) { + rc = -ENOMEM; + goto out; + } + + /* Lock the target slot */ + 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){ + if ( ssid==pos->ae.ssid && + tsid==pos->ae.tsid && + tclass==pos->ae.tclass ){ + orig = pos; + break; + } + } + + if (!orig) { + rc = -ENOENT; + avc_node_kill(node); + goto out_unlock; + } + + /* + * Copy and replace original node. + */ + + avc_node_populate(node, ssid, tsid, tclass, &orig->ae); + switch (event) { case AVC_CALLBACK_GRANT: node->ae.avd.allowed |= perms; @@ -709,255 +786,58 @@ node->ae.avd.auditdeny &= ~perms; break; } -} - -static int avc_update_cache(u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms) -{ - struct avc_node *node; - int i; - unsigned long flags; - - spin_lock_irqsave(&avc_lock,flags); - - if (ssid == SECSID_WILD || tsid == SECSID_WILD) { - /* apply to all matching nodes */ - for (i = 0; i < AVC_CACHE_SLOTS; i++) { - for (node = avc_cache.slots[i]; node; - node = node->next) { - if (avc_sidcmp(ssid, node->ae.ssid) && - avc_sidcmp(tsid, node->ae.tsid) && - tclass == node->ae.tclass) { - avc_update_node(event,node,perms); - } - } - } - } else { - /* apply to one node */ - node = avc_search_node(ssid, tsid, tclass, NULL); - if (node) { - avc_update_node(event,node,perms); - } - } - - spin_unlock_irqrestore(&avc_lock,flags); - - return 0; -} - -static int avc_control(u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms, - u32 seqno, u32 *out_retained) -{ - struct avc_callback_node *c; - u32 tretained = 0, cretained = 0; - int rc = 0; - unsigned long flags; - - /* - * try_revoke only removes permissions from the cache - * state if they are not retained by the object manager. - * Hence, try_revoke must wait until after the callbacks have - * been invoked to update the cache state. - */ - if (event != AVC_CALLBACK_TRY_REVOKE) - avc_update_cache(event,ssid,tsid,tclass,perms); - - for (c = avc_callbacks; c; c = c->next) - { - if ((c->events & event) && - avc_sidcmp(c->ssid, ssid) && - avc_sidcmp(c->tsid, tsid) && - c->tclass == tclass && - (c->perms & perms)) { - cretained = 0; - rc = c->callback(event, ssid, tsid, tclass, - (c->perms & perms), - &cretained); - if (rc) - goto out; - tretained |= cretained; - } - } - - if (event == AVC_CALLBACK_TRY_REVOKE) { - /* revoke any unretained permissions */ - perms &= ~tretained; - avc_update_cache(event,ssid,tsid,tclass,perms); - *out_retained = tretained; - } - - spin_lock_irqsave(&avc_lock,flags); - if (seqno > avc_cache.latest_notif) - avc_cache.latest_notif = seqno; - spin_unlock_irqrestore(&avc_lock,flags); - + avc_node_replace(node, orig); +out_unlock: + spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); out: return rc; } /** - * avc_ss_grant - Grant previously denied permissions. - * @ssid: source security identifier or %SECSID_WILD - * @tsid: target security identifier or %SECSID_WILD - * @tclass: target security class - * @perms: permissions to grant - * @seqno: policy sequence number - */ -int avc_ss_grant(u32 ssid, u32 tsid, u16 tclass, - u32 perms, u32 seqno) -{ - return avc_control(AVC_CALLBACK_GRANT, - ssid, tsid, tclass, perms, seqno, NULL); -} - -/** - * avc_ss_try_revoke - Try to revoke previously granted permissions. - * @ssid: source security identifier or %SECSID_WILD - * @tsid: target security identifier or %SECSID_WILD - * @tclass: target security class - * @perms: permissions to grant - * @seqno: policy sequence number - * @out_retained: subset of @perms that are retained - * - * Try to revoke previously granted permissions, but - * only if they are not retained as migrated permissions. - * Return the subset of permissions that are retained via @out_retained. - */ -int avc_ss_try_revoke(u32 ssid, u32 tsid, u16 tclass, - u32 perms, u32 seqno, u32 *out_retained) -{ - return avc_control(AVC_CALLBACK_TRY_REVOKE, - ssid, tsid, tclass, perms, seqno, out_retained); -} - -/** - * avc_ss_revoke - Revoke previously granted permissions. - * @ssid: source security identifier or %SECSID_WILD - * @tsid: target security identifier or %SECSID_WILD - * @tclass: target security class - * @perms: permissions to grant - * @seqno: policy sequence number - * - * Revoke previously granted permissions, even if - * they are retained as migrated permissions. - */ -int avc_ss_revoke(u32 ssid, u32 tsid, u16 tclass, - u32 perms, u32 seqno) -{ - return avc_control(AVC_CALLBACK_REVOKE, - ssid, tsid, tclass, perms, seqno, NULL); -} - -/** * avc_ss_reset - Flush the cache and revalidate migrated permissions. * @seqno: policy sequence number */ int avc_ss_reset(u32 seqno) { struct avc_callback_node *c; - int i, rc = 0; - struct avc_node *node, *tmp; - unsigned long flags; - - avc_hash_eval("reset"); + int i, rc = 0, tmprc; + unsigned long flag; + struct avc_node *node; - spin_lock_irqsave(&avc_lock,flags); - for (i = 0; i < AVC_CACHE_SLOTS; i++) { - node = avc_cache.slots[i]; - while (node) { - tmp = node; - node = node->next; - tmp->ae.ssid = tmp->ae.tsid = SECSID_NULL; - tmp->ae.tclass = SECCLASS_NULL; >>> TRUNCATED FOR MAIL (1000 lines) <<<