Commit 12a0a6c0 authored by David S. Miller's avatar David S. Miller
Browse files


Steffen Klassert says:

====================
pull request (net): ipsec 2017-04-19

Two fixes for af_key:

1) Add a lock to key dump to prevent a NULL pointer dereference.
   From Yuejie Shi.

2) Fix slab-out-of-bounds in parse_ipsecrequests.
   From Herbert Xu.

Please pull or let me know if there are problems.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9d386cd9 096f41d3
Loading
Loading
Loading
Loading
+64 −29
Original line number Diff line number Diff line
@@ -63,8 +63,13 @@ struct pfkey_sock {
		} u;
		struct sk_buff	*skb;
	} dump;
	struct mutex dump_lock;
};

static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
			       xfrm_address_t *saddr, xfrm_address_t *daddr,
			       u16 *family);

static inline struct pfkey_sock *pfkey_sk(struct sock *sk)
{
	return (struct pfkey_sock *)sk;
@@ -139,6 +144,7 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
{
	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
	struct sock *sk;
	struct pfkey_sock *pfk;
	int err;

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
@@ -153,6 +159,9 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
	if (sk == NULL)
		goto out;

	pfk = pfkey_sk(sk);
	mutex_init(&pfk->dump_lock);

	sock->ops = &pfkey_ops;
	sock_init_data(sock, sk);

@@ -281,13 +290,23 @@ static int pfkey_do_dump(struct pfkey_sock *pfk)
	struct sadb_msg *hdr;
	int rc;

	mutex_lock(&pfk->dump_lock);
	if (!pfk->dump.dump) {
		rc = 0;
		goto out;
	}

	rc = pfk->dump.dump(pfk);
	if (rc == -ENOBUFS)
		return 0;
	if (rc == -ENOBUFS) {
		rc = 0;
		goto out;
	}

	if (pfk->dump.skb) {
		if (!pfkey_can_dump(&pfk->sk))
			return 0;
		if (!pfkey_can_dump(&pfk->sk)) {
			rc = 0;
			goto out;
		}

		hdr = (struct sadb_msg *) pfk->dump.skb->data;
		hdr->sadb_msg_seq = 0;
@@ -298,6 +317,9 @@ static int pfkey_do_dump(struct pfkey_sock *pfk)
	}

	pfkey_terminate_dump(pfk);

out:
	mutex_unlock(&pfk->dump_lock);
	return rc;
}

@@ -1793,19 +1815,26 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
	struct xfrm_address_filter *filter = NULL;
	struct pfkey_sock *pfk = pfkey_sk(sk);

	if (pfk->dump.dump != NULL)
	mutex_lock(&pfk->dump_lock);
	if (pfk->dump.dump != NULL) {
		mutex_unlock(&pfk->dump_lock);
		return -EBUSY;
	}

	proto = pfkey_satype2proto(hdr->sadb_msg_satype);
	if (proto == 0)
	if (proto == 0) {
		mutex_unlock(&pfk->dump_lock);
		return -EINVAL;
	}

	if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
		struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];

		filter = kmalloc(sizeof(*filter), GFP_KERNEL);
		if (filter == NULL)
		if (filter == NULL) {
			mutex_unlock(&pfk->dump_lock);
			return -ENOMEM;
		}

		memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
		       sizeof(xfrm_address_t));
@@ -1821,6 +1850,7 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
	pfk->dump.dump = pfkey_dump_sa;
	pfk->dump.done = pfkey_dump_sa_done;
	xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
	mutex_unlock(&pfk->dump_lock);

	return pfkey_do_dump(pfk);
}
@@ -1913,19 +1943,14 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)

	/* addresses present only in tunnel mode */
	if (t->mode == XFRM_MODE_TUNNEL) {
		u8 *sa = (u8 *) (rq + 1);
		int family, socklen;

		family = pfkey_sockaddr_extract((struct sockaddr *)sa,
						&t->saddr);
		if (!family)
			return -EINVAL;
		int err;

		socklen = pfkey_sockaddr_len(family);
		if (pfkey_sockaddr_extract((struct sockaddr *)(sa + socklen),
					   &t->id.daddr) != family)
			return -EINVAL;
		t->encap_family = family;
		err = parse_sockaddr_pair(
			(struct sockaddr *)(rq + 1),
			rq->sadb_x_ipsecrequest_len - sizeof(*rq),
			&t->saddr, &t->id.daddr, &t->encap_family);
		if (err)
			return err;
	} else
		t->encap_family = xp->family;

@@ -1945,7 +1970,11 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
	if (pol->sadb_x_policy_len * 8 < sizeof(struct sadb_x_policy))
		return -EINVAL;

	while (len >= sizeof(struct sadb_x_ipsecrequest)) {
	while (len >= sizeof(*rq)) {
		if (len < rq->sadb_x_ipsecrequest_len ||
		    rq->sadb_x_ipsecrequest_len < sizeof(*rq))
			return -EINVAL;

		if ((err = parse_ipsecrequest(xp, rq)) < 0)
			return err;
		len -= rq->sadb_x_ipsecrequest_len;
@@ -2408,7 +2437,6 @@ out:
	return err;
}

#ifdef CONFIG_NET_KEY_MIGRATE
static int pfkey_sockaddr_pair_size(sa_family_t family)
{
	return PFKEY_ALIGN8(pfkey_sockaddr_len(family) * 2);
@@ -2420,7 +2448,7 @@ static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
{
	int af, socklen;

	if (ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
	if (ext_len < 2 || ext_len < pfkey_sockaddr_pair_size(sa->sa_family))
		return -EINVAL;

	af = pfkey_sockaddr_extract(sa, saddr);
@@ -2436,6 +2464,7 @@ static int parse_sockaddr_pair(struct sockaddr *sa, int ext_len,
	return 0;
}

#ifdef CONFIG_NET_KEY_MIGRATE
static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
				    struct xfrm_migrate *m)
{
@@ -2443,13 +2472,14 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
	struct sadb_x_ipsecrequest *rq2;
	int mode;

	if (len <= sizeof(struct sadb_x_ipsecrequest) ||
	    len < rq1->sadb_x_ipsecrequest_len)
	if (len < sizeof(*rq1) ||
	    len < rq1->sadb_x_ipsecrequest_len ||
	    rq1->sadb_x_ipsecrequest_len < sizeof(*rq1))
		return -EINVAL;

	/* old endoints */
	err = parse_sockaddr_pair((struct sockaddr *)(rq1 + 1),
				  rq1->sadb_x_ipsecrequest_len,
				  rq1->sadb_x_ipsecrequest_len - sizeof(*rq1),
				  &m->old_saddr, &m->old_daddr,
				  &m->old_family);
	if (err)
@@ -2458,13 +2488,14 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
	rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len);
	len -= rq1->sadb_x_ipsecrequest_len;

	if (len <= sizeof(struct sadb_x_ipsecrequest) ||
	    len < rq2->sadb_x_ipsecrequest_len)
	if (len <= sizeof(*rq2) ||
	    len < rq2->sadb_x_ipsecrequest_len ||
	    rq2->sadb_x_ipsecrequest_len < sizeof(*rq2))
		return -EINVAL;

	/* new endpoints */
	err = parse_sockaddr_pair((struct sockaddr *)(rq2 + 1),
				  rq2->sadb_x_ipsecrequest_len,
				  rq2->sadb_x_ipsecrequest_len - sizeof(*rq2),
				  &m->new_saddr, &m->new_daddr,
				  &m->new_family);
	if (err)
@@ -2679,14 +2710,18 @@ static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb
{
	struct pfkey_sock *pfk = pfkey_sk(sk);

	if (pfk->dump.dump != NULL)
	mutex_lock(&pfk->dump_lock);
	if (pfk->dump.dump != NULL) {
		mutex_unlock(&pfk->dump_lock);
		return -EBUSY;
	}

	pfk->dump.msg_version = hdr->sadb_msg_version;
	pfk->dump.msg_portid = hdr->sadb_msg_pid;
	pfk->dump.dump = pfkey_dump_sp;
	pfk->dump.done = pfkey_dump_sp_done;
	xfrm_policy_walk_init(&pfk->dump.u.policy, XFRM_POLICY_TYPE_MAIN);
	mutex_unlock(&pfk->dump_lock);

	return pfkey_do_dump(pfk);
}