11da177e4SLinus Torvalds /* xfrm_user.c: User interface to configure xfrm engine. 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * Copyright (C) 2002 David S. Miller (davem@redhat.com) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Changes: 61da177e4SLinus Torvalds * Mitsuru KANDA @USAGI 71da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 81da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 91da177e4SLinus Torvalds * IPv6 support 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 139409f38aSHerbert Xu #include <linux/crypto.h> 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/types.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 181da177e4SLinus Torvalds #include <linux/socket.h> 191da177e4SLinus Torvalds #include <linux/string.h> 201da177e4SLinus Torvalds #include <linux/net.h> 211da177e4SLinus Torvalds #include <linux/skbuff.h> 221da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 231da177e4SLinus Torvalds #include <linux/ipsec.h> 241da177e4SLinus Torvalds #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/security.h> 261da177e4SLinus Torvalds #include <net/sock.h> 271da177e4SLinus Torvalds #include <net/xfrm.h> 2888fc2c84SThomas Graf #include <net/netlink.h> 291da177e4SLinus Torvalds #include <asm/uaccess.h> 30e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 31e23c7194SMasahide NAKAMURA #include <linux/in6.h> 32e23c7194SMasahide NAKAMURA #endif 331da177e4SLinus Torvalds 341a6509d9SHerbert Xu static inline int aead_len(struct xfrm_algo_aead *alg) 351a6509d9SHerbert Xu { 361a6509d9SHerbert Xu return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); 371a6509d9SHerbert Xu } 381a6509d9SHerbert Xu 395424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) 401da177e4SLinus Torvalds { 415424f32eSThomas Graf struct nlattr *rt = attrs[type]; 421da177e4SLinus Torvalds struct xfrm_algo *algp; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds if (!rt) 451da177e4SLinus Torvalds return 0; 461da177e4SLinus Torvalds 475424f32eSThomas Graf algp = nla_data(rt); 480f99be0dSEric Dumazet if (nla_len(rt) < xfrm_alg_len(algp)) 4931c26852SHerbert Xu return -EINVAL; 5031c26852SHerbert Xu 511da177e4SLinus Torvalds switch (type) { 521da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 531da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 541da177e4SLinus Torvalds case XFRMA_ALG_COMP: 551da177e4SLinus Torvalds break; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds default: 581da177e4SLinus Torvalds return -EINVAL; 593ff50b79SStephen Hemminger } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 621da177e4SLinus Torvalds return 0; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651a6509d9SHerbert Xu static int verify_aead(struct nlattr **attrs) 661a6509d9SHerbert Xu { 671a6509d9SHerbert Xu struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; 681a6509d9SHerbert Xu struct xfrm_algo_aead *algp; 691a6509d9SHerbert Xu 701a6509d9SHerbert Xu if (!rt) 711a6509d9SHerbert Xu return 0; 721a6509d9SHerbert Xu 731a6509d9SHerbert Xu algp = nla_data(rt); 741a6509d9SHerbert Xu if (nla_len(rt) < aead_len(algp)) 751a6509d9SHerbert Xu return -EINVAL; 761a6509d9SHerbert Xu 771a6509d9SHerbert Xu algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 781a6509d9SHerbert Xu return 0; 791a6509d9SHerbert Xu } 801a6509d9SHerbert Xu 815424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, 82eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 83eb2971b6SMasahide NAKAMURA { 845424f32eSThomas Graf struct nlattr *rt = attrs[type]; 85eb2971b6SMasahide NAKAMURA 86cf5cb79fSThomas Graf if (rt && addrp) 875424f32eSThomas Graf *addrp = nla_data(rt); 88eb2971b6SMasahide NAKAMURA } 89df71837dSTrent Jaeger 905424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs) 91df71837dSTrent Jaeger { 925424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 93df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 94df71837dSTrent Jaeger 95df71837dSTrent Jaeger if (!rt) 96df71837dSTrent Jaeger return 0; 97df71837dSTrent Jaeger 985424f32eSThomas Graf uctx = nla_data(rt); 99cf5cb79fSThomas Graf if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) 100df71837dSTrent Jaeger return -EINVAL; 101df71837dSTrent Jaeger 102df71837dSTrent Jaeger return 0; 103df71837dSTrent Jaeger } 104df71837dSTrent Jaeger 105df71837dSTrent Jaeger 1061da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1075424f32eSThomas Graf struct nlattr **attrs) 1081da177e4SLinus Torvalds { 1091da177e4SLinus Torvalds int err; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds err = -EINVAL; 1121da177e4SLinus Torvalds switch (p->family) { 1131da177e4SLinus Torvalds case AF_INET: 1141da177e4SLinus Torvalds break; 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds case AF_INET6: 1171da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1181da177e4SLinus Torvalds break; 1191da177e4SLinus Torvalds #else 1201da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1211da177e4SLinus Torvalds goto out; 1221da177e4SLinus Torvalds #endif 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds default: 1251da177e4SLinus Torvalds goto out; 1263ff50b79SStephen Hemminger } 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds err = -EINVAL; 1291da177e4SLinus Torvalds switch (p->id.proto) { 1301da177e4SLinus Torvalds case IPPROTO_AH: 13135a7aa08SThomas Graf if (!attrs[XFRMA_ALG_AUTH] || 1321a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 13335a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 13435a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]) 1351da177e4SLinus Torvalds goto out; 1361da177e4SLinus Torvalds break; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds case IPPROTO_ESP: 1391a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1401a6509d9SHerbert Xu goto out; 1411a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1421a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1431a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1441a6509d9SHerbert Xu goto out; 1451a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1461a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1471a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1481da177e4SLinus Torvalds goto out; 1491da177e4SLinus Torvalds break; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds case IPPROTO_COMP: 15235a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 1531a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 15435a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 15535a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]) 1561da177e4SLinus Torvalds goto out; 1571da177e4SLinus Torvalds break; 1581da177e4SLinus Torvalds 159e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 160e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 161e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 16235a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 16335a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1641a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 16535a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 16635a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 16735a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 16835a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 169e23c7194SMasahide NAKAMURA goto out; 170e23c7194SMasahide NAKAMURA break; 171e23c7194SMasahide NAKAMURA #endif 172e23c7194SMasahide NAKAMURA 1731da177e4SLinus Torvalds default: 1741da177e4SLinus Torvalds goto out; 1753ff50b79SStephen Hemminger } 1761da177e4SLinus Torvalds 1771a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 1781a6509d9SHerbert Xu goto out; 17935a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 1801da177e4SLinus Torvalds goto out; 18135a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 1821da177e4SLinus Torvalds goto out; 18335a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 1841da177e4SLinus Torvalds goto out; 18535a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 186df71837dSTrent Jaeger goto out; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds err = -EINVAL; 1891da177e4SLinus Torvalds switch (p->mode) { 1907e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 1917e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 192060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 1930a69452cSDiego Beltrami case XFRM_MODE_BEET: 1941da177e4SLinus Torvalds break; 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds default: 1971da177e4SLinus Torvalds goto out; 1983ff50b79SStephen Hemminger } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds err = 0; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds out: 2031da177e4SLinus Torvalds return err; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2071da177e4SLinus Torvalds struct xfrm_algo_desc *(*get_byname)(char *, int), 2085424f32eSThomas Graf struct nlattr *rta) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2111da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds if (!rta) 2141da177e4SLinus Torvalds return 0; 2151da177e4SLinus Torvalds 2165424f32eSThomas Graf ualg = nla_data(rta); 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2191da177e4SLinus Torvalds if (!algo) 2201da177e4SLinus Torvalds return -ENOSYS; 2211da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2221da177e4SLinus Torvalds 2230f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2241da177e4SLinus Torvalds if (!p) 2251da177e4SLinus Torvalds return -ENOMEM; 2261da177e4SLinus Torvalds 22704ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2281da177e4SLinus Torvalds *algpp = p; 2291da177e4SLinus Torvalds return 0; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 2321a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 2331a6509d9SHerbert Xu struct nlattr *rta) 2341a6509d9SHerbert Xu { 2351a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 2361a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 2371a6509d9SHerbert Xu 2381a6509d9SHerbert Xu if (!rta) 2391a6509d9SHerbert Xu return 0; 2401a6509d9SHerbert Xu 2411a6509d9SHerbert Xu ualg = nla_data(rta); 2421a6509d9SHerbert Xu 2431a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 2441a6509d9SHerbert Xu if (!algo) 2451a6509d9SHerbert Xu return -ENOSYS; 2461a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 2471a6509d9SHerbert Xu 2481a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 2491a6509d9SHerbert Xu if (!p) 2501a6509d9SHerbert Xu return -ENOMEM; 2511a6509d9SHerbert Xu 2521a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 2531a6509d9SHerbert Xu *algpp = p; 2541a6509d9SHerbert Xu return 0; 2551a6509d9SHerbert Xu } 2561a6509d9SHerbert Xu 257661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 258df71837dSTrent Jaeger { 259df71837dSTrent Jaeger int len = 0; 260df71837dSTrent Jaeger 261df71837dSTrent Jaeger if (xfrm_ctx) { 262df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 263df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 264df71837dSTrent Jaeger } 265df71837dSTrent Jaeger return len; 266df71837dSTrent Jaeger } 267df71837dSTrent Jaeger 2681da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 2691da177e4SLinus Torvalds { 2701da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 2711da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 2721da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 2731da177e4SLinus Torvalds x->props.mode = p->mode; 2741da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 2751da177e4SLinus Torvalds x->props.reqid = p->reqid; 2761da177e4SLinus Torvalds x->props.family = p->family; 27754489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 2781da177e4SLinus Torvalds x->props.flags = p->flags; 279196b0036SHerbert Xu 280ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 281196b0036SHerbert Xu x->sel.family = p->family; 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds 284d51d081dSJamal Hadi Salim /* 285d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 286d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 287d51d081dSJamal Hadi Salim * 288d51d081dSJamal Hadi Salim */ 2895424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) 290d51d081dSJamal Hadi Salim { 2915424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 2925424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 2935424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 2945424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 295d51d081dSJamal Hadi Salim 296d51d081dSJamal Hadi Salim if (rp) { 297d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 2985424f32eSThomas Graf replay = nla_data(rp); 299d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 300d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 301d51d081dSJamal Hadi Salim } 302d51d081dSJamal Hadi Salim 303d51d081dSJamal Hadi Salim if (lt) { 304d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 3055424f32eSThomas Graf ltime = nla_data(lt); 306d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 307d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 308d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 309d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 310d51d081dSJamal Hadi Salim } 311d51d081dSJamal Hadi Salim 312cf5cb79fSThomas Graf if (et) 3135424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 314d51d081dSJamal Hadi Salim 315cf5cb79fSThomas Graf if (rt) 3165424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 317d51d081dSJamal Hadi Salim } 318d51d081dSJamal Hadi Salim 319*fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 320*fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 3215424f32eSThomas Graf struct nlattr **attrs, 3221da177e4SLinus Torvalds int *errp) 3231da177e4SLinus Torvalds { 324*fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 3251da177e4SLinus Torvalds int err = -ENOMEM; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds if (!x) 3281da177e4SLinus Torvalds goto error_no_put; 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds copy_from_user_state(x, p); 3311da177e4SLinus Torvalds 3321a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 3331a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 3341a6509d9SHerbert Xu goto error; 3351da177e4SLinus Torvalds if ((err = attach_one_algo(&x->aalg, &x->props.aalgo, 3361da177e4SLinus Torvalds xfrm_aalg_get_byname, 33735a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 3381da177e4SLinus Torvalds goto error; 3391da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 3401da177e4SLinus Torvalds xfrm_ealg_get_byname, 34135a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 3421da177e4SLinus Torvalds goto error; 3431da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 3441da177e4SLinus Torvalds xfrm_calg_get_byname, 34535a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 3461da177e4SLinus Torvalds goto error; 347fd21150aSThomas Graf 348fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 349fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 350fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 351fd21150aSThomas Graf if (x->encap == NULL) 3521da177e4SLinus Torvalds goto error; 353fd21150aSThomas Graf } 354fd21150aSThomas Graf 355fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 356fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 357fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 358fd21150aSThomas Graf if (x->coaddr == NULL) 359060f02a3SNoriaki TAKAMIYA goto error; 360fd21150aSThomas Graf } 361fd21150aSThomas Graf 36272cb6962SHerbert Xu err = xfrm_init_state(x); 3631da177e4SLinus Torvalds if (err) 3641da177e4SLinus Torvalds goto error; 3651da177e4SLinus Torvalds 366fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 367fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 368df71837dSTrent Jaeger goto error; 369df71837dSTrent Jaeger 3701da177e4SLinus Torvalds x->km.seq = p->seq; 371d51d081dSJamal Hadi Salim x->replay_maxdiff = sysctl_xfrm_aevent_rseqth; 372d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 373d51d081dSJamal Hadi Salim x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M; 374d51d081dSJamal Hadi Salim x->preplay.bitmap = 0; 375d51d081dSJamal Hadi Salim x->preplay.seq = x->replay.seq+x->replay_maxdiff; 376d51d081dSJamal Hadi Salim x->preplay.oseq = x->replay.oseq +x->replay_maxdiff; 377d51d081dSJamal Hadi Salim 378d51d081dSJamal Hadi Salim /* override default values from above */ 379d51d081dSJamal Hadi Salim 3805424f32eSThomas Graf xfrm_update_ae_params(x, attrs); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds return x; 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds error: 3851da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 3861da177e4SLinus Torvalds xfrm_state_put(x); 3871da177e4SLinus Torvalds error_no_put: 3881da177e4SLinus Torvalds *errp = err; 3891da177e4SLinus Torvalds return NULL; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds 39222e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 3935424f32eSThomas Graf struct nlattr **attrs) 3941da177e4SLinus Torvalds { 395*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 3967b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 3971da177e4SLinus Torvalds struct xfrm_state *x; 3981da177e4SLinus Torvalds int err; 39926b15dadSJamal Hadi Salim struct km_event c; 4002532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 4012532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 4022532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 4031da177e4SLinus Torvalds 40435a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 4051da177e4SLinus Torvalds if (err) 4061da177e4SLinus Torvalds return err; 4071da177e4SLinus Torvalds 408*fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 4091da177e4SLinus Torvalds if (!x) 4101da177e4SLinus Torvalds return err; 4111da177e4SLinus Torvalds 41226b15dadSJamal Hadi Salim xfrm_state_hold(x); 4131da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 4141da177e4SLinus Torvalds err = xfrm_state_add(x); 4151da177e4SLinus Torvalds else 4161da177e4SLinus Torvalds err = xfrm_state_update(x); 4171da177e4SLinus Torvalds 4182532386fSEric Paris xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); 419161a09e7SJoy Latten 4201da177e4SLinus Torvalds if (err < 0) { 4211da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 42221380b81SHerbert Xu __xfrm_state_put(x); 4237d6dfe1fSPatrick McHardy goto out; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 42626b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 42726b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 428f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 42926b15dadSJamal Hadi Salim 43026b15dadSJamal Hadi Salim km_state_notify(x, &c); 4317d6dfe1fSPatrick McHardy out: 43226b15dadSJamal Hadi Salim xfrm_state_put(x); 4331da177e4SLinus Torvalds return err; 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds 436*fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 437*fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 4385424f32eSThomas Graf struct nlattr **attrs, 439eb2971b6SMasahide NAKAMURA int *errp) 440eb2971b6SMasahide NAKAMURA { 441eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 442eb2971b6SMasahide NAKAMURA int err; 443eb2971b6SMasahide NAKAMURA 444eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 445eb2971b6SMasahide NAKAMURA err = -ESRCH; 446*fc34acd3SAlexey Dobriyan x = xfrm_state_lookup(net, &p->daddr, p->spi, p->proto, p->family); 447eb2971b6SMasahide NAKAMURA } else { 448eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 449eb2971b6SMasahide NAKAMURA 45035a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 451eb2971b6SMasahide NAKAMURA if (!saddr) { 452eb2971b6SMasahide NAKAMURA err = -EINVAL; 453eb2971b6SMasahide NAKAMURA goto out; 454eb2971b6SMasahide NAKAMURA } 455eb2971b6SMasahide NAKAMURA 4569abbffeeSMasahide NAKAMURA err = -ESRCH; 457*fc34acd3SAlexey Dobriyan x = xfrm_state_lookup_byaddr(net, &p->daddr, saddr, 458221df1edSAlexey Dobriyan p->proto, p->family); 459eb2971b6SMasahide NAKAMURA } 460eb2971b6SMasahide NAKAMURA 461eb2971b6SMasahide NAKAMURA out: 462eb2971b6SMasahide NAKAMURA if (!x && errp) 463eb2971b6SMasahide NAKAMURA *errp = err; 464eb2971b6SMasahide NAKAMURA return x; 465eb2971b6SMasahide NAKAMURA } 466eb2971b6SMasahide NAKAMURA 46722e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 4685424f32eSThomas Graf struct nlattr **attrs) 4691da177e4SLinus Torvalds { 470*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 4711da177e4SLinus Torvalds struct xfrm_state *x; 472eb2971b6SMasahide NAKAMURA int err = -ESRCH; 47326b15dadSJamal Hadi Salim struct km_event c; 4747b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 4752532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 4762532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 4772532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 4781da177e4SLinus Torvalds 479*fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 4801da177e4SLinus Torvalds if (x == NULL) 481eb2971b6SMasahide NAKAMURA return err; 4821da177e4SLinus Torvalds 4836f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 484c8c05a8eSCatherine Zhang goto out; 485c8c05a8eSCatherine Zhang 4861da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 487c8c05a8eSCatherine Zhang err = -EPERM; 488c8c05a8eSCatherine Zhang goto out; 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds 49126b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 492161a09e7SJoy Latten 493c8c05a8eSCatherine Zhang if (err < 0) 494c8c05a8eSCatherine Zhang goto out; 49526b15dadSJamal Hadi Salim 49626b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 49726b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 498f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 49926b15dadSJamal Hadi Salim km_state_notify(x, &c); 5001da177e4SLinus Torvalds 501c8c05a8eSCatherine Zhang out: 5022532386fSEric Paris xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); 503c8c05a8eSCatherine Zhang xfrm_state_put(x); 50426b15dadSJamal Hadi Salim return err; 5051da177e4SLinus Torvalds } 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 5081da177e4SLinus Torvalds { 5091da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 5101da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 5111da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 5121da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 5131da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 51454489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 5151da177e4SLinus Torvalds p->mode = x->props.mode; 5161da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 5171da177e4SLinus Torvalds p->reqid = x->props.reqid; 5181da177e4SLinus Torvalds p->family = x->props.family; 5191da177e4SLinus Torvalds p->flags = x->props.flags; 5201da177e4SLinus Torvalds p->seq = x->km.seq; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds struct xfrm_dump_info { 5241da177e4SLinus Torvalds struct sk_buff *in_skb; 5251da177e4SLinus Torvalds struct sk_buff *out_skb; 5261da177e4SLinus Torvalds u32 nlmsg_seq; 5271da177e4SLinus Torvalds u16 nlmsg_flags; 5281da177e4SLinus Torvalds }; 5291da177e4SLinus Torvalds 530c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 531c0144beaSThomas Graf { 532c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 533c0144beaSThomas Graf struct nlattr *attr; 53468325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 535c0144beaSThomas Graf 536c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 537c0144beaSThomas Graf if (attr == NULL) 538c0144beaSThomas Graf return -EMSGSIZE; 539c0144beaSThomas Graf 540c0144beaSThomas Graf uctx = nla_data(attr); 541c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 542c0144beaSThomas Graf uctx->len = ctx_size; 543c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 544c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 545c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 546c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 547c0144beaSThomas Graf 548c0144beaSThomas Graf return 0; 549c0144beaSThomas Graf } 550c0144beaSThomas Graf 55168325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 55268325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 55368325d3bSHerbert Xu struct xfrm_usersa_info *p, 55468325d3bSHerbert Xu struct sk_buff *skb) 5551da177e4SLinus Torvalds { 5561da177e4SLinus Torvalds copy_to_user_state(x, p); 5571da177e4SLinus Torvalds 558050f009eSHerbert Xu if (x->coaddr) 559050f009eSHerbert Xu NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 560050f009eSHerbert Xu 561050f009eSHerbert Xu if (x->lastused) 562050f009eSHerbert Xu NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); 563050f009eSHerbert Xu 5641a6509d9SHerbert Xu if (x->aead) 5651a6509d9SHerbert Xu NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 5661da177e4SLinus Torvalds if (x->aalg) 5670f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_AUTH, xfrm_alg_len(x->aalg), x->aalg); 5681da177e4SLinus Torvalds if (x->ealg) 5690f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 5701da177e4SLinus Torvalds if (x->calg) 571c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds if (x->encap) 574c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 5751da177e4SLinus Torvalds 576c0144beaSThomas Graf if (x->security && copy_sec_ctx(x->security, skb) < 0) 577c0144beaSThomas Graf goto nla_put_failure; 578060f02a3SNoriaki TAKAMIYA 57968325d3bSHerbert Xu return 0; 58068325d3bSHerbert Xu 58168325d3bSHerbert Xu nla_put_failure: 58268325d3bSHerbert Xu return -EMSGSIZE; 58368325d3bSHerbert Xu } 58468325d3bSHerbert Xu 58568325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 58668325d3bSHerbert Xu { 58768325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 58868325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 58968325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 59068325d3bSHerbert Xu struct xfrm_usersa_info *p; 59168325d3bSHerbert Xu struct nlmsghdr *nlh; 59268325d3bSHerbert Xu int err; 59368325d3bSHerbert Xu 59468325d3bSHerbert Xu nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 59568325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 59668325d3bSHerbert Xu if (nlh == NULL) 59768325d3bSHerbert Xu return -EMSGSIZE; 59868325d3bSHerbert Xu 59968325d3bSHerbert Xu p = nlmsg_data(nlh); 60068325d3bSHerbert Xu 60168325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 60268325d3bSHerbert Xu if (err) 60368325d3bSHerbert Xu goto nla_put_failure; 60468325d3bSHerbert Xu 6059825069dSThomas Graf nlmsg_end(skb, nlh); 6061da177e4SLinus Torvalds return 0; 6071da177e4SLinus Torvalds 608c0144beaSThomas Graf nla_put_failure: 6099825069dSThomas Graf nlmsg_cancel(skb, nlh); 61068325d3bSHerbert Xu return err; 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds 6134c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 6144c563f76STimo Teras { 6154c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 6164c563f76STimo Teras xfrm_state_walk_done(walk); 6174c563f76STimo Teras return 0; 6184c563f76STimo Teras } 6194c563f76STimo Teras 6201da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 6211da177e4SLinus Torvalds { 622*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6234c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 6241da177e4SLinus Torvalds struct xfrm_dump_info info; 6251da177e4SLinus Torvalds 6264c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 6274c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 6284c563f76STimo Teras 6291da177e4SLinus Torvalds info.in_skb = cb->skb; 6301da177e4SLinus Torvalds info.out_skb = skb; 6311da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 6321da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 6334c563f76STimo Teras 6344c563f76STimo Teras if (!cb->args[0]) { 6354c563f76STimo Teras cb->args[0] = 1; 6364c563f76STimo Teras xfrm_state_walk_init(walk, 0); 6374c563f76STimo Teras } 6384c563f76STimo Teras 639*fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds return skb->len; 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds 6441da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 6451da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 6461da177e4SLinus Torvalds { 6471da177e4SLinus Torvalds struct xfrm_dump_info info; 6481da177e4SLinus Torvalds struct sk_buff *skb; 6491da177e4SLinus Torvalds 6507deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 6511da177e4SLinus Torvalds if (!skb) 6521da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds info.in_skb = in_skb; 6551da177e4SLinus Torvalds info.out_skb = skb; 6561da177e4SLinus Torvalds info.nlmsg_seq = seq; 6571da177e4SLinus Torvalds info.nlmsg_flags = 0; 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds if (dump_one_state(x, 0, &info)) { 6601da177e4SLinus Torvalds kfree_skb(skb); 6611da177e4SLinus Torvalds return NULL; 6621da177e4SLinus Torvalds } 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds return skb; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6677deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 6687deb2264SThomas Graf { 6697deb2264SThomas Graf return NLMSG_ALIGN(4) 6707deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 6717deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 6727deb2264SThomas Graf } 6737deb2264SThomas Graf 674ecfd6b18SJamal Hadi Salim static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) 675ecfd6b18SJamal Hadi Salim { 6765a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 6775a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 6785a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 679ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 680ecfd6b18SJamal Hadi Salim u32 *f; 681ecfd6b18SJamal Hadi Salim 682ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 683ecfd6b18SJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 684ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 685ecfd6b18SJamal Hadi Salim 686ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 687ecfd6b18SJamal Hadi Salim *f = flags; 688ecfd6b18SJamal Hadi Salim xfrm_spd_getinfo(&si); 6895a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 6905a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 6915a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 6925a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 6935a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 6945a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 6955a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 6965a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 697ecfd6b18SJamal Hadi Salim 6985a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 6995a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 700ecfd6b18SJamal Hadi Salim 701ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 702ecfd6b18SJamal Hadi Salim 703ecfd6b18SJamal Hadi Salim nla_put_failure: 704ecfd6b18SJamal Hadi Salim nlmsg_cancel(skb, nlh); 705ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 706ecfd6b18SJamal Hadi Salim } 707ecfd6b18SJamal Hadi Salim 708ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 7095424f32eSThomas Graf struct nlattr **attrs) 710ecfd6b18SJamal Hadi Salim { 711a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 712ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 7137b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 714ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 715ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 716ecfd6b18SJamal Hadi Salim 7177deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 718ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 719ecfd6b18SJamal Hadi Salim return -ENOMEM; 720ecfd6b18SJamal Hadi Salim 721ecfd6b18SJamal Hadi Salim if (build_spdinfo(r_skb, spid, seq, *flags) < 0) 722ecfd6b18SJamal Hadi Salim BUG(); 723ecfd6b18SJamal Hadi Salim 724a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 725ecfd6b18SJamal Hadi Salim } 726ecfd6b18SJamal Hadi Salim 7277deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 7287deb2264SThomas Graf { 7297deb2264SThomas Graf return NLMSG_ALIGN(4) 7307deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 7317deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 7327deb2264SThomas Graf } 7337deb2264SThomas Graf 73428d8909bSJamal Hadi Salim static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) 73528d8909bSJamal Hadi Salim { 736af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 737af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 73828d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 73928d8909bSJamal Hadi Salim u32 *f; 74028d8909bSJamal Hadi Salim 74128d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 74228d8909bSJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 74328d8909bSJamal Hadi Salim return -EMSGSIZE; 74428d8909bSJamal Hadi Salim 74528d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 74628d8909bSJamal Hadi Salim *f = flags; 74728d8909bSJamal Hadi Salim xfrm_sad_getinfo(&si); 74828d8909bSJamal Hadi Salim 749af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 750af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 751af11e316SJamal Hadi Salim 752af11e316SJamal Hadi Salim NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); 753af11e316SJamal Hadi Salim NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 75428d8909bSJamal Hadi Salim 75528d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 75628d8909bSJamal Hadi Salim 75728d8909bSJamal Hadi Salim nla_put_failure: 75828d8909bSJamal Hadi Salim nlmsg_cancel(skb, nlh); 75928d8909bSJamal Hadi Salim return -EMSGSIZE; 76028d8909bSJamal Hadi Salim } 76128d8909bSJamal Hadi Salim 76228d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 7635424f32eSThomas Graf struct nlattr **attrs) 76428d8909bSJamal Hadi Salim { 765a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 76628d8909bSJamal Hadi Salim struct sk_buff *r_skb; 7677b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 76828d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 76928d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 77028d8909bSJamal Hadi Salim 7717deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 77228d8909bSJamal Hadi Salim if (r_skb == NULL) 77328d8909bSJamal Hadi Salim return -ENOMEM; 77428d8909bSJamal Hadi Salim 77528d8909bSJamal Hadi Salim if (build_sadinfo(r_skb, spid, seq, *flags) < 0) 77628d8909bSJamal Hadi Salim BUG(); 77728d8909bSJamal Hadi Salim 778a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 77928d8909bSJamal Hadi Salim } 78028d8909bSJamal Hadi Salim 78122e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 7825424f32eSThomas Graf struct nlattr **attrs) 7831da177e4SLinus Torvalds { 784*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 7857b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 7861da177e4SLinus Torvalds struct xfrm_state *x; 7871da177e4SLinus Torvalds struct sk_buff *resp_skb; 788eb2971b6SMasahide NAKAMURA int err = -ESRCH; 7891da177e4SLinus Torvalds 790*fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 7911da177e4SLinus Torvalds if (x == NULL) 7921da177e4SLinus Torvalds goto out_noput; 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 7951da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 7961da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 7971da177e4SLinus Torvalds } else { 798a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds xfrm_state_put(x); 8011da177e4SLinus Torvalds out_noput: 8021da177e4SLinus Torvalds return err; 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 8061da177e4SLinus Torvalds { 8071da177e4SLinus Torvalds switch (p->info.id.proto) { 8081da177e4SLinus Torvalds case IPPROTO_AH: 8091da177e4SLinus Torvalds case IPPROTO_ESP: 8101da177e4SLinus Torvalds break; 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds case IPPROTO_COMP: 8131da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 8141da177e4SLinus Torvalds if (p->max >= 0x10000) 8151da177e4SLinus Torvalds return -EINVAL; 8161da177e4SLinus Torvalds break; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds default: 8191da177e4SLinus Torvalds return -EINVAL; 8203ff50b79SStephen Hemminger } 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds if (p->min > p->max) 8231da177e4SLinus Torvalds return -EINVAL; 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds return 0; 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds 82822e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 8295424f32eSThomas Graf struct nlattr **attrs) 8301da177e4SLinus Torvalds { 831*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 8321da177e4SLinus Torvalds struct xfrm_state *x; 8331da177e4SLinus Torvalds struct xfrm_userspi_info *p; 8341da177e4SLinus Torvalds struct sk_buff *resp_skb; 8351da177e4SLinus Torvalds xfrm_address_t *daddr; 8361da177e4SLinus Torvalds int family; 8371da177e4SLinus Torvalds int err; 8381da177e4SLinus Torvalds 8397b67c857SThomas Graf p = nlmsg_data(nlh); 8401da177e4SLinus Torvalds err = verify_userspi_info(p); 8411da177e4SLinus Torvalds if (err) 8421da177e4SLinus Torvalds goto out_noput; 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds family = p->info.family; 8451da177e4SLinus Torvalds daddr = &p->info.id.daddr; 8461da177e4SLinus Torvalds 8471da177e4SLinus Torvalds x = NULL; 8481da177e4SLinus Torvalds if (p->info.seq) { 849a6483b79SAlexey Dobriyan x = xfrm_find_acq_byseq(net, p->info.seq); 8501da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 8511da177e4SLinus Torvalds xfrm_state_put(x); 8521da177e4SLinus Torvalds x = NULL; 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds 8561da177e4SLinus Torvalds if (!x) 857a6483b79SAlexey Dobriyan x = xfrm_find_acq(net, p->info.mode, p->info.reqid, 8581da177e4SLinus Torvalds p->info.id.proto, daddr, 8591da177e4SLinus Torvalds &p->info.saddr, 1, 8601da177e4SLinus Torvalds family); 8611da177e4SLinus Torvalds err = -ENOENT; 8621da177e4SLinus Torvalds if (x == NULL) 8631da177e4SLinus Torvalds goto out_noput; 8641da177e4SLinus Torvalds 865658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 866658b219eSHerbert Xu if (err) 867658b219eSHerbert Xu goto out; 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 8701da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 8711da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 8721da177e4SLinus Torvalds goto out; 8731da177e4SLinus Torvalds } 8741da177e4SLinus Torvalds 875a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 8761da177e4SLinus Torvalds 8771da177e4SLinus Torvalds out: 8781da177e4SLinus Torvalds xfrm_state_put(x); 8791da177e4SLinus Torvalds out_noput: 8801da177e4SLinus Torvalds return err; 8811da177e4SLinus Torvalds } 8821da177e4SLinus Torvalds 883b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 8841da177e4SLinus Torvalds { 8851da177e4SLinus Torvalds switch (dir) { 8861da177e4SLinus Torvalds case XFRM_POLICY_IN: 8871da177e4SLinus Torvalds case XFRM_POLICY_OUT: 8881da177e4SLinus Torvalds case XFRM_POLICY_FWD: 8891da177e4SLinus Torvalds break; 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds default: 8921da177e4SLinus Torvalds return -EINVAL; 8933ff50b79SStephen Hemminger } 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds return 0; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds 898b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 899f7b6983fSMasahide NAKAMURA { 900f7b6983fSMasahide NAKAMURA switch (type) { 901f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 902f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 903f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 904f7b6983fSMasahide NAKAMURA #endif 905f7b6983fSMasahide NAKAMURA break; 906f7b6983fSMasahide NAKAMURA 907f7b6983fSMasahide NAKAMURA default: 908f7b6983fSMasahide NAKAMURA return -EINVAL; 9093ff50b79SStephen Hemminger } 910f7b6983fSMasahide NAKAMURA 911f7b6983fSMasahide NAKAMURA return 0; 912f7b6983fSMasahide NAKAMURA } 913f7b6983fSMasahide NAKAMURA 9141da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 9151da177e4SLinus Torvalds { 9161da177e4SLinus Torvalds switch (p->share) { 9171da177e4SLinus Torvalds case XFRM_SHARE_ANY: 9181da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 9191da177e4SLinus Torvalds case XFRM_SHARE_USER: 9201da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 9211da177e4SLinus Torvalds break; 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds default: 9241da177e4SLinus Torvalds return -EINVAL; 9253ff50b79SStephen Hemminger } 9261da177e4SLinus Torvalds 9271da177e4SLinus Torvalds switch (p->action) { 9281da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 9291da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 9301da177e4SLinus Torvalds break; 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds default: 9331da177e4SLinus Torvalds return -EINVAL; 9343ff50b79SStephen Hemminger } 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds switch (p->sel.family) { 9371da177e4SLinus Torvalds case AF_INET: 9381da177e4SLinus Torvalds break; 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds case AF_INET6: 9411da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 9421da177e4SLinus Torvalds break; 9431da177e4SLinus Torvalds #else 9441da177e4SLinus Torvalds return -EAFNOSUPPORT; 9451da177e4SLinus Torvalds #endif 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds default: 9481da177e4SLinus Torvalds return -EINVAL; 9493ff50b79SStephen Hemminger } 9501da177e4SLinus Torvalds 9511da177e4SLinus Torvalds return verify_policy_dir(p->dir); 9521da177e4SLinus Torvalds } 9531da177e4SLinus Torvalds 9545424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 955df71837dSTrent Jaeger { 9565424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 957df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 958df71837dSTrent Jaeger 959df71837dSTrent Jaeger if (!rt) 960df71837dSTrent Jaeger return 0; 961df71837dSTrent Jaeger 9625424f32eSThomas Graf uctx = nla_data(rt); 96303e1ad7bSPaul Moore return security_xfrm_policy_alloc(&pol->security, uctx); 964df71837dSTrent Jaeger } 965df71837dSTrent Jaeger 9661da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 9671da177e4SLinus Torvalds int nr) 9681da177e4SLinus Torvalds { 9691da177e4SLinus Torvalds int i; 9701da177e4SLinus Torvalds 9711da177e4SLinus Torvalds xp->xfrm_nr = nr; 9721da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 9731da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 9741da177e4SLinus Torvalds 9751da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 9761da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 9771da177e4SLinus Torvalds sizeof(xfrm_address_t)); 9781da177e4SLinus Torvalds t->reqid = ut->reqid; 9791da177e4SLinus Torvalds t->mode = ut->mode; 9801da177e4SLinus Torvalds t->share = ut->share; 9811da177e4SLinus Torvalds t->optional = ut->optional; 9821da177e4SLinus Torvalds t->aalgos = ut->aalgos; 9831da177e4SLinus Torvalds t->ealgos = ut->ealgos; 9841da177e4SLinus Torvalds t->calgos = ut->calgos; 985c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 986c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 9878511d01dSMiika Komu t->encap_family = ut->family; 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds } 9901da177e4SLinus Torvalds 991b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 992b4ad86bfSDavid S. Miller { 993b4ad86bfSDavid S. Miller int i; 994b4ad86bfSDavid S. Miller 995b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 996b4ad86bfSDavid S. Miller return -EINVAL; 997b4ad86bfSDavid S. Miller 998b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 999b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1000b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1001b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1002b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1003b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1004b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1005b4ad86bfSDavid S. Miller */ 1006b4ad86bfSDavid S. Miller if (!ut[i].family) 1007b4ad86bfSDavid S. Miller ut[i].family = family; 1008b4ad86bfSDavid S. Miller 1009b4ad86bfSDavid S. Miller switch (ut[i].family) { 1010b4ad86bfSDavid S. Miller case AF_INET: 1011b4ad86bfSDavid S. Miller break; 1012b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1013b4ad86bfSDavid S. Miller case AF_INET6: 1014b4ad86bfSDavid S. Miller break; 1015b4ad86bfSDavid S. Miller #endif 1016b4ad86bfSDavid S. Miller default: 1017b4ad86bfSDavid S. Miller return -EINVAL; 10183ff50b79SStephen Hemminger } 1019b4ad86bfSDavid S. Miller } 1020b4ad86bfSDavid S. Miller 1021b4ad86bfSDavid S. Miller return 0; 1022b4ad86bfSDavid S. Miller } 1023b4ad86bfSDavid S. Miller 10245424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 10251da177e4SLinus Torvalds { 10265424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds if (!rt) { 10291da177e4SLinus Torvalds pol->xfrm_nr = 0; 10301da177e4SLinus Torvalds } else { 10315424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 10325424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1033b4ad86bfSDavid S. Miller int err; 10341da177e4SLinus Torvalds 1035b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1036b4ad86bfSDavid S. Miller if (err) 1037b4ad86bfSDavid S. Miller return err; 10381da177e4SLinus Torvalds 10395424f32eSThomas Graf copy_templates(pol, utmpl, nr); 10401da177e4SLinus Torvalds } 10411da177e4SLinus Torvalds return 0; 10421da177e4SLinus Torvalds } 10431da177e4SLinus Torvalds 10445424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1045f7b6983fSMasahide NAKAMURA { 10465424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1047f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1048b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1049f7b6983fSMasahide NAKAMURA int err; 1050f7b6983fSMasahide NAKAMURA 1051f7b6983fSMasahide NAKAMURA if (rt) { 10525424f32eSThomas Graf upt = nla_data(rt); 1053f7b6983fSMasahide NAKAMURA type = upt->type; 1054f7b6983fSMasahide NAKAMURA } 1055f7b6983fSMasahide NAKAMURA 1056f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1057f7b6983fSMasahide NAKAMURA if (err) 1058f7b6983fSMasahide NAKAMURA return err; 1059f7b6983fSMasahide NAKAMURA 1060f7b6983fSMasahide NAKAMURA *tp = type; 1061f7b6983fSMasahide NAKAMURA return 0; 1062f7b6983fSMasahide NAKAMURA } 1063f7b6983fSMasahide NAKAMURA 10641da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 10651da177e4SLinus Torvalds { 10661da177e4SLinus Torvalds xp->priority = p->priority; 10671da177e4SLinus Torvalds xp->index = p->index; 10681da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 10691da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 10701da177e4SLinus Torvalds xp->action = p->action; 10711da177e4SLinus Torvalds xp->flags = p->flags; 10721da177e4SLinus Torvalds xp->family = p->sel.family; 10731da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 10741da177e4SLinus Torvalds } 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 10771da177e4SLinus Torvalds { 10781da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 10791da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 10801da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 10811da177e4SLinus Torvalds p->priority = xp->priority; 10821da177e4SLinus Torvalds p->index = xp->index; 10831da177e4SLinus Torvalds p->sel.family = xp->family; 10841da177e4SLinus Torvalds p->dir = dir; 10851da177e4SLinus Torvalds p->action = xp->action; 10861da177e4SLinus Torvalds p->flags = xp->flags; 10871da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 10881da177e4SLinus Torvalds } 10891da177e4SLinus Torvalds 1090*fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 10911da177e4SLinus Torvalds { 1092*fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 10931da177e4SLinus Torvalds int err; 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds if (!xp) { 10961da177e4SLinus Torvalds *errp = -ENOMEM; 10971da177e4SLinus Torvalds return NULL; 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds 11001da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1101df71837dSTrent Jaeger 110235a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1103f7b6983fSMasahide NAKAMURA if (err) 1104f7b6983fSMasahide NAKAMURA goto error; 1105f7b6983fSMasahide NAKAMURA 110635a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 110735a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1108f7b6983fSMasahide NAKAMURA if (err) 1109f7b6983fSMasahide NAKAMURA goto error; 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds return xp; 1112f7b6983fSMasahide NAKAMURA error: 1113f7b6983fSMasahide NAKAMURA *errp = err; 111412a169e7SHerbert Xu xp->walk.dead = 1; 111564c31b3fSWANG Cong xfrm_policy_destroy(xp); 1116f7b6983fSMasahide NAKAMURA return NULL; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds 111922e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 11205424f32eSThomas Graf struct nlattr **attrs) 11211da177e4SLinus Torvalds { 1122*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 11237b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 11241da177e4SLinus Torvalds struct xfrm_policy *xp; 112526b15dadSJamal Hadi Salim struct km_event c; 11261da177e4SLinus Torvalds int err; 11271da177e4SLinus Torvalds int excl; 11282532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 11292532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 11302532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds err = verify_newpolicy_info(p); 11331da177e4SLinus Torvalds if (err) 11341da177e4SLinus Torvalds return err; 113535a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1136df71837dSTrent Jaeger if (err) 1137df71837dSTrent Jaeger return err; 11381da177e4SLinus Torvalds 1139*fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 11401da177e4SLinus Torvalds if (!xp) 11411da177e4SLinus Torvalds return err; 11421da177e4SLinus Torvalds 114326b15dadSJamal Hadi Salim /* shouldnt excl be based on nlh flags?? 114426b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 114526b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 114626b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 11471da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 11481da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 11492532386fSEric Paris xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); 1150161a09e7SJoy Latten 11511da177e4SLinus Torvalds if (err) { 115203e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 11531da177e4SLinus Torvalds kfree(xp); 11541da177e4SLinus Torvalds return err; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 1157f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 115826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 115926b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 116026b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 116126b15dadSJamal Hadi Salim 11621da177e4SLinus Torvalds xfrm_pol_put(xp); 11631da177e4SLinus Torvalds 11641da177e4SLinus Torvalds return 0; 11651da177e4SLinus Torvalds } 11661da177e4SLinus Torvalds 11671da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 11681da177e4SLinus Torvalds { 11691da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 11701da177e4SLinus Torvalds int i; 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 11731da177e4SLinus Torvalds return 0; 11741da177e4SLinus Torvalds 11751da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 11761da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 11771da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 11781da177e4SLinus Torvalds 11791da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 11808511d01dSMiika Komu up->family = kp->encap_family; 11811da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 11821da177e4SLinus Torvalds up->reqid = kp->reqid; 11831da177e4SLinus Torvalds up->mode = kp->mode; 11841da177e4SLinus Torvalds up->share = kp->share; 11851da177e4SLinus Torvalds up->optional = kp->optional; 11861da177e4SLinus Torvalds up->aalgos = kp->aalgos; 11871da177e4SLinus Torvalds up->ealgos = kp->ealgos; 11881da177e4SLinus Torvalds up->calgos = kp->calgos; 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds 1191c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1192c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1193df71837dSTrent Jaeger } 1194df71837dSTrent Jaeger 11950d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 11960d681623SSerge Hallyn { 11970d681623SSerge Hallyn if (x->security) { 11980d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 11990d681623SSerge Hallyn } 12000d681623SSerge Hallyn return 0; 12010d681623SSerge Hallyn } 12020d681623SSerge Hallyn 12030d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 12040d681623SSerge Hallyn { 12050d681623SSerge Hallyn if (xp->security) { 12060d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 12070d681623SSerge Hallyn } 12080d681623SSerge Hallyn return 0; 12090d681623SSerge Hallyn } 1210cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1211cfbfd45aSThomas Graf { 1212cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1213cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1214cfbfd45aSThomas Graf #else 1215cfbfd45aSThomas Graf return 0; 1216cfbfd45aSThomas Graf #endif 1217cfbfd45aSThomas Graf } 12180d681623SSerge Hallyn 1219f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1220b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1221f7b6983fSMasahide NAKAMURA { 1222c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1223c0144beaSThomas Graf .type = type, 1224c0144beaSThomas Graf }; 1225f7b6983fSMasahide NAKAMURA 1226c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1227f7b6983fSMasahide NAKAMURA } 1228f7b6983fSMasahide NAKAMURA 1229f7b6983fSMasahide NAKAMURA #else 1230b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1231f7b6983fSMasahide NAKAMURA { 1232f7b6983fSMasahide NAKAMURA return 0; 1233f7b6983fSMasahide NAKAMURA } 1234f7b6983fSMasahide NAKAMURA #endif 1235f7b6983fSMasahide NAKAMURA 12361da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 12371da177e4SLinus Torvalds { 12381da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 12391da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 12401da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 12411da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 12421da177e4SLinus Torvalds struct nlmsghdr *nlh; 12431da177e4SLinus Torvalds 124479b8b7f4SThomas Graf nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 124579b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 124679b8b7f4SThomas Graf if (nlh == NULL) 124779b8b7f4SThomas Graf return -EMSGSIZE; 12481da177e4SLinus Torvalds 12497b67c857SThomas Graf p = nlmsg_data(nlh); 12501da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 12511da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 12521da177e4SLinus Torvalds goto nlmsg_failure; 1253df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 1254df71837dSTrent Jaeger goto nlmsg_failure; 12551459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 1256f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 12571da177e4SLinus Torvalds 12589825069dSThomas Graf nlmsg_end(skb, nlh); 12591da177e4SLinus Torvalds return 0; 12601da177e4SLinus Torvalds 12611da177e4SLinus Torvalds nlmsg_failure: 12629825069dSThomas Graf nlmsg_cancel(skb, nlh); 12639825069dSThomas Graf return -EMSGSIZE; 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds 12664c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 12674c563f76STimo Teras { 12684c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 12694c563f76STimo Teras 12704c563f76STimo Teras xfrm_policy_walk_done(walk); 12714c563f76STimo Teras return 0; 12724c563f76STimo Teras } 12734c563f76STimo Teras 12741da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 12751da177e4SLinus Torvalds { 1276*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 12774c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 12781da177e4SLinus Torvalds struct xfrm_dump_info info; 12791da177e4SLinus Torvalds 12804c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 12814c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 12824c563f76STimo Teras 12831da177e4SLinus Torvalds info.in_skb = cb->skb; 12841da177e4SLinus Torvalds info.out_skb = skb; 12851da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 12861da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 12874c563f76STimo Teras 12884c563f76STimo Teras if (!cb->args[0]) { 12894c563f76STimo Teras cb->args[0] = 1; 12904c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 12914c563f76STimo Teras } 12924c563f76STimo Teras 1293*fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds return skb->len; 12961da177e4SLinus Torvalds } 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 12991da177e4SLinus Torvalds struct xfrm_policy *xp, 13001da177e4SLinus Torvalds int dir, u32 seq) 13011da177e4SLinus Torvalds { 13021da177e4SLinus Torvalds struct xfrm_dump_info info; 13031da177e4SLinus Torvalds struct sk_buff *skb; 13041da177e4SLinus Torvalds 13057deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 13061da177e4SLinus Torvalds if (!skb) 13071da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds info.in_skb = in_skb; 13101da177e4SLinus Torvalds info.out_skb = skb; 13111da177e4SLinus Torvalds info.nlmsg_seq = seq; 13121da177e4SLinus Torvalds info.nlmsg_flags = 0; 13131da177e4SLinus Torvalds 13141da177e4SLinus Torvalds if (dump_one_policy(xp, dir, 0, &info) < 0) { 13151da177e4SLinus Torvalds kfree_skb(skb); 13161da177e4SLinus Torvalds return NULL; 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds return skb; 13201da177e4SLinus Torvalds } 13211da177e4SLinus Torvalds 132222e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 13235424f32eSThomas Graf struct nlattr **attrs) 13241da177e4SLinus Torvalds { 1325*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 13261da177e4SLinus Torvalds struct xfrm_policy *xp; 13271da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1328b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 13291da177e4SLinus Torvalds int err; 133026b15dadSJamal Hadi Salim struct km_event c; 13311da177e4SLinus Torvalds int delete; 13321da177e4SLinus Torvalds 13337b67c857SThomas Graf p = nlmsg_data(nlh); 13341da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 13351da177e4SLinus Torvalds 133635a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1337f7b6983fSMasahide NAKAMURA if (err) 1338f7b6983fSMasahide NAKAMURA return err; 1339f7b6983fSMasahide NAKAMURA 13401da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 13411da177e4SLinus Torvalds if (err) 13421da177e4SLinus Torvalds return err; 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds if (p->index) 1345a6483b79SAlexey Dobriyan xp = xfrm_policy_byid(net, type, p->dir, p->index, delete, &err); 1346df71837dSTrent Jaeger else { 13475424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 134803e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1349df71837dSTrent Jaeger 135035a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1351df71837dSTrent Jaeger if (err) 1352df71837dSTrent Jaeger return err; 1353df71837dSTrent Jaeger 13542c8dd116SDenis V. Lunev ctx = NULL; 1355df71837dSTrent Jaeger if (rt) { 13565424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1357df71837dSTrent Jaeger 135803e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 135903e1ad7bSPaul Moore if (err) 1360df71837dSTrent Jaeger return err; 13612c8dd116SDenis V. Lunev } 1362a6483b79SAlexey Dobriyan xp = xfrm_policy_bysel_ctx(net, type, p->dir, &p->sel, ctx, 1363ef41aaa0SEric Paris delete, &err); 136403e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1365df71837dSTrent Jaeger } 13661da177e4SLinus Torvalds if (xp == NULL) 13671da177e4SLinus Torvalds return -ENOENT; 13681da177e4SLinus Torvalds 13691da177e4SLinus Torvalds if (!delete) { 13701da177e4SLinus Torvalds struct sk_buff *resp_skb; 13711da177e4SLinus Torvalds 13721da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 13731da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 13741da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 13751da177e4SLinus Torvalds } else { 1376a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 1377082a1ad5SThomas Graf NETLINK_CB(skb).pid); 13781da177e4SLinus Torvalds } 137926b15dadSJamal Hadi Salim } else { 13802532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 13812532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 13822532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 13832532386fSEric Paris 13842532386fSEric Paris xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, 13852532386fSEric Paris sid); 138613fcfbb0SDavid S. Miller 138713fcfbb0SDavid S. Miller if (err != 0) 1388c8c05a8eSCatherine Zhang goto out; 138913fcfbb0SDavid S. Miller 1390e7443892SHerbert Xu c.data.byid = p->index; 1391f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 139226b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 139326b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 139426b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 13951da177e4SLinus Torvalds } 13961da177e4SLinus Torvalds 1397c8c05a8eSCatherine Zhang out: 1398ef41aaa0SEric Paris xfrm_pol_put(xp); 13991da177e4SLinus Torvalds return err; 14001da177e4SLinus Torvalds } 14011da177e4SLinus Torvalds 140222e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 14035424f32eSThomas Graf struct nlattr **attrs) 14041da177e4SLinus Torvalds { 1405*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 140626b15dadSJamal Hadi Salim struct km_event c; 14077b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1408161a09e7SJoy Latten struct xfrm_audit audit_info; 14094aa2e62cSJoy Latten int err; 14101da177e4SLinus Torvalds 1411161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 14122532386fSEric Paris audit_info.sessionid = NETLINK_CB(skb).sessionid; 1413161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 1414*fc34acd3SAlexey Dobriyan err = xfrm_state_flush(net, p->proto, &audit_info); 14154aa2e62cSJoy Latten if (err) 14164aa2e62cSJoy Latten return err; 1417bf08867fSHerbert Xu c.data.proto = p->proto; 1418f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 141926b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 142026b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 142126b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 142226b15dadSJamal Hadi Salim 14231da177e4SLinus Torvalds return 0; 14241da177e4SLinus Torvalds } 14251da177e4SLinus Torvalds 14267deb2264SThomas Graf static inline size_t xfrm_aevent_msgsize(void) 14277deb2264SThomas Graf { 14287deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 14297deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_replay_state)) 14307deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 14317deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 14327deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 14337deb2264SThomas Graf } 1434d51d081dSJamal Hadi Salim 1435d51d081dSJamal Hadi Salim static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 1436d51d081dSJamal Hadi Salim { 1437d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1438d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 1439d51d081dSJamal Hadi Salim 144079b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 144179b8b7f4SThomas Graf if (nlh == NULL) 144279b8b7f4SThomas Graf return -EMSGSIZE; 1443d51d081dSJamal Hadi Salim 14447b67c857SThomas Graf id = nlmsg_data(nlh); 14452b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1446d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1447d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1448d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 14492b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 14502b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1451d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1452d51d081dSJamal Hadi Salim 1453c0144beaSThomas Graf NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); 1454c0144beaSThomas Graf NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 1455d51d081dSJamal Hadi Salim 1456c0144beaSThomas Graf if (id->flags & XFRM_AE_RTHR) 1457c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 1458d51d081dSJamal Hadi Salim 1459c0144beaSThomas Graf if (id->flags & XFRM_AE_ETHR) 1460c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH, 1461c0144beaSThomas Graf x->replay_maxage * 10 / HZ); 1462d51d081dSJamal Hadi Salim 14639825069dSThomas Graf return nlmsg_end(skb, nlh); 1464d51d081dSJamal Hadi Salim 1465c0144beaSThomas Graf nla_put_failure: 14669825069dSThomas Graf nlmsg_cancel(skb, nlh); 14679825069dSThomas Graf return -EMSGSIZE; 1468d51d081dSJamal Hadi Salim } 1469d51d081dSJamal Hadi Salim 147022e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 14715424f32eSThomas Graf struct nlattr **attrs) 1472d51d081dSJamal Hadi Salim { 1473*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1474d51d081dSJamal Hadi Salim struct xfrm_state *x; 1475d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1476d51d081dSJamal Hadi Salim int err; 1477d51d081dSJamal Hadi Salim struct km_event c; 14787b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1479d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1480d51d081dSJamal Hadi Salim 14817deb2264SThomas Graf r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 1482d51d081dSJamal Hadi Salim if (r_skb == NULL) 1483d51d081dSJamal Hadi Salim return -ENOMEM; 1484d51d081dSJamal Hadi Salim 1485a6483b79SAlexey Dobriyan x = xfrm_state_lookup(net, &id->daddr, id->spi, id->proto, id->family); 1486d51d081dSJamal Hadi Salim if (x == NULL) { 1487b08d5840SPatrick McHardy kfree_skb(r_skb); 1488d51d081dSJamal Hadi Salim return -ESRCH; 1489d51d081dSJamal Hadi Salim } 1490d51d081dSJamal Hadi Salim 1491d51d081dSJamal Hadi Salim /* 1492d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1493d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1494d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1495d51d081dSJamal Hadi Salim */ 1496d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1497d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1498d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1499d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1500d51d081dSJamal Hadi Salim 1501d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1502d51d081dSJamal Hadi Salim BUG(); 1503a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid); 1504d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1505d51d081dSJamal Hadi Salim xfrm_state_put(x); 1506d51d081dSJamal Hadi Salim return err; 1507d51d081dSJamal Hadi Salim } 1508d51d081dSJamal Hadi Salim 150922e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 15105424f32eSThomas Graf struct nlattr **attrs) 1511d51d081dSJamal Hadi Salim { 1512*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1513d51d081dSJamal Hadi Salim struct xfrm_state *x; 1514d51d081dSJamal Hadi Salim struct km_event c; 1515d51d081dSJamal Hadi Salim int err = - EINVAL; 15167b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 15175424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 15185424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1519d51d081dSJamal Hadi Salim 1520d51d081dSJamal Hadi Salim if (!lt && !rp) 1521d51d081dSJamal Hadi Salim return err; 1522d51d081dSJamal Hadi Salim 1523d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1524d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1525d51d081dSJamal Hadi Salim return err; 1526d51d081dSJamal Hadi Salim 1527*fc34acd3SAlexey Dobriyan x = xfrm_state_lookup(net, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1528d51d081dSJamal Hadi Salim if (x == NULL) 1529d51d081dSJamal Hadi Salim return -ESRCH; 1530d51d081dSJamal Hadi Salim 1531d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1532d51d081dSJamal Hadi Salim goto out; 1533d51d081dSJamal Hadi Salim 1534d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 153535a7aa08SThomas Graf xfrm_update_ae_params(x, attrs); 1536d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1537d51d081dSJamal Hadi Salim 1538d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1539d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1540d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1541d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1542d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1543d51d081dSJamal Hadi Salim err = 0; 1544d51d081dSJamal Hadi Salim out: 1545d51d081dSJamal Hadi Salim xfrm_state_put(x); 1546d51d081dSJamal Hadi Salim return err; 1547d51d081dSJamal Hadi Salim } 1548d51d081dSJamal Hadi Salim 154922e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 15505424f32eSThomas Graf struct nlattr **attrs) 15511da177e4SLinus Torvalds { 1552*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 155326b15dadSJamal Hadi Salim struct km_event c; 1554b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1555f7b6983fSMasahide NAKAMURA int err; 1556161a09e7SJoy Latten struct xfrm_audit audit_info; 155726b15dadSJamal Hadi Salim 155835a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1559f7b6983fSMasahide NAKAMURA if (err) 1560f7b6983fSMasahide NAKAMURA return err; 1561f7b6983fSMasahide NAKAMURA 1562161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 15632532386fSEric Paris audit_info.sessionid = NETLINK_CB(skb).sessionid; 1564161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 1565*fc34acd3SAlexey Dobriyan err = xfrm_policy_flush(net, type, &audit_info); 15664aa2e62cSJoy Latten if (err) 15674aa2e62cSJoy Latten return err; 1568f7b6983fSMasahide NAKAMURA c.data.type = type; 1569f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 157026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 157126b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 157226b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 15731da177e4SLinus Torvalds return 0; 15741da177e4SLinus Torvalds } 15751da177e4SLinus Torvalds 157622e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 15775424f32eSThomas Graf struct nlattr **attrs) 15786c5c8ca7SJamal Hadi Salim { 1579*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 15806c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 15817b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 15826c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1583b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 15846c5c8ca7SJamal Hadi Salim int err = -ENOENT; 15856c5c8ca7SJamal Hadi Salim 158635a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1587f7b6983fSMasahide NAKAMURA if (err) 1588f7b6983fSMasahide NAKAMURA return err; 1589f7b6983fSMasahide NAKAMURA 15906c5c8ca7SJamal Hadi Salim if (p->index) 1591*fc34acd3SAlexey Dobriyan xp = xfrm_policy_byid(net, type, p->dir, p->index, 0, &err); 15926c5c8ca7SJamal Hadi Salim else { 15935424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 159403e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 15956c5c8ca7SJamal Hadi Salim 159635a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 15976c5c8ca7SJamal Hadi Salim if (err) 15986c5c8ca7SJamal Hadi Salim return err; 15996c5c8ca7SJamal Hadi Salim 16002c8dd116SDenis V. Lunev ctx = NULL; 16016c5c8ca7SJamal Hadi Salim if (rt) { 16025424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 16036c5c8ca7SJamal Hadi Salim 160403e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 160503e1ad7bSPaul Moore if (err) 16066c5c8ca7SJamal Hadi Salim return err; 16072c8dd116SDenis V. Lunev } 1608*fc34acd3SAlexey Dobriyan xp = xfrm_policy_bysel_ctx(net, type, p->dir, &p->sel, ctx, 0, &err); 160903e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 16106c5c8ca7SJamal Hadi Salim } 16116c5c8ca7SJamal Hadi Salim if (xp == NULL) 1612ef41aaa0SEric Paris return -ENOENT; 161303e1ad7bSPaul Moore 16146c5c8ca7SJamal Hadi Salim read_lock(&xp->lock); 161512a169e7SHerbert Xu if (xp->walk.dead) { 16166c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 16176c5c8ca7SJamal Hadi Salim goto out; 16186c5c8ca7SJamal Hadi Salim } 16196c5c8ca7SJamal Hadi Salim 16206c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 16216c5c8ca7SJamal Hadi Salim err = 0; 16226c5c8ca7SJamal Hadi Salim if (up->hard) { 16232532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 16242532386fSEric Paris uid_t sessionid = NETLINK_CB(skb).sessionid; 16252532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 16266c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 16272532386fSEric Paris xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); 1628161a09e7SJoy Latten 16296c5c8ca7SJamal Hadi Salim } else { 16306c5c8ca7SJamal Hadi Salim // reset the timers here? 16316c5c8ca7SJamal Hadi Salim printk("Dont know what to do with soft policy expire\n"); 16326c5c8ca7SJamal Hadi Salim } 16336c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 16346c5c8ca7SJamal Hadi Salim 16356c5c8ca7SJamal Hadi Salim out: 16366c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 16376c5c8ca7SJamal Hadi Salim return err; 16386c5c8ca7SJamal Hadi Salim } 16396c5c8ca7SJamal Hadi Salim 164022e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 16415424f32eSThomas Graf struct nlattr **attrs) 164253bc6b4dSJamal Hadi Salim { 1643*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 164453bc6b4dSJamal Hadi Salim struct xfrm_state *x; 164553bc6b4dSJamal Hadi Salim int err; 16467b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 164753bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 164853bc6b4dSJamal Hadi Salim 1649*fc34acd3SAlexey Dobriyan x = xfrm_state_lookup(net, &p->id.daddr, p->id.spi, p->id.proto, p->family); 165053bc6b4dSJamal Hadi Salim 16513a765aa5SDavid S. Miller err = -ENOENT; 165253bc6b4dSJamal Hadi Salim if (x == NULL) 165353bc6b4dSJamal Hadi Salim return err; 165453bc6b4dSJamal Hadi Salim 165553bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 16563a765aa5SDavid S. Miller err = -EINVAL; 165753bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 165853bc6b4dSJamal Hadi Salim goto out; 165953bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 166053bc6b4dSJamal Hadi Salim 1661161a09e7SJoy Latten if (ue->hard) { 16622532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 16632532386fSEric Paris uid_t sessionid = NETLINK_CB(skb).sessionid; 16642532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 166553bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 16662532386fSEric Paris xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); 1667161a09e7SJoy Latten } 16683a765aa5SDavid S. Miller err = 0; 166953bc6b4dSJamal Hadi Salim out: 167053bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 167153bc6b4dSJamal Hadi Salim xfrm_state_put(x); 167253bc6b4dSJamal Hadi Salim return err; 167353bc6b4dSJamal Hadi Salim } 167453bc6b4dSJamal Hadi Salim 167522e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 16765424f32eSThomas Graf struct nlattr **attrs) 1677980ebd25SJamal Hadi Salim { 1678*fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1679980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1680980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1681980ebd25SJamal Hadi Salim int i; 16825424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 1683980ebd25SJamal Hadi Salim 16847b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 1685*fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 1686980ebd25SJamal Hadi Salim int err = -ENOMEM; 1687980ebd25SJamal Hadi Salim 1688980ebd25SJamal Hadi Salim if (!x) 1689980ebd25SJamal Hadi Salim return err; 1690980ebd25SJamal Hadi Salim 1691980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 1692980ebd25SJamal Hadi Salim if (err) { 1693980ebd25SJamal Hadi Salim printk("BAD policy passed\n"); 1694980ebd25SJamal Hadi Salim kfree(x); 1695980ebd25SJamal Hadi Salim return err; 1696980ebd25SJamal Hadi Salim } 1697980ebd25SJamal Hadi Salim 1698980ebd25SJamal Hadi Salim /* build an XP */ 1699*fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 1700b4ad86bfSDavid S. Miller if (!xp) { 1701980ebd25SJamal Hadi Salim kfree(x); 1702980ebd25SJamal Hadi Salim return err; 1703980ebd25SJamal Hadi Salim } 1704980ebd25SJamal Hadi Salim 1705980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 1706980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 1707980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 1708980ebd25SJamal Hadi Salim 17095424f32eSThomas Graf ut = nla_data(rt); 1710980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 1711980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 1712980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 1713980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 1714980ebd25SJamal Hadi Salim x->props.mode = t->mode; 1715980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 1716980ebd25SJamal Hadi Salim x->props.family = ut->family; 1717980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 1718980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 1719980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 1720980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 1721980ebd25SJamal Hadi Salim 1722980ebd25SJamal Hadi Salim } 1723980ebd25SJamal Hadi Salim 1724980ebd25SJamal Hadi Salim kfree(x); 1725980ebd25SJamal Hadi Salim kfree(xp); 1726980ebd25SJamal Hadi Salim 1727980ebd25SJamal Hadi Salim return 0; 1728980ebd25SJamal Hadi Salim } 1729980ebd25SJamal Hadi Salim 17305c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 17315c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 173213c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 17335424f32eSThomas Graf struct nlattr **attrs, int *num) 17345c79de6eSShinta Sugimoto { 17355424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 17365c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 17375c79de6eSShinta Sugimoto int i, num_migrate; 17385c79de6eSShinta Sugimoto 173913c1d189SArnaud Ebalard if (k != NULL) { 174013c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 174113c1d189SArnaud Ebalard 174213c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 174313c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 174413c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 174513c1d189SArnaud Ebalard k->family = uk->family; 174613c1d189SArnaud Ebalard k->reserved = uk->reserved; 174713c1d189SArnaud Ebalard } 174813c1d189SArnaud Ebalard 17495424f32eSThomas Graf um = nla_data(rt); 17505424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 17515c79de6eSShinta Sugimoto 17525c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 17535c79de6eSShinta Sugimoto return -EINVAL; 17545c79de6eSShinta Sugimoto 17555c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 17565c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 17575c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 17585c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 17595c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 17605c79de6eSShinta Sugimoto 17615c79de6eSShinta Sugimoto ma->proto = um->proto; 17625c79de6eSShinta Sugimoto ma->mode = um->mode; 17635c79de6eSShinta Sugimoto ma->reqid = um->reqid; 17645c79de6eSShinta Sugimoto 17655c79de6eSShinta Sugimoto ma->old_family = um->old_family; 17665c79de6eSShinta Sugimoto ma->new_family = um->new_family; 17675c79de6eSShinta Sugimoto } 17685c79de6eSShinta Sugimoto 17695c79de6eSShinta Sugimoto *num = i; 17705c79de6eSShinta Sugimoto return 0; 17715c79de6eSShinta Sugimoto } 17725c79de6eSShinta Sugimoto 17735c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 17745424f32eSThomas Graf struct nlattr **attrs) 17755c79de6eSShinta Sugimoto { 17767b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 17775c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 177813c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 17795c79de6eSShinta Sugimoto u8 type; 17805c79de6eSShinta Sugimoto int err; 17815c79de6eSShinta Sugimoto int n = 0; 17825c79de6eSShinta Sugimoto 178335a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 1784cf5cb79fSThomas Graf return -EINVAL; 17855c79de6eSShinta Sugimoto 178613c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 178713c1d189SArnaud Ebalard 17885424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 17895c79de6eSShinta Sugimoto if (err) 17905c79de6eSShinta Sugimoto return err; 17915c79de6eSShinta Sugimoto 179213c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 17935c79de6eSShinta Sugimoto if (err) 17945c79de6eSShinta Sugimoto return err; 17955c79de6eSShinta Sugimoto 17965c79de6eSShinta Sugimoto if (!n) 17975c79de6eSShinta Sugimoto return 0; 17985c79de6eSShinta Sugimoto 179913c1d189SArnaud Ebalard xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); 18005c79de6eSShinta Sugimoto 18015c79de6eSShinta Sugimoto return 0; 18025c79de6eSShinta Sugimoto } 18035c79de6eSShinta Sugimoto #else 18045c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 18055424f32eSThomas Graf struct nlattr **attrs) 18065c79de6eSShinta Sugimoto { 18075c79de6eSShinta Sugimoto return -ENOPROTOOPT; 18085c79de6eSShinta Sugimoto } 18095c79de6eSShinta Sugimoto #endif 18105c79de6eSShinta Sugimoto 18115c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 18125c79de6eSShinta Sugimoto static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) 18135c79de6eSShinta Sugimoto { 18145c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 18155c79de6eSShinta Sugimoto 18165c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 18175c79de6eSShinta Sugimoto um.proto = m->proto; 18185c79de6eSShinta Sugimoto um.mode = m->mode; 18195c79de6eSShinta Sugimoto um.reqid = m->reqid; 18205c79de6eSShinta Sugimoto um.old_family = m->old_family; 18215c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 18225c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 18235c79de6eSShinta Sugimoto um.new_family = m->new_family; 18245c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 18255c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 18265c79de6eSShinta Sugimoto 1827c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 18285c79de6eSShinta Sugimoto } 18295c79de6eSShinta Sugimoto 183013c1d189SArnaud Ebalard static int copy_to_user_kmaddress(struct xfrm_kmaddress *k, struct sk_buff *skb) 183113c1d189SArnaud Ebalard { 183213c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 183313c1d189SArnaud Ebalard 183413c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 183513c1d189SArnaud Ebalard uk.family = k->family; 183613c1d189SArnaud Ebalard uk.reserved = k->reserved; 183713c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 1838a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 183913c1d189SArnaud Ebalard 184013c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 184113c1d189SArnaud Ebalard } 184213c1d189SArnaud Ebalard 184313c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 18447deb2264SThomas Graf { 18457deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 184613c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 18477deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 18487deb2264SThomas Graf + userpolicy_type_attrsize(); 18497deb2264SThomas Graf } 18507deb2264SThomas Graf 18515c79de6eSShinta Sugimoto static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, 185213c1d189SArnaud Ebalard int num_migrate, struct xfrm_kmaddress *k, 185313c1d189SArnaud Ebalard struct xfrm_selector *sel, u8 dir, u8 type) 18545c79de6eSShinta Sugimoto { 18555c79de6eSShinta Sugimoto struct xfrm_migrate *mp; 18565c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 18575c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 18585c79de6eSShinta Sugimoto int i; 18595c79de6eSShinta Sugimoto 186079b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 186179b8b7f4SThomas Graf if (nlh == NULL) 186279b8b7f4SThomas Graf return -EMSGSIZE; 18635c79de6eSShinta Sugimoto 18647b67c857SThomas Graf pol_id = nlmsg_data(nlh); 18655c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 18665c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 18675c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 18685c79de6eSShinta Sugimoto pol_id->dir = dir; 18695c79de6eSShinta Sugimoto 187013c1d189SArnaud Ebalard if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0)) 187113c1d189SArnaud Ebalard goto nlmsg_failure; 187213c1d189SArnaud Ebalard 18735c79de6eSShinta Sugimoto if (copy_to_user_policy_type(type, skb) < 0) 18745c79de6eSShinta Sugimoto goto nlmsg_failure; 18755c79de6eSShinta Sugimoto 18765c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 18775c79de6eSShinta Sugimoto if (copy_to_user_migrate(mp, skb) < 0) 18785c79de6eSShinta Sugimoto goto nlmsg_failure; 18795c79de6eSShinta Sugimoto } 18805c79de6eSShinta Sugimoto 18819825069dSThomas Graf return nlmsg_end(skb, nlh); 18825c79de6eSShinta Sugimoto nlmsg_failure: 18839825069dSThomas Graf nlmsg_cancel(skb, nlh); 18849825069dSThomas Graf return -EMSGSIZE; 18855c79de6eSShinta Sugimoto } 18865c79de6eSShinta Sugimoto 18875c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 188813c1d189SArnaud Ebalard struct xfrm_migrate *m, int num_migrate, 188913c1d189SArnaud Ebalard struct xfrm_kmaddress *k) 18905c79de6eSShinta Sugimoto { 1891a6483b79SAlexey Dobriyan struct net *net = &init_net; 18925c79de6eSShinta Sugimoto struct sk_buff *skb; 18935c79de6eSShinta Sugimoto 189413c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 18955c79de6eSShinta Sugimoto if (skb == NULL) 18965c79de6eSShinta Sugimoto return -ENOMEM; 18975c79de6eSShinta Sugimoto 18985c79de6eSShinta Sugimoto /* build migrate */ 189913c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 19005c79de6eSShinta Sugimoto BUG(); 19015c79de6eSShinta Sugimoto 1902a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 19035c79de6eSShinta Sugimoto } 19045c79de6eSShinta Sugimoto #else 19055c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 190613c1d189SArnaud Ebalard struct xfrm_migrate *m, int num_migrate, 190713c1d189SArnaud Ebalard struct xfrm_kmaddress *k) 19085c79de6eSShinta Sugimoto { 19095c79de6eSShinta Sugimoto return -ENOPROTOOPT; 19105c79de6eSShinta Sugimoto } 19115c79de6eSShinta Sugimoto #endif 1912d51d081dSJamal Hadi Salim 1913a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 1914492b558bSThomas Graf 1915492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 1916492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 1917492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 1918492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 1919492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 1920492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1921492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1922492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 1923980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 192453bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 1925492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 1926492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 19276c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 1928492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 1929a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 1930d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 1931d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 193297a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 19335c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1934a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 1935a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 19361da177e4SLinus Torvalds }; 19371da177e4SLinus Torvalds 1938492b558bSThomas Graf #undef XMSGSIZE 1939492b558bSThomas Graf 1940cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 19411a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 1942cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 1943cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 1944cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 1945cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 1946cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 1947cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 1948cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 1949cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 1950cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 1951cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 1952cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 1953cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 1954cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 1955cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 195613c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 1957cf5cb79fSThomas Graf }; 1958cf5cb79fSThomas Graf 19591da177e4SLinus Torvalds static struct xfrm_link { 19605424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 19611da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 19624c563f76STimo Teras int (*done)(struct netlink_callback *); 1963492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 1964492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 1965492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 1966492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 19674c563f76STimo Teras .dump = xfrm_dump_sa, 19684c563f76STimo Teras .done = xfrm_dump_sa_done }, 1969492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 1970492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 1971492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 19724c563f76STimo Teras .dump = xfrm_dump_policy, 19734c563f76STimo Teras .done = xfrm_dump_policy_done }, 1974492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 1975980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 197653bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 1977492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 1978492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 19796c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 1980492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 1981492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 1982d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 1983d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 19845c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 198528d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 1986ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 19871da177e4SLinus Torvalds }; 19881da177e4SLinus Torvalds 19891d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 19901da177e4SLinus Torvalds { 1991a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 199235a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 19931da177e4SLinus Torvalds struct xfrm_link *link; 1994a7bd9a45SThomas Graf int type, err; 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds type = nlh->nlmsg_type; 19971da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 19981d00a4ebSThomas Graf return -EINVAL; 19991da177e4SLinus Torvalds 20001da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 20011da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 20021da177e4SLinus Torvalds 20031da177e4SLinus Torvalds /* All operations require privileges, even GET */ 20041d00a4ebSThomas Graf if (security_netlink_recv(skb, CAP_NET_ADMIN)) 20051d00a4ebSThomas Graf return -EPERM; 20061da177e4SLinus Torvalds 2007492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2008492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2009492b558bSThomas Graf (nlh->nlmsg_flags & NLM_F_DUMP)) { 20101da177e4SLinus Torvalds if (link->dump == NULL) 20111d00a4ebSThomas Graf return -EINVAL; 20121da177e4SLinus Torvalds 2013a6483b79SAlexey Dobriyan return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done); 20141da177e4SLinus Torvalds } 20151da177e4SLinus Torvalds 201635a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2017cf5cb79fSThomas Graf xfrma_policy); 2018a7bd9a45SThomas Graf if (err < 0) 2019a7bd9a45SThomas Graf return err; 20201da177e4SLinus Torvalds 20211da177e4SLinus Torvalds if (link->doit == NULL) 20221d00a4ebSThomas Graf return -EINVAL; 20231da177e4SLinus Torvalds 20245424f32eSThomas Graf return link->doit(skb, nlh, attrs); 20251da177e4SLinus Torvalds } 20261da177e4SLinus Torvalds 2027cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 20281da177e4SLinus Torvalds { 20294a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 2030cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 20314a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 20321da177e4SLinus Torvalds } 20331da177e4SLinus Torvalds 20347deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 20357deb2264SThomas Graf { 20367deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)); 20377deb2264SThomas Graf } 20387deb2264SThomas Graf 2039d51d081dSJamal Hadi Salim static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 20401da177e4SLinus Torvalds { 20411da177e4SLinus Torvalds struct xfrm_user_expire *ue; 20421da177e4SLinus Torvalds struct nlmsghdr *nlh; 20431da177e4SLinus Torvalds 204479b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 204579b8b7f4SThomas Graf if (nlh == NULL) 204679b8b7f4SThomas Graf return -EMSGSIZE; 20471da177e4SLinus Torvalds 20487b67c857SThomas Graf ue = nlmsg_data(nlh); 20491da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2050d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 20511da177e4SLinus Torvalds 20529825069dSThomas Graf return nlmsg_end(skb, nlh); 20531da177e4SLinus Torvalds } 20541da177e4SLinus Torvalds 205526b15dadSJamal Hadi Salim static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c) 20561da177e4SLinus Torvalds { 2057*fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 20581da177e4SLinus Torvalds struct sk_buff *skb; 20591da177e4SLinus Torvalds 20607deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 20611da177e4SLinus Torvalds if (skb == NULL) 20621da177e4SLinus Torvalds return -ENOMEM; 20631da177e4SLinus Torvalds 2064d51d081dSJamal Hadi Salim if (build_expire(skb, x, c) < 0) 20651da177e4SLinus Torvalds BUG(); 20661da177e4SLinus Torvalds 2067a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 20681da177e4SLinus Torvalds } 20691da177e4SLinus Torvalds 2070d51d081dSJamal Hadi Salim static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c) 2071d51d081dSJamal Hadi Salim { 2072*fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2073d51d081dSJamal Hadi Salim struct sk_buff *skb; 2074d51d081dSJamal Hadi Salim 20757deb2264SThomas Graf skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 2076d51d081dSJamal Hadi Salim if (skb == NULL) 2077d51d081dSJamal Hadi Salim return -ENOMEM; 2078d51d081dSJamal Hadi Salim 2079d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2080d51d081dSJamal Hadi Salim BUG(); 2081d51d081dSJamal Hadi Salim 2082a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2083d51d081dSJamal Hadi Salim } 2084d51d081dSJamal Hadi Salim 208526b15dadSJamal Hadi Salim static int xfrm_notify_sa_flush(struct km_event *c) 208626b15dadSJamal Hadi Salim { 2087a6483b79SAlexey Dobriyan struct net *net = &init_net; 208826b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 208926b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 209026b15dadSJamal Hadi Salim struct sk_buff *skb; 20917deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 209226b15dadSJamal Hadi Salim 20937deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 209426b15dadSJamal Hadi Salim if (skb == NULL) 209526b15dadSJamal Hadi Salim return -ENOMEM; 209626b15dadSJamal Hadi Salim 209779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 209879b8b7f4SThomas Graf if (nlh == NULL) { 209979b8b7f4SThomas Graf kfree_skb(skb); 210079b8b7f4SThomas Graf return -EMSGSIZE; 210179b8b7f4SThomas Graf } 210226b15dadSJamal Hadi Salim 21037b67c857SThomas Graf p = nlmsg_data(nlh); 2104bf08867fSHerbert Xu p->proto = c->data.proto; 210526b15dadSJamal Hadi Salim 21069825069dSThomas Graf nlmsg_end(skb, nlh); 210726b15dadSJamal Hadi Salim 2108a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 210926b15dadSJamal Hadi Salim } 211026b15dadSJamal Hadi Salim 21117deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 211226b15dadSJamal Hadi Salim { 21137deb2264SThomas Graf size_t l = 0; 21141a6509d9SHerbert Xu if (x->aead) 21151a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 211626b15dadSJamal Hadi Salim if (x->aalg) 21170f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->aalg)); 211826b15dadSJamal Hadi Salim if (x->ealg) 21190f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 212026b15dadSJamal Hadi Salim if (x->calg) 21217deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 212226b15dadSJamal Hadi Salim if (x->encap) 21237deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 212468325d3bSHerbert Xu if (x->security) 212568325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 212668325d3bSHerbert Xu x->security->ctx_len); 212768325d3bSHerbert Xu if (x->coaddr) 212868325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 212968325d3bSHerbert Xu 2130d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2131d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 213226b15dadSJamal Hadi Salim 213326b15dadSJamal Hadi Salim return l; 213426b15dadSJamal Hadi Salim } 213526b15dadSJamal Hadi Salim 213626b15dadSJamal Hadi Salim static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c) 213726b15dadSJamal Hadi Salim { 2138*fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 213926b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 21400603eac0SHerbert Xu struct xfrm_usersa_id *id; 214126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 214226b15dadSJamal Hadi Salim struct sk_buff *skb; 214326b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 21440603eac0SHerbert Xu int headlen; 21450603eac0SHerbert Xu 21460603eac0SHerbert Xu headlen = sizeof(*p); 21470603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 21487deb2264SThomas Graf len += nla_total_size(headlen); 21490603eac0SHerbert Xu headlen = sizeof(*id); 21500603eac0SHerbert Xu } 21517deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 215226b15dadSJamal Hadi Salim 21537deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 215426b15dadSJamal Hadi Salim if (skb == NULL) 215526b15dadSJamal Hadi Salim return -ENOMEM; 215626b15dadSJamal Hadi Salim 215779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 215879b8b7f4SThomas Graf if (nlh == NULL) 2159c0144beaSThomas Graf goto nla_put_failure; 216026b15dadSJamal Hadi Salim 21617b67c857SThomas Graf p = nlmsg_data(nlh); 21620603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2163c0144beaSThomas Graf struct nlattr *attr; 2164c0144beaSThomas Graf 21657b67c857SThomas Graf id = nlmsg_data(nlh); 21660603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 21670603eac0SHerbert Xu id->spi = x->id.spi; 21680603eac0SHerbert Xu id->family = x->props.family; 21690603eac0SHerbert Xu id->proto = x->id.proto; 21700603eac0SHerbert Xu 2171c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 2172c0144beaSThomas Graf if (attr == NULL) 2173c0144beaSThomas Graf goto nla_put_failure; 2174c0144beaSThomas Graf 2175c0144beaSThomas Graf p = nla_data(attr); 21760603eac0SHerbert Xu } 21770603eac0SHerbert Xu 217868325d3bSHerbert Xu if (copy_to_user_state_extra(x, p, skb)) 217968325d3bSHerbert Xu goto nla_put_failure; 218026b15dadSJamal Hadi Salim 21819825069dSThomas Graf nlmsg_end(skb, nlh); 218226b15dadSJamal Hadi Salim 2183a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 218426b15dadSJamal Hadi Salim 2185c0144beaSThomas Graf nla_put_failure: 218668325d3bSHerbert Xu /* Somebody screwed up with xfrm_sa_len! */ 218768325d3bSHerbert Xu WARN_ON(1); 218826b15dadSJamal Hadi Salim kfree_skb(skb); 218926b15dadSJamal Hadi Salim return -1; 219026b15dadSJamal Hadi Salim } 219126b15dadSJamal Hadi Salim 219226b15dadSJamal Hadi Salim static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c) 219326b15dadSJamal Hadi Salim { 219426b15dadSJamal Hadi Salim 219526b15dadSJamal Hadi Salim switch (c->event) { 2196f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 219726b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2198d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2199d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2200f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2201f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2202f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 220326b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2204f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 220526b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 220626b15dadSJamal Hadi Salim default: 220726b15dadSJamal Hadi Salim printk("xfrm_user: Unknown SA event %d\n", c->event); 220826b15dadSJamal Hadi Salim break; 220926b15dadSJamal Hadi Salim } 221026b15dadSJamal Hadi Salim 221126b15dadSJamal Hadi Salim return 0; 221226b15dadSJamal Hadi Salim 221326b15dadSJamal Hadi Salim } 221426b15dadSJamal Hadi Salim 22157deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 22167deb2264SThomas Graf struct xfrm_policy *xp) 22177deb2264SThomas Graf { 22187deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 22197deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 22207deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 22217deb2264SThomas Graf + userpolicy_type_attrsize(); 22227deb2264SThomas Graf } 22237deb2264SThomas Graf 22241da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 22251da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 22261da177e4SLinus Torvalds int dir) 22271da177e4SLinus Torvalds { 22281da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 22291da177e4SLinus Torvalds struct nlmsghdr *nlh; 22301da177e4SLinus Torvalds __u32 seq = xfrm_get_acqseq(); 22311da177e4SLinus Torvalds 223279b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 223379b8b7f4SThomas Graf if (nlh == NULL) 223479b8b7f4SThomas Graf return -EMSGSIZE; 22351da177e4SLinus Torvalds 22367b67c857SThomas Graf ua = nlmsg_data(nlh); 22371da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 22381da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 22391da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 22401da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 22411da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 22421da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 22431da177e4SLinus Torvalds ua->calgos = xt->calgos; 22441da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 22451da177e4SLinus Torvalds 22461da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 22471da177e4SLinus Torvalds goto nlmsg_failure; 22480d681623SSerge Hallyn if (copy_to_user_state_sec_ctx(x, skb)) 2249df71837dSTrent Jaeger goto nlmsg_failure; 22501459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2251f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 22521da177e4SLinus Torvalds 22539825069dSThomas Graf return nlmsg_end(skb, nlh); 22541da177e4SLinus Torvalds 22551da177e4SLinus Torvalds nlmsg_failure: 22569825069dSThomas Graf nlmsg_cancel(skb, nlh); 22579825069dSThomas Graf return -EMSGSIZE; 22581da177e4SLinus Torvalds } 22591da177e4SLinus Torvalds 22601da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 22611da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 22621da177e4SLinus Torvalds { 2263a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 22641da177e4SLinus Torvalds struct sk_buff *skb; 22651da177e4SLinus Torvalds 22667deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 22671da177e4SLinus Torvalds if (skb == NULL) 22681da177e4SLinus Torvalds return -ENOMEM; 22691da177e4SLinus Torvalds 22701da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 22711da177e4SLinus Torvalds BUG(); 22721da177e4SLinus Torvalds 2273a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 22741da177e4SLinus Torvalds } 22751da177e4SLinus Torvalds 22761da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 22771da177e4SLinus Torvalds * or more templates. 22781da177e4SLinus Torvalds */ 2279cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 22801da177e4SLinus Torvalds u8 *data, int len, int *dir) 22811da177e4SLinus Torvalds { 2282*fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 22831da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 22841da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 22851da177e4SLinus Torvalds struct xfrm_policy *xp; 22861da177e4SLinus Torvalds int nr; 22871da177e4SLinus Torvalds 2288cb969f07SVenkat Yekkirala switch (sk->sk_family) { 22891da177e4SLinus Torvalds case AF_INET: 22901da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 22911da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 22921da177e4SLinus Torvalds return NULL; 22931da177e4SLinus Torvalds } 22941da177e4SLinus Torvalds break; 22951da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 22961da177e4SLinus Torvalds case AF_INET6: 22971da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 22981da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 22991da177e4SLinus Torvalds return NULL; 23001da177e4SLinus Torvalds } 23011da177e4SLinus Torvalds break; 23021da177e4SLinus Torvalds #endif 23031da177e4SLinus Torvalds default: 23041da177e4SLinus Torvalds *dir = -EINVAL; 23051da177e4SLinus Torvalds return NULL; 23061da177e4SLinus Torvalds } 23071da177e4SLinus Torvalds 23081da177e4SLinus Torvalds *dir = -EINVAL; 23091da177e4SLinus Torvalds 23101da177e4SLinus Torvalds if (len < sizeof(*p) || 23111da177e4SLinus Torvalds verify_newpolicy_info(p)) 23121da177e4SLinus Torvalds return NULL; 23131da177e4SLinus Torvalds 23141da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2315b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 23161da177e4SLinus Torvalds return NULL; 23171da177e4SLinus Torvalds 2318a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2319a4f1bac6SHerbert Xu return NULL; 2320a4f1bac6SHerbert Xu 2321*fc34acd3SAlexey Dobriyan xp = xfrm_policy_alloc(net, GFP_KERNEL); 23221da177e4SLinus Torvalds if (xp == NULL) { 23231da177e4SLinus Torvalds *dir = -ENOBUFS; 23241da177e4SLinus Torvalds return NULL; 23251da177e4SLinus Torvalds } 23261da177e4SLinus Torvalds 23271da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2328f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 23291da177e4SLinus Torvalds copy_templates(xp, ut, nr); 23301da177e4SLinus Torvalds 23311da177e4SLinus Torvalds *dir = p->dir; 23321da177e4SLinus Torvalds 23331da177e4SLinus Torvalds return xp; 23341da177e4SLinus Torvalds } 23351da177e4SLinus Torvalds 23367deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 23377deb2264SThomas Graf { 23387deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 23397deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 23407deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 23417deb2264SThomas Graf + userpolicy_type_attrsize(); 23427deb2264SThomas Graf } 23437deb2264SThomas Graf 23441da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2345d51d081dSJamal Hadi Salim int dir, struct km_event *c) 23461da177e4SLinus Torvalds { 23471da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 23481da177e4SLinus Torvalds struct nlmsghdr *nlh; 2349d51d081dSJamal Hadi Salim int hard = c->data.hard; 23501da177e4SLinus Torvalds 235179b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 235279b8b7f4SThomas Graf if (nlh == NULL) 235379b8b7f4SThomas Graf return -EMSGSIZE; 23541da177e4SLinus Torvalds 23557b67c857SThomas Graf upe = nlmsg_data(nlh); 23561da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 23571da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 23581da177e4SLinus Torvalds goto nlmsg_failure; 2359df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 2360df71837dSTrent Jaeger goto nlmsg_failure; 23611459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2362f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 23631da177e4SLinus Torvalds upe->hard = !!hard; 23641da177e4SLinus Torvalds 23659825069dSThomas Graf return nlmsg_end(skb, nlh); 23661da177e4SLinus Torvalds 23671da177e4SLinus Torvalds nlmsg_failure: 23689825069dSThomas Graf nlmsg_cancel(skb, nlh); 23699825069dSThomas Graf return -EMSGSIZE; 23701da177e4SLinus Torvalds } 23711da177e4SLinus Torvalds 237226b15dadSJamal Hadi Salim static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 23731da177e4SLinus Torvalds { 2374*fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 23751da177e4SLinus Torvalds struct sk_buff *skb; 23761da177e4SLinus Torvalds 23777deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 23781da177e4SLinus Torvalds if (skb == NULL) 23791da177e4SLinus Torvalds return -ENOMEM; 23801da177e4SLinus Torvalds 2381d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 23821da177e4SLinus Torvalds BUG(); 23831da177e4SLinus Torvalds 2384a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 23851da177e4SLinus Torvalds } 23861da177e4SLinus Torvalds 238726b15dadSJamal Hadi Salim static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c) 238826b15dadSJamal Hadi Salim { 2389*fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 239026b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 23910603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 239226b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 239326b15dadSJamal Hadi Salim struct sk_buff *skb; 23947deb2264SThomas Graf int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 23950603eac0SHerbert Xu int headlen; 23960603eac0SHerbert Xu 23970603eac0SHerbert Xu headlen = sizeof(*p); 23980603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 23997deb2264SThomas Graf len += nla_total_size(headlen); 24000603eac0SHerbert Xu headlen = sizeof(*id); 24010603eac0SHerbert Xu } 2402cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 24037deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 240426b15dadSJamal Hadi Salim 24057deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 240626b15dadSJamal Hadi Salim if (skb == NULL) 240726b15dadSJamal Hadi Salim return -ENOMEM; 240826b15dadSJamal Hadi Salim 240979b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 241079b8b7f4SThomas Graf if (nlh == NULL) 241179b8b7f4SThomas Graf goto nlmsg_failure; 241226b15dadSJamal Hadi Salim 24137b67c857SThomas Graf p = nlmsg_data(nlh); 24140603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2415c0144beaSThomas Graf struct nlattr *attr; 2416c0144beaSThomas Graf 24177b67c857SThomas Graf id = nlmsg_data(nlh); 24180603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 24190603eac0SHerbert Xu id->dir = dir; 24200603eac0SHerbert Xu if (c->data.byid) 24210603eac0SHerbert Xu id->index = xp->index; 24220603eac0SHerbert Xu else 24230603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 24240603eac0SHerbert Xu 2425c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 2426c0144beaSThomas Graf if (attr == NULL) 2427c0144beaSThomas Graf goto nlmsg_failure; 2428c0144beaSThomas Graf 2429c0144beaSThomas Graf p = nla_data(attr); 24300603eac0SHerbert Xu } 243126b15dadSJamal Hadi Salim 243226b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 243326b15dadSJamal Hadi Salim if (copy_to_user_tmpl(xp, skb) < 0) 243426b15dadSJamal Hadi Salim goto nlmsg_failure; 24351459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2436f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 243726b15dadSJamal Hadi Salim 24389825069dSThomas Graf nlmsg_end(skb, nlh); 243926b15dadSJamal Hadi Salim 2440a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 244126b15dadSJamal Hadi Salim 244226b15dadSJamal Hadi Salim nlmsg_failure: 244326b15dadSJamal Hadi Salim kfree_skb(skb); 244426b15dadSJamal Hadi Salim return -1; 244526b15dadSJamal Hadi Salim } 244626b15dadSJamal Hadi Salim 244726b15dadSJamal Hadi Salim static int xfrm_notify_policy_flush(struct km_event *c) 244826b15dadSJamal Hadi Salim { 2449a6483b79SAlexey Dobriyan struct net *net = &init_net; 245026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 245126b15dadSJamal Hadi Salim struct sk_buff *skb; 245226b15dadSJamal Hadi Salim 24537deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 245426b15dadSJamal Hadi Salim if (skb == NULL) 245526b15dadSJamal Hadi Salim return -ENOMEM; 245626b15dadSJamal Hadi Salim 245779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 245879b8b7f4SThomas Graf if (nlh == NULL) 245979b8b7f4SThomas Graf goto nlmsg_failure; 24600c51f53cSJamal Hadi Salim if (copy_to_user_policy_type(c->data.type, skb) < 0) 24610c51f53cSJamal Hadi Salim goto nlmsg_failure; 246226b15dadSJamal Hadi Salim 24639825069dSThomas Graf nlmsg_end(skb, nlh); 246426b15dadSJamal Hadi Salim 2465a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 246626b15dadSJamal Hadi Salim 246726b15dadSJamal Hadi Salim nlmsg_failure: 246826b15dadSJamal Hadi Salim kfree_skb(skb); 246926b15dadSJamal Hadi Salim return -1; 247026b15dadSJamal Hadi Salim } 247126b15dadSJamal Hadi Salim 247226b15dadSJamal Hadi Salim static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 247326b15dadSJamal Hadi Salim { 247426b15dadSJamal Hadi Salim 247526b15dadSJamal Hadi Salim switch (c->event) { 2476f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2477f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2478f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 247926b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2480f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 248126b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2482f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 248326b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 248426b15dadSJamal Hadi Salim default: 248526b15dadSJamal Hadi Salim printk("xfrm_user: Unknown Policy event %d\n", c->event); 248626b15dadSJamal Hadi Salim } 248726b15dadSJamal Hadi Salim 248826b15dadSJamal Hadi Salim return 0; 248926b15dadSJamal Hadi Salim 249026b15dadSJamal Hadi Salim } 249126b15dadSJamal Hadi Salim 24927deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 24937deb2264SThomas Graf { 24947deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 24957deb2264SThomas Graf } 24967deb2264SThomas Graf 249797a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 249897a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 249997a64b45SMasahide NAKAMURA { 250097a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 250197a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 250297a64b45SMasahide NAKAMURA 250379b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 250479b8b7f4SThomas Graf if (nlh == NULL) 250579b8b7f4SThomas Graf return -EMSGSIZE; 250697a64b45SMasahide NAKAMURA 25077b67c857SThomas Graf ur = nlmsg_data(nlh); 250897a64b45SMasahide NAKAMURA ur->proto = proto; 250997a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 251097a64b45SMasahide NAKAMURA 251197a64b45SMasahide NAKAMURA if (addr) 2512c0144beaSThomas Graf NLA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); 251397a64b45SMasahide NAKAMURA 25149825069dSThomas Graf return nlmsg_end(skb, nlh); 251597a64b45SMasahide NAKAMURA 2516c0144beaSThomas Graf nla_put_failure: 25179825069dSThomas Graf nlmsg_cancel(skb, nlh); 25189825069dSThomas Graf return -EMSGSIZE; 251997a64b45SMasahide NAKAMURA } 252097a64b45SMasahide NAKAMURA 252197a64b45SMasahide NAKAMURA static int xfrm_send_report(u8 proto, struct xfrm_selector *sel, 252297a64b45SMasahide NAKAMURA xfrm_address_t *addr) 252397a64b45SMasahide NAKAMURA { 2524a6483b79SAlexey Dobriyan struct net *net = &init_net; 252597a64b45SMasahide NAKAMURA struct sk_buff *skb; 252697a64b45SMasahide NAKAMURA 25277deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 252897a64b45SMasahide NAKAMURA if (skb == NULL) 252997a64b45SMasahide NAKAMURA return -ENOMEM; 253097a64b45SMasahide NAKAMURA 253197a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 253297a64b45SMasahide NAKAMURA BUG(); 253397a64b45SMasahide NAKAMURA 2534a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 253597a64b45SMasahide NAKAMURA } 253697a64b45SMasahide NAKAMURA 25373a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 25383a2dfbe8SMartin Willi { 25393a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 25403a2dfbe8SMartin Willi } 25413a2dfbe8SMartin Willi 25423a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 25433a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 25443a2dfbe8SMartin Willi { 25453a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 25463a2dfbe8SMartin Willi struct nlmsghdr *nlh; 25473a2dfbe8SMartin Willi 25483a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 25493a2dfbe8SMartin Willi if (nlh == NULL) 25503a2dfbe8SMartin Willi return -EMSGSIZE; 25513a2dfbe8SMartin Willi 25523a2dfbe8SMartin Willi um = nlmsg_data(nlh); 25533a2dfbe8SMartin Willi 25543a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 25553a2dfbe8SMartin Willi um->id.spi = x->id.spi; 25563a2dfbe8SMartin Willi um->id.family = x->props.family; 25573a2dfbe8SMartin Willi um->id.proto = x->id.proto; 25583a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 25593a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 25603a2dfbe8SMartin Willi um->new_sport = new_sport; 25613a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 25623a2dfbe8SMartin Willi um->reqid = x->props.reqid; 25633a2dfbe8SMartin Willi 25643a2dfbe8SMartin Willi return nlmsg_end(skb, nlh); 25653a2dfbe8SMartin Willi } 25663a2dfbe8SMartin Willi 25673a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 25683a2dfbe8SMartin Willi __be16 sport) 25693a2dfbe8SMartin Willi { 2570a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 25713a2dfbe8SMartin Willi struct sk_buff *skb; 25723a2dfbe8SMartin Willi 25733a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 25743a2dfbe8SMartin Willi return -EINVAL; 25753a2dfbe8SMartin Willi 25763a2dfbe8SMartin Willi if (!x->encap) 25773a2dfbe8SMartin Willi return -EINVAL; 25783a2dfbe8SMartin Willi 25793a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 25803a2dfbe8SMartin Willi if (skb == NULL) 25813a2dfbe8SMartin Willi return -ENOMEM; 25823a2dfbe8SMartin Willi 25833a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 25843a2dfbe8SMartin Willi BUG(); 25853a2dfbe8SMartin Willi 2586a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 25873a2dfbe8SMartin Willi } 25883a2dfbe8SMartin Willi 25891da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 25901da177e4SLinus Torvalds .id = "netlink", 25911da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 25921da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 25931da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 25941da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 259597a64b45SMasahide NAKAMURA .report = xfrm_send_report, 25965c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 25973a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 25981da177e4SLinus Torvalds }; 25991da177e4SLinus Torvalds 2600a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 26011da177e4SLinus Torvalds { 2602be33690dSPatrick McHardy struct sock *nlsk; 2603be33690dSPatrick McHardy 2604a6483b79SAlexey Dobriyan nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, 2605af65bdfcSPatrick McHardy xfrm_netlink_rcv, NULL, THIS_MODULE); 2606be33690dSPatrick McHardy if (nlsk == NULL) 26071da177e4SLinus Torvalds return -ENOMEM; 2608a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, nlsk); 26091da177e4SLinus Torvalds return 0; 26101da177e4SLinus Torvalds } 26111da177e4SLinus Torvalds 2612a6483b79SAlexey Dobriyan static void __net_exit xfrm_user_net_exit(struct net *net) 2613a6483b79SAlexey Dobriyan { 2614a6483b79SAlexey Dobriyan struct sock *nlsk = net->xfrm.nlsk; 2615a6483b79SAlexey Dobriyan 2616a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, NULL); 2617a6483b79SAlexey Dobriyan synchronize_rcu(); 2618a6483b79SAlexey Dobriyan netlink_kernel_release(nlsk); 2619a6483b79SAlexey Dobriyan } 2620a6483b79SAlexey Dobriyan 2621a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 2622a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 2623a6483b79SAlexey Dobriyan .exit = xfrm_user_net_exit, 2624a6483b79SAlexey Dobriyan }; 2625a6483b79SAlexey Dobriyan 2626a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 2627a6483b79SAlexey Dobriyan { 2628a6483b79SAlexey Dobriyan int rv; 2629a6483b79SAlexey Dobriyan 2630a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 2631a6483b79SAlexey Dobriyan 2632a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 2633a6483b79SAlexey Dobriyan if (rv < 0) 2634a6483b79SAlexey Dobriyan return rv; 2635a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 2636a6483b79SAlexey Dobriyan if (rv < 0) 2637a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 2638a6483b79SAlexey Dobriyan return rv; 2639a6483b79SAlexey Dobriyan } 2640a6483b79SAlexey Dobriyan 26411da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 26421da177e4SLinus Torvalds { 26431da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 2644a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 26451da177e4SLinus Torvalds } 26461da177e4SLinus Torvalds 26471da177e4SLinus Torvalds module_init(xfrm_user_init); 26481da177e4SLinus Torvalds module_exit(xfrm_user_exit); 26491da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 26504fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 2651f8cd5488SJamal Hadi Salim 2652