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/rtnetlink.h> 231da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 241da177e4SLinus Torvalds #include <linux/ipsec.h> 251da177e4SLinus Torvalds #include <linux/init.h> 261da177e4SLinus Torvalds #include <linux/security.h> 271da177e4SLinus Torvalds #include <net/sock.h> 281da177e4SLinus Torvalds #include <net/xfrm.h> 2988fc2c84SThomas Graf #include <net/netlink.h> 301da177e4SLinus Torvalds #include <asm/uaccess.h> 31e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 32e23c7194SMasahide NAKAMURA #include <linux/in6.h> 33e23c7194SMasahide NAKAMURA #endif 34161a09e7SJoy Latten #include <linux/audit.h> 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds static int verify_one_alg(struct rtattr **xfrma, enum xfrm_attr_type_t type) 371da177e4SLinus Torvalds { 381da177e4SLinus Torvalds struct rtattr *rt = xfrma[type - 1]; 391da177e4SLinus Torvalds struct xfrm_algo *algp; 4031c26852SHerbert Xu int len; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds if (!rt) 431da177e4SLinus Torvalds return 0; 441da177e4SLinus Torvalds 4531c26852SHerbert Xu len = (rt->rta_len - sizeof(*rt)) - sizeof(*algp); 4631c26852SHerbert Xu if (len < 0) 471da177e4SLinus Torvalds return -EINVAL; 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds algp = RTA_DATA(rt); 5031c26852SHerbert Xu 5131c26852SHerbert Xu len -= (algp->alg_key_len + 7U) / 8; 5231c26852SHerbert Xu if (len < 0) 5331c26852SHerbert Xu return -EINVAL; 5431c26852SHerbert Xu 551da177e4SLinus Torvalds switch (type) { 561da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 571da177e4SLinus Torvalds if (!algp->alg_key_len && 581da177e4SLinus Torvalds strcmp(algp->alg_name, "digest_null") != 0) 591da177e4SLinus Torvalds return -EINVAL; 601da177e4SLinus Torvalds break; 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 631da177e4SLinus Torvalds if (!algp->alg_key_len && 641da177e4SLinus Torvalds strcmp(algp->alg_name, "cipher_null") != 0) 651da177e4SLinus Torvalds return -EINVAL; 661da177e4SLinus Torvalds break; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds case XFRMA_ALG_COMP: 691da177e4SLinus Torvalds /* Zero length keys are legal. */ 701da177e4SLinus Torvalds break; 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds default: 731da177e4SLinus Torvalds return -EINVAL; 743ff50b79SStephen Hemminger } 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 771da177e4SLinus Torvalds return 0; 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static int verify_encap_tmpl(struct rtattr **xfrma) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds struct rtattr *rt = xfrma[XFRMA_ENCAP - 1]; 831da177e4SLinus Torvalds struct xfrm_encap_tmpl *encap; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds if (!rt) 861da177e4SLinus Torvalds return 0; 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds if ((rt->rta_len - sizeof(*rt)) < sizeof(*encap)) 891da177e4SLinus Torvalds return -EINVAL; 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds return 0; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 94eb2971b6SMasahide NAKAMURA static int verify_one_addr(struct rtattr **xfrma, enum xfrm_attr_type_t type, 95eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 96eb2971b6SMasahide NAKAMURA { 97eb2971b6SMasahide NAKAMURA struct rtattr *rt = xfrma[type - 1]; 98eb2971b6SMasahide NAKAMURA 99eb2971b6SMasahide NAKAMURA if (!rt) 100eb2971b6SMasahide NAKAMURA return 0; 101eb2971b6SMasahide NAKAMURA 102eb2971b6SMasahide NAKAMURA if ((rt->rta_len - sizeof(*rt)) < sizeof(**addrp)) 103eb2971b6SMasahide NAKAMURA return -EINVAL; 104eb2971b6SMasahide NAKAMURA 105eb2971b6SMasahide NAKAMURA if (addrp) 106eb2971b6SMasahide NAKAMURA *addrp = RTA_DATA(rt); 107eb2971b6SMasahide NAKAMURA 108eb2971b6SMasahide NAKAMURA return 0; 109eb2971b6SMasahide NAKAMURA } 110df71837dSTrent Jaeger 111df71837dSTrent Jaeger static inline int verify_sec_ctx_len(struct rtattr **xfrma) 112df71837dSTrent Jaeger { 113df71837dSTrent Jaeger struct rtattr *rt = xfrma[XFRMA_SEC_CTX - 1]; 114df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 115df71837dSTrent Jaeger int len = 0; 116df71837dSTrent Jaeger 117df71837dSTrent Jaeger if (!rt) 118df71837dSTrent Jaeger return 0; 119df71837dSTrent Jaeger 120df71837dSTrent Jaeger if (rt->rta_len < sizeof(*uctx)) 121df71837dSTrent Jaeger return -EINVAL; 122df71837dSTrent Jaeger 123df71837dSTrent Jaeger uctx = RTA_DATA(rt); 124df71837dSTrent Jaeger 125df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 126df71837dSTrent Jaeger len += uctx->ctx_len; 127df71837dSTrent Jaeger 128df71837dSTrent Jaeger if (uctx->len != len) 129df71837dSTrent Jaeger return -EINVAL; 130df71837dSTrent Jaeger 131df71837dSTrent Jaeger return 0; 132df71837dSTrent Jaeger } 133df71837dSTrent Jaeger 134df71837dSTrent Jaeger 1351da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1361da177e4SLinus Torvalds struct rtattr **xfrma) 1371da177e4SLinus Torvalds { 1381da177e4SLinus Torvalds int err; 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds err = -EINVAL; 1411da177e4SLinus Torvalds switch (p->family) { 1421da177e4SLinus Torvalds case AF_INET: 1431da177e4SLinus Torvalds break; 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds case AF_INET6: 1461da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1471da177e4SLinus Torvalds break; 1481da177e4SLinus Torvalds #else 1491da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1501da177e4SLinus Torvalds goto out; 1511da177e4SLinus Torvalds #endif 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds default: 1541da177e4SLinus Torvalds goto out; 1553ff50b79SStephen Hemminger } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds err = -EINVAL; 1581da177e4SLinus Torvalds switch (p->id.proto) { 1591da177e4SLinus Torvalds case IPPROTO_AH: 1601da177e4SLinus Torvalds if (!xfrma[XFRMA_ALG_AUTH-1] || 1611da177e4SLinus Torvalds xfrma[XFRMA_ALG_CRYPT-1] || 1621da177e4SLinus Torvalds xfrma[XFRMA_ALG_COMP-1]) 1631da177e4SLinus Torvalds goto out; 1641da177e4SLinus Torvalds break; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds case IPPROTO_ESP: 1671da177e4SLinus Torvalds if ((!xfrma[XFRMA_ALG_AUTH-1] && 1681da177e4SLinus Torvalds !xfrma[XFRMA_ALG_CRYPT-1]) || 1691da177e4SLinus Torvalds xfrma[XFRMA_ALG_COMP-1]) 1701da177e4SLinus Torvalds goto out; 1711da177e4SLinus Torvalds break; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds case IPPROTO_COMP: 1741da177e4SLinus Torvalds if (!xfrma[XFRMA_ALG_COMP-1] || 1751da177e4SLinus Torvalds xfrma[XFRMA_ALG_AUTH-1] || 1761da177e4SLinus Torvalds xfrma[XFRMA_ALG_CRYPT-1]) 1771da177e4SLinus Torvalds goto out; 1781da177e4SLinus Torvalds break; 1791da177e4SLinus Torvalds 180e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 181e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 182e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 183e23c7194SMasahide NAKAMURA if (xfrma[XFRMA_ALG_COMP-1] || 184e23c7194SMasahide NAKAMURA xfrma[XFRMA_ALG_AUTH-1] || 185e23c7194SMasahide NAKAMURA xfrma[XFRMA_ALG_CRYPT-1] || 186e23c7194SMasahide NAKAMURA xfrma[XFRMA_ENCAP-1] || 187e23c7194SMasahide NAKAMURA xfrma[XFRMA_SEC_CTX-1] || 188e23c7194SMasahide NAKAMURA !xfrma[XFRMA_COADDR-1]) 189e23c7194SMasahide NAKAMURA goto out; 190e23c7194SMasahide NAKAMURA break; 191e23c7194SMasahide NAKAMURA #endif 192e23c7194SMasahide NAKAMURA 1931da177e4SLinus Torvalds default: 1941da177e4SLinus Torvalds goto out; 1953ff50b79SStephen Hemminger } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds if ((err = verify_one_alg(xfrma, XFRMA_ALG_AUTH))) 1981da177e4SLinus Torvalds goto out; 1991da177e4SLinus Torvalds if ((err = verify_one_alg(xfrma, XFRMA_ALG_CRYPT))) 2001da177e4SLinus Torvalds goto out; 2011da177e4SLinus Torvalds if ((err = verify_one_alg(xfrma, XFRMA_ALG_COMP))) 2021da177e4SLinus Torvalds goto out; 2031da177e4SLinus Torvalds if ((err = verify_encap_tmpl(xfrma))) 2041da177e4SLinus Torvalds goto out; 205df71837dSTrent Jaeger if ((err = verify_sec_ctx_len(xfrma))) 206df71837dSTrent Jaeger goto out; 207060f02a3SNoriaki TAKAMIYA if ((err = verify_one_addr(xfrma, XFRMA_COADDR, NULL))) 208060f02a3SNoriaki TAKAMIYA goto out; 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds err = -EINVAL; 2111da177e4SLinus Torvalds switch (p->mode) { 2127e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2137e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 214060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2150a69452cSDiego Beltrami case XFRM_MODE_BEET: 2161da177e4SLinus Torvalds break; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds default: 2191da177e4SLinus Torvalds goto out; 2203ff50b79SStephen Hemminger } 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds err = 0; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds out: 2251da177e4SLinus Torvalds return err; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2291da177e4SLinus Torvalds struct xfrm_algo_desc *(*get_byname)(char *, int), 2301da177e4SLinus Torvalds struct rtattr *u_arg) 2311da177e4SLinus Torvalds { 2321da177e4SLinus Torvalds struct rtattr *rta = u_arg; 2331da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2341da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 235b9e9deadSHerbert Xu int len; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds if (!rta) 2381da177e4SLinus Torvalds return 0; 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds ualg = RTA_DATA(rta); 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2431da177e4SLinus Torvalds if (!algo) 2441da177e4SLinus Torvalds return -ENOSYS; 2451da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2461da177e4SLinus Torvalds 247b9e9deadSHerbert Xu len = sizeof(*ualg) + (ualg->alg_key_len + 7U) / 8; 248cdbc6daeSArnaldo Carvalho de Melo p = kmemdup(ualg, len, GFP_KERNEL); 2491da177e4SLinus Torvalds if (!p) 2501da177e4SLinus Torvalds return -ENOMEM; 2511da177e4SLinus Torvalds 25204ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2531da177e4SLinus Torvalds *algpp = p; 2541da177e4SLinus Torvalds return 0; 2551da177e4SLinus Torvalds } 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds static int attach_encap_tmpl(struct xfrm_encap_tmpl **encapp, struct rtattr *u_arg) 2581da177e4SLinus Torvalds { 2591da177e4SLinus Torvalds struct rtattr *rta = u_arg; 2601da177e4SLinus Torvalds struct xfrm_encap_tmpl *p, *uencap; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds if (!rta) 2631da177e4SLinus Torvalds return 0; 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds uencap = RTA_DATA(rta); 266cdbc6daeSArnaldo Carvalho de Melo p = kmemdup(uencap, sizeof(*p), GFP_KERNEL); 2671da177e4SLinus Torvalds if (!p) 2681da177e4SLinus Torvalds return -ENOMEM; 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds *encapp = p; 2711da177e4SLinus Torvalds return 0; 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 274df71837dSTrent Jaeger 275661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 276df71837dSTrent Jaeger { 277df71837dSTrent Jaeger int len = 0; 278df71837dSTrent Jaeger 279df71837dSTrent Jaeger if (xfrm_ctx) { 280df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 281df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 282df71837dSTrent Jaeger } 283df71837dSTrent Jaeger return len; 284df71837dSTrent Jaeger } 285df71837dSTrent Jaeger 286df71837dSTrent Jaeger static int attach_sec_ctx(struct xfrm_state *x, struct rtattr *u_arg) 287df71837dSTrent Jaeger { 288df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 289df71837dSTrent Jaeger 290df71837dSTrent Jaeger if (!u_arg) 291df71837dSTrent Jaeger return 0; 292df71837dSTrent Jaeger 293df71837dSTrent Jaeger uctx = RTA_DATA(u_arg); 294df71837dSTrent Jaeger return security_xfrm_state_alloc(x, uctx); 295df71837dSTrent Jaeger } 296df71837dSTrent Jaeger 297060f02a3SNoriaki TAKAMIYA static int attach_one_addr(xfrm_address_t **addrpp, struct rtattr *u_arg) 298060f02a3SNoriaki TAKAMIYA { 299060f02a3SNoriaki TAKAMIYA struct rtattr *rta = u_arg; 300060f02a3SNoriaki TAKAMIYA xfrm_address_t *p, *uaddrp; 301060f02a3SNoriaki TAKAMIYA 302060f02a3SNoriaki TAKAMIYA if (!rta) 303060f02a3SNoriaki TAKAMIYA return 0; 304060f02a3SNoriaki TAKAMIYA 305060f02a3SNoriaki TAKAMIYA uaddrp = RTA_DATA(rta); 306cdbc6daeSArnaldo Carvalho de Melo p = kmemdup(uaddrp, sizeof(*p), GFP_KERNEL); 307060f02a3SNoriaki TAKAMIYA if (!p) 308060f02a3SNoriaki TAKAMIYA return -ENOMEM; 309060f02a3SNoriaki TAKAMIYA 310060f02a3SNoriaki TAKAMIYA *addrpp = p; 311060f02a3SNoriaki TAKAMIYA return 0; 312060f02a3SNoriaki TAKAMIYA } 313060f02a3SNoriaki TAKAMIYA 3141da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 3171da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 3181da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 3191da177e4SLinus Torvalds x->props.mode = p->mode; 3201da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 3211da177e4SLinus Torvalds x->props.reqid = p->reqid; 3221da177e4SLinus Torvalds x->props.family = p->family; 32354489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 3241da177e4SLinus Torvalds x->props.flags = p->flags; 325*196b0036SHerbert Xu 326*196b0036SHerbert Xu /* 327*196b0036SHerbert Xu * Set inner address family if the KM left it as zero. 328*196b0036SHerbert Xu * See comment in validate_tmpl. 329*196b0036SHerbert Xu */ 330*196b0036SHerbert Xu if (!x->sel.family) 331*196b0036SHerbert Xu x->sel.family = p->family; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 334d51d081dSJamal Hadi Salim /* 335d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 336d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 337d51d081dSJamal Hadi Salim * 338d51d081dSJamal Hadi Salim */ 339d51d081dSJamal Hadi Salim static int xfrm_update_ae_params(struct xfrm_state *x, struct rtattr **xfrma) 340d51d081dSJamal Hadi Salim { 341d51d081dSJamal Hadi Salim int err = - EINVAL; 342d51d081dSJamal Hadi Salim struct rtattr *rp = xfrma[XFRMA_REPLAY_VAL-1]; 343d51d081dSJamal Hadi Salim struct rtattr *lt = xfrma[XFRMA_LTIME_VAL-1]; 344d51d081dSJamal Hadi Salim struct rtattr *et = xfrma[XFRMA_ETIMER_THRESH-1]; 345d51d081dSJamal Hadi Salim struct rtattr *rt = xfrma[XFRMA_REPLAY_THRESH-1]; 346d51d081dSJamal Hadi Salim 347d51d081dSJamal Hadi Salim if (rp) { 348d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 349d51d081dSJamal Hadi Salim if (RTA_PAYLOAD(rp) < sizeof(*replay)) 350d51d081dSJamal Hadi Salim goto error; 351d51d081dSJamal Hadi Salim replay = RTA_DATA(rp); 352d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 353d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 354d51d081dSJamal Hadi Salim } 355d51d081dSJamal Hadi Salim 356d51d081dSJamal Hadi Salim if (lt) { 357d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 358d51d081dSJamal Hadi Salim if (RTA_PAYLOAD(lt) < sizeof(*ltime)) 359d51d081dSJamal Hadi Salim goto error; 360d51d081dSJamal Hadi Salim ltime = RTA_DATA(lt); 361d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 362d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 363d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 364d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 365d51d081dSJamal Hadi Salim } 366d51d081dSJamal Hadi Salim 367d51d081dSJamal Hadi Salim if (et) { 368d51d081dSJamal Hadi Salim if (RTA_PAYLOAD(et) < sizeof(u32)) 369d51d081dSJamal Hadi Salim goto error; 370d51d081dSJamal Hadi Salim x->replay_maxage = *(u32*)RTA_DATA(et); 371d51d081dSJamal Hadi Salim } 372d51d081dSJamal Hadi Salim 373d51d081dSJamal Hadi Salim if (rt) { 374d51d081dSJamal Hadi Salim if (RTA_PAYLOAD(rt) < sizeof(u32)) 375d51d081dSJamal Hadi Salim goto error; 376d51d081dSJamal Hadi Salim x->replay_maxdiff = *(u32*)RTA_DATA(rt); 377d51d081dSJamal Hadi Salim } 378d51d081dSJamal Hadi Salim 379d51d081dSJamal Hadi Salim return 0; 380d51d081dSJamal Hadi Salim error: 381d51d081dSJamal Hadi Salim return err; 382d51d081dSJamal Hadi Salim } 383d51d081dSJamal Hadi Salim 3841da177e4SLinus Torvalds static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, 3851da177e4SLinus Torvalds struct rtattr **xfrma, 3861da177e4SLinus Torvalds int *errp) 3871da177e4SLinus Torvalds { 3881da177e4SLinus Torvalds struct xfrm_state *x = xfrm_state_alloc(); 3891da177e4SLinus Torvalds int err = -ENOMEM; 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds if (!x) 3921da177e4SLinus Torvalds goto error_no_put; 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds copy_from_user_state(x, p); 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds if ((err = attach_one_algo(&x->aalg, &x->props.aalgo, 3971da177e4SLinus Torvalds xfrm_aalg_get_byname, 3981da177e4SLinus Torvalds xfrma[XFRMA_ALG_AUTH-1]))) 3991da177e4SLinus Torvalds goto error; 4001da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 4011da177e4SLinus Torvalds xfrm_ealg_get_byname, 4021da177e4SLinus Torvalds xfrma[XFRMA_ALG_CRYPT-1]))) 4031da177e4SLinus Torvalds goto error; 4041da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 4051da177e4SLinus Torvalds xfrm_calg_get_byname, 4061da177e4SLinus Torvalds xfrma[XFRMA_ALG_COMP-1]))) 4071da177e4SLinus Torvalds goto error; 4081da177e4SLinus Torvalds if ((err = attach_encap_tmpl(&x->encap, xfrma[XFRMA_ENCAP-1]))) 4091da177e4SLinus Torvalds goto error; 410060f02a3SNoriaki TAKAMIYA if ((err = attach_one_addr(&x->coaddr, xfrma[XFRMA_COADDR-1]))) 411060f02a3SNoriaki TAKAMIYA goto error; 41272cb6962SHerbert Xu err = xfrm_init_state(x); 4131da177e4SLinus Torvalds if (err) 4141da177e4SLinus Torvalds goto error; 4151da177e4SLinus Torvalds 416df71837dSTrent Jaeger if ((err = attach_sec_ctx(x, xfrma[XFRMA_SEC_CTX-1]))) 417df71837dSTrent Jaeger goto error; 418df71837dSTrent Jaeger 4191da177e4SLinus Torvalds x->km.seq = p->seq; 420d51d081dSJamal Hadi Salim x->replay_maxdiff = sysctl_xfrm_aevent_rseqth; 421d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 422d51d081dSJamal Hadi Salim x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M; 423d51d081dSJamal Hadi Salim x->preplay.bitmap = 0; 424d51d081dSJamal Hadi Salim x->preplay.seq = x->replay.seq+x->replay_maxdiff; 425d51d081dSJamal Hadi Salim x->preplay.oseq = x->replay.oseq +x->replay_maxdiff; 426d51d081dSJamal Hadi Salim 427d51d081dSJamal Hadi Salim /* override default values from above */ 428d51d081dSJamal Hadi Salim 429d51d081dSJamal Hadi Salim err = xfrm_update_ae_params(x, (struct rtattr **)xfrma); 430d51d081dSJamal Hadi Salim if (err < 0) 431d51d081dSJamal Hadi Salim goto error; 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds return x; 4341da177e4SLinus Torvalds 4351da177e4SLinus Torvalds error: 4361da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 4371da177e4SLinus Torvalds xfrm_state_put(x); 4381da177e4SLinus Torvalds error_no_put: 4391da177e4SLinus Torvalds *errp = err; 4401da177e4SLinus Torvalds return NULL; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 44322e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 44422e70050SChristoph Hellwig struct rtattr **xfrma) 4451da177e4SLinus Torvalds { 4461da177e4SLinus Torvalds struct xfrm_usersa_info *p = NLMSG_DATA(nlh); 4471da177e4SLinus Torvalds struct xfrm_state *x; 4481da177e4SLinus Torvalds int err; 44926b15dadSJamal Hadi Salim struct km_event c; 4501da177e4SLinus Torvalds 45122e70050SChristoph Hellwig err = verify_newsa_info(p, xfrma); 4521da177e4SLinus Torvalds if (err) 4531da177e4SLinus Torvalds return err; 4541da177e4SLinus Torvalds 45522e70050SChristoph Hellwig x = xfrm_state_construct(p, xfrma, &err); 4561da177e4SLinus Torvalds if (!x) 4571da177e4SLinus Torvalds return err; 4581da177e4SLinus Torvalds 45926b15dadSJamal Hadi Salim xfrm_state_hold(x); 4601da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 4611da177e4SLinus Torvalds err = xfrm_state_add(x); 4621da177e4SLinus Torvalds else 4631da177e4SLinus Torvalds err = xfrm_state_update(x); 4641da177e4SLinus Torvalds 465161a09e7SJoy Latten xfrm_audit_log(NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid, 466161a09e7SJoy Latten AUDIT_MAC_IPSEC_ADDSA, err ? 0 : 1, NULL, x); 467161a09e7SJoy Latten 4681da177e4SLinus Torvalds if (err < 0) { 4691da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 47021380b81SHerbert Xu __xfrm_state_put(x); 4717d6dfe1fSPatrick McHardy goto out; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds 47426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 47526b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 476f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 47726b15dadSJamal Hadi Salim 47826b15dadSJamal Hadi Salim km_state_notify(x, &c); 4797d6dfe1fSPatrick McHardy out: 48026b15dadSJamal Hadi Salim xfrm_state_put(x); 4811da177e4SLinus Torvalds return err; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 484eb2971b6SMasahide NAKAMURA static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p, 485eb2971b6SMasahide NAKAMURA struct rtattr **xfrma, 486eb2971b6SMasahide NAKAMURA int *errp) 487eb2971b6SMasahide NAKAMURA { 488eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 489eb2971b6SMasahide NAKAMURA int err; 490eb2971b6SMasahide NAKAMURA 491eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 492eb2971b6SMasahide NAKAMURA err = -ESRCH; 493eb2971b6SMasahide NAKAMURA x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family); 494eb2971b6SMasahide NAKAMURA } else { 495eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 496eb2971b6SMasahide NAKAMURA 497eb2971b6SMasahide NAKAMURA err = verify_one_addr(xfrma, XFRMA_SRCADDR, &saddr); 498eb2971b6SMasahide NAKAMURA if (err) 499eb2971b6SMasahide NAKAMURA goto out; 500eb2971b6SMasahide NAKAMURA 501eb2971b6SMasahide NAKAMURA if (!saddr) { 502eb2971b6SMasahide NAKAMURA err = -EINVAL; 503eb2971b6SMasahide NAKAMURA goto out; 504eb2971b6SMasahide NAKAMURA } 505eb2971b6SMasahide NAKAMURA 5069abbffeeSMasahide NAKAMURA err = -ESRCH; 507eb2971b6SMasahide NAKAMURA x = xfrm_state_lookup_byaddr(&p->daddr, saddr, p->proto, 508eb2971b6SMasahide NAKAMURA p->family); 509eb2971b6SMasahide NAKAMURA } 510eb2971b6SMasahide NAKAMURA 511eb2971b6SMasahide NAKAMURA out: 512eb2971b6SMasahide NAKAMURA if (!x && errp) 513eb2971b6SMasahide NAKAMURA *errp = err; 514eb2971b6SMasahide NAKAMURA return x; 515eb2971b6SMasahide NAKAMURA } 516eb2971b6SMasahide NAKAMURA 51722e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 51822e70050SChristoph Hellwig struct rtattr **xfrma) 5191da177e4SLinus Torvalds { 5201da177e4SLinus Torvalds struct xfrm_state *x; 521eb2971b6SMasahide NAKAMURA int err = -ESRCH; 52226b15dadSJamal Hadi Salim struct km_event c; 5231da177e4SLinus Torvalds struct xfrm_usersa_id *p = NLMSG_DATA(nlh); 5241da177e4SLinus Torvalds 52522e70050SChristoph Hellwig x = xfrm_user_state_lookup(p, xfrma, &err); 5261da177e4SLinus Torvalds if (x == NULL) 527eb2971b6SMasahide NAKAMURA return err; 5281da177e4SLinus Torvalds 5296f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 530c8c05a8eSCatherine Zhang goto out; 531c8c05a8eSCatherine Zhang 5321da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 533c8c05a8eSCatherine Zhang err = -EPERM; 534c8c05a8eSCatherine Zhang goto out; 5351da177e4SLinus Torvalds } 5361da177e4SLinus Torvalds 53726b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 538161a09e7SJoy Latten 539c8c05a8eSCatherine Zhang if (err < 0) 540c8c05a8eSCatherine Zhang goto out; 54126b15dadSJamal Hadi Salim 54226b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 54326b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 544f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 54526b15dadSJamal Hadi Salim km_state_notify(x, &c); 5461da177e4SLinus Torvalds 547c8c05a8eSCatherine Zhang out: 54816bec31dSEric Paris xfrm_audit_log(NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid, 54916bec31dSEric Paris AUDIT_MAC_IPSEC_DELSA, err ? 0 : 1, NULL, x); 550c8c05a8eSCatherine Zhang xfrm_state_put(x); 55126b15dadSJamal Hadi Salim return err; 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 5551da177e4SLinus Torvalds { 5561da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 5571da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 5581da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 5591da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 5601da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 56154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 5621da177e4SLinus Torvalds p->mode = x->props.mode; 5631da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 5641da177e4SLinus Torvalds p->reqid = x->props.reqid; 5651da177e4SLinus Torvalds p->family = x->props.family; 5661da177e4SLinus Torvalds p->flags = x->props.flags; 5671da177e4SLinus Torvalds p->seq = x->km.seq; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds struct xfrm_dump_info { 5711da177e4SLinus Torvalds struct sk_buff *in_skb; 5721da177e4SLinus Torvalds struct sk_buff *out_skb; 5731da177e4SLinus Torvalds u32 nlmsg_seq; 5741da177e4SLinus Torvalds u16 nlmsg_flags; 5751da177e4SLinus Torvalds int start_idx; 5761da177e4SLinus Torvalds int this_idx; 5771da177e4SLinus Torvalds }; 5781da177e4SLinus Torvalds 5791da177e4SLinus Torvalds static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 5801da177e4SLinus Torvalds { 5811da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 5821da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 5831da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 5841da177e4SLinus Torvalds struct xfrm_usersa_info *p; 5851da177e4SLinus Torvalds struct nlmsghdr *nlh; 58627a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds if (sp->this_idx < sp->start_idx) 5891da177e4SLinus Torvalds goto out; 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, 5921da177e4SLinus Torvalds sp->nlmsg_seq, 5931da177e4SLinus Torvalds XFRM_MSG_NEWSA, sizeof(*p)); 5941da177e4SLinus Torvalds nlh->nlmsg_flags = sp->nlmsg_flags; 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds p = NLMSG_DATA(nlh); 5971da177e4SLinus Torvalds copy_to_user_state(x, p); 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds if (x->aalg) 6001da177e4SLinus Torvalds RTA_PUT(skb, XFRMA_ALG_AUTH, 6011da177e4SLinus Torvalds sizeof(*(x->aalg))+(x->aalg->alg_key_len+7)/8, x->aalg); 6021da177e4SLinus Torvalds if (x->ealg) 6031da177e4SLinus Torvalds RTA_PUT(skb, XFRMA_ALG_CRYPT, 6041da177e4SLinus Torvalds sizeof(*(x->ealg))+(x->ealg->alg_key_len+7)/8, x->ealg); 6051da177e4SLinus Torvalds if (x->calg) 6061da177e4SLinus Torvalds RTA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds if (x->encap) 6091da177e4SLinus Torvalds RTA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 6101da177e4SLinus Torvalds 611df71837dSTrent Jaeger if (x->security) { 612df71837dSTrent Jaeger int ctx_size = sizeof(struct xfrm_sec_ctx) + 613df71837dSTrent Jaeger x->security->ctx_len; 614df71837dSTrent Jaeger struct rtattr *rt = __RTA_PUT(skb, XFRMA_SEC_CTX, ctx_size); 615df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt); 616df71837dSTrent Jaeger 617df71837dSTrent Jaeger uctx->exttype = XFRMA_SEC_CTX; 618df71837dSTrent Jaeger uctx->len = ctx_size; 619df71837dSTrent Jaeger uctx->ctx_doi = x->security->ctx_doi; 620df71837dSTrent Jaeger uctx->ctx_alg = x->security->ctx_alg; 621df71837dSTrent Jaeger uctx->ctx_len = x->security->ctx_len; 622df71837dSTrent Jaeger memcpy(uctx + 1, x->security->ctx_str, x->security->ctx_len); 623df71837dSTrent Jaeger } 624060f02a3SNoriaki TAKAMIYA 625060f02a3SNoriaki TAKAMIYA if (x->coaddr) 626060f02a3SNoriaki TAKAMIYA RTA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 627060f02a3SNoriaki TAKAMIYA 6289afaca05SMasahide NAKAMURA if (x->lastused) 6299afaca05SMasahide NAKAMURA RTA_PUT(skb, XFRMA_LASTUSED, sizeof(x->lastused), &x->lastused); 6309afaca05SMasahide NAKAMURA 63127a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 6321da177e4SLinus Torvalds out: 6331da177e4SLinus Torvalds sp->this_idx++; 6341da177e4SLinus Torvalds return 0; 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds nlmsg_failure: 6371da177e4SLinus Torvalds rtattr_failure: 638dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 6391da177e4SLinus Torvalds return -1; 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds 6421da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 6431da177e4SLinus Torvalds { 6441da177e4SLinus Torvalds struct xfrm_dump_info info; 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds info.in_skb = cb->skb; 6471da177e4SLinus Torvalds info.out_skb = skb; 6481da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 6491da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 6501da177e4SLinus Torvalds info.this_idx = 0; 6511da177e4SLinus Torvalds info.start_idx = cb->args[0]; 652dc00a525SMasahide NAKAMURA (void) xfrm_state_walk(0, dump_one_state, &info); 6531da177e4SLinus Torvalds cb->args[0] = info.this_idx; 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds return skb->len; 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 6581da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 6591da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 6601da177e4SLinus Torvalds { 6611da177e4SLinus Torvalds struct xfrm_dump_info info; 6621da177e4SLinus Torvalds struct sk_buff *skb; 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); 6651da177e4SLinus Torvalds if (!skb) 6661da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds info.in_skb = in_skb; 6691da177e4SLinus Torvalds info.out_skb = skb; 6701da177e4SLinus Torvalds info.nlmsg_seq = seq; 6711da177e4SLinus Torvalds info.nlmsg_flags = 0; 6721da177e4SLinus Torvalds info.this_idx = info.start_idx = 0; 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds if (dump_one_state(x, 0, &info)) { 6751da177e4SLinus Torvalds kfree_skb(skb); 6761da177e4SLinus Torvalds return NULL; 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds return skb; 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds 682ecfd6b18SJamal Hadi Salim static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) 683ecfd6b18SJamal Hadi Salim { 6845a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 6855a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 6865a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 687ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 688ecfd6b18SJamal Hadi Salim u32 *f; 689ecfd6b18SJamal Hadi Salim 690ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 691ecfd6b18SJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 692ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 693ecfd6b18SJamal Hadi Salim 694ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 695ecfd6b18SJamal Hadi Salim *f = flags; 696ecfd6b18SJamal Hadi Salim xfrm_spd_getinfo(&si); 6975a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 6985a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 6995a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 7005a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 7015a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 7025a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 7035a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 7045a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 705ecfd6b18SJamal Hadi Salim 7065a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 7075a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 708ecfd6b18SJamal Hadi Salim 709ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 710ecfd6b18SJamal Hadi Salim 711ecfd6b18SJamal Hadi Salim nla_put_failure: 712ecfd6b18SJamal Hadi Salim nlmsg_cancel(skb, nlh); 713ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 714ecfd6b18SJamal Hadi Salim } 715ecfd6b18SJamal Hadi Salim 716ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 717ecfd6b18SJamal Hadi Salim struct rtattr **xfrma) 718ecfd6b18SJamal Hadi Salim { 719ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 720ecfd6b18SJamal Hadi Salim u32 *flags = NLMSG_DATA(nlh); 721ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 722ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 723ecfd6b18SJamal Hadi Salim int len = NLMSG_LENGTH(sizeof(u32)); 724ecfd6b18SJamal Hadi Salim 7255a6d3416SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrmu_spdinfo)); 7265a6d3416SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrmu_spdhinfo)); 727ecfd6b18SJamal Hadi Salim 728ecfd6b18SJamal Hadi Salim r_skb = alloc_skb(len, GFP_ATOMIC); 729ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 730ecfd6b18SJamal Hadi Salim return -ENOMEM; 731ecfd6b18SJamal Hadi Salim 732ecfd6b18SJamal Hadi Salim if (build_spdinfo(r_skb, spid, seq, *flags) < 0) 733ecfd6b18SJamal Hadi Salim BUG(); 734ecfd6b18SJamal Hadi Salim 735ecfd6b18SJamal Hadi Salim return nlmsg_unicast(xfrm_nl, r_skb, spid); 736ecfd6b18SJamal Hadi Salim } 737ecfd6b18SJamal Hadi Salim 73828d8909bSJamal Hadi Salim static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags) 73928d8909bSJamal Hadi Salim { 740af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 741af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 74228d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 74328d8909bSJamal Hadi Salim u32 *f; 74428d8909bSJamal Hadi Salim 74528d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 74628d8909bSJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 74728d8909bSJamal Hadi Salim return -EMSGSIZE; 74828d8909bSJamal Hadi Salim 74928d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 75028d8909bSJamal Hadi Salim *f = flags; 75128d8909bSJamal Hadi Salim xfrm_sad_getinfo(&si); 75228d8909bSJamal Hadi Salim 753af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 754af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 755af11e316SJamal Hadi Salim 756af11e316SJamal Hadi Salim NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); 757af11e316SJamal Hadi Salim NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 75828d8909bSJamal Hadi Salim 75928d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 76028d8909bSJamal Hadi Salim 76128d8909bSJamal Hadi Salim nla_put_failure: 76228d8909bSJamal Hadi Salim nlmsg_cancel(skb, nlh); 76328d8909bSJamal Hadi Salim return -EMSGSIZE; 76428d8909bSJamal Hadi Salim } 76528d8909bSJamal Hadi Salim 76628d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 76728d8909bSJamal Hadi Salim struct rtattr **xfrma) 76828d8909bSJamal Hadi Salim { 76928d8909bSJamal Hadi Salim struct sk_buff *r_skb; 77028d8909bSJamal Hadi Salim u32 *flags = NLMSG_DATA(nlh); 77128d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 77228d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 77328d8909bSJamal Hadi Salim int len = NLMSG_LENGTH(sizeof(u32)); 77428d8909bSJamal Hadi Salim 775af11e316SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrmu_sadhinfo)); 77628d8909bSJamal Hadi Salim len += RTA_SPACE(sizeof(u32)); 77728d8909bSJamal Hadi Salim 77828d8909bSJamal Hadi Salim r_skb = alloc_skb(len, GFP_ATOMIC); 77928d8909bSJamal Hadi Salim 78028d8909bSJamal Hadi Salim if (r_skb == NULL) 78128d8909bSJamal Hadi Salim return -ENOMEM; 78228d8909bSJamal Hadi Salim 78328d8909bSJamal Hadi Salim if (build_sadinfo(r_skb, spid, seq, *flags) < 0) 78428d8909bSJamal Hadi Salim BUG(); 78528d8909bSJamal Hadi Salim 78628d8909bSJamal Hadi Salim return nlmsg_unicast(xfrm_nl, r_skb, spid); 78728d8909bSJamal Hadi Salim } 78828d8909bSJamal Hadi Salim 78922e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 79022e70050SChristoph Hellwig struct rtattr **xfrma) 7911da177e4SLinus Torvalds { 7921da177e4SLinus Torvalds struct xfrm_usersa_id *p = NLMSG_DATA(nlh); 7931da177e4SLinus Torvalds struct xfrm_state *x; 7941da177e4SLinus Torvalds struct sk_buff *resp_skb; 795eb2971b6SMasahide NAKAMURA int err = -ESRCH; 7961da177e4SLinus Torvalds 79722e70050SChristoph Hellwig x = xfrm_user_state_lookup(p, xfrma, &err); 7981da177e4SLinus Torvalds if (x == NULL) 7991da177e4SLinus Torvalds goto out_noput; 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 8021da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 8031da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 8041da177e4SLinus Torvalds } else { 8051da177e4SLinus Torvalds err = netlink_unicast(xfrm_nl, resp_skb, 8061da177e4SLinus Torvalds NETLINK_CB(skb).pid, MSG_DONTWAIT); 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds xfrm_state_put(x); 8091da177e4SLinus Torvalds out_noput: 8101da177e4SLinus Torvalds return err; 8111da177e4SLinus Torvalds } 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 8141da177e4SLinus Torvalds { 8151da177e4SLinus Torvalds switch (p->info.id.proto) { 8161da177e4SLinus Torvalds case IPPROTO_AH: 8171da177e4SLinus Torvalds case IPPROTO_ESP: 8181da177e4SLinus Torvalds break; 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds case IPPROTO_COMP: 8211da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 8221da177e4SLinus Torvalds if (p->max >= 0x10000) 8231da177e4SLinus Torvalds return -EINVAL; 8241da177e4SLinus Torvalds break; 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds default: 8271da177e4SLinus Torvalds return -EINVAL; 8283ff50b79SStephen Hemminger } 8291da177e4SLinus Torvalds 8301da177e4SLinus Torvalds if (p->min > p->max) 8311da177e4SLinus Torvalds return -EINVAL; 8321da177e4SLinus Torvalds 8331da177e4SLinus Torvalds return 0; 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds 83622e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 83722e70050SChristoph Hellwig struct rtattr **xfrma) 8381da177e4SLinus Torvalds { 8391da177e4SLinus Torvalds struct xfrm_state *x; 8401da177e4SLinus Torvalds struct xfrm_userspi_info *p; 8411da177e4SLinus Torvalds struct sk_buff *resp_skb; 8421da177e4SLinus Torvalds xfrm_address_t *daddr; 8431da177e4SLinus Torvalds int family; 8441da177e4SLinus Torvalds int err; 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds p = NLMSG_DATA(nlh); 8471da177e4SLinus Torvalds err = verify_userspi_info(p); 8481da177e4SLinus Torvalds if (err) 8491da177e4SLinus Torvalds goto out_noput; 8501da177e4SLinus Torvalds 8511da177e4SLinus Torvalds family = p->info.family; 8521da177e4SLinus Torvalds daddr = &p->info.id.daddr; 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds x = NULL; 8551da177e4SLinus Torvalds if (p->info.seq) { 8561da177e4SLinus Torvalds x = xfrm_find_acq_byseq(p->info.seq); 8571da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 8581da177e4SLinus Torvalds xfrm_state_put(x); 8591da177e4SLinus Torvalds x = NULL; 8601da177e4SLinus Torvalds } 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds 8631da177e4SLinus Torvalds if (!x) 8641da177e4SLinus Torvalds x = xfrm_find_acq(p->info.mode, p->info.reqid, 8651da177e4SLinus Torvalds p->info.id.proto, daddr, 8661da177e4SLinus Torvalds &p->info.saddr, 1, 8671da177e4SLinus Torvalds family); 8681da177e4SLinus Torvalds err = -ENOENT; 8691da177e4SLinus Torvalds if (x == NULL) 8701da177e4SLinus Torvalds goto out_noput; 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds resp_skb = ERR_PTR(-ENOENT); 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds spin_lock_bh(&x->lock); 8751da177e4SLinus Torvalds if (x->km.state != XFRM_STATE_DEAD) { 8761da177e4SLinus Torvalds xfrm_alloc_spi(x, htonl(p->min), htonl(p->max)); 8771da177e4SLinus Torvalds if (x->id.spi) 8781da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 8791da177e4SLinus Torvalds } 8801da177e4SLinus Torvalds spin_unlock_bh(&x->lock); 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 8831da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 8841da177e4SLinus Torvalds goto out; 8851da177e4SLinus Torvalds } 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds err = netlink_unicast(xfrm_nl, resp_skb, 8881da177e4SLinus Torvalds NETLINK_CB(skb).pid, MSG_DONTWAIT); 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds out: 8911da177e4SLinus Torvalds xfrm_state_put(x); 8921da177e4SLinus Torvalds out_noput: 8931da177e4SLinus Torvalds return err; 8941da177e4SLinus Torvalds } 8951da177e4SLinus Torvalds 896b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 8971da177e4SLinus Torvalds { 8981da177e4SLinus Torvalds switch (dir) { 8991da177e4SLinus Torvalds case XFRM_POLICY_IN: 9001da177e4SLinus Torvalds case XFRM_POLICY_OUT: 9011da177e4SLinus Torvalds case XFRM_POLICY_FWD: 9021da177e4SLinus Torvalds break; 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds default: 9051da177e4SLinus Torvalds return -EINVAL; 9063ff50b79SStephen Hemminger } 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds return 0; 9091da177e4SLinus Torvalds } 9101da177e4SLinus Torvalds 911b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 912f7b6983fSMasahide NAKAMURA { 913f7b6983fSMasahide NAKAMURA switch (type) { 914f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 915f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 916f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 917f7b6983fSMasahide NAKAMURA #endif 918f7b6983fSMasahide NAKAMURA break; 919f7b6983fSMasahide NAKAMURA 920f7b6983fSMasahide NAKAMURA default: 921f7b6983fSMasahide NAKAMURA return -EINVAL; 9223ff50b79SStephen Hemminger } 923f7b6983fSMasahide NAKAMURA 924f7b6983fSMasahide NAKAMURA return 0; 925f7b6983fSMasahide NAKAMURA } 926f7b6983fSMasahide NAKAMURA 9271da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 9281da177e4SLinus Torvalds { 9291da177e4SLinus Torvalds switch (p->share) { 9301da177e4SLinus Torvalds case XFRM_SHARE_ANY: 9311da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 9321da177e4SLinus Torvalds case XFRM_SHARE_USER: 9331da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 9341da177e4SLinus Torvalds break; 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds default: 9371da177e4SLinus Torvalds return -EINVAL; 9383ff50b79SStephen Hemminger } 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds switch (p->action) { 9411da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 9421da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 9431da177e4SLinus Torvalds break; 9441da177e4SLinus Torvalds 9451da177e4SLinus Torvalds default: 9461da177e4SLinus Torvalds return -EINVAL; 9473ff50b79SStephen Hemminger } 9481da177e4SLinus Torvalds 9491da177e4SLinus Torvalds switch (p->sel.family) { 9501da177e4SLinus Torvalds case AF_INET: 9511da177e4SLinus Torvalds break; 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds case AF_INET6: 9541da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 9551da177e4SLinus Torvalds break; 9561da177e4SLinus Torvalds #else 9571da177e4SLinus Torvalds return -EAFNOSUPPORT; 9581da177e4SLinus Torvalds #endif 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds default: 9611da177e4SLinus Torvalds return -EINVAL; 9623ff50b79SStephen Hemminger } 9631da177e4SLinus Torvalds 9641da177e4SLinus Torvalds return verify_policy_dir(p->dir); 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds 967df71837dSTrent Jaeger static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct rtattr **xfrma) 968df71837dSTrent Jaeger { 969df71837dSTrent Jaeger struct rtattr *rt = xfrma[XFRMA_SEC_CTX-1]; 970df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 971df71837dSTrent Jaeger 972df71837dSTrent Jaeger if (!rt) 973df71837dSTrent Jaeger return 0; 974df71837dSTrent Jaeger 975df71837dSTrent Jaeger uctx = RTA_DATA(rt); 976df71837dSTrent Jaeger return security_xfrm_policy_alloc(pol, uctx); 977df71837dSTrent Jaeger } 978df71837dSTrent Jaeger 9791da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 9801da177e4SLinus Torvalds int nr) 9811da177e4SLinus Torvalds { 9821da177e4SLinus Torvalds int i; 9831da177e4SLinus Torvalds 9841da177e4SLinus Torvalds xp->xfrm_nr = nr; 9851da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 9861da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 9871da177e4SLinus Torvalds 9881da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 9891da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 9901da177e4SLinus Torvalds sizeof(xfrm_address_t)); 9911da177e4SLinus Torvalds t->reqid = ut->reqid; 9921da177e4SLinus Torvalds t->mode = ut->mode; 9931da177e4SLinus Torvalds t->share = ut->share; 9941da177e4SLinus Torvalds t->optional = ut->optional; 9951da177e4SLinus Torvalds t->aalgos = ut->aalgos; 9961da177e4SLinus Torvalds t->ealgos = ut->ealgos; 9971da177e4SLinus Torvalds t->calgos = ut->calgos; 9988511d01dSMiika Komu t->encap_family = ut->family; 9991da177e4SLinus Torvalds } 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds 1002b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1003b4ad86bfSDavid S. Miller { 1004b4ad86bfSDavid S. Miller int i; 1005b4ad86bfSDavid S. Miller 1006b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1007b4ad86bfSDavid S. Miller return -EINVAL; 1008b4ad86bfSDavid S. Miller 1009b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1010b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1011b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1012b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1013b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1014b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1015b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1016b4ad86bfSDavid S. Miller */ 1017b4ad86bfSDavid S. Miller if (!ut[i].family) 1018b4ad86bfSDavid S. Miller ut[i].family = family; 1019b4ad86bfSDavid S. Miller 1020b4ad86bfSDavid S. Miller switch (ut[i].family) { 1021b4ad86bfSDavid S. Miller case AF_INET: 1022b4ad86bfSDavid S. Miller break; 1023b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1024b4ad86bfSDavid S. Miller case AF_INET6: 1025b4ad86bfSDavid S. Miller break; 1026b4ad86bfSDavid S. Miller #endif 1027b4ad86bfSDavid S. Miller default: 1028b4ad86bfSDavid S. Miller return -EINVAL; 10293ff50b79SStephen Hemminger } 1030b4ad86bfSDavid S. Miller } 1031b4ad86bfSDavid S. Miller 1032b4ad86bfSDavid S. Miller return 0; 1033b4ad86bfSDavid S. Miller } 1034b4ad86bfSDavid S. Miller 10351da177e4SLinus Torvalds static int copy_from_user_tmpl(struct xfrm_policy *pol, struct rtattr **xfrma) 10361da177e4SLinus Torvalds { 10371da177e4SLinus Torvalds struct rtattr *rt = xfrma[XFRMA_TMPL-1]; 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds if (!rt) { 10401da177e4SLinus Torvalds pol->xfrm_nr = 0; 10411da177e4SLinus Torvalds } else { 1042b4ad86bfSDavid S. Miller struct xfrm_user_tmpl *utmpl = RTA_DATA(rt); 1043b4ad86bfSDavid S. Miller int nr = (rt->rta_len - sizeof(*rt)) / sizeof(*utmpl); 1044b4ad86bfSDavid S. Miller int err; 10451da177e4SLinus Torvalds 1046b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1047b4ad86bfSDavid S. Miller if (err) 1048b4ad86bfSDavid S. Miller return err; 10491da177e4SLinus Torvalds 10501da177e4SLinus Torvalds copy_templates(pol, RTA_DATA(rt), nr); 10511da177e4SLinus Torvalds } 10521da177e4SLinus Torvalds return 0; 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds 1055f7b6983fSMasahide NAKAMURA static int copy_from_user_policy_type(u8 *tp, struct rtattr **xfrma) 1056f7b6983fSMasahide NAKAMURA { 1057f7b6983fSMasahide NAKAMURA struct rtattr *rt = xfrma[XFRMA_POLICY_TYPE-1]; 1058f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1059b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1060f7b6983fSMasahide NAKAMURA int err; 1061f7b6983fSMasahide NAKAMURA 1062f7b6983fSMasahide NAKAMURA if (rt) { 1063f7b6983fSMasahide NAKAMURA if (rt->rta_len < sizeof(*upt)) 1064f7b6983fSMasahide NAKAMURA return -EINVAL; 1065f7b6983fSMasahide NAKAMURA 1066f7b6983fSMasahide NAKAMURA upt = RTA_DATA(rt); 1067f7b6983fSMasahide NAKAMURA type = upt->type; 1068f7b6983fSMasahide NAKAMURA } 1069f7b6983fSMasahide NAKAMURA 1070f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1071f7b6983fSMasahide NAKAMURA if (err) 1072f7b6983fSMasahide NAKAMURA return err; 1073f7b6983fSMasahide NAKAMURA 1074f7b6983fSMasahide NAKAMURA *tp = type; 1075f7b6983fSMasahide NAKAMURA return 0; 1076f7b6983fSMasahide NAKAMURA } 1077f7b6983fSMasahide NAKAMURA 10781da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 10791da177e4SLinus Torvalds { 10801da177e4SLinus Torvalds xp->priority = p->priority; 10811da177e4SLinus Torvalds xp->index = p->index; 10821da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 10831da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 10841da177e4SLinus Torvalds xp->action = p->action; 10851da177e4SLinus Torvalds xp->flags = p->flags; 10861da177e4SLinus Torvalds xp->family = p->sel.family; 10871da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 10881da177e4SLinus Torvalds } 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 10911da177e4SLinus Torvalds { 10921da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 10931da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 10941da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 10951da177e4SLinus Torvalds p->priority = xp->priority; 10961da177e4SLinus Torvalds p->index = xp->index; 10971da177e4SLinus Torvalds p->sel.family = xp->family; 10981da177e4SLinus Torvalds p->dir = dir; 10991da177e4SLinus Torvalds p->action = xp->action; 11001da177e4SLinus Torvalds p->flags = xp->flags; 11011da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 11021da177e4SLinus Torvalds } 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, struct rtattr **xfrma, int *errp) 11051da177e4SLinus Torvalds { 11061da177e4SLinus Torvalds struct xfrm_policy *xp = xfrm_policy_alloc(GFP_KERNEL); 11071da177e4SLinus Torvalds int err; 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds if (!xp) { 11101da177e4SLinus Torvalds *errp = -ENOMEM; 11111da177e4SLinus Torvalds return NULL; 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds 11141da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1115df71837dSTrent Jaeger 1116f7b6983fSMasahide NAKAMURA err = copy_from_user_policy_type(&xp->type, xfrma); 1117f7b6983fSMasahide NAKAMURA if (err) 1118f7b6983fSMasahide NAKAMURA goto error; 1119f7b6983fSMasahide NAKAMURA 1120df71837dSTrent Jaeger if (!(err = copy_from_user_tmpl(xp, xfrma))) 1121df71837dSTrent Jaeger err = copy_from_user_sec_ctx(xp, xfrma); 1122f7b6983fSMasahide NAKAMURA if (err) 1123f7b6983fSMasahide NAKAMURA goto error; 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds return xp; 1126f7b6983fSMasahide NAKAMURA error: 1127f7b6983fSMasahide NAKAMURA *errp = err; 1128f7b6983fSMasahide NAKAMURA kfree(xp); 1129f7b6983fSMasahide NAKAMURA return NULL; 11301da177e4SLinus Torvalds } 11311da177e4SLinus Torvalds 113222e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 113322e70050SChristoph Hellwig struct rtattr **xfrma) 11341da177e4SLinus Torvalds { 11351da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = NLMSG_DATA(nlh); 11361da177e4SLinus Torvalds struct xfrm_policy *xp; 113726b15dadSJamal Hadi Salim struct km_event c; 11381da177e4SLinus Torvalds int err; 11391da177e4SLinus Torvalds int excl; 11401da177e4SLinus Torvalds 11411da177e4SLinus Torvalds err = verify_newpolicy_info(p); 11421da177e4SLinus Torvalds if (err) 11431da177e4SLinus Torvalds return err; 114422e70050SChristoph Hellwig err = verify_sec_ctx_len(xfrma); 1145df71837dSTrent Jaeger if (err) 1146df71837dSTrent Jaeger return err; 11471da177e4SLinus Torvalds 114822e70050SChristoph Hellwig xp = xfrm_policy_construct(p, xfrma, &err); 11491da177e4SLinus Torvalds if (!xp) 11501da177e4SLinus Torvalds return err; 11511da177e4SLinus Torvalds 115226b15dadSJamal Hadi Salim /* shouldnt excl be based on nlh flags?? 115326b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 115426b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 115526b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 11561da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 11571da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 1158161a09e7SJoy Latten xfrm_audit_log(NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid, 1159161a09e7SJoy Latten AUDIT_MAC_IPSEC_DELSPD, err ? 0 : 1, xp, NULL); 1160161a09e7SJoy Latten 11611da177e4SLinus Torvalds if (err) { 11625f8ac64bSTrent Jaeger security_xfrm_policy_free(xp); 11631da177e4SLinus Torvalds kfree(xp); 11641da177e4SLinus Torvalds return err; 11651da177e4SLinus Torvalds } 11661da177e4SLinus Torvalds 1167f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 116826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 116926b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 117026b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 117126b15dadSJamal Hadi Salim 11721da177e4SLinus Torvalds xfrm_pol_put(xp); 11731da177e4SLinus Torvalds 11741da177e4SLinus Torvalds return 0; 11751da177e4SLinus Torvalds } 11761da177e4SLinus Torvalds 11771da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 11781da177e4SLinus Torvalds { 11791da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 11801da177e4SLinus Torvalds int i; 11811da177e4SLinus Torvalds 11821da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 11831da177e4SLinus Torvalds return 0; 11841da177e4SLinus Torvalds 11851da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 11861da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 11871da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 11881da177e4SLinus Torvalds 11891da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 11908511d01dSMiika Komu up->family = kp->encap_family; 11911da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 11921da177e4SLinus Torvalds up->reqid = kp->reqid; 11931da177e4SLinus Torvalds up->mode = kp->mode; 11941da177e4SLinus Torvalds up->share = kp->share; 11951da177e4SLinus Torvalds up->optional = kp->optional; 11961da177e4SLinus Torvalds up->aalgos = kp->aalgos; 11971da177e4SLinus Torvalds up->ealgos = kp->ealgos; 11981da177e4SLinus Torvalds up->calgos = kp->calgos; 11991da177e4SLinus Torvalds } 12001da177e4SLinus Torvalds RTA_PUT(skb, XFRMA_TMPL, 12011da177e4SLinus Torvalds (sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr), 12021da177e4SLinus Torvalds vec); 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds return 0; 12051da177e4SLinus Torvalds 12061da177e4SLinus Torvalds rtattr_failure: 12071da177e4SLinus Torvalds return -1; 12081da177e4SLinus Torvalds } 12091da177e4SLinus Torvalds 12100d681623SSerge Hallyn static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 1211df71837dSTrent Jaeger { 12120d681623SSerge Hallyn int ctx_size = sizeof(struct xfrm_sec_ctx) + s->ctx_len; 1213df71837dSTrent Jaeger struct rtattr *rt = __RTA_PUT(skb, XFRMA_SEC_CTX, ctx_size); 1214df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt); 1215df71837dSTrent Jaeger 1216df71837dSTrent Jaeger uctx->exttype = XFRMA_SEC_CTX; 1217df71837dSTrent Jaeger uctx->len = ctx_size; 12180d681623SSerge Hallyn uctx->ctx_doi = s->ctx_doi; 12190d681623SSerge Hallyn uctx->ctx_alg = s->ctx_alg; 12200d681623SSerge Hallyn uctx->ctx_len = s->ctx_len; 12210d681623SSerge Hallyn memcpy(uctx + 1, s->ctx_str, s->ctx_len); 1222df71837dSTrent Jaeger return 0; 1223df71837dSTrent Jaeger 1224df71837dSTrent Jaeger rtattr_failure: 1225df71837dSTrent Jaeger return -1; 1226df71837dSTrent Jaeger } 1227df71837dSTrent Jaeger 12280d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 12290d681623SSerge Hallyn { 12300d681623SSerge Hallyn if (x->security) { 12310d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 12320d681623SSerge Hallyn } 12330d681623SSerge Hallyn return 0; 12340d681623SSerge Hallyn } 12350d681623SSerge Hallyn 12360d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 12370d681623SSerge Hallyn { 12380d681623SSerge Hallyn if (xp->security) { 12390d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 12400d681623SSerge Hallyn } 12410d681623SSerge Hallyn return 0; 12420d681623SSerge Hallyn } 12430d681623SSerge Hallyn 1244f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1245b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1246f7b6983fSMasahide NAKAMURA { 1247f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type upt; 1248f7b6983fSMasahide NAKAMURA 1249f7b6983fSMasahide NAKAMURA memset(&upt, 0, sizeof(upt)); 12501459bb36SJamal Hadi Salim upt.type = type; 1251f7b6983fSMasahide NAKAMURA 1252f7b6983fSMasahide NAKAMURA RTA_PUT(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1253f7b6983fSMasahide NAKAMURA 1254f7b6983fSMasahide NAKAMURA return 0; 1255f7b6983fSMasahide NAKAMURA 1256f7b6983fSMasahide NAKAMURA rtattr_failure: 1257f7b6983fSMasahide NAKAMURA return -1; 1258f7b6983fSMasahide NAKAMURA } 1259f7b6983fSMasahide NAKAMURA 1260f7b6983fSMasahide NAKAMURA #else 1261b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1262f7b6983fSMasahide NAKAMURA { 1263f7b6983fSMasahide NAKAMURA return 0; 1264f7b6983fSMasahide NAKAMURA } 1265f7b6983fSMasahide NAKAMURA #endif 1266f7b6983fSMasahide NAKAMURA 12671da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 12681da177e4SLinus Torvalds { 12691da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 12701da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 12711da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 12721da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 12731da177e4SLinus Torvalds struct nlmsghdr *nlh; 127427a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds if (sp->this_idx < sp->start_idx) 12771da177e4SLinus Torvalds goto out; 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, 12801da177e4SLinus Torvalds sp->nlmsg_seq, 12811da177e4SLinus Torvalds XFRM_MSG_NEWPOLICY, sizeof(*p)); 12821da177e4SLinus Torvalds p = NLMSG_DATA(nlh); 12831da177e4SLinus Torvalds nlh->nlmsg_flags = sp->nlmsg_flags; 12841da177e4SLinus Torvalds 12851da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 12861da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 12871da177e4SLinus Torvalds goto nlmsg_failure; 1288df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 1289df71837dSTrent Jaeger goto nlmsg_failure; 12901459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 1291f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 12921da177e4SLinus Torvalds 129327a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 12941da177e4SLinus Torvalds out: 12951da177e4SLinus Torvalds sp->this_idx++; 12961da177e4SLinus Torvalds return 0; 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds nlmsg_failure: 1299dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 13001da177e4SLinus Torvalds return -1; 13011da177e4SLinus Torvalds } 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 13041da177e4SLinus Torvalds { 13051da177e4SLinus Torvalds struct xfrm_dump_info info; 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds info.in_skb = cb->skb; 13081da177e4SLinus Torvalds info.out_skb = skb; 13091da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 13101da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 13111da177e4SLinus Torvalds info.this_idx = 0; 13121da177e4SLinus Torvalds info.start_idx = cb->args[0]; 1313f7b6983fSMasahide NAKAMURA (void) xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, dump_one_policy, &info); 1314f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1315f7b6983fSMasahide NAKAMURA (void) xfrm_policy_walk(XFRM_POLICY_TYPE_SUB, dump_one_policy, &info); 1316f7b6983fSMasahide NAKAMURA #endif 13171da177e4SLinus Torvalds cb->args[0] = info.this_idx; 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds return skb->len; 13201da177e4SLinus Torvalds } 13211da177e4SLinus Torvalds 13221da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 13231da177e4SLinus Torvalds struct xfrm_policy *xp, 13241da177e4SLinus Torvalds int dir, u32 seq) 13251da177e4SLinus Torvalds { 13261da177e4SLinus Torvalds struct xfrm_dump_info info; 13271da177e4SLinus Torvalds struct sk_buff *skb; 13281da177e4SLinus Torvalds 13291da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 13301da177e4SLinus Torvalds if (!skb) 13311da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 13321da177e4SLinus Torvalds 13331da177e4SLinus Torvalds info.in_skb = in_skb; 13341da177e4SLinus Torvalds info.out_skb = skb; 13351da177e4SLinus Torvalds info.nlmsg_seq = seq; 13361da177e4SLinus Torvalds info.nlmsg_flags = 0; 13371da177e4SLinus Torvalds info.this_idx = info.start_idx = 0; 13381da177e4SLinus Torvalds 13391da177e4SLinus Torvalds if (dump_one_policy(xp, dir, 0, &info) < 0) { 13401da177e4SLinus Torvalds kfree_skb(skb); 13411da177e4SLinus Torvalds return NULL; 13421da177e4SLinus Torvalds } 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds return skb; 13451da177e4SLinus Torvalds } 13461da177e4SLinus Torvalds 134722e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 134822e70050SChristoph Hellwig struct rtattr **xfrma) 13491da177e4SLinus Torvalds { 13501da177e4SLinus Torvalds struct xfrm_policy *xp; 13511da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1352b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 13531da177e4SLinus Torvalds int err; 135426b15dadSJamal Hadi Salim struct km_event c; 13551da177e4SLinus Torvalds int delete; 13561da177e4SLinus Torvalds 13571da177e4SLinus Torvalds p = NLMSG_DATA(nlh); 13581da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 13591da177e4SLinus Torvalds 136022e70050SChristoph Hellwig err = copy_from_user_policy_type(&type, xfrma); 1361f7b6983fSMasahide NAKAMURA if (err) 1362f7b6983fSMasahide NAKAMURA return err; 1363f7b6983fSMasahide NAKAMURA 13641da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 13651da177e4SLinus Torvalds if (err) 13661da177e4SLinus Torvalds return err; 13671da177e4SLinus Torvalds 13681da177e4SLinus Torvalds if (p->index) 1369ef41aaa0SEric Paris xp = xfrm_policy_byid(type, p->dir, p->index, delete, &err); 1370df71837dSTrent Jaeger else { 137122e70050SChristoph Hellwig struct rtattr *rt = xfrma[XFRMA_SEC_CTX-1]; 1372df71837dSTrent Jaeger struct xfrm_policy tmp; 1373df71837dSTrent Jaeger 137422e70050SChristoph Hellwig err = verify_sec_ctx_len(xfrma); 1375df71837dSTrent Jaeger if (err) 1376df71837dSTrent Jaeger return err; 1377df71837dSTrent Jaeger 1378df71837dSTrent Jaeger memset(&tmp, 0, sizeof(struct xfrm_policy)); 1379df71837dSTrent Jaeger if (rt) { 1380df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt); 1381df71837dSTrent Jaeger 1382df71837dSTrent Jaeger if ((err = security_xfrm_policy_alloc(&tmp, uctx))) 1383df71837dSTrent Jaeger return err; 1384df71837dSTrent Jaeger } 1385ef41aaa0SEric Paris xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, tmp.security, 1386ef41aaa0SEric Paris delete, &err); 1387df71837dSTrent Jaeger security_xfrm_policy_free(&tmp); 1388df71837dSTrent Jaeger } 13891da177e4SLinus Torvalds if (xp == NULL) 13901da177e4SLinus Torvalds return -ENOENT; 13911da177e4SLinus Torvalds 13921da177e4SLinus Torvalds if (!delete) { 13931da177e4SLinus Torvalds struct sk_buff *resp_skb; 13941da177e4SLinus Torvalds 13951da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 13961da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 13971da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 13981da177e4SLinus Torvalds } else { 13991da177e4SLinus Torvalds err = netlink_unicast(xfrm_nl, resp_skb, 14001da177e4SLinus Torvalds NETLINK_CB(skb).pid, 14011da177e4SLinus Torvalds MSG_DONTWAIT); 14021da177e4SLinus Torvalds } 140326b15dadSJamal Hadi Salim } else { 140413fcfbb0SDavid S. Miller xfrm_audit_log(NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid, 140513fcfbb0SDavid S. Miller AUDIT_MAC_IPSEC_DELSPD, err ? 0 : 1, xp, NULL); 140613fcfbb0SDavid S. Miller 140713fcfbb0SDavid S. Miller if (err != 0) 1408c8c05a8eSCatherine Zhang goto out; 140913fcfbb0SDavid S. Miller 1410e7443892SHerbert Xu c.data.byid = p->index; 1411f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 141226b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 141326b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 141426b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 14151da177e4SLinus Torvalds } 14161da177e4SLinus Torvalds 1417c8c05a8eSCatherine Zhang out: 1418ef41aaa0SEric Paris xfrm_pol_put(xp); 14191da177e4SLinus Torvalds return err; 14201da177e4SLinus Torvalds } 14211da177e4SLinus Torvalds 142222e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 142322e70050SChristoph Hellwig struct rtattr **xfrma) 14241da177e4SLinus Torvalds { 142526b15dadSJamal Hadi Salim struct km_event c; 14261da177e4SLinus Torvalds struct xfrm_usersa_flush *p = NLMSG_DATA(nlh); 1427161a09e7SJoy Latten struct xfrm_audit audit_info; 14284aa2e62cSJoy Latten int err; 14291da177e4SLinus Torvalds 1430161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 1431161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 14324aa2e62cSJoy Latten err = xfrm_state_flush(p->proto, &audit_info); 14334aa2e62cSJoy Latten if (err) 14344aa2e62cSJoy Latten return err; 1435bf08867fSHerbert Xu c.data.proto = p->proto; 1436f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 143726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 143826b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 143926b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 144026b15dadSJamal Hadi Salim 14411da177e4SLinus Torvalds return 0; 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds 1444d51d081dSJamal Hadi Salim 1445d51d081dSJamal Hadi Salim static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 1446d51d081dSJamal Hadi Salim { 1447d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1448d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 1449d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur ltime; 145027a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 1451d51d081dSJamal Hadi Salim 1452d51d081dSJamal Hadi Salim nlh = NLMSG_PUT(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id)); 1453d51d081dSJamal Hadi Salim id = NLMSG_DATA(nlh); 1454d51d081dSJamal Hadi Salim nlh->nlmsg_flags = 0; 1455d51d081dSJamal Hadi Salim 14562b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1457d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1458d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1459d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 14602b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 14612b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1462d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1463d51d081dSJamal Hadi Salim 1464d51d081dSJamal Hadi Salim RTA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); 1465d51d081dSJamal Hadi Salim 1466d51d081dSJamal Hadi Salim ltime.bytes = x->curlft.bytes; 1467d51d081dSJamal Hadi Salim ltime.packets = x->curlft.packets; 1468d51d081dSJamal Hadi Salim ltime.add_time = x->curlft.add_time; 1469d51d081dSJamal Hadi Salim ltime.use_time = x->curlft.use_time; 1470d51d081dSJamal Hadi Salim 1471d51d081dSJamal Hadi Salim RTA_PUT(skb, XFRMA_LTIME_VAL, sizeof(struct xfrm_lifetime_cur), <ime); 1472d51d081dSJamal Hadi Salim 1473d51d081dSJamal Hadi Salim if (id->flags&XFRM_AE_RTHR) { 1474d51d081dSJamal Hadi Salim RTA_PUT(skb,XFRMA_REPLAY_THRESH,sizeof(u32),&x->replay_maxdiff); 1475d51d081dSJamal Hadi Salim } 1476d51d081dSJamal Hadi Salim 1477d51d081dSJamal Hadi Salim if (id->flags&XFRM_AE_ETHR) { 1478d51d081dSJamal Hadi Salim u32 etimer = x->replay_maxage*10/HZ; 1479d51d081dSJamal Hadi Salim RTA_PUT(skb,XFRMA_ETIMER_THRESH,sizeof(u32),&etimer); 1480d51d081dSJamal Hadi Salim } 1481d51d081dSJamal Hadi Salim 148227a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 1483d51d081dSJamal Hadi Salim return skb->len; 1484d51d081dSJamal Hadi Salim 1485d51d081dSJamal Hadi Salim rtattr_failure: 1486d51d081dSJamal Hadi Salim nlmsg_failure: 1487dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 1488d51d081dSJamal Hadi Salim return -1; 1489d51d081dSJamal Hadi Salim } 1490d51d081dSJamal Hadi Salim 149122e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 149222e70050SChristoph Hellwig struct rtattr **xfrma) 1493d51d081dSJamal Hadi Salim { 1494d51d081dSJamal Hadi Salim struct xfrm_state *x; 1495d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1496d51d081dSJamal Hadi Salim int err; 1497d51d081dSJamal Hadi Salim struct km_event c; 1498d51d081dSJamal Hadi Salim struct xfrm_aevent_id *p = NLMSG_DATA(nlh); 1499d51d081dSJamal Hadi Salim int len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); 1500d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1501d51d081dSJamal Hadi Salim 1502d51d081dSJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_replay_state)); 1503d51d081dSJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_lifetime_cur)); 1504d51d081dSJamal Hadi Salim 1505d51d081dSJamal Hadi Salim if (p->flags&XFRM_AE_RTHR) 1506d51d081dSJamal Hadi Salim len+=RTA_SPACE(sizeof(u32)); 1507d51d081dSJamal Hadi Salim 1508d51d081dSJamal Hadi Salim if (p->flags&XFRM_AE_ETHR) 1509d51d081dSJamal Hadi Salim len+=RTA_SPACE(sizeof(u32)); 1510d51d081dSJamal Hadi Salim 1511d51d081dSJamal Hadi Salim r_skb = alloc_skb(len, GFP_ATOMIC); 1512d51d081dSJamal Hadi Salim if (r_skb == NULL) 1513d51d081dSJamal Hadi Salim return -ENOMEM; 1514d51d081dSJamal Hadi Salim 1515d51d081dSJamal Hadi Salim x = xfrm_state_lookup(&id->daddr, id->spi, id->proto, id->family); 1516d51d081dSJamal Hadi Salim if (x == NULL) { 1517b08d5840SPatrick McHardy kfree_skb(r_skb); 1518d51d081dSJamal Hadi Salim return -ESRCH; 1519d51d081dSJamal Hadi Salim } 1520d51d081dSJamal Hadi Salim 1521d51d081dSJamal Hadi Salim /* 1522d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1523d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1524d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1525d51d081dSJamal Hadi Salim */ 1526d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1527d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1528d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1529d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1530d51d081dSJamal Hadi Salim 1531d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1532d51d081dSJamal Hadi Salim BUG(); 1533d51d081dSJamal Hadi Salim err = netlink_unicast(xfrm_nl, r_skb, 1534d51d081dSJamal Hadi Salim NETLINK_CB(skb).pid, MSG_DONTWAIT); 1535d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1536d51d081dSJamal Hadi Salim xfrm_state_put(x); 1537d51d081dSJamal Hadi Salim return err; 1538d51d081dSJamal Hadi Salim } 1539d51d081dSJamal Hadi Salim 154022e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 154122e70050SChristoph Hellwig struct rtattr **xfrma) 1542d51d081dSJamal Hadi Salim { 1543d51d081dSJamal Hadi Salim struct xfrm_state *x; 1544d51d081dSJamal Hadi Salim struct km_event c; 1545d51d081dSJamal Hadi Salim int err = - EINVAL; 1546d51d081dSJamal Hadi Salim struct xfrm_aevent_id *p = NLMSG_DATA(nlh); 1547d51d081dSJamal Hadi Salim struct rtattr *rp = xfrma[XFRMA_REPLAY_VAL-1]; 1548d51d081dSJamal Hadi Salim struct rtattr *lt = xfrma[XFRMA_LTIME_VAL-1]; 1549d51d081dSJamal Hadi Salim 1550d51d081dSJamal Hadi Salim if (!lt && !rp) 1551d51d081dSJamal Hadi Salim return err; 1552d51d081dSJamal Hadi Salim 1553d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1554d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1555d51d081dSJamal Hadi Salim return err; 1556d51d081dSJamal Hadi Salim 1557d51d081dSJamal Hadi Salim x = xfrm_state_lookup(&p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1558d51d081dSJamal Hadi Salim if (x == NULL) 1559d51d081dSJamal Hadi Salim return -ESRCH; 1560d51d081dSJamal Hadi Salim 1561d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1562d51d081dSJamal Hadi Salim goto out; 1563d51d081dSJamal Hadi Salim 1564d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 156522e70050SChristoph Hellwig err = xfrm_update_ae_params(x, xfrma); 1566d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1567d51d081dSJamal Hadi Salim if (err < 0) 1568d51d081dSJamal Hadi Salim goto out; 1569d51d081dSJamal Hadi Salim 1570d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1571d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1572d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1573d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1574d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1575d51d081dSJamal Hadi Salim err = 0; 1576d51d081dSJamal Hadi Salim out: 1577d51d081dSJamal Hadi Salim xfrm_state_put(x); 1578d51d081dSJamal Hadi Salim return err; 1579d51d081dSJamal Hadi Salim } 1580d51d081dSJamal Hadi Salim 158122e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 158222e70050SChristoph Hellwig struct rtattr **xfrma) 15831da177e4SLinus Torvalds { 158426b15dadSJamal Hadi Salim struct km_event c; 1585b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1586f7b6983fSMasahide NAKAMURA int err; 1587161a09e7SJoy Latten struct xfrm_audit audit_info; 158826b15dadSJamal Hadi Salim 158922e70050SChristoph Hellwig err = copy_from_user_policy_type(&type, xfrma); 1590f7b6983fSMasahide NAKAMURA if (err) 1591f7b6983fSMasahide NAKAMURA return err; 1592f7b6983fSMasahide NAKAMURA 1593161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 1594161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 15954aa2e62cSJoy Latten err = xfrm_policy_flush(type, &audit_info); 15964aa2e62cSJoy Latten if (err) 15974aa2e62cSJoy Latten return err; 1598f7b6983fSMasahide NAKAMURA c.data.type = type; 1599f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 160026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 160126b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 160226b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 16031da177e4SLinus Torvalds return 0; 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds 160622e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 160722e70050SChristoph Hellwig struct rtattr **xfrma) 16086c5c8ca7SJamal Hadi Salim { 16096c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 16106c5c8ca7SJamal Hadi Salim struct xfrm_user_polexpire *up = NLMSG_DATA(nlh); 16116c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1612b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 16136c5c8ca7SJamal Hadi Salim int err = -ENOENT; 16146c5c8ca7SJamal Hadi Salim 161522e70050SChristoph Hellwig err = copy_from_user_policy_type(&type, xfrma); 1616f7b6983fSMasahide NAKAMURA if (err) 1617f7b6983fSMasahide NAKAMURA return err; 1618f7b6983fSMasahide NAKAMURA 16196c5c8ca7SJamal Hadi Salim if (p->index) 1620ef41aaa0SEric Paris xp = xfrm_policy_byid(type, p->dir, p->index, 0, &err); 16216c5c8ca7SJamal Hadi Salim else { 162222e70050SChristoph Hellwig struct rtattr *rt = xfrma[XFRMA_SEC_CTX-1]; 16236c5c8ca7SJamal Hadi Salim struct xfrm_policy tmp; 16246c5c8ca7SJamal Hadi Salim 162522e70050SChristoph Hellwig err = verify_sec_ctx_len(xfrma); 16266c5c8ca7SJamal Hadi Salim if (err) 16276c5c8ca7SJamal Hadi Salim return err; 16286c5c8ca7SJamal Hadi Salim 16296c5c8ca7SJamal Hadi Salim memset(&tmp, 0, sizeof(struct xfrm_policy)); 16306c5c8ca7SJamal Hadi Salim if (rt) { 16316c5c8ca7SJamal Hadi Salim struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt); 16326c5c8ca7SJamal Hadi Salim 16336c5c8ca7SJamal Hadi Salim if ((err = security_xfrm_policy_alloc(&tmp, uctx))) 16346c5c8ca7SJamal Hadi Salim return err; 16356c5c8ca7SJamal Hadi Salim } 1636ef41aaa0SEric Paris xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, tmp.security, 1637ef41aaa0SEric Paris 0, &err); 16386c5c8ca7SJamal Hadi Salim security_xfrm_policy_free(&tmp); 16396c5c8ca7SJamal Hadi Salim } 16406c5c8ca7SJamal Hadi Salim 16416c5c8ca7SJamal Hadi Salim if (xp == NULL) 1642ef41aaa0SEric Paris return -ENOENT; 16436c5c8ca7SJamal Hadi Salim read_lock(&xp->lock); 16446c5c8ca7SJamal Hadi Salim if (xp->dead) { 16456c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 16466c5c8ca7SJamal Hadi Salim goto out; 16476c5c8ca7SJamal Hadi Salim } 16486c5c8ca7SJamal Hadi Salim 16496c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 16506c5c8ca7SJamal Hadi Salim err = 0; 16516c5c8ca7SJamal Hadi Salim if (up->hard) { 16526c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 1653161a09e7SJoy Latten xfrm_audit_log(NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid, 1654161a09e7SJoy Latten AUDIT_MAC_IPSEC_DELSPD, 1, xp, NULL); 1655161a09e7SJoy Latten 16566c5c8ca7SJamal Hadi Salim } else { 16576c5c8ca7SJamal Hadi Salim // reset the timers here? 16586c5c8ca7SJamal Hadi Salim printk("Dont know what to do with soft policy expire\n"); 16596c5c8ca7SJamal Hadi Salim } 16606c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 16616c5c8ca7SJamal Hadi Salim 16626c5c8ca7SJamal Hadi Salim out: 16636c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 16646c5c8ca7SJamal Hadi Salim return err; 16656c5c8ca7SJamal Hadi Salim } 16666c5c8ca7SJamal Hadi Salim 166722e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 166822e70050SChristoph Hellwig struct rtattr **xfrma) 166953bc6b4dSJamal Hadi Salim { 167053bc6b4dSJamal Hadi Salim struct xfrm_state *x; 167153bc6b4dSJamal Hadi Salim int err; 167253bc6b4dSJamal Hadi Salim struct xfrm_user_expire *ue = NLMSG_DATA(nlh); 167353bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 167453bc6b4dSJamal Hadi Salim 167553bc6b4dSJamal Hadi Salim x = xfrm_state_lookup(&p->id.daddr, p->id.spi, p->id.proto, p->family); 167653bc6b4dSJamal Hadi Salim 16773a765aa5SDavid S. Miller err = -ENOENT; 167853bc6b4dSJamal Hadi Salim if (x == NULL) 167953bc6b4dSJamal Hadi Salim return err; 168053bc6b4dSJamal Hadi Salim 168153bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 16823a765aa5SDavid S. Miller err = -EINVAL; 168353bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 168453bc6b4dSJamal Hadi Salim goto out; 168553bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 168653bc6b4dSJamal Hadi Salim 1687161a09e7SJoy Latten if (ue->hard) { 168853bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 1689161a09e7SJoy Latten xfrm_audit_log(NETLINK_CB(skb).loginuid, NETLINK_CB(skb).sid, 1690161a09e7SJoy Latten AUDIT_MAC_IPSEC_DELSA, 1, NULL, x); 1691161a09e7SJoy Latten } 16923a765aa5SDavid S. Miller err = 0; 169353bc6b4dSJamal Hadi Salim out: 169453bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 169553bc6b4dSJamal Hadi Salim xfrm_state_put(x); 169653bc6b4dSJamal Hadi Salim return err; 169753bc6b4dSJamal Hadi Salim } 169853bc6b4dSJamal Hadi Salim 169922e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 170022e70050SChristoph Hellwig struct rtattr **xfrma) 1701980ebd25SJamal Hadi Salim { 1702980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1703980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1704980ebd25SJamal Hadi Salim int i; 1705980ebd25SJamal Hadi Salim struct rtattr *rt = xfrma[XFRMA_TMPL-1]; 1706980ebd25SJamal Hadi Salim 1707980ebd25SJamal Hadi Salim struct xfrm_user_acquire *ua = NLMSG_DATA(nlh); 1708980ebd25SJamal Hadi Salim struct xfrm_state *x = xfrm_state_alloc(); 1709980ebd25SJamal Hadi Salim int err = -ENOMEM; 1710980ebd25SJamal Hadi Salim 1711980ebd25SJamal Hadi Salim if (!x) 1712980ebd25SJamal Hadi Salim return err; 1713980ebd25SJamal Hadi Salim 1714980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 1715980ebd25SJamal Hadi Salim if (err) { 1716980ebd25SJamal Hadi Salim printk("BAD policy passed\n"); 1717980ebd25SJamal Hadi Salim kfree(x); 1718980ebd25SJamal Hadi Salim return err; 1719980ebd25SJamal Hadi Salim } 1720980ebd25SJamal Hadi Salim 1721980ebd25SJamal Hadi Salim /* build an XP */ 1722b4ad86bfSDavid S. Miller xp = xfrm_policy_construct(&ua->policy, (struct rtattr **) xfrma, &err); 1723b4ad86bfSDavid S. Miller if (!xp) { 1724980ebd25SJamal Hadi Salim kfree(x); 1725980ebd25SJamal Hadi Salim return err; 1726980ebd25SJamal Hadi Salim } 1727980ebd25SJamal Hadi Salim 1728980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 1729980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 1730980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 1731980ebd25SJamal Hadi Salim 1732980ebd25SJamal Hadi Salim ut = RTA_DATA(rt); 1733980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 1734980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 1735980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 1736980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 1737980ebd25SJamal Hadi Salim x->props.mode = t->mode; 1738980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 1739980ebd25SJamal Hadi Salim x->props.family = ut->family; 1740980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 1741980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 1742980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 1743980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 1744980ebd25SJamal Hadi Salim 1745980ebd25SJamal Hadi Salim } 1746980ebd25SJamal Hadi Salim 1747980ebd25SJamal Hadi Salim kfree(x); 1748980ebd25SJamal Hadi Salim kfree(xp); 1749980ebd25SJamal Hadi Salim 1750980ebd25SJamal Hadi Salim return 0; 1751980ebd25SJamal Hadi Salim } 1752980ebd25SJamal Hadi Salim 17535c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 17545c79de6eSShinta Sugimoto static int verify_user_migrate(struct rtattr **xfrma) 17555c79de6eSShinta Sugimoto { 17565c79de6eSShinta Sugimoto struct rtattr *rt = xfrma[XFRMA_MIGRATE-1]; 17575c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 17585c79de6eSShinta Sugimoto 17595c79de6eSShinta Sugimoto if (!rt) 17605c79de6eSShinta Sugimoto return -EINVAL; 17615c79de6eSShinta Sugimoto 17625c79de6eSShinta Sugimoto if ((rt->rta_len - sizeof(*rt)) < sizeof(*um)) 17635c79de6eSShinta Sugimoto return -EINVAL; 17645c79de6eSShinta Sugimoto 17655c79de6eSShinta Sugimoto return 0; 17665c79de6eSShinta Sugimoto } 17675c79de6eSShinta Sugimoto 17685c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 17695c79de6eSShinta Sugimoto struct rtattr **xfrma, int *num) 17705c79de6eSShinta Sugimoto { 17715c79de6eSShinta Sugimoto struct rtattr *rt = xfrma[XFRMA_MIGRATE-1]; 17725c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 17735c79de6eSShinta Sugimoto int i, num_migrate; 17745c79de6eSShinta Sugimoto 17755c79de6eSShinta Sugimoto um = RTA_DATA(rt); 17765c79de6eSShinta Sugimoto num_migrate = (rt->rta_len - sizeof(*rt)) / sizeof(*um); 17775c79de6eSShinta Sugimoto 17785c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 17795c79de6eSShinta Sugimoto return -EINVAL; 17805c79de6eSShinta Sugimoto 17815c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 17825c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 17835c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 17845c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 17855c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 17865c79de6eSShinta Sugimoto 17875c79de6eSShinta Sugimoto ma->proto = um->proto; 17885c79de6eSShinta Sugimoto ma->mode = um->mode; 17895c79de6eSShinta Sugimoto ma->reqid = um->reqid; 17905c79de6eSShinta Sugimoto 17915c79de6eSShinta Sugimoto ma->old_family = um->old_family; 17925c79de6eSShinta Sugimoto ma->new_family = um->new_family; 17935c79de6eSShinta Sugimoto } 17945c79de6eSShinta Sugimoto 17955c79de6eSShinta Sugimoto *num = i; 17965c79de6eSShinta Sugimoto return 0; 17975c79de6eSShinta Sugimoto } 17985c79de6eSShinta Sugimoto 17995c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 18005c79de6eSShinta Sugimoto struct rtattr **xfrma) 18015c79de6eSShinta Sugimoto { 18025c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pi = NLMSG_DATA(nlh); 18035c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 18045c79de6eSShinta Sugimoto u8 type; 18055c79de6eSShinta Sugimoto int err; 18065c79de6eSShinta Sugimoto int n = 0; 18075c79de6eSShinta Sugimoto 18085c79de6eSShinta Sugimoto err = verify_user_migrate((struct rtattr **)xfrma); 18095c79de6eSShinta Sugimoto if (err) 18105c79de6eSShinta Sugimoto return err; 18115c79de6eSShinta Sugimoto 18125c79de6eSShinta Sugimoto err = copy_from_user_policy_type(&type, (struct rtattr **)xfrma); 18135c79de6eSShinta Sugimoto if (err) 18145c79de6eSShinta Sugimoto return err; 18155c79de6eSShinta Sugimoto 18165c79de6eSShinta Sugimoto err = copy_from_user_migrate((struct xfrm_migrate *)m, 18175c79de6eSShinta Sugimoto (struct rtattr **)xfrma, &n); 18185c79de6eSShinta Sugimoto if (err) 18195c79de6eSShinta Sugimoto return err; 18205c79de6eSShinta Sugimoto 18215c79de6eSShinta Sugimoto if (!n) 18225c79de6eSShinta Sugimoto return 0; 18235c79de6eSShinta Sugimoto 18245c79de6eSShinta Sugimoto xfrm_migrate(&pi->sel, pi->dir, type, m, n); 18255c79de6eSShinta Sugimoto 18265c79de6eSShinta Sugimoto return 0; 18275c79de6eSShinta Sugimoto } 18285c79de6eSShinta Sugimoto #else 18295c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 18305c79de6eSShinta Sugimoto struct rtattr **xfrma) 18315c79de6eSShinta Sugimoto { 18325c79de6eSShinta Sugimoto return -ENOPROTOOPT; 18335c79de6eSShinta Sugimoto } 18345c79de6eSShinta Sugimoto #endif 18355c79de6eSShinta Sugimoto 18365c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 18375c79de6eSShinta Sugimoto static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) 18385c79de6eSShinta Sugimoto { 18395c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 18405c79de6eSShinta Sugimoto 18415c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 18425c79de6eSShinta Sugimoto um.proto = m->proto; 18435c79de6eSShinta Sugimoto um.mode = m->mode; 18445c79de6eSShinta Sugimoto um.reqid = m->reqid; 18455c79de6eSShinta Sugimoto um.old_family = m->old_family; 18465c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 18475c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 18485c79de6eSShinta Sugimoto um.new_family = m->new_family; 18495c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 18505c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 18515c79de6eSShinta Sugimoto 18525c79de6eSShinta Sugimoto RTA_PUT(skb, XFRMA_MIGRATE, sizeof(um), &um); 18535c79de6eSShinta Sugimoto return 0; 18545c79de6eSShinta Sugimoto 18555c79de6eSShinta Sugimoto rtattr_failure: 18565c79de6eSShinta Sugimoto return -1; 18575c79de6eSShinta Sugimoto } 18585c79de6eSShinta Sugimoto 18595c79de6eSShinta Sugimoto static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, 18605c79de6eSShinta Sugimoto int num_migrate, struct xfrm_selector *sel, 18615c79de6eSShinta Sugimoto u8 dir, u8 type) 18625c79de6eSShinta Sugimoto { 18635c79de6eSShinta Sugimoto struct xfrm_migrate *mp; 18645c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 18655c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 186627a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 18675c79de6eSShinta Sugimoto int i; 18685c79de6eSShinta Sugimoto 18695c79de6eSShinta Sugimoto nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id)); 18705c79de6eSShinta Sugimoto pol_id = NLMSG_DATA(nlh); 18715c79de6eSShinta Sugimoto nlh->nlmsg_flags = 0; 18725c79de6eSShinta Sugimoto 18735c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 18745c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 18755c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 18765c79de6eSShinta Sugimoto pol_id->dir = dir; 18775c79de6eSShinta Sugimoto 18785c79de6eSShinta Sugimoto if (copy_to_user_policy_type(type, skb) < 0) 18795c79de6eSShinta Sugimoto goto nlmsg_failure; 18805c79de6eSShinta Sugimoto 18815c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 18825c79de6eSShinta Sugimoto if (copy_to_user_migrate(mp, skb) < 0) 18835c79de6eSShinta Sugimoto goto nlmsg_failure; 18845c79de6eSShinta Sugimoto } 18855c79de6eSShinta Sugimoto 188627a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 18875c79de6eSShinta Sugimoto return skb->len; 18885c79de6eSShinta Sugimoto nlmsg_failure: 1889dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 18905c79de6eSShinta Sugimoto return -1; 18915c79de6eSShinta Sugimoto } 18925c79de6eSShinta Sugimoto 18935c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 18945c79de6eSShinta Sugimoto struct xfrm_migrate *m, int num_migrate) 18955c79de6eSShinta Sugimoto { 18965c79de6eSShinta Sugimoto struct sk_buff *skb; 18975c79de6eSShinta Sugimoto size_t len; 18985c79de6eSShinta Sugimoto 18995c79de6eSShinta Sugimoto len = RTA_SPACE(sizeof(struct xfrm_user_migrate) * num_migrate); 19005c79de6eSShinta Sugimoto len += NLMSG_SPACE(sizeof(struct xfrm_userpolicy_id)); 19015c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_SUB_POLICY 19025c79de6eSShinta Sugimoto len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type)); 19035c79de6eSShinta Sugimoto #endif 19045c79de6eSShinta Sugimoto skb = alloc_skb(len, GFP_ATOMIC); 19055c79de6eSShinta Sugimoto if (skb == NULL) 19065c79de6eSShinta Sugimoto return -ENOMEM; 19075c79de6eSShinta Sugimoto 19085c79de6eSShinta Sugimoto /* build migrate */ 19095c79de6eSShinta Sugimoto if (build_migrate(skb, m, num_migrate, sel, dir, type) < 0) 19105c79de6eSShinta Sugimoto BUG(); 19115c79de6eSShinta Sugimoto 19125c79de6eSShinta Sugimoto NETLINK_CB(skb).dst_group = XFRMNLGRP_MIGRATE; 19135c79de6eSShinta Sugimoto return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_MIGRATE, 19145c79de6eSShinta Sugimoto GFP_ATOMIC); 19155c79de6eSShinta Sugimoto } 19165c79de6eSShinta Sugimoto #else 19175c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 19185c79de6eSShinta Sugimoto struct xfrm_migrate *m, int num_migrate) 19195c79de6eSShinta Sugimoto { 19205c79de6eSShinta Sugimoto return -ENOPROTOOPT; 19215c79de6eSShinta Sugimoto } 19225c79de6eSShinta Sugimoto #endif 1923d51d081dSJamal Hadi Salim 1924492b558bSThomas Graf #define XMSGSIZE(type) NLMSG_LENGTH(sizeof(struct type)) 1925492b558bSThomas Graf 1926492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 1927492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 1928492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 1929492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 1930492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 1931492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1932492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1933492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 1934980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 193553bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 1936492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 1937492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 19386c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 1939492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 1940492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = NLMSG_LENGTH(0), 1941d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 1942d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 194397a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 19445c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 1945566ec034SJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)), 1946ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)), 19471da177e4SLinus Torvalds }; 19481da177e4SLinus Torvalds 1949492b558bSThomas Graf #undef XMSGSIZE 1950492b558bSThomas Graf 19511da177e4SLinus Torvalds static struct xfrm_link { 195222e70050SChristoph Hellwig int (*doit)(struct sk_buff *, struct nlmsghdr *, struct rtattr **); 19531da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 1954492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 1955492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 1956492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 1957492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 1958492b558bSThomas Graf .dump = xfrm_dump_sa }, 1959492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 1960492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 1961492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 1962492b558bSThomas Graf .dump = xfrm_dump_policy }, 1963492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 1964980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 196553bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 1966492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 1967492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 19686c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 1969492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 1970492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 1971d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 1972d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 19735c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 197428d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 1975ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 19761da177e4SLinus Torvalds }; 19771da177e4SLinus Torvalds 19781d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 19791da177e4SLinus Torvalds { 19801da177e4SLinus Torvalds struct rtattr *xfrma[XFRMA_MAX]; 19811da177e4SLinus Torvalds struct xfrm_link *link; 1982c702e804SThomas Graf int type, min_len; 19831da177e4SLinus Torvalds 19841da177e4SLinus Torvalds type = nlh->nlmsg_type; 19851da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 19861d00a4ebSThomas Graf return -EINVAL; 19871da177e4SLinus Torvalds 19881da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 19891da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 19901da177e4SLinus Torvalds 19911da177e4SLinus Torvalds /* All operations require privileges, even GET */ 19921d00a4ebSThomas Graf if (security_netlink_recv(skb, CAP_NET_ADMIN)) 19931d00a4ebSThomas Graf return -EPERM; 19941da177e4SLinus Torvalds 1995492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 1996492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 1997492b558bSThomas Graf (nlh->nlmsg_flags & NLM_F_DUMP)) { 19981da177e4SLinus Torvalds if (link->dump == NULL) 19991d00a4ebSThomas Graf return -EINVAL; 20001da177e4SLinus Torvalds 2001c702e804SThomas Graf return netlink_dump_start(xfrm_nl, skb, nlh, link->dump, NULL); 20021da177e4SLinus Torvalds } 20031da177e4SLinus Torvalds 20041da177e4SLinus Torvalds memset(xfrma, 0, sizeof(xfrma)); 20051da177e4SLinus Torvalds 20061da177e4SLinus Torvalds if (nlh->nlmsg_len < (min_len = xfrm_msg_min[type])) 20071d00a4ebSThomas Graf return -EINVAL; 20081da177e4SLinus Torvalds 20091da177e4SLinus Torvalds if (nlh->nlmsg_len > min_len) { 20101da177e4SLinus Torvalds int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); 20111da177e4SLinus Torvalds struct rtattr *attr = (void *) nlh + NLMSG_ALIGN(min_len); 20121da177e4SLinus Torvalds 20131da177e4SLinus Torvalds while (RTA_OK(attr, attrlen)) { 20141da177e4SLinus Torvalds unsigned short flavor = attr->rta_type; 20151da177e4SLinus Torvalds if (flavor) { 20161da177e4SLinus Torvalds if (flavor > XFRMA_MAX) 20171d00a4ebSThomas Graf return -EINVAL; 20181da177e4SLinus Torvalds xfrma[flavor - 1] = attr; 20191da177e4SLinus Torvalds } 20201da177e4SLinus Torvalds attr = RTA_NEXT(attr, attrlen); 20211da177e4SLinus Torvalds } 20221da177e4SLinus Torvalds } 20231da177e4SLinus Torvalds 20241da177e4SLinus Torvalds if (link->doit == NULL) 20251d00a4ebSThomas Graf return -EINVAL; 20261da177e4SLinus Torvalds 20271d00a4ebSThomas Graf return link->doit(skb, nlh, xfrma); 20281da177e4SLinus Torvalds } 20291da177e4SLinus Torvalds 20301da177e4SLinus Torvalds static void xfrm_netlink_rcv(struct sock *sk, int len) 20311da177e4SLinus Torvalds { 203288fc2c84SThomas Graf unsigned int qlen = 0; 20332a0a6ebeSHerbert Xu 20341da177e4SLinus Torvalds do { 20354a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 203688fc2c84SThomas Graf netlink_run_queue(sk, &qlen, &xfrm_user_rcv_msg); 20374a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 20381da177e4SLinus Torvalds 20392a0a6ebeSHerbert Xu } while (qlen); 20401da177e4SLinus Torvalds } 20411da177e4SLinus Torvalds 2042d51d081dSJamal Hadi Salim static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 20431da177e4SLinus Torvalds { 20441da177e4SLinus Torvalds struct xfrm_user_expire *ue; 20451da177e4SLinus Torvalds struct nlmsghdr *nlh; 204627a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 20471da177e4SLinus Torvalds 2048d51d081dSJamal Hadi Salim nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_EXPIRE, 20491da177e4SLinus Torvalds sizeof(*ue)); 20501da177e4SLinus Torvalds ue = NLMSG_DATA(nlh); 20511da177e4SLinus Torvalds nlh->nlmsg_flags = 0; 20521da177e4SLinus Torvalds 20531da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2054d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 20551da177e4SLinus Torvalds 205627a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 20571da177e4SLinus Torvalds return skb->len; 20581da177e4SLinus Torvalds 20591da177e4SLinus Torvalds nlmsg_failure: 2060dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 20611da177e4SLinus Torvalds return -1; 20621da177e4SLinus Torvalds } 20631da177e4SLinus Torvalds 206426b15dadSJamal Hadi Salim static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c) 20651da177e4SLinus Torvalds { 20661da177e4SLinus Torvalds struct sk_buff *skb; 2067ee57eef9SJamal Hadi Salim int len = NLMSG_LENGTH(sizeof(struct xfrm_user_expire)); 20681da177e4SLinus Torvalds 2069ee57eef9SJamal Hadi Salim skb = alloc_skb(len, GFP_ATOMIC); 20701da177e4SLinus Torvalds if (skb == NULL) 20711da177e4SLinus Torvalds return -ENOMEM; 20721da177e4SLinus Torvalds 2073d51d081dSJamal Hadi Salim if (build_expire(skb, x, c) < 0) 20741da177e4SLinus Torvalds BUG(); 20751da177e4SLinus Torvalds 2076ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_EXPIRE; 2077ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 20781da177e4SLinus Torvalds } 20791da177e4SLinus Torvalds 2080d51d081dSJamal Hadi Salim static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c) 2081d51d081dSJamal Hadi Salim { 2082d51d081dSJamal Hadi Salim struct sk_buff *skb; 2083d51d081dSJamal Hadi Salim int len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); 2084d51d081dSJamal Hadi Salim 2085d51d081dSJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_replay_state)); 2086d51d081dSJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_lifetime_cur)); 2087d51d081dSJamal Hadi Salim skb = alloc_skb(len, GFP_ATOMIC); 2088d51d081dSJamal Hadi Salim if (skb == NULL) 2089d51d081dSJamal Hadi Salim return -ENOMEM; 2090d51d081dSJamal Hadi Salim 2091d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2092d51d081dSJamal Hadi Salim BUG(); 2093d51d081dSJamal Hadi Salim 2094d51d081dSJamal Hadi Salim NETLINK_CB(skb).dst_group = XFRMNLGRP_AEVENTS; 2095d51d081dSJamal Hadi Salim return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2096d51d081dSJamal Hadi Salim } 2097d51d081dSJamal Hadi Salim 209826b15dadSJamal Hadi Salim static int xfrm_notify_sa_flush(struct km_event *c) 209926b15dadSJamal Hadi Salim { 210026b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 210126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 210226b15dadSJamal Hadi Salim struct sk_buff *skb; 210327a884dcSArnaldo Carvalho de Melo sk_buff_data_t b; 210426b15dadSJamal Hadi Salim int len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush)); 210526b15dadSJamal Hadi Salim 210626b15dadSJamal Hadi Salim skb = alloc_skb(len, GFP_ATOMIC); 210726b15dadSJamal Hadi Salim if (skb == NULL) 210826b15dadSJamal Hadi Salim return -ENOMEM; 210926b15dadSJamal Hadi Salim b = skb->tail; 211026b15dadSJamal Hadi Salim 211126b15dadSJamal Hadi Salim nlh = NLMSG_PUT(skb, c->pid, c->seq, 211226b15dadSJamal Hadi Salim XFRM_MSG_FLUSHSA, sizeof(*p)); 211326b15dadSJamal Hadi Salim nlh->nlmsg_flags = 0; 211426b15dadSJamal Hadi Salim 211526b15dadSJamal Hadi Salim p = NLMSG_DATA(nlh); 2116bf08867fSHerbert Xu p->proto = c->data.proto; 211726b15dadSJamal Hadi Salim 211826b15dadSJamal Hadi Salim nlh->nlmsg_len = skb->tail - b; 211926b15dadSJamal Hadi Salim 2120ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_SA; 2121ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 212226b15dadSJamal Hadi Salim 212326b15dadSJamal Hadi Salim nlmsg_failure: 212426b15dadSJamal Hadi Salim kfree_skb(skb); 212526b15dadSJamal Hadi Salim return -1; 212626b15dadSJamal Hadi Salim } 212726b15dadSJamal Hadi Salim 2128b6f99a21SDave Jones static inline int xfrm_sa_len(struct xfrm_state *x) 212926b15dadSJamal Hadi Salim { 21300603eac0SHerbert Xu int l = 0; 213126b15dadSJamal Hadi Salim if (x->aalg) 213226b15dadSJamal Hadi Salim l += RTA_SPACE(sizeof(*x->aalg) + (x->aalg->alg_key_len+7)/8); 213326b15dadSJamal Hadi Salim if (x->ealg) 213426b15dadSJamal Hadi Salim l += RTA_SPACE(sizeof(*x->ealg) + (x->ealg->alg_key_len+7)/8); 213526b15dadSJamal Hadi Salim if (x->calg) 213626b15dadSJamal Hadi Salim l += RTA_SPACE(sizeof(*x->calg)); 213726b15dadSJamal Hadi Salim if (x->encap) 213826b15dadSJamal Hadi Salim l += RTA_SPACE(sizeof(*x->encap)); 213926b15dadSJamal Hadi Salim 214026b15dadSJamal Hadi Salim return l; 214126b15dadSJamal Hadi Salim } 214226b15dadSJamal Hadi Salim 214326b15dadSJamal Hadi Salim static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c) 214426b15dadSJamal Hadi Salim { 214526b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 21460603eac0SHerbert Xu struct xfrm_usersa_id *id; 214726b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 214826b15dadSJamal Hadi Salim struct sk_buff *skb; 214927a884dcSArnaldo Carvalho de Melo sk_buff_data_t b; 215026b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 21510603eac0SHerbert Xu int headlen; 21520603eac0SHerbert Xu 21530603eac0SHerbert Xu headlen = sizeof(*p); 21540603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 21550603eac0SHerbert Xu len += RTA_SPACE(headlen); 21560603eac0SHerbert Xu headlen = sizeof(*id); 21570603eac0SHerbert Xu } 21580603eac0SHerbert Xu len += NLMSG_SPACE(headlen); 215926b15dadSJamal Hadi Salim 216026b15dadSJamal Hadi Salim skb = alloc_skb(len, GFP_ATOMIC); 216126b15dadSJamal Hadi Salim if (skb == NULL) 216226b15dadSJamal Hadi Salim return -ENOMEM; 216326b15dadSJamal Hadi Salim b = skb->tail; 216426b15dadSJamal Hadi Salim 21650603eac0SHerbert Xu nlh = NLMSG_PUT(skb, c->pid, c->seq, c->event, headlen); 216626b15dadSJamal Hadi Salim nlh->nlmsg_flags = 0; 216726b15dadSJamal Hadi Salim 216826b15dadSJamal Hadi Salim p = NLMSG_DATA(nlh); 21690603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 21700603eac0SHerbert Xu id = NLMSG_DATA(nlh); 21710603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 21720603eac0SHerbert Xu id->spi = x->id.spi; 21730603eac0SHerbert Xu id->family = x->props.family; 21740603eac0SHerbert Xu id->proto = x->id.proto; 21750603eac0SHerbert Xu 21760603eac0SHerbert Xu p = RTA_DATA(__RTA_PUT(skb, XFRMA_SA, sizeof(*p))); 21770603eac0SHerbert Xu } 21780603eac0SHerbert Xu 217926b15dadSJamal Hadi Salim copy_to_user_state(x, p); 218026b15dadSJamal Hadi Salim 218126b15dadSJamal Hadi Salim if (x->aalg) 218226b15dadSJamal Hadi Salim RTA_PUT(skb, XFRMA_ALG_AUTH, 218326b15dadSJamal Hadi Salim sizeof(*(x->aalg))+(x->aalg->alg_key_len+7)/8, x->aalg); 218426b15dadSJamal Hadi Salim if (x->ealg) 218526b15dadSJamal Hadi Salim RTA_PUT(skb, XFRMA_ALG_CRYPT, 218626b15dadSJamal Hadi Salim sizeof(*(x->ealg))+(x->ealg->alg_key_len+7)/8, x->ealg); 218726b15dadSJamal Hadi Salim if (x->calg) 218826b15dadSJamal Hadi Salim RTA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 218926b15dadSJamal Hadi Salim 219026b15dadSJamal Hadi Salim if (x->encap) 219126b15dadSJamal Hadi Salim RTA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 219226b15dadSJamal Hadi Salim 219326b15dadSJamal Hadi Salim nlh->nlmsg_len = skb->tail - b; 219426b15dadSJamal Hadi Salim 2195ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_SA; 2196ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 219726b15dadSJamal Hadi Salim 219826b15dadSJamal Hadi Salim nlmsg_failure: 219926b15dadSJamal Hadi Salim rtattr_failure: 220026b15dadSJamal Hadi Salim kfree_skb(skb); 220126b15dadSJamal Hadi Salim return -1; 220226b15dadSJamal Hadi Salim } 220326b15dadSJamal Hadi Salim 220426b15dadSJamal Hadi Salim static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c) 220526b15dadSJamal Hadi Salim { 220626b15dadSJamal Hadi Salim 220726b15dadSJamal Hadi Salim switch (c->event) { 2208f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 220926b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2210d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2211d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2212f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2213f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2214f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 221526b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2216f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 221726b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 221826b15dadSJamal Hadi Salim default: 221926b15dadSJamal Hadi Salim printk("xfrm_user: Unknown SA event %d\n", c->event); 222026b15dadSJamal Hadi Salim break; 222126b15dadSJamal Hadi Salim } 222226b15dadSJamal Hadi Salim 222326b15dadSJamal Hadi Salim return 0; 222426b15dadSJamal Hadi Salim 222526b15dadSJamal Hadi Salim } 222626b15dadSJamal Hadi Salim 22271da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 22281da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 22291da177e4SLinus Torvalds int dir) 22301da177e4SLinus Torvalds { 22311da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 22321da177e4SLinus Torvalds struct nlmsghdr *nlh; 223327a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 22341da177e4SLinus Torvalds __u32 seq = xfrm_get_acqseq(); 22351da177e4SLinus Torvalds 22361da177e4SLinus Torvalds nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_ACQUIRE, 22371da177e4SLinus Torvalds sizeof(*ua)); 22381da177e4SLinus Torvalds ua = NLMSG_DATA(nlh); 22391da177e4SLinus Torvalds nlh->nlmsg_flags = 0; 22401da177e4SLinus Torvalds 22411da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 22421da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 22431da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 22441da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 22451da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 22461da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 22471da177e4SLinus Torvalds ua->calgos = xt->calgos; 22481da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 22491da177e4SLinus Torvalds 22501da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 22511da177e4SLinus Torvalds goto nlmsg_failure; 22520d681623SSerge Hallyn if (copy_to_user_state_sec_ctx(x, skb)) 2253df71837dSTrent Jaeger goto nlmsg_failure; 22541459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2255f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 22561da177e4SLinus Torvalds 225727a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 22581da177e4SLinus Torvalds return skb->len; 22591da177e4SLinus Torvalds 22601da177e4SLinus Torvalds nlmsg_failure: 2261dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 22621da177e4SLinus Torvalds return -1; 22631da177e4SLinus Torvalds } 22641da177e4SLinus Torvalds 22651da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 22661da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 22671da177e4SLinus Torvalds { 22681da177e4SLinus Torvalds struct sk_buff *skb; 22691da177e4SLinus Torvalds size_t len; 22701da177e4SLinus Torvalds 22711da177e4SLinus Torvalds len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 22721da177e4SLinus Torvalds len += NLMSG_SPACE(sizeof(struct xfrm_user_acquire)); 2273661697f7SJoy Latten len += RTA_SPACE(xfrm_user_sec_ctx_size(x->security)); 2274785fd8b8SJamal Hadi Salim #ifdef CONFIG_XFRM_SUB_POLICY 2275785fd8b8SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type)); 2276785fd8b8SJamal Hadi Salim #endif 22771da177e4SLinus Torvalds skb = alloc_skb(len, GFP_ATOMIC); 22781da177e4SLinus Torvalds if (skb == NULL) 22791da177e4SLinus Torvalds return -ENOMEM; 22801da177e4SLinus Torvalds 22811da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 22821da177e4SLinus Torvalds BUG(); 22831da177e4SLinus Torvalds 2284ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_ACQUIRE; 2285ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 22861da177e4SLinus Torvalds } 22871da177e4SLinus Torvalds 22881da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 22891da177e4SLinus Torvalds * or more templates. 22901da177e4SLinus Torvalds */ 2291cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 22921da177e4SLinus Torvalds u8 *data, int len, int *dir) 22931da177e4SLinus Torvalds { 22941da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 22951da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 22961da177e4SLinus Torvalds struct xfrm_policy *xp; 22971da177e4SLinus Torvalds int nr; 22981da177e4SLinus Torvalds 2299cb969f07SVenkat Yekkirala switch (sk->sk_family) { 23001da177e4SLinus Torvalds case AF_INET: 23011da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 23021da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 23031da177e4SLinus Torvalds return NULL; 23041da177e4SLinus Torvalds } 23051da177e4SLinus Torvalds break; 23061da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 23071da177e4SLinus Torvalds case AF_INET6: 23081da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 23091da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 23101da177e4SLinus Torvalds return NULL; 23111da177e4SLinus Torvalds } 23121da177e4SLinus Torvalds break; 23131da177e4SLinus Torvalds #endif 23141da177e4SLinus Torvalds default: 23151da177e4SLinus Torvalds *dir = -EINVAL; 23161da177e4SLinus Torvalds return NULL; 23171da177e4SLinus Torvalds } 23181da177e4SLinus Torvalds 23191da177e4SLinus Torvalds *dir = -EINVAL; 23201da177e4SLinus Torvalds 23211da177e4SLinus Torvalds if (len < sizeof(*p) || 23221da177e4SLinus Torvalds verify_newpolicy_info(p)) 23231da177e4SLinus Torvalds return NULL; 23241da177e4SLinus Torvalds 23251da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2326b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 23271da177e4SLinus Torvalds return NULL; 23281da177e4SLinus Torvalds 2329a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2330a4f1bac6SHerbert Xu return NULL; 2331a4f1bac6SHerbert Xu 23321da177e4SLinus Torvalds xp = xfrm_policy_alloc(GFP_KERNEL); 23331da177e4SLinus Torvalds if (xp == NULL) { 23341da177e4SLinus Torvalds *dir = -ENOBUFS; 23351da177e4SLinus Torvalds return NULL; 23361da177e4SLinus Torvalds } 23371da177e4SLinus Torvalds 23381da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2339f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 23401da177e4SLinus Torvalds copy_templates(xp, ut, nr); 23411da177e4SLinus Torvalds 23421da177e4SLinus Torvalds *dir = p->dir; 23431da177e4SLinus Torvalds 23441da177e4SLinus Torvalds return xp; 23451da177e4SLinus Torvalds } 23461da177e4SLinus Torvalds 23471da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2348d51d081dSJamal Hadi Salim int dir, struct km_event *c) 23491da177e4SLinus Torvalds { 23501da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 23511da177e4SLinus Torvalds struct nlmsghdr *nlh; 2352d51d081dSJamal Hadi Salim int hard = c->data.hard; 235327a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 23541da177e4SLinus Torvalds 2355d51d081dSJamal Hadi Salim nlh = NLMSG_PUT(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe)); 23561da177e4SLinus Torvalds upe = NLMSG_DATA(nlh); 23571da177e4SLinus Torvalds nlh->nlmsg_flags = 0; 23581da177e4SLinus Torvalds 23591da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 23601da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 23611da177e4SLinus Torvalds goto nlmsg_failure; 2362df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 2363df71837dSTrent Jaeger goto nlmsg_failure; 23641459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2365f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 23661da177e4SLinus Torvalds upe->hard = !!hard; 23671da177e4SLinus Torvalds 236827a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 23691da177e4SLinus Torvalds return skb->len; 23701da177e4SLinus Torvalds 23711da177e4SLinus Torvalds nlmsg_failure: 2372dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 23731da177e4SLinus Torvalds return -1; 23741da177e4SLinus Torvalds } 23751da177e4SLinus Torvalds 237626b15dadSJamal Hadi Salim static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 23771da177e4SLinus Torvalds { 23781da177e4SLinus Torvalds struct sk_buff *skb; 23791da177e4SLinus Torvalds size_t len; 23801da177e4SLinus Torvalds 23811da177e4SLinus Torvalds len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 23821da177e4SLinus Torvalds len += NLMSG_SPACE(sizeof(struct xfrm_user_polexpire)); 2383661697f7SJoy Latten len += RTA_SPACE(xfrm_user_sec_ctx_size(xp->security)); 2384785fd8b8SJamal Hadi Salim #ifdef CONFIG_XFRM_SUB_POLICY 2385785fd8b8SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type)); 2386785fd8b8SJamal Hadi Salim #endif 23871da177e4SLinus Torvalds skb = alloc_skb(len, GFP_ATOMIC); 23881da177e4SLinus Torvalds if (skb == NULL) 23891da177e4SLinus Torvalds return -ENOMEM; 23901da177e4SLinus Torvalds 2391d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 23921da177e4SLinus Torvalds BUG(); 23931da177e4SLinus Torvalds 2394ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_EXPIRE; 2395ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 23961da177e4SLinus Torvalds } 23971da177e4SLinus Torvalds 239826b15dadSJamal Hadi Salim static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c) 239926b15dadSJamal Hadi Salim { 240026b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 24010603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 240226b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 240326b15dadSJamal Hadi Salim struct sk_buff *skb; 240427a884dcSArnaldo Carvalho de Melo sk_buff_data_t b; 240526b15dadSJamal Hadi Salim int len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 24060603eac0SHerbert Xu int headlen; 24070603eac0SHerbert Xu 24080603eac0SHerbert Xu headlen = sizeof(*p); 24090603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 24100603eac0SHerbert Xu len += RTA_SPACE(headlen); 24110603eac0SHerbert Xu headlen = sizeof(*id); 24120603eac0SHerbert Xu } 2413334f3d45SJamal Hadi Salim #ifdef CONFIG_XFRM_SUB_POLICY 2414334f3d45SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type)); 2415334f3d45SJamal Hadi Salim #endif 24160603eac0SHerbert Xu len += NLMSG_SPACE(headlen); 241726b15dadSJamal Hadi Salim 241826b15dadSJamal Hadi Salim skb = alloc_skb(len, GFP_ATOMIC); 241926b15dadSJamal Hadi Salim if (skb == NULL) 242026b15dadSJamal Hadi Salim return -ENOMEM; 242126b15dadSJamal Hadi Salim b = skb->tail; 242226b15dadSJamal Hadi Salim 24230603eac0SHerbert Xu nlh = NLMSG_PUT(skb, c->pid, c->seq, c->event, headlen); 242426b15dadSJamal Hadi Salim 242526b15dadSJamal Hadi Salim p = NLMSG_DATA(nlh); 24260603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 24270603eac0SHerbert Xu id = NLMSG_DATA(nlh); 24280603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 24290603eac0SHerbert Xu id->dir = dir; 24300603eac0SHerbert Xu if (c->data.byid) 24310603eac0SHerbert Xu id->index = xp->index; 24320603eac0SHerbert Xu else 24330603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 24340603eac0SHerbert Xu 24350603eac0SHerbert Xu p = RTA_DATA(__RTA_PUT(skb, XFRMA_POLICY, sizeof(*p))); 24360603eac0SHerbert Xu } 243726b15dadSJamal Hadi Salim 243826b15dadSJamal Hadi Salim nlh->nlmsg_flags = 0; 243926b15dadSJamal Hadi Salim 244026b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 244126b15dadSJamal Hadi Salim if (copy_to_user_tmpl(xp, skb) < 0) 244226b15dadSJamal Hadi Salim goto nlmsg_failure; 24431459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2444f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 244526b15dadSJamal Hadi Salim 244626b15dadSJamal Hadi Salim nlh->nlmsg_len = skb->tail - b; 244726b15dadSJamal Hadi Salim 2448ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_POLICY; 2449ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 245026b15dadSJamal Hadi Salim 245126b15dadSJamal Hadi Salim nlmsg_failure: 24520603eac0SHerbert Xu rtattr_failure: 245326b15dadSJamal Hadi Salim kfree_skb(skb); 245426b15dadSJamal Hadi Salim return -1; 245526b15dadSJamal Hadi Salim } 245626b15dadSJamal Hadi Salim 245726b15dadSJamal Hadi Salim static int xfrm_notify_policy_flush(struct km_event *c) 245826b15dadSJamal Hadi Salim { 245926b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 246026b15dadSJamal Hadi Salim struct sk_buff *skb; 246127a884dcSArnaldo Carvalho de Melo sk_buff_data_t b; 2462785fd8b8SJamal Hadi Salim int len = 0; 2463f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 2464785fd8b8SJamal Hadi Salim len += RTA_SPACE(sizeof(struct xfrm_userpolicy_type)); 2465f7b6983fSMasahide NAKAMURA #endif 2466785fd8b8SJamal Hadi Salim len += NLMSG_LENGTH(0); 246726b15dadSJamal Hadi Salim 246826b15dadSJamal Hadi Salim skb = alloc_skb(len, GFP_ATOMIC); 246926b15dadSJamal Hadi Salim if (skb == NULL) 247026b15dadSJamal Hadi Salim return -ENOMEM; 247126b15dadSJamal Hadi Salim b = skb->tail; 247226b15dadSJamal Hadi Salim 247326b15dadSJamal Hadi Salim 247426b15dadSJamal Hadi Salim nlh = NLMSG_PUT(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0); 2475f7b6983fSMasahide NAKAMURA nlh->nlmsg_flags = 0; 24760c51f53cSJamal Hadi Salim if (copy_to_user_policy_type(c->data.type, skb) < 0) 24770c51f53cSJamal Hadi Salim goto nlmsg_failure; 247826b15dadSJamal Hadi Salim 247926b15dadSJamal Hadi Salim nlh->nlmsg_len = skb->tail - b; 248026b15dadSJamal Hadi Salim 2481ac6d439dSPatrick McHardy NETLINK_CB(skb).dst_group = XFRMNLGRP_POLICY; 2482ac6d439dSPatrick McHardy return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 248326b15dadSJamal Hadi Salim 248426b15dadSJamal Hadi Salim nlmsg_failure: 248526b15dadSJamal Hadi Salim kfree_skb(skb); 248626b15dadSJamal Hadi Salim return -1; 248726b15dadSJamal Hadi Salim } 248826b15dadSJamal Hadi Salim 248926b15dadSJamal Hadi Salim static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 249026b15dadSJamal Hadi Salim { 249126b15dadSJamal Hadi Salim 249226b15dadSJamal Hadi Salim switch (c->event) { 2493f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2494f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2495f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 249626b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2497f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 249826b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2499f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 250026b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 250126b15dadSJamal Hadi Salim default: 250226b15dadSJamal Hadi Salim printk("xfrm_user: Unknown Policy event %d\n", c->event); 250326b15dadSJamal Hadi Salim } 250426b15dadSJamal Hadi Salim 250526b15dadSJamal Hadi Salim return 0; 250626b15dadSJamal Hadi Salim 250726b15dadSJamal Hadi Salim } 250826b15dadSJamal Hadi Salim 250997a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 251097a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 251197a64b45SMasahide NAKAMURA { 251297a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 251397a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 251427a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 251597a64b45SMasahide NAKAMURA 251697a64b45SMasahide NAKAMURA nlh = NLMSG_PUT(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur)); 251797a64b45SMasahide NAKAMURA ur = NLMSG_DATA(nlh); 251897a64b45SMasahide NAKAMURA nlh->nlmsg_flags = 0; 251997a64b45SMasahide NAKAMURA 252097a64b45SMasahide NAKAMURA ur->proto = proto; 252197a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 252297a64b45SMasahide NAKAMURA 252397a64b45SMasahide NAKAMURA if (addr) 252497a64b45SMasahide NAKAMURA RTA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); 252597a64b45SMasahide NAKAMURA 252627a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b; 252797a64b45SMasahide NAKAMURA return skb->len; 252897a64b45SMasahide NAKAMURA 252997a64b45SMasahide NAKAMURA nlmsg_failure: 253097a64b45SMasahide NAKAMURA rtattr_failure: 2531dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 253297a64b45SMasahide NAKAMURA return -1; 253397a64b45SMasahide NAKAMURA } 253497a64b45SMasahide NAKAMURA 253597a64b45SMasahide NAKAMURA static int xfrm_send_report(u8 proto, struct xfrm_selector *sel, 253697a64b45SMasahide NAKAMURA xfrm_address_t *addr) 253797a64b45SMasahide NAKAMURA { 253897a64b45SMasahide NAKAMURA struct sk_buff *skb; 253997a64b45SMasahide NAKAMURA size_t len; 254097a64b45SMasahide NAKAMURA 254197a64b45SMasahide NAKAMURA len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct xfrm_user_report))); 254297a64b45SMasahide NAKAMURA skb = alloc_skb(len, GFP_ATOMIC); 254397a64b45SMasahide NAKAMURA if (skb == NULL) 254497a64b45SMasahide NAKAMURA return -ENOMEM; 254597a64b45SMasahide NAKAMURA 254697a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 254797a64b45SMasahide NAKAMURA BUG(); 254897a64b45SMasahide NAKAMURA 254997a64b45SMasahide NAKAMURA NETLINK_CB(skb).dst_group = XFRMNLGRP_REPORT; 255097a64b45SMasahide NAKAMURA return netlink_broadcast(xfrm_nl, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 255197a64b45SMasahide NAKAMURA } 255297a64b45SMasahide NAKAMURA 25531da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 25541da177e4SLinus Torvalds .id = "netlink", 25551da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 25561da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 25571da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 25581da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 255997a64b45SMasahide NAKAMURA .report = xfrm_send_report, 25605c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 25611da177e4SLinus Torvalds }; 25621da177e4SLinus Torvalds 25631da177e4SLinus Torvalds static int __init xfrm_user_init(void) 25641da177e4SLinus Torvalds { 2565be33690dSPatrick McHardy struct sock *nlsk; 2566be33690dSPatrick McHardy 2567654b32c6SMasahide NAKAMURA printk(KERN_INFO "Initializing XFRM netlink socket\n"); 25681da177e4SLinus Torvalds 2569be33690dSPatrick McHardy nlsk = netlink_kernel_create(NETLINK_XFRM, XFRMNLGRP_MAX, 2570af65bdfcSPatrick McHardy xfrm_netlink_rcv, NULL, THIS_MODULE); 2571be33690dSPatrick McHardy if (nlsk == NULL) 25721da177e4SLinus Torvalds return -ENOMEM; 2573be33690dSPatrick McHardy rcu_assign_pointer(xfrm_nl, nlsk); 25741da177e4SLinus Torvalds 25751da177e4SLinus Torvalds xfrm_register_km(&netlink_mgr); 25761da177e4SLinus Torvalds 25771da177e4SLinus Torvalds return 0; 25781da177e4SLinus Torvalds } 25791da177e4SLinus Torvalds 25801da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 25811da177e4SLinus Torvalds { 2582be33690dSPatrick McHardy struct sock *nlsk = xfrm_nl; 2583be33690dSPatrick McHardy 25841da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 2585be33690dSPatrick McHardy rcu_assign_pointer(xfrm_nl, NULL); 2586be33690dSPatrick McHardy synchronize_rcu(); 2587be33690dSPatrick McHardy sock_release(nlsk->sk_socket); 25881da177e4SLinus Torvalds } 25891da177e4SLinus Torvalds 25901da177e4SLinus Torvalds module_init(xfrm_user_init); 25911da177e4SLinus Torvalds module_exit(xfrm_user_exit); 25921da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 25934fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 2594f8cd5488SJamal Hadi Salim 2595