xref: /linux/net/xfrm/xfrm_user.c (revision 221df1ed33c9284fc7a6f6e47ca7f8d5f3665d43)
11da177e4SLinus Torvalds /* xfrm_user.c: User interface to configure xfrm engine.
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Changes:
61da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI
71da177e4SLinus Torvalds  * 	Kazunori MIYAZAWA @USAGI
81da177e4SLinus Torvalds  * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
91da177e4SLinus Torvalds  * 		IPv6 support
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
139409f38aSHerbert Xu #include <linux/crypto.h>
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/types.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
181da177e4SLinus Torvalds #include <linux/socket.h>
191da177e4SLinus Torvalds #include <linux/string.h>
201da177e4SLinus Torvalds #include <linux/net.h>
211da177e4SLinus Torvalds #include <linux/skbuff.h>
221da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
231da177e4SLinus Torvalds #include <linux/ipsec.h>
241da177e4SLinus Torvalds #include <linux/init.h>
251da177e4SLinus Torvalds #include <linux/security.h>
261da177e4SLinus Torvalds #include <net/sock.h>
271da177e4SLinus Torvalds #include <net/xfrm.h>
2888fc2c84SThomas Graf #include <net/netlink.h>
291da177e4SLinus Torvalds #include <asm/uaccess.h>
30e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
31e23c7194SMasahide NAKAMURA #include <linux/in6.h>
32e23c7194SMasahide NAKAMURA #endif
331da177e4SLinus Torvalds 
341a6509d9SHerbert Xu static inline int aead_len(struct xfrm_algo_aead *alg)
351a6509d9SHerbert Xu {
361a6509d9SHerbert Xu 	return sizeof(*alg) + ((alg->alg_key_len + 7) / 8);
371a6509d9SHerbert Xu }
381a6509d9SHerbert Xu 
395424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
401da177e4SLinus Torvalds {
415424f32eSThomas Graf 	struct nlattr *rt = attrs[type];
421da177e4SLinus Torvalds 	struct xfrm_algo *algp;
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds 	if (!rt)
451da177e4SLinus Torvalds 		return 0;
461da177e4SLinus Torvalds 
475424f32eSThomas Graf 	algp = nla_data(rt);
480f99be0dSEric Dumazet 	if (nla_len(rt) < xfrm_alg_len(algp))
4931c26852SHerbert Xu 		return -EINVAL;
5031c26852SHerbert Xu 
511da177e4SLinus Torvalds 	switch (type) {
521da177e4SLinus Torvalds 	case XFRMA_ALG_AUTH:
531da177e4SLinus Torvalds 	case XFRMA_ALG_CRYPT:
541da177e4SLinus Torvalds 	case XFRMA_ALG_COMP:
551da177e4SLinus Torvalds 		break;
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	default:
581da177e4SLinus Torvalds 		return -EINVAL;
593ff50b79SStephen Hemminger 	}
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
621da177e4SLinus Torvalds 	return 0;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651a6509d9SHerbert Xu static int verify_aead(struct nlattr **attrs)
661a6509d9SHerbert Xu {
671a6509d9SHerbert Xu 	struct nlattr *rt = attrs[XFRMA_ALG_AEAD];
681a6509d9SHerbert Xu 	struct xfrm_algo_aead *algp;
691a6509d9SHerbert Xu 
701a6509d9SHerbert Xu 	if (!rt)
711a6509d9SHerbert Xu 		return 0;
721a6509d9SHerbert Xu 
731a6509d9SHerbert Xu 	algp = nla_data(rt);
741a6509d9SHerbert Xu 	if (nla_len(rt) < aead_len(algp))
751a6509d9SHerbert Xu 		return -EINVAL;
761a6509d9SHerbert Xu 
771a6509d9SHerbert Xu 	algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
781a6509d9SHerbert Xu 	return 0;
791a6509d9SHerbert Xu }
801a6509d9SHerbert Xu 
815424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,
82eb2971b6SMasahide NAKAMURA 			   xfrm_address_t **addrp)
83eb2971b6SMasahide NAKAMURA {
845424f32eSThomas Graf 	struct nlattr *rt = attrs[type];
85eb2971b6SMasahide NAKAMURA 
86cf5cb79fSThomas Graf 	if (rt && addrp)
875424f32eSThomas Graf 		*addrp = nla_data(rt);
88eb2971b6SMasahide NAKAMURA }
89df71837dSTrent Jaeger 
905424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs)
91df71837dSTrent Jaeger {
925424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_SEC_CTX];
93df71837dSTrent Jaeger 	struct xfrm_user_sec_ctx *uctx;
94df71837dSTrent Jaeger 
95df71837dSTrent Jaeger 	if (!rt)
96df71837dSTrent Jaeger 		return 0;
97df71837dSTrent Jaeger 
985424f32eSThomas Graf 	uctx = nla_data(rt);
99cf5cb79fSThomas Graf 	if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len))
100df71837dSTrent Jaeger 		return -EINVAL;
101df71837dSTrent Jaeger 
102df71837dSTrent Jaeger 	return 0;
103df71837dSTrent Jaeger }
104df71837dSTrent Jaeger 
105df71837dSTrent Jaeger 
1061da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p,
1075424f32eSThomas Graf 			     struct nlattr **attrs)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds 	int err;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	err = -EINVAL;
1121da177e4SLinus Torvalds 	switch (p->family) {
1131da177e4SLinus Torvalds 	case AF_INET:
1141da177e4SLinus Torvalds 		break;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	case AF_INET6:
1171da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
1181da177e4SLinus Torvalds 		break;
1191da177e4SLinus Torvalds #else
1201da177e4SLinus Torvalds 		err = -EAFNOSUPPORT;
1211da177e4SLinus Torvalds 		goto out;
1221da177e4SLinus Torvalds #endif
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	default:
1251da177e4SLinus Torvalds 		goto out;
1263ff50b79SStephen Hemminger 	}
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	err = -EINVAL;
1291da177e4SLinus Torvalds 	switch (p->id.proto) {
1301da177e4SLinus Torvalds 	case IPPROTO_AH:
13135a7aa08SThomas Graf 		if (!attrs[XFRMA_ALG_AUTH]	||
1321a6509d9SHerbert Xu 		    attrs[XFRMA_ALG_AEAD]	||
13335a7aa08SThomas Graf 		    attrs[XFRMA_ALG_CRYPT]	||
13435a7aa08SThomas Graf 		    attrs[XFRMA_ALG_COMP])
1351da177e4SLinus Torvalds 			goto out;
1361da177e4SLinus Torvalds 		break;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	case IPPROTO_ESP:
1391a6509d9SHerbert Xu 		if (attrs[XFRMA_ALG_COMP])
1401a6509d9SHerbert Xu 			goto out;
1411a6509d9SHerbert Xu 		if (!attrs[XFRMA_ALG_AUTH] &&
1421a6509d9SHerbert Xu 		    !attrs[XFRMA_ALG_CRYPT] &&
1431a6509d9SHerbert Xu 		    !attrs[XFRMA_ALG_AEAD])
1441a6509d9SHerbert Xu 			goto out;
1451a6509d9SHerbert Xu 		if ((attrs[XFRMA_ALG_AUTH] ||
1461a6509d9SHerbert Xu 		     attrs[XFRMA_ALG_CRYPT]) &&
1471a6509d9SHerbert Xu 		    attrs[XFRMA_ALG_AEAD])
1481da177e4SLinus Torvalds 			goto out;
1491da177e4SLinus Torvalds 		break;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	case IPPROTO_COMP:
15235a7aa08SThomas Graf 		if (!attrs[XFRMA_ALG_COMP]	||
1531a6509d9SHerbert Xu 		    attrs[XFRMA_ALG_AEAD]	||
15435a7aa08SThomas Graf 		    attrs[XFRMA_ALG_AUTH]	||
15535a7aa08SThomas Graf 		    attrs[XFRMA_ALG_CRYPT])
1561da177e4SLinus Torvalds 			goto out;
1571da177e4SLinus Torvalds 		break;
1581da177e4SLinus Torvalds 
159e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
160e23c7194SMasahide NAKAMURA 	case IPPROTO_DSTOPTS:
161e23c7194SMasahide NAKAMURA 	case IPPROTO_ROUTING:
16235a7aa08SThomas Graf 		if (attrs[XFRMA_ALG_COMP]	||
16335a7aa08SThomas Graf 		    attrs[XFRMA_ALG_AUTH]	||
1641a6509d9SHerbert Xu 		    attrs[XFRMA_ALG_AEAD]	||
16535a7aa08SThomas Graf 		    attrs[XFRMA_ALG_CRYPT]	||
16635a7aa08SThomas Graf 		    attrs[XFRMA_ENCAP]		||
16735a7aa08SThomas Graf 		    attrs[XFRMA_SEC_CTX]	||
16835a7aa08SThomas Graf 		    !attrs[XFRMA_COADDR])
169e23c7194SMasahide NAKAMURA 			goto out;
170e23c7194SMasahide NAKAMURA 		break;
171e23c7194SMasahide NAKAMURA #endif
172e23c7194SMasahide NAKAMURA 
1731da177e4SLinus Torvalds 	default:
1741da177e4SLinus Torvalds 		goto out;
1753ff50b79SStephen Hemminger 	}
1761da177e4SLinus Torvalds 
1771a6509d9SHerbert Xu 	if ((err = verify_aead(attrs)))
1781a6509d9SHerbert Xu 		goto out;
17935a7aa08SThomas Graf 	if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH)))
1801da177e4SLinus Torvalds 		goto out;
18135a7aa08SThomas Graf 	if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT)))
1821da177e4SLinus Torvalds 		goto out;
18335a7aa08SThomas Graf 	if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP)))
1841da177e4SLinus Torvalds 		goto out;
18535a7aa08SThomas Graf 	if ((err = verify_sec_ctx_len(attrs)))
186df71837dSTrent Jaeger 		goto out;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	err = -EINVAL;
1891da177e4SLinus Torvalds 	switch (p->mode) {
1907e49e6deSMasahide NAKAMURA 	case XFRM_MODE_TRANSPORT:
1917e49e6deSMasahide NAKAMURA 	case XFRM_MODE_TUNNEL:
192060f02a3SNoriaki TAKAMIYA 	case XFRM_MODE_ROUTEOPTIMIZATION:
1930a69452cSDiego Beltrami 	case XFRM_MODE_BEET:
1941da177e4SLinus Torvalds 		break;
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	default:
1971da177e4SLinus Torvalds 		goto out;
1983ff50b79SStephen Hemminger 	}
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	err = 0;
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds out:
2031da177e4SLinus Torvalds 	return err;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
2071da177e4SLinus Torvalds 			   struct xfrm_algo_desc *(*get_byname)(char *, int),
2085424f32eSThomas Graf 			   struct nlattr *rta)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds 	struct xfrm_algo *p, *ualg;
2111da177e4SLinus Torvalds 	struct xfrm_algo_desc *algo;
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 	if (!rta)
2141da177e4SLinus Torvalds 		return 0;
2151da177e4SLinus Torvalds 
2165424f32eSThomas Graf 	ualg = nla_data(rta);
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	algo = get_byname(ualg->alg_name, 1);
2191da177e4SLinus Torvalds 	if (!algo)
2201da177e4SLinus Torvalds 		return -ENOSYS;
2211da177e4SLinus Torvalds 	*props = algo->desc.sadb_alg_id;
2221da177e4SLinus Torvalds 
2230f99be0dSEric Dumazet 	p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
2241da177e4SLinus Torvalds 	if (!p)
2251da177e4SLinus Torvalds 		return -ENOMEM;
2261da177e4SLinus Torvalds 
22704ff1260SHerbert Xu 	strcpy(p->alg_name, algo->name);
2281da177e4SLinus Torvalds 	*algpp = p;
2291da177e4SLinus Torvalds 	return 0;
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
2321a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
2331a6509d9SHerbert Xu 		       struct nlattr *rta)
2341a6509d9SHerbert Xu {
2351a6509d9SHerbert Xu 	struct xfrm_algo_aead *p, *ualg;
2361a6509d9SHerbert Xu 	struct xfrm_algo_desc *algo;
2371a6509d9SHerbert Xu 
2381a6509d9SHerbert Xu 	if (!rta)
2391a6509d9SHerbert Xu 		return 0;
2401a6509d9SHerbert Xu 
2411a6509d9SHerbert Xu 	ualg = nla_data(rta);
2421a6509d9SHerbert Xu 
2431a6509d9SHerbert Xu 	algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
2441a6509d9SHerbert Xu 	if (!algo)
2451a6509d9SHerbert Xu 		return -ENOSYS;
2461a6509d9SHerbert Xu 	*props = algo->desc.sadb_alg_id;
2471a6509d9SHerbert Xu 
2481a6509d9SHerbert Xu 	p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
2491a6509d9SHerbert Xu 	if (!p)
2501a6509d9SHerbert Xu 		return -ENOMEM;
2511a6509d9SHerbert Xu 
2521a6509d9SHerbert Xu 	strcpy(p->alg_name, algo->name);
2531a6509d9SHerbert Xu 	*algpp = p;
2541a6509d9SHerbert Xu 	return 0;
2551a6509d9SHerbert Xu }
2561a6509d9SHerbert Xu 
257661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
258df71837dSTrent Jaeger {
259df71837dSTrent Jaeger 	int len = 0;
260df71837dSTrent Jaeger 
261df71837dSTrent Jaeger 	if (xfrm_ctx) {
262df71837dSTrent Jaeger 		len += sizeof(struct xfrm_user_sec_ctx);
263df71837dSTrent Jaeger 		len += xfrm_ctx->ctx_len;
264df71837dSTrent Jaeger 	}
265df71837dSTrent Jaeger 	return len;
266df71837dSTrent Jaeger }
267df71837dSTrent Jaeger 
2681da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds 	memcpy(&x->id, &p->id, sizeof(x->id));
2711da177e4SLinus Torvalds 	memcpy(&x->sel, &p->sel, sizeof(x->sel));
2721da177e4SLinus Torvalds 	memcpy(&x->lft, &p->lft, sizeof(x->lft));
2731da177e4SLinus Torvalds 	x->props.mode = p->mode;
2741da177e4SLinus Torvalds 	x->props.replay_window = p->replay_window;
2751da177e4SLinus Torvalds 	x->props.reqid = p->reqid;
2761da177e4SLinus Torvalds 	x->props.family = p->family;
27754489c14SDavid S. Miller 	memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
2781da177e4SLinus Torvalds 	x->props.flags = p->flags;
279196b0036SHerbert Xu 
280ccf9b3b8SSteffen Klassert 	if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC))
281196b0036SHerbert Xu 		x->sel.family = p->family;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
284d51d081dSJamal Hadi Salim /*
285d51d081dSJamal Hadi Salim  * someday when pfkey also has support, we could have the code
286d51d081dSJamal Hadi Salim  * somehow made shareable and move it to xfrm_state.c - JHS
287d51d081dSJamal Hadi Salim  *
288d51d081dSJamal Hadi Salim */
2895424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
290d51d081dSJamal Hadi Salim {
2915424f32eSThomas Graf 	struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
2925424f32eSThomas Graf 	struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
2935424f32eSThomas Graf 	struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
2945424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
295d51d081dSJamal Hadi Salim 
296d51d081dSJamal Hadi Salim 	if (rp) {
297d51d081dSJamal Hadi Salim 		struct xfrm_replay_state *replay;
2985424f32eSThomas Graf 		replay = nla_data(rp);
299d51d081dSJamal Hadi Salim 		memcpy(&x->replay, replay, sizeof(*replay));
300d51d081dSJamal Hadi Salim 		memcpy(&x->preplay, replay, sizeof(*replay));
301d51d081dSJamal Hadi Salim 	}
302d51d081dSJamal Hadi Salim 
303d51d081dSJamal Hadi Salim 	if (lt) {
304d51d081dSJamal Hadi Salim 		struct xfrm_lifetime_cur *ltime;
3055424f32eSThomas Graf 		ltime = nla_data(lt);
306d51d081dSJamal Hadi Salim 		x->curlft.bytes = ltime->bytes;
307d51d081dSJamal Hadi Salim 		x->curlft.packets = ltime->packets;
308d51d081dSJamal Hadi Salim 		x->curlft.add_time = ltime->add_time;
309d51d081dSJamal Hadi Salim 		x->curlft.use_time = ltime->use_time;
310d51d081dSJamal Hadi Salim 	}
311d51d081dSJamal Hadi Salim 
312cf5cb79fSThomas Graf 	if (et)
3135424f32eSThomas Graf 		x->replay_maxage = nla_get_u32(et);
314d51d081dSJamal Hadi Salim 
315cf5cb79fSThomas Graf 	if (rt)
3165424f32eSThomas Graf 		x->replay_maxdiff = nla_get_u32(rt);
317d51d081dSJamal Hadi Salim }
318d51d081dSJamal Hadi Salim 
3191da177e4SLinus Torvalds static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
3205424f32eSThomas Graf 					       struct nlattr **attrs,
3211da177e4SLinus Torvalds 					       int *errp)
3221da177e4SLinus Torvalds {
323673c09beSAlexey Dobriyan 	struct xfrm_state *x = xfrm_state_alloc(&init_net);
3241da177e4SLinus Torvalds 	int err = -ENOMEM;
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	if (!x)
3271da177e4SLinus Torvalds 		goto error_no_put;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	copy_from_user_state(x, p);
3301da177e4SLinus Torvalds 
3311a6509d9SHerbert Xu 	if ((err = attach_aead(&x->aead, &x->props.ealgo,
3321a6509d9SHerbert Xu 			       attrs[XFRMA_ALG_AEAD])))
3331a6509d9SHerbert Xu 		goto error;
3341da177e4SLinus Torvalds 	if ((err = attach_one_algo(&x->aalg, &x->props.aalgo,
3351da177e4SLinus Torvalds 				   xfrm_aalg_get_byname,
33635a7aa08SThomas Graf 				   attrs[XFRMA_ALG_AUTH])))
3371da177e4SLinus Torvalds 		goto error;
3381da177e4SLinus Torvalds 	if ((err = attach_one_algo(&x->ealg, &x->props.ealgo,
3391da177e4SLinus Torvalds 				   xfrm_ealg_get_byname,
34035a7aa08SThomas Graf 				   attrs[XFRMA_ALG_CRYPT])))
3411da177e4SLinus Torvalds 		goto error;
3421da177e4SLinus Torvalds 	if ((err = attach_one_algo(&x->calg, &x->props.calgo,
3431da177e4SLinus Torvalds 				   xfrm_calg_get_byname,
34435a7aa08SThomas Graf 				   attrs[XFRMA_ALG_COMP])))
3451da177e4SLinus Torvalds 		goto error;
346fd21150aSThomas Graf 
347fd21150aSThomas Graf 	if (attrs[XFRMA_ENCAP]) {
348fd21150aSThomas Graf 		x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]),
349fd21150aSThomas Graf 				   sizeof(*x->encap), GFP_KERNEL);
350fd21150aSThomas Graf 		if (x->encap == NULL)
3511da177e4SLinus Torvalds 			goto error;
352fd21150aSThomas Graf 	}
353fd21150aSThomas Graf 
354fd21150aSThomas Graf 	if (attrs[XFRMA_COADDR]) {
355fd21150aSThomas Graf 		x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]),
356fd21150aSThomas Graf 				    sizeof(*x->coaddr), GFP_KERNEL);
357fd21150aSThomas Graf 		if (x->coaddr == NULL)
358060f02a3SNoriaki TAKAMIYA 			goto error;
359fd21150aSThomas Graf 	}
360fd21150aSThomas Graf 
36172cb6962SHerbert Xu 	err = xfrm_init_state(x);
3621da177e4SLinus Torvalds 	if (err)
3631da177e4SLinus Torvalds 		goto error;
3641da177e4SLinus Torvalds 
365fd21150aSThomas Graf 	if (attrs[XFRMA_SEC_CTX] &&
366fd21150aSThomas Graf 	    security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX])))
367df71837dSTrent Jaeger 		goto error;
368df71837dSTrent Jaeger 
3691da177e4SLinus Torvalds 	x->km.seq = p->seq;
370d51d081dSJamal Hadi Salim 	x->replay_maxdiff = sysctl_xfrm_aevent_rseqth;
371d51d081dSJamal Hadi Salim 	/* sysctl_xfrm_aevent_etime is in 100ms units */
372d51d081dSJamal Hadi Salim 	x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M;
373d51d081dSJamal Hadi Salim 	x->preplay.bitmap = 0;
374d51d081dSJamal Hadi Salim 	x->preplay.seq = x->replay.seq+x->replay_maxdiff;
375d51d081dSJamal Hadi Salim 	x->preplay.oseq = x->replay.oseq +x->replay_maxdiff;
376d51d081dSJamal Hadi Salim 
377d51d081dSJamal Hadi Salim 	/* override default values from above */
378d51d081dSJamal Hadi Salim 
3795424f32eSThomas Graf 	xfrm_update_ae_params(x, attrs);
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	return x;
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds error:
3841da177e4SLinus Torvalds 	x->km.state = XFRM_STATE_DEAD;
3851da177e4SLinus Torvalds 	xfrm_state_put(x);
3861da177e4SLinus Torvalds error_no_put:
3871da177e4SLinus Torvalds 	*errp = err;
3881da177e4SLinus Torvalds 	return NULL;
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
39122e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
3925424f32eSThomas Graf 		struct nlattr **attrs)
3931da177e4SLinus Torvalds {
3947b67c857SThomas Graf 	struct xfrm_usersa_info *p = nlmsg_data(nlh);
3951da177e4SLinus Torvalds 	struct xfrm_state *x;
3961da177e4SLinus Torvalds 	int err;
39726b15dadSJamal Hadi Salim 	struct km_event c;
3982532386fSEric Paris 	uid_t loginuid = NETLINK_CB(skb).loginuid;
3992532386fSEric Paris 	u32 sessionid = NETLINK_CB(skb).sessionid;
4002532386fSEric Paris 	u32 sid = NETLINK_CB(skb).sid;
4011da177e4SLinus Torvalds 
40235a7aa08SThomas Graf 	err = verify_newsa_info(p, attrs);
4031da177e4SLinus Torvalds 	if (err)
4041da177e4SLinus Torvalds 		return err;
4051da177e4SLinus Torvalds 
40635a7aa08SThomas Graf 	x = xfrm_state_construct(p, attrs, &err);
4071da177e4SLinus Torvalds 	if (!x)
4081da177e4SLinus Torvalds 		return err;
4091da177e4SLinus Torvalds 
41026b15dadSJamal Hadi Salim 	xfrm_state_hold(x);
4111da177e4SLinus Torvalds 	if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
4121da177e4SLinus Torvalds 		err = xfrm_state_add(x);
4131da177e4SLinus Torvalds 	else
4141da177e4SLinus Torvalds 		err = xfrm_state_update(x);
4151da177e4SLinus Torvalds 
4162532386fSEric Paris 	xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid);
417161a09e7SJoy Latten 
4181da177e4SLinus Torvalds 	if (err < 0) {
4191da177e4SLinus Torvalds 		x->km.state = XFRM_STATE_DEAD;
42021380b81SHerbert Xu 		__xfrm_state_put(x);
4217d6dfe1fSPatrick McHardy 		goto out;
4221da177e4SLinus Torvalds 	}
4231da177e4SLinus Torvalds 
42426b15dadSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
42526b15dadSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
426f60f6b8fSHerbert Xu 	c.event = nlh->nlmsg_type;
42726b15dadSJamal Hadi Salim 
42826b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
4297d6dfe1fSPatrick McHardy out:
43026b15dadSJamal Hadi Salim 	xfrm_state_put(x);
4311da177e4SLinus Torvalds 	return err;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
434eb2971b6SMasahide NAKAMURA static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p,
4355424f32eSThomas Graf 						 struct nlattr **attrs,
436eb2971b6SMasahide NAKAMURA 						 int *errp)
437eb2971b6SMasahide NAKAMURA {
438eb2971b6SMasahide NAKAMURA 	struct xfrm_state *x = NULL;
439eb2971b6SMasahide NAKAMURA 	int err;
440eb2971b6SMasahide NAKAMURA 
441eb2971b6SMasahide NAKAMURA 	if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) {
442eb2971b6SMasahide NAKAMURA 		err = -ESRCH;
443*221df1edSAlexey Dobriyan 		x = xfrm_state_lookup(&init_net, &p->daddr, p->spi, p->proto, p->family);
444eb2971b6SMasahide NAKAMURA 	} else {
445eb2971b6SMasahide NAKAMURA 		xfrm_address_t *saddr = NULL;
446eb2971b6SMasahide NAKAMURA 
44735a7aa08SThomas Graf 		verify_one_addr(attrs, XFRMA_SRCADDR, &saddr);
448eb2971b6SMasahide NAKAMURA 		if (!saddr) {
449eb2971b6SMasahide NAKAMURA 			err = -EINVAL;
450eb2971b6SMasahide NAKAMURA 			goto out;
451eb2971b6SMasahide NAKAMURA 		}
452eb2971b6SMasahide NAKAMURA 
4539abbffeeSMasahide NAKAMURA 		err = -ESRCH;
454*221df1edSAlexey Dobriyan 		x = xfrm_state_lookup_byaddr(&init_net, &p->daddr, saddr,
455*221df1edSAlexey Dobriyan 					     p->proto, p->family);
456eb2971b6SMasahide NAKAMURA 	}
457eb2971b6SMasahide NAKAMURA 
458eb2971b6SMasahide NAKAMURA  out:
459eb2971b6SMasahide NAKAMURA 	if (!x && errp)
460eb2971b6SMasahide NAKAMURA 		*errp = err;
461eb2971b6SMasahide NAKAMURA 	return x;
462eb2971b6SMasahide NAKAMURA }
463eb2971b6SMasahide NAKAMURA 
46422e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
4655424f32eSThomas Graf 		struct nlattr **attrs)
4661da177e4SLinus Torvalds {
4671da177e4SLinus Torvalds 	struct xfrm_state *x;
468eb2971b6SMasahide NAKAMURA 	int err = -ESRCH;
46926b15dadSJamal Hadi Salim 	struct km_event c;
4707b67c857SThomas Graf 	struct xfrm_usersa_id *p = nlmsg_data(nlh);
4712532386fSEric Paris 	uid_t loginuid = NETLINK_CB(skb).loginuid;
4722532386fSEric Paris 	u32 sessionid = NETLINK_CB(skb).sessionid;
4732532386fSEric Paris 	u32 sid = NETLINK_CB(skb).sid;
4741da177e4SLinus Torvalds 
47535a7aa08SThomas Graf 	x = xfrm_user_state_lookup(p, attrs, &err);
4761da177e4SLinus Torvalds 	if (x == NULL)
477eb2971b6SMasahide NAKAMURA 		return err;
4781da177e4SLinus Torvalds 
4796f68dc37SDavid S. Miller 	if ((err = security_xfrm_state_delete(x)) != 0)
480c8c05a8eSCatherine Zhang 		goto out;
481c8c05a8eSCatherine Zhang 
4821da177e4SLinus Torvalds 	if (xfrm_state_kern(x)) {
483c8c05a8eSCatherine Zhang 		err = -EPERM;
484c8c05a8eSCatherine Zhang 		goto out;
4851da177e4SLinus Torvalds 	}
4861da177e4SLinus Torvalds 
48726b15dadSJamal Hadi Salim 	err = xfrm_state_delete(x);
488161a09e7SJoy Latten 
489c8c05a8eSCatherine Zhang 	if (err < 0)
490c8c05a8eSCatherine Zhang 		goto out;
49126b15dadSJamal Hadi Salim 
49226b15dadSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
49326b15dadSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
494f60f6b8fSHerbert Xu 	c.event = nlh->nlmsg_type;
49526b15dadSJamal Hadi Salim 	km_state_notify(x, &c);
4961da177e4SLinus Torvalds 
497c8c05a8eSCatherine Zhang out:
4982532386fSEric Paris 	xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid);
499c8c05a8eSCatherine Zhang 	xfrm_state_put(x);
50026b15dadSJamal Hadi Salim 	return err;
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
5041da177e4SLinus Torvalds {
5051da177e4SLinus Torvalds 	memcpy(&p->id, &x->id, sizeof(p->id));
5061da177e4SLinus Torvalds 	memcpy(&p->sel, &x->sel, sizeof(p->sel));
5071da177e4SLinus Torvalds 	memcpy(&p->lft, &x->lft, sizeof(p->lft));
5081da177e4SLinus Torvalds 	memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));
5091da177e4SLinus Torvalds 	memcpy(&p->stats, &x->stats, sizeof(p->stats));
51054489c14SDavid S. Miller 	memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr));
5111da177e4SLinus Torvalds 	p->mode = x->props.mode;
5121da177e4SLinus Torvalds 	p->replay_window = x->props.replay_window;
5131da177e4SLinus Torvalds 	p->reqid = x->props.reqid;
5141da177e4SLinus Torvalds 	p->family = x->props.family;
5151da177e4SLinus Torvalds 	p->flags = x->props.flags;
5161da177e4SLinus Torvalds 	p->seq = x->km.seq;
5171da177e4SLinus Torvalds }
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds struct xfrm_dump_info {
5201da177e4SLinus Torvalds 	struct sk_buff *in_skb;
5211da177e4SLinus Torvalds 	struct sk_buff *out_skb;
5221da177e4SLinus Torvalds 	u32 nlmsg_seq;
5231da177e4SLinus Torvalds 	u16 nlmsg_flags;
5241da177e4SLinus Torvalds };
5251da177e4SLinus Torvalds 
526c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
527c0144beaSThomas Graf {
528c0144beaSThomas Graf 	struct xfrm_user_sec_ctx *uctx;
529c0144beaSThomas Graf 	struct nlattr *attr;
53068325d3bSHerbert Xu 	int ctx_size = sizeof(*uctx) + s->ctx_len;
531c0144beaSThomas Graf 
532c0144beaSThomas Graf 	attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size);
533c0144beaSThomas Graf 	if (attr == NULL)
534c0144beaSThomas Graf 		return -EMSGSIZE;
535c0144beaSThomas Graf 
536c0144beaSThomas Graf 	uctx = nla_data(attr);
537c0144beaSThomas Graf 	uctx->exttype = XFRMA_SEC_CTX;
538c0144beaSThomas Graf 	uctx->len = ctx_size;
539c0144beaSThomas Graf 	uctx->ctx_doi = s->ctx_doi;
540c0144beaSThomas Graf 	uctx->ctx_alg = s->ctx_alg;
541c0144beaSThomas Graf 	uctx->ctx_len = s->ctx_len;
542c0144beaSThomas Graf 	memcpy(uctx + 1, s->ctx_str, s->ctx_len);
543c0144beaSThomas Graf 
544c0144beaSThomas Graf 	return 0;
545c0144beaSThomas Graf }
546c0144beaSThomas Graf 
54768325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */
54868325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x,
54968325d3bSHerbert Xu 				    struct xfrm_usersa_info *p,
55068325d3bSHerbert Xu 				    struct sk_buff *skb)
5511da177e4SLinus Torvalds {
5521da177e4SLinus Torvalds 	copy_to_user_state(x, p);
5531da177e4SLinus Torvalds 
554050f009eSHerbert Xu 	if (x->coaddr)
555050f009eSHerbert Xu 		NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);
556050f009eSHerbert Xu 
557050f009eSHerbert Xu 	if (x->lastused)
558050f009eSHerbert Xu 		NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused);
559050f009eSHerbert Xu 
5601a6509d9SHerbert Xu 	if (x->aead)
5611a6509d9SHerbert Xu 		NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead);
5621da177e4SLinus Torvalds 	if (x->aalg)
5630f99be0dSEric Dumazet 		NLA_PUT(skb, XFRMA_ALG_AUTH, xfrm_alg_len(x->aalg), x->aalg);
5641da177e4SLinus Torvalds 	if (x->ealg)
5650f99be0dSEric Dumazet 		NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg);
5661da177e4SLinus Torvalds 	if (x->calg)
567c0144beaSThomas Graf 		NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg);
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	if (x->encap)
570c0144beaSThomas Graf 		NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
5711da177e4SLinus Torvalds 
572c0144beaSThomas Graf 	if (x->security && copy_sec_ctx(x->security, skb) < 0)
573c0144beaSThomas Graf 		goto nla_put_failure;
574060f02a3SNoriaki TAKAMIYA 
57568325d3bSHerbert Xu 	return 0;
57668325d3bSHerbert Xu 
57768325d3bSHerbert Xu nla_put_failure:
57868325d3bSHerbert Xu 	return -EMSGSIZE;
57968325d3bSHerbert Xu }
58068325d3bSHerbert Xu 
58168325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr)
58268325d3bSHerbert Xu {
58368325d3bSHerbert Xu 	struct xfrm_dump_info *sp = ptr;
58468325d3bSHerbert Xu 	struct sk_buff *in_skb = sp->in_skb;
58568325d3bSHerbert Xu 	struct sk_buff *skb = sp->out_skb;
58668325d3bSHerbert Xu 	struct xfrm_usersa_info *p;
58768325d3bSHerbert Xu 	struct nlmsghdr *nlh;
58868325d3bSHerbert Xu 	int err;
58968325d3bSHerbert Xu 
59068325d3bSHerbert Xu 	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq,
59168325d3bSHerbert Xu 			XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags);
59268325d3bSHerbert Xu 	if (nlh == NULL)
59368325d3bSHerbert Xu 		return -EMSGSIZE;
59468325d3bSHerbert Xu 
59568325d3bSHerbert Xu 	p = nlmsg_data(nlh);
59668325d3bSHerbert Xu 
59768325d3bSHerbert Xu 	err = copy_to_user_state_extra(x, p, skb);
59868325d3bSHerbert Xu 	if (err)
59968325d3bSHerbert Xu 		goto nla_put_failure;
60068325d3bSHerbert Xu 
6019825069dSThomas Graf 	nlmsg_end(skb, nlh);
6021da177e4SLinus Torvalds 	return 0;
6031da177e4SLinus Torvalds 
604c0144beaSThomas Graf nla_put_failure:
6059825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
60668325d3bSHerbert Xu 	return err;
6071da177e4SLinus Torvalds }
6081da177e4SLinus Torvalds 
6094c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb)
6104c563f76STimo Teras {
6114c563f76STimo Teras 	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
6124c563f76STimo Teras 	xfrm_state_walk_done(walk);
6134c563f76STimo Teras 	return 0;
6144c563f76STimo Teras }
6154c563f76STimo Teras 
6161da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
6171da177e4SLinus Torvalds {
6184c563f76STimo Teras 	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
6191da177e4SLinus Torvalds 	struct xfrm_dump_info info;
6201da177e4SLinus Torvalds 
6214c563f76STimo Teras 	BUILD_BUG_ON(sizeof(struct xfrm_state_walk) >
6224c563f76STimo Teras 		     sizeof(cb->args) - sizeof(cb->args[0]));
6234c563f76STimo Teras 
6241da177e4SLinus Torvalds 	info.in_skb = cb->skb;
6251da177e4SLinus Torvalds 	info.out_skb = skb;
6261da177e4SLinus Torvalds 	info.nlmsg_seq = cb->nlh->nlmsg_seq;
6271da177e4SLinus Torvalds 	info.nlmsg_flags = NLM_F_MULTI;
6284c563f76STimo Teras 
6294c563f76STimo Teras 	if (!cb->args[0]) {
6304c563f76STimo Teras 		cb->args[0] = 1;
6314c563f76STimo Teras 		xfrm_state_walk_init(walk, 0);
6324c563f76STimo Teras 	}
6334c563f76STimo Teras 
6344c563f76STimo Teras 	(void) xfrm_state_walk(walk, dump_one_state, &info);
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 	return skb->len;
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
6401da177e4SLinus Torvalds 					  struct xfrm_state *x, u32 seq)
6411da177e4SLinus Torvalds {
6421da177e4SLinus Torvalds 	struct xfrm_dump_info info;
6431da177e4SLinus Torvalds 	struct sk_buff *skb;
6441da177e4SLinus Torvalds 
6457deb2264SThomas Graf 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
6461da177e4SLinus Torvalds 	if (!skb)
6471da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 	info.in_skb = in_skb;
6501da177e4SLinus Torvalds 	info.out_skb = skb;
6511da177e4SLinus Torvalds 	info.nlmsg_seq = seq;
6521da177e4SLinus Torvalds 	info.nlmsg_flags = 0;
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 	if (dump_one_state(x, 0, &info)) {
6551da177e4SLinus Torvalds 		kfree_skb(skb);
6561da177e4SLinus Torvalds 		return NULL;
6571da177e4SLinus Torvalds 	}
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	return skb;
6601da177e4SLinus Torvalds }
6611da177e4SLinus Torvalds 
6627deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void)
6637deb2264SThomas Graf {
6647deb2264SThomas Graf 	return NLMSG_ALIGN(4)
6657deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrmu_spdinfo))
6667deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrmu_spdhinfo));
6677deb2264SThomas Graf }
6687deb2264SThomas Graf 
669ecfd6b18SJamal Hadi Salim static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags)
670ecfd6b18SJamal Hadi Salim {
6715a6d3416SJamal Hadi Salim 	struct xfrmk_spdinfo si;
6725a6d3416SJamal Hadi Salim 	struct xfrmu_spdinfo spc;
6735a6d3416SJamal Hadi Salim 	struct xfrmu_spdhinfo sph;
674ecfd6b18SJamal Hadi Salim 	struct nlmsghdr *nlh;
675ecfd6b18SJamal Hadi Salim 	u32 *f;
676ecfd6b18SJamal Hadi Salim 
677ecfd6b18SJamal Hadi Salim 	nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
678ecfd6b18SJamal Hadi Salim 	if (nlh == NULL) /* shouldnt really happen ... */
679ecfd6b18SJamal Hadi Salim 		return -EMSGSIZE;
680ecfd6b18SJamal Hadi Salim 
681ecfd6b18SJamal Hadi Salim 	f = nlmsg_data(nlh);
682ecfd6b18SJamal Hadi Salim 	*f = flags;
683ecfd6b18SJamal Hadi Salim 	xfrm_spd_getinfo(&si);
6845a6d3416SJamal Hadi Salim 	spc.incnt = si.incnt;
6855a6d3416SJamal Hadi Salim 	spc.outcnt = si.outcnt;
6865a6d3416SJamal Hadi Salim 	spc.fwdcnt = si.fwdcnt;
6875a6d3416SJamal Hadi Salim 	spc.inscnt = si.inscnt;
6885a6d3416SJamal Hadi Salim 	spc.outscnt = si.outscnt;
6895a6d3416SJamal Hadi Salim 	spc.fwdscnt = si.fwdscnt;
6905a6d3416SJamal Hadi Salim 	sph.spdhcnt = si.spdhcnt;
6915a6d3416SJamal Hadi Salim 	sph.spdhmcnt = si.spdhmcnt;
692ecfd6b18SJamal Hadi Salim 
6935a6d3416SJamal Hadi Salim 	NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc);
6945a6d3416SJamal Hadi Salim 	NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph);
695ecfd6b18SJamal Hadi Salim 
696ecfd6b18SJamal Hadi Salim 	return nlmsg_end(skb, nlh);
697ecfd6b18SJamal Hadi Salim 
698ecfd6b18SJamal Hadi Salim nla_put_failure:
699ecfd6b18SJamal Hadi Salim 	nlmsg_cancel(skb, nlh);
700ecfd6b18SJamal Hadi Salim 	return -EMSGSIZE;
701ecfd6b18SJamal Hadi Salim }
702ecfd6b18SJamal Hadi Salim 
703ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
7045424f32eSThomas Graf 		struct nlattr **attrs)
705ecfd6b18SJamal Hadi Salim {
706ecfd6b18SJamal Hadi Salim 	struct sk_buff *r_skb;
7077b67c857SThomas Graf 	u32 *flags = nlmsg_data(nlh);
708ecfd6b18SJamal Hadi Salim 	u32 spid = NETLINK_CB(skb).pid;
709ecfd6b18SJamal Hadi Salim 	u32 seq = nlh->nlmsg_seq;
710ecfd6b18SJamal Hadi Salim 
7117deb2264SThomas Graf 	r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC);
712ecfd6b18SJamal Hadi Salim 	if (r_skb == NULL)
713ecfd6b18SJamal Hadi Salim 		return -ENOMEM;
714ecfd6b18SJamal Hadi Salim 
715ecfd6b18SJamal Hadi Salim 	if (build_spdinfo(r_skb, spid, seq, *flags) < 0)
716ecfd6b18SJamal Hadi Salim 		BUG();
717ecfd6b18SJamal Hadi Salim 
718ecfd6b18SJamal Hadi Salim 	return nlmsg_unicast(xfrm_nl, r_skb, spid);
719ecfd6b18SJamal Hadi Salim }
720ecfd6b18SJamal Hadi Salim 
7217deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void)
7227deb2264SThomas Graf {
7237deb2264SThomas Graf 	return NLMSG_ALIGN(4)
7247deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrmu_sadhinfo))
7257deb2264SThomas Graf 	       + nla_total_size(4); /* XFRMA_SAD_CNT */
7267deb2264SThomas Graf }
7277deb2264SThomas Graf 
72828d8909bSJamal Hadi Salim static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags)
72928d8909bSJamal Hadi Salim {
730af11e316SJamal Hadi Salim 	struct xfrmk_sadinfo si;
731af11e316SJamal Hadi Salim 	struct xfrmu_sadhinfo sh;
73228d8909bSJamal Hadi Salim 	struct nlmsghdr *nlh;
73328d8909bSJamal Hadi Salim 	u32 *f;
73428d8909bSJamal Hadi Salim 
73528d8909bSJamal Hadi Salim 	nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0);
73628d8909bSJamal Hadi Salim 	if (nlh == NULL) /* shouldnt really happen ... */
73728d8909bSJamal Hadi Salim 		return -EMSGSIZE;
73828d8909bSJamal Hadi Salim 
73928d8909bSJamal Hadi Salim 	f = nlmsg_data(nlh);
74028d8909bSJamal Hadi Salim 	*f = flags;
74128d8909bSJamal Hadi Salim 	xfrm_sad_getinfo(&si);
74228d8909bSJamal Hadi Salim 
743af11e316SJamal Hadi Salim 	sh.sadhmcnt = si.sadhmcnt;
744af11e316SJamal Hadi Salim 	sh.sadhcnt = si.sadhcnt;
745af11e316SJamal Hadi Salim 
746af11e316SJamal Hadi Salim 	NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt);
747af11e316SJamal Hadi Salim 	NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh);
74828d8909bSJamal Hadi Salim 
74928d8909bSJamal Hadi Salim 	return nlmsg_end(skb, nlh);
75028d8909bSJamal Hadi Salim 
75128d8909bSJamal Hadi Salim nla_put_failure:
75228d8909bSJamal Hadi Salim 	nlmsg_cancel(skb, nlh);
75328d8909bSJamal Hadi Salim 	return -EMSGSIZE;
75428d8909bSJamal Hadi Salim }
75528d8909bSJamal Hadi Salim 
75628d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
7575424f32eSThomas Graf 		struct nlattr **attrs)
75828d8909bSJamal Hadi Salim {
75928d8909bSJamal Hadi Salim 	struct sk_buff *r_skb;
7607b67c857SThomas Graf 	u32 *flags = nlmsg_data(nlh);
76128d8909bSJamal Hadi Salim 	u32 spid = NETLINK_CB(skb).pid;
76228d8909bSJamal Hadi Salim 	u32 seq = nlh->nlmsg_seq;
76328d8909bSJamal Hadi Salim 
7647deb2264SThomas Graf 	r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC);
76528d8909bSJamal Hadi Salim 	if (r_skb == NULL)
76628d8909bSJamal Hadi Salim 		return -ENOMEM;
76728d8909bSJamal Hadi Salim 
76828d8909bSJamal Hadi Salim 	if (build_sadinfo(r_skb, spid, seq, *flags) < 0)
76928d8909bSJamal Hadi Salim 		BUG();
77028d8909bSJamal Hadi Salim 
77128d8909bSJamal Hadi Salim 	return nlmsg_unicast(xfrm_nl, r_skb, spid);
77228d8909bSJamal Hadi Salim }
77328d8909bSJamal Hadi Salim 
77422e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
7755424f32eSThomas Graf 		struct nlattr **attrs)
7761da177e4SLinus Torvalds {
7777b67c857SThomas Graf 	struct xfrm_usersa_id *p = nlmsg_data(nlh);
7781da177e4SLinus Torvalds 	struct xfrm_state *x;
7791da177e4SLinus Torvalds 	struct sk_buff *resp_skb;
780eb2971b6SMasahide NAKAMURA 	int err = -ESRCH;
7811da177e4SLinus Torvalds 
78235a7aa08SThomas Graf 	x = xfrm_user_state_lookup(p, attrs, &err);
7831da177e4SLinus Torvalds 	if (x == NULL)
7841da177e4SLinus Torvalds 		goto out_noput;
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds 	resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
7871da177e4SLinus Torvalds 	if (IS_ERR(resp_skb)) {
7881da177e4SLinus Torvalds 		err = PTR_ERR(resp_skb);
7891da177e4SLinus Torvalds 	} else {
790082a1ad5SThomas Graf 		err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid);
7911da177e4SLinus Torvalds 	}
7921da177e4SLinus Torvalds 	xfrm_state_put(x);
7931da177e4SLinus Torvalds out_noput:
7941da177e4SLinus Torvalds 	return err;
7951da177e4SLinus Torvalds }
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p)
7981da177e4SLinus Torvalds {
7991da177e4SLinus Torvalds 	switch (p->info.id.proto) {
8001da177e4SLinus Torvalds 	case IPPROTO_AH:
8011da177e4SLinus Torvalds 	case IPPROTO_ESP:
8021da177e4SLinus Torvalds 		break;
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds 	case IPPROTO_COMP:
8051da177e4SLinus Torvalds 		/* IPCOMP spi is 16-bits. */
8061da177e4SLinus Torvalds 		if (p->max >= 0x10000)
8071da177e4SLinus Torvalds 			return -EINVAL;
8081da177e4SLinus Torvalds 		break;
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 	default:
8111da177e4SLinus Torvalds 		return -EINVAL;
8123ff50b79SStephen Hemminger 	}
8131da177e4SLinus Torvalds 
8141da177e4SLinus Torvalds 	if (p->min > p->max)
8151da177e4SLinus Torvalds 		return -EINVAL;
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 	return 0;
8181da177e4SLinus Torvalds }
8191da177e4SLinus Torvalds 
82022e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
8215424f32eSThomas Graf 		struct nlattr **attrs)
8221da177e4SLinus Torvalds {
8231da177e4SLinus Torvalds 	struct xfrm_state *x;
8241da177e4SLinus Torvalds 	struct xfrm_userspi_info *p;
8251da177e4SLinus Torvalds 	struct sk_buff *resp_skb;
8261da177e4SLinus Torvalds 	xfrm_address_t *daddr;
8271da177e4SLinus Torvalds 	int family;
8281da177e4SLinus Torvalds 	int err;
8291da177e4SLinus Torvalds 
8307b67c857SThomas Graf 	p = nlmsg_data(nlh);
8311da177e4SLinus Torvalds 	err = verify_userspi_info(p);
8321da177e4SLinus Torvalds 	if (err)
8331da177e4SLinus Torvalds 		goto out_noput;
8341da177e4SLinus Torvalds 
8351da177e4SLinus Torvalds 	family = p->info.family;
8361da177e4SLinus Torvalds 	daddr = &p->info.id.daddr;
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 	x = NULL;
8391da177e4SLinus Torvalds 	if (p->info.seq) {
8401da177e4SLinus Torvalds 		x = xfrm_find_acq_byseq(p->info.seq);
8411da177e4SLinus Torvalds 		if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) {
8421da177e4SLinus Torvalds 			xfrm_state_put(x);
8431da177e4SLinus Torvalds 			x = NULL;
8441da177e4SLinus Torvalds 		}
8451da177e4SLinus Torvalds 	}
8461da177e4SLinus Torvalds 
8471da177e4SLinus Torvalds 	if (!x)
8481da177e4SLinus Torvalds 		x = xfrm_find_acq(p->info.mode, p->info.reqid,
8491da177e4SLinus Torvalds 				  p->info.id.proto, daddr,
8501da177e4SLinus Torvalds 				  &p->info.saddr, 1,
8511da177e4SLinus Torvalds 				  family);
8521da177e4SLinus Torvalds 	err = -ENOENT;
8531da177e4SLinus Torvalds 	if (x == NULL)
8541da177e4SLinus Torvalds 		goto out_noput;
8551da177e4SLinus Torvalds 
856658b219eSHerbert Xu 	err = xfrm_alloc_spi(x, p->min, p->max);
857658b219eSHerbert Xu 	if (err)
858658b219eSHerbert Xu 		goto out;
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 	resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
8611da177e4SLinus Torvalds 	if (IS_ERR(resp_skb)) {
8621da177e4SLinus Torvalds 		err = PTR_ERR(resp_skb);
8631da177e4SLinus Torvalds 		goto out;
8641da177e4SLinus Torvalds 	}
8651da177e4SLinus Torvalds 
866082a1ad5SThomas Graf 	err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid);
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds out:
8691da177e4SLinus Torvalds 	xfrm_state_put(x);
8701da177e4SLinus Torvalds out_noput:
8711da177e4SLinus Torvalds 	return err;
8721da177e4SLinus Torvalds }
8731da177e4SLinus Torvalds 
874b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir)
8751da177e4SLinus Torvalds {
8761da177e4SLinus Torvalds 	switch (dir) {
8771da177e4SLinus Torvalds 	case XFRM_POLICY_IN:
8781da177e4SLinus Torvalds 	case XFRM_POLICY_OUT:
8791da177e4SLinus Torvalds 	case XFRM_POLICY_FWD:
8801da177e4SLinus Torvalds 		break;
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	default:
8831da177e4SLinus Torvalds 		return -EINVAL;
8843ff50b79SStephen Hemminger 	}
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 	return 0;
8871da177e4SLinus Torvalds }
8881da177e4SLinus Torvalds 
889b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type)
890f7b6983fSMasahide NAKAMURA {
891f7b6983fSMasahide NAKAMURA 	switch (type) {
892f7b6983fSMasahide NAKAMURA 	case XFRM_POLICY_TYPE_MAIN:
893f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
894f7b6983fSMasahide NAKAMURA 	case XFRM_POLICY_TYPE_SUB:
895f7b6983fSMasahide NAKAMURA #endif
896f7b6983fSMasahide NAKAMURA 		break;
897f7b6983fSMasahide NAKAMURA 
898f7b6983fSMasahide NAKAMURA 	default:
899f7b6983fSMasahide NAKAMURA 		return -EINVAL;
9003ff50b79SStephen Hemminger 	}
901f7b6983fSMasahide NAKAMURA 
902f7b6983fSMasahide NAKAMURA 	return 0;
903f7b6983fSMasahide NAKAMURA }
904f7b6983fSMasahide NAKAMURA 
9051da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
9061da177e4SLinus Torvalds {
9071da177e4SLinus Torvalds 	switch (p->share) {
9081da177e4SLinus Torvalds 	case XFRM_SHARE_ANY:
9091da177e4SLinus Torvalds 	case XFRM_SHARE_SESSION:
9101da177e4SLinus Torvalds 	case XFRM_SHARE_USER:
9111da177e4SLinus Torvalds 	case XFRM_SHARE_UNIQUE:
9121da177e4SLinus Torvalds 		break;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	default:
9151da177e4SLinus Torvalds 		return -EINVAL;
9163ff50b79SStephen Hemminger 	}
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds 	switch (p->action) {
9191da177e4SLinus Torvalds 	case XFRM_POLICY_ALLOW:
9201da177e4SLinus Torvalds 	case XFRM_POLICY_BLOCK:
9211da177e4SLinus Torvalds 		break;
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	default:
9241da177e4SLinus Torvalds 		return -EINVAL;
9253ff50b79SStephen Hemminger 	}
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	switch (p->sel.family) {
9281da177e4SLinus Torvalds 	case AF_INET:
9291da177e4SLinus Torvalds 		break;
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 	case AF_INET6:
9321da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
9331da177e4SLinus Torvalds 		break;
9341da177e4SLinus Torvalds #else
9351da177e4SLinus Torvalds 		return  -EAFNOSUPPORT;
9361da177e4SLinus Torvalds #endif
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 	default:
9391da177e4SLinus Torvalds 		return -EINVAL;
9403ff50b79SStephen Hemminger 	}
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	return verify_policy_dir(p->dir);
9431da177e4SLinus Torvalds }
9441da177e4SLinus Torvalds 
9455424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs)
946df71837dSTrent Jaeger {
9475424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_SEC_CTX];
948df71837dSTrent Jaeger 	struct xfrm_user_sec_ctx *uctx;
949df71837dSTrent Jaeger 
950df71837dSTrent Jaeger 	if (!rt)
951df71837dSTrent Jaeger 		return 0;
952df71837dSTrent Jaeger 
9535424f32eSThomas Graf 	uctx = nla_data(rt);
95403e1ad7bSPaul Moore 	return security_xfrm_policy_alloc(&pol->security, uctx);
955df71837dSTrent Jaeger }
956df71837dSTrent Jaeger 
9571da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut,
9581da177e4SLinus Torvalds 			   int nr)
9591da177e4SLinus Torvalds {
9601da177e4SLinus Torvalds 	int i;
9611da177e4SLinus Torvalds 
9621da177e4SLinus Torvalds 	xp->xfrm_nr = nr;
9631da177e4SLinus Torvalds 	for (i = 0; i < nr; i++, ut++) {
9641da177e4SLinus Torvalds 		struct xfrm_tmpl *t = &xp->xfrm_vec[i];
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 		memcpy(&t->id, &ut->id, sizeof(struct xfrm_id));
9671da177e4SLinus Torvalds 		memcpy(&t->saddr, &ut->saddr,
9681da177e4SLinus Torvalds 		       sizeof(xfrm_address_t));
9691da177e4SLinus Torvalds 		t->reqid = ut->reqid;
9701da177e4SLinus Torvalds 		t->mode = ut->mode;
9711da177e4SLinus Torvalds 		t->share = ut->share;
9721da177e4SLinus Torvalds 		t->optional = ut->optional;
9731da177e4SLinus Torvalds 		t->aalgos = ut->aalgos;
9741da177e4SLinus Torvalds 		t->ealgos = ut->ealgos;
9751da177e4SLinus Torvalds 		t->calgos = ut->calgos;
976c5d18e98SHerbert Xu 		/* If all masks are ~0, then we allow all algorithms. */
977c5d18e98SHerbert Xu 		t->allalgs = !~(t->aalgos & t->ealgos & t->calgos);
9788511d01dSMiika Komu 		t->encap_family = ut->family;
9791da177e4SLinus Torvalds 	}
9801da177e4SLinus Torvalds }
9811da177e4SLinus Torvalds 
982b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
983b4ad86bfSDavid S. Miller {
984b4ad86bfSDavid S. Miller 	int i;
985b4ad86bfSDavid S. Miller 
986b4ad86bfSDavid S. Miller 	if (nr > XFRM_MAX_DEPTH)
987b4ad86bfSDavid S. Miller 		return -EINVAL;
988b4ad86bfSDavid S. Miller 
989b4ad86bfSDavid S. Miller 	for (i = 0; i < nr; i++) {
990b4ad86bfSDavid S. Miller 		/* We never validated the ut->family value, so many
991b4ad86bfSDavid S. Miller 		 * applications simply leave it at zero.  The check was
992b4ad86bfSDavid S. Miller 		 * never made and ut->family was ignored because all
993b4ad86bfSDavid S. Miller 		 * templates could be assumed to have the same family as
994b4ad86bfSDavid S. Miller 		 * the policy itself.  Now that we will have ipv4-in-ipv6
995b4ad86bfSDavid S. Miller 		 * and ipv6-in-ipv4 tunnels, this is no longer true.
996b4ad86bfSDavid S. Miller 		 */
997b4ad86bfSDavid S. Miller 		if (!ut[i].family)
998b4ad86bfSDavid S. Miller 			ut[i].family = family;
999b4ad86bfSDavid S. Miller 
1000b4ad86bfSDavid S. Miller 		switch (ut[i].family) {
1001b4ad86bfSDavid S. Miller 		case AF_INET:
1002b4ad86bfSDavid S. Miller 			break;
1003b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
1004b4ad86bfSDavid S. Miller 		case AF_INET6:
1005b4ad86bfSDavid S. Miller 			break;
1006b4ad86bfSDavid S. Miller #endif
1007b4ad86bfSDavid S. Miller 		default:
1008b4ad86bfSDavid S. Miller 			return -EINVAL;
10093ff50b79SStephen Hemminger 		}
1010b4ad86bfSDavid S. Miller 	}
1011b4ad86bfSDavid S. Miller 
1012b4ad86bfSDavid S. Miller 	return 0;
1013b4ad86bfSDavid S. Miller }
1014b4ad86bfSDavid S. Miller 
10155424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs)
10161da177e4SLinus Torvalds {
10175424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_TMPL];
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds 	if (!rt) {
10201da177e4SLinus Torvalds 		pol->xfrm_nr = 0;
10211da177e4SLinus Torvalds 	} else {
10225424f32eSThomas Graf 		struct xfrm_user_tmpl *utmpl = nla_data(rt);
10235424f32eSThomas Graf 		int nr = nla_len(rt) / sizeof(*utmpl);
1024b4ad86bfSDavid S. Miller 		int err;
10251da177e4SLinus Torvalds 
1026b4ad86bfSDavid S. Miller 		err = validate_tmpl(nr, utmpl, pol->family);
1027b4ad86bfSDavid S. Miller 		if (err)
1028b4ad86bfSDavid S. Miller 			return err;
10291da177e4SLinus Torvalds 
10305424f32eSThomas Graf 		copy_templates(pol, utmpl, nr);
10311da177e4SLinus Torvalds 	}
10321da177e4SLinus Torvalds 	return 0;
10331da177e4SLinus Torvalds }
10341da177e4SLinus Torvalds 
10355424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs)
1036f7b6983fSMasahide NAKAMURA {
10375424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_POLICY_TYPE];
1038f7b6983fSMasahide NAKAMURA 	struct xfrm_userpolicy_type *upt;
1039b798a9edSJamal Hadi Salim 	u8 type = XFRM_POLICY_TYPE_MAIN;
1040f7b6983fSMasahide NAKAMURA 	int err;
1041f7b6983fSMasahide NAKAMURA 
1042f7b6983fSMasahide NAKAMURA 	if (rt) {
10435424f32eSThomas Graf 		upt = nla_data(rt);
1044f7b6983fSMasahide NAKAMURA 		type = upt->type;
1045f7b6983fSMasahide NAKAMURA 	}
1046f7b6983fSMasahide NAKAMURA 
1047f7b6983fSMasahide NAKAMURA 	err = verify_policy_type(type);
1048f7b6983fSMasahide NAKAMURA 	if (err)
1049f7b6983fSMasahide NAKAMURA 		return err;
1050f7b6983fSMasahide NAKAMURA 
1051f7b6983fSMasahide NAKAMURA 	*tp = type;
1052f7b6983fSMasahide NAKAMURA 	return 0;
1053f7b6983fSMasahide NAKAMURA }
1054f7b6983fSMasahide NAKAMURA 
10551da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p)
10561da177e4SLinus Torvalds {
10571da177e4SLinus Torvalds 	xp->priority = p->priority;
10581da177e4SLinus Torvalds 	xp->index = p->index;
10591da177e4SLinus Torvalds 	memcpy(&xp->selector, &p->sel, sizeof(xp->selector));
10601da177e4SLinus Torvalds 	memcpy(&xp->lft, &p->lft, sizeof(xp->lft));
10611da177e4SLinus Torvalds 	xp->action = p->action;
10621da177e4SLinus Torvalds 	xp->flags = p->flags;
10631da177e4SLinus Torvalds 	xp->family = p->sel.family;
10641da177e4SLinus Torvalds 	/* XXX xp->share = p->share; */
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir)
10681da177e4SLinus Torvalds {
10691da177e4SLinus Torvalds 	memcpy(&p->sel, &xp->selector, sizeof(p->sel));
10701da177e4SLinus Torvalds 	memcpy(&p->lft, &xp->lft, sizeof(p->lft));
10711da177e4SLinus Torvalds 	memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
10721da177e4SLinus Torvalds 	p->priority = xp->priority;
10731da177e4SLinus Torvalds 	p->index = xp->index;
10741da177e4SLinus Torvalds 	p->sel.family = xp->family;
10751da177e4SLinus Torvalds 	p->dir = dir;
10761da177e4SLinus Torvalds 	p->action = xp->action;
10771da177e4SLinus Torvalds 	p->flags = xp->flags;
10781da177e4SLinus Torvalds 	p->share = XFRM_SHARE_ANY; /* XXX xp->share */
10791da177e4SLinus Torvalds }
10801da177e4SLinus Torvalds 
10815424f32eSThomas Graf static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp)
10821da177e4SLinus Torvalds {
10830331b1f3SAlexey Dobriyan 	struct xfrm_policy *xp = xfrm_policy_alloc(&init_net, GFP_KERNEL);
10841da177e4SLinus Torvalds 	int err;
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds 	if (!xp) {
10871da177e4SLinus Torvalds 		*errp = -ENOMEM;
10881da177e4SLinus Torvalds 		return NULL;
10891da177e4SLinus Torvalds 	}
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	copy_from_user_policy(xp, p);
1092df71837dSTrent Jaeger 
109335a7aa08SThomas Graf 	err = copy_from_user_policy_type(&xp->type, attrs);
1094f7b6983fSMasahide NAKAMURA 	if (err)
1095f7b6983fSMasahide NAKAMURA 		goto error;
1096f7b6983fSMasahide NAKAMURA 
109735a7aa08SThomas Graf 	if (!(err = copy_from_user_tmpl(xp, attrs)))
109835a7aa08SThomas Graf 		err = copy_from_user_sec_ctx(xp, attrs);
1099f7b6983fSMasahide NAKAMURA 	if (err)
1100f7b6983fSMasahide NAKAMURA 		goto error;
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds 	return xp;
1103f7b6983fSMasahide NAKAMURA  error:
1104f7b6983fSMasahide NAKAMURA 	*errp = err;
110512a169e7SHerbert Xu 	xp->walk.dead = 1;
110664c31b3fSWANG Cong 	xfrm_policy_destroy(xp);
1107f7b6983fSMasahide NAKAMURA 	return NULL;
11081da177e4SLinus Torvalds }
11091da177e4SLinus Torvalds 
111022e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
11115424f32eSThomas Graf 		struct nlattr **attrs)
11121da177e4SLinus Torvalds {
11137b67c857SThomas Graf 	struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
11141da177e4SLinus Torvalds 	struct xfrm_policy *xp;
111526b15dadSJamal Hadi Salim 	struct km_event c;
11161da177e4SLinus Torvalds 	int err;
11171da177e4SLinus Torvalds 	int excl;
11182532386fSEric Paris 	uid_t loginuid = NETLINK_CB(skb).loginuid;
11192532386fSEric Paris 	u32 sessionid = NETLINK_CB(skb).sessionid;
11202532386fSEric Paris 	u32 sid = NETLINK_CB(skb).sid;
11211da177e4SLinus Torvalds 
11221da177e4SLinus Torvalds 	err = verify_newpolicy_info(p);
11231da177e4SLinus Torvalds 	if (err)
11241da177e4SLinus Torvalds 		return err;
112535a7aa08SThomas Graf 	err = verify_sec_ctx_len(attrs);
1126df71837dSTrent Jaeger 	if (err)
1127df71837dSTrent Jaeger 		return err;
11281da177e4SLinus Torvalds 
112935a7aa08SThomas Graf 	xp = xfrm_policy_construct(p, attrs, &err);
11301da177e4SLinus Torvalds 	if (!xp)
11311da177e4SLinus Torvalds 		return err;
11321da177e4SLinus Torvalds 
113326b15dadSJamal Hadi Salim 	/* shouldnt excl be based on nlh flags??
113426b15dadSJamal Hadi Salim 	 * Aha! this is anti-netlink really i.e  more pfkey derived
113526b15dadSJamal Hadi Salim 	 * in netlink excl is a flag and you wouldnt need
113626b15dadSJamal Hadi Salim 	 * a type XFRM_MSG_UPDPOLICY - JHS */
11371da177e4SLinus Torvalds 	excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
11381da177e4SLinus Torvalds 	err = xfrm_policy_insert(p->dir, xp, excl);
11392532386fSEric Paris 	xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid);
1140161a09e7SJoy Latten 
11411da177e4SLinus Torvalds 	if (err) {
114203e1ad7bSPaul Moore 		security_xfrm_policy_free(xp->security);
11431da177e4SLinus Torvalds 		kfree(xp);
11441da177e4SLinus Torvalds 		return err;
11451da177e4SLinus Torvalds 	}
11461da177e4SLinus Torvalds 
1147f60f6b8fSHerbert Xu 	c.event = nlh->nlmsg_type;
114826b15dadSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
114926b15dadSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
115026b15dadSJamal Hadi Salim 	km_policy_notify(xp, p->dir, &c);
115126b15dadSJamal Hadi Salim 
11521da177e4SLinus Torvalds 	xfrm_pol_put(xp);
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds 	return 0;
11551da177e4SLinus Torvalds }
11561da177e4SLinus Torvalds 
11571da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
11581da177e4SLinus Torvalds {
11591da177e4SLinus Torvalds 	struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH];
11601da177e4SLinus Torvalds 	int i;
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 	if (xp->xfrm_nr == 0)
11631da177e4SLinus Torvalds 		return 0;
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds 	for (i = 0; i < xp->xfrm_nr; i++) {
11661da177e4SLinus Torvalds 		struct xfrm_user_tmpl *up = &vec[i];
11671da177e4SLinus Torvalds 		struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 		memcpy(&up->id, &kp->id, sizeof(up->id));
11708511d01dSMiika Komu 		up->family = kp->encap_family;
11711da177e4SLinus Torvalds 		memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr));
11721da177e4SLinus Torvalds 		up->reqid = kp->reqid;
11731da177e4SLinus Torvalds 		up->mode = kp->mode;
11741da177e4SLinus Torvalds 		up->share = kp->share;
11751da177e4SLinus Torvalds 		up->optional = kp->optional;
11761da177e4SLinus Torvalds 		up->aalgos = kp->aalgos;
11771da177e4SLinus Torvalds 		up->ealgos = kp->ealgos;
11781da177e4SLinus Torvalds 		up->calgos = kp->calgos;
11791da177e4SLinus Torvalds 	}
11801da177e4SLinus Torvalds 
1181c0144beaSThomas Graf 	return nla_put(skb, XFRMA_TMPL,
1182c0144beaSThomas Graf 		       sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec);
1183df71837dSTrent Jaeger }
1184df71837dSTrent Jaeger 
11850d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb)
11860d681623SSerge Hallyn {
11870d681623SSerge Hallyn 	if (x->security) {
11880d681623SSerge Hallyn 		return copy_sec_ctx(x->security, skb);
11890d681623SSerge Hallyn 	}
11900d681623SSerge Hallyn 	return 0;
11910d681623SSerge Hallyn }
11920d681623SSerge Hallyn 
11930d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb)
11940d681623SSerge Hallyn {
11950d681623SSerge Hallyn 	if (xp->security) {
11960d681623SSerge Hallyn 		return copy_sec_ctx(xp->security, skb);
11970d681623SSerge Hallyn 	}
11980d681623SSerge Hallyn 	return 0;
11990d681623SSerge Hallyn }
1200cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void)
1201cfbfd45aSThomas Graf {
1202cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY
1203cfbfd45aSThomas Graf 	return nla_total_size(sizeof(struct xfrm_userpolicy_type));
1204cfbfd45aSThomas Graf #else
1205cfbfd45aSThomas Graf 	return 0;
1206cfbfd45aSThomas Graf #endif
1207cfbfd45aSThomas Graf }
12080d681623SSerge Hallyn 
1209f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
1210b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
1211f7b6983fSMasahide NAKAMURA {
1212c0144beaSThomas Graf 	struct xfrm_userpolicy_type upt = {
1213c0144beaSThomas Graf 		.type = type,
1214c0144beaSThomas Graf 	};
1215f7b6983fSMasahide NAKAMURA 
1216c0144beaSThomas Graf 	return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt);
1217f7b6983fSMasahide NAKAMURA }
1218f7b6983fSMasahide NAKAMURA 
1219f7b6983fSMasahide NAKAMURA #else
1220b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
1221f7b6983fSMasahide NAKAMURA {
1222f7b6983fSMasahide NAKAMURA 	return 0;
1223f7b6983fSMasahide NAKAMURA }
1224f7b6983fSMasahide NAKAMURA #endif
1225f7b6983fSMasahide NAKAMURA 
12261da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr)
12271da177e4SLinus Torvalds {
12281da177e4SLinus Torvalds 	struct xfrm_dump_info *sp = ptr;
12291da177e4SLinus Torvalds 	struct xfrm_userpolicy_info *p;
12301da177e4SLinus Torvalds 	struct sk_buff *in_skb = sp->in_skb;
12311da177e4SLinus Torvalds 	struct sk_buff *skb = sp->out_skb;
12321da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
12331da177e4SLinus Torvalds 
123479b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq,
123579b8b7f4SThomas Graf 			XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags);
123679b8b7f4SThomas Graf 	if (nlh == NULL)
123779b8b7f4SThomas Graf 		return -EMSGSIZE;
12381da177e4SLinus Torvalds 
12397b67c857SThomas Graf 	p = nlmsg_data(nlh);
12401da177e4SLinus Torvalds 	copy_to_user_policy(xp, p, dir);
12411da177e4SLinus Torvalds 	if (copy_to_user_tmpl(xp, skb) < 0)
12421da177e4SLinus Torvalds 		goto nlmsg_failure;
1243df71837dSTrent Jaeger 	if (copy_to_user_sec_ctx(xp, skb))
1244df71837dSTrent Jaeger 		goto nlmsg_failure;
12451459bb36SJamal Hadi Salim 	if (copy_to_user_policy_type(xp->type, skb) < 0)
1246f7b6983fSMasahide NAKAMURA 		goto nlmsg_failure;
12471da177e4SLinus Torvalds 
12489825069dSThomas Graf 	nlmsg_end(skb, nlh);
12491da177e4SLinus Torvalds 	return 0;
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds nlmsg_failure:
12529825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
12539825069dSThomas Graf 	return -EMSGSIZE;
12541da177e4SLinus Torvalds }
12551da177e4SLinus Torvalds 
12564c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb)
12574c563f76STimo Teras {
12584c563f76STimo Teras 	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
12594c563f76STimo Teras 
12604c563f76STimo Teras 	xfrm_policy_walk_done(walk);
12614c563f76STimo Teras 	return 0;
12624c563f76STimo Teras }
12634c563f76STimo Teras 
12641da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
12651da177e4SLinus Torvalds {
12664c563f76STimo Teras 	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
12671da177e4SLinus Torvalds 	struct xfrm_dump_info info;
12681da177e4SLinus Torvalds 
12694c563f76STimo Teras 	BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) >
12704c563f76STimo Teras 		     sizeof(cb->args) - sizeof(cb->args[0]));
12714c563f76STimo Teras 
12721da177e4SLinus Torvalds 	info.in_skb = cb->skb;
12731da177e4SLinus Torvalds 	info.out_skb = skb;
12741da177e4SLinus Torvalds 	info.nlmsg_seq = cb->nlh->nlmsg_seq;
12751da177e4SLinus Torvalds 	info.nlmsg_flags = NLM_F_MULTI;
12764c563f76STimo Teras 
12774c563f76STimo Teras 	if (!cb->args[0]) {
12784c563f76STimo Teras 		cb->args[0] = 1;
12794c563f76STimo Teras 		xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
12804c563f76STimo Teras 	}
12814c563f76STimo Teras 
12824c563f76STimo Teras 	(void) xfrm_policy_walk(walk, dump_one_policy, &info);
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds 	return skb->len;
12851da177e4SLinus Torvalds }
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
12881da177e4SLinus Torvalds 					  struct xfrm_policy *xp,
12891da177e4SLinus Torvalds 					  int dir, u32 seq)
12901da177e4SLinus Torvalds {
12911da177e4SLinus Torvalds 	struct xfrm_dump_info info;
12921da177e4SLinus Torvalds 	struct sk_buff *skb;
12931da177e4SLinus Torvalds 
12947deb2264SThomas Graf 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
12951da177e4SLinus Torvalds 	if (!skb)
12961da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
12971da177e4SLinus Torvalds 
12981da177e4SLinus Torvalds 	info.in_skb = in_skb;
12991da177e4SLinus Torvalds 	info.out_skb = skb;
13001da177e4SLinus Torvalds 	info.nlmsg_seq = seq;
13011da177e4SLinus Torvalds 	info.nlmsg_flags = 0;
13021da177e4SLinus Torvalds 
13031da177e4SLinus Torvalds 	if (dump_one_policy(xp, dir, 0, &info) < 0) {
13041da177e4SLinus Torvalds 		kfree_skb(skb);
13051da177e4SLinus Torvalds 		return NULL;
13061da177e4SLinus Torvalds 	}
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds 	return skb;
13091da177e4SLinus Torvalds }
13101da177e4SLinus Torvalds 
131122e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
13125424f32eSThomas Graf 		struct nlattr **attrs)
13131da177e4SLinus Torvalds {
13141da177e4SLinus Torvalds 	struct xfrm_policy *xp;
13151da177e4SLinus Torvalds 	struct xfrm_userpolicy_id *p;
1316b798a9edSJamal Hadi Salim 	u8 type = XFRM_POLICY_TYPE_MAIN;
13171da177e4SLinus Torvalds 	int err;
131826b15dadSJamal Hadi Salim 	struct km_event c;
13191da177e4SLinus Torvalds 	int delete;
13201da177e4SLinus Torvalds 
13217b67c857SThomas Graf 	p = nlmsg_data(nlh);
13221da177e4SLinus Torvalds 	delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
13231da177e4SLinus Torvalds 
132435a7aa08SThomas Graf 	err = copy_from_user_policy_type(&type, attrs);
1325f7b6983fSMasahide NAKAMURA 	if (err)
1326f7b6983fSMasahide NAKAMURA 		return err;
1327f7b6983fSMasahide NAKAMURA 
13281da177e4SLinus Torvalds 	err = verify_policy_dir(p->dir);
13291da177e4SLinus Torvalds 	if (err)
13301da177e4SLinus Torvalds 		return err;
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	if (p->index)
1333ef41aaa0SEric Paris 		xp = xfrm_policy_byid(type, p->dir, p->index, delete, &err);
1334df71837dSTrent Jaeger 	else {
13355424f32eSThomas Graf 		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
133603e1ad7bSPaul Moore 		struct xfrm_sec_ctx *ctx;
1337df71837dSTrent Jaeger 
133835a7aa08SThomas Graf 		err = verify_sec_ctx_len(attrs);
1339df71837dSTrent Jaeger 		if (err)
1340df71837dSTrent Jaeger 			return err;
1341df71837dSTrent Jaeger 
13422c8dd116SDenis V. Lunev 		ctx = NULL;
1343df71837dSTrent Jaeger 		if (rt) {
13445424f32eSThomas Graf 			struct xfrm_user_sec_ctx *uctx = nla_data(rt);
1345df71837dSTrent Jaeger 
134603e1ad7bSPaul Moore 			err = security_xfrm_policy_alloc(&ctx, uctx);
134703e1ad7bSPaul Moore 			if (err)
1348df71837dSTrent Jaeger 				return err;
13492c8dd116SDenis V. Lunev 		}
135003e1ad7bSPaul Moore 		xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, ctx,
1351ef41aaa0SEric Paris 					   delete, &err);
135203e1ad7bSPaul Moore 		security_xfrm_policy_free(ctx);
1353df71837dSTrent Jaeger 	}
13541da177e4SLinus Torvalds 	if (xp == NULL)
13551da177e4SLinus Torvalds 		return -ENOENT;
13561da177e4SLinus Torvalds 
13571da177e4SLinus Torvalds 	if (!delete) {
13581da177e4SLinus Torvalds 		struct sk_buff *resp_skb;
13591da177e4SLinus Torvalds 
13601da177e4SLinus Torvalds 		resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq);
13611da177e4SLinus Torvalds 		if (IS_ERR(resp_skb)) {
13621da177e4SLinus Torvalds 			err = PTR_ERR(resp_skb);
13631da177e4SLinus Torvalds 		} else {
1364082a1ad5SThomas Graf 			err = nlmsg_unicast(xfrm_nl, resp_skb,
1365082a1ad5SThomas Graf 					    NETLINK_CB(skb).pid);
13661da177e4SLinus Torvalds 		}
136726b15dadSJamal Hadi Salim 	} else {
13682532386fSEric Paris 		uid_t loginuid = NETLINK_CB(skb).loginuid;
13692532386fSEric Paris 		u32 sessionid = NETLINK_CB(skb).sessionid;
13702532386fSEric Paris 		u32 sid = NETLINK_CB(skb).sid;
13712532386fSEric Paris 
13722532386fSEric Paris 		xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid,
13732532386fSEric Paris 					 sid);
137413fcfbb0SDavid S. Miller 
137513fcfbb0SDavid S. Miller 		if (err != 0)
1376c8c05a8eSCatherine Zhang 			goto out;
137713fcfbb0SDavid S. Miller 
1378e7443892SHerbert Xu 		c.data.byid = p->index;
1379f60f6b8fSHerbert Xu 		c.event = nlh->nlmsg_type;
138026b15dadSJamal Hadi Salim 		c.seq = nlh->nlmsg_seq;
138126b15dadSJamal Hadi Salim 		c.pid = nlh->nlmsg_pid;
138226b15dadSJamal Hadi Salim 		km_policy_notify(xp, p->dir, &c);
13831da177e4SLinus Torvalds 	}
13841da177e4SLinus Torvalds 
1385c8c05a8eSCatherine Zhang out:
1386ef41aaa0SEric Paris 	xfrm_pol_put(xp);
13871da177e4SLinus Torvalds 	return err;
13881da177e4SLinus Torvalds }
13891da177e4SLinus Torvalds 
139022e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
13915424f32eSThomas Graf 		struct nlattr **attrs)
13921da177e4SLinus Torvalds {
139326b15dadSJamal Hadi Salim 	struct km_event c;
13947b67c857SThomas Graf 	struct xfrm_usersa_flush *p = nlmsg_data(nlh);
1395161a09e7SJoy Latten 	struct xfrm_audit audit_info;
13964aa2e62cSJoy Latten 	int err;
13971da177e4SLinus Torvalds 
1398161a09e7SJoy Latten 	audit_info.loginuid = NETLINK_CB(skb).loginuid;
13992532386fSEric Paris 	audit_info.sessionid = NETLINK_CB(skb).sessionid;
1400161a09e7SJoy Latten 	audit_info.secid = NETLINK_CB(skb).sid;
14010e602451SAlexey Dobriyan 	err = xfrm_state_flush(&init_net, p->proto, &audit_info);
14024aa2e62cSJoy Latten 	if (err)
14034aa2e62cSJoy Latten 		return err;
1404bf08867fSHerbert Xu 	c.data.proto = p->proto;
1405f60f6b8fSHerbert Xu 	c.event = nlh->nlmsg_type;
140626b15dadSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
140726b15dadSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
140826b15dadSJamal Hadi Salim 	km_state_notify(NULL, &c);
140926b15dadSJamal Hadi Salim 
14101da177e4SLinus Torvalds 	return 0;
14111da177e4SLinus Torvalds }
14121da177e4SLinus Torvalds 
14137deb2264SThomas Graf static inline size_t xfrm_aevent_msgsize(void)
14147deb2264SThomas Graf {
14157deb2264SThomas Graf 	return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id))
14167deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrm_replay_state))
14177deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrm_lifetime_cur))
14187deb2264SThomas Graf 	       + nla_total_size(4) /* XFRM_AE_RTHR */
14197deb2264SThomas Graf 	       + nla_total_size(4); /* XFRM_AE_ETHR */
14207deb2264SThomas Graf }
1421d51d081dSJamal Hadi Salim 
1422d51d081dSJamal Hadi Salim static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c)
1423d51d081dSJamal Hadi Salim {
1424d51d081dSJamal Hadi Salim 	struct xfrm_aevent_id *id;
1425d51d081dSJamal Hadi Salim 	struct nlmsghdr *nlh;
1426d51d081dSJamal Hadi Salim 
142779b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0);
142879b8b7f4SThomas Graf 	if (nlh == NULL)
142979b8b7f4SThomas Graf 		return -EMSGSIZE;
1430d51d081dSJamal Hadi Salim 
14317b67c857SThomas Graf 	id = nlmsg_data(nlh);
14322b5f6dccSJamal Hadi Salim 	memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr));
1433d51d081dSJamal Hadi Salim 	id->sa_id.spi = x->id.spi;
1434d51d081dSJamal Hadi Salim 	id->sa_id.family = x->props.family;
1435d51d081dSJamal Hadi Salim 	id->sa_id.proto = x->id.proto;
14362b5f6dccSJamal Hadi Salim 	memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr));
14372b5f6dccSJamal Hadi Salim 	id->reqid = x->props.reqid;
1438d51d081dSJamal Hadi Salim 	id->flags = c->data.aevent;
1439d51d081dSJamal Hadi Salim 
1440c0144beaSThomas Graf 	NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay);
1441c0144beaSThomas Graf 	NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft);
1442d51d081dSJamal Hadi Salim 
1443c0144beaSThomas Graf 	if (id->flags & XFRM_AE_RTHR)
1444c0144beaSThomas Graf 		NLA_PUT_U32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff);
1445d51d081dSJamal Hadi Salim 
1446c0144beaSThomas Graf 	if (id->flags & XFRM_AE_ETHR)
1447c0144beaSThomas Graf 		NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH,
1448c0144beaSThomas Graf 			    x->replay_maxage * 10 / HZ);
1449d51d081dSJamal Hadi Salim 
14509825069dSThomas Graf 	return nlmsg_end(skb, nlh);
1451d51d081dSJamal Hadi Salim 
1452c0144beaSThomas Graf nla_put_failure:
14539825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
14549825069dSThomas Graf 	return -EMSGSIZE;
1455d51d081dSJamal Hadi Salim }
1456d51d081dSJamal Hadi Salim 
145722e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
14585424f32eSThomas Graf 		struct nlattr **attrs)
1459d51d081dSJamal Hadi Salim {
1460d51d081dSJamal Hadi Salim 	struct xfrm_state *x;
1461d51d081dSJamal Hadi Salim 	struct sk_buff *r_skb;
1462d51d081dSJamal Hadi Salim 	int err;
1463d51d081dSJamal Hadi Salim 	struct km_event c;
14647b67c857SThomas Graf 	struct xfrm_aevent_id *p = nlmsg_data(nlh);
1465d51d081dSJamal Hadi Salim 	struct xfrm_usersa_id *id = &p->sa_id;
1466d51d081dSJamal Hadi Salim 
14677deb2264SThomas Graf 	r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC);
1468d51d081dSJamal Hadi Salim 	if (r_skb == NULL)
1469d51d081dSJamal Hadi Salim 		return -ENOMEM;
1470d51d081dSJamal Hadi Salim 
1471*221df1edSAlexey Dobriyan 	x = xfrm_state_lookup(&init_net, &id->daddr, id->spi, id->proto, id->family);
1472d51d081dSJamal Hadi Salim 	if (x == NULL) {
1473b08d5840SPatrick McHardy 		kfree_skb(r_skb);
1474d51d081dSJamal Hadi Salim 		return -ESRCH;
1475d51d081dSJamal Hadi Salim 	}
1476d51d081dSJamal Hadi Salim 
1477d51d081dSJamal Hadi Salim 	/*
1478d51d081dSJamal Hadi Salim 	 * XXX: is this lock really needed - none of the other
1479d51d081dSJamal Hadi Salim 	 * gets lock (the concern is things getting updated
1480d51d081dSJamal Hadi Salim 	 * while we are still reading) - jhs
1481d51d081dSJamal Hadi Salim 	*/
1482d51d081dSJamal Hadi Salim 	spin_lock_bh(&x->lock);
1483d51d081dSJamal Hadi Salim 	c.data.aevent = p->flags;
1484d51d081dSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
1485d51d081dSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
1486d51d081dSJamal Hadi Salim 
1487d51d081dSJamal Hadi Salim 	if (build_aevent(r_skb, x, &c) < 0)
1488d51d081dSJamal Hadi Salim 		BUG();
1489082a1ad5SThomas Graf 	err = nlmsg_unicast(xfrm_nl, r_skb, NETLINK_CB(skb).pid);
1490d51d081dSJamal Hadi Salim 	spin_unlock_bh(&x->lock);
1491d51d081dSJamal Hadi Salim 	xfrm_state_put(x);
1492d51d081dSJamal Hadi Salim 	return err;
1493d51d081dSJamal Hadi Salim }
1494d51d081dSJamal Hadi Salim 
149522e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
14965424f32eSThomas Graf 		struct nlattr **attrs)
1497d51d081dSJamal Hadi Salim {
1498d51d081dSJamal Hadi Salim 	struct xfrm_state *x;
1499d51d081dSJamal Hadi Salim 	struct km_event c;
1500d51d081dSJamal Hadi Salim 	int err = - EINVAL;
15017b67c857SThomas Graf 	struct xfrm_aevent_id *p = nlmsg_data(nlh);
15025424f32eSThomas Graf 	struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
15035424f32eSThomas Graf 	struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
1504d51d081dSJamal Hadi Salim 
1505d51d081dSJamal Hadi Salim 	if (!lt && !rp)
1506d51d081dSJamal Hadi Salim 		return err;
1507d51d081dSJamal Hadi Salim 
1508d51d081dSJamal Hadi Salim 	/* pedantic mode - thou shalt sayeth replaceth */
1509d51d081dSJamal Hadi Salim 	if (!(nlh->nlmsg_flags&NLM_F_REPLACE))
1510d51d081dSJamal Hadi Salim 		return err;
1511d51d081dSJamal Hadi Salim 
1512*221df1edSAlexey Dobriyan 	x = xfrm_state_lookup(&init_net, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
1513d51d081dSJamal Hadi Salim 	if (x == NULL)
1514d51d081dSJamal Hadi Salim 		return -ESRCH;
1515d51d081dSJamal Hadi Salim 
1516d51d081dSJamal Hadi Salim 	if (x->km.state != XFRM_STATE_VALID)
1517d51d081dSJamal Hadi Salim 		goto out;
1518d51d081dSJamal Hadi Salim 
1519d51d081dSJamal Hadi Salim 	spin_lock_bh(&x->lock);
152035a7aa08SThomas Graf 	xfrm_update_ae_params(x, attrs);
1521d51d081dSJamal Hadi Salim 	spin_unlock_bh(&x->lock);
1522d51d081dSJamal Hadi Salim 
1523d51d081dSJamal Hadi Salim 	c.event = nlh->nlmsg_type;
1524d51d081dSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
1525d51d081dSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
1526d51d081dSJamal Hadi Salim 	c.data.aevent = XFRM_AE_CU;
1527d51d081dSJamal Hadi Salim 	km_state_notify(x, &c);
1528d51d081dSJamal Hadi Salim 	err = 0;
1529d51d081dSJamal Hadi Salim out:
1530d51d081dSJamal Hadi Salim 	xfrm_state_put(x);
1531d51d081dSJamal Hadi Salim 	return err;
1532d51d081dSJamal Hadi Salim }
1533d51d081dSJamal Hadi Salim 
153422e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
15355424f32eSThomas Graf 		struct nlattr **attrs)
15361da177e4SLinus Torvalds {
153726b15dadSJamal Hadi Salim 	struct km_event c;
1538b798a9edSJamal Hadi Salim 	u8 type = XFRM_POLICY_TYPE_MAIN;
1539f7b6983fSMasahide NAKAMURA 	int err;
1540161a09e7SJoy Latten 	struct xfrm_audit audit_info;
154126b15dadSJamal Hadi Salim 
154235a7aa08SThomas Graf 	err = copy_from_user_policy_type(&type, attrs);
1543f7b6983fSMasahide NAKAMURA 	if (err)
1544f7b6983fSMasahide NAKAMURA 		return err;
1545f7b6983fSMasahide NAKAMURA 
1546161a09e7SJoy Latten 	audit_info.loginuid = NETLINK_CB(skb).loginuid;
15472532386fSEric Paris 	audit_info.sessionid = NETLINK_CB(skb).sessionid;
1548161a09e7SJoy Latten 	audit_info.secid = NETLINK_CB(skb).sid;
15494aa2e62cSJoy Latten 	err = xfrm_policy_flush(type, &audit_info);
15504aa2e62cSJoy Latten 	if (err)
15514aa2e62cSJoy Latten 		return err;
1552f7b6983fSMasahide NAKAMURA 	c.data.type = type;
1553f60f6b8fSHerbert Xu 	c.event = nlh->nlmsg_type;
155426b15dadSJamal Hadi Salim 	c.seq = nlh->nlmsg_seq;
155526b15dadSJamal Hadi Salim 	c.pid = nlh->nlmsg_pid;
155626b15dadSJamal Hadi Salim 	km_policy_notify(NULL, 0, &c);
15571da177e4SLinus Torvalds 	return 0;
15581da177e4SLinus Torvalds }
15591da177e4SLinus Torvalds 
156022e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
15615424f32eSThomas Graf 		struct nlattr **attrs)
15626c5c8ca7SJamal Hadi Salim {
15636c5c8ca7SJamal Hadi Salim 	struct xfrm_policy *xp;
15647b67c857SThomas Graf 	struct xfrm_user_polexpire *up = nlmsg_data(nlh);
15656c5c8ca7SJamal Hadi Salim 	struct xfrm_userpolicy_info *p = &up->pol;
1566b798a9edSJamal Hadi Salim 	u8 type = XFRM_POLICY_TYPE_MAIN;
15676c5c8ca7SJamal Hadi Salim 	int err = -ENOENT;
15686c5c8ca7SJamal Hadi Salim 
156935a7aa08SThomas Graf 	err = copy_from_user_policy_type(&type, attrs);
1570f7b6983fSMasahide NAKAMURA 	if (err)
1571f7b6983fSMasahide NAKAMURA 		return err;
1572f7b6983fSMasahide NAKAMURA 
15736c5c8ca7SJamal Hadi Salim 	if (p->index)
1574ef41aaa0SEric Paris 		xp = xfrm_policy_byid(type, p->dir, p->index, 0, &err);
15756c5c8ca7SJamal Hadi Salim 	else {
15765424f32eSThomas Graf 		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
157703e1ad7bSPaul Moore 		struct xfrm_sec_ctx *ctx;
15786c5c8ca7SJamal Hadi Salim 
157935a7aa08SThomas Graf 		err = verify_sec_ctx_len(attrs);
15806c5c8ca7SJamal Hadi Salim 		if (err)
15816c5c8ca7SJamal Hadi Salim 			return err;
15826c5c8ca7SJamal Hadi Salim 
15832c8dd116SDenis V. Lunev 		ctx = NULL;
15846c5c8ca7SJamal Hadi Salim 		if (rt) {
15855424f32eSThomas Graf 			struct xfrm_user_sec_ctx *uctx = nla_data(rt);
15866c5c8ca7SJamal Hadi Salim 
158703e1ad7bSPaul Moore 			err = security_xfrm_policy_alloc(&ctx, uctx);
158803e1ad7bSPaul Moore 			if (err)
15896c5c8ca7SJamal Hadi Salim 				return err;
15902c8dd116SDenis V. Lunev 		}
159103e1ad7bSPaul Moore 		xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, ctx, 0, &err);
159203e1ad7bSPaul Moore 		security_xfrm_policy_free(ctx);
15936c5c8ca7SJamal Hadi Salim 	}
15946c5c8ca7SJamal Hadi Salim 	if (xp == NULL)
1595ef41aaa0SEric Paris 		return -ENOENT;
159603e1ad7bSPaul Moore 
15976c5c8ca7SJamal Hadi Salim 	read_lock(&xp->lock);
159812a169e7SHerbert Xu 	if (xp->walk.dead) {
15996c5c8ca7SJamal Hadi Salim 		read_unlock(&xp->lock);
16006c5c8ca7SJamal Hadi Salim 		goto out;
16016c5c8ca7SJamal Hadi Salim 	}
16026c5c8ca7SJamal Hadi Salim 
16036c5c8ca7SJamal Hadi Salim 	read_unlock(&xp->lock);
16046c5c8ca7SJamal Hadi Salim 	err = 0;
16056c5c8ca7SJamal Hadi Salim 	if (up->hard) {
16062532386fSEric Paris 		uid_t loginuid = NETLINK_CB(skb).loginuid;
16072532386fSEric Paris 		uid_t sessionid = NETLINK_CB(skb).sessionid;
16082532386fSEric Paris 		u32 sid = NETLINK_CB(skb).sid;
16096c5c8ca7SJamal Hadi Salim 		xfrm_policy_delete(xp, p->dir);
16102532386fSEric Paris 		xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid);
1611161a09e7SJoy Latten 
16126c5c8ca7SJamal Hadi Salim 	} else {
16136c5c8ca7SJamal Hadi Salim 		// reset the timers here?
16146c5c8ca7SJamal Hadi Salim 		printk("Dont know what to do with soft policy expire\n");
16156c5c8ca7SJamal Hadi Salim 	}
16166c5c8ca7SJamal Hadi Salim 	km_policy_expired(xp, p->dir, up->hard, current->pid);
16176c5c8ca7SJamal Hadi Salim 
16186c5c8ca7SJamal Hadi Salim out:
16196c5c8ca7SJamal Hadi Salim 	xfrm_pol_put(xp);
16206c5c8ca7SJamal Hadi Salim 	return err;
16216c5c8ca7SJamal Hadi Salim }
16226c5c8ca7SJamal Hadi Salim 
162322e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
16245424f32eSThomas Graf 		struct nlattr **attrs)
162553bc6b4dSJamal Hadi Salim {
162653bc6b4dSJamal Hadi Salim 	struct xfrm_state *x;
162753bc6b4dSJamal Hadi Salim 	int err;
16287b67c857SThomas Graf 	struct xfrm_user_expire *ue = nlmsg_data(nlh);
162953bc6b4dSJamal Hadi Salim 	struct xfrm_usersa_info *p = &ue->state;
163053bc6b4dSJamal Hadi Salim 
1631*221df1edSAlexey Dobriyan 	x = xfrm_state_lookup(&init_net, &p->id.daddr, p->id.spi, p->id.proto, p->family);
163253bc6b4dSJamal Hadi Salim 
16333a765aa5SDavid S. Miller 	err = -ENOENT;
163453bc6b4dSJamal Hadi Salim 	if (x == NULL)
163553bc6b4dSJamal Hadi Salim 		return err;
163653bc6b4dSJamal Hadi Salim 
163753bc6b4dSJamal Hadi Salim 	spin_lock_bh(&x->lock);
16383a765aa5SDavid S. Miller 	err = -EINVAL;
163953bc6b4dSJamal Hadi Salim 	if (x->km.state != XFRM_STATE_VALID)
164053bc6b4dSJamal Hadi Salim 		goto out;
164153bc6b4dSJamal Hadi Salim 	km_state_expired(x, ue->hard, current->pid);
164253bc6b4dSJamal Hadi Salim 
1643161a09e7SJoy Latten 	if (ue->hard) {
16442532386fSEric Paris 		uid_t loginuid = NETLINK_CB(skb).loginuid;
16452532386fSEric Paris 		uid_t sessionid = NETLINK_CB(skb).sessionid;
16462532386fSEric Paris 		u32 sid = NETLINK_CB(skb).sid;
164753bc6b4dSJamal Hadi Salim 		__xfrm_state_delete(x);
16482532386fSEric Paris 		xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid);
1649161a09e7SJoy Latten 	}
16503a765aa5SDavid S. Miller 	err = 0;
165153bc6b4dSJamal Hadi Salim out:
165253bc6b4dSJamal Hadi Salim 	spin_unlock_bh(&x->lock);
165353bc6b4dSJamal Hadi Salim 	xfrm_state_put(x);
165453bc6b4dSJamal Hadi Salim 	return err;
165553bc6b4dSJamal Hadi Salim }
165653bc6b4dSJamal Hadi Salim 
165722e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
16585424f32eSThomas Graf 		struct nlattr **attrs)
1659980ebd25SJamal Hadi Salim {
1660980ebd25SJamal Hadi Salim 	struct xfrm_policy *xp;
1661980ebd25SJamal Hadi Salim 	struct xfrm_user_tmpl *ut;
1662980ebd25SJamal Hadi Salim 	int i;
16635424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_TMPL];
1664980ebd25SJamal Hadi Salim 
16657b67c857SThomas Graf 	struct xfrm_user_acquire *ua = nlmsg_data(nlh);
1666673c09beSAlexey Dobriyan 	struct xfrm_state *x = xfrm_state_alloc(&init_net);
1667980ebd25SJamal Hadi Salim 	int err = -ENOMEM;
1668980ebd25SJamal Hadi Salim 
1669980ebd25SJamal Hadi Salim 	if (!x)
1670980ebd25SJamal Hadi Salim 		return err;
1671980ebd25SJamal Hadi Salim 
1672980ebd25SJamal Hadi Salim 	err = verify_newpolicy_info(&ua->policy);
1673980ebd25SJamal Hadi Salim 	if (err) {
1674980ebd25SJamal Hadi Salim 		printk("BAD policy passed\n");
1675980ebd25SJamal Hadi Salim 		kfree(x);
1676980ebd25SJamal Hadi Salim 		return err;
1677980ebd25SJamal Hadi Salim 	}
1678980ebd25SJamal Hadi Salim 
1679980ebd25SJamal Hadi Salim 	/*   build an XP */
16805424f32eSThomas Graf 	xp = xfrm_policy_construct(&ua->policy, attrs, &err);
1681b4ad86bfSDavid S. Miller 	if (!xp) {
1682980ebd25SJamal Hadi Salim 		kfree(x);
1683980ebd25SJamal Hadi Salim 		return err;
1684980ebd25SJamal Hadi Salim 	}
1685980ebd25SJamal Hadi Salim 
1686980ebd25SJamal Hadi Salim 	memcpy(&x->id, &ua->id, sizeof(ua->id));
1687980ebd25SJamal Hadi Salim 	memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
1688980ebd25SJamal Hadi Salim 	memcpy(&x->sel, &ua->sel, sizeof(ua->sel));
1689980ebd25SJamal Hadi Salim 
16905424f32eSThomas Graf 	ut = nla_data(rt);
1691980ebd25SJamal Hadi Salim 	/* extract the templates and for each call km_key */
1692980ebd25SJamal Hadi Salim 	for (i = 0; i < xp->xfrm_nr; i++, ut++) {
1693980ebd25SJamal Hadi Salim 		struct xfrm_tmpl *t = &xp->xfrm_vec[i];
1694980ebd25SJamal Hadi Salim 		memcpy(&x->id, &t->id, sizeof(x->id));
1695980ebd25SJamal Hadi Salim 		x->props.mode = t->mode;
1696980ebd25SJamal Hadi Salim 		x->props.reqid = t->reqid;
1697980ebd25SJamal Hadi Salim 		x->props.family = ut->family;
1698980ebd25SJamal Hadi Salim 		t->aalgos = ua->aalgos;
1699980ebd25SJamal Hadi Salim 		t->ealgos = ua->ealgos;
1700980ebd25SJamal Hadi Salim 		t->calgos = ua->calgos;
1701980ebd25SJamal Hadi Salim 		err = km_query(x, t, xp);
1702980ebd25SJamal Hadi Salim 
1703980ebd25SJamal Hadi Salim 	}
1704980ebd25SJamal Hadi Salim 
1705980ebd25SJamal Hadi Salim 	kfree(x);
1706980ebd25SJamal Hadi Salim 	kfree(xp);
1707980ebd25SJamal Hadi Salim 
1708980ebd25SJamal Hadi Salim 	return 0;
1709980ebd25SJamal Hadi Salim }
1710980ebd25SJamal Hadi Salim 
17115c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
17125c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma,
171313c1d189SArnaud Ebalard 				  struct xfrm_kmaddress *k,
17145424f32eSThomas Graf 				  struct nlattr **attrs, int *num)
17155c79de6eSShinta Sugimoto {
17165424f32eSThomas Graf 	struct nlattr *rt = attrs[XFRMA_MIGRATE];
17175c79de6eSShinta Sugimoto 	struct xfrm_user_migrate *um;
17185c79de6eSShinta Sugimoto 	int i, num_migrate;
17195c79de6eSShinta Sugimoto 
172013c1d189SArnaud Ebalard 	if (k != NULL) {
172113c1d189SArnaud Ebalard 		struct xfrm_user_kmaddress *uk;
172213c1d189SArnaud Ebalard 
172313c1d189SArnaud Ebalard 		uk = nla_data(attrs[XFRMA_KMADDRESS]);
172413c1d189SArnaud Ebalard 		memcpy(&k->local, &uk->local, sizeof(k->local));
172513c1d189SArnaud Ebalard 		memcpy(&k->remote, &uk->remote, sizeof(k->remote));
172613c1d189SArnaud Ebalard 		k->family = uk->family;
172713c1d189SArnaud Ebalard 		k->reserved = uk->reserved;
172813c1d189SArnaud Ebalard 	}
172913c1d189SArnaud Ebalard 
17305424f32eSThomas Graf 	um = nla_data(rt);
17315424f32eSThomas Graf 	num_migrate = nla_len(rt) / sizeof(*um);
17325c79de6eSShinta Sugimoto 
17335c79de6eSShinta Sugimoto 	if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH)
17345c79de6eSShinta Sugimoto 		return -EINVAL;
17355c79de6eSShinta Sugimoto 
17365c79de6eSShinta Sugimoto 	for (i = 0; i < num_migrate; i++, um++, ma++) {
17375c79de6eSShinta Sugimoto 		memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr));
17385c79de6eSShinta Sugimoto 		memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr));
17395c79de6eSShinta Sugimoto 		memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr));
17405c79de6eSShinta Sugimoto 		memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr));
17415c79de6eSShinta Sugimoto 
17425c79de6eSShinta Sugimoto 		ma->proto = um->proto;
17435c79de6eSShinta Sugimoto 		ma->mode = um->mode;
17445c79de6eSShinta Sugimoto 		ma->reqid = um->reqid;
17455c79de6eSShinta Sugimoto 
17465c79de6eSShinta Sugimoto 		ma->old_family = um->old_family;
17475c79de6eSShinta Sugimoto 		ma->new_family = um->new_family;
17485c79de6eSShinta Sugimoto 	}
17495c79de6eSShinta Sugimoto 
17505c79de6eSShinta Sugimoto 	*num = i;
17515c79de6eSShinta Sugimoto 	return 0;
17525c79de6eSShinta Sugimoto }
17535c79de6eSShinta Sugimoto 
17545c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
17555424f32eSThomas Graf 			   struct nlattr **attrs)
17565c79de6eSShinta Sugimoto {
17577b67c857SThomas Graf 	struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
17585c79de6eSShinta Sugimoto 	struct xfrm_migrate m[XFRM_MAX_DEPTH];
175913c1d189SArnaud Ebalard 	struct xfrm_kmaddress km, *kmp;
17605c79de6eSShinta Sugimoto 	u8 type;
17615c79de6eSShinta Sugimoto 	int err;
17625c79de6eSShinta Sugimoto 	int n = 0;
17635c79de6eSShinta Sugimoto 
176435a7aa08SThomas Graf 	if (attrs[XFRMA_MIGRATE] == NULL)
1765cf5cb79fSThomas Graf 		return -EINVAL;
17665c79de6eSShinta Sugimoto 
176713c1d189SArnaud Ebalard 	kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL;
176813c1d189SArnaud Ebalard 
17695424f32eSThomas Graf 	err = copy_from_user_policy_type(&type, attrs);
17705c79de6eSShinta Sugimoto 	if (err)
17715c79de6eSShinta Sugimoto 		return err;
17725c79de6eSShinta Sugimoto 
177313c1d189SArnaud Ebalard 	err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n);
17745c79de6eSShinta Sugimoto 	if (err)
17755c79de6eSShinta Sugimoto 		return err;
17765c79de6eSShinta Sugimoto 
17775c79de6eSShinta Sugimoto 	if (!n)
17785c79de6eSShinta Sugimoto 		return 0;
17795c79de6eSShinta Sugimoto 
178013c1d189SArnaud Ebalard 	xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp);
17815c79de6eSShinta Sugimoto 
17825c79de6eSShinta Sugimoto 	return 0;
17835c79de6eSShinta Sugimoto }
17845c79de6eSShinta Sugimoto #else
17855c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
17865424f32eSThomas Graf 			   struct nlattr **attrs)
17875c79de6eSShinta Sugimoto {
17885c79de6eSShinta Sugimoto 	return -ENOPROTOOPT;
17895c79de6eSShinta Sugimoto }
17905c79de6eSShinta Sugimoto #endif
17915c79de6eSShinta Sugimoto 
17925c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
17935c79de6eSShinta Sugimoto static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb)
17945c79de6eSShinta Sugimoto {
17955c79de6eSShinta Sugimoto 	struct xfrm_user_migrate um;
17965c79de6eSShinta Sugimoto 
17975c79de6eSShinta Sugimoto 	memset(&um, 0, sizeof(um));
17985c79de6eSShinta Sugimoto 	um.proto = m->proto;
17995c79de6eSShinta Sugimoto 	um.mode = m->mode;
18005c79de6eSShinta Sugimoto 	um.reqid = m->reqid;
18015c79de6eSShinta Sugimoto 	um.old_family = m->old_family;
18025c79de6eSShinta Sugimoto 	memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr));
18035c79de6eSShinta Sugimoto 	memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr));
18045c79de6eSShinta Sugimoto 	um.new_family = m->new_family;
18055c79de6eSShinta Sugimoto 	memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr));
18065c79de6eSShinta Sugimoto 	memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr));
18075c79de6eSShinta Sugimoto 
1808c0144beaSThomas Graf 	return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um);
18095c79de6eSShinta Sugimoto }
18105c79de6eSShinta Sugimoto 
181113c1d189SArnaud Ebalard static int copy_to_user_kmaddress(struct xfrm_kmaddress *k, struct sk_buff *skb)
181213c1d189SArnaud Ebalard {
181313c1d189SArnaud Ebalard 	struct xfrm_user_kmaddress uk;
181413c1d189SArnaud Ebalard 
181513c1d189SArnaud Ebalard 	memset(&uk, 0, sizeof(uk));
181613c1d189SArnaud Ebalard 	uk.family = k->family;
181713c1d189SArnaud Ebalard 	uk.reserved = k->reserved;
181813c1d189SArnaud Ebalard 	memcpy(&uk.local, &k->local, sizeof(uk.local));
1819a1caa322SArnaud Ebalard 	memcpy(&uk.remote, &k->remote, sizeof(uk.remote));
182013c1d189SArnaud Ebalard 
182113c1d189SArnaud Ebalard 	return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk);
182213c1d189SArnaud Ebalard }
182313c1d189SArnaud Ebalard 
182413c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma)
18257deb2264SThomas Graf {
18267deb2264SThomas Graf 	return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id))
182713c1d189SArnaud Ebalard 	      + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0)
18287deb2264SThomas Graf 	      + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate)
18297deb2264SThomas Graf 	      + userpolicy_type_attrsize();
18307deb2264SThomas Graf }
18317deb2264SThomas Graf 
18325c79de6eSShinta Sugimoto static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m,
183313c1d189SArnaud Ebalard 			 int num_migrate, struct xfrm_kmaddress *k,
183413c1d189SArnaud Ebalard 			 struct xfrm_selector *sel, u8 dir, u8 type)
18355c79de6eSShinta Sugimoto {
18365c79de6eSShinta Sugimoto 	struct xfrm_migrate *mp;
18375c79de6eSShinta Sugimoto 	struct xfrm_userpolicy_id *pol_id;
18385c79de6eSShinta Sugimoto 	struct nlmsghdr *nlh;
18395c79de6eSShinta Sugimoto 	int i;
18405c79de6eSShinta Sugimoto 
184179b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0);
184279b8b7f4SThomas Graf 	if (nlh == NULL)
184379b8b7f4SThomas Graf 		return -EMSGSIZE;
18445c79de6eSShinta Sugimoto 
18457b67c857SThomas Graf 	pol_id = nlmsg_data(nlh);
18465c79de6eSShinta Sugimoto 	/* copy data from selector, dir, and type to the pol_id */
18475c79de6eSShinta Sugimoto 	memset(pol_id, 0, sizeof(*pol_id));
18485c79de6eSShinta Sugimoto 	memcpy(&pol_id->sel, sel, sizeof(pol_id->sel));
18495c79de6eSShinta Sugimoto 	pol_id->dir = dir;
18505c79de6eSShinta Sugimoto 
185113c1d189SArnaud Ebalard 	if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0))
185213c1d189SArnaud Ebalard 			goto nlmsg_failure;
185313c1d189SArnaud Ebalard 
18545c79de6eSShinta Sugimoto 	if (copy_to_user_policy_type(type, skb) < 0)
18555c79de6eSShinta Sugimoto 		goto nlmsg_failure;
18565c79de6eSShinta Sugimoto 
18575c79de6eSShinta Sugimoto 	for (i = 0, mp = m ; i < num_migrate; i++, mp++) {
18585c79de6eSShinta Sugimoto 		if (copy_to_user_migrate(mp, skb) < 0)
18595c79de6eSShinta Sugimoto 			goto nlmsg_failure;
18605c79de6eSShinta Sugimoto 	}
18615c79de6eSShinta Sugimoto 
18629825069dSThomas Graf 	return nlmsg_end(skb, nlh);
18635c79de6eSShinta Sugimoto nlmsg_failure:
18649825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
18659825069dSThomas Graf 	return -EMSGSIZE;
18665c79de6eSShinta Sugimoto }
18675c79de6eSShinta Sugimoto 
18685c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
186913c1d189SArnaud Ebalard 			     struct xfrm_migrate *m, int num_migrate,
187013c1d189SArnaud Ebalard 			     struct xfrm_kmaddress *k)
18715c79de6eSShinta Sugimoto {
18725c79de6eSShinta Sugimoto 	struct sk_buff *skb;
18735c79de6eSShinta Sugimoto 
187413c1d189SArnaud Ebalard 	skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC);
18755c79de6eSShinta Sugimoto 	if (skb == NULL)
18765c79de6eSShinta Sugimoto 		return -ENOMEM;
18775c79de6eSShinta Sugimoto 
18785c79de6eSShinta Sugimoto 	/* build migrate */
187913c1d189SArnaud Ebalard 	if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0)
18805c79de6eSShinta Sugimoto 		BUG();
18815c79de6eSShinta Sugimoto 
1882082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC);
18835c79de6eSShinta Sugimoto }
18845c79de6eSShinta Sugimoto #else
18855c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
188613c1d189SArnaud Ebalard 			     struct xfrm_migrate *m, int num_migrate,
188713c1d189SArnaud Ebalard 			     struct xfrm_kmaddress *k)
18885c79de6eSShinta Sugimoto {
18895c79de6eSShinta Sugimoto 	return -ENOPROTOOPT;
18905c79de6eSShinta Sugimoto }
18915c79de6eSShinta Sugimoto #endif
1892d51d081dSJamal Hadi Salim 
1893a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type)
1894492b558bSThomas Graf 
1895492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
1896492b558bSThomas Graf 	[XFRM_MSG_NEWSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info),
1897492b558bSThomas Graf 	[XFRM_MSG_DELSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
1898492b558bSThomas Graf 	[XFRM_MSG_GETSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
1899492b558bSThomas Graf 	[XFRM_MSG_NEWPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info),
1900492b558bSThomas Graf 	[XFRM_MSG_DELPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
1901492b558bSThomas Graf 	[XFRM_MSG_GETPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
1902492b558bSThomas Graf 	[XFRM_MSG_ALLOCSPI    - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info),
1903980ebd25SJamal Hadi Salim 	[XFRM_MSG_ACQUIRE     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire),
190453bc6b4dSJamal Hadi Salim 	[XFRM_MSG_EXPIRE      - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire),
1905492b558bSThomas Graf 	[XFRM_MSG_UPDPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info),
1906492b558bSThomas Graf 	[XFRM_MSG_UPDSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info),
19076c5c8ca7SJamal Hadi Salim 	[XFRM_MSG_POLEXPIRE   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire),
1908492b558bSThomas Graf 	[XFRM_MSG_FLUSHSA     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush),
1909a7bd9a45SThomas Graf 	[XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0,
1910d51d081dSJamal Hadi Salim 	[XFRM_MSG_NEWAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
1911d51d081dSJamal Hadi Salim 	[XFRM_MSG_GETAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
191297a64b45SMasahide NAKAMURA 	[XFRM_MSG_REPORT      - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
19135c79de6eSShinta Sugimoto 	[XFRM_MSG_MIGRATE     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
1914a7bd9a45SThomas Graf 	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
1915a7bd9a45SThomas Graf 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
19161da177e4SLinus Torvalds };
19171da177e4SLinus Torvalds 
1918492b558bSThomas Graf #undef XMSGSIZE
1919492b558bSThomas Graf 
1920cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
19211a6509d9SHerbert Xu 	[XFRMA_ALG_AEAD]	= { .len = sizeof(struct xfrm_algo_aead) },
1922cf5cb79fSThomas Graf 	[XFRMA_ALG_AUTH]	= { .len = sizeof(struct xfrm_algo) },
1923cf5cb79fSThomas Graf 	[XFRMA_ALG_CRYPT]	= { .len = sizeof(struct xfrm_algo) },
1924cf5cb79fSThomas Graf 	[XFRMA_ALG_COMP]	= { .len = sizeof(struct xfrm_algo) },
1925cf5cb79fSThomas Graf 	[XFRMA_ENCAP]		= { .len = sizeof(struct xfrm_encap_tmpl) },
1926cf5cb79fSThomas Graf 	[XFRMA_TMPL]		= { .len = sizeof(struct xfrm_user_tmpl) },
1927cf5cb79fSThomas Graf 	[XFRMA_SEC_CTX]		= { .len = sizeof(struct xfrm_sec_ctx) },
1928cf5cb79fSThomas Graf 	[XFRMA_LTIME_VAL]	= { .len = sizeof(struct xfrm_lifetime_cur) },
1929cf5cb79fSThomas Graf 	[XFRMA_REPLAY_VAL]	= { .len = sizeof(struct xfrm_replay_state) },
1930cf5cb79fSThomas Graf 	[XFRMA_REPLAY_THRESH]	= { .type = NLA_U32 },
1931cf5cb79fSThomas Graf 	[XFRMA_ETIMER_THRESH]	= { .type = NLA_U32 },
1932cf5cb79fSThomas Graf 	[XFRMA_SRCADDR]		= { .len = sizeof(xfrm_address_t) },
1933cf5cb79fSThomas Graf 	[XFRMA_COADDR]		= { .len = sizeof(xfrm_address_t) },
1934cf5cb79fSThomas Graf 	[XFRMA_POLICY_TYPE]	= { .len = sizeof(struct xfrm_userpolicy_type)},
1935cf5cb79fSThomas Graf 	[XFRMA_MIGRATE]		= { .len = sizeof(struct xfrm_user_migrate) },
193613c1d189SArnaud Ebalard 	[XFRMA_KMADDRESS]	= { .len = sizeof(struct xfrm_user_kmaddress) },
1937cf5cb79fSThomas Graf };
1938cf5cb79fSThomas Graf 
19391da177e4SLinus Torvalds static struct xfrm_link {
19405424f32eSThomas Graf 	int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
19411da177e4SLinus Torvalds 	int (*dump)(struct sk_buff *, struct netlink_callback *);
19424c563f76STimo Teras 	int (*done)(struct netlink_callback *);
1943492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = {
1944492b558bSThomas Graf 	[XFRM_MSG_NEWSA       - XFRM_MSG_BASE] = { .doit = xfrm_add_sa        },
1945492b558bSThomas Graf 	[XFRM_MSG_DELSA       - XFRM_MSG_BASE] = { .doit = xfrm_del_sa        },
1946492b558bSThomas Graf 	[XFRM_MSG_GETSA       - XFRM_MSG_BASE] = { .doit = xfrm_get_sa,
19474c563f76STimo Teras 						   .dump = xfrm_dump_sa,
19484c563f76STimo Teras 						   .done = xfrm_dump_sa_done  },
1949492b558bSThomas Graf 	[XFRM_MSG_NEWPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_add_policy    },
1950492b558bSThomas Graf 	[XFRM_MSG_DELPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_get_policy    },
1951492b558bSThomas Graf 	[XFRM_MSG_GETPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_get_policy,
19524c563f76STimo Teras 						   .dump = xfrm_dump_policy,
19534c563f76STimo Teras 						   .done = xfrm_dump_policy_done },
1954492b558bSThomas Graf 	[XFRM_MSG_ALLOCSPI    - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi },
1955980ebd25SJamal Hadi Salim 	[XFRM_MSG_ACQUIRE     - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire   },
195653bc6b4dSJamal Hadi Salim 	[XFRM_MSG_EXPIRE      - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire },
1957492b558bSThomas Graf 	[XFRM_MSG_UPDPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_add_policy    },
1958492b558bSThomas Graf 	[XFRM_MSG_UPDSA       - XFRM_MSG_BASE] = { .doit = xfrm_add_sa        },
19596c5c8ca7SJamal Hadi Salim 	[XFRM_MSG_POLEXPIRE   - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire},
1960492b558bSThomas Graf 	[XFRM_MSG_FLUSHSA     - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa      },
1961492b558bSThomas Graf 	[XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy  },
1962d51d081dSJamal Hadi Salim 	[XFRM_MSG_NEWAE       - XFRM_MSG_BASE] = { .doit = xfrm_new_ae  },
1963d51d081dSJamal Hadi Salim 	[XFRM_MSG_GETAE       - XFRM_MSG_BASE] = { .doit = xfrm_get_ae  },
19645c79de6eSShinta Sugimoto 	[XFRM_MSG_MIGRATE     - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate    },
196528d8909bSJamal Hadi Salim 	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo   },
1966ecfd6b18SJamal Hadi Salim 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo   },
19671da177e4SLinus Torvalds };
19681da177e4SLinus Torvalds 
19691d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
19701da177e4SLinus Torvalds {
197135a7aa08SThomas Graf 	struct nlattr *attrs[XFRMA_MAX+1];
19721da177e4SLinus Torvalds 	struct xfrm_link *link;
1973a7bd9a45SThomas Graf 	int type, err;
19741da177e4SLinus Torvalds 
19751da177e4SLinus Torvalds 	type = nlh->nlmsg_type;
19761da177e4SLinus Torvalds 	if (type > XFRM_MSG_MAX)
19771d00a4ebSThomas Graf 		return -EINVAL;
19781da177e4SLinus Torvalds 
19791da177e4SLinus Torvalds 	type -= XFRM_MSG_BASE;
19801da177e4SLinus Torvalds 	link = &xfrm_dispatch[type];
19811da177e4SLinus Torvalds 
19821da177e4SLinus Torvalds 	/* All operations require privileges, even GET */
19831d00a4ebSThomas Graf 	if (security_netlink_recv(skb, CAP_NET_ADMIN))
19841d00a4ebSThomas Graf 		return -EPERM;
19851da177e4SLinus Torvalds 
1986492b558bSThomas Graf 	if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
1987492b558bSThomas Graf 	     type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) &&
1988492b558bSThomas Graf 	    (nlh->nlmsg_flags & NLM_F_DUMP)) {
19891da177e4SLinus Torvalds 		if (link->dump == NULL)
19901d00a4ebSThomas Graf 			return -EINVAL;
19911da177e4SLinus Torvalds 
19924c563f76STimo Teras 		return netlink_dump_start(xfrm_nl, skb, nlh, link->dump, link->done);
19931da177e4SLinus Torvalds 	}
19941da177e4SLinus Torvalds 
199535a7aa08SThomas Graf 	err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX,
1996cf5cb79fSThomas Graf 			  xfrma_policy);
1997a7bd9a45SThomas Graf 	if (err < 0)
1998a7bd9a45SThomas Graf 		return err;
19991da177e4SLinus Torvalds 
20001da177e4SLinus Torvalds 	if (link->doit == NULL)
20011d00a4ebSThomas Graf 		return -EINVAL;
20021da177e4SLinus Torvalds 
20035424f32eSThomas Graf 	return link->doit(skb, nlh, attrs);
20041da177e4SLinus Torvalds }
20051da177e4SLinus Torvalds 
2006cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb)
20071da177e4SLinus Torvalds {
20084a3e2f71SArjan van de Ven 	mutex_lock(&xfrm_cfg_mutex);
2009cd40b7d3SDenis V. Lunev 	netlink_rcv_skb(skb, &xfrm_user_rcv_msg);
20104a3e2f71SArjan van de Ven 	mutex_unlock(&xfrm_cfg_mutex);
20111da177e4SLinus Torvalds }
20121da177e4SLinus Torvalds 
20137deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void)
20147deb2264SThomas Graf {
20157deb2264SThomas Graf 	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire));
20167deb2264SThomas Graf }
20177deb2264SThomas Graf 
2018d51d081dSJamal Hadi Salim static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c)
20191da177e4SLinus Torvalds {
20201da177e4SLinus Torvalds 	struct xfrm_user_expire *ue;
20211da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
20221da177e4SLinus Torvalds 
202379b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);
202479b8b7f4SThomas Graf 	if (nlh == NULL)
202579b8b7f4SThomas Graf 		return -EMSGSIZE;
20261da177e4SLinus Torvalds 
20277b67c857SThomas Graf 	ue = nlmsg_data(nlh);
20281da177e4SLinus Torvalds 	copy_to_user_state(x, &ue->state);
2029d51d081dSJamal Hadi Salim 	ue->hard = (c->data.hard != 0) ? 1 : 0;
20301da177e4SLinus Torvalds 
20319825069dSThomas Graf 	return nlmsg_end(skb, nlh);
20321da177e4SLinus Torvalds }
20331da177e4SLinus Torvalds 
203426b15dadSJamal Hadi Salim static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c)
20351da177e4SLinus Torvalds {
20361da177e4SLinus Torvalds 	struct sk_buff *skb;
20371da177e4SLinus Torvalds 
20387deb2264SThomas Graf 	skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
20391da177e4SLinus Torvalds 	if (skb == NULL)
20401da177e4SLinus Torvalds 		return -ENOMEM;
20411da177e4SLinus Torvalds 
2042d51d081dSJamal Hadi Salim 	if (build_expire(skb, x, c) < 0)
20431da177e4SLinus Torvalds 		BUG();
20441da177e4SLinus Torvalds 
2045082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
20461da177e4SLinus Torvalds }
20471da177e4SLinus Torvalds 
2048d51d081dSJamal Hadi Salim static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c)
2049d51d081dSJamal Hadi Salim {
2050d51d081dSJamal Hadi Salim 	struct sk_buff *skb;
2051d51d081dSJamal Hadi Salim 
20527deb2264SThomas Graf 	skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC);
2053d51d081dSJamal Hadi Salim 	if (skb == NULL)
2054d51d081dSJamal Hadi Salim 		return -ENOMEM;
2055d51d081dSJamal Hadi Salim 
2056d51d081dSJamal Hadi Salim 	if (build_aevent(skb, x, c) < 0)
2057d51d081dSJamal Hadi Salim 		BUG();
2058d51d081dSJamal Hadi Salim 
2059082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC);
2060d51d081dSJamal Hadi Salim }
2061d51d081dSJamal Hadi Salim 
206226b15dadSJamal Hadi Salim static int xfrm_notify_sa_flush(struct km_event *c)
206326b15dadSJamal Hadi Salim {
206426b15dadSJamal Hadi Salim 	struct xfrm_usersa_flush *p;
206526b15dadSJamal Hadi Salim 	struct nlmsghdr *nlh;
206626b15dadSJamal Hadi Salim 	struct sk_buff *skb;
20677deb2264SThomas Graf 	int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush));
206826b15dadSJamal Hadi Salim 
20697deb2264SThomas Graf 	skb = nlmsg_new(len, GFP_ATOMIC);
207026b15dadSJamal Hadi Salim 	if (skb == NULL)
207126b15dadSJamal Hadi Salim 		return -ENOMEM;
207226b15dadSJamal Hadi Salim 
207379b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0);
207479b8b7f4SThomas Graf 	if (nlh == NULL) {
207579b8b7f4SThomas Graf 		kfree_skb(skb);
207679b8b7f4SThomas Graf 		return -EMSGSIZE;
207779b8b7f4SThomas Graf 	}
207826b15dadSJamal Hadi Salim 
20797b67c857SThomas Graf 	p = nlmsg_data(nlh);
2080bf08867fSHerbert Xu 	p->proto = c->data.proto;
208126b15dadSJamal Hadi Salim 
20829825069dSThomas Graf 	nlmsg_end(skb, nlh);
208326b15dadSJamal Hadi Salim 
2084082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
208526b15dadSJamal Hadi Salim }
208626b15dadSJamal Hadi Salim 
20877deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x)
208826b15dadSJamal Hadi Salim {
20897deb2264SThomas Graf 	size_t l = 0;
20901a6509d9SHerbert Xu 	if (x->aead)
20911a6509d9SHerbert Xu 		l += nla_total_size(aead_len(x->aead));
209226b15dadSJamal Hadi Salim 	if (x->aalg)
20930f99be0dSEric Dumazet 		l += nla_total_size(xfrm_alg_len(x->aalg));
209426b15dadSJamal Hadi Salim 	if (x->ealg)
20950f99be0dSEric Dumazet 		l += nla_total_size(xfrm_alg_len(x->ealg));
209626b15dadSJamal Hadi Salim 	if (x->calg)
20977deb2264SThomas Graf 		l += nla_total_size(sizeof(*x->calg));
209826b15dadSJamal Hadi Salim 	if (x->encap)
20997deb2264SThomas Graf 		l += nla_total_size(sizeof(*x->encap));
210068325d3bSHerbert Xu 	if (x->security)
210168325d3bSHerbert Xu 		l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) +
210268325d3bSHerbert Xu 				    x->security->ctx_len);
210368325d3bSHerbert Xu 	if (x->coaddr)
210468325d3bSHerbert Xu 		l += nla_total_size(sizeof(*x->coaddr));
210568325d3bSHerbert Xu 
2106d26f3984SHerbert Xu 	/* Must count x->lastused as it may become non-zero behind our back. */
2107d26f3984SHerbert Xu 	l += nla_total_size(sizeof(u64));
210826b15dadSJamal Hadi Salim 
210926b15dadSJamal Hadi Salim 	return l;
211026b15dadSJamal Hadi Salim }
211126b15dadSJamal Hadi Salim 
211226b15dadSJamal Hadi Salim static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c)
211326b15dadSJamal Hadi Salim {
211426b15dadSJamal Hadi Salim 	struct xfrm_usersa_info *p;
21150603eac0SHerbert Xu 	struct xfrm_usersa_id *id;
211626b15dadSJamal Hadi Salim 	struct nlmsghdr *nlh;
211726b15dadSJamal Hadi Salim 	struct sk_buff *skb;
211826b15dadSJamal Hadi Salim 	int len = xfrm_sa_len(x);
21190603eac0SHerbert Xu 	int headlen;
21200603eac0SHerbert Xu 
21210603eac0SHerbert Xu 	headlen = sizeof(*p);
21220603eac0SHerbert Xu 	if (c->event == XFRM_MSG_DELSA) {
21237deb2264SThomas Graf 		len += nla_total_size(headlen);
21240603eac0SHerbert Xu 		headlen = sizeof(*id);
21250603eac0SHerbert Xu 	}
21267deb2264SThomas Graf 	len += NLMSG_ALIGN(headlen);
212726b15dadSJamal Hadi Salim 
21287deb2264SThomas Graf 	skb = nlmsg_new(len, GFP_ATOMIC);
212926b15dadSJamal Hadi Salim 	if (skb == NULL)
213026b15dadSJamal Hadi Salim 		return -ENOMEM;
213126b15dadSJamal Hadi Salim 
213279b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0);
213379b8b7f4SThomas Graf 	if (nlh == NULL)
2134c0144beaSThomas Graf 		goto nla_put_failure;
213526b15dadSJamal Hadi Salim 
21367b67c857SThomas Graf 	p = nlmsg_data(nlh);
21370603eac0SHerbert Xu 	if (c->event == XFRM_MSG_DELSA) {
2138c0144beaSThomas Graf 		struct nlattr *attr;
2139c0144beaSThomas Graf 
21407b67c857SThomas Graf 		id = nlmsg_data(nlh);
21410603eac0SHerbert Xu 		memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr));
21420603eac0SHerbert Xu 		id->spi = x->id.spi;
21430603eac0SHerbert Xu 		id->family = x->props.family;
21440603eac0SHerbert Xu 		id->proto = x->id.proto;
21450603eac0SHerbert Xu 
2146c0144beaSThomas Graf 		attr = nla_reserve(skb, XFRMA_SA, sizeof(*p));
2147c0144beaSThomas Graf 		if (attr == NULL)
2148c0144beaSThomas Graf 			goto nla_put_failure;
2149c0144beaSThomas Graf 
2150c0144beaSThomas Graf 		p = nla_data(attr);
21510603eac0SHerbert Xu 	}
21520603eac0SHerbert Xu 
215368325d3bSHerbert Xu 	if (copy_to_user_state_extra(x, p, skb))
215468325d3bSHerbert Xu 		goto nla_put_failure;
215526b15dadSJamal Hadi Salim 
21569825069dSThomas Graf 	nlmsg_end(skb, nlh);
215726b15dadSJamal Hadi Salim 
2158082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
215926b15dadSJamal Hadi Salim 
2160c0144beaSThomas Graf nla_put_failure:
216168325d3bSHerbert Xu 	/* Somebody screwed up with xfrm_sa_len! */
216268325d3bSHerbert Xu 	WARN_ON(1);
216326b15dadSJamal Hadi Salim 	kfree_skb(skb);
216426b15dadSJamal Hadi Salim 	return -1;
216526b15dadSJamal Hadi Salim }
216626b15dadSJamal Hadi Salim 
216726b15dadSJamal Hadi Salim static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c)
216826b15dadSJamal Hadi Salim {
216926b15dadSJamal Hadi Salim 
217026b15dadSJamal Hadi Salim 	switch (c->event) {
2171f60f6b8fSHerbert Xu 	case XFRM_MSG_EXPIRE:
217226b15dadSJamal Hadi Salim 		return xfrm_exp_state_notify(x, c);
2173d51d081dSJamal Hadi Salim 	case XFRM_MSG_NEWAE:
2174d51d081dSJamal Hadi Salim 		return xfrm_aevent_state_notify(x, c);
2175f60f6b8fSHerbert Xu 	case XFRM_MSG_DELSA:
2176f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDSA:
2177f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWSA:
217826b15dadSJamal Hadi Salim 		return xfrm_notify_sa(x, c);
2179f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHSA:
218026b15dadSJamal Hadi Salim 		return xfrm_notify_sa_flush(c);
218126b15dadSJamal Hadi Salim 	default:
218226b15dadSJamal Hadi Salim 		 printk("xfrm_user: Unknown SA event %d\n", c->event);
218326b15dadSJamal Hadi Salim 		 break;
218426b15dadSJamal Hadi Salim 	}
218526b15dadSJamal Hadi Salim 
218626b15dadSJamal Hadi Salim 	return 0;
218726b15dadSJamal Hadi Salim 
218826b15dadSJamal Hadi Salim }
218926b15dadSJamal Hadi Salim 
21907deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x,
21917deb2264SThomas Graf 					  struct xfrm_policy *xp)
21927deb2264SThomas Graf {
21937deb2264SThomas Graf 	return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
21947deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
21957deb2264SThomas Graf 	       + nla_total_size(xfrm_user_sec_ctx_size(x->security))
21967deb2264SThomas Graf 	       + userpolicy_type_attrsize();
21977deb2264SThomas Graf }
21987deb2264SThomas Graf 
21991da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
22001da177e4SLinus Torvalds 			 struct xfrm_tmpl *xt, struct xfrm_policy *xp,
22011da177e4SLinus Torvalds 			 int dir)
22021da177e4SLinus Torvalds {
22031da177e4SLinus Torvalds 	struct xfrm_user_acquire *ua;
22041da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
22051da177e4SLinus Torvalds 	__u32 seq = xfrm_get_acqseq();
22061da177e4SLinus Torvalds 
220779b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0);
220879b8b7f4SThomas Graf 	if (nlh == NULL)
220979b8b7f4SThomas Graf 		return -EMSGSIZE;
22101da177e4SLinus Torvalds 
22117b67c857SThomas Graf 	ua = nlmsg_data(nlh);
22121da177e4SLinus Torvalds 	memcpy(&ua->id, &x->id, sizeof(ua->id));
22131da177e4SLinus Torvalds 	memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr));
22141da177e4SLinus Torvalds 	memcpy(&ua->sel, &x->sel, sizeof(ua->sel));
22151da177e4SLinus Torvalds 	copy_to_user_policy(xp, &ua->policy, dir);
22161da177e4SLinus Torvalds 	ua->aalgos = xt->aalgos;
22171da177e4SLinus Torvalds 	ua->ealgos = xt->ealgos;
22181da177e4SLinus Torvalds 	ua->calgos = xt->calgos;
22191da177e4SLinus Torvalds 	ua->seq = x->km.seq = seq;
22201da177e4SLinus Torvalds 
22211da177e4SLinus Torvalds 	if (copy_to_user_tmpl(xp, skb) < 0)
22221da177e4SLinus Torvalds 		goto nlmsg_failure;
22230d681623SSerge Hallyn 	if (copy_to_user_state_sec_ctx(x, skb))
2224df71837dSTrent Jaeger 		goto nlmsg_failure;
22251459bb36SJamal Hadi Salim 	if (copy_to_user_policy_type(xp->type, skb) < 0)
2226f7b6983fSMasahide NAKAMURA 		goto nlmsg_failure;
22271da177e4SLinus Torvalds 
22289825069dSThomas Graf 	return nlmsg_end(skb, nlh);
22291da177e4SLinus Torvalds 
22301da177e4SLinus Torvalds nlmsg_failure:
22319825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
22329825069dSThomas Graf 	return -EMSGSIZE;
22331da177e4SLinus Torvalds }
22341da177e4SLinus Torvalds 
22351da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
22361da177e4SLinus Torvalds 			     struct xfrm_policy *xp, int dir)
22371da177e4SLinus Torvalds {
22381da177e4SLinus Torvalds 	struct sk_buff *skb;
22391da177e4SLinus Torvalds 
22407deb2264SThomas Graf 	skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
22411da177e4SLinus Torvalds 	if (skb == NULL)
22421da177e4SLinus Torvalds 		return -ENOMEM;
22431da177e4SLinus Torvalds 
22441da177e4SLinus Torvalds 	if (build_acquire(skb, x, xt, xp, dir) < 0)
22451da177e4SLinus Torvalds 		BUG();
22461da177e4SLinus Torvalds 
2247082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC);
22481da177e4SLinus Torvalds }
22491da177e4SLinus Torvalds 
22501da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0
22511da177e4SLinus Torvalds  * or more templates.
22521da177e4SLinus Torvalds  */
2253cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
22541da177e4SLinus Torvalds 					       u8 *data, int len, int *dir)
22551da177e4SLinus Torvalds {
22561da177e4SLinus Torvalds 	struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data;
22571da177e4SLinus Torvalds 	struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1);
22581da177e4SLinus Torvalds 	struct xfrm_policy *xp;
22591da177e4SLinus Torvalds 	int nr;
22601da177e4SLinus Torvalds 
2261cb969f07SVenkat Yekkirala 	switch (sk->sk_family) {
22621da177e4SLinus Torvalds 	case AF_INET:
22631da177e4SLinus Torvalds 		if (opt != IP_XFRM_POLICY) {
22641da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
22651da177e4SLinus Torvalds 			return NULL;
22661da177e4SLinus Torvalds 		}
22671da177e4SLinus Torvalds 		break;
22681da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
22691da177e4SLinus Torvalds 	case AF_INET6:
22701da177e4SLinus Torvalds 		if (opt != IPV6_XFRM_POLICY) {
22711da177e4SLinus Torvalds 			*dir = -EOPNOTSUPP;
22721da177e4SLinus Torvalds 			return NULL;
22731da177e4SLinus Torvalds 		}
22741da177e4SLinus Torvalds 		break;
22751da177e4SLinus Torvalds #endif
22761da177e4SLinus Torvalds 	default:
22771da177e4SLinus Torvalds 		*dir = -EINVAL;
22781da177e4SLinus Torvalds 		return NULL;
22791da177e4SLinus Torvalds 	}
22801da177e4SLinus Torvalds 
22811da177e4SLinus Torvalds 	*dir = -EINVAL;
22821da177e4SLinus Torvalds 
22831da177e4SLinus Torvalds 	if (len < sizeof(*p) ||
22841da177e4SLinus Torvalds 	    verify_newpolicy_info(p))
22851da177e4SLinus Torvalds 		return NULL;
22861da177e4SLinus Torvalds 
22871da177e4SLinus Torvalds 	nr = ((len - sizeof(*p)) / sizeof(*ut));
2288b4ad86bfSDavid S. Miller 	if (validate_tmpl(nr, ut, p->sel.family))
22891da177e4SLinus Torvalds 		return NULL;
22901da177e4SLinus Torvalds 
2291a4f1bac6SHerbert Xu 	if (p->dir > XFRM_POLICY_OUT)
2292a4f1bac6SHerbert Xu 		return NULL;
2293a4f1bac6SHerbert Xu 
22940331b1f3SAlexey Dobriyan 	xp = xfrm_policy_alloc(&init_net, GFP_KERNEL);
22951da177e4SLinus Torvalds 	if (xp == NULL) {
22961da177e4SLinus Torvalds 		*dir = -ENOBUFS;
22971da177e4SLinus Torvalds 		return NULL;
22981da177e4SLinus Torvalds 	}
22991da177e4SLinus Torvalds 
23001da177e4SLinus Torvalds 	copy_from_user_policy(xp, p);
2301f7b6983fSMasahide NAKAMURA 	xp->type = XFRM_POLICY_TYPE_MAIN;
23021da177e4SLinus Torvalds 	copy_templates(xp, ut, nr);
23031da177e4SLinus Torvalds 
23041da177e4SLinus Torvalds 	*dir = p->dir;
23051da177e4SLinus Torvalds 
23061da177e4SLinus Torvalds 	return xp;
23071da177e4SLinus Torvalds }
23081da177e4SLinus Torvalds 
23097deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp)
23107deb2264SThomas Graf {
23117deb2264SThomas Graf 	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
23127deb2264SThomas Graf 	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
23137deb2264SThomas Graf 	       + nla_total_size(xfrm_user_sec_ctx_size(xp->security))
23147deb2264SThomas Graf 	       + userpolicy_type_attrsize();
23157deb2264SThomas Graf }
23167deb2264SThomas Graf 
23171da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
2318d51d081dSJamal Hadi Salim 			   int dir, struct km_event *c)
23191da177e4SLinus Torvalds {
23201da177e4SLinus Torvalds 	struct xfrm_user_polexpire *upe;
23211da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2322d51d081dSJamal Hadi Salim 	int hard = c->data.hard;
23231da177e4SLinus Torvalds 
232479b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0);
232579b8b7f4SThomas Graf 	if (nlh == NULL)
232679b8b7f4SThomas Graf 		return -EMSGSIZE;
23271da177e4SLinus Torvalds 
23287b67c857SThomas Graf 	upe = nlmsg_data(nlh);
23291da177e4SLinus Torvalds 	copy_to_user_policy(xp, &upe->pol, dir);
23301da177e4SLinus Torvalds 	if (copy_to_user_tmpl(xp, skb) < 0)
23311da177e4SLinus Torvalds 		goto nlmsg_failure;
2332df71837dSTrent Jaeger 	if (copy_to_user_sec_ctx(xp, skb))
2333df71837dSTrent Jaeger 		goto nlmsg_failure;
23341459bb36SJamal Hadi Salim 	if (copy_to_user_policy_type(xp->type, skb) < 0)
2335f7b6983fSMasahide NAKAMURA 		goto nlmsg_failure;
23361da177e4SLinus Torvalds 	upe->hard = !!hard;
23371da177e4SLinus Torvalds 
23389825069dSThomas Graf 	return nlmsg_end(skb, nlh);
23391da177e4SLinus Torvalds 
23401da177e4SLinus Torvalds nlmsg_failure:
23419825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
23429825069dSThomas Graf 	return -EMSGSIZE;
23431da177e4SLinus Torvalds }
23441da177e4SLinus Torvalds 
234526b15dadSJamal Hadi Salim static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
23461da177e4SLinus Torvalds {
23471da177e4SLinus Torvalds 	struct sk_buff *skb;
23481da177e4SLinus Torvalds 
23497deb2264SThomas Graf 	skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC);
23501da177e4SLinus Torvalds 	if (skb == NULL)
23511da177e4SLinus Torvalds 		return -ENOMEM;
23521da177e4SLinus Torvalds 
2353d51d081dSJamal Hadi Salim 	if (build_polexpire(skb, xp, dir, c) < 0)
23541da177e4SLinus Torvalds 		BUG();
23551da177e4SLinus Torvalds 
2356082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
23571da177e4SLinus Torvalds }
23581da177e4SLinus Torvalds 
235926b15dadSJamal Hadi Salim static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c)
236026b15dadSJamal Hadi Salim {
236126b15dadSJamal Hadi Salim 	struct xfrm_userpolicy_info *p;
23620603eac0SHerbert Xu 	struct xfrm_userpolicy_id *id;
236326b15dadSJamal Hadi Salim 	struct nlmsghdr *nlh;
236426b15dadSJamal Hadi Salim 	struct sk_buff *skb;
23657deb2264SThomas Graf 	int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
23660603eac0SHerbert Xu 	int headlen;
23670603eac0SHerbert Xu 
23680603eac0SHerbert Xu 	headlen = sizeof(*p);
23690603eac0SHerbert Xu 	if (c->event == XFRM_MSG_DELPOLICY) {
23707deb2264SThomas Graf 		len += nla_total_size(headlen);
23710603eac0SHerbert Xu 		headlen = sizeof(*id);
23720603eac0SHerbert Xu 	}
2373cfbfd45aSThomas Graf 	len += userpolicy_type_attrsize();
23747deb2264SThomas Graf 	len += NLMSG_ALIGN(headlen);
237526b15dadSJamal Hadi Salim 
23767deb2264SThomas Graf 	skb = nlmsg_new(len, GFP_ATOMIC);
237726b15dadSJamal Hadi Salim 	if (skb == NULL)
237826b15dadSJamal Hadi Salim 		return -ENOMEM;
237926b15dadSJamal Hadi Salim 
238079b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0);
238179b8b7f4SThomas Graf 	if (nlh == NULL)
238279b8b7f4SThomas Graf 		goto nlmsg_failure;
238326b15dadSJamal Hadi Salim 
23847b67c857SThomas Graf 	p = nlmsg_data(nlh);
23850603eac0SHerbert Xu 	if (c->event == XFRM_MSG_DELPOLICY) {
2386c0144beaSThomas Graf 		struct nlattr *attr;
2387c0144beaSThomas Graf 
23887b67c857SThomas Graf 		id = nlmsg_data(nlh);
23890603eac0SHerbert Xu 		memset(id, 0, sizeof(*id));
23900603eac0SHerbert Xu 		id->dir = dir;
23910603eac0SHerbert Xu 		if (c->data.byid)
23920603eac0SHerbert Xu 			id->index = xp->index;
23930603eac0SHerbert Xu 		else
23940603eac0SHerbert Xu 			memcpy(&id->sel, &xp->selector, sizeof(id->sel));
23950603eac0SHerbert Xu 
2396c0144beaSThomas Graf 		attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p));
2397c0144beaSThomas Graf 		if (attr == NULL)
2398c0144beaSThomas Graf 			goto nlmsg_failure;
2399c0144beaSThomas Graf 
2400c0144beaSThomas Graf 		p = nla_data(attr);
24010603eac0SHerbert Xu 	}
240226b15dadSJamal Hadi Salim 
240326b15dadSJamal Hadi Salim 	copy_to_user_policy(xp, p, dir);
240426b15dadSJamal Hadi Salim 	if (copy_to_user_tmpl(xp, skb) < 0)
240526b15dadSJamal Hadi Salim 		goto nlmsg_failure;
24061459bb36SJamal Hadi Salim 	if (copy_to_user_policy_type(xp->type, skb) < 0)
2407f7b6983fSMasahide NAKAMURA 		goto nlmsg_failure;
240826b15dadSJamal Hadi Salim 
24099825069dSThomas Graf 	nlmsg_end(skb, nlh);
241026b15dadSJamal Hadi Salim 
2411082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
241226b15dadSJamal Hadi Salim 
241326b15dadSJamal Hadi Salim nlmsg_failure:
241426b15dadSJamal Hadi Salim 	kfree_skb(skb);
241526b15dadSJamal Hadi Salim 	return -1;
241626b15dadSJamal Hadi Salim }
241726b15dadSJamal Hadi Salim 
241826b15dadSJamal Hadi Salim static int xfrm_notify_policy_flush(struct km_event *c)
241926b15dadSJamal Hadi Salim {
242026b15dadSJamal Hadi Salim 	struct nlmsghdr *nlh;
242126b15dadSJamal Hadi Salim 	struct sk_buff *skb;
242226b15dadSJamal Hadi Salim 
24237deb2264SThomas Graf 	skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC);
242426b15dadSJamal Hadi Salim 	if (skb == NULL)
242526b15dadSJamal Hadi Salim 		return -ENOMEM;
242626b15dadSJamal Hadi Salim 
242779b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0);
242879b8b7f4SThomas Graf 	if (nlh == NULL)
242979b8b7f4SThomas Graf 		goto nlmsg_failure;
24300c51f53cSJamal Hadi Salim 	if (copy_to_user_policy_type(c->data.type, skb) < 0)
24310c51f53cSJamal Hadi Salim 		goto nlmsg_failure;
243226b15dadSJamal Hadi Salim 
24339825069dSThomas Graf 	nlmsg_end(skb, nlh);
243426b15dadSJamal Hadi Salim 
2435082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
243626b15dadSJamal Hadi Salim 
243726b15dadSJamal Hadi Salim nlmsg_failure:
243826b15dadSJamal Hadi Salim 	kfree_skb(skb);
243926b15dadSJamal Hadi Salim 	return -1;
244026b15dadSJamal Hadi Salim }
244126b15dadSJamal Hadi Salim 
244226b15dadSJamal Hadi Salim static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
244326b15dadSJamal Hadi Salim {
244426b15dadSJamal Hadi Salim 
244526b15dadSJamal Hadi Salim 	switch (c->event) {
2446f60f6b8fSHerbert Xu 	case XFRM_MSG_NEWPOLICY:
2447f60f6b8fSHerbert Xu 	case XFRM_MSG_UPDPOLICY:
2448f60f6b8fSHerbert Xu 	case XFRM_MSG_DELPOLICY:
244926b15dadSJamal Hadi Salim 		return xfrm_notify_policy(xp, dir, c);
2450f60f6b8fSHerbert Xu 	case XFRM_MSG_FLUSHPOLICY:
245126b15dadSJamal Hadi Salim 		return xfrm_notify_policy_flush(c);
2452f60f6b8fSHerbert Xu 	case XFRM_MSG_POLEXPIRE:
245326b15dadSJamal Hadi Salim 		return xfrm_exp_policy_notify(xp, dir, c);
245426b15dadSJamal Hadi Salim 	default:
245526b15dadSJamal Hadi Salim 		printk("xfrm_user: Unknown Policy event %d\n", c->event);
245626b15dadSJamal Hadi Salim 	}
245726b15dadSJamal Hadi Salim 
245826b15dadSJamal Hadi Salim 	return 0;
245926b15dadSJamal Hadi Salim 
246026b15dadSJamal Hadi Salim }
246126b15dadSJamal Hadi Salim 
24627deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void)
24637deb2264SThomas Graf {
24647deb2264SThomas Graf 	return NLMSG_ALIGN(sizeof(struct xfrm_user_report));
24657deb2264SThomas Graf }
24667deb2264SThomas Graf 
246797a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto,
246897a64b45SMasahide NAKAMURA 			struct xfrm_selector *sel, xfrm_address_t *addr)
246997a64b45SMasahide NAKAMURA {
247097a64b45SMasahide NAKAMURA 	struct xfrm_user_report *ur;
247197a64b45SMasahide NAKAMURA 	struct nlmsghdr *nlh;
247297a64b45SMasahide NAKAMURA 
247379b8b7f4SThomas Graf 	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0);
247479b8b7f4SThomas Graf 	if (nlh == NULL)
247579b8b7f4SThomas Graf 		return -EMSGSIZE;
247697a64b45SMasahide NAKAMURA 
24777b67c857SThomas Graf 	ur = nlmsg_data(nlh);
247897a64b45SMasahide NAKAMURA 	ur->proto = proto;
247997a64b45SMasahide NAKAMURA 	memcpy(&ur->sel, sel, sizeof(ur->sel));
248097a64b45SMasahide NAKAMURA 
248197a64b45SMasahide NAKAMURA 	if (addr)
2482c0144beaSThomas Graf 		NLA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr);
248397a64b45SMasahide NAKAMURA 
24849825069dSThomas Graf 	return nlmsg_end(skb, nlh);
248597a64b45SMasahide NAKAMURA 
2486c0144beaSThomas Graf nla_put_failure:
24879825069dSThomas Graf 	nlmsg_cancel(skb, nlh);
24889825069dSThomas Graf 	return -EMSGSIZE;
248997a64b45SMasahide NAKAMURA }
249097a64b45SMasahide NAKAMURA 
249197a64b45SMasahide NAKAMURA static int xfrm_send_report(u8 proto, struct xfrm_selector *sel,
249297a64b45SMasahide NAKAMURA 			    xfrm_address_t *addr)
249397a64b45SMasahide NAKAMURA {
249497a64b45SMasahide NAKAMURA 	struct sk_buff *skb;
249597a64b45SMasahide NAKAMURA 
24967deb2264SThomas Graf 	skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC);
249797a64b45SMasahide NAKAMURA 	if (skb == NULL)
249897a64b45SMasahide NAKAMURA 		return -ENOMEM;
249997a64b45SMasahide NAKAMURA 
250097a64b45SMasahide NAKAMURA 	if (build_report(skb, proto, sel, addr) < 0)
250197a64b45SMasahide NAKAMURA 		BUG();
250297a64b45SMasahide NAKAMURA 
2503082a1ad5SThomas Graf 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC);
250497a64b45SMasahide NAKAMURA }
250597a64b45SMasahide NAKAMURA 
25063a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void)
25073a2dfbe8SMartin Willi {
25083a2dfbe8SMartin Willi 	return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping));
25093a2dfbe8SMartin Willi }
25103a2dfbe8SMartin Willi 
25113a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x,
25123a2dfbe8SMartin Willi 			 xfrm_address_t *new_saddr, __be16 new_sport)
25133a2dfbe8SMartin Willi {
25143a2dfbe8SMartin Willi 	struct xfrm_user_mapping *um;
25153a2dfbe8SMartin Willi 	struct nlmsghdr *nlh;
25163a2dfbe8SMartin Willi 
25173a2dfbe8SMartin Willi 	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0);
25183a2dfbe8SMartin Willi 	if (nlh == NULL)
25193a2dfbe8SMartin Willi 		return -EMSGSIZE;
25203a2dfbe8SMartin Willi 
25213a2dfbe8SMartin Willi 	um = nlmsg_data(nlh);
25223a2dfbe8SMartin Willi 
25233a2dfbe8SMartin Willi 	memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr));
25243a2dfbe8SMartin Willi 	um->id.spi = x->id.spi;
25253a2dfbe8SMartin Willi 	um->id.family = x->props.family;
25263a2dfbe8SMartin Willi 	um->id.proto = x->id.proto;
25273a2dfbe8SMartin Willi 	memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr));
25283a2dfbe8SMartin Willi 	memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr));
25293a2dfbe8SMartin Willi 	um->new_sport = new_sport;
25303a2dfbe8SMartin Willi 	um->old_sport = x->encap->encap_sport;
25313a2dfbe8SMartin Willi 	um->reqid = x->props.reqid;
25323a2dfbe8SMartin Willi 
25333a2dfbe8SMartin Willi 	return nlmsg_end(skb, nlh);
25343a2dfbe8SMartin Willi }
25353a2dfbe8SMartin Willi 
25363a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
25373a2dfbe8SMartin Willi 			     __be16 sport)
25383a2dfbe8SMartin Willi {
25393a2dfbe8SMartin Willi 	struct sk_buff *skb;
25403a2dfbe8SMartin Willi 
25413a2dfbe8SMartin Willi 	if (x->id.proto != IPPROTO_ESP)
25423a2dfbe8SMartin Willi 		return -EINVAL;
25433a2dfbe8SMartin Willi 
25443a2dfbe8SMartin Willi 	if (!x->encap)
25453a2dfbe8SMartin Willi 		return -EINVAL;
25463a2dfbe8SMartin Willi 
25473a2dfbe8SMartin Willi 	skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC);
25483a2dfbe8SMartin Willi 	if (skb == NULL)
25493a2dfbe8SMartin Willi 		return -ENOMEM;
25503a2dfbe8SMartin Willi 
25513a2dfbe8SMartin Willi 	if (build_mapping(skb, x, ipaddr, sport) < 0)
25523a2dfbe8SMartin Willi 		BUG();
25533a2dfbe8SMartin Willi 
25543a2dfbe8SMartin Willi 	return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
25553a2dfbe8SMartin Willi }
25563a2dfbe8SMartin Willi 
25571da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = {
25581da177e4SLinus Torvalds 	.id		= "netlink",
25591da177e4SLinus Torvalds 	.notify		= xfrm_send_state_notify,
25601da177e4SLinus Torvalds 	.acquire	= xfrm_send_acquire,
25611da177e4SLinus Torvalds 	.compile_policy	= xfrm_compile_policy,
25621da177e4SLinus Torvalds 	.notify_policy	= xfrm_send_policy_notify,
256397a64b45SMasahide NAKAMURA 	.report		= xfrm_send_report,
25645c79de6eSShinta Sugimoto 	.migrate	= xfrm_send_migrate,
25653a2dfbe8SMartin Willi 	.new_mapping	= xfrm_send_mapping,
25661da177e4SLinus Torvalds };
25671da177e4SLinus Torvalds 
25681da177e4SLinus Torvalds static int __init xfrm_user_init(void)
25691da177e4SLinus Torvalds {
2570be33690dSPatrick McHardy 	struct sock *nlsk;
2571be33690dSPatrick McHardy 
2572654b32c6SMasahide NAKAMURA 	printk(KERN_INFO "Initializing XFRM netlink socket\n");
25731da177e4SLinus Torvalds 
2574b4b51029SEric W. Biederman 	nlsk = netlink_kernel_create(&init_net, NETLINK_XFRM, XFRMNLGRP_MAX,
2575af65bdfcSPatrick McHardy 				     xfrm_netlink_rcv, NULL, THIS_MODULE);
2576be33690dSPatrick McHardy 	if (nlsk == NULL)
25771da177e4SLinus Torvalds 		return -ENOMEM;
2578be33690dSPatrick McHardy 	rcu_assign_pointer(xfrm_nl, nlsk);
25791da177e4SLinus Torvalds 
25801da177e4SLinus Torvalds 	xfrm_register_km(&netlink_mgr);
25811da177e4SLinus Torvalds 
25821da177e4SLinus Torvalds 	return 0;
25831da177e4SLinus Torvalds }
25841da177e4SLinus Torvalds 
25851da177e4SLinus Torvalds static void __exit xfrm_user_exit(void)
25861da177e4SLinus Torvalds {
2587be33690dSPatrick McHardy 	struct sock *nlsk = xfrm_nl;
2588be33690dSPatrick McHardy 
25891da177e4SLinus Torvalds 	xfrm_unregister_km(&netlink_mgr);
2590be33690dSPatrick McHardy 	rcu_assign_pointer(xfrm_nl, NULL);
2591be33690dSPatrick McHardy 	synchronize_rcu();
2592b7c6ba6eSDenis V. Lunev 	netlink_kernel_release(nlsk);
25931da177e4SLinus Torvalds }
25941da177e4SLinus Torvalds 
25951da177e4SLinus Torvalds module_init(xfrm_user_init);
25961da177e4SLinus Torvalds module_exit(xfrm_user_exit);
25971da177e4SLinus Torvalds MODULE_LICENSE("GPL");
25984fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM);
2599f8cd5488SJamal Hadi Salim 
2600