11da177e4SLinus Torvalds /* xfrm_user.c: User interface to configure xfrm engine. 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * Copyright (C) 2002 David S. Miller (davem@redhat.com) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Changes: 61da177e4SLinus Torvalds * Mitsuru KANDA @USAGI 71da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 81da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 91da177e4SLinus Torvalds * IPv6 support 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 139409f38aSHerbert Xu #include <linux/crypto.h> 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/types.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 181da177e4SLinus Torvalds #include <linux/socket.h> 191da177e4SLinus Torvalds #include <linux/string.h> 201da177e4SLinus Torvalds #include <linux/net.h> 211da177e4SLinus Torvalds #include <linux/skbuff.h> 221da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 231da177e4SLinus Torvalds #include <linux/ipsec.h> 241da177e4SLinus Torvalds #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/security.h> 261da177e4SLinus Torvalds #include <net/sock.h> 271da177e4SLinus Torvalds #include <net/xfrm.h> 2888fc2c84SThomas Graf #include <net/netlink.h> 29fa6dd8a2SNicolas Dichtel #include <net/ah.h> 301da177e4SLinus Torvalds #include <asm/uaccess.h> 31dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 32e23c7194SMasahide NAKAMURA #include <linux/in6.h> 33e23c7194SMasahide NAKAMURA #endif 341da177e4SLinus Torvalds 355424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) 361da177e4SLinus Torvalds { 375424f32eSThomas Graf struct nlattr *rt = attrs[type]; 381da177e4SLinus Torvalds struct xfrm_algo *algp; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds if (!rt) 411da177e4SLinus Torvalds return 0; 421da177e4SLinus Torvalds 435424f32eSThomas Graf algp = nla_data(rt); 440f99be0dSEric Dumazet if (nla_len(rt) < xfrm_alg_len(algp)) 4531c26852SHerbert Xu return -EINVAL; 4631c26852SHerbert Xu 471da177e4SLinus Torvalds switch (type) { 481da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 491da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 501da177e4SLinus Torvalds case XFRMA_ALG_COMP: 511da177e4SLinus Torvalds break; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds default: 541da177e4SLinus Torvalds return -EINVAL; 553ff50b79SStephen Hemminger } 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 581da177e4SLinus Torvalds return 0; 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 614447bb33SMartin Willi static int verify_auth_trunc(struct nlattr **attrs) 624447bb33SMartin Willi { 634447bb33SMartin Willi struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; 644447bb33SMartin Willi struct xfrm_algo_auth *algp; 654447bb33SMartin Willi 664447bb33SMartin Willi if (!rt) 674447bb33SMartin Willi return 0; 684447bb33SMartin Willi 694447bb33SMartin Willi algp = nla_data(rt); 704447bb33SMartin Willi if (nla_len(rt) < xfrm_alg_auth_len(algp)) 714447bb33SMartin Willi return -EINVAL; 724447bb33SMartin Willi 734447bb33SMartin Willi algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 744447bb33SMartin Willi return 0; 754447bb33SMartin Willi } 764447bb33SMartin Willi 771a6509d9SHerbert Xu static int verify_aead(struct nlattr **attrs) 781a6509d9SHerbert Xu { 791a6509d9SHerbert Xu struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; 801a6509d9SHerbert Xu struct xfrm_algo_aead *algp; 811a6509d9SHerbert Xu 821a6509d9SHerbert Xu if (!rt) 831a6509d9SHerbert Xu return 0; 841a6509d9SHerbert Xu 851a6509d9SHerbert Xu algp = nla_data(rt); 861a6509d9SHerbert Xu if (nla_len(rt) < aead_len(algp)) 871a6509d9SHerbert Xu return -EINVAL; 881a6509d9SHerbert Xu 891a6509d9SHerbert Xu algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 901a6509d9SHerbert Xu return 0; 911a6509d9SHerbert Xu } 921a6509d9SHerbert Xu 935424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, 94eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 95eb2971b6SMasahide NAKAMURA { 965424f32eSThomas Graf struct nlattr *rt = attrs[type]; 97eb2971b6SMasahide NAKAMURA 98cf5cb79fSThomas Graf if (rt && addrp) 995424f32eSThomas Graf *addrp = nla_data(rt); 100eb2971b6SMasahide NAKAMURA } 101df71837dSTrent Jaeger 1025424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs) 103df71837dSTrent Jaeger { 1045424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 105df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 106df71837dSTrent Jaeger 107df71837dSTrent Jaeger if (!rt) 108df71837dSTrent Jaeger return 0; 109df71837dSTrent Jaeger 1105424f32eSThomas Graf uctx = nla_data(rt); 111cf5cb79fSThomas Graf if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) 112df71837dSTrent Jaeger return -EINVAL; 113df71837dSTrent Jaeger 114df71837dSTrent Jaeger return 0; 115df71837dSTrent Jaeger } 116df71837dSTrent Jaeger 117d8647b79SSteffen Klassert static inline int verify_replay(struct xfrm_usersa_info *p, 118d8647b79SSteffen Klassert struct nlattr **attrs) 119d8647b79SSteffen Klassert { 120d8647b79SSteffen Klassert struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; 121ecd79187SMathias Krause struct xfrm_replay_state_esn *rs; 122d8647b79SSteffen Klassert 123ecd79187SMathias Krause if (p->flags & XFRM_STATE_ESN) { 124ecd79187SMathias Krause if (!rt) 1257833aa05SSteffen Klassert return -EINVAL; 1267833aa05SSteffen Klassert 127ecd79187SMathias Krause rs = nla_data(rt); 128ecd79187SMathias Krause 129ecd79187SMathias Krause if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) 130ecd79187SMathias Krause return -EINVAL; 131ecd79187SMathias Krause 132ecd79187SMathias Krause if (nla_len(rt) < xfrm_replay_state_esn_len(rs) && 133ecd79187SMathias Krause nla_len(rt) != sizeof(*rs)) 134ecd79187SMathias Krause return -EINVAL; 135ecd79187SMathias Krause } 136ecd79187SMathias Krause 137d8647b79SSteffen Klassert if (!rt) 138d8647b79SSteffen Klassert return 0; 139d8647b79SSteffen Klassert 14001714109SFan Du /* As only ESP and AH support ESN feature. */ 14101714109SFan Du if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) 14202aadf72SSteffen Klassert return -EINVAL; 14302aadf72SSteffen Klassert 144d8647b79SSteffen Klassert if (p->replay_window != 0) 145d8647b79SSteffen Klassert return -EINVAL; 146d8647b79SSteffen Klassert 147d8647b79SSteffen Klassert return 0; 148d8647b79SSteffen Klassert } 149df71837dSTrent Jaeger 1501da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1515424f32eSThomas Graf struct nlattr **attrs) 1521da177e4SLinus Torvalds { 1531da177e4SLinus Torvalds int err; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds err = -EINVAL; 1561da177e4SLinus Torvalds switch (p->family) { 1571da177e4SLinus Torvalds case AF_INET: 1581da177e4SLinus Torvalds break; 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds case AF_INET6: 161dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1621da177e4SLinus Torvalds break; 1631da177e4SLinus Torvalds #else 1641da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1651da177e4SLinus Torvalds goto out; 1661da177e4SLinus Torvalds #endif 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds default: 1691da177e4SLinus Torvalds goto out; 1703ff50b79SStephen Hemminger } 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds err = -EINVAL; 1731da177e4SLinus Torvalds switch (p->id.proto) { 1741da177e4SLinus Torvalds case IPPROTO_AH: 1754447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1764447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1771a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 17835a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 17935d2856bSMartin Willi attrs[XFRMA_ALG_COMP] || 180a0e5ef53STobias Brunner attrs[XFRMA_TFCPAD]) 1811da177e4SLinus Torvalds goto out; 1821da177e4SLinus Torvalds break; 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds case IPPROTO_ESP: 1851a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1861a6509d9SHerbert Xu goto out; 1871a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1884447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1891a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1901a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1911a6509d9SHerbert Xu goto out; 1921a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1934447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1941a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1951a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1961da177e4SLinus Torvalds goto out; 19735d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] && 19835d2856bSMartin Willi p->mode != XFRM_MODE_TUNNEL) 19935d2856bSMartin Willi goto out; 2001da177e4SLinus Torvalds break; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds case IPPROTO_COMP: 20335a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 2041a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 20535a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2064447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 20735d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] || 208a0e5ef53STobias Brunner attrs[XFRMA_TFCPAD] || 209a0e5ef53STobias Brunner (ntohl(p->id.spi) >= 0x10000)) 2101da177e4SLinus Torvalds goto out; 2111da177e4SLinus Torvalds break; 2121da177e4SLinus Torvalds 213dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 214e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 215e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 21635a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 21735a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2184447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 2191a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 22035a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 22135a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 22235a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 22335d2856bSMartin Willi attrs[XFRMA_TFCPAD] || 22435a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 225e23c7194SMasahide NAKAMURA goto out; 226e23c7194SMasahide NAKAMURA break; 227e23c7194SMasahide NAKAMURA #endif 228e23c7194SMasahide NAKAMURA 2291da177e4SLinus Torvalds default: 2301da177e4SLinus Torvalds goto out; 2313ff50b79SStephen Hemminger } 2321da177e4SLinus Torvalds 2331a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 2341a6509d9SHerbert Xu goto out; 2354447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2364447bb33SMartin Willi goto out; 23735a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2381da177e4SLinus Torvalds goto out; 23935a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2401da177e4SLinus Torvalds goto out; 24135a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2421da177e4SLinus Torvalds goto out; 24335a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 244df71837dSTrent Jaeger goto out; 245d8647b79SSteffen Klassert if ((err = verify_replay(p, attrs))) 246d8647b79SSteffen Klassert goto out; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds err = -EINVAL; 2491da177e4SLinus Torvalds switch (p->mode) { 2507e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2517e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 252060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2530a69452cSDiego Beltrami case XFRM_MODE_BEET: 2541da177e4SLinus Torvalds break; 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds default: 2571da177e4SLinus Torvalds goto out; 2583ff50b79SStephen Hemminger } 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds err = 0; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds out: 2631da177e4SLinus Torvalds return err; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2676f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int), 2685424f32eSThomas Graf struct nlattr *rta) 2691da177e4SLinus Torvalds { 2701da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2711da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds if (!rta) 2741da177e4SLinus Torvalds return 0; 2751da177e4SLinus Torvalds 2765424f32eSThomas Graf ualg = nla_data(rta); 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2791da177e4SLinus Torvalds if (!algo) 2801da177e4SLinus Torvalds return -ENOSYS; 2811da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2821da177e4SLinus Torvalds 2830f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2841da177e4SLinus Torvalds if (!p) 2851da177e4SLinus Torvalds return -ENOMEM; 2861da177e4SLinus Torvalds 28704ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2881da177e4SLinus Torvalds *algpp = p; 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2924447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 2934447bb33SMartin Willi struct nlattr *rta) 2944447bb33SMartin Willi { 2954447bb33SMartin Willi struct xfrm_algo *ualg; 2964447bb33SMartin Willi struct xfrm_algo_auth *p; 2974447bb33SMartin Willi struct xfrm_algo_desc *algo; 2984447bb33SMartin Willi 2994447bb33SMartin Willi if (!rta) 3004447bb33SMartin Willi return 0; 3014447bb33SMartin Willi 3024447bb33SMartin Willi ualg = nla_data(rta); 3034447bb33SMartin Willi 3044447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3054447bb33SMartin Willi if (!algo) 3064447bb33SMartin Willi return -ENOSYS; 3074447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3084447bb33SMartin Willi 3094447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 3104447bb33SMartin Willi if (!p) 3114447bb33SMartin Willi return -ENOMEM; 3124447bb33SMartin Willi 3134447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3144447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 3154447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3164447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 3174447bb33SMartin Willi 3184447bb33SMartin Willi *algpp = p; 3194447bb33SMartin Willi return 0; 3204447bb33SMartin Willi } 3214447bb33SMartin Willi 3224447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 3234447bb33SMartin Willi struct nlattr *rta) 3244447bb33SMartin Willi { 3254447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 3264447bb33SMartin Willi struct xfrm_algo_desc *algo; 3274447bb33SMartin Willi 3284447bb33SMartin Willi if (!rta) 3294447bb33SMartin Willi return 0; 3304447bb33SMartin Willi 3314447bb33SMartin Willi ualg = nla_data(rta); 3324447bb33SMartin Willi 3334447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3344447bb33SMartin Willi if (!algo) 3354447bb33SMartin Willi return -ENOSYS; 336689f1c9dSHerbert Xu if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3374447bb33SMartin Willi return -EINVAL; 3384447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3394447bb33SMartin Willi 3404447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3414447bb33SMartin Willi if (!p) 3424447bb33SMartin Willi return -ENOMEM; 3434447bb33SMartin Willi 3444447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3454447bb33SMartin Willi if (!p->alg_trunc_len) 3464447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3474447bb33SMartin Willi 3484447bb33SMartin Willi *algpp = p; 3494447bb33SMartin Willi return 0; 3504447bb33SMartin Willi } 3514447bb33SMartin Willi 3521a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 3531a6509d9SHerbert Xu struct nlattr *rta) 3541a6509d9SHerbert Xu { 3551a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3561a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3571a6509d9SHerbert Xu 3581a6509d9SHerbert Xu if (!rta) 3591a6509d9SHerbert Xu return 0; 3601a6509d9SHerbert Xu 3611a6509d9SHerbert Xu ualg = nla_data(rta); 3621a6509d9SHerbert Xu 3631a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3641a6509d9SHerbert Xu if (!algo) 3651a6509d9SHerbert Xu return -ENOSYS; 3661a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 3671a6509d9SHerbert Xu 3681a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3691a6509d9SHerbert Xu if (!p) 3701a6509d9SHerbert Xu return -ENOMEM; 3711a6509d9SHerbert Xu 3721a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 3731a6509d9SHerbert Xu *algpp = p; 3741a6509d9SHerbert Xu return 0; 3751a6509d9SHerbert Xu } 3761a6509d9SHerbert Xu 377e2b19125SSteffen Klassert static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn, 378e2b19125SSteffen Klassert struct nlattr *rp) 379e2b19125SSteffen Klassert { 380e2b19125SSteffen Klassert struct xfrm_replay_state_esn *up; 381ecd79187SMathias Krause int ulen; 382e2b19125SSteffen Klassert 383e2b19125SSteffen Klassert if (!replay_esn || !rp) 384e2b19125SSteffen Klassert return 0; 385e2b19125SSteffen Klassert 386e2b19125SSteffen Klassert up = nla_data(rp); 387ecd79187SMathias Krause ulen = xfrm_replay_state_esn_len(up); 388e2b19125SSteffen Klassert 389ecd79187SMathias Krause if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen) 390e2b19125SSteffen Klassert return -EINVAL; 391e2b19125SSteffen Klassert 392e2b19125SSteffen Klassert return 0; 393e2b19125SSteffen Klassert } 394e2b19125SSteffen Klassert 395d8647b79SSteffen Klassert static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn, 396d8647b79SSteffen Klassert struct xfrm_replay_state_esn **preplay_esn, 397d8647b79SSteffen Klassert struct nlattr *rta) 398d8647b79SSteffen Klassert { 399d8647b79SSteffen Klassert struct xfrm_replay_state_esn *p, *pp, *up; 400ecd79187SMathias Krause int klen, ulen; 401d8647b79SSteffen Klassert 402d8647b79SSteffen Klassert if (!rta) 403d8647b79SSteffen Klassert return 0; 404d8647b79SSteffen Klassert 405d8647b79SSteffen Klassert up = nla_data(rta); 406ecd79187SMathias Krause klen = xfrm_replay_state_esn_len(up); 407ecd79187SMathias Krause ulen = nla_len(rta) >= klen ? klen : sizeof(*up); 408d8647b79SSteffen Klassert 409ecd79187SMathias Krause p = kzalloc(klen, GFP_KERNEL); 410d8647b79SSteffen Klassert if (!p) 411d8647b79SSteffen Klassert return -ENOMEM; 412d8647b79SSteffen Klassert 413ecd79187SMathias Krause pp = kzalloc(klen, GFP_KERNEL); 414d8647b79SSteffen Klassert if (!pp) { 415d8647b79SSteffen Klassert kfree(p); 416d8647b79SSteffen Klassert return -ENOMEM; 417d8647b79SSteffen Klassert } 418d8647b79SSteffen Klassert 419ecd79187SMathias Krause memcpy(p, up, ulen); 420ecd79187SMathias Krause memcpy(pp, up, ulen); 421ecd79187SMathias Krause 422d8647b79SSteffen Klassert *replay_esn = p; 423d8647b79SSteffen Klassert *preplay_esn = pp; 424d8647b79SSteffen Klassert 425d8647b79SSteffen Klassert return 0; 426d8647b79SSteffen Klassert } 427d8647b79SSteffen Klassert 428661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 429df71837dSTrent Jaeger { 430df71837dSTrent Jaeger int len = 0; 431df71837dSTrent Jaeger 432df71837dSTrent Jaeger if (xfrm_ctx) { 433df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 434df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 435df71837dSTrent Jaeger } 436df71837dSTrent Jaeger return len; 437df71837dSTrent Jaeger } 438df71837dSTrent Jaeger 4391da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 4401da177e4SLinus Torvalds { 4411da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 4421da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 4431da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 4441da177e4SLinus Torvalds x->props.mode = p->mode; 44533fce60dSFan Du x->props.replay_window = min_t(unsigned int, p->replay_window, 44633fce60dSFan Du sizeof(x->replay.bitmap) * 8); 4471da177e4SLinus Torvalds x->props.reqid = p->reqid; 4481da177e4SLinus Torvalds x->props.family = p->family; 44954489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 4501da177e4SLinus Torvalds x->props.flags = p->flags; 451196b0036SHerbert Xu 452ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 453196b0036SHerbert Xu x->sel.family = p->family; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 456d51d081dSJamal Hadi Salim /* 457d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 458d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 459d51d081dSJamal Hadi Salim * 460d51d081dSJamal Hadi Salim */ 461e3ac104dSMathias Krause static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs, 462e3ac104dSMathias Krause int update_esn) 463d51d081dSJamal Hadi Salim { 4645424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 465e3ac104dSMathias Krause struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL; 4665424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 4675424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 4685424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 469d51d081dSJamal Hadi Salim 470d8647b79SSteffen Klassert if (re) { 471d8647b79SSteffen Klassert struct xfrm_replay_state_esn *replay_esn; 472d8647b79SSteffen Klassert replay_esn = nla_data(re); 473d8647b79SSteffen Klassert memcpy(x->replay_esn, replay_esn, 474d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 475d8647b79SSteffen Klassert memcpy(x->preplay_esn, replay_esn, 476d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 477d8647b79SSteffen Klassert } 478d8647b79SSteffen Klassert 479d51d081dSJamal Hadi Salim if (rp) { 480d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 4815424f32eSThomas Graf replay = nla_data(rp); 482d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 483d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 484d51d081dSJamal Hadi Salim } 485d51d081dSJamal Hadi Salim 486d51d081dSJamal Hadi Salim if (lt) { 487d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 4885424f32eSThomas Graf ltime = nla_data(lt); 489d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 490d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 491d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 492d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 493d51d081dSJamal Hadi Salim } 494d51d081dSJamal Hadi Salim 495cf5cb79fSThomas Graf if (et) 4965424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 497d51d081dSJamal Hadi Salim 498cf5cb79fSThomas Graf if (rt) 4995424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 500d51d081dSJamal Hadi Salim } 501d51d081dSJamal Hadi Salim 502fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 503fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 5045424f32eSThomas Graf struct nlattr **attrs, 5051da177e4SLinus Torvalds int *errp) 5061da177e4SLinus Torvalds { 507fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 5081da177e4SLinus Torvalds int err = -ENOMEM; 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds if (!x) 5111da177e4SLinus Torvalds goto error_no_put; 5121da177e4SLinus Torvalds 5131da177e4SLinus Torvalds copy_from_user_state(x, p); 5141da177e4SLinus Torvalds 515a947b0a9SNicolas Dichtel if (attrs[XFRMA_SA_EXTRA_FLAGS]) 516a947b0a9SNicolas Dichtel x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]); 517a947b0a9SNicolas Dichtel 5181a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 5191a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 5201a6509d9SHerbert Xu goto error; 5214447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 5224447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 5234447bb33SMartin Willi goto error; 5244447bb33SMartin Willi if (!x->props.aalgo) { 5254447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 52635a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 5271da177e4SLinus Torvalds goto error; 5284447bb33SMartin Willi } 5291da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 5301da177e4SLinus Torvalds xfrm_ealg_get_byname, 53135a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 5321da177e4SLinus Torvalds goto error; 5331da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 5341da177e4SLinus Torvalds xfrm_calg_get_byname, 53535a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 5361da177e4SLinus Torvalds goto error; 537fd21150aSThomas Graf 538fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 539fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 540fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 541fd21150aSThomas Graf if (x->encap == NULL) 5421da177e4SLinus Torvalds goto error; 543fd21150aSThomas Graf } 544fd21150aSThomas Graf 54535d2856bSMartin Willi if (attrs[XFRMA_TFCPAD]) 54635d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); 54735d2856bSMartin Willi 548fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 549fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 550fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 551fd21150aSThomas Graf if (x->coaddr == NULL) 552060f02a3SNoriaki TAKAMIYA goto error; 553fd21150aSThomas Graf } 554fd21150aSThomas Graf 5556f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark); 5566f26b61eSJamal Hadi Salim 557a454f0ccSWei Yongjun err = __xfrm_init_state(x, false); 5581da177e4SLinus Torvalds if (err) 5591da177e4SLinus Torvalds goto error; 5601da177e4SLinus Torvalds 561fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 562fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 563df71837dSTrent Jaeger goto error; 564df71837dSTrent Jaeger 565d8647b79SSteffen Klassert if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, 566d8647b79SSteffen Klassert attrs[XFRMA_REPLAY_ESN_VAL]))) 567d8647b79SSteffen Klassert goto error; 568d8647b79SSteffen Klassert 5691da177e4SLinus Torvalds x->km.seq = p->seq; 570b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 571d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 572b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 573d51d081dSJamal Hadi Salim 5749fdc4883SSteffen Klassert if ((err = xfrm_init_replay(x))) 5759fdc4883SSteffen Klassert goto error; 576d51d081dSJamal Hadi Salim 5779fdc4883SSteffen Klassert /* override default values from above */ 578e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 0); 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds return x; 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds error: 5831da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 5841da177e4SLinus Torvalds xfrm_state_put(x); 5851da177e4SLinus Torvalds error_no_put: 5861da177e4SLinus Torvalds *errp = err; 5871da177e4SLinus Torvalds return NULL; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds 59022e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 5915424f32eSThomas Graf struct nlattr **attrs) 5921da177e4SLinus Torvalds { 593fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 5947b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 5951da177e4SLinus Torvalds struct xfrm_state *x; 5961da177e4SLinus Torvalds int err; 59726b15dadSJamal Hadi Salim struct km_event c; 5981da177e4SLinus Torvalds 59935a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 6001da177e4SLinus Torvalds if (err) 6011da177e4SLinus Torvalds return err; 6021da177e4SLinus Torvalds 603fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 6041da177e4SLinus Torvalds if (!x) 6051da177e4SLinus Torvalds return err; 6061da177e4SLinus Torvalds 60726b15dadSJamal Hadi Salim xfrm_state_hold(x); 6081da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 6091da177e4SLinus Torvalds err = xfrm_state_add(x); 6101da177e4SLinus Torvalds else 6111da177e4SLinus Torvalds err = xfrm_state_update(x); 6121da177e4SLinus Torvalds 6132e71029eSTetsuo Handa xfrm_audit_state_add(x, err ? 0 : 1, true); 614161a09e7SJoy Latten 6151da177e4SLinus Torvalds if (err < 0) { 6161da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 61721380b81SHerbert Xu __xfrm_state_put(x); 6187d6dfe1fSPatrick McHardy goto out; 6191da177e4SLinus Torvalds } 6201da177e4SLinus Torvalds 62126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 62215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 623f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 62426b15dadSJamal Hadi Salim 62526b15dadSJamal Hadi Salim km_state_notify(x, &c); 6267d6dfe1fSPatrick McHardy out: 62726b15dadSJamal Hadi Salim xfrm_state_put(x); 6281da177e4SLinus Torvalds return err; 6291da177e4SLinus Torvalds } 6301da177e4SLinus Torvalds 631fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 632fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 6335424f32eSThomas Graf struct nlattr **attrs, 634eb2971b6SMasahide NAKAMURA int *errp) 635eb2971b6SMasahide NAKAMURA { 636eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 6376f26b61eSJamal Hadi Salim struct xfrm_mark m; 638eb2971b6SMasahide NAKAMURA int err; 6396f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 640eb2971b6SMasahide NAKAMURA 641eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 642eb2971b6SMasahide NAKAMURA err = -ESRCH; 6436f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family); 644eb2971b6SMasahide NAKAMURA } else { 645eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 646eb2971b6SMasahide NAKAMURA 64735a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 648eb2971b6SMasahide NAKAMURA if (!saddr) { 649eb2971b6SMasahide NAKAMURA err = -EINVAL; 650eb2971b6SMasahide NAKAMURA goto out; 651eb2971b6SMasahide NAKAMURA } 652eb2971b6SMasahide NAKAMURA 6539abbffeeSMasahide NAKAMURA err = -ESRCH; 6546f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark, 6556f26b61eSJamal Hadi Salim &p->daddr, saddr, 656221df1edSAlexey Dobriyan p->proto, p->family); 657eb2971b6SMasahide NAKAMURA } 658eb2971b6SMasahide NAKAMURA 659eb2971b6SMasahide NAKAMURA out: 660eb2971b6SMasahide NAKAMURA if (!x && errp) 661eb2971b6SMasahide NAKAMURA *errp = err; 662eb2971b6SMasahide NAKAMURA return x; 663eb2971b6SMasahide NAKAMURA } 664eb2971b6SMasahide NAKAMURA 66522e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 6665424f32eSThomas Graf struct nlattr **attrs) 6671da177e4SLinus Torvalds { 668fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6691da177e4SLinus Torvalds struct xfrm_state *x; 670eb2971b6SMasahide NAKAMURA int err = -ESRCH; 67126b15dadSJamal Hadi Salim struct km_event c; 6727b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 6731da177e4SLinus Torvalds 674fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 6751da177e4SLinus Torvalds if (x == NULL) 676eb2971b6SMasahide NAKAMURA return err; 6771da177e4SLinus Torvalds 6786f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 679c8c05a8eSCatherine Zhang goto out; 680c8c05a8eSCatherine Zhang 6811da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 682c8c05a8eSCatherine Zhang err = -EPERM; 683c8c05a8eSCatherine Zhang goto out; 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds 68626b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 687161a09e7SJoy Latten 688c8c05a8eSCatherine Zhang if (err < 0) 689c8c05a8eSCatherine Zhang goto out; 69026b15dadSJamal Hadi Salim 69126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 69215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 693f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 69426b15dadSJamal Hadi Salim km_state_notify(x, &c); 6951da177e4SLinus Torvalds 696c8c05a8eSCatherine Zhang out: 6972e71029eSTetsuo Handa xfrm_audit_state_delete(x, err ? 0 : 1, true); 698c8c05a8eSCatherine Zhang xfrm_state_put(x); 69926b15dadSJamal Hadi Salim return err; 7001da177e4SLinus Torvalds } 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 7031da177e4SLinus Torvalds { 704f778a636SMathias Krause memset(p, 0, sizeof(*p)); 7051da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 7061da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 7071da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 7081da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 7091da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 71054489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 7111da177e4SLinus Torvalds p->mode = x->props.mode; 7121da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 7131da177e4SLinus Torvalds p->reqid = x->props.reqid; 7141da177e4SLinus Torvalds p->family = x->props.family; 7151da177e4SLinus Torvalds p->flags = x->props.flags; 7161da177e4SLinus Torvalds p->seq = x->km.seq; 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds 7191da177e4SLinus Torvalds struct xfrm_dump_info { 7201da177e4SLinus Torvalds struct sk_buff *in_skb; 7211da177e4SLinus Torvalds struct sk_buff *out_skb; 7221da177e4SLinus Torvalds u32 nlmsg_seq; 7231da177e4SLinus Torvalds u16 nlmsg_flags; 7241da177e4SLinus Torvalds }; 7251da177e4SLinus Torvalds 726c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 727c0144beaSThomas Graf { 728c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 729c0144beaSThomas Graf struct nlattr *attr; 73068325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 731c0144beaSThomas Graf 732c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 733c0144beaSThomas Graf if (attr == NULL) 734c0144beaSThomas Graf return -EMSGSIZE; 735c0144beaSThomas Graf 736c0144beaSThomas Graf uctx = nla_data(attr); 737c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 738c0144beaSThomas Graf uctx->len = ctx_size; 739c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 740c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 741c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 742c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 743c0144beaSThomas Graf 744c0144beaSThomas Graf return 0; 745c0144beaSThomas Graf } 746c0144beaSThomas Graf 7474447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 7484447bb33SMartin Willi { 7494447bb33SMartin Willi struct xfrm_algo *algo; 7504447bb33SMartin Willi struct nlattr *nla; 7514447bb33SMartin Willi 7524447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 7534447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 7544447bb33SMartin Willi if (!nla) 7554447bb33SMartin Willi return -EMSGSIZE; 7564447bb33SMartin Willi 7574447bb33SMartin Willi algo = nla_data(nla); 7584c87308bSMathias Krause strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name)); 7594447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 7604447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 7614447bb33SMartin Willi 7624447bb33SMartin Willi return 0; 7634447bb33SMartin Willi } 7644447bb33SMartin Willi 76568325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 76668325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 76768325d3bSHerbert Xu struct xfrm_usersa_info *p, 76868325d3bSHerbert Xu struct sk_buff *skb) 7691da177e4SLinus Torvalds { 7701d1e34ddSDavid S. Miller int ret = 0; 7711d1e34ddSDavid S. Miller 7721da177e4SLinus Torvalds copy_to_user_state(x, p); 7731da177e4SLinus Torvalds 774a947b0a9SNicolas Dichtel if (x->props.extra_flags) { 775a947b0a9SNicolas Dichtel ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS, 776a947b0a9SNicolas Dichtel x->props.extra_flags); 777a947b0a9SNicolas Dichtel if (ret) 778a947b0a9SNicolas Dichtel goto out; 779a947b0a9SNicolas Dichtel } 780a947b0a9SNicolas Dichtel 7811d1e34ddSDavid S. Miller if (x->coaddr) { 7821d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 7831d1e34ddSDavid S. Miller if (ret) 7841d1e34ddSDavid S. Miller goto out; 7851d1e34ddSDavid S. Miller } 7861d1e34ddSDavid S. Miller if (x->lastused) { 7871d1e34ddSDavid S. Miller ret = nla_put_u64(skb, XFRMA_LASTUSED, x->lastused); 7881d1e34ddSDavid S. Miller if (ret) 7891d1e34ddSDavid S. Miller goto out; 7901d1e34ddSDavid S. Miller } 7911d1e34ddSDavid S. Miller if (x->aead) { 7921d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 7931d1e34ddSDavid S. Miller if (ret) 7941d1e34ddSDavid S. Miller goto out; 7951d1e34ddSDavid S. Miller } 7961d1e34ddSDavid S. Miller if (x->aalg) { 7971d1e34ddSDavid S. Miller ret = copy_to_user_auth(x->aalg, skb); 7981d1e34ddSDavid S. Miller if (!ret) 7991d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC, 8001d1e34ddSDavid S. Miller xfrm_alg_auth_len(x->aalg), x->aalg); 8011d1e34ddSDavid S. Miller if (ret) 8021d1e34ddSDavid S. Miller goto out; 8031d1e34ddSDavid S. Miller } 8041d1e34ddSDavid S. Miller if (x->ealg) { 8051d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 8061d1e34ddSDavid S. Miller if (ret) 8071d1e34ddSDavid S. Miller goto out; 8081d1e34ddSDavid S. Miller } 8091d1e34ddSDavid S. Miller if (x->calg) { 8101d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 8111d1e34ddSDavid S. Miller if (ret) 8121d1e34ddSDavid S. Miller goto out; 8131d1e34ddSDavid S. Miller } 8141d1e34ddSDavid S. Miller if (x->encap) { 8151d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 8161d1e34ddSDavid S. Miller if (ret) 8171d1e34ddSDavid S. Miller goto out; 8181d1e34ddSDavid S. Miller } 8191d1e34ddSDavid S. Miller if (x->tfcpad) { 8201d1e34ddSDavid S. Miller ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad); 8211d1e34ddSDavid S. Miller if (ret) 8221d1e34ddSDavid S. Miller goto out; 8231d1e34ddSDavid S. Miller } 8241d1e34ddSDavid S. Miller ret = xfrm_mark_put(skb, &x->mark); 8251d1e34ddSDavid S. Miller if (ret) 8261d1e34ddSDavid S. Miller goto out; 827f293a5e3Sdingzhi if (x->replay_esn) 8281d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 829d0fde795SDavid S. Miller xfrm_replay_state_esn_len(x->replay_esn), 8301d1e34ddSDavid S. Miller x->replay_esn); 831f293a5e3Sdingzhi else 832f293a5e3Sdingzhi ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), 833f293a5e3Sdingzhi &x->replay); 8341d1e34ddSDavid S. Miller if (ret) 8351d1e34ddSDavid S. Miller goto out; 8361d1e34ddSDavid S. Miller if (x->security) 8371d1e34ddSDavid S. Miller ret = copy_sec_ctx(x->security, skb); 8381d1e34ddSDavid S. Miller out: 8391d1e34ddSDavid S. Miller return ret; 84068325d3bSHerbert Xu } 84168325d3bSHerbert Xu 84268325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 84368325d3bSHerbert Xu { 84468325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 84568325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 84668325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 84768325d3bSHerbert Xu struct xfrm_usersa_info *p; 84868325d3bSHerbert Xu struct nlmsghdr *nlh; 84968325d3bSHerbert Xu int err; 85068325d3bSHerbert Xu 85115e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, 85268325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 85368325d3bSHerbert Xu if (nlh == NULL) 85468325d3bSHerbert Xu return -EMSGSIZE; 85568325d3bSHerbert Xu 85668325d3bSHerbert Xu p = nlmsg_data(nlh); 85768325d3bSHerbert Xu 85868325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 8591d1e34ddSDavid S. Miller if (err) { 8609825069dSThomas Graf nlmsg_cancel(skb, nlh); 86168325d3bSHerbert Xu return err; 8621da177e4SLinus Torvalds } 8631d1e34ddSDavid S. Miller nlmsg_end(skb, nlh); 8641d1e34ddSDavid S. Miller return 0; 8651d1e34ddSDavid S. Miller } 8661da177e4SLinus Torvalds 8674c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 8684c563f76STimo Teras { 8694c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 870283bc9f3SFan Du struct sock *sk = cb->skb->sk; 871283bc9f3SFan Du struct net *net = sock_net(sk); 872283bc9f3SFan Du 873283bc9f3SFan Du xfrm_state_walk_done(walk, net); 8744c563f76STimo Teras return 0; 8754c563f76STimo Teras } 8764c563f76STimo Teras 877d3623099SNicolas Dichtel static const struct nla_policy xfrma_policy[XFRMA_MAX+1]; 8781da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 8791da177e4SLinus Torvalds { 880fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 8814c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8821da177e4SLinus Torvalds struct xfrm_dump_info info; 8831da177e4SLinus Torvalds 8844c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 8854c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 8864c563f76STimo Teras 8871da177e4SLinus Torvalds info.in_skb = cb->skb; 8881da177e4SLinus Torvalds info.out_skb = skb; 8891da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 8901da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 8914c563f76STimo Teras 8924c563f76STimo Teras if (!cb->args[0]) { 893d3623099SNicolas Dichtel struct nlattr *attrs[XFRMA_MAX+1]; 894870a2df4SNicolas Dichtel struct xfrm_address_filter *filter = NULL; 895d3623099SNicolas Dichtel u8 proto = 0; 896d3623099SNicolas Dichtel int err; 897d3623099SNicolas Dichtel 8984c563f76STimo Teras cb->args[0] = 1; 899d3623099SNicolas Dichtel 900d3623099SNicolas Dichtel err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX, 901d3623099SNicolas Dichtel xfrma_policy); 902d3623099SNicolas Dichtel if (err < 0) 903d3623099SNicolas Dichtel return err; 904d3623099SNicolas Dichtel 905870a2df4SNicolas Dichtel if (attrs[XFRMA_ADDRESS_FILTER]) { 906d3623099SNicolas Dichtel filter = kmalloc(sizeof(*filter), GFP_KERNEL); 907d3623099SNicolas Dichtel if (filter == NULL) 908d3623099SNicolas Dichtel return -ENOMEM; 909d3623099SNicolas Dichtel 910870a2df4SNicolas Dichtel memcpy(filter, nla_data(attrs[XFRMA_ADDRESS_FILTER]), 911d3623099SNicolas Dichtel sizeof(*filter)); 912d3623099SNicolas Dichtel } 913d3623099SNicolas Dichtel 914d3623099SNicolas Dichtel if (attrs[XFRMA_PROTO]) 915d3623099SNicolas Dichtel proto = nla_get_u8(attrs[XFRMA_PROTO]); 916d3623099SNicolas Dichtel 917d3623099SNicolas Dichtel xfrm_state_walk_init(walk, proto, filter); 9184c563f76STimo Teras } 9194c563f76STimo Teras 920fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds return skb->len; 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 9261da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 9271da177e4SLinus Torvalds { 9281da177e4SLinus Torvalds struct xfrm_dump_info info; 9291da177e4SLinus Torvalds struct sk_buff *skb; 930864745d2SMathias Krause int err; 9311da177e4SLinus Torvalds 9327deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 9331da177e4SLinus Torvalds if (!skb) 9341da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds info.in_skb = in_skb; 9371da177e4SLinus Torvalds info.out_skb = skb; 9381da177e4SLinus Torvalds info.nlmsg_seq = seq; 9391da177e4SLinus Torvalds info.nlmsg_flags = 0; 9401da177e4SLinus Torvalds 941864745d2SMathias Krause err = dump_one_state(x, 0, &info); 942864745d2SMathias Krause if (err) { 9431da177e4SLinus Torvalds kfree_skb(skb); 944864745d2SMathias Krause return ERR_PTR(err); 9451da177e4SLinus Torvalds } 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds return skb; 9481da177e4SLinus Torvalds } 9491da177e4SLinus Torvalds 95021ee543eSMichal Kubecek /* A wrapper for nlmsg_multicast() checking that nlsk is still available. 95121ee543eSMichal Kubecek * Must be called with RCU read lock. 95221ee543eSMichal Kubecek */ 95321ee543eSMichal Kubecek static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb, 95421ee543eSMichal Kubecek u32 pid, unsigned int group) 95521ee543eSMichal Kubecek { 95621ee543eSMichal Kubecek struct sock *nlsk = rcu_dereference(net->xfrm.nlsk); 95721ee543eSMichal Kubecek 95821ee543eSMichal Kubecek if (nlsk) 95921ee543eSMichal Kubecek return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC); 96021ee543eSMichal Kubecek else 96121ee543eSMichal Kubecek return -1; 96221ee543eSMichal Kubecek } 96321ee543eSMichal Kubecek 9647deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 9657deb2264SThomas Graf { 9667deb2264SThomas Graf return NLMSG_ALIGN(4) 9677deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 968880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhinfo)) 969880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhthresh)) 970880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhthresh)); 9717deb2264SThomas Graf } 9727deb2264SThomas Graf 973e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 97415e47304SEric W. Biederman u32 portid, u32 seq, u32 flags) 975ecfd6b18SJamal Hadi Salim { 9765a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 9775a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 9785a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 979880a6fabSChristophe Gouault struct xfrmu_spdhthresh spt4, spt6; 980ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 9811d1e34ddSDavid S. Miller int err; 982ecfd6b18SJamal Hadi Salim u32 *f; 983880a6fabSChristophe Gouault unsigned lseq; 984ecfd6b18SJamal Hadi Salim 98515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 98625985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 987ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 988ecfd6b18SJamal Hadi Salim 989ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 990ecfd6b18SJamal Hadi Salim *f = flags; 991e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 9925a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 9935a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 9945a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 9955a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 9965a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 9975a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 9985a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 9995a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 1000ecfd6b18SJamal Hadi Salim 1001880a6fabSChristophe Gouault do { 1002880a6fabSChristophe Gouault lseq = read_seqbegin(&net->xfrm.policy_hthresh.lock); 1003880a6fabSChristophe Gouault 1004880a6fabSChristophe Gouault spt4.lbits = net->xfrm.policy_hthresh.lbits4; 1005880a6fabSChristophe Gouault spt4.rbits = net->xfrm.policy_hthresh.rbits4; 1006880a6fabSChristophe Gouault spt6.lbits = net->xfrm.policy_hthresh.lbits6; 1007880a6fabSChristophe Gouault spt6.rbits = net->xfrm.policy_hthresh.rbits6; 1008880a6fabSChristophe Gouault } while (read_seqretry(&net->xfrm.policy_hthresh.lock, lseq)); 1009880a6fabSChristophe Gouault 10101d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 10111d1e34ddSDavid S. Miller if (!err) 10121d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 1013880a6fabSChristophe Gouault if (!err) 1014880a6fabSChristophe Gouault err = nla_put(skb, XFRMA_SPD_IPV4_HTHRESH, sizeof(spt4), &spt4); 1015880a6fabSChristophe Gouault if (!err) 1016880a6fabSChristophe Gouault err = nla_put(skb, XFRMA_SPD_IPV6_HTHRESH, sizeof(spt6), &spt6); 10171d1e34ddSDavid S. Miller if (err) { 10181d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 10191d1e34ddSDavid S. Miller return err; 10201d1e34ddSDavid S. Miller } 1021ecfd6b18SJamal Hadi Salim 1022*053c095aSJohannes Berg nlmsg_end(skb, nlh); 1023*053c095aSJohannes Berg return 0; 1024ecfd6b18SJamal Hadi Salim } 1025ecfd6b18SJamal Hadi Salim 1026880a6fabSChristophe Gouault static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 1027880a6fabSChristophe Gouault struct nlattr **attrs) 1028880a6fabSChristophe Gouault { 1029880a6fabSChristophe Gouault struct net *net = sock_net(skb->sk); 1030880a6fabSChristophe Gouault struct xfrmu_spdhthresh *thresh4 = NULL; 1031880a6fabSChristophe Gouault struct xfrmu_spdhthresh *thresh6 = NULL; 1032880a6fabSChristophe Gouault 1033880a6fabSChristophe Gouault /* selector prefixlen thresholds to hash policies */ 1034880a6fabSChristophe Gouault if (attrs[XFRMA_SPD_IPV4_HTHRESH]) { 1035880a6fabSChristophe Gouault struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH]; 1036880a6fabSChristophe Gouault 1037880a6fabSChristophe Gouault if (nla_len(rta) < sizeof(*thresh4)) 1038880a6fabSChristophe Gouault return -EINVAL; 1039880a6fabSChristophe Gouault thresh4 = nla_data(rta); 1040880a6fabSChristophe Gouault if (thresh4->lbits > 32 || thresh4->rbits > 32) 1041880a6fabSChristophe Gouault return -EINVAL; 1042880a6fabSChristophe Gouault } 1043880a6fabSChristophe Gouault if (attrs[XFRMA_SPD_IPV6_HTHRESH]) { 1044880a6fabSChristophe Gouault struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH]; 1045880a6fabSChristophe Gouault 1046880a6fabSChristophe Gouault if (nla_len(rta) < sizeof(*thresh6)) 1047880a6fabSChristophe Gouault return -EINVAL; 1048880a6fabSChristophe Gouault thresh6 = nla_data(rta); 1049880a6fabSChristophe Gouault if (thresh6->lbits > 128 || thresh6->rbits > 128) 1050880a6fabSChristophe Gouault return -EINVAL; 1051880a6fabSChristophe Gouault } 1052880a6fabSChristophe Gouault 1053880a6fabSChristophe Gouault if (thresh4 || thresh6) { 1054880a6fabSChristophe Gouault write_seqlock(&net->xfrm.policy_hthresh.lock); 1055880a6fabSChristophe Gouault if (thresh4) { 1056880a6fabSChristophe Gouault net->xfrm.policy_hthresh.lbits4 = thresh4->lbits; 1057880a6fabSChristophe Gouault net->xfrm.policy_hthresh.rbits4 = thresh4->rbits; 1058880a6fabSChristophe Gouault } 1059880a6fabSChristophe Gouault if (thresh6) { 1060880a6fabSChristophe Gouault net->xfrm.policy_hthresh.lbits6 = thresh6->lbits; 1061880a6fabSChristophe Gouault net->xfrm.policy_hthresh.rbits6 = thresh6->rbits; 1062880a6fabSChristophe Gouault } 1063880a6fabSChristophe Gouault write_sequnlock(&net->xfrm.policy_hthresh.lock); 1064880a6fabSChristophe Gouault 1065880a6fabSChristophe Gouault xfrm_policy_hash_rebuild(net); 1066880a6fabSChristophe Gouault } 1067880a6fabSChristophe Gouault 1068880a6fabSChristophe Gouault return 0; 1069880a6fabSChristophe Gouault } 1070880a6fabSChristophe Gouault 1071ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 10725424f32eSThomas Graf struct nlattr **attrs) 1073ecfd6b18SJamal Hadi Salim { 1074a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1075ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 10767b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 107715e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid; 1078ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 1079ecfd6b18SJamal Hadi Salim 10807deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 1081ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 1082ecfd6b18SJamal Hadi Salim return -ENOMEM; 1083ecfd6b18SJamal Hadi Salim 108415e47304SEric W. Biederman if (build_spdinfo(r_skb, net, sportid, seq, *flags) < 0) 1085ecfd6b18SJamal Hadi Salim BUG(); 1086ecfd6b18SJamal Hadi Salim 108715e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); 1088ecfd6b18SJamal Hadi Salim } 1089ecfd6b18SJamal Hadi Salim 10907deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 10917deb2264SThomas Graf { 10927deb2264SThomas Graf return NLMSG_ALIGN(4) 10937deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 10947deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 10957deb2264SThomas Graf } 10967deb2264SThomas Graf 1097e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 109815e47304SEric W. Biederman u32 portid, u32 seq, u32 flags) 109928d8909bSJamal Hadi Salim { 1100af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 1101af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 110228d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 11031d1e34ddSDavid S. Miller int err; 110428d8909bSJamal Hadi Salim u32 *f; 110528d8909bSJamal Hadi Salim 110615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 110725985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 110828d8909bSJamal Hadi Salim return -EMSGSIZE; 110928d8909bSJamal Hadi Salim 111028d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 111128d8909bSJamal Hadi Salim *f = flags; 1112e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 111328d8909bSJamal Hadi Salim 1114af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 1115af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 1116af11e316SJamal Hadi Salim 11171d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt); 11181d1e34ddSDavid S. Miller if (!err) 11191d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 11201d1e34ddSDavid S. Miller if (err) { 11211d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 11221d1e34ddSDavid S. Miller return err; 11231d1e34ddSDavid S. Miller } 112428d8909bSJamal Hadi Salim 1125*053c095aSJohannes Berg nlmsg_end(skb, nlh); 1126*053c095aSJohannes Berg return 0; 112728d8909bSJamal Hadi Salim } 112828d8909bSJamal Hadi Salim 112928d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 11305424f32eSThomas Graf struct nlattr **attrs) 113128d8909bSJamal Hadi Salim { 1132a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 113328d8909bSJamal Hadi Salim struct sk_buff *r_skb; 11347b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 113515e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid; 113628d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 113728d8909bSJamal Hadi Salim 11387deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 113928d8909bSJamal Hadi Salim if (r_skb == NULL) 114028d8909bSJamal Hadi Salim return -ENOMEM; 114128d8909bSJamal Hadi Salim 114215e47304SEric W. Biederman if (build_sadinfo(r_skb, net, sportid, seq, *flags) < 0) 114328d8909bSJamal Hadi Salim BUG(); 114428d8909bSJamal Hadi Salim 114515e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); 114628d8909bSJamal Hadi Salim } 114728d8909bSJamal Hadi Salim 114822e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 11495424f32eSThomas Graf struct nlattr **attrs) 11501da177e4SLinus Torvalds { 1151fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 11527b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 11531da177e4SLinus Torvalds struct xfrm_state *x; 11541da177e4SLinus Torvalds struct sk_buff *resp_skb; 1155eb2971b6SMasahide NAKAMURA int err = -ESRCH; 11561da177e4SLinus Torvalds 1157fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 11581da177e4SLinus Torvalds if (x == NULL) 11591da177e4SLinus Torvalds goto out_noput; 11601da177e4SLinus Torvalds 11611da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 11621da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 11631da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 11641da177e4SLinus Torvalds } else { 116515e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); 11661da177e4SLinus Torvalds } 11671da177e4SLinus Torvalds xfrm_state_put(x); 11681da177e4SLinus Torvalds out_noput: 11691da177e4SLinus Torvalds return err; 11701da177e4SLinus Torvalds } 11711da177e4SLinus Torvalds 117222e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 11735424f32eSThomas Graf struct nlattr **attrs) 11741da177e4SLinus Torvalds { 1175fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 11761da177e4SLinus Torvalds struct xfrm_state *x; 11771da177e4SLinus Torvalds struct xfrm_userspi_info *p; 11781da177e4SLinus Torvalds struct sk_buff *resp_skb; 11791da177e4SLinus Torvalds xfrm_address_t *daddr; 11801da177e4SLinus Torvalds int family; 11811da177e4SLinus Torvalds int err; 11826f26b61eSJamal Hadi Salim u32 mark; 11836f26b61eSJamal Hadi Salim struct xfrm_mark m; 11841da177e4SLinus Torvalds 11857b67c857SThomas Graf p = nlmsg_data(nlh); 1186776e9dd9SFan Du err = verify_spi_info(p->info.id.proto, p->min, p->max); 11871da177e4SLinus Torvalds if (err) 11881da177e4SLinus Torvalds goto out_noput; 11891da177e4SLinus Torvalds 11901da177e4SLinus Torvalds family = p->info.family; 11911da177e4SLinus Torvalds daddr = &p->info.id.daddr; 11921da177e4SLinus Torvalds 11931da177e4SLinus Torvalds x = NULL; 11946f26b61eSJamal Hadi Salim 11956f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 11961da177e4SLinus Torvalds if (p->info.seq) { 11976f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq); 119870e94e66SYOSHIFUJI Hideaki / 吉藤英明 if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) { 11991da177e4SLinus Torvalds xfrm_state_put(x); 12001da177e4SLinus Torvalds x = NULL; 12011da177e4SLinus Torvalds } 12021da177e4SLinus Torvalds } 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds if (!x) 12056f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid, 12061da177e4SLinus Torvalds p->info.id.proto, daddr, 12071da177e4SLinus Torvalds &p->info.saddr, 1, 12081da177e4SLinus Torvalds family); 12091da177e4SLinus Torvalds err = -ENOENT; 12101da177e4SLinus Torvalds if (x == NULL) 12111da177e4SLinus Torvalds goto out_noput; 12121da177e4SLinus Torvalds 1213658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 1214658b219eSHerbert Xu if (err) 1215658b219eSHerbert Xu goto out; 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 12181da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 12191da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 12201da177e4SLinus Torvalds goto out; 12211da177e4SLinus Torvalds } 12221da177e4SLinus Torvalds 122315e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); 12241da177e4SLinus Torvalds 12251da177e4SLinus Torvalds out: 12261da177e4SLinus Torvalds xfrm_state_put(x); 12271da177e4SLinus Torvalds out_noput: 12281da177e4SLinus Torvalds return err; 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds 1231b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 12321da177e4SLinus Torvalds { 12331da177e4SLinus Torvalds switch (dir) { 12341da177e4SLinus Torvalds case XFRM_POLICY_IN: 12351da177e4SLinus Torvalds case XFRM_POLICY_OUT: 12361da177e4SLinus Torvalds case XFRM_POLICY_FWD: 12371da177e4SLinus Torvalds break; 12381da177e4SLinus Torvalds 12391da177e4SLinus Torvalds default: 12401da177e4SLinus Torvalds return -EINVAL; 12413ff50b79SStephen Hemminger } 12421da177e4SLinus Torvalds 12431da177e4SLinus Torvalds return 0; 12441da177e4SLinus Torvalds } 12451da177e4SLinus Torvalds 1246b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1247f7b6983fSMasahide NAKAMURA { 1248f7b6983fSMasahide NAKAMURA switch (type) { 1249f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1250f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1251f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1252f7b6983fSMasahide NAKAMURA #endif 1253f7b6983fSMasahide NAKAMURA break; 1254f7b6983fSMasahide NAKAMURA 1255f7b6983fSMasahide NAKAMURA default: 1256f7b6983fSMasahide NAKAMURA return -EINVAL; 12573ff50b79SStephen Hemminger } 1258f7b6983fSMasahide NAKAMURA 1259f7b6983fSMasahide NAKAMURA return 0; 1260f7b6983fSMasahide NAKAMURA } 1261f7b6983fSMasahide NAKAMURA 12621da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 12631da177e4SLinus Torvalds { 1264e682adf0SFan Du int ret; 1265e682adf0SFan Du 12661da177e4SLinus Torvalds switch (p->share) { 12671da177e4SLinus Torvalds case XFRM_SHARE_ANY: 12681da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 12691da177e4SLinus Torvalds case XFRM_SHARE_USER: 12701da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 12711da177e4SLinus Torvalds break; 12721da177e4SLinus Torvalds 12731da177e4SLinus Torvalds default: 12741da177e4SLinus Torvalds return -EINVAL; 12753ff50b79SStephen Hemminger } 12761da177e4SLinus Torvalds 12771da177e4SLinus Torvalds switch (p->action) { 12781da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 12791da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 12801da177e4SLinus Torvalds break; 12811da177e4SLinus Torvalds 12821da177e4SLinus Torvalds default: 12831da177e4SLinus Torvalds return -EINVAL; 12843ff50b79SStephen Hemminger } 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds switch (p->sel.family) { 12871da177e4SLinus Torvalds case AF_INET: 12881da177e4SLinus Torvalds break; 12891da177e4SLinus Torvalds 12901da177e4SLinus Torvalds case AF_INET6: 1291dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 12921da177e4SLinus Torvalds break; 12931da177e4SLinus Torvalds #else 12941da177e4SLinus Torvalds return -EAFNOSUPPORT; 12951da177e4SLinus Torvalds #endif 12961da177e4SLinus Torvalds 12971da177e4SLinus Torvalds default: 12981da177e4SLinus Torvalds return -EINVAL; 12993ff50b79SStephen Hemminger } 13001da177e4SLinus Torvalds 1301e682adf0SFan Du ret = verify_policy_dir(p->dir); 1302e682adf0SFan Du if (ret) 1303e682adf0SFan Du return ret; 1304e682adf0SFan Du if (p->index && ((p->index & XFRM_POLICY_MAX) != p->dir)) 1305e682adf0SFan Du return -EINVAL; 1306e682adf0SFan Du 1307e682adf0SFan Du return 0; 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds 13105424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1311df71837dSTrent Jaeger { 13125424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1313df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1314df71837dSTrent Jaeger 1315df71837dSTrent Jaeger if (!rt) 1316df71837dSTrent Jaeger return 0; 1317df71837dSTrent Jaeger 13185424f32eSThomas Graf uctx = nla_data(rt); 131952a4c640SNikolay Aleksandrov return security_xfrm_policy_alloc(&pol->security, uctx, GFP_KERNEL); 1320df71837dSTrent Jaeger } 1321df71837dSTrent Jaeger 13221da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 13231da177e4SLinus Torvalds int nr) 13241da177e4SLinus Torvalds { 13251da177e4SLinus Torvalds int i; 13261da177e4SLinus Torvalds 13271da177e4SLinus Torvalds xp->xfrm_nr = nr; 13281da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 13291da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 13301da177e4SLinus Torvalds 13311da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 13321da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 13331da177e4SLinus Torvalds sizeof(xfrm_address_t)); 13341da177e4SLinus Torvalds t->reqid = ut->reqid; 13351da177e4SLinus Torvalds t->mode = ut->mode; 13361da177e4SLinus Torvalds t->share = ut->share; 13371da177e4SLinus Torvalds t->optional = ut->optional; 13381da177e4SLinus Torvalds t->aalgos = ut->aalgos; 13391da177e4SLinus Torvalds t->ealgos = ut->ealgos; 13401da177e4SLinus Torvalds t->calgos = ut->calgos; 1341c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1342c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 13438511d01dSMiika Komu t->encap_family = ut->family; 13441da177e4SLinus Torvalds } 13451da177e4SLinus Torvalds } 13461da177e4SLinus Torvalds 1347b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1348b4ad86bfSDavid S. Miller { 1349b4ad86bfSDavid S. Miller int i; 1350b4ad86bfSDavid S. Miller 1351b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1352b4ad86bfSDavid S. Miller return -EINVAL; 1353b4ad86bfSDavid S. Miller 1354b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1355b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1356b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1357b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1358b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1359b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1360b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1361b4ad86bfSDavid S. Miller */ 1362b4ad86bfSDavid S. Miller if (!ut[i].family) 1363b4ad86bfSDavid S. Miller ut[i].family = family; 1364b4ad86bfSDavid S. Miller 1365b4ad86bfSDavid S. Miller switch (ut[i].family) { 1366b4ad86bfSDavid S. Miller case AF_INET: 1367b4ad86bfSDavid S. Miller break; 1368dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1369b4ad86bfSDavid S. Miller case AF_INET6: 1370b4ad86bfSDavid S. Miller break; 1371b4ad86bfSDavid S. Miller #endif 1372b4ad86bfSDavid S. Miller default: 1373b4ad86bfSDavid S. Miller return -EINVAL; 13743ff50b79SStephen Hemminger } 1375b4ad86bfSDavid S. Miller } 1376b4ad86bfSDavid S. Miller 1377b4ad86bfSDavid S. Miller return 0; 1378b4ad86bfSDavid S. Miller } 1379b4ad86bfSDavid S. Miller 13805424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 13811da177e4SLinus Torvalds { 13825424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds if (!rt) { 13851da177e4SLinus Torvalds pol->xfrm_nr = 0; 13861da177e4SLinus Torvalds } else { 13875424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 13885424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1389b4ad86bfSDavid S. Miller int err; 13901da177e4SLinus Torvalds 1391b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1392b4ad86bfSDavid S. Miller if (err) 1393b4ad86bfSDavid S. Miller return err; 13941da177e4SLinus Torvalds 13955424f32eSThomas Graf copy_templates(pol, utmpl, nr); 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds return 0; 13981da177e4SLinus Torvalds } 13991da177e4SLinus Torvalds 14005424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1401f7b6983fSMasahide NAKAMURA { 14025424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1403f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1404b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1405f7b6983fSMasahide NAKAMURA int err; 1406f7b6983fSMasahide NAKAMURA 1407f7b6983fSMasahide NAKAMURA if (rt) { 14085424f32eSThomas Graf upt = nla_data(rt); 1409f7b6983fSMasahide NAKAMURA type = upt->type; 1410f7b6983fSMasahide NAKAMURA } 1411f7b6983fSMasahide NAKAMURA 1412f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1413f7b6983fSMasahide NAKAMURA if (err) 1414f7b6983fSMasahide NAKAMURA return err; 1415f7b6983fSMasahide NAKAMURA 1416f7b6983fSMasahide NAKAMURA *tp = type; 1417f7b6983fSMasahide NAKAMURA return 0; 1418f7b6983fSMasahide NAKAMURA } 1419f7b6983fSMasahide NAKAMURA 14201da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 14211da177e4SLinus Torvalds { 14221da177e4SLinus Torvalds xp->priority = p->priority; 14231da177e4SLinus Torvalds xp->index = p->index; 14241da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 14251da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 14261da177e4SLinus Torvalds xp->action = p->action; 14271da177e4SLinus Torvalds xp->flags = p->flags; 14281da177e4SLinus Torvalds xp->family = p->sel.family; 14291da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds 14321da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 14331da177e4SLinus Torvalds { 14347b789836SMathias Krause memset(p, 0, sizeof(*p)); 14351da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 14361da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 14371da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 14381da177e4SLinus Torvalds p->priority = xp->priority; 14391da177e4SLinus Torvalds p->index = xp->index; 14401da177e4SLinus Torvalds p->sel.family = xp->family; 14411da177e4SLinus Torvalds p->dir = dir; 14421da177e4SLinus Torvalds p->action = xp->action; 14431da177e4SLinus Torvalds p->flags = xp->flags; 14441da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds 1447fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 14481da177e4SLinus Torvalds { 1449fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 14501da177e4SLinus Torvalds int err; 14511da177e4SLinus Torvalds 14521da177e4SLinus Torvalds if (!xp) { 14531da177e4SLinus Torvalds *errp = -ENOMEM; 14541da177e4SLinus Torvalds return NULL; 14551da177e4SLinus Torvalds } 14561da177e4SLinus Torvalds 14571da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1458df71837dSTrent Jaeger 145935a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1460f7b6983fSMasahide NAKAMURA if (err) 1461f7b6983fSMasahide NAKAMURA goto error; 1462f7b6983fSMasahide NAKAMURA 146335a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 146435a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1465f7b6983fSMasahide NAKAMURA if (err) 1466f7b6983fSMasahide NAKAMURA goto error; 14671da177e4SLinus Torvalds 1468295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark); 1469295fae56SJamal Hadi Salim 14701da177e4SLinus Torvalds return xp; 1471f7b6983fSMasahide NAKAMURA error: 1472f7b6983fSMasahide NAKAMURA *errp = err; 147312a169e7SHerbert Xu xp->walk.dead = 1; 147464c31b3fSWANG Cong xfrm_policy_destroy(xp); 1475f7b6983fSMasahide NAKAMURA return NULL; 14761da177e4SLinus Torvalds } 14771da177e4SLinus Torvalds 147822e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 14795424f32eSThomas Graf struct nlattr **attrs) 14801da177e4SLinus Torvalds { 1481fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 14827b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 14831da177e4SLinus Torvalds struct xfrm_policy *xp; 148426b15dadSJamal Hadi Salim struct km_event c; 14851da177e4SLinus Torvalds int err; 14861da177e4SLinus Torvalds int excl; 14871da177e4SLinus Torvalds 14881da177e4SLinus Torvalds err = verify_newpolicy_info(p); 14891da177e4SLinus Torvalds if (err) 14901da177e4SLinus Torvalds return err; 149135a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1492df71837dSTrent Jaeger if (err) 1493df71837dSTrent Jaeger return err; 14941da177e4SLinus Torvalds 1495fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 14961da177e4SLinus Torvalds if (!xp) 14971da177e4SLinus Torvalds return err; 14981da177e4SLinus Torvalds 149925985edcSLucas De Marchi /* shouldn't excl be based on nlh flags?? 150026b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 150126b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 150226b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 15031da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 15041da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 15052e71029eSTetsuo Handa xfrm_audit_policy_add(xp, err ? 0 : 1, true); 1506161a09e7SJoy Latten 15071da177e4SLinus Torvalds if (err) { 150803e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 15091da177e4SLinus Torvalds kfree(xp); 15101da177e4SLinus Torvalds return err; 15111da177e4SLinus Torvalds } 15121da177e4SLinus Torvalds 1513f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 151426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 151515e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 151626b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 151726b15dadSJamal Hadi Salim 15181da177e4SLinus Torvalds xfrm_pol_put(xp); 15191da177e4SLinus Torvalds 15201da177e4SLinus Torvalds return 0; 15211da177e4SLinus Torvalds } 15221da177e4SLinus Torvalds 15231da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 15241da177e4SLinus Torvalds { 15251da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 15261da177e4SLinus Torvalds int i; 15271da177e4SLinus Torvalds 15281da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 15291da177e4SLinus Torvalds return 0; 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 15321da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 15331da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 15341da177e4SLinus Torvalds 15351f86840fSMathias Krause memset(up, 0, sizeof(*up)); 15361da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 15378511d01dSMiika Komu up->family = kp->encap_family; 15381da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 15391da177e4SLinus Torvalds up->reqid = kp->reqid; 15401da177e4SLinus Torvalds up->mode = kp->mode; 15411da177e4SLinus Torvalds up->share = kp->share; 15421da177e4SLinus Torvalds up->optional = kp->optional; 15431da177e4SLinus Torvalds up->aalgos = kp->aalgos; 15441da177e4SLinus Torvalds up->ealgos = kp->ealgos; 15451da177e4SLinus Torvalds up->calgos = kp->calgos; 15461da177e4SLinus Torvalds } 15471da177e4SLinus Torvalds 1548c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1549c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1550df71837dSTrent Jaeger } 1551df71837dSTrent Jaeger 15520d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 15530d681623SSerge Hallyn { 15540d681623SSerge Hallyn if (x->security) { 15550d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 15560d681623SSerge Hallyn } 15570d681623SSerge Hallyn return 0; 15580d681623SSerge Hallyn } 15590d681623SSerge Hallyn 15600d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 15610d681623SSerge Hallyn { 15621d1e34ddSDavid S. Miller if (xp->security) 15630d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 15640d681623SSerge Hallyn return 0; 15650d681623SSerge Hallyn } 1566cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1567cfbfd45aSThomas Graf { 1568cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1569cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1570cfbfd45aSThomas Graf #else 1571cfbfd45aSThomas Graf return 0; 1572cfbfd45aSThomas Graf #endif 1573cfbfd45aSThomas Graf } 15740d681623SSerge Hallyn 1575f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1576b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1577f7b6983fSMasahide NAKAMURA { 1578c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1579c0144beaSThomas Graf .type = type, 1580c0144beaSThomas Graf }; 1581f7b6983fSMasahide NAKAMURA 1582c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1583f7b6983fSMasahide NAKAMURA } 1584f7b6983fSMasahide NAKAMURA 1585f7b6983fSMasahide NAKAMURA #else 1586b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1587f7b6983fSMasahide NAKAMURA { 1588f7b6983fSMasahide NAKAMURA return 0; 1589f7b6983fSMasahide NAKAMURA } 1590f7b6983fSMasahide NAKAMURA #endif 1591f7b6983fSMasahide NAKAMURA 15921da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 15931da177e4SLinus Torvalds { 15941da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 15951da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 15961da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 15971da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 15981da177e4SLinus Torvalds struct nlmsghdr *nlh; 15991d1e34ddSDavid S. Miller int err; 16001da177e4SLinus Torvalds 160115e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, 160279b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 160379b8b7f4SThomas Graf if (nlh == NULL) 160479b8b7f4SThomas Graf return -EMSGSIZE; 16051da177e4SLinus Torvalds 16067b67c857SThomas Graf p = nlmsg_data(nlh); 16071da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 16081d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 16091d1e34ddSDavid S. Miller if (!err) 16101d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 16111d1e34ddSDavid S. Miller if (!err) 16121d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 16131d1e34ddSDavid S. Miller if (!err) 16141d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 16151d1e34ddSDavid S. Miller if (err) { 16161d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 16171d1e34ddSDavid S. Miller return err; 16181d1e34ddSDavid S. Miller } 16199825069dSThomas Graf nlmsg_end(skb, nlh); 16201da177e4SLinus Torvalds return 0; 16211da177e4SLinus Torvalds } 16221da177e4SLinus Torvalds 16234c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 16244c563f76STimo Teras { 16254c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 1626283bc9f3SFan Du struct net *net = sock_net(cb->skb->sk); 16274c563f76STimo Teras 1628283bc9f3SFan Du xfrm_policy_walk_done(walk, net); 16294c563f76STimo Teras return 0; 16304c563f76STimo Teras } 16314c563f76STimo Teras 16321da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 16331da177e4SLinus Torvalds { 1634fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 16354c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 16361da177e4SLinus Torvalds struct xfrm_dump_info info; 16371da177e4SLinus Torvalds 16384c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 16394c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 16404c563f76STimo Teras 16411da177e4SLinus Torvalds info.in_skb = cb->skb; 16421da177e4SLinus Torvalds info.out_skb = skb; 16431da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 16441da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 16454c563f76STimo Teras 16464c563f76STimo Teras if (!cb->args[0]) { 16474c563f76STimo Teras cb->args[0] = 1; 16484c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 16494c563f76STimo Teras } 16504c563f76STimo Teras 1651fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 16521da177e4SLinus Torvalds 16531da177e4SLinus Torvalds return skb->len; 16541da177e4SLinus Torvalds } 16551da177e4SLinus Torvalds 16561da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 16571da177e4SLinus Torvalds struct xfrm_policy *xp, 16581da177e4SLinus Torvalds int dir, u32 seq) 16591da177e4SLinus Torvalds { 16601da177e4SLinus Torvalds struct xfrm_dump_info info; 16611da177e4SLinus Torvalds struct sk_buff *skb; 1662c2546372SMathias Krause int err; 16631da177e4SLinus Torvalds 16647deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 16651da177e4SLinus Torvalds if (!skb) 16661da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 16671da177e4SLinus Torvalds 16681da177e4SLinus Torvalds info.in_skb = in_skb; 16691da177e4SLinus Torvalds info.out_skb = skb; 16701da177e4SLinus Torvalds info.nlmsg_seq = seq; 16711da177e4SLinus Torvalds info.nlmsg_flags = 0; 16721da177e4SLinus Torvalds 1673c2546372SMathias Krause err = dump_one_policy(xp, dir, 0, &info); 1674c2546372SMathias Krause if (err) { 16751da177e4SLinus Torvalds kfree_skb(skb); 1676c2546372SMathias Krause return ERR_PTR(err); 16771da177e4SLinus Torvalds } 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds return skb; 16801da177e4SLinus Torvalds } 16811da177e4SLinus Torvalds 168222e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 16835424f32eSThomas Graf struct nlattr **attrs) 16841da177e4SLinus Torvalds { 1685fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 16861da177e4SLinus Torvalds struct xfrm_policy *xp; 16871da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1688b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 16891da177e4SLinus Torvalds int err; 169026b15dadSJamal Hadi Salim struct km_event c; 16911da177e4SLinus Torvalds int delete; 1692295fae56SJamal Hadi Salim struct xfrm_mark m; 1693295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 16941da177e4SLinus Torvalds 16957b67c857SThomas Graf p = nlmsg_data(nlh); 16961da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 16971da177e4SLinus Torvalds 169835a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1699f7b6983fSMasahide NAKAMURA if (err) 1700f7b6983fSMasahide NAKAMURA return err; 1701f7b6983fSMasahide NAKAMURA 17021da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 17031da177e4SLinus Torvalds if (err) 17041da177e4SLinus Torvalds return err; 17051da177e4SLinus Torvalds 17061da177e4SLinus Torvalds if (p->index) 1707295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err); 1708df71837dSTrent Jaeger else { 17095424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 171003e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1711df71837dSTrent Jaeger 171235a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1713df71837dSTrent Jaeger if (err) 1714df71837dSTrent Jaeger return err; 1715df71837dSTrent Jaeger 17162c8dd116SDenis V. Lunev ctx = NULL; 1717df71837dSTrent Jaeger if (rt) { 17185424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1719df71837dSTrent Jaeger 172052a4c640SNikolay Aleksandrov err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL); 172103e1ad7bSPaul Moore if (err) 1722df71837dSTrent Jaeger return err; 17232c8dd116SDenis V. Lunev } 1724295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel, 17256f26b61eSJamal Hadi Salim ctx, delete, &err); 172603e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1727df71837dSTrent Jaeger } 17281da177e4SLinus Torvalds if (xp == NULL) 17291da177e4SLinus Torvalds return -ENOENT; 17301da177e4SLinus Torvalds 17311da177e4SLinus Torvalds if (!delete) { 17321da177e4SLinus Torvalds struct sk_buff *resp_skb; 17331da177e4SLinus Torvalds 17341da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 17351da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 17361da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 17371da177e4SLinus Torvalds } else { 1738a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 173915e47304SEric W. Biederman NETLINK_CB(skb).portid); 17401da177e4SLinus Torvalds } 174126b15dadSJamal Hadi Salim } else { 17422e71029eSTetsuo Handa xfrm_audit_policy_delete(xp, err ? 0 : 1, true); 174313fcfbb0SDavid S. Miller 174413fcfbb0SDavid S. Miller if (err != 0) 1745c8c05a8eSCatherine Zhang goto out; 174613fcfbb0SDavid S. Miller 1747e7443892SHerbert Xu c.data.byid = p->index; 1748f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 174926b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 175015e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 175126b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 17521da177e4SLinus Torvalds } 17531da177e4SLinus Torvalds 1754c8c05a8eSCatherine Zhang out: 1755ef41aaa0SEric Paris xfrm_pol_put(xp); 1756e4c17216SPaul Moore if (delete && err == 0) 1757e4c17216SPaul Moore xfrm_garbage_collect(net); 17581da177e4SLinus Torvalds return err; 17591da177e4SLinus Torvalds } 17601da177e4SLinus Torvalds 176122e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 17625424f32eSThomas Graf struct nlattr **attrs) 17631da177e4SLinus Torvalds { 1764fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 176526b15dadSJamal Hadi Salim struct km_event c; 17667b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 17674aa2e62cSJoy Latten int err; 17681da177e4SLinus Torvalds 17692e71029eSTetsuo Handa err = xfrm_state_flush(net, p->proto, true); 17709e64cc95SJamal Hadi Salim if (err) { 17719e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */ 17729e64cc95SJamal Hadi Salim return 0; 1773069c474eSDavid S. Miller return err; 17749e64cc95SJamal Hadi Salim } 1775bf08867fSHerbert Xu c.data.proto = p->proto; 1776f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 177726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 177815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 17797067802eSAlexey Dobriyan c.net = net; 178026b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 178126b15dadSJamal Hadi Salim 17821da177e4SLinus Torvalds return 0; 17831da177e4SLinus Torvalds } 17841da177e4SLinus Torvalds 1785d8647b79SSteffen Klassert static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x) 17867deb2264SThomas Graf { 1787d8647b79SSteffen Klassert size_t replay_size = x->replay_esn ? 1788d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn) : 1789d8647b79SSteffen Klassert sizeof(struct xfrm_replay_state); 1790d8647b79SSteffen Klassert 17917deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 1792d8647b79SSteffen Klassert + nla_total_size(replay_size) 17937deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 17946f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 17957deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 17967deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 17977deb2264SThomas Graf } 1798d51d081dSJamal Hadi Salim 1799214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 1800d51d081dSJamal Hadi Salim { 1801d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1802d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 18031d1e34ddSDavid S. Miller int err; 1804d51d081dSJamal Hadi Salim 180515e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 180679b8b7f4SThomas Graf if (nlh == NULL) 180779b8b7f4SThomas Graf return -EMSGSIZE; 1808d51d081dSJamal Hadi Salim 18097b67c857SThomas Graf id = nlmsg_data(nlh); 18102b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr, sizeof(x->id.daddr)); 1811d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1812d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1813d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 18142b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr, sizeof(x->props.saddr)); 18152b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1816d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1817d51d081dSJamal Hadi Salim 1818d0fde795SDavid S. Miller if (x->replay_esn) { 18191d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 1820d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn), 18211d1e34ddSDavid S. Miller x->replay_esn); 1822d0fde795SDavid S. Miller } else { 18231d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), 18241d1e34ddSDavid S. Miller &x->replay); 1825d0fde795SDavid S. Miller } 18261d1e34ddSDavid S. Miller if (err) 18271d1e34ddSDavid S. Miller goto out_cancel; 18281d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 18291d1e34ddSDavid S. Miller if (err) 18301d1e34ddSDavid S. Miller goto out_cancel; 1831d8647b79SSteffen Klassert 18321d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_RTHR) { 18331d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 18341d1e34ddSDavid S. Miller if (err) 18351d1e34ddSDavid S. Miller goto out_cancel; 18361d1e34ddSDavid S. Miller } 18371d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_ETHR) { 18381d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_ETIMER_THRESH, 18391d1e34ddSDavid S. Miller x->replay_maxage * 10 / HZ); 18401d1e34ddSDavid S. Miller if (err) 18411d1e34ddSDavid S. Miller goto out_cancel; 18421d1e34ddSDavid S. Miller } 18431d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 18441d1e34ddSDavid S. Miller if (err) 18451d1e34ddSDavid S. Miller goto out_cancel; 18466f26b61eSJamal Hadi Salim 1847*053c095aSJohannes Berg nlmsg_end(skb, nlh); 1848*053c095aSJohannes Berg return 0; 1849d51d081dSJamal Hadi Salim 18501d1e34ddSDavid S. Miller out_cancel: 18519825069dSThomas Graf nlmsg_cancel(skb, nlh); 18521d1e34ddSDavid S. Miller return err; 1853d51d081dSJamal Hadi Salim } 1854d51d081dSJamal Hadi Salim 185522e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 18565424f32eSThomas Graf struct nlattr **attrs) 1857d51d081dSJamal Hadi Salim { 1858fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1859d51d081dSJamal Hadi Salim struct xfrm_state *x; 1860d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1861d51d081dSJamal Hadi Salim int err; 1862d51d081dSJamal Hadi Salim struct km_event c; 18636f26b61eSJamal Hadi Salim u32 mark; 18646f26b61eSJamal Hadi Salim struct xfrm_mark m; 18657b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1866d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1867d51d081dSJamal Hadi Salim 18686f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 18696f26b61eSJamal Hadi Salim 18706f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); 1871d8647b79SSteffen Klassert if (x == NULL) 1872d51d081dSJamal Hadi Salim return -ESRCH; 1873d8647b79SSteffen Klassert 1874d8647b79SSteffen Klassert r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 1875d8647b79SSteffen Klassert if (r_skb == NULL) { 1876d8647b79SSteffen Klassert xfrm_state_put(x); 1877d8647b79SSteffen Klassert return -ENOMEM; 1878d51d081dSJamal Hadi Salim } 1879d51d081dSJamal Hadi Salim 1880d51d081dSJamal Hadi Salim /* 1881d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1882d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1883d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1884d51d081dSJamal Hadi Salim */ 1885d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1886d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1887d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 188815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 1889d51d081dSJamal Hadi Salim 1890d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1891d51d081dSJamal Hadi Salim BUG(); 189215e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).portid); 1893d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1894d51d081dSJamal Hadi Salim xfrm_state_put(x); 1895d51d081dSJamal Hadi Salim return err; 1896d51d081dSJamal Hadi Salim } 1897d51d081dSJamal Hadi Salim 189822e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 18995424f32eSThomas Graf struct nlattr **attrs) 1900d51d081dSJamal Hadi Salim { 1901fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1902d51d081dSJamal Hadi Salim struct xfrm_state *x; 1903d51d081dSJamal Hadi Salim struct km_event c; 1904d51d081dSJamal Hadi Salim int err = -EINVAL; 19056f26b61eSJamal Hadi Salim u32 mark = 0; 19066f26b61eSJamal Hadi Salim struct xfrm_mark m; 19077b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 19085424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 1909d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 19105424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1911d51d081dSJamal Hadi Salim 1912d8647b79SSteffen Klassert if (!lt && !rp && !re) 1913d51d081dSJamal Hadi Salim return err; 1914d51d081dSJamal Hadi Salim 1915d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1916d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1917d51d081dSJamal Hadi Salim return err; 1918d51d081dSJamal Hadi Salim 19196f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 19206f26b61eSJamal Hadi Salim 19216f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1922d51d081dSJamal Hadi Salim if (x == NULL) 1923d51d081dSJamal Hadi Salim return -ESRCH; 1924d51d081dSJamal Hadi Salim 1925d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1926d51d081dSJamal Hadi Salim goto out; 1927d51d081dSJamal Hadi Salim 19284479ff76SSteffen Klassert err = xfrm_replay_verify_len(x->replay_esn, re); 1929e2b19125SSteffen Klassert if (err) 1930e2b19125SSteffen Klassert goto out; 1931e2b19125SSteffen Klassert 1932d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1933e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 1); 1934d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1935d51d081dSJamal Hadi Salim 1936d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1937d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 193815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 1939d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1940d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1941d51d081dSJamal Hadi Salim err = 0; 1942d51d081dSJamal Hadi Salim out: 1943d51d081dSJamal Hadi Salim xfrm_state_put(x); 1944d51d081dSJamal Hadi Salim return err; 1945d51d081dSJamal Hadi Salim } 1946d51d081dSJamal Hadi Salim 194722e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 19485424f32eSThomas Graf struct nlattr **attrs) 19491da177e4SLinus Torvalds { 1950fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 195126b15dadSJamal Hadi Salim struct km_event c; 1952b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1953f7b6983fSMasahide NAKAMURA int err; 195426b15dadSJamal Hadi Salim 195535a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1956f7b6983fSMasahide NAKAMURA if (err) 1957f7b6983fSMasahide NAKAMURA return err; 1958f7b6983fSMasahide NAKAMURA 19592e71029eSTetsuo Handa err = xfrm_policy_flush(net, type, true); 19602f1eb65fSJamal Hadi Salim if (err) { 19612f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */ 19622f1eb65fSJamal Hadi Salim return 0; 1963069c474eSDavid S. Miller return err; 19642f1eb65fSJamal Hadi Salim } 19652f1eb65fSJamal Hadi Salim 1966f7b6983fSMasahide NAKAMURA c.data.type = type; 1967f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 196826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 196915e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 19707067802eSAlexey Dobriyan c.net = net; 197126b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 19721da177e4SLinus Torvalds return 0; 19731da177e4SLinus Torvalds } 19741da177e4SLinus Torvalds 197522e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 19765424f32eSThomas Graf struct nlattr **attrs) 19776c5c8ca7SJamal Hadi Salim { 1978fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 19796c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 19807b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 19816c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1982b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 19836c5c8ca7SJamal Hadi Salim int err = -ENOENT; 1984295fae56SJamal Hadi Salim struct xfrm_mark m; 1985295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 19866c5c8ca7SJamal Hadi Salim 198735a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1988f7b6983fSMasahide NAKAMURA if (err) 1989f7b6983fSMasahide NAKAMURA return err; 1990f7b6983fSMasahide NAKAMURA 1991c8bf4d04STimo Teräs err = verify_policy_dir(p->dir); 1992c8bf4d04STimo Teräs if (err) 1993c8bf4d04STimo Teräs return err; 1994c8bf4d04STimo Teräs 19956c5c8ca7SJamal Hadi Salim if (p->index) 1996295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err); 19976c5c8ca7SJamal Hadi Salim else { 19985424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 199903e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 20006c5c8ca7SJamal Hadi Salim 200135a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 20026c5c8ca7SJamal Hadi Salim if (err) 20036c5c8ca7SJamal Hadi Salim return err; 20046c5c8ca7SJamal Hadi Salim 20052c8dd116SDenis V. Lunev ctx = NULL; 20066c5c8ca7SJamal Hadi Salim if (rt) { 20075424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 20086c5c8ca7SJamal Hadi Salim 200952a4c640SNikolay Aleksandrov err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL); 201003e1ad7bSPaul Moore if (err) 20116c5c8ca7SJamal Hadi Salim return err; 20122c8dd116SDenis V. Lunev } 2013295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, 20146f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err); 201503e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 20166c5c8ca7SJamal Hadi Salim } 20176c5c8ca7SJamal Hadi Salim if (xp == NULL) 2018ef41aaa0SEric Paris return -ENOENT; 201903e1ad7bSPaul Moore 2020ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead)) 20216c5c8ca7SJamal Hadi Salim goto out; 20226c5c8ca7SJamal Hadi Salim 20236c5c8ca7SJamal Hadi Salim err = 0; 20246c5c8ca7SJamal Hadi Salim if (up->hard) { 20256c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 20262e71029eSTetsuo Handa xfrm_audit_policy_delete(xp, 1, true); 20276c5c8ca7SJamal Hadi Salim } else { 20286c5c8ca7SJamal Hadi Salim // reset the timers here? 202962db5cfdSstephen hemminger WARN(1, "Dont know what to do with soft policy expire\n"); 20306c5c8ca7SJamal Hadi Salim } 2031c6bb8136SEric W. Biederman km_policy_expired(xp, p->dir, up->hard, nlh->nlmsg_pid); 20326c5c8ca7SJamal Hadi Salim 20336c5c8ca7SJamal Hadi Salim out: 20346c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 20356c5c8ca7SJamal Hadi Salim return err; 20366c5c8ca7SJamal Hadi Salim } 20376c5c8ca7SJamal Hadi Salim 203822e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 20395424f32eSThomas Graf struct nlattr **attrs) 204053bc6b4dSJamal Hadi Salim { 2041fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 204253bc6b4dSJamal Hadi Salim struct xfrm_state *x; 204353bc6b4dSJamal Hadi Salim int err; 20447b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 204553bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 20466f26b61eSJamal Hadi Salim struct xfrm_mark m; 2047928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m); 204853bc6b4dSJamal Hadi Salim 20496f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family); 205053bc6b4dSJamal Hadi Salim 20513a765aa5SDavid S. Miller err = -ENOENT; 205253bc6b4dSJamal Hadi Salim if (x == NULL) 205353bc6b4dSJamal Hadi Salim return err; 205453bc6b4dSJamal Hadi Salim 205553bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 20563a765aa5SDavid S. Miller err = -EINVAL; 205753bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 205853bc6b4dSJamal Hadi Salim goto out; 2059c6bb8136SEric W. Biederman km_state_expired(x, ue->hard, nlh->nlmsg_pid); 206053bc6b4dSJamal Hadi Salim 2061161a09e7SJoy Latten if (ue->hard) { 206253bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 20632e71029eSTetsuo Handa xfrm_audit_state_delete(x, 1, true); 2064161a09e7SJoy Latten } 20653a765aa5SDavid S. Miller err = 0; 206653bc6b4dSJamal Hadi Salim out: 206753bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 206853bc6b4dSJamal Hadi Salim xfrm_state_put(x); 206953bc6b4dSJamal Hadi Salim return err; 207053bc6b4dSJamal Hadi Salim } 207153bc6b4dSJamal Hadi Salim 207222e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 20735424f32eSThomas Graf struct nlattr **attrs) 2074980ebd25SJamal Hadi Salim { 2075fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 2076980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 2077980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 2078980ebd25SJamal Hadi Salim int i; 20795424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 20806f26b61eSJamal Hadi Salim struct xfrm_mark mark; 2081980ebd25SJamal Hadi Salim 20827b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 2083fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 2084980ebd25SJamal Hadi Salim int err = -ENOMEM; 2085980ebd25SJamal Hadi Salim 2086980ebd25SJamal Hadi Salim if (!x) 2087d8eb9307SIlpo Järvinen goto nomem; 2088980ebd25SJamal Hadi Salim 20896f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark); 20906f26b61eSJamal Hadi Salim 2091980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 2092d8eb9307SIlpo Järvinen if (err) 2093d8eb9307SIlpo Järvinen goto bad_policy; 2094980ebd25SJamal Hadi Salim 2095980ebd25SJamal Hadi Salim /* build an XP */ 2096fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 2097d8eb9307SIlpo Järvinen if (!xp) 2098d8eb9307SIlpo Järvinen goto free_state; 2099980ebd25SJamal Hadi Salim 2100980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 2101980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 2102980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 21036f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m; 21046f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v; 21055424f32eSThomas Graf ut = nla_data(rt); 2106980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 2107980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 2108980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 2109980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 2110980ebd25SJamal Hadi Salim x->props.mode = t->mode; 2111980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 2112980ebd25SJamal Hadi Salim x->props.family = ut->family; 2113980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 2114980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 2115980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 2116980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 2117980ebd25SJamal Hadi Salim 2118980ebd25SJamal Hadi Salim } 2119980ebd25SJamal Hadi Salim 2120980ebd25SJamal Hadi Salim kfree(x); 2121980ebd25SJamal Hadi Salim kfree(xp); 2122980ebd25SJamal Hadi Salim 2123980ebd25SJamal Hadi Salim return 0; 2124d8eb9307SIlpo Järvinen 2125d8eb9307SIlpo Järvinen bad_policy: 212662db5cfdSstephen hemminger WARN(1, "BAD policy passed\n"); 2127d8eb9307SIlpo Järvinen free_state: 2128d8eb9307SIlpo Järvinen kfree(x); 2129d8eb9307SIlpo Järvinen nomem: 2130d8eb9307SIlpo Järvinen return err; 2131980ebd25SJamal Hadi Salim } 2132980ebd25SJamal Hadi Salim 21335c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 21345c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 213513c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 21365424f32eSThomas Graf struct nlattr **attrs, int *num) 21375c79de6eSShinta Sugimoto { 21385424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 21395c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 21405c79de6eSShinta Sugimoto int i, num_migrate; 21415c79de6eSShinta Sugimoto 214213c1d189SArnaud Ebalard if (k != NULL) { 214313c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 214413c1d189SArnaud Ebalard 214513c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 214613c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 214713c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 214813c1d189SArnaud Ebalard k->family = uk->family; 214913c1d189SArnaud Ebalard k->reserved = uk->reserved; 215013c1d189SArnaud Ebalard } 215113c1d189SArnaud Ebalard 21525424f32eSThomas Graf um = nla_data(rt); 21535424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 21545c79de6eSShinta Sugimoto 21555c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 21565c79de6eSShinta Sugimoto return -EINVAL; 21575c79de6eSShinta Sugimoto 21585c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 21595c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 21605c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 21615c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 21625c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 21635c79de6eSShinta Sugimoto 21645c79de6eSShinta Sugimoto ma->proto = um->proto; 21655c79de6eSShinta Sugimoto ma->mode = um->mode; 21665c79de6eSShinta Sugimoto ma->reqid = um->reqid; 21675c79de6eSShinta Sugimoto 21685c79de6eSShinta Sugimoto ma->old_family = um->old_family; 21695c79de6eSShinta Sugimoto ma->new_family = um->new_family; 21705c79de6eSShinta Sugimoto } 21715c79de6eSShinta Sugimoto 21725c79de6eSShinta Sugimoto *num = i; 21735c79de6eSShinta Sugimoto return 0; 21745c79de6eSShinta Sugimoto } 21755c79de6eSShinta Sugimoto 21765c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 21775424f32eSThomas Graf struct nlattr **attrs) 21785c79de6eSShinta Sugimoto { 21797b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 21805c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 218113c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 21825c79de6eSShinta Sugimoto u8 type; 21835c79de6eSShinta Sugimoto int err; 21845c79de6eSShinta Sugimoto int n = 0; 21858d549c4fSFan Du struct net *net = sock_net(skb->sk); 21865c79de6eSShinta Sugimoto 218735a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 2188cf5cb79fSThomas Graf return -EINVAL; 21895c79de6eSShinta Sugimoto 219013c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 219113c1d189SArnaud Ebalard 21925424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 21935c79de6eSShinta Sugimoto if (err) 21945c79de6eSShinta Sugimoto return err; 21955c79de6eSShinta Sugimoto 219613c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 21975c79de6eSShinta Sugimoto if (err) 21985c79de6eSShinta Sugimoto return err; 21995c79de6eSShinta Sugimoto 22005c79de6eSShinta Sugimoto if (!n) 22015c79de6eSShinta Sugimoto return 0; 22025c79de6eSShinta Sugimoto 22038d549c4fSFan Du xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net); 22045c79de6eSShinta Sugimoto 22055c79de6eSShinta Sugimoto return 0; 22065c79de6eSShinta Sugimoto } 22075c79de6eSShinta Sugimoto #else 22085c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 22095424f32eSThomas Graf struct nlattr **attrs) 22105c79de6eSShinta Sugimoto { 22115c79de6eSShinta Sugimoto return -ENOPROTOOPT; 22125c79de6eSShinta Sugimoto } 22135c79de6eSShinta Sugimoto #endif 22145c79de6eSShinta Sugimoto 22155c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 2216183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) 22175c79de6eSShinta Sugimoto { 22185c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 22195c79de6eSShinta Sugimoto 22205c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 22215c79de6eSShinta Sugimoto um.proto = m->proto; 22225c79de6eSShinta Sugimoto um.mode = m->mode; 22235c79de6eSShinta Sugimoto um.reqid = m->reqid; 22245c79de6eSShinta Sugimoto um.old_family = m->old_family; 22255c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 22265c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 22275c79de6eSShinta Sugimoto um.new_family = m->new_family; 22285c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 22295c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 22305c79de6eSShinta Sugimoto 2231c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 22325c79de6eSShinta Sugimoto } 22335c79de6eSShinta Sugimoto 2234183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) 223513c1d189SArnaud Ebalard { 223613c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 223713c1d189SArnaud Ebalard 223813c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 223913c1d189SArnaud Ebalard uk.family = k->family; 224013c1d189SArnaud Ebalard uk.reserved = k->reserved; 224113c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 2242a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 224313c1d189SArnaud Ebalard 224413c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 224513c1d189SArnaud Ebalard } 224613c1d189SArnaud Ebalard 224713c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 22487deb2264SThomas Graf { 22497deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 225013c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 22517deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 22527deb2264SThomas Graf + userpolicy_type_attrsize(); 22537deb2264SThomas Graf } 22547deb2264SThomas Graf 2255183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, 2256183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k, 2257183cad12SDavid S. Miller const struct xfrm_selector *sel, u8 dir, u8 type) 22585c79de6eSShinta Sugimoto { 2259183cad12SDavid S. Miller const struct xfrm_migrate *mp; 22605c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 22615c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 22621d1e34ddSDavid S. Miller int i, err; 22635c79de6eSShinta Sugimoto 226479b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 226579b8b7f4SThomas Graf if (nlh == NULL) 226679b8b7f4SThomas Graf return -EMSGSIZE; 22675c79de6eSShinta Sugimoto 22687b67c857SThomas Graf pol_id = nlmsg_data(nlh); 22695c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 22705c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 22715c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 22725c79de6eSShinta Sugimoto pol_id->dir = dir; 22735c79de6eSShinta Sugimoto 22741d1e34ddSDavid S. Miller if (k != NULL) { 22751d1e34ddSDavid S. Miller err = copy_to_user_kmaddress(k, skb); 22761d1e34ddSDavid S. Miller if (err) 22771d1e34ddSDavid S. Miller goto out_cancel; 22781d1e34ddSDavid S. Miller } 22791d1e34ddSDavid S. Miller err = copy_to_user_policy_type(type, skb); 22801d1e34ddSDavid S. Miller if (err) 22811d1e34ddSDavid S. Miller goto out_cancel; 22825c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 22831d1e34ddSDavid S. Miller err = copy_to_user_migrate(mp, skb); 22841d1e34ddSDavid S. Miller if (err) 22851d1e34ddSDavid S. Miller goto out_cancel; 22865c79de6eSShinta Sugimoto } 22875c79de6eSShinta Sugimoto 2288*053c095aSJohannes Berg nlmsg_end(skb, nlh); 2289*053c095aSJohannes Berg return 0; 22901d1e34ddSDavid S. Miller 22911d1e34ddSDavid S. Miller out_cancel: 22929825069dSThomas Graf nlmsg_cancel(skb, nlh); 22931d1e34ddSDavid S. Miller return err; 22945c79de6eSShinta Sugimoto } 22955c79de6eSShinta Sugimoto 2296183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2297183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2298183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 22995c79de6eSShinta Sugimoto { 2300a6483b79SAlexey Dobriyan struct net *net = &init_net; 23015c79de6eSShinta Sugimoto struct sk_buff *skb; 23025c79de6eSShinta Sugimoto 230313c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 23045c79de6eSShinta Sugimoto if (skb == NULL) 23055c79de6eSShinta Sugimoto return -ENOMEM; 23065c79de6eSShinta Sugimoto 23075c79de6eSShinta Sugimoto /* build migrate */ 230813c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 23095c79de6eSShinta Sugimoto BUG(); 23105c79de6eSShinta Sugimoto 231121ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE); 23125c79de6eSShinta Sugimoto } 23135c79de6eSShinta Sugimoto #else 2314183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2315183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2316183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 23175c79de6eSShinta Sugimoto { 23185c79de6eSShinta Sugimoto return -ENOPROTOOPT; 23195c79de6eSShinta Sugimoto } 23205c79de6eSShinta Sugimoto #endif 2321d51d081dSJamal Hadi Salim 2322a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2323492b558bSThomas Graf 2324492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 232566f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2326492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2327492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2328492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2329492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2330492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2331492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2332980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 233353bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2334492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 233566f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 23366c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2337492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2338a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2339d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2340d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 234197a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 23425c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2343a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2344880a6fabSChristophe Gouault [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 2345a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 23461da177e4SLinus Torvalds }; 23471da177e4SLinus Torvalds 2348492b558bSThomas Graf #undef XMSGSIZE 2349492b558bSThomas Graf 2350cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 2351c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)}, 2352c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)}, 2353c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64}, 2354c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 23551a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2356cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2357cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2358cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2359cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2360cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2361cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2362cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2363cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2364cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2365cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2366cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2367cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2368cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2369cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 237013c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 23716f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 237235d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2373d8647b79SSteffen Klassert [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 2374a947b0a9SNicolas Dichtel [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 2375d3623099SNicolas Dichtel [XFRMA_PROTO] = { .type = NLA_U8 }, 2376870a2df4SNicolas Dichtel [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 2377cf5cb79fSThomas Graf }; 2378cf5cb79fSThomas Graf 2379880a6fabSChristophe Gouault static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { 2380880a6fabSChristophe Gouault [XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, 2381880a6fabSChristophe Gouault [XFRMA_SPD_IPV6_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, 2382880a6fabSChristophe Gouault }; 2383880a6fabSChristophe Gouault 238405600a79SMathias Krause static const struct xfrm_link { 23855424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 23861da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 23874c563f76STimo Teras int (*done)(struct netlink_callback *); 2388880a6fabSChristophe Gouault const struct nla_policy *nla_pol; 2389880a6fabSChristophe Gouault int nla_max; 2390492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2391492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2392492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2393492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 23944c563f76STimo Teras .dump = xfrm_dump_sa, 23954c563f76STimo Teras .done = xfrm_dump_sa_done }, 2396492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2397492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2398492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 23994c563f76STimo Teras .dump = xfrm_dump_policy, 24004c563f76STimo Teras .done = xfrm_dump_policy_done }, 2401492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2402980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 240353bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2404492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2405492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 24066c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2407492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2408492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2409d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2410d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 24115c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 241228d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2413880a6fabSChristophe Gouault [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_set_spdinfo, 2414880a6fabSChristophe Gouault .nla_pol = xfrma_spd_policy, 2415880a6fabSChristophe Gouault .nla_max = XFRMA_SPD_MAX }, 2416ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 24171da177e4SLinus Torvalds }; 24181da177e4SLinus Torvalds 24191d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 24201da177e4SLinus Torvalds { 2421a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 242235a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 242305600a79SMathias Krause const struct xfrm_link *link; 2424a7bd9a45SThomas Graf int type, err; 24251da177e4SLinus Torvalds 24261da177e4SLinus Torvalds type = nlh->nlmsg_type; 24271da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 24281d00a4ebSThomas Graf return -EINVAL; 24291da177e4SLinus Torvalds 24301da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 24311da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 24321da177e4SLinus Torvalds 24331da177e4SLinus Torvalds /* All operations require privileges, even GET */ 243490f62cf3SEric W. Biederman if (!netlink_net_capable(skb, CAP_NET_ADMIN)) 24351d00a4ebSThomas Graf return -EPERM; 24361da177e4SLinus Torvalds 2437492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2438492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2439b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) { 24401da177e4SLinus Torvalds if (link->dump == NULL) 24411d00a4ebSThomas Graf return -EINVAL; 24421da177e4SLinus Torvalds 244380d326faSPablo Neira Ayuso { 244480d326faSPablo Neira Ayuso struct netlink_dump_control c = { 244580d326faSPablo Neira Ayuso .dump = link->dump, 244680d326faSPablo Neira Ayuso .done = link->done, 244780d326faSPablo Neira Ayuso }; 244880d326faSPablo Neira Ayuso return netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c); 244980d326faSPablo Neira Ayuso } 24501da177e4SLinus Torvalds } 24511da177e4SLinus Torvalds 2452880a6fabSChristophe Gouault err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, 2453880a6fabSChristophe Gouault link->nla_max ? : XFRMA_MAX, 2454880a6fabSChristophe Gouault link->nla_pol ? : xfrma_policy); 2455a7bd9a45SThomas Graf if (err < 0) 2456a7bd9a45SThomas Graf return err; 24571da177e4SLinus Torvalds 24581da177e4SLinus Torvalds if (link->doit == NULL) 24591d00a4ebSThomas Graf return -EINVAL; 24601da177e4SLinus Torvalds 24615424f32eSThomas Graf return link->doit(skb, nlh, attrs); 24621da177e4SLinus Torvalds } 24631da177e4SLinus Torvalds 2464cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 24651da177e4SLinus Torvalds { 2466283bc9f3SFan Du struct net *net = sock_net(skb->sk); 2467283bc9f3SFan Du 2468283bc9f3SFan Du mutex_lock(&net->xfrm.xfrm_cfg_mutex); 2469cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 2470283bc9f3SFan Du mutex_unlock(&net->xfrm.xfrm_cfg_mutex); 24711da177e4SLinus Torvalds } 24721da177e4SLinus Torvalds 24737deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 24747deb2264SThomas Graf { 24756f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) 24766f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)); 24777deb2264SThomas Graf } 24787deb2264SThomas Graf 2479214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 24801da177e4SLinus Torvalds { 24811da177e4SLinus Torvalds struct xfrm_user_expire *ue; 24821da177e4SLinus Torvalds struct nlmsghdr *nlh; 24831d1e34ddSDavid S. Miller int err; 24841da177e4SLinus Torvalds 248515e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 248679b8b7f4SThomas Graf if (nlh == NULL) 248779b8b7f4SThomas Graf return -EMSGSIZE; 24881da177e4SLinus Torvalds 24897b67c857SThomas Graf ue = nlmsg_data(nlh); 24901da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2491d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 24921da177e4SLinus Torvalds 24931d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 24941d1e34ddSDavid S. Miller if (err) 24951d1e34ddSDavid S. Miller return err; 24966f26b61eSJamal Hadi Salim 2497*053c095aSJohannes Berg nlmsg_end(skb, nlh); 2498*053c095aSJohannes Berg return 0; 24991da177e4SLinus Torvalds } 25001da177e4SLinus Torvalds 2501214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) 25021da177e4SLinus Torvalds { 2503fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 25041da177e4SLinus Torvalds struct sk_buff *skb; 25051da177e4SLinus Torvalds 25067deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 25071da177e4SLinus Torvalds if (skb == NULL) 25081da177e4SLinus Torvalds return -ENOMEM; 25091da177e4SLinus Torvalds 25106f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) { 25116f26b61eSJamal Hadi Salim kfree_skb(skb); 25126f26b61eSJamal Hadi Salim return -EMSGSIZE; 25136f26b61eSJamal Hadi Salim } 25141da177e4SLinus Torvalds 251521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE); 25161da177e4SLinus Torvalds } 25171da177e4SLinus Torvalds 2518214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) 2519d51d081dSJamal Hadi Salim { 2520fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2521d51d081dSJamal Hadi Salim struct sk_buff *skb; 2522d51d081dSJamal Hadi Salim 2523d8647b79SSteffen Klassert skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 2524d51d081dSJamal Hadi Salim if (skb == NULL) 2525d51d081dSJamal Hadi Salim return -ENOMEM; 2526d51d081dSJamal Hadi Salim 2527d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2528d51d081dSJamal Hadi Salim BUG(); 2529d51d081dSJamal Hadi Salim 253021ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS); 2531d51d081dSJamal Hadi Salim } 2532d51d081dSJamal Hadi Salim 2533214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c) 253426b15dadSJamal Hadi Salim { 25357067802eSAlexey Dobriyan struct net *net = c->net; 253626b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 253726b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 253826b15dadSJamal Hadi Salim struct sk_buff *skb; 25397deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 254026b15dadSJamal Hadi Salim 25417deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 254226b15dadSJamal Hadi Salim if (skb == NULL) 254326b15dadSJamal Hadi Salim return -ENOMEM; 254426b15dadSJamal Hadi Salim 254515e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 254679b8b7f4SThomas Graf if (nlh == NULL) { 254779b8b7f4SThomas Graf kfree_skb(skb); 254879b8b7f4SThomas Graf return -EMSGSIZE; 254979b8b7f4SThomas Graf } 255026b15dadSJamal Hadi Salim 25517b67c857SThomas Graf p = nlmsg_data(nlh); 2552bf08867fSHerbert Xu p->proto = c->data.proto; 255326b15dadSJamal Hadi Salim 25549825069dSThomas Graf nlmsg_end(skb, nlh); 255526b15dadSJamal Hadi Salim 255621ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA); 255726b15dadSJamal Hadi Salim } 255826b15dadSJamal Hadi Salim 25597deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 256026b15dadSJamal Hadi Salim { 25617deb2264SThomas Graf size_t l = 0; 25621a6509d9SHerbert Xu if (x->aead) 25631a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 25644447bb33SMartin Willi if (x->aalg) { 25654447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 25664447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 25674447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 25684447bb33SMartin Willi } 256926b15dadSJamal Hadi Salim if (x->ealg) 25700f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 257126b15dadSJamal Hadi Salim if (x->calg) 25727deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 257326b15dadSJamal Hadi Salim if (x->encap) 25747deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 257535d2856bSMartin Willi if (x->tfcpad) 257635d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad)); 2577d8647b79SSteffen Klassert if (x->replay_esn) 2578d8647b79SSteffen Klassert l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn)); 2579f293a5e3Sdingzhi else 2580f293a5e3Sdingzhi l += nla_total_size(sizeof(struct xfrm_replay_state)); 258168325d3bSHerbert Xu if (x->security) 258268325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 258368325d3bSHerbert Xu x->security->ctx_len); 258468325d3bSHerbert Xu if (x->coaddr) 258568325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 2586a947b0a9SNicolas Dichtel if (x->props.extra_flags) 2587a947b0a9SNicolas Dichtel l += nla_total_size(sizeof(x->props.extra_flags)); 258868325d3bSHerbert Xu 2589d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2590d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 259126b15dadSJamal Hadi Salim 259226b15dadSJamal Hadi Salim return l; 259326b15dadSJamal Hadi Salim } 259426b15dadSJamal Hadi Salim 2595214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) 259626b15dadSJamal Hadi Salim { 2597fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 259826b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 25990603eac0SHerbert Xu struct xfrm_usersa_id *id; 260026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 260126b15dadSJamal Hadi Salim struct sk_buff *skb; 260226b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 26031d1e34ddSDavid S. Miller int headlen, err; 26040603eac0SHerbert Xu 26050603eac0SHerbert Xu headlen = sizeof(*p); 26060603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 26077deb2264SThomas Graf len += nla_total_size(headlen); 26080603eac0SHerbert Xu headlen = sizeof(*id); 26096f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 26100603eac0SHerbert Xu } 26117deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 261226b15dadSJamal Hadi Salim 26137deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 261426b15dadSJamal Hadi Salim if (skb == NULL) 261526b15dadSJamal Hadi Salim return -ENOMEM; 261626b15dadSJamal Hadi Salim 261715e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); 26181d1e34ddSDavid S. Miller err = -EMSGSIZE; 261979b8b7f4SThomas Graf if (nlh == NULL) 26201d1e34ddSDavid S. Miller goto out_free_skb; 262126b15dadSJamal Hadi Salim 26227b67c857SThomas Graf p = nlmsg_data(nlh); 26230603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2624c0144beaSThomas Graf struct nlattr *attr; 2625c0144beaSThomas Graf 26267b67c857SThomas Graf id = nlmsg_data(nlh); 26270603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 26280603eac0SHerbert Xu id->spi = x->id.spi; 26290603eac0SHerbert Xu id->family = x->props.family; 26300603eac0SHerbert Xu id->proto = x->id.proto; 26310603eac0SHerbert Xu 2632c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 26331d1e34ddSDavid S. Miller err = -EMSGSIZE; 2634c0144beaSThomas Graf if (attr == NULL) 26351d1e34ddSDavid S. Miller goto out_free_skb; 2636c0144beaSThomas Graf 2637c0144beaSThomas Graf p = nla_data(attr); 26380603eac0SHerbert Xu } 26391d1e34ddSDavid S. Miller err = copy_to_user_state_extra(x, p, skb); 26401d1e34ddSDavid S. Miller if (err) 26411d1e34ddSDavid S. Miller goto out_free_skb; 264226b15dadSJamal Hadi Salim 26439825069dSThomas Graf nlmsg_end(skb, nlh); 264426b15dadSJamal Hadi Salim 264521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA); 264626b15dadSJamal Hadi Salim 26471d1e34ddSDavid S. Miller out_free_skb: 264826b15dadSJamal Hadi Salim kfree_skb(skb); 26491d1e34ddSDavid S. Miller return err; 265026b15dadSJamal Hadi Salim } 265126b15dadSJamal Hadi Salim 2652214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) 265326b15dadSJamal Hadi Salim { 265426b15dadSJamal Hadi Salim 265526b15dadSJamal Hadi Salim switch (c->event) { 2656f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 265726b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2658d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2659d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2660f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2661f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2662f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 266326b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2664f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 266526b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 266626b15dadSJamal Hadi Salim default: 266762db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n", 266862db5cfdSstephen hemminger c->event); 266926b15dadSJamal Hadi Salim break; 267026b15dadSJamal Hadi Salim } 267126b15dadSJamal Hadi Salim 267226b15dadSJamal Hadi Salim return 0; 267326b15dadSJamal Hadi Salim 267426b15dadSJamal Hadi Salim } 267526b15dadSJamal Hadi Salim 26767deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 26777deb2264SThomas Graf struct xfrm_policy *xp) 26787deb2264SThomas Graf { 26797deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 26807deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 26816f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 26827deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 26837deb2264SThomas Graf + userpolicy_type_attrsize(); 26847deb2264SThomas Graf } 26857deb2264SThomas Graf 26861da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 268765e0736bSFan Du struct xfrm_tmpl *xt, struct xfrm_policy *xp) 26881da177e4SLinus Torvalds { 26891d1e34ddSDavid S. Miller __u32 seq = xfrm_get_acqseq(); 26901da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 26911da177e4SLinus Torvalds struct nlmsghdr *nlh; 26921d1e34ddSDavid S. Miller int err; 26931da177e4SLinus Torvalds 269479b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 269579b8b7f4SThomas Graf if (nlh == NULL) 269679b8b7f4SThomas Graf return -EMSGSIZE; 26971da177e4SLinus Torvalds 26987b67c857SThomas Graf ua = nlmsg_data(nlh); 26991da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 27001da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 27011da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 270265e0736bSFan Du copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT); 27031da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 27041da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 27051da177e4SLinus Torvalds ua->calgos = xt->calgos; 27061da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 27071da177e4SLinus Torvalds 27081d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 27091d1e34ddSDavid S. Miller if (!err) 27101d1e34ddSDavid S. Miller err = copy_to_user_state_sec_ctx(x, skb); 27111d1e34ddSDavid S. Miller if (!err) 27121d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 27131d1e34ddSDavid S. Miller if (!err) 27141d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 27151d1e34ddSDavid S. Miller if (err) { 27161d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 27171d1e34ddSDavid S. Miller return err; 27181d1e34ddSDavid S. Miller } 27191da177e4SLinus Torvalds 2720*053c095aSJohannes Berg nlmsg_end(skb, nlh); 2721*053c095aSJohannes Berg return 0; 27221da177e4SLinus Torvalds } 27231da177e4SLinus Torvalds 27241da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 272565e0736bSFan Du struct xfrm_policy *xp) 27261da177e4SLinus Torvalds { 2727a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 27281da177e4SLinus Torvalds struct sk_buff *skb; 27291da177e4SLinus Torvalds 27307deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 27311da177e4SLinus Torvalds if (skb == NULL) 27321da177e4SLinus Torvalds return -ENOMEM; 27331da177e4SLinus Torvalds 273465e0736bSFan Du if (build_acquire(skb, x, xt, xp) < 0) 27351da177e4SLinus Torvalds BUG(); 27361da177e4SLinus Torvalds 273721ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE); 27381da177e4SLinus Torvalds } 27391da177e4SLinus Torvalds 27401da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 27411da177e4SLinus Torvalds * or more templates. 27421da177e4SLinus Torvalds */ 2743cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 27441da177e4SLinus Torvalds u8 *data, int len, int *dir) 27451da177e4SLinus Torvalds { 2746fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 27471da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 27481da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 27491da177e4SLinus Torvalds struct xfrm_policy *xp; 27501da177e4SLinus Torvalds int nr; 27511da177e4SLinus Torvalds 2752cb969f07SVenkat Yekkirala switch (sk->sk_family) { 27531da177e4SLinus Torvalds case AF_INET: 27541da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 27551da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 27561da177e4SLinus Torvalds return NULL; 27571da177e4SLinus Torvalds } 27581da177e4SLinus Torvalds break; 2759dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 27601da177e4SLinus Torvalds case AF_INET6: 27611da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 27621da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 27631da177e4SLinus Torvalds return NULL; 27641da177e4SLinus Torvalds } 27651da177e4SLinus Torvalds break; 27661da177e4SLinus Torvalds #endif 27671da177e4SLinus Torvalds default: 27681da177e4SLinus Torvalds *dir = -EINVAL; 27691da177e4SLinus Torvalds return NULL; 27701da177e4SLinus Torvalds } 27711da177e4SLinus Torvalds 27721da177e4SLinus Torvalds *dir = -EINVAL; 27731da177e4SLinus Torvalds 27741da177e4SLinus Torvalds if (len < sizeof(*p) || 27751da177e4SLinus Torvalds verify_newpolicy_info(p)) 27761da177e4SLinus Torvalds return NULL; 27771da177e4SLinus Torvalds 27781da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2779b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 27801da177e4SLinus Torvalds return NULL; 27811da177e4SLinus Torvalds 2782a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2783a4f1bac6SHerbert Xu return NULL; 2784a4f1bac6SHerbert Xu 27852f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC); 27861da177e4SLinus Torvalds if (xp == NULL) { 27871da177e4SLinus Torvalds *dir = -ENOBUFS; 27881da177e4SLinus Torvalds return NULL; 27891da177e4SLinus Torvalds } 27901da177e4SLinus Torvalds 27911da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2792f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 27931da177e4SLinus Torvalds copy_templates(xp, ut, nr); 27941da177e4SLinus Torvalds 27951da177e4SLinus Torvalds *dir = p->dir; 27961da177e4SLinus Torvalds 27971da177e4SLinus Torvalds return xp; 27981da177e4SLinus Torvalds } 27991da177e4SLinus Torvalds 28007deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 28017deb2264SThomas Graf { 28027deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 28037deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 28047deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 2805295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 28067deb2264SThomas Graf + userpolicy_type_attrsize(); 28077deb2264SThomas Graf } 28087deb2264SThomas Graf 28091da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2810214e005bSDavid S. Miller int dir, const struct km_event *c) 28111da177e4SLinus Torvalds { 28121da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 2813d51d081dSJamal Hadi Salim int hard = c->data.hard; 28141d1e34ddSDavid S. Miller struct nlmsghdr *nlh; 28151d1e34ddSDavid S. Miller int err; 28161da177e4SLinus Torvalds 281715e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 281879b8b7f4SThomas Graf if (nlh == NULL) 281979b8b7f4SThomas Graf return -EMSGSIZE; 28201da177e4SLinus Torvalds 28217b67c857SThomas Graf upe = nlmsg_data(nlh); 28221da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 28231d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 28241d1e34ddSDavid S. Miller if (!err) 28251d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 28261d1e34ddSDavid S. Miller if (!err) 28271d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 28281d1e34ddSDavid S. Miller if (!err) 28291d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 28301d1e34ddSDavid S. Miller if (err) { 28311d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 28321d1e34ddSDavid S. Miller return err; 28331d1e34ddSDavid S. Miller } 28341da177e4SLinus Torvalds upe->hard = !!hard; 28351da177e4SLinus Torvalds 2836*053c095aSJohannes Berg nlmsg_end(skb, nlh); 2837*053c095aSJohannes Berg return 0; 28381da177e4SLinus Torvalds } 28391da177e4SLinus Torvalds 2840214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 28411da177e4SLinus Torvalds { 2842fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 28431da177e4SLinus Torvalds struct sk_buff *skb; 28441da177e4SLinus Torvalds 28457deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 28461da177e4SLinus Torvalds if (skb == NULL) 28471da177e4SLinus Torvalds return -ENOMEM; 28481da177e4SLinus Torvalds 2849d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 28501da177e4SLinus Torvalds BUG(); 28511da177e4SLinus Torvalds 285221ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE); 28531da177e4SLinus Torvalds } 28541da177e4SLinus Torvalds 2855214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) 285626b15dadSJamal Hadi Salim { 28571d1e34ddSDavid S. Miller int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 2858fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 285926b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 28600603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 286126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 286226b15dadSJamal Hadi Salim struct sk_buff *skb; 28631d1e34ddSDavid S. Miller int headlen, err; 28640603eac0SHerbert Xu 28650603eac0SHerbert Xu headlen = sizeof(*p); 28660603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 28677deb2264SThomas Graf len += nla_total_size(headlen); 28680603eac0SHerbert Xu headlen = sizeof(*id); 28690603eac0SHerbert Xu } 2870cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 2871295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 28727deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 287326b15dadSJamal Hadi Salim 28747deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 287526b15dadSJamal Hadi Salim if (skb == NULL) 287626b15dadSJamal Hadi Salim return -ENOMEM; 287726b15dadSJamal Hadi Salim 287815e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); 28791d1e34ddSDavid S. Miller err = -EMSGSIZE; 288079b8b7f4SThomas Graf if (nlh == NULL) 28811d1e34ddSDavid S. Miller goto out_free_skb; 288226b15dadSJamal Hadi Salim 28837b67c857SThomas Graf p = nlmsg_data(nlh); 28840603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2885c0144beaSThomas Graf struct nlattr *attr; 2886c0144beaSThomas Graf 28877b67c857SThomas Graf id = nlmsg_data(nlh); 28880603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 28890603eac0SHerbert Xu id->dir = dir; 28900603eac0SHerbert Xu if (c->data.byid) 28910603eac0SHerbert Xu id->index = xp->index; 28920603eac0SHerbert Xu else 28930603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 28940603eac0SHerbert Xu 2895c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 28961d1e34ddSDavid S. Miller err = -EMSGSIZE; 2897c0144beaSThomas Graf if (attr == NULL) 28981d1e34ddSDavid S. Miller goto out_free_skb; 2899c0144beaSThomas Graf 2900c0144beaSThomas Graf p = nla_data(attr); 29010603eac0SHerbert Xu } 290226b15dadSJamal Hadi Salim 290326b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 29041d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 29051d1e34ddSDavid S. Miller if (!err) 29061d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 29071d1e34ddSDavid S. Miller if (!err) 29081d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 29091d1e34ddSDavid S. Miller if (err) 29101d1e34ddSDavid S. Miller goto out_free_skb; 2911295fae56SJamal Hadi Salim 29129825069dSThomas Graf nlmsg_end(skb, nlh); 291326b15dadSJamal Hadi Salim 291421ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY); 291526b15dadSJamal Hadi Salim 29161d1e34ddSDavid S. Miller out_free_skb: 291726b15dadSJamal Hadi Salim kfree_skb(skb); 29181d1e34ddSDavid S. Miller return err; 291926b15dadSJamal Hadi Salim } 292026b15dadSJamal Hadi Salim 2921214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c) 292226b15dadSJamal Hadi Salim { 29237067802eSAlexey Dobriyan struct net *net = c->net; 292426b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 292526b15dadSJamal Hadi Salim struct sk_buff *skb; 29261d1e34ddSDavid S. Miller int err; 292726b15dadSJamal Hadi Salim 29287deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 292926b15dadSJamal Hadi Salim if (skb == NULL) 293026b15dadSJamal Hadi Salim return -ENOMEM; 293126b15dadSJamal Hadi Salim 293215e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 29331d1e34ddSDavid S. Miller err = -EMSGSIZE; 293479b8b7f4SThomas Graf if (nlh == NULL) 29351d1e34ddSDavid S. Miller goto out_free_skb; 29361d1e34ddSDavid S. Miller err = copy_to_user_policy_type(c->data.type, skb); 29371d1e34ddSDavid S. Miller if (err) 29381d1e34ddSDavid S. Miller goto out_free_skb; 293926b15dadSJamal Hadi Salim 29409825069dSThomas Graf nlmsg_end(skb, nlh); 294126b15dadSJamal Hadi Salim 294221ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY); 294326b15dadSJamal Hadi Salim 29441d1e34ddSDavid S. Miller out_free_skb: 294526b15dadSJamal Hadi Salim kfree_skb(skb); 29461d1e34ddSDavid S. Miller return err; 294726b15dadSJamal Hadi Salim } 294826b15dadSJamal Hadi Salim 2949214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 295026b15dadSJamal Hadi Salim { 295126b15dadSJamal Hadi Salim 295226b15dadSJamal Hadi Salim switch (c->event) { 2953f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2954f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2955f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 295626b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2957f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 295826b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2959f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 296026b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 296126b15dadSJamal Hadi Salim default: 296262db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n", 296362db5cfdSstephen hemminger c->event); 296426b15dadSJamal Hadi Salim } 296526b15dadSJamal Hadi Salim 296626b15dadSJamal Hadi Salim return 0; 296726b15dadSJamal Hadi Salim 296826b15dadSJamal Hadi Salim } 296926b15dadSJamal Hadi Salim 29707deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 29717deb2264SThomas Graf { 29727deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 29737deb2264SThomas Graf } 29747deb2264SThomas Graf 297597a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 297697a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 297797a64b45SMasahide NAKAMURA { 297897a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 297997a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 298097a64b45SMasahide NAKAMURA 298179b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 298279b8b7f4SThomas Graf if (nlh == NULL) 298379b8b7f4SThomas Graf return -EMSGSIZE; 298497a64b45SMasahide NAKAMURA 29857b67c857SThomas Graf ur = nlmsg_data(nlh); 298697a64b45SMasahide NAKAMURA ur->proto = proto; 298797a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 298897a64b45SMasahide NAKAMURA 29891d1e34ddSDavid S. Miller if (addr) { 29901d1e34ddSDavid S. Miller int err = nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr); 29911d1e34ddSDavid S. Miller if (err) { 29929825069dSThomas Graf nlmsg_cancel(skb, nlh); 29931d1e34ddSDavid S. Miller return err; 29941d1e34ddSDavid S. Miller } 29951d1e34ddSDavid S. Miller } 2996*053c095aSJohannes Berg nlmsg_end(skb, nlh); 2997*053c095aSJohannes Berg return 0; 299897a64b45SMasahide NAKAMURA } 299997a64b45SMasahide NAKAMURA 3000db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 3001db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 300297a64b45SMasahide NAKAMURA { 300397a64b45SMasahide NAKAMURA struct sk_buff *skb; 300497a64b45SMasahide NAKAMURA 30057deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 300697a64b45SMasahide NAKAMURA if (skb == NULL) 300797a64b45SMasahide NAKAMURA return -ENOMEM; 300897a64b45SMasahide NAKAMURA 300997a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 301097a64b45SMasahide NAKAMURA BUG(); 301197a64b45SMasahide NAKAMURA 301221ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT); 301397a64b45SMasahide NAKAMURA } 301497a64b45SMasahide NAKAMURA 30153a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 30163a2dfbe8SMartin Willi { 30173a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 30183a2dfbe8SMartin Willi } 30193a2dfbe8SMartin Willi 30203a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 30213a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 30223a2dfbe8SMartin Willi { 30233a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 30243a2dfbe8SMartin Willi struct nlmsghdr *nlh; 30253a2dfbe8SMartin Willi 30263a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 30273a2dfbe8SMartin Willi if (nlh == NULL) 30283a2dfbe8SMartin Willi return -EMSGSIZE; 30293a2dfbe8SMartin Willi 30303a2dfbe8SMartin Willi um = nlmsg_data(nlh); 30313a2dfbe8SMartin Willi 30323a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 30333a2dfbe8SMartin Willi um->id.spi = x->id.spi; 30343a2dfbe8SMartin Willi um->id.family = x->props.family; 30353a2dfbe8SMartin Willi um->id.proto = x->id.proto; 30363a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 30373a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 30383a2dfbe8SMartin Willi um->new_sport = new_sport; 30393a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 30403a2dfbe8SMartin Willi um->reqid = x->props.reqid; 30413a2dfbe8SMartin Willi 3042*053c095aSJohannes Berg nlmsg_end(skb, nlh); 3043*053c095aSJohannes Berg return 0; 30443a2dfbe8SMartin Willi } 30453a2dfbe8SMartin Willi 30463a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 30473a2dfbe8SMartin Willi __be16 sport) 30483a2dfbe8SMartin Willi { 3049a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 30503a2dfbe8SMartin Willi struct sk_buff *skb; 30513a2dfbe8SMartin Willi 30523a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 30533a2dfbe8SMartin Willi return -EINVAL; 30543a2dfbe8SMartin Willi 30553a2dfbe8SMartin Willi if (!x->encap) 30563a2dfbe8SMartin Willi return -EINVAL; 30573a2dfbe8SMartin Willi 30583a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 30593a2dfbe8SMartin Willi if (skb == NULL) 30603a2dfbe8SMartin Willi return -ENOMEM; 30613a2dfbe8SMartin Willi 30623a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 30633a2dfbe8SMartin Willi BUG(); 30643a2dfbe8SMartin Willi 306521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING); 30663a2dfbe8SMartin Willi } 30673a2dfbe8SMartin Willi 30680f24558eSHoria Geanta static bool xfrm_is_alive(const struct km_event *c) 30690f24558eSHoria Geanta { 30700f24558eSHoria Geanta return (bool)xfrm_acquire_is_on(c->net); 30710f24558eSHoria Geanta } 30720f24558eSHoria Geanta 30731da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 30741da177e4SLinus Torvalds .id = "netlink", 30751da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 30761da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 30771da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 30781da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 307997a64b45SMasahide NAKAMURA .report = xfrm_send_report, 30805c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 30813a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 30820f24558eSHoria Geanta .is_alive = xfrm_is_alive, 30831da177e4SLinus Torvalds }; 30841da177e4SLinus Torvalds 3085a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 30861da177e4SLinus Torvalds { 3087be33690dSPatrick McHardy struct sock *nlsk; 3088a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = { 3089a31f2d17SPablo Neira Ayuso .groups = XFRMNLGRP_MAX, 3090a31f2d17SPablo Neira Ayuso .input = xfrm_netlink_rcv, 3091a31f2d17SPablo Neira Ayuso }; 3092be33690dSPatrick McHardy 30939f00d977SPablo Neira Ayuso nlsk = netlink_kernel_create(net, NETLINK_XFRM, &cfg); 3094be33690dSPatrick McHardy if (nlsk == NULL) 30951da177e4SLinus Torvalds return -ENOMEM; 3096d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 3097cf778b00SEric Dumazet rcu_assign_pointer(net->xfrm.nlsk, nlsk); 30981da177e4SLinus Torvalds return 0; 30991da177e4SLinus Torvalds } 31001da177e4SLinus Torvalds 3101d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 3102a6483b79SAlexey Dobriyan { 3103d79d792eSEric W. Biederman struct net *net; 3104d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 3105a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(net->xfrm.nlsk, NULL); 3106d79d792eSEric W. Biederman synchronize_net(); 3107d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 3108d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 3109a6483b79SAlexey Dobriyan } 3110a6483b79SAlexey Dobriyan 3111a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 3112a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 3113d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 3114a6483b79SAlexey Dobriyan }; 3115a6483b79SAlexey Dobriyan 3116a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 3117a6483b79SAlexey Dobriyan { 3118a6483b79SAlexey Dobriyan int rv; 3119a6483b79SAlexey Dobriyan 3120a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 3121a6483b79SAlexey Dobriyan 3122a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 3123a6483b79SAlexey Dobriyan if (rv < 0) 3124a6483b79SAlexey Dobriyan return rv; 3125a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 3126a6483b79SAlexey Dobriyan if (rv < 0) 3127a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 3128a6483b79SAlexey Dobriyan return rv; 3129a6483b79SAlexey Dobriyan } 3130a6483b79SAlexey Dobriyan 31311da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 31321da177e4SLinus Torvalds { 31331da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 3134a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 31351da177e4SLinus Torvalds } 31361da177e4SLinus Torvalds 31371da177e4SLinus Torvalds module_init(xfrm_user_init); 31381da177e4SLinus Torvalds module_exit(xfrm_user_exit); 31391da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 31404fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 3141f8cd5488SJamal Hadi Salim 3142