GIT f10b7468c250735642245e0f46205d26448f9aa5 git://git.infradead.org/users/jmorris/selinux-2.6.git commit Author: Chad Sellers Date: Fri Oct 6 16:09:52 2006 -0400 SELinux: Bug fix in polidydb_destroy This patch fixes two bugs in policydb_destroy. Two list pointers (policydb.ocontexts[i] and policydb.genfs) were not being reset to NULL when the lists they pointed to were being freed. This caused a problem when the initial policy load failed, as the policydb being destroyed was not a temporary new policydb that was thrown away, but rather was the global (active) policydb. Consequently, later functions, particularly sys_bind->selinux_socket_bind->security_node_sid and do_rw_proc->selinux_sysctl->selinux_proc_get_sid->security_genfs_sid tried to dereference memory that had previously been freed. Signed-off-by: Chad Sellers Signed-off-by: James Morris commit d35780737540fe4ba6698225a9e32e873a6d78bc Author: Venkat Yekkirala Date: Thu Oct 5 15:42:35 2006 -0500 IPsec: fix handling of errors for socket policies This treats the security errors encountered in the case of socket policy matching, the same as how these are treated in the case of main/sub policies, which is to return a full lookup failure. Signed-off-by: Venkat Yekkirala Signed-off-by: James Morris commit 26c9c266e9855f6d4cbe27c346ca58d9add09cbe Author: Venkat Yekkirala Date: Thu Oct 5 15:42:18 2006 -0500 IPsec: correct semantics for SELinux policy matching Currently when an IPSec policy rule doesn't specify a security context, it is assumed to be "unlabeled" by SELinux, and so the IPSec policy rule fails to match to a flow that it would otherwise match to, unless one has explicitly added an SELinux policy rule allowing the flow to "polmatch" to the "unlabeled" IPSec policy rules. In the absence of such an explicitly added SELinux policy rule, the IPSec policy rule fails to match and so the packet(s) flow in clear text without the otherwise applicable xfrm(s) applied. The above SELinux behavior violates the SELinux security notion of "deny by default" which should actually translate to "encrypt by default" in the above case. This was first reported by Evgeniy Polyakov and the way James Morris was seeing the problem was when connecting via IPsec to a confined service on an SELinux box (vsftpd), which did not have the appropriate SELinux policy permissions to send packets via IPsec. With this patch applied, SELinux "polmatching" of flows Vs. IPSec policy rules will only come into play when there's a explicit context specified for the IPSec policy rule (which also means there's corresponding SELinux policy allowing appropriate domains/flows to polmatch to this context). Secondly, when a security module is loaded (in this case, SELinux), the security_xfrm_policy_lookup() hook can return errors other than access denied, such as -EINVAL. We were not handling that correctly, and in fact inverting the return logic and propagating a false "ok" back up to xfrm_lookup(), which then allowed packets to pass as if they were not associated with an xfrm policy. The solution for this is to first ensure that errno values are correctly propagated all the way back up through the various call chains from security_xfrm_policy_lookup(), and handled correctly. Then, flow_cache_lookup() is modified, so that if the policy resolver fails (typically a permission denied via the security module), the flow cache entry is killed rather than having a null policy assigned (which indicates that the packet can pass freely). This also forces any future lookups for the same flow to consult the security module (e.g. SELinux) for current security policy (rather than, say, caching the error on the flow cache entry). This patch: Fix the selinux side of things. This makes sure SELinux polmatching of flow contexts to IPSec policy rules comes into play only when an explicit context is associated with the IPSec policy rule. Also, this no longer defaults the context of a socket policy to the context of the socket since the "no explicit context" case is now handled properly. Signed-off-by: Venkat Yekkirala Signed-off-by: James Morris commit 169ae2c0c0ac808989efc1d7a7de2e174a3f034c Author: James Morris Date: Thu Oct 5 15:42:27 2006 -0500 IPsec: propagate security module errors up from flow_cache_lookup When a security module is loaded (in this case, SELinux), the security_xfrm_policy_lookup() hook can return an access denied permission (or other error). We were not handling that correctly, and in fact inverting the return logic and propagating a false "ok" back up to xfrm_lookup(), which then allowed packets to pass as if they were not associated with an xfrm policy. The way I was seeing the problem was when connecting via IPsec to a confined service on an SELinux box (vsftpd), which did not have the appropriate SELinux policy permissions to send packets via IPsec. The first SYNACK would be blocked, because of an uncached lookup via flow_cache_lookup(), which would fail to resolve an xfrm policy because the SELinux policy is checked at that point via the resolver. However, retransmitted SYNACKs would then find a cached flow entry when calling into flow_cache_lookup() with a null xfrm policy, which is interpreted by xfrm_lookup() as the packet not having any associated policy and similarly to the first case, allowing it to pass without transformation. The solution presented here is to first ensure that errno values are correctly propagated all the way back up through the various call chains from security_xfrm_policy_lookup(), and handled correctly. Then, flow_cache_lookup() is modified, so that if the policy resolver fails (typically a permission denied via the security module), the flow cache entry is killed rather than having a null policy assigned (which indicates that the packet can pass freely). This also forces any future lookups for the same flow to consult the security module (e.g. SELinux) for current security policy (rather than, say, caching the error on the flow cache entry). Signed-off-by: James Morris commit b9a37d4ee17984828889034163b6694074d3092c Author: paul.moore@hp.com Date: Thu Oct 5 18:28:24 2006 -0400 NetLabel: use SECINITSID_UNLABELED for a base SID This patch changes NetLabel to use SECINITSID_UNLABLELED as it's source of SELinux type information when generating a NetLabel context. Signed-off-by: Paul Moore Signed-off-by: James Morris commit b4810dab47c581c705c7249d559418e9ae21e9dd Author: paul.moore@hp.com Date: Wed Oct 4 11:46:31 2006 -0400 NetLabel: fix a cache race condition Testing revealed a problem with the NetLabel cache where a cached entry could be freed while in use by the LSM layer causing an oops and other problems. This patch fixes that problem by introducing a reference counter to the cache entry so that it is only freed when it is no longer in use. Signed-off-by: Paul Moore Signed-off-by: James Morris include/linux/security.h | 24 +++------ include/net/flow.h | 2 - include/net/netlabel.h | 62 ++++++++++++++++++------ include/net/xfrm.h | 3 + net/core/flow.c | 42 +++++++++++----- net/ipv4/cipso_ipv4.c | 18 ++++--- net/ipv4/xfrm4_policy.c | 2 - net/ipv6/xfrm6_policy.c | 2 - net/key/af_key.c | 5 -- net/netlabel/netlabel_kapi.c | 2 - net/xfrm/xfrm_policy.c | 101 +++++++++++++++++++++++++++++---------- net/xfrm/xfrm_user.c | 9 --- security/dummy.c | 3 + security/selinux/include/xfrm.h | 3 + security/selinux/ss/policydb.c | 2 + security/selinux/ss/services.c | 66 ++++++++++++------------- security/selinux/xfrm.c | 53 ++++++++++++++++---- 17 files changed, 252 insertions(+), 147 deletions(-) diff --git a/include/linux/security.h b/include/linux/security.h index 9b5fea8..b200b98 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -882,7 +882,8 @@ #ifdef CONFIG_SECURITY * Check permission when a flow selects a xfrm_policy for processing * XFRMs on a packet. The hook is called when selecting either a * per-socket policy or a generic xfrm policy. - * Return 0 if permission is granted. + * Return 0 if permission is granted, -ESRCH otherwise, or -errno + * on other errors. * @xfrm_state_pol_flow_match: * @x contains the state to match. * @xp contains the policy to check for a match. @@ -891,6 +892,7 @@ #ifdef CONFIG_SECURITY * @xfrm_flow_state_match: * @fl contains the flow key to match. * @xfrm points to the xfrm_state to match. + * @xp points to the xfrm_policy to match. * Return 1 if there is a match. * @xfrm_decode_session: * @skb points to skb to decode. @@ -1388,7 +1390,8 @@ #ifdef CONFIG_SECURITY_NETWORK_XFRM int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 fl_secid, u8 dir); int (*xfrm_state_pol_flow_match)(struct xfrm_state *x, struct xfrm_policy *xp, struct flowi *fl); - int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm); + int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm, + struct xfrm_policy *xp); int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall); #endif /* CONFIG_SECURITY_NETWORK_XFRM */ @@ -3120,11 +3123,6 @@ static inline int security_xfrm_policy_a return security_ops->xfrm_policy_alloc_security(xp, sec_ctx, NULL); } -static inline int security_xfrm_sock_policy_alloc(struct xfrm_policy *xp, struct sock *sk) -{ - return security_ops->xfrm_policy_alloc_security(xp, NULL, sk); -} - static inline int security_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new) { return security_ops->xfrm_policy_clone_security(old, new); @@ -3175,9 +3173,10 @@ static inline int security_xfrm_state_po return security_ops->xfrm_state_pol_flow_match(x, xp, fl); } -static inline int security_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm) +static inline int security_xfrm_flow_state_match(struct flowi *fl, + struct xfrm_state *xfrm, struct xfrm_policy *xp) { - return security_ops->xfrm_flow_state_match(fl, xfrm); + return security_ops->xfrm_flow_state_match(fl, xfrm, xp); } static inline int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid) @@ -3197,11 +3196,6 @@ static inline int security_xfrm_policy_a return 0; } -static inline int security_xfrm_sock_policy_alloc(struct xfrm_policy *xp, struct sock *sk) -{ - return 0; -} - static inline int security_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new) { return 0; @@ -3249,7 +3243,7 @@ static inline int security_xfrm_state_po } static inline int security_xfrm_flow_state_match(struct flowi *fl, - struct xfrm_state *xfrm) + struct xfrm_state *xfrm, struct xfrm_policy *xp) { return 1; } diff --git a/include/net/flow.h b/include/net/flow.h index ddf5f3c..3b44d72 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -97,7 +97,7 @@ #define FLOW_DIR_OUT 1 #define FLOW_DIR_FWD 2 struct sock; -typedef void (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir, +typedef int (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir, void **objp, atomic_t **obj_refp); extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir, diff --git a/include/net/netlabel.h b/include/net/netlabel.h index c63a580..113337c 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -34,6 +34,7 @@ #include #include #include #include +#include /* * NetLabel - A management interface for maintaining network packet label @@ -106,6 +107,7 @@ int netlbl_domhsh_remove(const char *dom /* LSM security attributes */ struct netlbl_lsm_cache { + atomic_t refcount; void (*free) (const void *data); void *data; }; @@ -117,7 +119,7 @@ struct netlbl_lsm_secattr { unsigned char *mls_cat; size_t mls_cat_len; - struct netlbl_lsm_cache cache; + struct netlbl_lsm_cache *cache; }; /* @@ -126,6 +128,43 @@ struct netlbl_lsm_secattr { /** + * netlbl_secattr_cache_alloc - Allocate and initialize a secattr cache + * @flags: the memory allocation flags + * + * Description: + * Allocate and initialize a netlbl_lsm_cache structure. Returns a pointer + * on success, NULL on failure. + * + */ +static inline struct netlbl_lsm_cache *netlbl_secattr_cache_alloc(int flags) +{ + struct netlbl_lsm_cache *cache; + + cache = kzalloc(sizeof(*cache), flags); + if (cache) + atomic_set(&cache->refcount, 1); + return cache; +} + +/** + * netlbl_secattr_cache_free - Frees a netlbl_lsm_cache struct + * @cache: the struct to free + * + * Description: + * Frees @secattr including all of the internal buffers. + * + */ +static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) +{ + if (!atomic_dec_and_test(&cache->refcount)) + return; + + if (cache->free) + cache->free(cache->data); + kfree(cache); +} + +/** * netlbl_secattr_init - Initialize a netlbl_lsm_secattr struct * @secattr: the struct to initialize * @@ -143,20 +182,16 @@ static inline int netlbl_secattr_init(st /** * netlbl_secattr_destroy - Clears a netlbl_lsm_secattr struct * @secattr: the struct to clear - * @clear_cache: cache clear flag * * Description: * Destroys the @secattr struct, including freeing all of the internal buffers. - * If @clear_cache is true then free the cache fields, otherwise leave them - * intact. The struct must be reset with a call to netlbl_secattr_init() - * before reuse. + * The struct must be reset with a call to netlbl_secattr_init() before reuse. * */ -static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr, - u32 clear_cache) +static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr) { - if (clear_cache && secattr->cache.data != NULL && secattr->cache.free) - secattr->cache.free(secattr->cache.data); + if (secattr->cache) + netlbl_secattr_cache_free(secattr->cache); kfree(secattr->domain); kfree(secattr->mls_cat); } @@ -178,17 +213,14 @@ static inline struct netlbl_lsm_secattr /** * netlbl_secattr_free - Frees a netlbl_lsm_secattr struct * @secattr: the struct to free - * @clear_cache: cache clear flag * * Description: - * Frees @secattr including all of the internal buffers. If @clear_cache is - * true then free the cache fields, otherwise leave them intact. + * Frees @secattr including all of the internal buffers. * */ -static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr, - u32 clear_cache) +static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr) { - netlbl_secattr_destroy(secattr, clear_cache); + netlbl_secattr_destroy(secattr); kfree(secattr); } diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 1e2a4dd..737fdb2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -995,7 +995,8 @@ struct xfrm_state * xfrm_find_acq(u8 mod int create, unsigned short family); extern void xfrm_policy_flush(u8 type); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); -extern int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl, int family, int strict); +extern int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *xdst, + struct flowi *fl, int family, int strict); extern void xfrm_init_pmtu(struct dst_entry *dst); extern wait_queue_head_t km_waitq; diff --git a/net/core/flow.c b/net/core/flow.c index f23e7e3..b16d31a 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -85,6 +85,14 @@ static void flow_cache_new_hashrnd(unsig add_timer(&flow_hash_rnd_timer); } +static void flow_entry_kill(int cpu, struct flow_cache_entry *fle) +{ + if (fle->object) + atomic_dec(fle->object_ref); + kmem_cache_free(flow_cachep, fle); + flow_count(cpu)--; +} + static void __flow_cache_shrink(int cpu, int shrink_to) { struct flow_cache_entry *fle, **flp; @@ -100,10 +108,7 @@ static void __flow_cache_shrink(int cpu, } while ((fle = *flp) != NULL) { *flp = fle->next; - if (fle->object) - atomic_dec(fle->object_ref); - kmem_cache_free(flow_cachep, fle); - flow_count(cpu)--; + flow_entry_kill(cpu, fle); } } } @@ -220,24 +225,33 @@ void *flow_cache_lookup(struct flowi *ke nocache: { + int err; void *obj; atomic_t *obj_ref; - resolver(key, family, dir, &obj, &obj_ref); + err = resolver(key, family, dir, &obj, &obj_ref); if (fle) { - fle->genid = atomic_read(&flow_cache_genid); - - if (fle->object) - atomic_dec(fle->object_ref); - - fle->object = obj; - fle->object_ref = obj_ref; - if (obj) - atomic_inc(fle->object_ref); + if (err) { + /* Force security policy check on next lookup */ + *head = fle->next; + flow_entry_kill(cpu, fle); + } else { + fle->genid = atomic_read(&flow_cache_genid); + + if (fle->object) + atomic_dec(fle->object_ref); + + fle->object = obj; + fle->object_ref = obj_ref; + if (obj) + atomic_inc(fle->object_ref); + } } local_bh_enable(); + if (err) + obj = ERR_PTR(err); return obj; } } diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index a8e2e87..bde8cca 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -43,6 +43,7 @@ #include #include #include #include +#include #include struct cipso_v4_domhsh_entry { @@ -79,7 +80,7 @@ struct cipso_v4_map_cache_entry { unsigned char *key; size_t key_len; - struct netlbl_lsm_cache lsm_data; + struct netlbl_lsm_cache *lsm_data; u32 activity; struct list_head list; @@ -188,13 +189,14 @@ static void cipso_v4_doi_domhsh_free(str * @entry: the entry to free * * Description: - * This function frees the memory associated with a cache entry. + * This function frees the memory associated with a cache entry including the + * LSM cache data if there are no longer any users, i.e. reference count == 0. * */ static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry) { - if (entry->lsm_data.free) - entry->lsm_data.free(entry->lsm_data.data); + if (entry->lsm_data) + netlbl_secattr_cache_free(entry->lsm_data); kfree(entry->key); kfree(entry); } @@ -315,8 +317,8 @@ static int cipso_v4_cache_check(const un entry->key_len == key_len && memcmp(entry->key, key, key_len) == 0) { entry->activity += 1; - secattr->cache.free = entry->lsm_data.free; - secattr->cache.data = entry->lsm_data.data; + atomic_inc(&entry->lsm_data->refcount); + secattr->cache = entry->lsm_data; if (prev_entry == NULL) { spin_unlock_bh(&cipso_v4_cache[bkt].lock); return 0; @@ -383,8 +385,8 @@ int cipso_v4_cache_add(const struct sk_b memcpy(entry->key, cipso_ptr, cipso_ptr_len); entry->key_len = cipso_ptr_len; entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len); - entry->lsm_data.free = secattr->cache.free; - entry->lsm_data.data = secattr->cache.data; + atomic_inc(&secattr->cache->refcount); + entry->lsm_data = secattr->cache; bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); spin_lock_bh(&cipso_v4_cache[bkt].lock); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 7a7a001..1bed0cd 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -52,7 +52,7 @@ __xfrm4_find_bundle(struct flowi *fl, st xdst->u.rt.fl.fl4_dst == fl->fl4_dst && xdst->u.rt.fl.fl4_src == fl->fl4_src && xdst->u.rt.fl.fl4_tos == fl->fl4_tos && - xfrm_bundle_ok(xdst, fl, AF_INET, 0)) { + xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) { dst_clone(dst); break; } diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 6a252e2..73cee2e 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -73,7 +73,7 @@ __xfrm6_find_bundle(struct flowi *fl, st xdst->u.rt6.rt6i_src.plen); if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) && ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) && - xfrm_bundle_ok(xdst, fl, AF_INET6, + xfrm_bundle_ok(policy, xdst, fl, AF_INET6, (xdst->u.rt6.rt6i_dst.plen != 128 || xdst->u.rt6.rt6i_src.plen != 128))) { dst_clone(dst); diff --git a/net/key/af_key.c b/net/key/af_key.c index ff98e70..20ff7cc 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2928,11 +2928,6 @@ #endif if (*dir) goto out; } - else { - *dir = security_xfrm_sock_policy_alloc(xp, sk); - if (*dir) - goto out; - } *dir = pol->sadb_x_policy_dir-1; return xp; diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 54fb7de..ff97110 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -200,7 +200,7 @@ void netlbl_cache_invalidate(void) int netlbl_cache_add(const struct sk_buff *skb, const struct netlbl_lsm_secattr *secattr) { - if (secattr->cache.data == NULL) + if (secattr->cache == NULL) return -ENOMSG; if (CIPSO_V4_OPTEXIST(skb)) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 2a78616..7736b23 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -883,30 +883,32 @@ out: } EXPORT_SYMBOL(xfrm_policy_walk); -/* Find policy to apply to this flow. */ - +/* + * Find policy to apply to this flow. + * + * Returns 0 if policy found, else an -errno. + */ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, u8 type, u16 family, int dir) { struct xfrm_selector *sel = &pol->selector; - int match; + int match, ret = -ESRCH; if (pol->family != family || pol->type != type) - return 0; + return ret; match = xfrm_selector_match(sel, fl, family); - if (match) { - if (!security_xfrm_policy_lookup(pol, fl->secid, dir)) - return 1; - } + if (match) + ret = security_xfrm_policy_lookup(pol, fl->secid, dir); - return 0; + return ret; } static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, u16 family, u8 dir) { + int err; struct xfrm_policy *pol, *ret; xfrm_address_t *daddr, *saddr; struct hlist_node *entry; @@ -922,7 +924,15 @@ static struct xfrm_policy *xfrm_policy_l chain = policy_hash_direct(daddr, saddr, family, dir); ret = NULL; hlist_for_each_entry(pol, entry, chain, bydst) { - if (xfrm_policy_match(pol, fl, type, family, dir)) { + err = xfrm_policy_match(pol, fl, type, family, dir); + if (err) { + if (err == -ESRCH) + continue; + else { + ret = ERR_PTR(err); + goto fail; + } + } else { ret = pol; priority = ret->priority; break; @@ -930,36 +940,53 @@ static struct xfrm_policy *xfrm_policy_l } chain = &xfrm_policy_inexact[dir]; hlist_for_each_entry(pol, entry, chain, bydst) { - if (xfrm_policy_match(pol, fl, type, family, dir) && - pol->priority < priority) { + err = xfrm_policy_match(pol, fl, type, family, dir); + if (err) { + if (err == -ESRCH) + continue; + else { + ret = ERR_PTR(err); + goto fail; + } + } else if (pol->priority < priority) { ret = pol; break; } } if (ret) xfrm_pol_hold(ret); +fail: read_unlock_bh(&xfrm_policy_lock); return ret; } -static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, +static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, void **objp, atomic_t **obj_refp) { struct xfrm_policy *pol; + int err = 0; #ifdef CONFIG_XFRM_SUB_POLICY pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir); - if (pol) + if (IS_ERR(pol)) { + err = PTR_ERR(pol); + pol = NULL; + } + if (pol || err) goto end; #endif pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir); - + if (IS_ERR(pol)) { + err = PTR_ERR(pol); + pol = NULL; + } #ifdef CONFIG_XFRM_SUB_POLICY end: #endif if ((*objp = (void *) pol) != NULL) *obj_refp = &pol->refcnt; + return err; } static inline int policy_to_flow_dir(int dir) @@ -989,12 +1016,16 @@ static struct xfrm_policy *xfrm_sk_polic sk->sk_family); int err = 0; - if (match) - err = security_xfrm_policy_lookup(pol, fl->secid, policy_to_flow_dir(dir)); - - if (match && !err) - xfrm_pol_hold(pol); - else + if (match) { + err = security_xfrm_policy_lookup(pol, fl->secid, + policy_to_flow_dir(dir)); + if (!err) + xfrm_pol_hold(pol); + else if (err == -ESRCH) + pol = NULL; + else + pol = ERR_PTR(err); + } else pol = NULL; } read_unlock_bh(&xfrm_policy_lock); @@ -1286,8 +1317,11 @@ restart: pol_dead = 0; xfrm_nr = 0; - if (sk && sk->sk_policy[1]) + if (sk && sk->sk_policy[1]) { policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl); + if (IS_ERR(policy)) + return PTR_ERR(policy); + } if (!policy) { /* To accelerate a bit... */ @@ -1297,6 +1331,8 @@ restart: policy = flow_cache_lookup(fl, dst_orig->ops->family, dir, xfrm_policy_lookup); + if (IS_ERR(policy)) + return PTR_ERR(policy); } if (!policy) @@ -1343,6 +1379,10 @@ #ifdef CONFIG_XFRM_SUB_POLICY fl, family, XFRM_POLICY_OUT); if (pols[1]) { + if (IS_ERR(pols[1])) { + err = PTR_ERR(pols[1]); + goto error; + } if (pols[1]->action == XFRM_POLICY_BLOCK) { err = -EPERM; goto error; @@ -1574,13 +1614,19 @@ int __xfrm_policy_check(struct sock *sk, } pol = NULL; - if (sk && sk->sk_policy[dir]) + if (sk && sk->sk_policy[dir]) { pol = xfrm_sk_policy_lookup(sk, dir, &fl); + if (IS_ERR(pol)) + return 0; + } if (!pol) pol = flow_cache_lookup(&fl, family, fl_dir, xfrm_policy_lookup); + if (IS_ERR(pol)) + return 0; + if (!pol) { if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) { xfrm_secpath_reject(xerr_idx, skb, &fl); @@ -1599,6 +1645,8 @@ #ifdef CONFIG_XFRM_SUB_POLICY &fl, family, XFRM_POLICY_IN); if (pols[1]) { + if (IS_ERR(pols[1])) + return 0; pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec; npols ++; } @@ -1706,7 +1754,7 @@ static struct dst_entry *xfrm_dst_check( static int stale_bundle(struct dst_entry *dst) { - return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0); + return !xfrm_bundle_ok(NULL, (struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0); } void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev) @@ -1828,7 +1876,8 @@ EXPORT_SYMBOL(xfrm_init_pmtu); * still valid. */ -int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family, int strict) +int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first, + struct flowi *fl, int family, int strict) { struct dst_entry *dst = &first->u.dst; struct xfrm_dst *last; @@ -1845,7 +1894,7 @@ int xfrm_bundle_ok(struct xfrm_dst *firs if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family)) return 0; - if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm)) + if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm, pol)) return 0; if (dst->xfrm->km.state != XFRM_STATE_VALID) return 0; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d54b3a7..2b2e59d 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1992,15 +1992,6 @@ #endif xp->type = XFRM_POLICY_TYPE_MAIN; copy_templates(xp, ut, nr); - if (!xp->security) { - int err = security_xfrm_sock_policy_alloc(xp, sk); - if (err) { - kfree(xp); - *dir = err; - return NULL; - } - } - *dir = p->dir; return xp; diff --git a/security/dummy.c b/security/dummy.c index aeee705..43874c1 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -881,7 +881,8 @@ static int dummy_xfrm_state_pol_flow_mat return 1; } -static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm) +static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, + struct xfrm_policy *xp) { return 1; } diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 81eb598..526b280 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -19,7 +19,8 @@ int selinux_xfrm_state_delete(struct xfr int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir); int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, struct flowi *fl); -int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm); +int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, + struct xfrm_policy *xp); /* diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index b188953..ba48961 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -618,6 +618,7 @@ void policydb_destroy(struct policydb *p c = c->next; ocontext_destroy(ctmp,i); } + p->ocontexts[i] = NULL; } g = p->genfs; @@ -633,6 +634,7 @@ void policydb_destroy(struct policydb *p g = g->next; kfree(gtmp); } + p->genfs = NULL; cond_policydb_destroy(p); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 0c219a1..18274b0 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2172,7 +2172,12 @@ struct netlbl_cache { */ static void selinux_netlbl_cache_free(const void *data) { - struct netlbl_cache *cache = NETLBL_CACHE(data); + struct netlbl_cache *cache; + + if (data == NULL) + return; + + cache = NETLBL_CACHE(data); switch (cache->type) { case NETLBL_CACHE_T_MLS: ebitmap_destroy(&cache->data.mls_label.level[0].cat); @@ -2197,17 +2202,20 @@ static void selinux_netlbl_cache_add(str struct netlbl_lsm_secattr secattr; netlbl_secattr_init(&secattr); + secattr.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); + if (secattr.cache == NULL) + goto netlbl_cache_add_return; cache = kzalloc(sizeof(*cache), GFP_ATOMIC); if (cache == NULL) - goto netlbl_cache_add_failure; - secattr.cache.free = selinux_netlbl_cache_free; - secattr.cache.data = (void *)cache; + goto netlbl_cache_add_return; + secattr.cache->free = selinux_netlbl_cache_free; + secattr.cache->data = (void *)cache; cache->type = NETLBL_CACHE_T_MLS; if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, &ctx->range.level[0].cat) != 0) - goto netlbl_cache_add_failure; + goto netlbl_cache_add_return; cache->data.mls_label.level[1].cat.highbit = cache->data.mls_label.level[0].cat.highbit; cache->data.mls_label.level[1].cat.node = @@ -2215,13 +2223,10 @@ static void selinux_netlbl_cache_add(str cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; - if (netlbl_cache_add(skb, &secattr) != 0) - goto netlbl_cache_add_failure; + netlbl_cache_add(skb, &secattr); - return; - -netlbl_cache_add_failure: - netlbl_secattr_destroy(&secattr, 1); +netlbl_cache_add_return: + netlbl_secattr_destroy(&secattr); } /** @@ -2263,8 +2268,8 @@ static int selinux_netlbl_secattr_to_sid POLICY_RDLOCK; - if (secattr->cache.data) { - cache = NETLBL_CACHE(secattr->cache.data); + if (secattr->cache) { + cache = NETLBL_CACHE(secattr->cache->data); switch (cache->type) { case NETLBL_CACHE_T_SID: *sid = cache->data.sid; @@ -2331,7 +2336,7 @@ static int selinux_netlbl_secattr_to_sid selinux_netlbl_cache_add(skb, &ctx_new); ebitmap_destroy(&ctx_new.range.level[0].cat); } else { - *sid = SECINITSID_UNLABELED; + *sid = SECSID_NULL; rc = 0; } @@ -2369,7 +2374,7 @@ static int selinux_netlbl_skbuff_getsid( &secattr, base_sid, sid); - netlbl_secattr_destroy(&secattr, 0); + netlbl_secattr_destroy(&secattr); return rc; } @@ -2415,7 +2420,7 @@ static int selinux_netlbl_socket_setsid( if (rc == 0) sksec->nlbl_state = NLBL_LABELED; - netlbl_secattr_destroy(&secattr, 0); + netlbl_secattr_destroy(&secattr); netlbl_socket_setsid_return: POLICY_RDUNLOCK; @@ -2514,10 +2519,10 @@ void selinux_netlbl_sock_graft(struct so if (netlbl_sock_getattr(sk, &secattr) == 0 && selinux_netlbl_secattr_to_sid(NULL, &secattr, - sksec->sid, + SECINITSID_UNLABELED, &nlbl_peer_sid) == 0) sksec->peer_sid = nlbl_peer_sid; - netlbl_secattr_destroy(&secattr, 0); + netlbl_secattr_destroy(&secattr); sksec->nlbl_state = NLBL_REQUIRE; @@ -2547,9 +2552,6 @@ u32 selinux_netlbl_inet_conn_request(str if (rc != 0) return SECSID_NULL; - if (peer_sid == SECINITSID_UNLABELED) - return SECSID_NULL; - return peer_sid; } @@ -2611,11 +2613,13 @@ int selinux_netlbl_sock_rcv_skb(struct s u32 netlbl_sid; u32 recv_perm; - rc = selinux_netlbl_skbuff_getsid(skb, SECINITSID_NETMSG, &netlbl_sid); + rc = selinux_netlbl_skbuff_getsid(skb, + SECINITSID_UNLABELED, + &netlbl_sid); if (rc != 0) return rc; - if (netlbl_sid == SECINITSID_UNLABELED) + if (netlbl_sid == SECSID_NULL) return 0; switch (sksec->sclass) { @@ -2653,10 +2657,6 @@ int selinux_netlbl_sock_rcv_skb(struct s u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock) { struct sk_security_struct *sksec = sock->sk->sk_security; - - if (sksec->peer_sid == SECINITSID_UNLABELED) - return SECSID_NULL; - return sksec->peer_sid; } @@ -2672,16 +2672,10 @@ u32 selinux_netlbl_socket_getpeersec_str u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) { int peer_sid; - struct sock *sk = skb->sk; - struct inode_security_struct *isec; - if (sk == NULL || sk->sk_socket == NULL) - return SECSID_NULL; - - isec = SOCK_INODE(sk->sk_socket)->i_security; - if (selinux_netlbl_skbuff_getsid(skb, isec->sid, &peer_sid) != 0) - return SECSID_NULL; - if (peer_sid == SECINITSID_UNLABELED) + if (selinux_netlbl_skbuff_getsid(skb, + SECINITSID_UNLABELED, + &peer_sid) != 0) return SECSID_NULL; return peer_sid; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 3e742b8..675b995 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -77,8 +77,8 @@ static inline int selinux_authorizable_x */ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir) { - int rc = 0; - u32 sel_sid = SECINITSID_UNLABELED; + int rc; + u32 sel_sid; struct xfrm_sec_ctx *ctx; /* Context sid is either set to label or ANY_ASSOC */ @@ -88,11 +88,21 @@ int selinux_xfrm_policy_lookup(struct xf sel_sid = ctx->ctx_sid; } + else + /* + * All flows should be treated as polmatch'ing an + * otherwise applicable "non-labeled" policy. This + * would prevent inadvertent "leaks". + */ + return 0; rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); + if (rc == -EACCES) + rc = -ESRCH; + return rc; } @@ -108,15 +118,20 @@ int selinux_xfrm_state_pol_flow_match(st u32 pol_sid; int err; - if (x->security) - state_sid = x->security->ctx_sid; - else - state_sid = SECINITSID_UNLABELED; - - if (xp->security) + if (xp->security) { + if (!x->security) + /* unlabeled SA and labeled policy can't match */ + return 0; + else + state_sid = x->security->ctx_sid; pol_sid = xp->security->ctx_sid; - else - pol_sid = SECINITSID_UNLABELED; + } else + if (x->security) + /* unlabeled policy and labeled SA can't match */ + return 0; + else + /* unlabeled policy and unlabeled SA match all flows */ + return 1; err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, @@ -125,7 +140,11 @@ int selinux_xfrm_state_pol_flow_match(st if (err) return 0; - return selinux_xfrm_flow_state_match(fl, x); + err = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION, + ASSOCIATION__SENDTO, + NULL)? 0:1; + + return err; } /* @@ -133,12 +152,22 @@ int selinux_xfrm_state_pol_flow_match(st * can use a given security association. */ -int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm) +int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, + struct xfrm_policy *xp) { int rc = 0; u32 sel_sid = SECINITSID_UNLABELED; struct xfrm_sec_ctx *ctx; + if (!xp->security) + if (!xfrm->security) + return 1; + else + return 0; + else + if (!xfrm->security) + return 0; + /* Context sid is either set to label or ANY_ASSOC */ if ((ctx = xfrm->security)) { if (!selinux_authorizable_ctx(ctx))