aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJames Morris <jmorris@namei.org>2006-10-05 16:42:27 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-10-12 02:59:34 -0400
commit134b0fc544ba062498451611cb6f3e4454221b3d (patch)
tree84120e405d2bc7112b971fc82b718ae23991351b /net
parent388b24057f90ba109d4bf855006a8809c383eb76 (diff)
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 <jmorris@namei.org>
Diffstat (limited to 'net')
-rw-r--r--net/core/flow.c42
-rw-r--r--net/xfrm/xfrm_policy.c68
2 files changed, 81 insertions, 29 deletions
diff --git a/net/core/flow.c b/net/core/flow.c
index f23e7e386543..b16d31ae5e54 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -85,6 +85,14 @@ static void flow_cache_new_hashrnd(unsigned long arg)
85 add_timer(&flow_hash_rnd_timer); 85 add_timer(&flow_hash_rnd_timer);
86} 86}
87 87
88static void flow_entry_kill(int cpu, struct flow_cache_entry *fle)
89{
90 if (fle->object)
91 atomic_dec(fle->object_ref);
92 kmem_cache_free(flow_cachep, fle);
93 flow_count(cpu)--;
94}
95
88static void __flow_cache_shrink(int cpu, int shrink_to) 96static void __flow_cache_shrink(int cpu, int shrink_to)
89{ 97{
90 struct flow_cache_entry *fle, **flp; 98 struct flow_cache_entry *fle, **flp;
@@ -100,10 +108,7 @@ static void __flow_cache_shrink(int cpu, int shrink_to)
100 } 108 }
101 while ((fle = *flp) != NULL) { 109 while ((fle = *flp) != NULL) {
102 *flp = fle->next; 110 *flp = fle->next;
103 if (fle->object) 111 flow_entry_kill(cpu, fle);
104 atomic_dec(fle->object_ref);
105 kmem_cache_free(flow_cachep, fle);
106 flow_count(cpu)--;
107 } 112 }
108 } 113 }
109} 114}
@@ -220,24 +225,33 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
220 225
221nocache: 226nocache:
222 { 227 {
228 int err;
223 void *obj; 229 void *obj;
224 atomic_t *obj_ref; 230 atomic_t *obj_ref;
225 231
226 resolver(key, family, dir, &obj, &obj_ref); 232 err = resolver(key, family, dir, &obj, &obj_ref);
227 233
228 if (fle) { 234 if (fle) {
229 fle->genid = atomic_read(&flow_cache_genid); 235 if (err) {
230 236 /* Force security policy check on next lookup */
231 if (fle->object) 237 *head = fle->next;
232 atomic_dec(fle->object_ref); 238 flow_entry_kill(cpu, fle);
233 239 } else {
234 fle->object = obj; 240 fle->genid = atomic_read(&flow_cache_genid);
235 fle->object_ref = obj_ref; 241
236 if (obj) 242 if (fle->object)
237 atomic_inc(fle->object_ref); 243 atomic_dec(fle->object_ref);
244
245 fle->object = obj;
246 fle->object_ref = obj_ref;
247 if (obj)
248 atomic_inc(fle->object_ref);
249 }
238 } 250 }
239 local_bh_enable(); 251 local_bh_enable();
240 252
253 if (err)
254 obj = ERR_PTR(err);
241 return obj; 255 return obj;
242 } 256 }
243} 257}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 2a7861661f14..fffdd34f3baf 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -883,30 +883,32 @@ out:
883} 883}
884EXPORT_SYMBOL(xfrm_policy_walk); 884EXPORT_SYMBOL(xfrm_policy_walk);
885 885
886/* Find policy to apply to this flow. */ 886/*
887 887 * Find policy to apply to this flow.
888 *
889 * Returns 0 if policy found, else an -errno.
890 */
888static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, 891static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
889 u8 type, u16 family, int dir) 892 u8 type, u16 family, int dir)
890{ 893{
891 struct xfrm_selector *sel = &pol->selector; 894 struct xfrm_selector *sel = &pol->selector;
892 int match; 895 int match, ret = -ESRCH;
893 896
894 if (pol->family != family || 897 if (pol->family != family ||
895 pol->type != type) 898 pol->type != type)
896 return 0; 899 return ret;
897 900
898 match = xfrm_selector_match(sel, fl, family); 901 match = xfrm_selector_match(sel, fl, family);
899 if (match) { 902 if (match)
900 if (!security_xfrm_policy_lookup(pol, fl->secid, dir)) 903 ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
901 return 1;
902 }
903 904
904 return 0; 905 return ret;
905} 906}
906 907
907static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, 908static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
908 u16 family, u8 dir) 909 u16 family, u8 dir)
909{ 910{
911 int err;
910 struct xfrm_policy *pol, *ret; 912 struct xfrm_policy *pol, *ret;
911 xfrm_address_t *daddr, *saddr; 913 xfrm_address_t *daddr, *saddr;
912 struct hlist_node *entry; 914 struct hlist_node *entry;
@@ -922,7 +924,15 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
922 chain = policy_hash_direct(daddr, saddr, family, dir); 924 chain = policy_hash_direct(daddr, saddr, family, dir);
923 ret = NULL; 925 ret = NULL;
924 hlist_for_each_entry(pol, entry, chain, bydst) { 926 hlist_for_each_entry(pol, entry, chain, bydst) {
925 if (xfrm_policy_match(pol, fl, type, family, dir)) { 927 err = xfrm_policy_match(pol, fl, type, family, dir);
928 if (err) {
929 if (err == -ESRCH)
930 continue;
931 else {
932 ret = ERR_PTR(err);
933 goto fail;
934 }
935 } else {
926 ret = pol; 936 ret = pol;
927 priority = ret->priority; 937 priority = ret->priority;
928 break; 938 break;
@@ -930,36 +940,53 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
930 } 940 }
931 chain = &xfrm_policy_inexact[dir]; 941 chain = &xfrm_policy_inexact[dir];
932 hlist_for_each_entry(pol, entry, chain, bydst) { 942 hlist_for_each_entry(pol, entry, chain, bydst) {
933 if (xfrm_policy_match(pol, fl, type, family, dir) && 943 err = xfrm_policy_match(pol, fl, type, family, dir);
934 pol->priority < priority) { 944 if (err) {
945 if (err == -ESRCH)
946 continue;
947 else {
948 ret = ERR_PTR(err);
949 goto fail;
950 }
951 } else if (pol->priority < priority) {
935 ret = pol; 952 ret = pol;
936 break; 953 break;
937 } 954 }
938 } 955 }
939 if (ret) 956 if (ret)
940 xfrm_pol_hold(ret); 957 xfrm_pol_hold(ret);
958fail:
941 read_unlock_bh(&xfrm_policy_lock); 959 read_unlock_bh(&xfrm_policy_lock);
942 960
943 return ret; 961 return ret;
944} 962}
945 963
946static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, 964static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
947 void **objp, atomic_t **obj_refp) 965 void **objp, atomic_t **obj_refp)
948{ 966{
949 struct xfrm_policy *pol; 967 struct xfrm_policy *pol;
968 int err = 0;
950 969
951#ifdef CONFIG_XFRM_SUB_POLICY 970#ifdef CONFIG_XFRM_SUB_POLICY
952 pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir); 971 pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
953 if (pol) 972 if (IS_ERR(pol)) {
973 err = PTR_ERR(pol);
974 pol = NULL;
975 }
976 if (pol || err)
954 goto end; 977 goto end;
955#endif 978#endif
956 pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir); 979 pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
957 980 if (IS_ERR(pol)) {
981 err = PTR_ERR(pol);
982 pol = NULL;
983 }
958#ifdef CONFIG_XFRM_SUB_POLICY 984#ifdef CONFIG_XFRM_SUB_POLICY
959end: 985end:
960#endif 986#endif
961 if ((*objp = (void *) pol) != NULL) 987 if ((*objp = (void *) pol) != NULL)
962 *obj_refp = &pol->refcnt; 988 *obj_refp = &pol->refcnt;
989 return err;
963} 990}
964 991
965static inline int policy_to_flow_dir(int dir) 992static inline int policy_to_flow_dir(int dir)
@@ -1297,6 +1324,8 @@ restart:
1297 1324
1298 policy = flow_cache_lookup(fl, dst_orig->ops->family, 1325 policy = flow_cache_lookup(fl, dst_orig->ops->family,
1299 dir, xfrm_policy_lookup); 1326 dir, xfrm_policy_lookup);
1327 if (IS_ERR(policy))
1328 return PTR_ERR(policy);
1300 } 1329 }
1301 1330
1302 if (!policy) 1331 if (!policy)
@@ -1343,6 +1372,10 @@ restart:
1343 fl, family, 1372 fl, family,
1344 XFRM_POLICY_OUT); 1373 XFRM_POLICY_OUT);
1345 if (pols[1]) { 1374 if (pols[1]) {
1375 if (IS_ERR(pols[1])) {
1376 err = PTR_ERR(pols[1]);
1377 goto error;
1378 }
1346 if (pols[1]->action == XFRM_POLICY_BLOCK) { 1379 if (pols[1]->action == XFRM_POLICY_BLOCK) {
1347 err = -EPERM; 1380 err = -EPERM;
1348 goto error; 1381 goto error;
@@ -1581,6 +1614,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
1581 pol = flow_cache_lookup(&fl, family, fl_dir, 1614 pol = flow_cache_lookup(&fl, family, fl_dir,
1582 xfrm_policy_lookup); 1615 xfrm_policy_lookup);
1583 1616
1617 if (IS_ERR(pol))
1618 return 0;
1619
1584 if (!pol) { 1620 if (!pol) {
1585 if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) { 1621 if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
1586 xfrm_secpath_reject(xerr_idx, skb, &fl); 1622 xfrm_secpath_reject(xerr_idx, skb, &fl);
@@ -1599,6 +1635,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
1599 &fl, family, 1635 &fl, family,
1600 XFRM_POLICY_IN); 1636 XFRM_POLICY_IN);
1601 if (pols[1]) { 1637 if (pols[1]) {
1638 if (IS_ERR(pols[1]))
1639 return 0;
1602 pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec; 1640 pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec;
1603 npols ++; 1641 npols ++;
1604 } 1642 }