[IPSEC] Close SADB_ADD race

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Dave:

This patch closes the SADB_ADD race with xfrm_find_acq/xfrm_state_insert.
It also realigns the netlink interface with the pfkey interface for this
operation.

This adds a new XFRM_MSG_UPDSA which does the same thing as SADB_UPDATE.

Cheers,
-- 
Debian GNU/Linux 3.0 is out! ( http://www.debian.org/ )
Email:  Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Index: kernel-source-2.5/include/net/xfrm.h
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/include/net/xfrm.h,v
retrieving revision 1.5
diff -u -r1.5 xfrm.h
--- kernel-source-2.5/include/net/xfrm.h	20 Jun 2003 11:49:07 -0000	1.5
+++ kernel-source-2.5/include/net/xfrm.h	22 Jun 2003 00:18:29 -0000
@@ -767,6 +767,7 @@
 					  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);
Index: kernel-source-2.5/net/netsyms.c
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/netsyms.c,v
retrieving revision 1.3
diff -u -r1.3 netsyms.c
--- kernel-source-2.5/net/netsyms.c	20 Jun 2003 11:49:08 -0000	1.3
+++ kernel-source-2.5/net/netsyms.c	22 Jun 2003 00:18:38 -0000
@@ -306,6 +306,7 @@
 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);
Index: kernel-source-2.5/net/xfrm/xfrm_user.c
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/xfrm/xfrm_user.c,v
retrieving revision 1.5
diff -u -r1.5 xfrm_user.c
--- kernel-source-2.5/net/xfrm/xfrm_user.c	20 Jun 2003 11:49:08 -0000	1.5
+++ kernel-source-2.5/net/xfrm/xfrm_user.c	22 Jun 2003 00:23:44 -0000
@@ -249,7 +249,7 @@
 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 @@
 	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 @@
 	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 @@
 	{},
 	{},
 	{	.doit	=	xfrm_add_policy 	},
+	{	.doit	=	xfrm_add_sa, 		},
 };
 
 static int xfrm_done(struct netlink_callback *cb)
Index: kernel-source-2.5/include/linux/xfrm.h
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/include/linux/xfrm.h,v
retrieving revision 1.1.1.6
diff -u -r1.1.1.6 xfrm.h
--- kernel-source-2.5/include/linux/xfrm.h	17 Jun 2003 04:19:42 -0000	1.1.1.6
+++ kernel-source-2.5/include/linux/xfrm.h	21 Jun 2003 23:13:35 -0000
@@ -117,8 +117,9 @@
 #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;
Index: kernel-source-2.5/net/xfrm/xfrm_state.c
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/xfrm/xfrm_state.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 xfrm_state.c
--- kernel-source-2.5/net/xfrm/xfrm_state.c	17 Jun 2003 04:20:19 -0000	1.1.1.4
+++ kernel-source-2.5/net/xfrm/xfrm_state.c	22 Jun 2003 00:31:09 -0000
@@ -370,11 +370,10 @@
 	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,8 +385,60 @@
 	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)
Index: kernel-source-2.5/net/key/af_key.c
===================================================================
RCS file: /home/gondolin/herbert/src/CVS/debian/kernel-source-2.5/net/key/af_key.c,v
retrieving revision 1.6
diff -u -r1.6 af_key.c
--- kernel-source-2.5/net/key/af_key.c	20 Jun 2003 11:49:08 -0000	1.6
+++ kernel-source-2.5/net/key/af_key.c	22 Jun 2003 00:32:48 -0000
@@ -1215,7 +1215,7 @@
 	struct sk_buff *out_skb;
 	struct sadb_msg *out_hdr;
 	struct xfrm_state *x;
-	struct xfrm_state *x1;
+	int err;
 
 	xfrm_probe_algs();
 	
@@ -1223,31 +1223,11 @@
 	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);

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux