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
#define XFRM_MSG_EXPIRE (XFRM_MSG_BASE + 8)
#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_id id;
......
......@@ -767,6 +767,7 @@ extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t
unsigned short family);
extern int xfrm_state_check_expire(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 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);
......
......@@ -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 sadb_msg *out_hdr;
struct xfrm_state *x;
struct xfrm_state *x1;
int err;
xfrm_probe_algs();
......@@ -1221,31 +1221,11 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
if (IS_ERR(x))
return PTR_ERR(x);
/* XXX there is race condition */
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
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)))) {
err = xfrm_state_replace(x, hdr->sadb_msg_type == SADB_ADD);
if (err < 0) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
xfrm_state_put(x1);
return -EEXIST;
}
xfrm_state_insert(x);
if (x1) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
return err;
}
out_skb = pfkey_xfrm_state2msg(x, 0, 3);
......
......@@ -306,6 +306,7 @@ EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy);
EXPORT_SYMBOL(xfrm_state_find);
EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_replace);
EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space);
EXPORT_SYMBOL(xfrm_state_lookup);
......
......@@ -370,11 +370,10 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
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);
spin_lock_bh(&xfrm_state_lock);
list_add(&x->bydst, xfrm_state_bydst+h);
xfrm_state_hold(x);
......@@ -386,10 +385,62 @@ void xfrm_state_insert(struct xfrm_state *x)
if (!mod_timer(&x->timer, jiffies + HZ))
xfrm_state_hold(x);
spin_unlock_bh(&xfrm_state_lock);
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)
{
if (!x->curlft.use_time)
......
......@@ -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)
{
struct xfrm_usersa_info *p = NLMSG_DATA(nlh);
struct xfrm_state *x, *x1;
struct xfrm_state *x;
int err;
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)
if (!x)
return err;
x1 = xfrm_state_lookup(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
if (x1) {
err = xfrm_state_replace(x, nlh->nlmsg_type == XFRM_MSG_NEWSA);
if (err < 0) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
xfrm_state_put(x1);
return -EEXIST;
}
xfrm_state_insert(x);
return 0;
return err;
}
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)] = {
NLMSG_LENGTH(sizeof(struct xfrm_user_acquire)), /* ACQUIRE */
NLMSG_LENGTH(sizeof(struct xfrm_user_expire)), /* EXPIRE */
NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)),/* UPD POLICY */
NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)), /* UPD SA */
};
static struct xfrm_link {
......@@ -823,6 +821,7 @@ static struct xfrm_link {
{},
{},
{ .doit = xfrm_add_policy },
{ .doit = xfrm_add_sa, },
};
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