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 345424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) 351da177e4SLinus Torvalds { 365424f32eSThomas Graf struct nlattr *rt = attrs[type]; 371da177e4SLinus Torvalds struct xfrm_algo *algp; 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds if (!rt) 401da177e4SLinus Torvalds return 0; 411da177e4SLinus Torvalds 425424f32eSThomas Graf algp = nla_data(rt); 430f99be0dSEric Dumazet if (nla_len(rt) < xfrm_alg_len(algp)) 4431c26852SHerbert Xu return -EINVAL; 4531c26852SHerbert Xu 461da177e4SLinus Torvalds switch (type) { 471da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 481da177e4SLinus Torvalds if (!algp->alg_key_len && 491da177e4SLinus Torvalds strcmp(algp->alg_name, "digest_null") != 0) 501da177e4SLinus Torvalds return -EINVAL; 511da177e4SLinus Torvalds break; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 541da177e4SLinus Torvalds if (!algp->alg_key_len && 551da177e4SLinus Torvalds strcmp(algp->alg_name, "cipher_null") != 0) 561da177e4SLinus Torvalds return -EINVAL; 571da177e4SLinus Torvalds break; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds case XFRMA_ALG_COMP: 601da177e4SLinus Torvalds /* Zero length keys are legal. */ 611da177e4SLinus Torvalds break; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds default: 641da177e4SLinus Torvalds return -EINVAL; 653ff50b79SStephen Hemminger } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 681da177e4SLinus Torvalds return 0; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 715424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, 72eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 73eb2971b6SMasahide NAKAMURA { 745424f32eSThomas Graf struct nlattr *rt = attrs[type]; 75eb2971b6SMasahide NAKAMURA 76cf5cb79fSThomas Graf if (rt && addrp) 775424f32eSThomas Graf *addrp = nla_data(rt); 78eb2971b6SMasahide NAKAMURA } 79df71837dSTrent Jaeger 805424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs) 81df71837dSTrent Jaeger { 825424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 83df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 84df71837dSTrent Jaeger 85df71837dSTrent Jaeger if (!rt) 86df71837dSTrent Jaeger return 0; 87df71837dSTrent Jaeger 885424f32eSThomas Graf uctx = nla_data(rt); 89cf5cb79fSThomas Graf if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) 90df71837dSTrent Jaeger return -EINVAL; 91df71837dSTrent Jaeger 92df71837dSTrent Jaeger return 0; 93df71837dSTrent Jaeger } 94df71837dSTrent Jaeger 95df71837dSTrent Jaeger 961da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 975424f32eSThomas Graf struct nlattr **attrs) 981da177e4SLinus Torvalds { 991da177e4SLinus Torvalds int err; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds err = -EINVAL; 1021da177e4SLinus Torvalds switch (p->family) { 1031da177e4SLinus Torvalds case AF_INET: 1041da177e4SLinus Torvalds break; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds case AF_INET6: 1071da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1081da177e4SLinus Torvalds break; 1091da177e4SLinus Torvalds #else 1101da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1111da177e4SLinus Torvalds goto out; 1121da177e4SLinus Torvalds #endif 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds default: 1151da177e4SLinus Torvalds goto out; 1163ff50b79SStephen Hemminger } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds err = -EINVAL; 1191da177e4SLinus Torvalds switch (p->id.proto) { 1201da177e4SLinus Torvalds case IPPROTO_AH: 12135a7aa08SThomas Graf if (!attrs[XFRMA_ALG_AUTH] || 12235a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 12335a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]) 1241da177e4SLinus Torvalds goto out; 1251da177e4SLinus Torvalds break; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds case IPPROTO_ESP: 12835a7aa08SThomas Graf if ((!attrs[XFRMA_ALG_AUTH] && 12935a7aa08SThomas Graf !attrs[XFRMA_ALG_CRYPT]) || 13035a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]) 1311da177e4SLinus Torvalds goto out; 1321da177e4SLinus Torvalds break; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds case IPPROTO_COMP: 13535a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 13635a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 13735a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]) 1381da177e4SLinus Torvalds goto out; 1391da177e4SLinus Torvalds break; 1401da177e4SLinus Torvalds 141e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 142e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 143e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 14435a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 14535a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 14635a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 14735a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 14835a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 14935a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 150e23c7194SMasahide NAKAMURA goto out; 151e23c7194SMasahide NAKAMURA break; 152e23c7194SMasahide NAKAMURA #endif 153e23c7194SMasahide NAKAMURA 1541da177e4SLinus Torvalds default: 1551da177e4SLinus Torvalds goto out; 1563ff50b79SStephen Hemminger } 1571da177e4SLinus Torvalds 15835a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 1591da177e4SLinus Torvalds goto out; 16035a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 1611da177e4SLinus Torvalds goto out; 16235a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 1631da177e4SLinus Torvalds goto out; 16435a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 165df71837dSTrent Jaeger goto out; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds err = -EINVAL; 1681da177e4SLinus Torvalds switch (p->mode) { 1697e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 1707e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 171060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 1720a69452cSDiego Beltrami case XFRM_MODE_BEET: 1731da177e4SLinus Torvalds break; 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds default: 1761da177e4SLinus Torvalds goto out; 1773ff50b79SStephen Hemminger } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds err = 0; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds out: 1821da177e4SLinus Torvalds return err; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 1861da177e4SLinus Torvalds struct xfrm_algo_desc *(*get_byname)(char *, int), 1875424f32eSThomas Graf struct nlattr *rta) 1881da177e4SLinus Torvalds { 1891da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 1901da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds if (!rta) 1931da177e4SLinus Torvalds return 0; 1941da177e4SLinus Torvalds 1955424f32eSThomas Graf ualg = nla_data(rta); 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 1981da177e4SLinus Torvalds if (!algo) 1991da177e4SLinus Torvalds return -ENOSYS; 2001da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2011da177e4SLinus Torvalds 2020f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2031da177e4SLinus Torvalds if (!p) 2041da177e4SLinus Torvalds return -ENOMEM; 2051da177e4SLinus Torvalds 20604ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2071da177e4SLinus Torvalds *algpp = p; 2081da177e4SLinus Torvalds return 0; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 211661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 212df71837dSTrent Jaeger { 213df71837dSTrent Jaeger int len = 0; 214df71837dSTrent Jaeger 215df71837dSTrent Jaeger if (xfrm_ctx) { 216df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 217df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 218df71837dSTrent Jaeger } 219df71837dSTrent Jaeger return len; 220df71837dSTrent Jaeger } 221df71837dSTrent Jaeger 2221da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 2231da177e4SLinus Torvalds { 2241da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 2251da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 2261da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 2271da177e4SLinus Torvalds x->props.mode = p->mode; 2281da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 2291da177e4SLinus Torvalds x->props.reqid = p->reqid; 2301da177e4SLinus Torvalds x->props.family = p->family; 23154489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 2321da177e4SLinus Torvalds x->props.flags = p->flags; 233196b0036SHerbert Xu 234196b0036SHerbert Xu /* 235196b0036SHerbert Xu * Set inner address family if the KM left it as zero. 236196b0036SHerbert Xu * See comment in validate_tmpl. 237196b0036SHerbert Xu */ 238196b0036SHerbert Xu if (!x->sel.family) 239196b0036SHerbert Xu x->sel.family = p->family; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 242d51d081dSJamal Hadi Salim /* 243d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 244d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 245d51d081dSJamal Hadi Salim * 246d51d081dSJamal Hadi Salim */ 2475424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) 248d51d081dSJamal Hadi Salim { 2495424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 2505424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 2515424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 2525424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 253d51d081dSJamal Hadi Salim 254d51d081dSJamal Hadi Salim if (rp) { 255d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 2565424f32eSThomas Graf replay = nla_data(rp); 257d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 258d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 259d51d081dSJamal Hadi Salim } 260d51d081dSJamal Hadi Salim 261d51d081dSJamal Hadi Salim if (lt) { 262d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 2635424f32eSThomas Graf ltime = nla_data(lt); 264d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 265d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 266d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 267d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 268d51d081dSJamal Hadi Salim } 269d51d081dSJamal Hadi Salim 270cf5cb79fSThomas Graf if (et) 2715424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 272d51d081dSJamal Hadi Salim 273cf5cb79fSThomas Graf if (rt) 2745424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 275d51d081dSJamal Hadi Salim } 276d51d081dSJamal Hadi Salim 2771da177e4SLinus Torvalds static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, 2785424f32eSThomas Graf struct nlattr **attrs, 2791da177e4SLinus Torvalds int *errp) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds struct xfrm_state *x = xfrm_state_alloc(); 2821da177e4SLinus Torvalds int err = -ENOMEM; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds if (!x) 2851da177e4SLinus Torvalds goto error_no_put; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds copy_from_user_state(x, p); 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds if ((err = attach_one_algo(&x->aalg, &x->props.aalgo, 2901da177e4SLinus Torvalds xfrm_aalg_get_byname, 29135a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 2921da177e4SLinus Torvalds goto error; 2931da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 2941da177e4SLinus Torvalds xfrm_ealg_get_byname, 29535a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 2961da177e4SLinus Torvalds goto error; 2971da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 2981da177e4SLinus Torvalds xfrm_calg_get_byname, 29935a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 3001da177e4SLinus Torvalds goto error; 301fd21150aSThomas Graf 302fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 303fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 304fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 305fd21150aSThomas Graf if (x->encap == NULL) 3061da177e4SLinus Torvalds goto error; 307fd21150aSThomas Graf } 308fd21150aSThomas Graf 309fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 310fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 311fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 312fd21150aSThomas Graf if (x->coaddr == NULL) 313060f02a3SNoriaki TAKAMIYA goto error; 314fd21150aSThomas Graf } 315fd21150aSThomas Graf 31672cb6962SHerbert Xu err = xfrm_init_state(x); 3171da177e4SLinus Torvalds if (err) 3181da177e4SLinus Torvalds goto error; 3191da177e4SLinus Torvalds 320fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 321fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 322df71837dSTrent Jaeger goto error; 323df71837dSTrent Jaeger 3241da177e4SLinus Torvalds x->km.seq = p->seq; 325d51d081dSJamal Hadi Salim x->replay_maxdiff = sysctl_xfrm_aevent_rseqth; 326d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 327d51d081dSJamal Hadi Salim x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M; 328d51d081dSJamal Hadi Salim x->preplay.bitmap = 0; 329d51d081dSJamal Hadi Salim x->preplay.seq = x->replay.seq+x->replay_maxdiff; 330d51d081dSJamal Hadi Salim x->preplay.oseq = x->replay.oseq +x->replay_maxdiff; 331d51d081dSJamal Hadi Salim 332d51d081dSJamal Hadi Salim /* override default values from above */ 333d51d081dSJamal Hadi Salim 3345424f32eSThomas Graf xfrm_update_ae_params(x, attrs); 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds return x; 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds error: 3391da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 3401da177e4SLinus Torvalds xfrm_state_put(x); 3411da177e4SLinus Torvalds error_no_put: 3421da177e4SLinus Torvalds *errp = err; 3431da177e4SLinus Torvalds return NULL; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 34622e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 3475424f32eSThomas Graf struct nlattr **attrs) 3481da177e4SLinus Torvalds { 3497b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 3501da177e4SLinus Torvalds struct xfrm_state *x; 3511da177e4SLinus Torvalds int err; 35226b15dadSJamal Hadi Salim struct km_event c; 3531da177e4SLinus Torvalds 35435a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 3551da177e4SLinus Torvalds if (err) 3561da177e4SLinus Torvalds return err; 3571da177e4SLinus Torvalds 35835a7aa08SThomas Graf x = xfrm_state_construct(p, attrs, &err); 3591da177e4SLinus Torvalds if (!x) 3601da177e4SLinus Torvalds return err; 3611da177e4SLinus Torvalds 36226b15dadSJamal Hadi Salim xfrm_state_hold(x); 3631da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 3641da177e4SLinus Torvalds err = xfrm_state_add(x); 3651da177e4SLinus Torvalds else 3661da177e4SLinus Torvalds err = xfrm_state_update(x); 3671da177e4SLinus Torvalds 368ab5f5e8bSJoy Latten xfrm_audit_state_add(x, err ? 0 : 1, NETLINK_CB(skb).loginuid, 369ab5f5e8bSJoy Latten NETLINK_CB(skb).sid); 370161a09e7SJoy Latten 3711da177e4SLinus Torvalds if (err < 0) { 3721da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 37321380b81SHerbert Xu __xfrm_state_put(x); 3747d6dfe1fSPatrick McHardy goto out; 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 37726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 37826b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 379f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 38026b15dadSJamal Hadi Salim 38126b15dadSJamal Hadi Salim km_state_notify(x, &c); 3827d6dfe1fSPatrick McHardy out: 38326b15dadSJamal Hadi Salim xfrm_state_put(x); 3841da177e4SLinus Torvalds return err; 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds 387eb2971b6SMasahide NAKAMURA static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p, 3885424f32eSThomas Graf struct nlattr **attrs, 389eb2971b6SMasahide NAKAMURA int *errp) 390eb2971b6SMasahide NAKAMURA { 391eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 392eb2971b6SMasahide NAKAMURA int err; 393eb2971b6SMasahide NAKAMURA 394eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 395eb2971b6SMasahide NAKAMURA err = -ESRCH; 396eb2971b6SMasahide NAKAMURA x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family); 397eb2971b6SMasahide NAKAMURA } else { 398eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 399eb2971b6SMasahide NAKAMURA 40035a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 401eb2971b6SMasahide NAKAMURA if (!saddr) { 402eb2971b6SMasahide NAKAMURA err = -EINVAL; 403eb2971b6SMasahide NAKAMURA goto out; 404eb2971b6SMasahide NAKAMURA } 405eb2971b6SMasahide NAKAMURA 4069abbffeeSMasahide NAKAMURA err = -ESRCH; 407eb2971b6SMasahide NAKAMURA x = xfrm_state_lookup_byaddr(&p->daddr, saddr, p->proto, 408eb2971b6SMasahide NAKAMURA p->family); 409eb2971b6SMasahide NAKAMURA } 410eb2971b6SMasahide NAKAMURA 411eb2971b6SMasahide NAKAMURA out: 412eb2971b6SMasahide NAKAMURA if (!x && errp) 413eb2971b6SMasahide NAKAMURA *errp = err; 414eb2971b6SMasahide NAKAMURA return x; 415eb2971b6SMasahide NAKAMURA } 416eb2971b6SMasahide NAKAMURA 41722e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 4185424f32eSThomas Graf struct nlattr **attrs) 4191da177e4SLinus Torvalds { 4201da177e4SLinus Torvalds struct xfrm_state *x; 421eb2971b6SMasahide NAKAMURA int err = -ESRCH; 42226b15dadSJamal Hadi Salim struct km_event c; 4237b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 4241da177e4SLinus Torvalds 42535a7aa08SThomas Graf x = xfrm_user_state_lookup(p, attrs, &err); 4261da177e4SLinus Torvalds if (x == NULL) 427eb2971b6SMasahide NAKAMURA return err; 4281da177e4SLinus Torvalds 4296f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 430c8c05a8eSCatherine Zhang goto out; 431c8c05a8eSCatherine Zhang 4321da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 433c8c05a8eSCatherine Zhang err = -EPERM; 434c8c05a8eSCatherine Zhang goto out; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds 43726b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 438161a09e7SJoy Latten 439c8c05a8eSCatherine Zhang if (err < 0) 440c8c05a8eSCatherine Zhang goto out; 44126b15dadSJamal Hadi Salim 44226b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 44326b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 444f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 44526b15dadSJamal Hadi Salim km_state_notify(x, &c); 4461da177e4SLinus Torvalds 447c8c05a8eSCatherine Zhang out: 448ab5f5e8bSJoy Latten xfrm_audit_state_delete(x, err ? 0 : 1, NETLINK_CB(skb).loginuid, 449ab5f5e8bSJoy Latten NETLINK_CB(skb).sid); 450c8c05a8eSCatherine Zhang xfrm_state_put(x); 45126b15dadSJamal Hadi Salim return err; 4521da177e4SLinus Torvalds } 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 4551da177e4SLinus Torvalds { 4561da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 4571da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 4581da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 4591da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 4601da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 46154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 4621da177e4SLinus Torvalds p->mode = x->props.mode; 4631da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 4641da177e4SLinus Torvalds p->reqid = x->props.reqid; 4651da177e4SLinus Torvalds p->family = x->props.family; 4661da177e4SLinus Torvalds p->flags = x->props.flags; 4671da177e4SLinus Torvalds p->seq = x->km.seq; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds struct xfrm_dump_info { 4711da177e4SLinus Torvalds struct sk_buff *in_skb; 4721da177e4SLinus Torvalds struct sk_buff *out_skb; 4731da177e4SLinus Torvalds u32 nlmsg_seq; 4741da177e4SLinus Torvalds u16 nlmsg_flags; 4751da177e4SLinus Torvalds int start_idx; 4761da177e4SLinus Torvalds int this_idx; 4771da177e4SLinus Torvalds }; 4781da177e4SLinus Torvalds 479c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 480c0144beaSThomas Graf { 481c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 482c0144beaSThomas Graf struct nlattr *attr; 48368325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 484c0144beaSThomas Graf 485c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 486c0144beaSThomas Graf if (attr == NULL) 487c0144beaSThomas Graf return -EMSGSIZE; 488c0144beaSThomas Graf 489c0144beaSThomas Graf uctx = nla_data(attr); 490c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 491c0144beaSThomas Graf uctx->len = ctx_size; 492c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 493c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 494c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 495c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 496c0144beaSThomas Graf 497c0144beaSThomas Graf return 0; 498c0144beaSThomas Graf } 499c0144beaSThomas Graf 50068325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 50168325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 50268325d3bSHerbert Xu struct xfrm_usersa_info *p, 50368325d3bSHerbert Xu struct sk_buff *skb) 5041da177e4SLinus Torvalds { 5051da177e4SLinus Torvalds copy_to_user_state(x, p); 5061da177e4SLinus Torvalds 507050f009eSHerbert Xu if (x->coaddr) 508050f009eSHerbert Xu NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 509050f009eSHerbert Xu 510050f009eSHerbert Xu if (x->lastused) 511050f009eSHerbert Xu NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); 512050f009eSHerbert Xu 5131da177e4SLinus Torvalds if (x->aalg) 5140f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_AUTH, xfrm_alg_len(x->aalg), x->aalg); 5151da177e4SLinus Torvalds if (x->ealg) 5160f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 5171da177e4SLinus Torvalds if (x->calg) 518c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds if (x->encap) 521c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 5221da177e4SLinus Torvalds 523c0144beaSThomas Graf if (x->security && copy_sec_ctx(x->security, skb) < 0) 524c0144beaSThomas Graf goto nla_put_failure; 525060f02a3SNoriaki TAKAMIYA 52668325d3bSHerbert Xu return 0; 52768325d3bSHerbert Xu 52868325d3bSHerbert Xu nla_put_failure: 52968325d3bSHerbert Xu return -EMSGSIZE; 53068325d3bSHerbert Xu } 53168325d3bSHerbert Xu 53268325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 53368325d3bSHerbert Xu { 53468325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 53568325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 53668325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 53768325d3bSHerbert Xu struct xfrm_usersa_info *p; 53868325d3bSHerbert Xu struct nlmsghdr *nlh; 53968325d3bSHerbert Xu int err; 54068325d3bSHerbert Xu 54168325d3bSHerbert Xu if (sp->this_idx < sp->start_idx) 54268325d3bSHerbert Xu goto out; 54368325d3bSHerbert Xu 54468325d3bSHerbert Xu nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 54568325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 54668325d3bSHerbert Xu if (nlh == NULL) 54768325d3bSHerbert Xu return -EMSGSIZE; 54868325d3bSHerbert Xu 54968325d3bSHerbert Xu p = nlmsg_data(nlh); 55068325d3bSHerbert Xu 55168325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 55268325d3bSHerbert Xu if (err) 55368325d3bSHerbert Xu goto nla_put_failure; 55468325d3bSHerbert Xu 5559825069dSThomas Graf nlmsg_end(skb, nlh); 5561da177e4SLinus Torvalds out: 5571da177e4SLinus Torvalds sp->this_idx++; 5581da177e4SLinus Torvalds return 0; 5591da177e4SLinus Torvalds 560c0144beaSThomas Graf nla_put_failure: 5619825069dSThomas Graf nlmsg_cancel(skb, nlh); 56268325d3bSHerbert Xu return err; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 5661da177e4SLinus Torvalds { 5671da177e4SLinus Torvalds struct xfrm_dump_info info; 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds info.in_skb = cb->skb; 5701da177e4SLinus Torvalds info.out_skb = skb; 5711da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 5721da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 5731da177e4SLinus Torvalds info.this_idx = 0; 5741da177e4SLinus Torvalds info.start_idx = cb->args[0]; 575dc00a525SMasahide NAKAMURA (void) xfrm_state_walk(0, dump_one_state, &info); 5761da177e4SLinus Torvalds cb->args[0] = info.this_idx; 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds return skb->len; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 5821da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 5831da177e4SLinus Torvalds { 5841da177e4SLinus Torvalds struct xfrm_dump_info info; 5851da177e4SLinus Torvalds struct sk_buff *skb; 5861da177e4SLinus Torvalds 5877deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5881da177e4SLinus Torvalds if (!skb) 5891da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds info.in_skb = in_skb; 5921da177e4SLinus Torvalds info.out_skb = skb; 5931da177e4SLinus Torvalds info.nlmsg_seq = seq; 5941da177e4SLinus Torvalds info.nlmsg_flags = 0; 5951da177e4SLinus Torvalds info.this_idx = info.start_idx = 0; 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds if (dump_one_state(x, 0, &info)) { 5981da177e4SLinus Torvalds kfree_skb(skb); 5991da177e4SLinus Torvalds return NULL; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds return skb; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds 6057deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 6067deb2264SThomas Graf { 6077deb2264SThomas Graf return NLMSG_ALIGN(4) 6087deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 6097deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 6107deb2264SThomas Graf } 6117deb2264SThomas Graf 612ecfd6b18SJamal Hadi Salim static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) 613ecfd6b18SJamal Hadi Salim { 6145a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 6155a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 6165a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 617ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 618ecfd6b18SJamal Hadi Salim u32 *f; 619ecfd6b18SJamal Hadi Salim 620ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 621ecfd6b18SJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 622ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 623ecfd6b18SJamal Hadi Salim 624ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 625ecfd6b18SJamal Hadi Salim *f = flags; 626ecfd6b18SJamal Hadi Salim xfrm_spd_getinfo(&si); 6275a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 6285a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 6295a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 6305a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 6315a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 6325a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 6335a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 6345a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 635ecfd6b18SJamal Hadi Salim 6365a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 6375a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 638ecfd6b18SJamal Hadi Salim 639ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 640ecfd6b18SJamal Hadi Salim 641ecfd6b18SJamal Hadi Salim nla_put_failure: 642ecfd6b18SJamal Hadi Salim nlmsg_cancel(skb, nlh); 643ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 644ecfd6b18SJamal Hadi Salim } 645ecfd6b18SJamal Hadi Salim 646ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 6475424f32eSThomas Graf struct nlattr **attrs) 648ecfd6b18SJamal Hadi Salim { 649ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 6507b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 651ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 652ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 653ecfd6b18SJamal Hadi Salim 6547deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 655ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 656ecfd6b18SJamal Hadi Salim return -ENOMEM; 657ecfd6b18SJamal Hadi Salim 658ecfd6b18SJamal Hadi Salim if (build_spdinfo(r_skb, spid, seq, *flags) < 0) 659ecfd6b18SJamal Hadi Salim BUG(); 660ecfd6b18SJamal Hadi Salim 661ecfd6b18SJamal Hadi Salim return nlmsg_unicast(xfrm_nl, r_skb, spid); 662ecfd6b18SJamal Hadi Salim } 663ecfd6b18SJamal Hadi Salim 6647deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 6657deb2264SThomas Graf { 6667deb2264SThomas Graf return NLMSG_ALIGN(4) 6677deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 6687deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 6697deb2264SThomas Graf } 6707deb2264SThomas Graf 67128d8909bSJamal Hadi Salim static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) 67228d8909bSJamal Hadi Salim { 673af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 674af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 67528d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 67628d8909bSJamal Hadi Salim u32 *f; 67728d8909bSJamal Hadi Salim 67828d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 67928d8909bSJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 68028d8909bSJamal Hadi Salim return -EMSGSIZE; 68128d8909bSJamal Hadi Salim 68228d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 68328d8909bSJamal Hadi Salim *f = flags; 68428d8909bSJamal Hadi Salim xfrm_sad_getinfo(&si); 68528d8909bSJamal Hadi Salim 686af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 687af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 688af11e316SJamal Hadi Salim 689af11e316SJamal Hadi Salim NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); 690af11e316SJamal Hadi Salim NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 69128d8909bSJamal Hadi Salim 69228d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 69328d8909bSJamal Hadi Salim 69428d8909bSJamal Hadi Salim nla_put_failure: 69528d8909bSJamal Hadi Salim nlmsg_cancel(skb, nlh); 69628d8909bSJamal Hadi Salim return -EMSGSIZE; 69728d8909bSJamal Hadi Salim } 69828d8909bSJamal Hadi Salim 69928d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 7005424f32eSThomas Graf struct nlattr **attrs) 70128d8909bSJamal Hadi Salim { 70228d8909bSJamal Hadi Salim struct sk_buff *r_skb; 7037b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 70428d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 70528d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 70628d8909bSJamal Hadi Salim 7077deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 70828d8909bSJamal Hadi Salim if (r_skb == NULL) 70928d8909bSJamal Hadi Salim return -ENOMEM; 71028d8909bSJamal Hadi Salim 71128d8909bSJamal Hadi Salim if (build_sadinfo(r_skb, spid, seq, *flags) < 0) 71228d8909bSJamal Hadi Salim BUG(); 71328d8909bSJamal Hadi Salim 71428d8909bSJamal Hadi Salim return nlmsg_unicast(xfrm_nl, r_skb, spid); 71528d8909bSJamal Hadi Salim } 71628d8909bSJamal Hadi Salim 71722e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 7185424f32eSThomas Graf struct nlattr **attrs) 7191da177e4SLinus Torvalds { 7207b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 7211da177e4SLinus Torvalds struct xfrm_state *x; 7221da177e4SLinus Torvalds struct sk_buff *resp_skb; 723eb2971b6SMasahide NAKAMURA int err = -ESRCH; 7241da177e4SLinus Torvalds 72535a7aa08SThomas Graf x = xfrm_user_state_lookup(p, attrs, &err); 7261da177e4SLinus Torvalds if (x == NULL) 7271da177e4SLinus Torvalds goto out_noput; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 7301da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 7311da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 7321da177e4SLinus Torvalds } else { 733082a1ad5SThomas Graf err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid); 7341da177e4SLinus Torvalds } 7351da177e4SLinus Torvalds xfrm_state_put(x); 7361da177e4SLinus Torvalds out_noput: 7371da177e4SLinus Torvalds return err; 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 7411da177e4SLinus Torvalds { 7421da177e4SLinus Torvalds switch (p->info.id.proto) { 7431da177e4SLinus Torvalds case IPPROTO_AH: 7441da177e4SLinus Torvalds case IPPROTO_ESP: 7451da177e4SLinus Torvalds break; 7461da177e4SLinus Torvalds 7471da177e4SLinus Torvalds case IPPROTO_COMP: 7481da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 7491da177e4SLinus Torvalds if (p->max >= 0x10000) 7501da177e4SLinus Torvalds return -EINVAL; 7511da177e4SLinus Torvalds break; 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds default: 7541da177e4SLinus Torvalds return -EINVAL; 7553ff50b79SStephen Hemminger } 7561da177e4SLinus Torvalds 7571da177e4SLinus Torvalds if (p->min > p->max) 7581da177e4SLinus Torvalds return -EINVAL; 7591da177e4SLinus Torvalds 7601da177e4SLinus Torvalds return 0; 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds 76322e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 7645424f32eSThomas Graf struct nlattr **attrs) 7651da177e4SLinus Torvalds { 7661da177e4SLinus Torvalds struct xfrm_state *x; 7671da177e4SLinus Torvalds struct xfrm_userspi_info *p; 7681da177e4SLinus Torvalds struct sk_buff *resp_skb; 7691da177e4SLinus Torvalds xfrm_address_t *daddr; 7701da177e4SLinus Torvalds int family; 7711da177e4SLinus Torvalds int err; 7721da177e4SLinus Torvalds 7737b67c857SThomas Graf p = nlmsg_data(nlh); 7741da177e4SLinus Torvalds err = verify_userspi_info(p); 7751da177e4SLinus Torvalds if (err) 7761da177e4SLinus Torvalds goto out_noput; 7771da177e4SLinus Torvalds 7781da177e4SLinus Torvalds family = p->info.family; 7791da177e4SLinus Torvalds daddr = &p->info.id.daddr; 7801da177e4SLinus Torvalds 7811da177e4SLinus Torvalds x = NULL; 7821da177e4SLinus Torvalds if (p->info.seq) { 7831da177e4SLinus Torvalds x = xfrm_find_acq_byseq(p->info.seq); 7841da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 7851da177e4SLinus Torvalds xfrm_state_put(x); 7861da177e4SLinus Torvalds x = NULL; 7871da177e4SLinus Torvalds } 7881da177e4SLinus Torvalds } 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds if (!x) 7911da177e4SLinus Torvalds x = xfrm_find_acq(p->info.mode, p->info.reqid, 7921da177e4SLinus Torvalds p->info.id.proto, daddr, 7931da177e4SLinus Torvalds &p->info.saddr, 1, 7941da177e4SLinus Torvalds family); 7951da177e4SLinus Torvalds err = -ENOENT; 7961da177e4SLinus Torvalds if (x == NULL) 7971da177e4SLinus Torvalds goto out_noput; 7981da177e4SLinus Torvalds 799658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 800658b219eSHerbert Xu if (err) 801658b219eSHerbert Xu goto out; 8021da177e4SLinus Torvalds 8031da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 8041da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 8051da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 8061da177e4SLinus Torvalds goto out; 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds 809082a1ad5SThomas Graf err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid); 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds out: 8121da177e4SLinus Torvalds xfrm_state_put(x); 8131da177e4SLinus Torvalds out_noput: 8141da177e4SLinus Torvalds return err; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds 817b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 8181da177e4SLinus Torvalds { 8191da177e4SLinus Torvalds switch (dir) { 8201da177e4SLinus Torvalds case XFRM_POLICY_IN: 8211da177e4SLinus Torvalds case XFRM_POLICY_OUT: 8221da177e4SLinus Torvalds case XFRM_POLICY_FWD: 8231da177e4SLinus Torvalds break; 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds default: 8261da177e4SLinus Torvalds return -EINVAL; 8273ff50b79SStephen Hemminger } 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds return 0; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds 832b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 833f7b6983fSMasahide NAKAMURA { 834f7b6983fSMasahide NAKAMURA switch (type) { 835f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 836f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 837f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 838f7b6983fSMasahide NAKAMURA #endif 839f7b6983fSMasahide NAKAMURA break; 840f7b6983fSMasahide NAKAMURA 841f7b6983fSMasahide NAKAMURA default: 842f7b6983fSMasahide NAKAMURA return -EINVAL; 8433ff50b79SStephen Hemminger } 844f7b6983fSMasahide NAKAMURA 845f7b6983fSMasahide NAKAMURA return 0; 846f7b6983fSMasahide NAKAMURA } 847f7b6983fSMasahide NAKAMURA 8481da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 8491da177e4SLinus Torvalds { 8501da177e4SLinus Torvalds switch (p->share) { 8511da177e4SLinus Torvalds case XFRM_SHARE_ANY: 8521da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 8531da177e4SLinus Torvalds case XFRM_SHARE_USER: 8541da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 8551da177e4SLinus Torvalds break; 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds default: 8581da177e4SLinus Torvalds return -EINVAL; 8593ff50b79SStephen Hemminger } 8601da177e4SLinus Torvalds 8611da177e4SLinus Torvalds switch (p->action) { 8621da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 8631da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 8641da177e4SLinus Torvalds break; 8651da177e4SLinus Torvalds 8661da177e4SLinus Torvalds default: 8671da177e4SLinus Torvalds return -EINVAL; 8683ff50b79SStephen Hemminger } 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds switch (p->sel.family) { 8711da177e4SLinus Torvalds case AF_INET: 8721da177e4SLinus Torvalds break; 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds case AF_INET6: 8751da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 8761da177e4SLinus Torvalds break; 8771da177e4SLinus Torvalds #else 8781da177e4SLinus Torvalds return -EAFNOSUPPORT; 8791da177e4SLinus Torvalds #endif 8801da177e4SLinus Torvalds 8811da177e4SLinus Torvalds default: 8821da177e4SLinus Torvalds return -EINVAL; 8833ff50b79SStephen Hemminger } 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds return verify_policy_dir(p->dir); 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds 8885424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 889df71837dSTrent Jaeger { 8905424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 891df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 892df71837dSTrent Jaeger 893df71837dSTrent Jaeger if (!rt) 894df71837dSTrent Jaeger return 0; 895df71837dSTrent Jaeger 8965424f32eSThomas Graf uctx = nla_data(rt); 897df71837dSTrent Jaeger return security_xfrm_policy_alloc(pol, uctx); 898df71837dSTrent Jaeger } 899df71837dSTrent Jaeger 9001da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 9011da177e4SLinus Torvalds int nr) 9021da177e4SLinus Torvalds { 9031da177e4SLinus Torvalds int i; 9041da177e4SLinus Torvalds 9051da177e4SLinus Torvalds xp->xfrm_nr = nr; 9061da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 9071da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 9101da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 9111da177e4SLinus Torvalds sizeof(xfrm_address_t)); 9121da177e4SLinus Torvalds t->reqid = ut->reqid; 9131da177e4SLinus Torvalds t->mode = ut->mode; 9141da177e4SLinus Torvalds t->share = ut->share; 9151da177e4SLinus Torvalds t->optional = ut->optional; 9161da177e4SLinus Torvalds t->aalgos = ut->aalgos; 9171da177e4SLinus Torvalds t->ealgos = ut->ealgos; 9181da177e4SLinus Torvalds t->calgos = ut->calgos; 9198511d01dSMiika Komu t->encap_family = ut->family; 9201da177e4SLinus Torvalds } 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 923b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 924b4ad86bfSDavid S. Miller { 925b4ad86bfSDavid S. Miller int i; 926b4ad86bfSDavid S. Miller 927b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 928b4ad86bfSDavid S. Miller return -EINVAL; 929b4ad86bfSDavid S. Miller 930b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 931b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 932b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 933b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 934b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 935b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 936b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 937b4ad86bfSDavid S. Miller */ 938b4ad86bfSDavid S. Miller if (!ut[i].family) 939b4ad86bfSDavid S. Miller ut[i].family = family; 940b4ad86bfSDavid S. Miller 941b4ad86bfSDavid S. Miller switch (ut[i].family) { 942b4ad86bfSDavid S. Miller case AF_INET: 943b4ad86bfSDavid S. Miller break; 944b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 945b4ad86bfSDavid S. Miller case AF_INET6: 946b4ad86bfSDavid S. Miller break; 947b4ad86bfSDavid S. Miller #endif 948b4ad86bfSDavid S. Miller default: 949b4ad86bfSDavid S. Miller return -EINVAL; 9503ff50b79SStephen Hemminger } 951b4ad86bfSDavid S. Miller } 952b4ad86bfSDavid S. Miller 953b4ad86bfSDavid S. Miller return 0; 954b4ad86bfSDavid S. Miller } 955b4ad86bfSDavid S. Miller 9565424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 9571da177e4SLinus Torvalds { 9585424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds if (!rt) { 9611da177e4SLinus Torvalds pol->xfrm_nr = 0; 9621da177e4SLinus Torvalds } else { 9635424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 9645424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 965b4ad86bfSDavid S. Miller int err; 9661da177e4SLinus Torvalds 967b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 968b4ad86bfSDavid S. Miller if (err) 969b4ad86bfSDavid S. Miller return err; 9701da177e4SLinus Torvalds 9715424f32eSThomas Graf copy_templates(pol, utmpl, nr); 9721da177e4SLinus Torvalds } 9731da177e4SLinus Torvalds return 0; 9741da177e4SLinus Torvalds } 9751da177e4SLinus Torvalds 9765424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 977f7b6983fSMasahide NAKAMURA { 9785424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 979f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 980b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 981f7b6983fSMasahide NAKAMURA int err; 982f7b6983fSMasahide NAKAMURA 983f7b6983fSMasahide NAKAMURA if (rt) { 9845424f32eSThomas Graf upt = nla_data(rt); 985f7b6983fSMasahide NAKAMURA type = upt->type; 986f7b6983fSMasahide NAKAMURA } 987f7b6983fSMasahide NAKAMURA 988f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 989f7b6983fSMasahide NAKAMURA if (err) 990f7b6983fSMasahide NAKAMURA return err; 991f7b6983fSMasahide NAKAMURA 992f7b6983fSMasahide NAKAMURA *tp = type; 993f7b6983fSMasahide NAKAMURA return 0; 994f7b6983fSMasahide NAKAMURA } 995f7b6983fSMasahide NAKAMURA 9961da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 9971da177e4SLinus Torvalds { 9981da177e4SLinus Torvalds xp->priority = p->priority; 9991da177e4SLinus Torvalds xp->index = p->index; 10001da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 10011da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 10021da177e4SLinus Torvalds xp->action = p->action; 10031da177e4SLinus Torvalds xp->flags = p->flags; 10041da177e4SLinus Torvalds xp->family = p->sel.family; 10051da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 10091da177e4SLinus Torvalds { 10101da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 10111da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 10121da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 10131da177e4SLinus Torvalds p->priority = xp->priority; 10141da177e4SLinus Torvalds p->index = xp->index; 10151da177e4SLinus Torvalds p->sel.family = xp->family; 10161da177e4SLinus Torvalds p->dir = dir; 10171da177e4SLinus Torvalds p->action = xp->action; 10181da177e4SLinus Torvalds p->flags = xp->flags; 10191da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 10201da177e4SLinus Torvalds } 10211da177e4SLinus Torvalds 10225424f32eSThomas Graf static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 10231da177e4SLinus Torvalds { 10241da177e4SLinus Torvalds struct xfrm_policy *xp = xfrm_policy_alloc(GFP_KERNEL); 10251da177e4SLinus Torvalds int err; 10261da177e4SLinus Torvalds 10271da177e4SLinus Torvalds if (!xp) { 10281da177e4SLinus Torvalds *errp = -ENOMEM; 10291da177e4SLinus Torvalds return NULL; 10301da177e4SLinus Torvalds } 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1033df71837dSTrent Jaeger 103435a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1035f7b6983fSMasahide NAKAMURA if (err) 1036f7b6983fSMasahide NAKAMURA goto error; 1037f7b6983fSMasahide NAKAMURA 103835a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 103935a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1040f7b6983fSMasahide NAKAMURA if (err) 1041f7b6983fSMasahide NAKAMURA goto error; 10421da177e4SLinus Torvalds 10431da177e4SLinus Torvalds return xp; 1044f7b6983fSMasahide NAKAMURA error: 1045f7b6983fSMasahide NAKAMURA *errp = err; 1046f7b6983fSMasahide NAKAMURA kfree(xp); 1047f7b6983fSMasahide NAKAMURA return NULL; 10481da177e4SLinus Torvalds } 10491da177e4SLinus Torvalds 105022e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 10515424f32eSThomas Graf struct nlattr **attrs) 10521da177e4SLinus Torvalds { 10537b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 10541da177e4SLinus Torvalds struct xfrm_policy *xp; 105526b15dadSJamal Hadi Salim struct km_event c; 10561da177e4SLinus Torvalds int err; 10571da177e4SLinus Torvalds int excl; 10581da177e4SLinus Torvalds 10591da177e4SLinus Torvalds err = verify_newpolicy_info(p); 10601da177e4SLinus Torvalds if (err) 10611da177e4SLinus Torvalds return err; 106235a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1063df71837dSTrent Jaeger if (err) 1064df71837dSTrent Jaeger return err; 10651da177e4SLinus Torvalds 106635a7aa08SThomas Graf xp = xfrm_policy_construct(p, attrs, &err); 10671da177e4SLinus Torvalds if (!xp) 10681da177e4SLinus Torvalds return err; 10691da177e4SLinus Torvalds 107026b15dadSJamal Hadi Salim /* shouldnt excl be based on nlh flags?? 107126b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 107226b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 107326b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 10741da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 10751da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 1076ab5f5e8bSJoy Latten xfrm_audit_policy_add(xp, err ? 0 : 1, NETLINK_CB(skb).loginuid, 1077ab5f5e8bSJoy Latten NETLINK_CB(skb).sid); 1078161a09e7SJoy Latten 10791da177e4SLinus Torvalds if (err) { 10805f8ac64bSTrent Jaeger security_xfrm_policy_free(xp); 10811da177e4SLinus Torvalds kfree(xp); 10821da177e4SLinus Torvalds return err; 10831da177e4SLinus Torvalds } 10841da177e4SLinus Torvalds 1085f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 108626b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 108726b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 108826b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 108926b15dadSJamal Hadi Salim 10901da177e4SLinus Torvalds xfrm_pol_put(xp); 10911da177e4SLinus Torvalds 10921da177e4SLinus Torvalds return 0; 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 10961da177e4SLinus Torvalds { 10971da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 10981da177e4SLinus Torvalds int i; 10991da177e4SLinus Torvalds 11001da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 11011da177e4SLinus Torvalds return 0; 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 11041da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 11051da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 11088511d01dSMiika Komu up->family = kp->encap_family; 11091da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 11101da177e4SLinus Torvalds up->reqid = kp->reqid; 11111da177e4SLinus Torvalds up->mode = kp->mode; 11121da177e4SLinus Torvalds up->share = kp->share; 11131da177e4SLinus Torvalds up->optional = kp->optional; 11141da177e4SLinus Torvalds up->aalgos = kp->aalgos; 11151da177e4SLinus Torvalds up->ealgos = kp->ealgos; 11161da177e4SLinus Torvalds up->calgos = kp->calgos; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds 1119c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1120c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1121df71837dSTrent Jaeger } 1122df71837dSTrent Jaeger 11230d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 11240d681623SSerge Hallyn { 11250d681623SSerge Hallyn if (x->security) { 11260d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 11270d681623SSerge Hallyn } 11280d681623SSerge Hallyn return 0; 11290d681623SSerge Hallyn } 11300d681623SSerge Hallyn 11310d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 11320d681623SSerge Hallyn { 11330d681623SSerge Hallyn if (xp->security) { 11340d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 11350d681623SSerge Hallyn } 11360d681623SSerge Hallyn return 0; 11370d681623SSerge Hallyn } 1138cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1139cfbfd45aSThomas Graf { 1140cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1141cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1142cfbfd45aSThomas Graf #else 1143cfbfd45aSThomas Graf return 0; 1144cfbfd45aSThomas Graf #endif 1145cfbfd45aSThomas Graf } 11460d681623SSerge Hallyn 1147f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1148b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1149f7b6983fSMasahide NAKAMURA { 1150c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1151c0144beaSThomas Graf .type = type, 1152c0144beaSThomas Graf }; 1153f7b6983fSMasahide NAKAMURA 1154c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1155f7b6983fSMasahide NAKAMURA } 1156f7b6983fSMasahide NAKAMURA 1157f7b6983fSMasahide NAKAMURA #else 1158b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1159f7b6983fSMasahide NAKAMURA { 1160f7b6983fSMasahide NAKAMURA return 0; 1161f7b6983fSMasahide NAKAMURA } 1162f7b6983fSMasahide NAKAMURA #endif 1163f7b6983fSMasahide NAKAMURA 11641da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 11651da177e4SLinus Torvalds { 11661da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 11671da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 11681da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 11691da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 11701da177e4SLinus Torvalds struct nlmsghdr *nlh; 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds if (sp->this_idx < sp->start_idx) 11731da177e4SLinus Torvalds goto out; 11741da177e4SLinus Torvalds 117579b8b7f4SThomas Graf nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 117679b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 117779b8b7f4SThomas Graf if (nlh == NULL) 117879b8b7f4SThomas Graf return -EMSGSIZE; 11791da177e4SLinus Torvalds 11807b67c857SThomas Graf p = nlmsg_data(nlh); 11811da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 11821da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 11831da177e4SLinus Torvalds goto nlmsg_failure; 1184df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 1185df71837dSTrent Jaeger goto nlmsg_failure; 11861459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 1187f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 11881da177e4SLinus Torvalds 11899825069dSThomas Graf nlmsg_end(skb, nlh); 11901da177e4SLinus Torvalds out: 11911da177e4SLinus Torvalds sp->this_idx++; 11921da177e4SLinus Torvalds return 0; 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds nlmsg_failure: 11959825069dSThomas Graf nlmsg_cancel(skb, nlh); 11969825069dSThomas Graf return -EMSGSIZE; 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 12001da177e4SLinus Torvalds { 12011da177e4SLinus Torvalds struct xfrm_dump_info info; 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds info.in_skb = cb->skb; 12041da177e4SLinus Torvalds info.out_skb = skb; 12051da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 12061da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 12071da177e4SLinus Torvalds info.this_idx = 0; 12081da177e4SLinus Torvalds info.start_idx = cb->args[0]; 1209f7b6983fSMasahide NAKAMURA (void) xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, dump_one_policy, &info); 1210f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1211f7b6983fSMasahide NAKAMURA (void) xfrm_policy_walk(XFRM_POLICY_TYPE_SUB, dump_one_policy, &info); 1212f7b6983fSMasahide NAKAMURA #endif 12131da177e4SLinus Torvalds cb->args[0] = info.this_idx; 12141da177e4SLinus Torvalds 12151da177e4SLinus Torvalds return skb->len; 12161da177e4SLinus Torvalds } 12171da177e4SLinus Torvalds 12181da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 12191da177e4SLinus Torvalds struct xfrm_policy *xp, 12201da177e4SLinus Torvalds int dir, u32 seq) 12211da177e4SLinus Torvalds { 12221da177e4SLinus Torvalds struct xfrm_dump_info info; 12231da177e4SLinus Torvalds struct sk_buff *skb; 12241da177e4SLinus Torvalds 12257deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 12261da177e4SLinus Torvalds if (!skb) 12271da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds info.in_skb = in_skb; 12301da177e4SLinus Torvalds info.out_skb = skb; 12311da177e4SLinus Torvalds info.nlmsg_seq = seq; 12321da177e4SLinus Torvalds info.nlmsg_flags = 0; 12331da177e4SLinus Torvalds info.this_idx = info.start_idx = 0; 12341da177e4SLinus Torvalds 12351da177e4SLinus Torvalds if (dump_one_policy(xp, dir, 0, &info) < 0) { 12361da177e4SLinus Torvalds kfree_skb(skb); 12371da177e4SLinus Torvalds return NULL; 12381da177e4SLinus Torvalds } 12391da177e4SLinus Torvalds 12401da177e4SLinus Torvalds return skb; 12411da177e4SLinus Torvalds } 12421da177e4SLinus Torvalds 124322e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 12445424f32eSThomas Graf struct nlattr **attrs) 12451da177e4SLinus Torvalds { 12461da177e4SLinus Torvalds struct xfrm_policy *xp; 12471da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1248b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 12491da177e4SLinus Torvalds int err; 125026b15dadSJamal Hadi Salim struct km_event c; 12511da177e4SLinus Torvalds int delete; 12521da177e4SLinus Torvalds 12537b67c857SThomas Graf p = nlmsg_data(nlh); 12541da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 12551da177e4SLinus Torvalds 125635a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1257f7b6983fSMasahide NAKAMURA if (err) 1258f7b6983fSMasahide NAKAMURA return err; 1259f7b6983fSMasahide NAKAMURA 12601da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 12611da177e4SLinus Torvalds if (err) 12621da177e4SLinus Torvalds return err; 12631da177e4SLinus Torvalds 12641da177e4SLinus Torvalds if (p->index) 1265ef41aaa0SEric Paris xp = xfrm_policy_byid(type, p->dir, p->index, delete, &err); 1266df71837dSTrent Jaeger else { 12675424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1268df71837dSTrent Jaeger struct xfrm_policy tmp; 1269df71837dSTrent Jaeger 127035a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1271df71837dSTrent Jaeger if (err) 1272df71837dSTrent Jaeger return err; 1273df71837dSTrent Jaeger 1274df71837dSTrent Jaeger memset(&tmp, 0, sizeof(struct xfrm_policy)); 1275df71837dSTrent Jaeger if (rt) { 12765424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1277df71837dSTrent Jaeger 1278df71837dSTrent Jaeger if ((err = security_xfrm_policy_alloc(&tmp, uctx))) 1279df71837dSTrent Jaeger return err; 1280df71837dSTrent Jaeger } 1281ef41aaa0SEric Paris xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, tmp.security, 1282ef41aaa0SEric Paris delete, &err); 1283df71837dSTrent Jaeger security_xfrm_policy_free(&tmp); 1284df71837dSTrent Jaeger } 12851da177e4SLinus Torvalds if (xp == NULL) 12861da177e4SLinus Torvalds return -ENOENT; 12871da177e4SLinus Torvalds 12881da177e4SLinus Torvalds if (!delete) { 12891da177e4SLinus Torvalds struct sk_buff *resp_skb; 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 12921da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 12931da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 12941da177e4SLinus Torvalds } else { 1295082a1ad5SThomas Graf err = nlmsg_unicast(xfrm_nl, resp_skb, 1296082a1ad5SThomas Graf NETLINK_CB(skb).pid); 12971da177e4SLinus Torvalds } 129826b15dadSJamal Hadi Salim } else { 1299ab5f5e8bSJoy Latten xfrm_audit_policy_delete(xp, err ? 0 : 1, 1300ab5f5e8bSJoy Latten NETLINK_CB(skb).loginuid, 1301ab5f5e8bSJoy Latten NETLINK_CB(skb).sid); 130213fcfbb0SDavid S. Miller 130313fcfbb0SDavid S. Miller if (err != 0) 1304c8c05a8eSCatherine Zhang goto out; 130513fcfbb0SDavid S. Miller 1306e7443892SHerbert Xu c.data.byid = p->index; 1307f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 130826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 130926b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 131026b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 13111da177e4SLinus Torvalds } 13121da177e4SLinus Torvalds 1313c8c05a8eSCatherine Zhang out: 1314ef41aaa0SEric Paris xfrm_pol_put(xp); 13151da177e4SLinus Torvalds return err; 13161da177e4SLinus Torvalds } 13171da177e4SLinus Torvalds 131822e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 13195424f32eSThomas Graf struct nlattr **attrs) 13201da177e4SLinus Torvalds { 132126b15dadSJamal Hadi Salim struct km_event c; 13227b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1323161a09e7SJoy Latten struct xfrm_audit audit_info; 13244aa2e62cSJoy Latten int err; 13251da177e4SLinus Torvalds 1326161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 1327161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 13284aa2e62cSJoy Latten err = xfrm_state_flush(p->proto, &audit_info); 13294aa2e62cSJoy Latten if (err) 13304aa2e62cSJoy Latten return err; 1331bf08867fSHerbert Xu c.data.proto = p->proto; 1332f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 133326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 133426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 133526b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 133626b15dadSJamal Hadi Salim 13371da177e4SLinus Torvalds return 0; 13381da177e4SLinus Torvalds } 13391da177e4SLinus Torvalds 13407deb2264SThomas Graf static inline size_t xfrm_aevent_msgsize(void) 13417deb2264SThomas Graf { 13427deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 13437deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_replay_state)) 13447deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 13457deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 13467deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 13477deb2264SThomas Graf } 1348d51d081dSJamal Hadi Salim 1349d51d081dSJamal Hadi Salim static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 1350d51d081dSJamal Hadi Salim { 1351d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1352d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 1353d51d081dSJamal Hadi Salim 135479b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 135579b8b7f4SThomas Graf if (nlh == NULL) 135679b8b7f4SThomas Graf return -EMSGSIZE; 1357d51d081dSJamal Hadi Salim 13587b67c857SThomas Graf id = nlmsg_data(nlh); 13592b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1360d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1361d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1362d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 13632b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 13642b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1365d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1366d51d081dSJamal Hadi Salim 1367c0144beaSThomas Graf NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); 1368c0144beaSThomas Graf NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 1369d51d081dSJamal Hadi Salim 1370c0144beaSThomas Graf if (id->flags & XFRM_AE_RTHR) 1371c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 1372d51d081dSJamal Hadi Salim 1373c0144beaSThomas Graf if (id->flags & XFRM_AE_ETHR) 1374c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH, 1375c0144beaSThomas Graf x->replay_maxage * 10 / HZ); 1376d51d081dSJamal Hadi Salim 13779825069dSThomas Graf return nlmsg_end(skb, nlh); 1378d51d081dSJamal Hadi Salim 1379c0144beaSThomas Graf nla_put_failure: 13809825069dSThomas Graf nlmsg_cancel(skb, nlh); 13819825069dSThomas Graf return -EMSGSIZE; 1382d51d081dSJamal Hadi Salim } 1383d51d081dSJamal Hadi Salim 138422e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 13855424f32eSThomas Graf struct nlattr **attrs) 1386d51d081dSJamal Hadi Salim { 1387d51d081dSJamal Hadi Salim struct xfrm_state *x; 1388d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1389d51d081dSJamal Hadi Salim int err; 1390d51d081dSJamal Hadi Salim struct km_event c; 13917b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1392d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1393d51d081dSJamal Hadi Salim 13947deb2264SThomas Graf r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 1395d51d081dSJamal Hadi Salim if (r_skb == NULL) 1396d51d081dSJamal Hadi Salim return -ENOMEM; 1397d51d081dSJamal Hadi Salim 1398d51d081dSJamal Hadi Salim x = xfrm_state_lookup(&id->daddr, id->spi, id->proto, id->family); 1399d51d081dSJamal Hadi Salim if (x == NULL) { 1400b08d5840SPatrick McHardy kfree_skb(r_skb); 1401d51d081dSJamal Hadi Salim return -ESRCH; 1402d51d081dSJamal Hadi Salim } 1403d51d081dSJamal Hadi Salim 1404d51d081dSJamal Hadi Salim /* 1405d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1406d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1407d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1408d51d081dSJamal Hadi Salim */ 1409d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1410d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1411d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1412d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1413d51d081dSJamal Hadi Salim 1414d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1415d51d081dSJamal Hadi Salim BUG(); 1416082a1ad5SThomas Graf err = nlmsg_unicast(xfrm_nl, r_skb, NETLINK_CB(skb).pid); 1417d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1418d51d081dSJamal Hadi Salim xfrm_state_put(x); 1419d51d081dSJamal Hadi Salim return err; 1420d51d081dSJamal Hadi Salim } 1421d51d081dSJamal Hadi Salim 142222e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 14235424f32eSThomas Graf struct nlattr **attrs) 1424d51d081dSJamal Hadi Salim { 1425d51d081dSJamal Hadi Salim struct xfrm_state *x; 1426d51d081dSJamal Hadi Salim struct km_event c; 1427d51d081dSJamal Hadi Salim int err = - EINVAL; 14287b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 14295424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 14305424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1431d51d081dSJamal Hadi Salim 1432d51d081dSJamal Hadi Salim if (!lt && !rp) 1433d51d081dSJamal Hadi Salim return err; 1434d51d081dSJamal Hadi Salim 1435d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1436d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1437d51d081dSJamal Hadi Salim return err; 1438d51d081dSJamal Hadi Salim 1439d51d081dSJamal Hadi Salim x = xfrm_state_lookup(&p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1440d51d081dSJamal Hadi Salim if (x == NULL) 1441d51d081dSJamal Hadi Salim return -ESRCH; 1442d51d081dSJamal Hadi Salim 1443d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1444d51d081dSJamal Hadi Salim goto out; 1445d51d081dSJamal Hadi Salim 1446d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 144735a7aa08SThomas Graf xfrm_update_ae_params(x, attrs); 1448d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1449d51d081dSJamal Hadi Salim 1450d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1451d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1452d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1453d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1454d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1455d51d081dSJamal Hadi Salim err = 0; 1456d51d081dSJamal Hadi Salim out: 1457d51d081dSJamal Hadi Salim xfrm_state_put(x); 1458d51d081dSJamal Hadi Salim return err; 1459d51d081dSJamal Hadi Salim } 1460d51d081dSJamal Hadi Salim 146122e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 14625424f32eSThomas Graf struct nlattr **attrs) 14631da177e4SLinus Torvalds { 146426b15dadSJamal Hadi Salim struct km_event c; 1465b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1466f7b6983fSMasahide NAKAMURA int err; 1467161a09e7SJoy Latten struct xfrm_audit audit_info; 146826b15dadSJamal Hadi Salim 146935a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1470f7b6983fSMasahide NAKAMURA if (err) 1471f7b6983fSMasahide NAKAMURA return err; 1472f7b6983fSMasahide NAKAMURA 1473161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 1474161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 14754aa2e62cSJoy Latten err = xfrm_policy_flush(type, &audit_info); 14764aa2e62cSJoy Latten if (err) 14774aa2e62cSJoy Latten return err; 1478f7b6983fSMasahide NAKAMURA c.data.type = type; 1479f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 148026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 148126b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 148226b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 14831da177e4SLinus Torvalds return 0; 14841da177e4SLinus Torvalds } 14851da177e4SLinus Torvalds 148622e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 14875424f32eSThomas Graf struct nlattr **attrs) 14886c5c8ca7SJamal Hadi Salim { 14896c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 14907b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 14916c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1492b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 14936c5c8ca7SJamal Hadi Salim int err = -ENOENT; 14946c5c8ca7SJamal Hadi Salim 149535a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1496f7b6983fSMasahide NAKAMURA if (err) 1497f7b6983fSMasahide NAKAMURA return err; 1498f7b6983fSMasahide NAKAMURA 14996c5c8ca7SJamal Hadi Salim if (p->index) 1500ef41aaa0SEric Paris xp = xfrm_policy_byid(type, p->dir, p->index, 0, &err); 15016c5c8ca7SJamal Hadi Salim else { 15025424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 15036c5c8ca7SJamal Hadi Salim struct xfrm_policy tmp; 15046c5c8ca7SJamal Hadi Salim 150535a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 15066c5c8ca7SJamal Hadi Salim if (err) 15076c5c8ca7SJamal Hadi Salim return err; 15086c5c8ca7SJamal Hadi Salim 15096c5c8ca7SJamal Hadi Salim memset(&tmp, 0, sizeof(struct xfrm_policy)); 15106c5c8ca7SJamal Hadi Salim if (rt) { 15115424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 15126c5c8ca7SJamal Hadi Salim 15136c5c8ca7SJamal Hadi Salim if ((err = security_xfrm_policy_alloc(&tmp, uctx))) 15146c5c8ca7SJamal Hadi Salim return err; 15156c5c8ca7SJamal Hadi Salim } 1516ef41aaa0SEric Paris xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, tmp.security, 1517ef41aaa0SEric Paris 0, &err); 15186c5c8ca7SJamal Hadi Salim security_xfrm_policy_free(&tmp); 15196c5c8ca7SJamal Hadi Salim } 15206c5c8ca7SJamal Hadi Salim 15216c5c8ca7SJamal Hadi Salim if (xp == NULL) 1522ef41aaa0SEric Paris return -ENOENT; 15236c5c8ca7SJamal Hadi Salim read_lock(&xp->lock); 15246c5c8ca7SJamal Hadi Salim if (xp->dead) { 15256c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 15266c5c8ca7SJamal Hadi Salim goto out; 15276c5c8ca7SJamal Hadi Salim } 15286c5c8ca7SJamal Hadi Salim 15296c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 15306c5c8ca7SJamal Hadi Salim err = 0; 15316c5c8ca7SJamal Hadi Salim if (up->hard) { 15326c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 1533ab5f5e8bSJoy Latten xfrm_audit_policy_delete(xp, 1, NETLINK_CB(skb).loginuid, 1534ab5f5e8bSJoy Latten NETLINK_CB(skb).sid); 1535161a09e7SJoy Latten 15366c5c8ca7SJamal Hadi Salim } else { 15376c5c8ca7SJamal Hadi Salim // reset the timers here? 15386c5c8ca7SJamal Hadi Salim printk("Dont know what to do with soft policy expire\n"); 15396c5c8ca7SJamal Hadi Salim } 15406c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 15416c5c8ca7SJamal Hadi Salim 15426c5c8ca7SJamal Hadi Salim out: 15436c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 15446c5c8ca7SJamal Hadi Salim return err; 15456c5c8ca7SJamal Hadi Salim } 15466c5c8ca7SJamal Hadi Salim 154722e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 15485424f32eSThomas Graf struct nlattr **attrs) 154953bc6b4dSJamal Hadi Salim { 155053bc6b4dSJamal Hadi Salim struct xfrm_state *x; 155153bc6b4dSJamal Hadi Salim int err; 15527b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 155353bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 155453bc6b4dSJamal Hadi Salim 155553bc6b4dSJamal Hadi Salim x = xfrm_state_lookup(&p->id.daddr, p->id.spi, p->id.proto, p->family); 155653bc6b4dSJamal Hadi Salim 15573a765aa5SDavid S. Miller err = -ENOENT; 155853bc6b4dSJamal Hadi Salim if (x == NULL) 155953bc6b4dSJamal Hadi Salim return err; 156053bc6b4dSJamal Hadi Salim 156153bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 15623a765aa5SDavid S. Miller err = -EINVAL; 156353bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 156453bc6b4dSJamal Hadi Salim goto out; 156553bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 156653bc6b4dSJamal Hadi Salim 1567161a09e7SJoy Latten if (ue->hard) { 156853bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 1569ab5f5e8bSJoy Latten xfrm_audit_state_delete(x, 1, NETLINK_CB(skb).loginuid, 1570ab5f5e8bSJoy Latten NETLINK_CB(skb).sid); 1571161a09e7SJoy Latten } 15723a765aa5SDavid S. Miller err = 0; 157353bc6b4dSJamal Hadi Salim out: 157453bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 157553bc6b4dSJamal Hadi Salim xfrm_state_put(x); 157653bc6b4dSJamal Hadi Salim return err; 157753bc6b4dSJamal Hadi Salim } 157853bc6b4dSJamal Hadi Salim 157922e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 15805424f32eSThomas Graf struct nlattr **attrs) 1581980ebd25SJamal Hadi Salim { 1582980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1583980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1584980ebd25SJamal Hadi Salim int i; 15855424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 1586980ebd25SJamal Hadi Salim 15877b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 1588980ebd25SJamal Hadi Salim struct xfrm_state *x = xfrm_state_alloc(); 1589980ebd25SJamal Hadi Salim int err = -ENOMEM; 1590980ebd25SJamal Hadi Salim 1591980ebd25SJamal Hadi Salim if (!x) 1592980ebd25SJamal Hadi Salim return err; 1593980ebd25SJamal Hadi Salim 1594980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 1595980ebd25SJamal Hadi Salim if (err) { 1596980ebd25SJamal Hadi Salim printk("BAD policy passed\n"); 1597980ebd25SJamal Hadi Salim kfree(x); 1598980ebd25SJamal Hadi Salim return err; 1599980ebd25SJamal Hadi Salim } 1600980ebd25SJamal Hadi Salim 1601980ebd25SJamal Hadi Salim /* build an XP */ 16025424f32eSThomas Graf xp = xfrm_policy_construct(&ua->policy, attrs, &err); 1603b4ad86bfSDavid S. Miller if (!xp) { 1604980ebd25SJamal Hadi Salim kfree(x); 1605980ebd25SJamal Hadi Salim return err; 1606980ebd25SJamal Hadi Salim } 1607980ebd25SJamal Hadi Salim 1608980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 1609980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 1610980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 1611980ebd25SJamal Hadi Salim 16125424f32eSThomas Graf ut = nla_data(rt); 1613980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 1614980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 1615980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 1616980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 1617980ebd25SJamal Hadi Salim x->props.mode = t->mode; 1618980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 1619980ebd25SJamal Hadi Salim x->props.family = ut->family; 1620980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 1621980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 1622980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 1623980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 1624980ebd25SJamal Hadi Salim 1625980ebd25SJamal Hadi Salim } 1626980ebd25SJamal Hadi Salim 1627980ebd25SJamal Hadi Salim kfree(x); 1628980ebd25SJamal Hadi Salim kfree(xp); 1629980ebd25SJamal Hadi Salim 1630980ebd25SJamal Hadi Salim return 0; 1631980ebd25SJamal Hadi Salim } 1632980ebd25SJamal Hadi Salim 16335c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 16345c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 16355424f32eSThomas Graf struct nlattr **attrs, int *num) 16365c79de6eSShinta Sugimoto { 16375424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 16385c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 16395c79de6eSShinta Sugimoto int i, num_migrate; 16405c79de6eSShinta Sugimoto 16415424f32eSThomas Graf um = nla_data(rt); 16425424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 16435c79de6eSShinta Sugimoto 16445c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 16455c79de6eSShinta Sugimoto return -EINVAL; 16465c79de6eSShinta Sugimoto 16475c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 16485c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 16495c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 16505c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 16515c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 16525c79de6eSShinta Sugimoto 16535c79de6eSShinta Sugimoto ma->proto = um->proto; 16545c79de6eSShinta Sugimoto ma->mode = um->mode; 16555c79de6eSShinta Sugimoto ma->reqid = um->reqid; 16565c79de6eSShinta Sugimoto 16575c79de6eSShinta Sugimoto ma->old_family = um->old_family; 16585c79de6eSShinta Sugimoto ma->new_family = um->new_family; 16595c79de6eSShinta Sugimoto } 16605c79de6eSShinta Sugimoto 16615c79de6eSShinta Sugimoto *num = i; 16625c79de6eSShinta Sugimoto return 0; 16635c79de6eSShinta Sugimoto } 16645c79de6eSShinta Sugimoto 16655c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 16665424f32eSThomas Graf struct nlattr **attrs) 16675c79de6eSShinta Sugimoto { 16687b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 16695c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 16705c79de6eSShinta Sugimoto u8 type; 16715c79de6eSShinta Sugimoto int err; 16725c79de6eSShinta Sugimoto int n = 0; 16735c79de6eSShinta Sugimoto 167435a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 1675cf5cb79fSThomas Graf return -EINVAL; 16765c79de6eSShinta Sugimoto 16775424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 16785c79de6eSShinta Sugimoto if (err) 16795c79de6eSShinta Sugimoto return err; 16805c79de6eSShinta Sugimoto 16815c79de6eSShinta Sugimoto err = copy_from_user_migrate((struct xfrm_migrate *)m, 16825424f32eSThomas Graf attrs, &n); 16835c79de6eSShinta Sugimoto if (err) 16845c79de6eSShinta Sugimoto return err; 16855c79de6eSShinta Sugimoto 16865c79de6eSShinta Sugimoto if (!n) 16875c79de6eSShinta Sugimoto return 0; 16885c79de6eSShinta Sugimoto 16895c79de6eSShinta Sugimoto xfrm_migrate(&pi->sel, pi->dir, type, m, n); 16905c79de6eSShinta Sugimoto 16915c79de6eSShinta Sugimoto return 0; 16925c79de6eSShinta Sugimoto } 16935c79de6eSShinta Sugimoto #else 16945c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 16955424f32eSThomas Graf struct nlattr **attrs) 16965c79de6eSShinta Sugimoto { 16975c79de6eSShinta Sugimoto return -ENOPROTOOPT; 16985c79de6eSShinta Sugimoto } 16995c79de6eSShinta Sugimoto #endif 17005c79de6eSShinta Sugimoto 17015c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 17025c79de6eSShinta Sugimoto static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) 17035c79de6eSShinta Sugimoto { 17045c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 17055c79de6eSShinta Sugimoto 17065c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 17075c79de6eSShinta Sugimoto um.proto = m->proto; 17085c79de6eSShinta Sugimoto um.mode = m->mode; 17095c79de6eSShinta Sugimoto um.reqid = m->reqid; 17105c79de6eSShinta Sugimoto um.old_family = m->old_family; 17115c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 17125c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 17135c79de6eSShinta Sugimoto um.new_family = m->new_family; 17145c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 17155c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 17165c79de6eSShinta Sugimoto 1717c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 17185c79de6eSShinta Sugimoto } 17195c79de6eSShinta Sugimoto 17207deb2264SThomas Graf static inline size_t xfrm_migrate_msgsize(int num_migrate) 17217deb2264SThomas Graf { 17227deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 17237deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 17247deb2264SThomas Graf + userpolicy_type_attrsize(); 17257deb2264SThomas Graf } 17267deb2264SThomas Graf 17275c79de6eSShinta Sugimoto static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, 17285c79de6eSShinta Sugimoto int num_migrate, struct xfrm_selector *sel, 17295c79de6eSShinta Sugimoto u8 dir, u8 type) 17305c79de6eSShinta Sugimoto { 17315c79de6eSShinta Sugimoto struct xfrm_migrate *mp; 17325c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 17335c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 17345c79de6eSShinta Sugimoto int i; 17355c79de6eSShinta Sugimoto 173679b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 173779b8b7f4SThomas Graf if (nlh == NULL) 173879b8b7f4SThomas Graf return -EMSGSIZE; 17395c79de6eSShinta Sugimoto 17407b67c857SThomas Graf pol_id = nlmsg_data(nlh); 17415c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 17425c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 17435c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 17445c79de6eSShinta Sugimoto pol_id->dir = dir; 17455c79de6eSShinta Sugimoto 17465c79de6eSShinta Sugimoto if (copy_to_user_policy_type(type, skb) < 0) 17475c79de6eSShinta Sugimoto goto nlmsg_failure; 17485c79de6eSShinta Sugimoto 17495c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 17505c79de6eSShinta Sugimoto if (copy_to_user_migrate(mp, skb) < 0) 17515c79de6eSShinta Sugimoto goto nlmsg_failure; 17525c79de6eSShinta Sugimoto } 17535c79de6eSShinta Sugimoto 17549825069dSThomas Graf return nlmsg_end(skb, nlh); 17555c79de6eSShinta Sugimoto nlmsg_failure: 17569825069dSThomas Graf nlmsg_cancel(skb, nlh); 17579825069dSThomas Graf return -EMSGSIZE; 17585c79de6eSShinta Sugimoto } 17595c79de6eSShinta Sugimoto 17605c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 17615c79de6eSShinta Sugimoto struct xfrm_migrate *m, int num_migrate) 17625c79de6eSShinta Sugimoto { 17635c79de6eSShinta Sugimoto struct sk_buff *skb; 17645c79de6eSShinta Sugimoto 17657deb2264SThomas Graf skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate), GFP_ATOMIC); 17665c79de6eSShinta Sugimoto if (skb == NULL) 17675c79de6eSShinta Sugimoto return -ENOMEM; 17685c79de6eSShinta Sugimoto 17695c79de6eSShinta Sugimoto /* build migrate */ 17705c79de6eSShinta Sugimoto if (build_migrate(skb, m, num_migrate, sel, dir, type) < 0) 17715c79de6eSShinta Sugimoto BUG(); 17725c79de6eSShinta Sugimoto 1773082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 17745c79de6eSShinta Sugimoto } 17755c79de6eSShinta Sugimoto #else 17765c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 17775c79de6eSShinta Sugimoto struct xfrm_migrate *m, int num_migrate) 17785c79de6eSShinta Sugimoto { 17795c79de6eSShinta Sugimoto return -ENOPROTOOPT; 17805c79de6eSShinta Sugimoto } 17815c79de6eSShinta Sugimoto #endif 1782d51d081dSJamal Hadi Salim 1783a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 1784492b558bSThomas Graf 1785492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 1786492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 1787492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 1788492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 1789492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 1790492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1791492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1792492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 1793980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 179453bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 1795492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 1796492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 17976c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 1798492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 1799a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 1800d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 1801d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 180297a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 18035c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1804a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 1805a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 18061da177e4SLinus Torvalds }; 18071da177e4SLinus Torvalds 1808492b558bSThomas Graf #undef XMSGSIZE 1809492b558bSThomas Graf 1810cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 1811cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 1812cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 1813cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 1814cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 1815cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 1816cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 1817cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 1818cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 1819cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 1820cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 1821cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 1822cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 1823cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 1824cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 1825cf5cb79fSThomas Graf }; 1826cf5cb79fSThomas Graf 18271da177e4SLinus Torvalds static struct xfrm_link { 18285424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 18291da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 1830492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 1831492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 1832492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 1833492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 1834492b558bSThomas Graf .dump = xfrm_dump_sa }, 1835492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 1836492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 1837492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 1838492b558bSThomas Graf .dump = xfrm_dump_policy }, 1839492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 1840980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 184153bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 1842492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 1843492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 18446c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 1845492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 1846492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 1847d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 1848d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 18495c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 185028d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 1851ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 18521da177e4SLinus Torvalds }; 18531da177e4SLinus Torvalds 18541d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 18551da177e4SLinus Torvalds { 185635a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 18571da177e4SLinus Torvalds struct xfrm_link *link; 1858a7bd9a45SThomas Graf int type, err; 18591da177e4SLinus Torvalds 18601da177e4SLinus Torvalds type = nlh->nlmsg_type; 18611da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 18621d00a4ebSThomas Graf return -EINVAL; 18631da177e4SLinus Torvalds 18641da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 18651da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 18661da177e4SLinus Torvalds 18671da177e4SLinus Torvalds /* All operations require privileges, even GET */ 18681d00a4ebSThomas Graf if (security_netlink_recv(skb, CAP_NET_ADMIN)) 18691d00a4ebSThomas Graf return -EPERM; 18701da177e4SLinus Torvalds 1871492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 1872492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 1873492b558bSThomas Graf (nlh->nlmsg_flags & NLM_F_DUMP)) { 18741da177e4SLinus Torvalds if (link->dump == NULL) 18751d00a4ebSThomas Graf return -EINVAL; 18761da177e4SLinus Torvalds 1877c702e804SThomas Graf return netlink_dump_start(xfrm_nl, skb, nlh, link->dump, NULL); 18781da177e4SLinus Torvalds } 18791da177e4SLinus Torvalds 188035a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 1881cf5cb79fSThomas Graf xfrma_policy); 1882a7bd9a45SThomas Graf if (err < 0) 1883a7bd9a45SThomas Graf return err; 18841da177e4SLinus Torvalds 18851da177e4SLinus Torvalds if (link->doit == NULL) 18861d00a4ebSThomas Graf return -EINVAL; 18871da177e4SLinus Torvalds 18885424f32eSThomas Graf return link->doit(skb, nlh, attrs); 18891da177e4SLinus Torvalds } 18901da177e4SLinus Torvalds 1891cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 18921da177e4SLinus Torvalds { 18934a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 1894cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 18954a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 18961da177e4SLinus Torvalds } 18971da177e4SLinus Torvalds 18987deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 18997deb2264SThomas Graf { 19007deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)); 19017deb2264SThomas Graf } 19027deb2264SThomas Graf 1903d51d081dSJamal Hadi Salim static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 19041da177e4SLinus Torvalds { 19051da177e4SLinus Torvalds struct xfrm_user_expire *ue; 19061da177e4SLinus Torvalds struct nlmsghdr *nlh; 19071da177e4SLinus Torvalds 190879b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 190979b8b7f4SThomas Graf if (nlh == NULL) 191079b8b7f4SThomas Graf return -EMSGSIZE; 19111da177e4SLinus Torvalds 19127b67c857SThomas Graf ue = nlmsg_data(nlh); 19131da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 1914d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 19151da177e4SLinus Torvalds 19169825069dSThomas Graf return nlmsg_end(skb, nlh); 19171da177e4SLinus Torvalds } 19181da177e4SLinus Torvalds 191926b15dadSJamal Hadi Salim static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c) 19201da177e4SLinus Torvalds { 19211da177e4SLinus Torvalds struct sk_buff *skb; 19221da177e4SLinus Torvalds 19237deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 19241da177e4SLinus Torvalds if (skb == NULL) 19251da177e4SLinus Torvalds return -ENOMEM; 19261da177e4SLinus Torvalds 1927d51d081dSJamal Hadi Salim if (build_expire(skb, x, c) < 0) 19281da177e4SLinus Torvalds BUG(); 19291da177e4SLinus Torvalds 1930082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 19311da177e4SLinus Torvalds } 19321da177e4SLinus Torvalds 1933d51d081dSJamal Hadi Salim static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c) 1934d51d081dSJamal Hadi Salim { 1935d51d081dSJamal Hadi Salim struct sk_buff *skb; 1936d51d081dSJamal Hadi Salim 19377deb2264SThomas Graf skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 1938d51d081dSJamal Hadi Salim if (skb == NULL) 1939d51d081dSJamal Hadi Salim return -ENOMEM; 1940d51d081dSJamal Hadi Salim 1941d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 1942d51d081dSJamal Hadi Salim BUG(); 1943d51d081dSJamal Hadi Salim 1944082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 1945d51d081dSJamal Hadi Salim } 1946d51d081dSJamal Hadi Salim 194726b15dadSJamal Hadi Salim static int xfrm_notify_sa_flush(struct km_event *c) 194826b15dadSJamal Hadi Salim { 194926b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 195026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 195126b15dadSJamal Hadi Salim struct sk_buff *skb; 19527deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 195326b15dadSJamal Hadi Salim 19547deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 195526b15dadSJamal Hadi Salim if (skb == NULL) 195626b15dadSJamal Hadi Salim return -ENOMEM; 195726b15dadSJamal Hadi Salim 195879b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 195979b8b7f4SThomas Graf if (nlh == NULL) { 196079b8b7f4SThomas Graf kfree_skb(skb); 196179b8b7f4SThomas Graf return -EMSGSIZE; 196279b8b7f4SThomas Graf } 196326b15dadSJamal Hadi Salim 19647b67c857SThomas Graf p = nlmsg_data(nlh); 1965bf08867fSHerbert Xu p->proto = c->data.proto; 196626b15dadSJamal Hadi Salim 19679825069dSThomas Graf nlmsg_end(skb, nlh); 196826b15dadSJamal Hadi Salim 1969082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 197026b15dadSJamal Hadi Salim } 197126b15dadSJamal Hadi Salim 19727deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 197326b15dadSJamal Hadi Salim { 19747deb2264SThomas Graf size_t l = 0; 197526b15dadSJamal Hadi Salim if (x->aalg) 19760f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->aalg)); 197726b15dadSJamal Hadi Salim if (x->ealg) 19780f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 197926b15dadSJamal Hadi Salim if (x->calg) 19807deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 198126b15dadSJamal Hadi Salim if (x->encap) 19827deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 198368325d3bSHerbert Xu if (x->security) 198468325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 198568325d3bSHerbert Xu x->security->ctx_len); 198668325d3bSHerbert Xu if (x->coaddr) 198768325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 198868325d3bSHerbert Xu 1989*d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 1990*d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 199126b15dadSJamal Hadi Salim 199226b15dadSJamal Hadi Salim return l; 199326b15dadSJamal Hadi Salim } 199426b15dadSJamal Hadi Salim 199526b15dadSJamal Hadi Salim static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c) 199626b15dadSJamal Hadi Salim { 199726b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 19980603eac0SHerbert Xu struct xfrm_usersa_id *id; 199926b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 200026b15dadSJamal Hadi Salim struct sk_buff *skb; 200126b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 20020603eac0SHerbert Xu int headlen; 20030603eac0SHerbert Xu 20040603eac0SHerbert Xu headlen = sizeof(*p); 20050603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 20067deb2264SThomas Graf len += nla_total_size(headlen); 20070603eac0SHerbert Xu headlen = sizeof(*id); 20080603eac0SHerbert Xu } 20097deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 201026b15dadSJamal Hadi Salim 20117deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 201226b15dadSJamal Hadi Salim if (skb == NULL) 201326b15dadSJamal Hadi Salim return -ENOMEM; 201426b15dadSJamal Hadi Salim 201579b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 201679b8b7f4SThomas Graf if (nlh == NULL) 2017c0144beaSThomas Graf goto nla_put_failure; 201826b15dadSJamal Hadi Salim 20197b67c857SThomas Graf p = nlmsg_data(nlh); 20200603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2021c0144beaSThomas Graf struct nlattr *attr; 2022c0144beaSThomas Graf 20237b67c857SThomas Graf id = nlmsg_data(nlh); 20240603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 20250603eac0SHerbert Xu id->spi = x->id.spi; 20260603eac0SHerbert Xu id->family = x->props.family; 20270603eac0SHerbert Xu id->proto = x->id.proto; 20280603eac0SHerbert Xu 2029c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 2030c0144beaSThomas Graf if (attr == NULL) 2031c0144beaSThomas Graf goto nla_put_failure; 2032c0144beaSThomas Graf 2033c0144beaSThomas Graf p = nla_data(attr); 20340603eac0SHerbert Xu } 20350603eac0SHerbert Xu 203668325d3bSHerbert Xu if (copy_to_user_state_extra(x, p, skb)) 203768325d3bSHerbert Xu goto nla_put_failure; 203826b15dadSJamal Hadi Salim 20399825069dSThomas Graf nlmsg_end(skb, nlh); 204026b15dadSJamal Hadi Salim 2041082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 204226b15dadSJamal Hadi Salim 2043c0144beaSThomas Graf nla_put_failure: 204468325d3bSHerbert Xu /* Somebody screwed up with xfrm_sa_len! */ 204568325d3bSHerbert Xu WARN_ON(1); 204626b15dadSJamal Hadi Salim kfree_skb(skb); 204726b15dadSJamal Hadi Salim return -1; 204826b15dadSJamal Hadi Salim } 204926b15dadSJamal Hadi Salim 205026b15dadSJamal Hadi Salim static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c) 205126b15dadSJamal Hadi Salim { 205226b15dadSJamal Hadi Salim 205326b15dadSJamal Hadi Salim switch (c->event) { 2054f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 205526b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2056d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2057d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2058f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2059f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2060f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 206126b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2062f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 206326b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 206426b15dadSJamal Hadi Salim default: 206526b15dadSJamal Hadi Salim printk("xfrm_user: Unknown SA event %d\n", c->event); 206626b15dadSJamal Hadi Salim break; 206726b15dadSJamal Hadi Salim } 206826b15dadSJamal Hadi Salim 206926b15dadSJamal Hadi Salim return 0; 207026b15dadSJamal Hadi Salim 207126b15dadSJamal Hadi Salim } 207226b15dadSJamal Hadi Salim 20737deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 20747deb2264SThomas Graf struct xfrm_policy *xp) 20757deb2264SThomas Graf { 20767deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 20777deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 20787deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 20797deb2264SThomas Graf + userpolicy_type_attrsize(); 20807deb2264SThomas Graf } 20817deb2264SThomas Graf 20821da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 20831da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 20841da177e4SLinus Torvalds int dir) 20851da177e4SLinus Torvalds { 20861da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 20871da177e4SLinus Torvalds struct nlmsghdr *nlh; 20881da177e4SLinus Torvalds __u32 seq = xfrm_get_acqseq(); 20891da177e4SLinus Torvalds 209079b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 209179b8b7f4SThomas Graf if (nlh == NULL) 209279b8b7f4SThomas Graf return -EMSGSIZE; 20931da177e4SLinus Torvalds 20947b67c857SThomas Graf ua = nlmsg_data(nlh); 20951da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 20961da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 20971da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 20981da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 20991da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 21001da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 21011da177e4SLinus Torvalds ua->calgos = xt->calgos; 21021da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 21031da177e4SLinus Torvalds 21041da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 21051da177e4SLinus Torvalds goto nlmsg_failure; 21060d681623SSerge Hallyn if (copy_to_user_state_sec_ctx(x, skb)) 2107df71837dSTrent Jaeger goto nlmsg_failure; 21081459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2109f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 21101da177e4SLinus Torvalds 21119825069dSThomas Graf return nlmsg_end(skb, nlh); 21121da177e4SLinus Torvalds 21131da177e4SLinus Torvalds nlmsg_failure: 21149825069dSThomas Graf nlmsg_cancel(skb, nlh); 21159825069dSThomas Graf return -EMSGSIZE; 21161da177e4SLinus Torvalds } 21171da177e4SLinus Torvalds 21181da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 21191da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 21201da177e4SLinus Torvalds { 21211da177e4SLinus Torvalds struct sk_buff *skb; 21221da177e4SLinus Torvalds 21237deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 21241da177e4SLinus Torvalds if (skb == NULL) 21251da177e4SLinus Torvalds return -ENOMEM; 21261da177e4SLinus Torvalds 21271da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 21281da177e4SLinus Torvalds BUG(); 21291da177e4SLinus Torvalds 2130082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 21311da177e4SLinus Torvalds } 21321da177e4SLinus Torvalds 21331da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 21341da177e4SLinus Torvalds * or more templates. 21351da177e4SLinus Torvalds */ 2136cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 21371da177e4SLinus Torvalds u8 *data, int len, int *dir) 21381da177e4SLinus Torvalds { 21391da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 21401da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 21411da177e4SLinus Torvalds struct xfrm_policy *xp; 21421da177e4SLinus Torvalds int nr; 21431da177e4SLinus Torvalds 2144cb969f07SVenkat Yekkirala switch (sk->sk_family) { 21451da177e4SLinus Torvalds case AF_INET: 21461da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 21471da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 21481da177e4SLinus Torvalds return NULL; 21491da177e4SLinus Torvalds } 21501da177e4SLinus Torvalds break; 21511da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 21521da177e4SLinus Torvalds case AF_INET6: 21531da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 21541da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 21551da177e4SLinus Torvalds return NULL; 21561da177e4SLinus Torvalds } 21571da177e4SLinus Torvalds break; 21581da177e4SLinus Torvalds #endif 21591da177e4SLinus Torvalds default: 21601da177e4SLinus Torvalds *dir = -EINVAL; 21611da177e4SLinus Torvalds return NULL; 21621da177e4SLinus Torvalds } 21631da177e4SLinus Torvalds 21641da177e4SLinus Torvalds *dir = -EINVAL; 21651da177e4SLinus Torvalds 21661da177e4SLinus Torvalds if (len < sizeof(*p) || 21671da177e4SLinus Torvalds verify_newpolicy_info(p)) 21681da177e4SLinus Torvalds return NULL; 21691da177e4SLinus Torvalds 21701da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2171b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 21721da177e4SLinus Torvalds return NULL; 21731da177e4SLinus Torvalds 2174a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2175a4f1bac6SHerbert Xu return NULL; 2176a4f1bac6SHerbert Xu 21771da177e4SLinus Torvalds xp = xfrm_policy_alloc(GFP_KERNEL); 21781da177e4SLinus Torvalds if (xp == NULL) { 21791da177e4SLinus Torvalds *dir = -ENOBUFS; 21801da177e4SLinus Torvalds return NULL; 21811da177e4SLinus Torvalds } 21821da177e4SLinus Torvalds 21831da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2184f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 21851da177e4SLinus Torvalds copy_templates(xp, ut, nr); 21861da177e4SLinus Torvalds 21871da177e4SLinus Torvalds *dir = p->dir; 21881da177e4SLinus Torvalds 21891da177e4SLinus Torvalds return xp; 21901da177e4SLinus Torvalds } 21911da177e4SLinus Torvalds 21927deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 21937deb2264SThomas Graf { 21947deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 21957deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 21967deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 21977deb2264SThomas Graf + userpolicy_type_attrsize(); 21987deb2264SThomas Graf } 21997deb2264SThomas Graf 22001da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2201d51d081dSJamal Hadi Salim int dir, struct km_event *c) 22021da177e4SLinus Torvalds { 22031da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 22041da177e4SLinus Torvalds struct nlmsghdr *nlh; 2205d51d081dSJamal Hadi Salim int hard = c->data.hard; 22061da177e4SLinus Torvalds 220779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 220879b8b7f4SThomas Graf if (nlh == NULL) 220979b8b7f4SThomas Graf return -EMSGSIZE; 22101da177e4SLinus Torvalds 22117b67c857SThomas Graf upe = nlmsg_data(nlh); 22121da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 22131da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 22141da177e4SLinus Torvalds goto nlmsg_failure; 2215df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 2216df71837dSTrent Jaeger goto nlmsg_failure; 22171459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2218f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 22191da177e4SLinus Torvalds upe->hard = !!hard; 22201da177e4SLinus Torvalds 22219825069dSThomas Graf return nlmsg_end(skb, nlh); 22221da177e4SLinus Torvalds 22231da177e4SLinus Torvalds nlmsg_failure: 22249825069dSThomas Graf nlmsg_cancel(skb, nlh); 22259825069dSThomas Graf return -EMSGSIZE; 22261da177e4SLinus Torvalds } 22271da177e4SLinus Torvalds 222826b15dadSJamal Hadi Salim static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 22291da177e4SLinus Torvalds { 22301da177e4SLinus Torvalds struct sk_buff *skb; 22311da177e4SLinus Torvalds 22327deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 22331da177e4SLinus Torvalds if (skb == NULL) 22341da177e4SLinus Torvalds return -ENOMEM; 22351da177e4SLinus Torvalds 2236d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 22371da177e4SLinus Torvalds BUG(); 22381da177e4SLinus Torvalds 2239082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 22401da177e4SLinus Torvalds } 22411da177e4SLinus Torvalds 224226b15dadSJamal Hadi Salim static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c) 224326b15dadSJamal Hadi Salim { 224426b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 22450603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 224626b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 224726b15dadSJamal Hadi Salim struct sk_buff *skb; 22487deb2264SThomas Graf int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 22490603eac0SHerbert Xu int headlen; 22500603eac0SHerbert Xu 22510603eac0SHerbert Xu headlen = sizeof(*p); 22520603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 22537deb2264SThomas Graf len += nla_total_size(headlen); 22540603eac0SHerbert Xu headlen = sizeof(*id); 22550603eac0SHerbert Xu } 2256cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 22577deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 225826b15dadSJamal Hadi Salim 22597deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 226026b15dadSJamal Hadi Salim if (skb == NULL) 226126b15dadSJamal Hadi Salim return -ENOMEM; 226226b15dadSJamal Hadi Salim 226379b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 226479b8b7f4SThomas Graf if (nlh == NULL) 226579b8b7f4SThomas Graf goto nlmsg_failure; 226626b15dadSJamal Hadi Salim 22677b67c857SThomas Graf p = nlmsg_data(nlh); 22680603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2269c0144beaSThomas Graf struct nlattr *attr; 2270c0144beaSThomas Graf 22717b67c857SThomas Graf id = nlmsg_data(nlh); 22720603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 22730603eac0SHerbert Xu id->dir = dir; 22740603eac0SHerbert Xu if (c->data.byid) 22750603eac0SHerbert Xu id->index = xp->index; 22760603eac0SHerbert Xu else 22770603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 22780603eac0SHerbert Xu 2279c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 2280c0144beaSThomas Graf if (attr == NULL) 2281c0144beaSThomas Graf goto nlmsg_failure; 2282c0144beaSThomas Graf 2283c0144beaSThomas Graf p = nla_data(attr); 22840603eac0SHerbert Xu } 228526b15dadSJamal Hadi Salim 228626b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 228726b15dadSJamal Hadi Salim if (copy_to_user_tmpl(xp, skb) < 0) 228826b15dadSJamal Hadi Salim goto nlmsg_failure; 22891459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2290f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 229126b15dadSJamal Hadi Salim 22929825069dSThomas Graf nlmsg_end(skb, nlh); 229326b15dadSJamal Hadi Salim 2294082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 229526b15dadSJamal Hadi Salim 229626b15dadSJamal Hadi Salim nlmsg_failure: 229726b15dadSJamal Hadi Salim kfree_skb(skb); 229826b15dadSJamal Hadi Salim return -1; 229926b15dadSJamal Hadi Salim } 230026b15dadSJamal Hadi Salim 230126b15dadSJamal Hadi Salim static int xfrm_notify_policy_flush(struct km_event *c) 230226b15dadSJamal Hadi Salim { 230326b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 230426b15dadSJamal Hadi Salim struct sk_buff *skb; 230526b15dadSJamal Hadi Salim 23067deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 230726b15dadSJamal Hadi Salim if (skb == NULL) 230826b15dadSJamal Hadi Salim return -ENOMEM; 230926b15dadSJamal Hadi Salim 231079b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 231179b8b7f4SThomas Graf if (nlh == NULL) 231279b8b7f4SThomas Graf goto nlmsg_failure; 23130c51f53cSJamal Hadi Salim if (copy_to_user_policy_type(c->data.type, skb) < 0) 23140c51f53cSJamal Hadi Salim goto nlmsg_failure; 231526b15dadSJamal Hadi Salim 23169825069dSThomas Graf nlmsg_end(skb, nlh); 231726b15dadSJamal Hadi Salim 2318082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 231926b15dadSJamal Hadi Salim 232026b15dadSJamal Hadi Salim nlmsg_failure: 232126b15dadSJamal Hadi Salim kfree_skb(skb); 232226b15dadSJamal Hadi Salim return -1; 232326b15dadSJamal Hadi Salim } 232426b15dadSJamal Hadi Salim 232526b15dadSJamal Hadi Salim static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 232626b15dadSJamal Hadi Salim { 232726b15dadSJamal Hadi Salim 232826b15dadSJamal Hadi Salim switch (c->event) { 2329f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2330f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2331f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 233226b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2333f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 233426b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2335f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 233626b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 233726b15dadSJamal Hadi Salim default: 233826b15dadSJamal Hadi Salim printk("xfrm_user: Unknown Policy event %d\n", c->event); 233926b15dadSJamal Hadi Salim } 234026b15dadSJamal Hadi Salim 234126b15dadSJamal Hadi Salim return 0; 234226b15dadSJamal Hadi Salim 234326b15dadSJamal Hadi Salim } 234426b15dadSJamal Hadi Salim 23457deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 23467deb2264SThomas Graf { 23477deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 23487deb2264SThomas Graf } 23497deb2264SThomas Graf 235097a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 235197a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 235297a64b45SMasahide NAKAMURA { 235397a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 235497a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 235597a64b45SMasahide NAKAMURA 235679b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 235779b8b7f4SThomas Graf if (nlh == NULL) 235879b8b7f4SThomas Graf return -EMSGSIZE; 235997a64b45SMasahide NAKAMURA 23607b67c857SThomas Graf ur = nlmsg_data(nlh); 236197a64b45SMasahide NAKAMURA ur->proto = proto; 236297a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 236397a64b45SMasahide NAKAMURA 236497a64b45SMasahide NAKAMURA if (addr) 2365c0144beaSThomas Graf NLA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); 236697a64b45SMasahide NAKAMURA 23679825069dSThomas Graf return nlmsg_end(skb, nlh); 236897a64b45SMasahide NAKAMURA 2369c0144beaSThomas Graf nla_put_failure: 23709825069dSThomas Graf nlmsg_cancel(skb, nlh); 23719825069dSThomas Graf return -EMSGSIZE; 237297a64b45SMasahide NAKAMURA } 237397a64b45SMasahide NAKAMURA 237497a64b45SMasahide NAKAMURA static int xfrm_send_report(u8 proto, struct xfrm_selector *sel, 237597a64b45SMasahide NAKAMURA xfrm_address_t *addr) 237697a64b45SMasahide NAKAMURA { 237797a64b45SMasahide NAKAMURA struct sk_buff *skb; 237897a64b45SMasahide NAKAMURA 23797deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 238097a64b45SMasahide NAKAMURA if (skb == NULL) 238197a64b45SMasahide NAKAMURA return -ENOMEM; 238297a64b45SMasahide NAKAMURA 238397a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 238497a64b45SMasahide NAKAMURA BUG(); 238597a64b45SMasahide NAKAMURA 2386082a1ad5SThomas Graf return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 238797a64b45SMasahide NAKAMURA } 238897a64b45SMasahide NAKAMURA 23891da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 23901da177e4SLinus Torvalds .id = "netlink", 23911da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 23921da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 23931da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 23941da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 239597a64b45SMasahide NAKAMURA .report = xfrm_send_report, 23965c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 23971da177e4SLinus Torvalds }; 23981da177e4SLinus Torvalds 23991da177e4SLinus Torvalds static int __init xfrm_user_init(void) 24001da177e4SLinus Torvalds { 2401be33690dSPatrick McHardy struct sock *nlsk; 2402be33690dSPatrick McHardy 2403654b32c6SMasahide NAKAMURA printk(KERN_INFO "Initializing XFRM netlink socket\n"); 24041da177e4SLinus Torvalds 2405b4b51029SEric W. Biederman nlsk = netlink_kernel_create(&init_net, NETLINK_XFRM, XFRMNLGRP_MAX, 2406af65bdfcSPatrick McHardy xfrm_netlink_rcv, NULL, THIS_MODULE); 2407be33690dSPatrick McHardy if (nlsk == NULL) 24081da177e4SLinus Torvalds return -ENOMEM; 2409be33690dSPatrick McHardy rcu_assign_pointer(xfrm_nl, nlsk); 24101da177e4SLinus Torvalds 24111da177e4SLinus Torvalds xfrm_register_km(&netlink_mgr); 24121da177e4SLinus Torvalds 24131da177e4SLinus Torvalds return 0; 24141da177e4SLinus Torvalds } 24151da177e4SLinus Torvalds 24161da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 24171da177e4SLinus Torvalds { 2418be33690dSPatrick McHardy struct sock *nlsk = xfrm_nl; 2419be33690dSPatrick McHardy 24201da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 2421be33690dSPatrick McHardy rcu_assign_pointer(xfrm_nl, NULL); 2422be33690dSPatrick McHardy synchronize_rcu(); 2423be33690dSPatrick McHardy sock_release(nlsk->sk_socket); 24241da177e4SLinus Torvalds } 24251da177e4SLinus Torvalds 24261da177e4SLinus Torvalds module_init(xfrm_user_init); 24271da177e4SLinus Torvalds module_exit(xfrm_user_exit); 24281da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 24294fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 2430f8cd5488SJamal Hadi Salim 2431