Commit 52fc047b authored by Herbert Xu's avatar Herbert Xu Committed by Hideaki Yoshifuji

[IPSEC]: Close SADB_ADD race and add XFRM_MSG_UPDSA (SADB_UPDATE equivalent).

parent 1258b47b
...@@ -117,8 +117,9 @@ enum ...@@ -117,8 +117,9 @@ enum
#define XFRM_MSG_EXPIRE (XFRM_MSG_BASE + 8) #define XFRM_MSG_EXPIRE (XFRM_MSG_BASE + 8)
#define XFRM_MSG_UPDPOLICY (XFRM_MSG_BASE + 9) #define XFRM_MSG_UPDPOLICY (XFRM_MSG_BASE + 9)
#define XFRM_MSG_UPDSA (XFRM_MSG_BASE + 10)
#define XFRM_MSG_MAX (XFRM_MSG_UPDPOLICY+1) #define XFRM_MSG_MAX (XFRM_MSG_UPDSA+1)
struct xfrm_user_tmpl { struct xfrm_user_tmpl {
struct xfrm_id id; struct xfrm_id id;
......
...@@ -767,6 +767,7 @@ extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t ...@@ -767,6 +767,7 @@ extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t
unsigned short family); unsigned short family);
extern int xfrm_state_check_expire(struct xfrm_state *x); extern int xfrm_state_check_expire(struct xfrm_state *x);
extern void xfrm_state_insert(struct xfrm_state *x); extern void xfrm_state_insert(struct xfrm_state *x);
extern int xfrm_state_replace(struct xfrm_state *x, int excl);
extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb); extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb);
extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family); extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family);
extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq); extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
......
...@@ -1213,7 +1213,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, ...@@ -1213,7 +1213,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
struct sk_buff *out_skb; struct sk_buff *out_skb;
struct sadb_msg *out_hdr; struct sadb_msg *out_hdr;
struct xfrm_state *x; struct xfrm_state *x;
struct xfrm_state *x1; int err;
xfrm_probe_algs(); xfrm_probe_algs();
...@@ -1221,31 +1221,11 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, ...@@ -1221,31 +1221,11 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
if (IS_ERR(x)) if (IS_ERR(x))
return PTR_ERR(x); return PTR_ERR(x);
/* XXX there is race condition */ err = xfrm_state_replace(x, hdr->sadb_msg_type == SADB_ADD);
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs); if (err < 0) {
if (!x1) {
x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
&x->id.daddr,
&x->props.saddr, 0, x->props.family);
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
}
}
if (x1 && ((x1->id.spi && hdr->sadb_msg_type == SADB_ADD) ||
(hdr->sadb_msg_type == SADB_UPDATE && xfrm_state_kern(x1)))) {
x->km.state = XFRM_STATE_DEAD; x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x); xfrm_state_put(x);
xfrm_state_put(x1); return err;
return -EEXIST;
}
xfrm_state_insert(x);
if (x1) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
} }
out_skb = pfkey_xfrm_state2msg(x, 0, 3); out_skb = pfkey_xfrm_state2msg(x, 0, 3);
......
...@@ -306,6 +306,7 @@ EXPORT_SYMBOL(xfrm_state_alloc); ...@@ -306,6 +306,7 @@ EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy); EXPORT_SYMBOL(__xfrm_state_destroy);
EXPORT_SYMBOL(xfrm_state_find); EXPORT_SYMBOL(xfrm_state_find);
EXPORT_SYMBOL(xfrm_state_insert); EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_replace);
EXPORT_SYMBOL(xfrm_state_check_expire); EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space); EXPORT_SYMBOL(xfrm_state_check_space);
EXPORT_SYMBOL(xfrm_state_lookup); EXPORT_SYMBOL(xfrm_state_lookup);
......
...@@ -370,11 +370,10 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -370,11 +370,10 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
return x; return x;
} }
void xfrm_state_insert(struct xfrm_state *x) static void __xfrm_state_insert(struct xfrm_state *x)
{ {
unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family); unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
spin_lock_bh(&xfrm_state_lock);
list_add(&x->bydst, xfrm_state_bydst+h); list_add(&x->bydst, xfrm_state_bydst+h);
xfrm_state_hold(x); xfrm_state_hold(x);
...@@ -386,10 +385,62 @@ void xfrm_state_insert(struct xfrm_state *x) ...@@ -386,10 +385,62 @@ void xfrm_state_insert(struct xfrm_state *x)
if (!mod_timer(&x->timer, jiffies + HZ)) if (!mod_timer(&x->timer, jiffies + HZ))
xfrm_state_hold(x); xfrm_state_hold(x);
spin_unlock_bh(&xfrm_state_lock);
wake_up(&km_waitq); wake_up(&km_waitq);
} }
void xfrm_state_insert(struct xfrm_state *x)
{
spin_lock_bh(&xfrm_state_lock);
__xfrm_state_insert(x);
spin_unlock_bh(&xfrm_state_lock);
}
int xfrm_state_replace(struct xfrm_state *x, int excl)
{
struct xfrm_state_afinfo *afinfo;
struct xfrm_state *x1;
int err;
afinfo = xfrm_state_get_afinfo(x->props.family);
x1 = NULL;
spin_lock_bh(&xfrm_state_lock);
if (afinfo) {
x1 = afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto);
if (!x1) {
x1 = afinfo->find_acq(
x->props.mode, x->props.reqid, x->id.proto,
&x->id.daddr, &x->props.saddr, 0);
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
}
}
if (x1 && (excl ? x1->id.spi : xfrm_state_kern(x1))) {
xfrm_state_put(x1);
x1 = NULL;
err = -EEXIST;
goto out;
}
}
__xfrm_state_insert(x);
err = 0;
out:
spin_unlock_bh(&xfrm_state_lock);
if (x1) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
}
xfrm_state_put_afinfo(afinfo);
return err;
}
int xfrm_state_check_expire(struct xfrm_state *x) int xfrm_state_check_expire(struct xfrm_state *x)
{ {
if (!x->curlft.use_time) if (!x->curlft.use_time)
......
...@@ -249,7 +249,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, ...@@ -249,7 +249,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
{ {
struct xfrm_usersa_info *p = NLMSG_DATA(nlh); struct xfrm_usersa_info *p = NLMSG_DATA(nlh);
struct xfrm_state *x, *x1; struct xfrm_state *x;
int err; int err;
err = verify_newsa_info(p, (struct rtattr **) xfrma); err = verify_newsa_info(p, (struct rtattr **) xfrma);
...@@ -260,16 +260,13 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) ...@@ -260,16 +260,13 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
if (!x) if (!x)
return err; return err;
x1 = xfrm_state_lookup(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); err = xfrm_state_replace(x, nlh->nlmsg_type == XFRM_MSG_NEWSA);
if (x1) { if (err < 0) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x); xfrm_state_put(x);
xfrm_state_put(x1);
return -EEXIST;
} }
xfrm_state_insert(x); return err;
return 0;
} }
static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
...@@ -801,6 +798,7 @@ static const int xfrm_msg_min[(XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)] = { ...@@ -801,6 +798,7 @@ static const int xfrm_msg_min[(XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)] = {
NLMSG_LENGTH(sizeof(struct xfrm_user_acquire)), /* ACQUIRE */ NLMSG_LENGTH(sizeof(struct xfrm_user_acquire)), /* ACQUIRE */
NLMSG_LENGTH(sizeof(struct xfrm_user_expire)), /* EXPIRE */ NLMSG_LENGTH(sizeof(struct xfrm_user_expire)), /* EXPIRE */
NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)),/* UPD POLICY */ NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)),/* UPD POLICY */
NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)), /* UPD SA */
}; };
static struct xfrm_link { static struct xfrm_link {
...@@ -823,6 +821,7 @@ static struct xfrm_link { ...@@ -823,6 +821,7 @@ static struct xfrm_link {
{}, {},
{}, {},
{ .doit = xfrm_add_policy }, { .doit = xfrm_add_policy },
{ .doit = xfrm_add_sa, },
}; };
static int xfrm_done(struct netlink_callback *cb) static int xfrm_done(struct netlink_callback *cb)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment