lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 28 Nov 2011 21:14:52 +0100
From:	Ulrich Weber <ulrich.weber@...hos.com>
To:	<netdev@...r.kernel.org>
CC:	<davem@...emloft.net>
Subject: [PATCH 3/3] xfrm: allow to overwrite incoming dev after decryption

and flush related xfrm states if interface goes down.

If XFRMA_RECV_DEV is set to an interface index, all decrypted
packets of the associated xfrm state will have the incoming
interface set to that interface.

This allows to create multiple virtual IPsec devices, which
all receive their packets via the same uplink interface.

Xfrm policies can then match against these virtual IPsec
interfaces. Otherwise only one policy could be installed
matching the uplink interface.

Signed-off-by: Ulrich Weber <ulrich.weber@...hos.com>
---
 include/linux/xfrm.h  |    1 +
 include/net/xfrm.h    |    6 +++++-
 net/key/af_key.c      |    2 +-
 net/xfrm/xfrm_input.c |    5 +++++
 net/xfrm/xfrm_state.c |   44 +++++++++++++++++++++++++++++++++++++++-----
 net/xfrm/xfrm_user.c  |   14 +++++++++++++-
 6 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index bb1bb49..13b04f4 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -295,6 +295,7 @@ enum xfrm_attr_type_t {
 	XFRMA_MARK,		/* struct xfrm_mark */
 	XFRMA_TFCPAD,		/* __u32 */
 	XFRMA_REPLAY_ESN_VAL,	/* struct xfrm_replay_esn */
+	XFRMA_RECV_DEV,		/* __u32 */
 	__XFRMA_MAX
 
 #define XFRMA_MAX (__XFRMA_MAX - 1)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 89174e2..3febf6a 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -226,6 +226,9 @@ struct xfrm_state {
 	/* Security context */
 	struct xfrm_sec_ctx	*security;
 
+	/* Overwrite incoming device */
+	struct net_device	*dev;
+
 	/* Private data of this transformer, format is opaque,
 	 * interpreted by xfrm_type methods. */
 	void			*data;
@@ -1442,7 +1445,8 @@ struct xfrmk_spdinfo {
 extern struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark,
 					      u32 seq);
 extern int xfrm_state_delete(struct xfrm_state *x);
-extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
+extern int xfrm_state_flush(struct net *net, u8 proto, struct net_device *dev,
+			    struct xfrm_audit *audit_info);
 extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
 extern void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
 extern u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index bfc0bef..3bd8075 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1726,7 +1726,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
 	audit_info.loginuid = audit_get_loginuid(current);
 	audit_info.sessionid = audit_get_sessionid(current);
 	audit_info.secid = 0;
-	err = xfrm_state_flush(net, proto, &audit_info);
+	err = xfrm_state_flush(net, proto, NULL, &audit_info);
 	err2 = unicast_flush_resp(sk, hdr);
 	if (err || err2) {
 		if (err == -ESRCH) /* empty table - go quietly */
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 54a0dc2..80eb73a 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -239,6 +239,11 @@ resume:
 			goto drop;
 		}
 
+		if (x->dev) {
+			skb->dev = x->dev;
+			skb->skb_iif = skb->dev->ifindex;
+		}
+
 		if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
 			decaps = 1;
 			break;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5b228f9..41c6cc5 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -368,6 +368,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
 		x->type->destructor(x);
 		xfrm_put_type(x->type);
 	}
+	if (x->dev)
+		dev_put(x->dev);
 	security_xfrm_state_free(x);
 	kfree(x);
 }
@@ -567,7 +569,8 @@ EXPORT_SYMBOL(xfrm_state_delete);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 static inline int
-xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct net_device *dev,
+			      struct xfrm_audit *audit_info)
 {
 	int i, err = 0;
 
@@ -577,6 +580,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi
 
 		hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
 			if (xfrm_id_proto_match(x->id.proto, proto) &&
+			   (!dev || dev == x->dev) &&
 			   (err = security_xfrm_state_delete(x)) != 0) {
 				xfrm_audit_state_delete(x, 0,
 							audit_info->loginuid,
@@ -591,18 +595,20 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi
 }
 #else
 static inline int
-xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct net_device *dev,
+			      struct xfrm_audit *audit_info)
 {
 	return 0;
 }
 #endif
 
-int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+int xfrm_state_flush(struct net *net, u8 proto, struct net_device *dev,
+		     struct xfrm_audit *audit_info)
 {
 	int i, err = 0, cnt = 0;
 
 	spin_lock_bh(&xfrm_state_lock);
-	err = xfrm_state_flush_secctx_check(net, proto, audit_info);
+	err = xfrm_state_flush_secctx_check(net, proto, dev, audit_info);
 	if (err)
 		goto out;
 
@@ -613,6 +619,7 @@ int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
 restart:
 		hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
 			if (!xfrm_state_kern(x) &&
+			    (!dev || x->dev == dev) &&
 			    xfrm_id_proto_match(x->id.proto, proto)) {
 				xfrm_state_hold(x);
 				spin_unlock_bh(&xfrm_state_lock);
@@ -1185,6 +1192,11 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
 
 	memcpy(&x->mark, &orig->mark, sizeof(x->mark));
 
+	if (orig->dev) {
+		dev_hold(dev);
+		x->dev = orig->dev;
+	}
+
 	err = xfrm_init_state(x);
 	if (err)
 		goto error;
@@ -2005,6 +2017,26 @@ int xfrm_init_state(struct xfrm_state *x)
 
 EXPORT_SYMBOL(xfrm_init_state);
 
+
+static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct net_device *dev = ptr;
+	struct xfrm_audit audit_info;
+
+	switch (event) {
+	case NETDEV_DOWN:
+		audit_info.loginuid = -1;
+		audit_info.sessionid = -1;
+		audit_info.secid = 0;
+		xfrm_state_flush(dev_net(dev), IPSEC_PROTO_ANY, dev, &audit_info);
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block xfrm_dev_notifier = {
+	.notifier_call	= xfrm_dev_event,
+};
+
 int __net_init xfrm_state_init(struct net *net)
 {
 	unsigned int sz;
@@ -2029,6 +2061,8 @@ int __net_init xfrm_state_init(struct net *net)
 	INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
 	INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
 	init_waitqueue_head(&net->xfrm.km_waitq);
+	if (net_eq(net, &init_net))
+		register_netdevice_notifier(&xfrm_dev_notifier);
 	return 0;
 
 out_byspi:
@@ -2048,7 +2082,7 @@ void xfrm_state_fini(struct net *net)
 	audit_info.loginuid = -1;
 	audit_info.sessionid = -1;
 	audit_info.secid = 0;
-	xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
+	xfrm_state_flush(net, IPSEC_PROTO_ANY, NULL, &audit_info);
 	flush_work(&net->xfrm.state_gc_work);
 
 	WARN_ON(!list_empty(&net->xfrm.state_all));
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index d0a42df..46bd7ad 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -532,6 +532,12 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 			goto error;
 	}
 
+	if (attrs[XFRMA_RECV_DEV]) {
+		x->dev = dev_get_by_index(net, *(int *) nla_data(attrs[XFRMA_RECV_DEV]));
+		if (x->dev == NULL)
+			goto error;
+	}
+
 	xfrm_mark_get(attrs, &x->mark);
 
 	err = __xfrm_init_state(x, false);
@@ -762,6 +768,9 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
 	if (x->lastused)
 		NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused);
 
+	if (x->dev)
+		NLA_PUT_U32(skb, XFRMA_RECV_DEV, x->dev->ifindex);
+
 	if (x->aead)
 		NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead);
 	if (x->aalg) {
@@ -1642,7 +1651,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 	audit_info.loginuid = audit_get_loginuid(current);
 	audit_info.sessionid = audit_get_sessionid(current);
 	security_task_getsecid(current, &audit_info.secid);
-	err = xfrm_state_flush(net, p->proto, &audit_info);
+	err = xfrm_state_flush(net, p->proto, NULL, &audit_info);
 	if (err) {
 		if (err == -ESRCH) /* empty table */
 			return 0;
@@ -2243,6 +2252,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
 	[XFRMA_MARK]		= { .len = sizeof(struct xfrm_mark) },
 	[XFRMA_TFCPAD]		= { .type = NLA_U32 },
 	[XFRMA_REPLAY_ESN_VAL]	= { .len = sizeof(struct xfrm_replay_state_esn) },
+	[XFRMA_RECV_DEV]	= { .type = NLA_U32 },
 };
 
 static struct xfrm_link {
@@ -2432,6 +2442,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
 				    x->security->ctx_len);
 	if (x->coaddr)
 		l += nla_total_size(sizeof(*x->coaddr));
+	if (x->dev)
+		l += nla_total_size(sizeof(u32));
 
 	/* Must count x->lastused as it may become non-zero behind our back. */
 	l += nla_total_size(sizeof(u64));
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ