diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-10-09 13:31:47 -0700 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 16:55:02 -0700 |
commit | 050f009e16f908932070313c1745d09dc69fd62b (patch) | |
tree | 2176b8034065bf2e8b401865efcfaab912bb1997 | |
parent | 68325d3b12ad5bce650c2883bb878257f197efff (diff) | |
download | linux-3.10-050f009e16f908932070313c1745d09dc69fd62b.tar.gz linux-3.10-050f009e16f908932070313c1745d09dc69fd62b.tar.bz2 linux-3.10-050f009e16f908932070313c1745d09dc69fd62b.zip |
[IPSEC]: Lock state when copying non-atomic fields to user-space
This patch adds locking so that when we're copying non-atomic fields such as
life-time or coaddr to user-space we don't get a partial result.
For af_key I've changed every instance of pfkey_xfrm_state2msg apart from
expiration notification to include the keys and life-times. This is in-line
with XFRM behaviour.
The actual cases affected are:
* pfkey_getspi: No change as we don't have any keys to copy.
* key_notify_sa:
+ ADD/UPD: This wouldn't work otherwise.
+ DEL: It can't hurt.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/key/af_key.c | 35 | ||||
-rw-r--r-- | net/xfrm/xfrm_user.c | 14 |
2 files changed, 33 insertions, 16 deletions
diff --git a/net/key/af_key.c b/net/key/af_key.c index 143d46f6329..7969f8a716d 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -655,7 +655,8 @@ static inline int pfkey_mode_to_xfrm(int mode) } } -static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, int hsc) +static struct sk_buff *__pfkey_xfrm_state2msg(struct xfrm_state *x, + int add_keys, int hsc) { struct sk_buff *skb; struct sadb_msg *hdr; @@ -1009,6 +1010,24 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, return skb; } + +static inline struct sk_buff *pfkey_xfrm_state2msg(struct xfrm_state *x) +{ + struct sk_buff *skb; + + spin_lock_bh(&x->lock); + skb = __pfkey_xfrm_state2msg(x, 1, 3); + spin_unlock_bh(&x->lock); + + return skb; +} + +static inline struct sk_buff *pfkey_xfrm_state2msg_expire(struct xfrm_state *x, + int hsc) +{ + return __pfkey_xfrm_state2msg(x, 0, hsc); +} + static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, void **ext_hdrs) { @@ -1322,7 +1341,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h } err = xfrm_alloc_spi(x, min_spi, max_spi); - resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x, 0, 3); + resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x); if (IS_ERR(resp_skb)) { xfrm_state_put(x); @@ -1412,12 +1431,8 @@ static int key_notify_sa(struct xfrm_state *x, struct km_event *c) { struct sk_buff *skb; struct sadb_msg *hdr; - int hsc = 3; - - if (c->event == XFRM_MSG_DELSA) - hsc = 0; - skb = pfkey_xfrm_state2msg(x, 0, hsc); + skb = pfkey_xfrm_state2msg(x); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1529,7 +1544,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, if (x == NULL) return -ESRCH; - out_skb = pfkey_xfrm_state2msg(x, 1, 3); + out_skb = pfkey_xfrm_state2msg(x); proto = x->id.proto; xfrm_state_put(x); if (IS_ERR(out_skb)) @@ -1709,7 +1724,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr) struct sk_buff *out_skb; struct sadb_msg *out_hdr; - out_skb = pfkey_xfrm_state2msg(x, 1, 3); + out_skb = pfkey_xfrm_state2msg(x); if (IS_ERR(out_skb)) return PTR_ERR(out_skb); @@ -2910,7 +2925,7 @@ static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c) else hsc = 1; - out_skb = pfkey_xfrm_state2msg(x, 0, hsc); + out_skb = pfkey_xfrm_state2msg_expire(x, hsc); if (IS_ERR(out_skb)) return PTR_ERR(out_skb); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 2cbbe5e93a7..5238f6a8dfa 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -507,8 +507,16 @@ static int copy_to_user_state_extra(struct xfrm_state *x, struct xfrm_usersa_info *p, struct sk_buff *skb) { + spin_lock_bh(&x->lock); copy_to_user_state(x, p); + if (x->coaddr) + NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); + + if (x->lastused) + NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); + spin_unlock_bh(&x->lock); + if (x->aalg) NLA_PUT(skb, XFRMA_ALG_AUTH, alg_len(x->aalg), x->aalg); if (x->ealg) @@ -522,12 +530,6 @@ static int copy_to_user_state_extra(struct xfrm_state *x, if (x->security && copy_sec_ctx(x->security, skb) < 0) goto nla_put_failure; - if (x->coaddr) - NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); - - if (x->lastused) - NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); - return 0; nla_put_failure: |