diff options
Diffstat (limited to 'net/key')
| -rw-r--r-- | net/key/af_key.c | 127 | 
1 files changed, 79 insertions, 48 deletions
diff --git a/net/key/af_key.c b/net/key/af_key.c index 9d585370c5b..ba2a2f95911 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -205,7 +205,7 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,  		if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {  			skb_set_owner_r(*skb2, sk);  			skb_queue_tail(&sk->sk_receive_queue, *skb2); -			sk->sk_data_ready(sk, (*skb2)->len); +			sk->sk_data_ready(sk);  			*skb2 = NULL;  			err = 0;  		} @@ -365,6 +365,7 @@ static const u8 sadb_ext_min_len[] = {  	[SADB_X_EXT_NAT_T_OA]		= (u8) sizeof(struct sadb_address),  	[SADB_X_EXT_SEC_CTX]		= (u8) sizeof(struct sadb_x_sec_ctx),  	[SADB_X_EXT_KMADDRESS]		= (u8) sizeof(struct sadb_x_kmaddress), +	[SADB_X_EXT_FILTER]		= (u8) sizeof(struct sadb_x_filter),  };  /* Verify sadb_address_{len,prefixlen} against sa_family.  */ @@ -433,12 +434,13 @@ static inline int verify_sec_ctx_len(const void *p)  	return 0;  } -static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx) +static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx, +								     gfp_t gfp)  {  	struct xfrm_user_sec_ctx *uctx = NULL;  	int ctx_size = sec_ctx->sadb_x_ctx_len; -	uctx = kmalloc((sizeof(*uctx)+ctx_size), GFP_KERNEL); +	uctx = kmalloc((sizeof(*uctx)+ctx_size), gfp);  	if (!uctx)  		return NULL; @@ -1098,7 +1100,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,  	x->id.proto = proto;  	x->id.spi = sa->sadb_sa_spi; -	x->props.replay_window = sa->sadb_sa_replay; +	x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay, +					(sizeof(x->replay.bitmap) * 8));  	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)  		x->props.flags |= XFRM_STATE_NOECN;  	if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP) @@ -1123,7 +1126,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,  	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];  	if (sec_ctx != NULL) { -		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); +		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);  		if (!uctx)  			goto out; @@ -1339,6 +1342,12 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_  		max_spi = range->sadb_spirange_max;  	} +	err = verify_spi_info(x->id.proto, min_spi, max_spi); +	if (err) { +		xfrm_state_put(x); +		return err; +	} +  	err = xfrm_alloc_spi(x, min_spi, max_spi);  	resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x); @@ -1379,10 +1388,9 @@ static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, const struct sadb  		return 0;  	spin_lock_bh(&x->lock); -	if (x->km.state == XFRM_STATE_ACQ) { +	if (x->km.state == XFRM_STATE_ACQ)  		x->km.state = XFRM_STATE_ERROR; -		wake_up(&net->xfrm.km_waitq); -	} +  	spin_unlock_bh(&x->lock);  	xfrm_state_put(x);  	return 0; @@ -1468,9 +1476,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg  	else  		err = xfrm_state_update(x); -	xfrm_audit_state_add(x, err ? 0 : 1, -			     audit_get_loginuid(current), -			     audit_get_sessionid(current), 0); +	xfrm_audit_state_add(x, err ? 0 : 1, true);  	if (err < 0) {  		x->km.state = XFRM_STATE_DEAD; @@ -1524,9 +1530,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_  	c.event = XFRM_MSG_DELSA;  	km_state_notify(x, &c);  out: -	xfrm_audit_state_delete(x, err ? 0 : 1, -				audit_get_loginuid(current), -				audit_get_sessionid(current), 0); +	xfrm_audit_state_delete(x, err ? 0 : 1, true);  	xfrm_state_put(x);  	return err; @@ -1718,17 +1722,13 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m  	struct net *net = sock_net(sk);  	unsigned int proto;  	struct km_event c; -	struct xfrm_audit audit_info;  	int err, err2;  	proto = pfkey_satype2proto(hdr->sadb_msg_satype);  	if (proto == 0)  		return -EINVAL; -	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, true);  	err2 = unicast_flush_resp(sk, hdr);  	if (err || err2) {  		if (err == -ESRCH) /* empty table - go quietly */ @@ -1784,12 +1784,15 @@ static int pfkey_dump_sa(struct pfkey_sock *pfk)  static void pfkey_dump_sa_done(struct pfkey_sock *pfk)  { -	xfrm_state_walk_done(&pfk->dump.u.state); +	struct net *net = sock_net(&pfk->sk); + +	xfrm_state_walk_done(&pfk->dump.u.state, net);  }  static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)  {  	u8 proto; +	struct xfrm_address_filter *filter = NULL;  	struct pfkey_sock *pfk = pfkey_sk(sk);  	if (pfk->dump.dump != NULL) @@ -1799,11 +1802,27 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms  	if (proto == 0)  		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) +			return -ENOMEM; + +		memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr, +		       sizeof(xfrm_address_t)); +		memcpy(&filter->daddr, &xfilter->sadb_x_filter_daddr, +		       sizeof(xfrm_address_t)); +		filter->family = xfilter->sadb_x_filter_family; +		filter->splen = xfilter->sadb_x_filter_splen; +		filter->dplen = xfilter->sadb_x_filter_dplen; +	} +  	pfk->dump.msg_version = hdr->sadb_msg_version;  	pfk->dump.msg_portid = hdr->sadb_msg_pid;  	pfk->dump.dump = pfkey_dump_sa;  	pfk->dump.done = pfkey_dump_sa_done; -	xfrm_state_walk_init(&pfk->dump.u.state, proto); +	xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);  	return pfkey_do_dump(pfk);  } @@ -1860,7 +1879,7 @@ static u32 gen_reqid(struct net *net)  			reqid = IPSEC_MANUAL_REQID_MAX+1;  		xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);  		rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid); -		xfrm_policy_walk_done(&walk); +		xfrm_policy_walk_done(&walk, net);  		if (rc != -EEXIST)  			return reqid;  	} while (reqid != start); @@ -2223,14 +2242,14 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_  	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];  	if (sec_ctx != NULL) { -		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); +		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);  		if (!uctx) {  			err = -ENOBUFS;  			goto out;  		} -		err = security_xfrm_policy_alloc(&xp->security, uctx); +		err = security_xfrm_policy_alloc(&xp->security, uctx, GFP_KERNEL);  		kfree(uctx);  		if (err) @@ -2261,9 +2280,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_  	err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,  				 hdr->sadb_msg_type != SADB_X_SPDUPDATE); -	xfrm_audit_policy_add(xp, err ? 0 : 1, -			      audit_get_loginuid(current), -			      audit_get_sessionid(current), 0); +	xfrm_audit_policy_add(xp, err ? 0 : 1, true);  	if (err)  		goto out; @@ -2327,12 +2344,12 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa  	sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];  	if (sec_ctx != NULL) { -		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); +		struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);  		if (!uctx)  			return -ENOMEM; -		err = security_xfrm_policy_alloc(&pol_ctx, uctx); +		err = security_xfrm_policy_alloc(&pol_ctx, uctx, GFP_KERNEL);  		kfree(uctx);  		if (err)  			return err; @@ -2345,9 +2362,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa  	if (xp == NULL)  		return -ENOENT; -	xfrm_audit_policy_delete(xp, err ? 0 : 1, -				 audit_get_loginuid(current), -				 audit_get_sessionid(current), 0); +	xfrm_audit_policy_delete(xp, err ? 0 : 1, true);  	if (err)  		goto out; @@ -2484,6 +2499,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,  	struct xfrm_selector sel;  	struct xfrm_migrate m[XFRM_MAX_DEPTH];  	struct xfrm_kmaddress k; +	struct net *net = sock_net(sk);  	if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1],  				     ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) || @@ -2525,7 +2541,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,  		sel.sport_mask = htons(0xffff);  	/* set destination address info of selector */ -	sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1], +	sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1];  	pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);  	sel.prefixlen_d = sa->sadb_address_prefixlen;  	sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto); @@ -2557,7 +2573,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,  	}  	return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i, -			    kma ? &k : NULL); +			    kma ? &k : NULL, net);   out:  	return err; @@ -2594,9 +2610,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_  		return -ENOENT;  	if (delete) { -		xfrm_audit_policy_delete(xp, err ? 0 : 1, -				audit_get_loginuid(current), -				audit_get_sessionid(current), 0); +		xfrm_audit_policy_delete(xp, err ? 0 : 1, true);  		if (err)  			goto out; @@ -2658,7 +2672,9 @@ static int pfkey_dump_sp(struct pfkey_sock *pfk)  static void pfkey_dump_sp_done(struct pfkey_sock *pfk)  { -	xfrm_policy_walk_done(&pfk->dump.u.policy); +	struct net *net = sock_net((struct sock *)pfk); + +	xfrm_policy_walk_done(&pfk->dump.u.policy, net);  }  static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) @@ -2703,13 +2719,9 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad  {  	struct net *net = sock_net(sk);  	struct km_event c; -	struct xfrm_audit audit_info;  	int err, err2; -	audit_info.loginuid = audit_get_loginuid(current); -	audit_info.sessionid = audit_get_sessionid(current); -	audit_info.secid = 0; -	err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); +	err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true);  	err2 = unicast_flush_resp(sk, hdr);  	if (err || err2) {  		if (err == -ESRCH) /* empty table - old silent behavior */ @@ -3048,6 +3060,24 @@ static u32 get_acqseq(void)  	return res;  } +static bool pfkey_is_alive(const struct km_event *c) +{ +	struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id); +	struct sock *sk; +	bool is_alive = false; + +	rcu_read_lock(); +	sk_for_each_rcu(sk, &net_pfkey->table) { +		if (pfkey_sk(sk)->registered) { +			is_alive = true; +			break; +		} +	} +	rcu_read_unlock(); + +	return is_alive; +} +  static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)  {  	struct sk_buff *skb; @@ -3228,8 +3258,8 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,  		}  		if ((*dir = verify_sec_ctx_len(p)))  			goto out; -		uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); -		*dir = security_xfrm_policy_alloc(&xp->security, uctx); +		uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_ATOMIC); +		*dir = security_xfrm_policy_alloc(&xp->security, uctx, GFP_ATOMIC);  		kfree(uctx);  		if (*dir) @@ -3568,6 +3598,7 @@ static int pfkey_sendmsg(struct kiocb *kiocb,  	struct sk_buff *skb = NULL;  	struct sadb_msg *hdr = NULL;  	int err; +	struct net *net = sock_net(sk);  	err = -EOPNOTSUPP;  	if (msg->msg_flags & MSG_OOB) @@ -3590,9 +3621,9 @@ static int pfkey_sendmsg(struct kiocb *kiocb,  	if (!hdr)  		goto out; -	mutex_lock(&xfrm_cfg_mutex); +	mutex_lock(&net->xfrm.xfrm_cfg_mutex);  	err = pfkey_process(sk, skb, hdr); -	mutex_unlock(&xfrm_cfg_mutex); +	mutex_unlock(&net->xfrm.xfrm_cfg_mutex);  out:  	if (err && hdr && pfkey_error(hdr, err, sk) == 0) @@ -3615,7 +3646,6 @@ static int pfkey_recvmsg(struct kiocb *kiocb,  	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))  		goto out; -	msg->msg_namelen = 0;  	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);  	if (skb == NULL)  		goto out; @@ -3773,6 +3803,7 @@ static struct xfrm_mgr pfkeyv2_mgr =  	.new_mapping	= pfkey_send_new_mapping,  	.notify_policy	= pfkey_send_policy_notify,  	.migrate	= pfkey_send_migrate, +	.is_alive	= pfkey_is_alive,  };  static int __net_init pfkey_net_init(struct net *net)  | 
