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 351a6509d9SHerbert Xu static inline int aead_len(struct xfrm_algo_aead *alg) 361a6509d9SHerbert Xu { 371a6509d9SHerbert Xu return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); 381a6509d9SHerbert Xu } 391a6509d9SHerbert Xu 405424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) 411da177e4SLinus Torvalds { 425424f32eSThomas Graf struct nlattr *rt = attrs[type]; 431da177e4SLinus Torvalds struct xfrm_algo *algp; 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds if (!rt) 461da177e4SLinus Torvalds return 0; 471da177e4SLinus Torvalds 485424f32eSThomas Graf algp = nla_data(rt); 490f99be0dSEric Dumazet if (nla_len(rt) < xfrm_alg_len(algp)) 5031c26852SHerbert Xu return -EINVAL; 5131c26852SHerbert Xu 521da177e4SLinus Torvalds switch (type) { 531da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 541da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 551da177e4SLinus Torvalds case XFRMA_ALG_COMP: 561da177e4SLinus Torvalds break; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds default: 591da177e4SLinus Torvalds return -EINVAL; 603ff50b79SStephen Hemminger } 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 631da177e4SLinus Torvalds return 0; 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 664447bb33SMartin Willi static int verify_auth_trunc(struct nlattr **attrs) 674447bb33SMartin Willi { 684447bb33SMartin Willi struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; 694447bb33SMartin Willi struct xfrm_algo_auth *algp; 704447bb33SMartin Willi 714447bb33SMartin Willi if (!rt) 724447bb33SMartin Willi return 0; 734447bb33SMartin Willi 744447bb33SMartin Willi algp = nla_data(rt); 754447bb33SMartin Willi if (nla_len(rt) < xfrm_alg_auth_len(algp)) 764447bb33SMartin Willi return -EINVAL; 774447bb33SMartin Willi 784447bb33SMartin Willi algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 794447bb33SMartin Willi return 0; 804447bb33SMartin Willi } 814447bb33SMartin Willi 821a6509d9SHerbert Xu static int verify_aead(struct nlattr **attrs) 831a6509d9SHerbert Xu { 841a6509d9SHerbert Xu struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; 851a6509d9SHerbert Xu struct xfrm_algo_aead *algp; 861a6509d9SHerbert Xu 871a6509d9SHerbert Xu if (!rt) 881a6509d9SHerbert Xu return 0; 891a6509d9SHerbert Xu 901a6509d9SHerbert Xu algp = nla_data(rt); 911a6509d9SHerbert Xu if (nla_len(rt) < aead_len(algp)) 921a6509d9SHerbert Xu return -EINVAL; 931a6509d9SHerbert Xu 941a6509d9SHerbert Xu algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 951a6509d9SHerbert Xu return 0; 961a6509d9SHerbert Xu } 971a6509d9SHerbert Xu 985424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, 99eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 100eb2971b6SMasahide NAKAMURA { 1015424f32eSThomas Graf struct nlattr *rt = attrs[type]; 102eb2971b6SMasahide NAKAMURA 103cf5cb79fSThomas Graf if (rt && addrp) 1045424f32eSThomas Graf *addrp = nla_data(rt); 105eb2971b6SMasahide NAKAMURA } 106df71837dSTrent Jaeger 1075424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs) 108df71837dSTrent Jaeger { 1095424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 110df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 111df71837dSTrent Jaeger 112df71837dSTrent Jaeger if (!rt) 113df71837dSTrent Jaeger return 0; 114df71837dSTrent Jaeger 1155424f32eSThomas Graf uctx = nla_data(rt); 116cf5cb79fSThomas Graf if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) 117df71837dSTrent Jaeger return -EINVAL; 118df71837dSTrent Jaeger 119df71837dSTrent Jaeger return 0; 120df71837dSTrent Jaeger } 121df71837dSTrent Jaeger 122d8647b79SSteffen Klassert static inline int verify_replay(struct xfrm_usersa_info *p, 123d8647b79SSteffen Klassert struct nlattr **attrs) 124d8647b79SSteffen Klassert { 125d8647b79SSteffen Klassert struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; 126ecd79187SMathias Krause struct xfrm_replay_state_esn *rs; 127d8647b79SSteffen Klassert 128ecd79187SMathias Krause if (p->flags & XFRM_STATE_ESN) { 129ecd79187SMathias Krause if (!rt) 1307833aa05SSteffen Klassert return -EINVAL; 1317833aa05SSteffen Klassert 132ecd79187SMathias Krause rs = nla_data(rt); 133ecd79187SMathias Krause 134ecd79187SMathias Krause if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) 135ecd79187SMathias Krause return -EINVAL; 136ecd79187SMathias Krause 137ecd79187SMathias Krause if (nla_len(rt) < xfrm_replay_state_esn_len(rs) && 138ecd79187SMathias Krause nla_len(rt) != sizeof(*rs)) 139ecd79187SMathias Krause return -EINVAL; 140ecd79187SMathias Krause } 141ecd79187SMathias Krause 142d8647b79SSteffen Klassert if (!rt) 143d8647b79SSteffen Klassert return 0; 144d8647b79SSteffen Klassert 14502aadf72SSteffen Klassert if (p->id.proto != IPPROTO_ESP) 14602aadf72SSteffen Klassert return -EINVAL; 14702aadf72SSteffen Klassert 148d8647b79SSteffen Klassert if (p->replay_window != 0) 149d8647b79SSteffen Klassert return -EINVAL; 150d8647b79SSteffen Klassert 151d8647b79SSteffen Klassert return 0; 152d8647b79SSteffen Klassert } 153df71837dSTrent Jaeger 1541da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1555424f32eSThomas Graf struct nlattr **attrs) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds int err; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds err = -EINVAL; 1601da177e4SLinus Torvalds switch (p->family) { 1611da177e4SLinus Torvalds case AF_INET: 1621da177e4SLinus Torvalds break; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds case AF_INET6: 165dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1661da177e4SLinus Torvalds break; 1671da177e4SLinus Torvalds #else 1681da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1691da177e4SLinus Torvalds goto out; 1701da177e4SLinus Torvalds #endif 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds default: 1731da177e4SLinus Torvalds goto out; 1743ff50b79SStephen Hemminger } 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds err = -EINVAL; 1771da177e4SLinus Torvalds switch (p->id.proto) { 1781da177e4SLinus Torvalds case IPPROTO_AH: 1794447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1804447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1811a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 18235a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 18335d2856bSMartin Willi attrs[XFRMA_ALG_COMP] || 18435d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 1851da177e4SLinus Torvalds goto out; 1861da177e4SLinus Torvalds break; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds case IPPROTO_ESP: 1891a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1901a6509d9SHerbert Xu goto out; 1911a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1924447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1931a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1941a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1951a6509d9SHerbert Xu goto out; 1961a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1974447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1981a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1991a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 2001da177e4SLinus Torvalds goto out; 20135d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] && 20235d2856bSMartin Willi p->mode != XFRM_MODE_TUNNEL) 20335d2856bSMartin Willi goto out; 2041da177e4SLinus Torvalds break; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds case IPPROTO_COMP: 20735a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 2081a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 20935a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2104447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 21135d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] || 21235d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 2131da177e4SLinus Torvalds goto out; 2141da177e4SLinus Torvalds break; 2151da177e4SLinus Torvalds 216dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 217e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 218e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 21935a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 22035a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2214447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 2221a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 22335a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 22435a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 22535a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 22635d2856bSMartin Willi attrs[XFRMA_TFCPAD] || 22735a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 228e23c7194SMasahide NAKAMURA goto out; 229e23c7194SMasahide NAKAMURA break; 230e23c7194SMasahide NAKAMURA #endif 231e23c7194SMasahide NAKAMURA 2321da177e4SLinus Torvalds default: 2331da177e4SLinus Torvalds goto out; 2343ff50b79SStephen Hemminger } 2351da177e4SLinus Torvalds 2361a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 2371a6509d9SHerbert Xu goto out; 2384447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2394447bb33SMartin Willi goto out; 24035a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2411da177e4SLinus Torvalds goto out; 24235a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2431da177e4SLinus Torvalds goto out; 24435a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2451da177e4SLinus Torvalds goto out; 24635a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 247df71837dSTrent Jaeger goto out; 248d8647b79SSteffen Klassert if ((err = verify_replay(p, attrs))) 249d8647b79SSteffen Klassert goto out; 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds err = -EINVAL; 2521da177e4SLinus Torvalds switch (p->mode) { 2537e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2547e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 255060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2560a69452cSDiego Beltrami case XFRM_MODE_BEET: 2571da177e4SLinus Torvalds break; 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds default: 2601da177e4SLinus Torvalds goto out; 2613ff50b79SStephen Hemminger } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds err = 0; 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds out: 2661da177e4SLinus Torvalds return err; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2706f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int), 2715424f32eSThomas Graf struct nlattr *rta) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2741da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds if (!rta) 2771da177e4SLinus Torvalds return 0; 2781da177e4SLinus Torvalds 2795424f32eSThomas Graf ualg = nla_data(rta); 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2821da177e4SLinus Torvalds if (!algo) 2831da177e4SLinus Torvalds return -ENOSYS; 2841da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2851da177e4SLinus Torvalds 2860f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2871da177e4SLinus Torvalds if (!p) 2881da177e4SLinus Torvalds return -ENOMEM; 2891da177e4SLinus Torvalds 29004ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2911da177e4SLinus Torvalds *algpp = p; 2921da177e4SLinus Torvalds return 0; 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 2954447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 2964447bb33SMartin Willi struct nlattr *rta) 2974447bb33SMartin Willi { 2984447bb33SMartin Willi struct xfrm_algo *ualg; 2994447bb33SMartin Willi struct xfrm_algo_auth *p; 3004447bb33SMartin Willi struct xfrm_algo_desc *algo; 3014447bb33SMartin Willi 3024447bb33SMartin Willi if (!rta) 3034447bb33SMartin Willi return 0; 3044447bb33SMartin Willi 3054447bb33SMartin Willi ualg = nla_data(rta); 3064447bb33SMartin Willi 3074447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3084447bb33SMartin Willi if (!algo) 3094447bb33SMartin Willi return -ENOSYS; 3104447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3114447bb33SMartin Willi 3124447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 3134447bb33SMartin Willi if (!p) 3144447bb33SMartin Willi return -ENOMEM; 3154447bb33SMartin Willi 3164447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3174447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 3184447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3194447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 3204447bb33SMartin Willi 3214447bb33SMartin Willi *algpp = p; 3224447bb33SMartin Willi return 0; 3234447bb33SMartin Willi } 3244447bb33SMartin Willi 3254447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 3264447bb33SMartin Willi struct nlattr *rta) 3274447bb33SMartin Willi { 3284447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 3294447bb33SMartin Willi struct xfrm_algo_desc *algo; 3304447bb33SMartin Willi 3314447bb33SMartin Willi if (!rta) 3324447bb33SMartin Willi return 0; 3334447bb33SMartin Willi 3344447bb33SMartin Willi ualg = nla_data(rta); 3354447bb33SMartin Willi 3364447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3374447bb33SMartin Willi if (!algo) 3384447bb33SMartin Willi return -ENOSYS; 339fa6dd8a2SNicolas Dichtel if ((ualg->alg_trunc_len / 8) > MAX_AH_AUTH_LEN || 340fa6dd8a2SNicolas Dichtel ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3414447bb33SMartin Willi return -EINVAL; 3424447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3434447bb33SMartin Willi 3444447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3454447bb33SMartin Willi if (!p) 3464447bb33SMartin Willi return -ENOMEM; 3474447bb33SMartin Willi 3484447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3494447bb33SMartin Willi if (!p->alg_trunc_len) 3504447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3514447bb33SMartin Willi 3524447bb33SMartin Willi *algpp = p; 3534447bb33SMartin Willi return 0; 3544447bb33SMartin Willi } 3554447bb33SMartin Willi 3561a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 3571a6509d9SHerbert Xu struct nlattr *rta) 3581a6509d9SHerbert Xu { 3591a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3601a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3611a6509d9SHerbert Xu 3621a6509d9SHerbert Xu if (!rta) 3631a6509d9SHerbert Xu return 0; 3641a6509d9SHerbert Xu 3651a6509d9SHerbert Xu ualg = nla_data(rta); 3661a6509d9SHerbert Xu 3671a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3681a6509d9SHerbert Xu if (!algo) 3691a6509d9SHerbert Xu return -ENOSYS; 3701a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 3711a6509d9SHerbert Xu 3721a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3731a6509d9SHerbert Xu if (!p) 3741a6509d9SHerbert Xu return -ENOMEM; 3751a6509d9SHerbert Xu 3761a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 3771a6509d9SHerbert Xu *algpp = p; 3781a6509d9SHerbert Xu return 0; 3791a6509d9SHerbert Xu } 3801a6509d9SHerbert Xu 381e2b19125SSteffen Klassert static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn, 382e2b19125SSteffen Klassert struct nlattr *rp) 383e2b19125SSteffen Klassert { 384e2b19125SSteffen Klassert struct xfrm_replay_state_esn *up; 385ecd79187SMathias Krause int ulen; 386e2b19125SSteffen Klassert 387e2b19125SSteffen Klassert if (!replay_esn || !rp) 388e2b19125SSteffen Klassert return 0; 389e2b19125SSteffen Klassert 390e2b19125SSteffen Klassert up = nla_data(rp); 391ecd79187SMathias Krause ulen = xfrm_replay_state_esn_len(up); 392e2b19125SSteffen Klassert 393ecd79187SMathias Krause if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen) 394e2b19125SSteffen Klassert return -EINVAL; 395e2b19125SSteffen Klassert 396e2b19125SSteffen Klassert return 0; 397e2b19125SSteffen Klassert } 398e2b19125SSteffen Klassert 399d8647b79SSteffen Klassert static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn, 400d8647b79SSteffen Klassert struct xfrm_replay_state_esn **preplay_esn, 401d8647b79SSteffen Klassert struct nlattr *rta) 402d8647b79SSteffen Klassert { 403d8647b79SSteffen Klassert struct xfrm_replay_state_esn *p, *pp, *up; 404ecd79187SMathias Krause int klen, ulen; 405d8647b79SSteffen Klassert 406d8647b79SSteffen Klassert if (!rta) 407d8647b79SSteffen Klassert return 0; 408d8647b79SSteffen Klassert 409d8647b79SSteffen Klassert up = nla_data(rta); 410ecd79187SMathias Krause klen = xfrm_replay_state_esn_len(up); 411ecd79187SMathias Krause ulen = nla_len(rta) >= klen ? klen : sizeof(*up); 412d8647b79SSteffen Klassert 413ecd79187SMathias Krause p = kzalloc(klen, GFP_KERNEL); 414d8647b79SSteffen Klassert if (!p) 415d8647b79SSteffen Klassert return -ENOMEM; 416d8647b79SSteffen Klassert 417ecd79187SMathias Krause pp = kzalloc(klen, GFP_KERNEL); 418d8647b79SSteffen Klassert if (!pp) { 419d8647b79SSteffen Klassert kfree(p); 420d8647b79SSteffen Klassert return -ENOMEM; 421d8647b79SSteffen Klassert } 422d8647b79SSteffen Klassert 423ecd79187SMathias Krause memcpy(p, up, ulen); 424ecd79187SMathias Krause memcpy(pp, up, ulen); 425ecd79187SMathias Krause 426d8647b79SSteffen Klassert *replay_esn = p; 427d8647b79SSteffen Klassert *preplay_esn = pp; 428d8647b79SSteffen Klassert 429d8647b79SSteffen Klassert return 0; 430d8647b79SSteffen Klassert } 431d8647b79SSteffen Klassert 432661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 433df71837dSTrent Jaeger { 434df71837dSTrent Jaeger int len = 0; 435df71837dSTrent Jaeger 436df71837dSTrent Jaeger if (xfrm_ctx) { 437df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 438df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 439df71837dSTrent Jaeger } 440df71837dSTrent Jaeger return len; 441df71837dSTrent Jaeger } 442df71837dSTrent Jaeger 4431da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 4441da177e4SLinus Torvalds { 4451da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 4461da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 4471da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 4481da177e4SLinus Torvalds x->props.mode = p->mode; 4491da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 4501da177e4SLinus Torvalds x->props.reqid = p->reqid; 4511da177e4SLinus Torvalds x->props.family = p->family; 45254489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 4531da177e4SLinus Torvalds x->props.flags = p->flags; 454196b0036SHerbert Xu 455ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 456196b0036SHerbert Xu x->sel.family = p->family; 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds 459d51d081dSJamal Hadi Salim /* 460d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 461d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 462d51d081dSJamal Hadi Salim * 463d51d081dSJamal Hadi Salim */ 464e3ac104dSMathias Krause static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs, 465e3ac104dSMathias Krause int update_esn) 466d51d081dSJamal Hadi Salim { 4675424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 468e3ac104dSMathias Krause struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL; 4695424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 4705424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 4715424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 472d51d081dSJamal Hadi Salim 473d8647b79SSteffen Klassert if (re) { 474d8647b79SSteffen Klassert struct xfrm_replay_state_esn *replay_esn; 475d8647b79SSteffen Klassert replay_esn = nla_data(re); 476d8647b79SSteffen Klassert memcpy(x->replay_esn, replay_esn, 477d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 478d8647b79SSteffen Klassert memcpy(x->preplay_esn, replay_esn, 479d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 480d8647b79SSteffen Klassert } 481d8647b79SSteffen Klassert 482d51d081dSJamal Hadi Salim if (rp) { 483d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 4845424f32eSThomas Graf replay = nla_data(rp); 485d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 486d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 487d51d081dSJamal Hadi Salim } 488d51d081dSJamal Hadi Salim 489d51d081dSJamal Hadi Salim if (lt) { 490d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 4915424f32eSThomas Graf ltime = nla_data(lt); 492d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 493d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 494d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 495d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 496d51d081dSJamal Hadi Salim } 497d51d081dSJamal Hadi Salim 498cf5cb79fSThomas Graf if (et) 4995424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 500d51d081dSJamal Hadi Salim 501cf5cb79fSThomas Graf if (rt) 5025424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 503d51d081dSJamal Hadi Salim } 504d51d081dSJamal Hadi Salim 505fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 506fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 5075424f32eSThomas Graf struct nlattr **attrs, 5081da177e4SLinus Torvalds int *errp) 5091da177e4SLinus Torvalds { 510fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 5111da177e4SLinus Torvalds int err = -ENOMEM; 5121da177e4SLinus Torvalds 5131da177e4SLinus Torvalds if (!x) 5141da177e4SLinus Torvalds goto error_no_put; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds copy_from_user_state(x, p); 5171da177e4SLinus Torvalds 518a947b0a9SNicolas Dichtel if (attrs[XFRMA_SA_EXTRA_FLAGS]) 519a947b0a9SNicolas Dichtel x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]); 520a947b0a9SNicolas Dichtel 5211a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 5221a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 5231a6509d9SHerbert Xu goto error; 5244447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 5254447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 5264447bb33SMartin Willi goto error; 5274447bb33SMartin Willi if (!x->props.aalgo) { 5284447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 52935a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 5301da177e4SLinus Torvalds goto error; 5314447bb33SMartin Willi } 5321da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 5331da177e4SLinus Torvalds xfrm_ealg_get_byname, 53435a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 5351da177e4SLinus Torvalds goto error; 5361da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 5371da177e4SLinus Torvalds xfrm_calg_get_byname, 53835a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 5391da177e4SLinus Torvalds goto error; 540fd21150aSThomas Graf 541fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 542fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 543fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 544fd21150aSThomas Graf if (x->encap == NULL) 5451da177e4SLinus Torvalds goto error; 546fd21150aSThomas Graf } 547fd21150aSThomas Graf 54835d2856bSMartin Willi if (attrs[XFRMA_TFCPAD]) 54935d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); 55035d2856bSMartin Willi 551fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 552fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 553fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 554fd21150aSThomas Graf if (x->coaddr == NULL) 555060f02a3SNoriaki TAKAMIYA goto error; 556fd21150aSThomas Graf } 557fd21150aSThomas Graf 5586f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark); 5596f26b61eSJamal Hadi Salim 560a454f0ccSWei Yongjun err = __xfrm_init_state(x, false); 5611da177e4SLinus Torvalds if (err) 5621da177e4SLinus Torvalds goto error; 5631da177e4SLinus Torvalds 564fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 565fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 566df71837dSTrent Jaeger goto error; 567df71837dSTrent Jaeger 568d8647b79SSteffen Klassert if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, 569d8647b79SSteffen Klassert attrs[XFRMA_REPLAY_ESN_VAL]))) 570d8647b79SSteffen Klassert goto error; 571d8647b79SSteffen Klassert 5721da177e4SLinus Torvalds x->km.seq = p->seq; 573b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 574d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 575b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 576d51d081dSJamal Hadi Salim 5779fdc4883SSteffen Klassert if ((err = xfrm_init_replay(x))) 5789fdc4883SSteffen Klassert goto error; 579d51d081dSJamal Hadi Salim 5809fdc4883SSteffen Klassert /* override default values from above */ 581e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 0); 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds return x; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds error: 5861da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 5871da177e4SLinus Torvalds xfrm_state_put(x); 5881da177e4SLinus Torvalds error_no_put: 5891da177e4SLinus Torvalds *errp = err; 5901da177e4SLinus Torvalds return NULL; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 59322e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 5945424f32eSThomas Graf struct nlattr **attrs) 5951da177e4SLinus Torvalds { 596fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 5977b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 5981da177e4SLinus Torvalds struct xfrm_state *x; 5991da177e4SLinus Torvalds int err; 60026b15dadSJamal Hadi Salim struct km_event c; 601e1760bd5SEric W. Biederman kuid_t loginuid = audit_get_loginuid(current); 602c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 603c53fa1edSPatrick McHardy u32 sid; 6041da177e4SLinus Torvalds 60535a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 6061da177e4SLinus Torvalds if (err) 6071da177e4SLinus Torvalds return err; 6081da177e4SLinus Torvalds 609fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 6101da177e4SLinus Torvalds if (!x) 6111da177e4SLinus Torvalds return err; 6121da177e4SLinus Torvalds 61326b15dadSJamal Hadi Salim xfrm_state_hold(x); 6141da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 6151da177e4SLinus Torvalds err = xfrm_state_add(x); 6161da177e4SLinus Torvalds else 6171da177e4SLinus Torvalds err = xfrm_state_update(x); 6181da177e4SLinus Torvalds 619c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 6202532386fSEric Paris xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); 621161a09e7SJoy Latten 6221da177e4SLinus Torvalds if (err < 0) { 6231da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 62421380b81SHerbert Xu __xfrm_state_put(x); 6257d6dfe1fSPatrick McHardy goto out; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 62826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 62915e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 630f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 63126b15dadSJamal Hadi Salim 63226b15dadSJamal Hadi Salim km_state_notify(x, &c); 6337d6dfe1fSPatrick McHardy out: 63426b15dadSJamal Hadi Salim xfrm_state_put(x); 6351da177e4SLinus Torvalds return err; 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds 638fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 639fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 6405424f32eSThomas Graf struct nlattr **attrs, 641eb2971b6SMasahide NAKAMURA int *errp) 642eb2971b6SMasahide NAKAMURA { 643eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 6446f26b61eSJamal Hadi Salim struct xfrm_mark m; 645eb2971b6SMasahide NAKAMURA int err; 6466f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 647eb2971b6SMasahide NAKAMURA 648eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 649eb2971b6SMasahide NAKAMURA err = -ESRCH; 6506f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family); 651eb2971b6SMasahide NAKAMURA } else { 652eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 653eb2971b6SMasahide NAKAMURA 65435a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 655eb2971b6SMasahide NAKAMURA if (!saddr) { 656eb2971b6SMasahide NAKAMURA err = -EINVAL; 657eb2971b6SMasahide NAKAMURA goto out; 658eb2971b6SMasahide NAKAMURA } 659eb2971b6SMasahide NAKAMURA 6609abbffeeSMasahide NAKAMURA err = -ESRCH; 6616f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark, 6626f26b61eSJamal Hadi Salim &p->daddr, saddr, 663221df1edSAlexey Dobriyan p->proto, p->family); 664eb2971b6SMasahide NAKAMURA } 665eb2971b6SMasahide NAKAMURA 666eb2971b6SMasahide NAKAMURA out: 667eb2971b6SMasahide NAKAMURA if (!x && errp) 668eb2971b6SMasahide NAKAMURA *errp = err; 669eb2971b6SMasahide NAKAMURA return x; 670eb2971b6SMasahide NAKAMURA } 671eb2971b6SMasahide NAKAMURA 67222e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 6735424f32eSThomas Graf struct nlattr **attrs) 6741da177e4SLinus Torvalds { 675fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6761da177e4SLinus Torvalds struct xfrm_state *x; 677eb2971b6SMasahide NAKAMURA int err = -ESRCH; 67826b15dadSJamal Hadi Salim struct km_event c; 6797b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 680e1760bd5SEric W. Biederman kuid_t loginuid = audit_get_loginuid(current); 681c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 682c53fa1edSPatrick McHardy u32 sid; 6831da177e4SLinus Torvalds 684fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 6851da177e4SLinus Torvalds if (x == NULL) 686eb2971b6SMasahide NAKAMURA return err; 6871da177e4SLinus Torvalds 6886f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 689c8c05a8eSCatherine Zhang goto out; 690c8c05a8eSCatherine Zhang 6911da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 692c8c05a8eSCatherine Zhang err = -EPERM; 693c8c05a8eSCatherine Zhang goto out; 6941da177e4SLinus Torvalds } 6951da177e4SLinus Torvalds 69626b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 697161a09e7SJoy Latten 698c8c05a8eSCatherine Zhang if (err < 0) 699c8c05a8eSCatherine Zhang goto out; 70026b15dadSJamal Hadi Salim 70126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 70215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 703f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 70426b15dadSJamal Hadi Salim km_state_notify(x, &c); 7051da177e4SLinus Torvalds 706c8c05a8eSCatherine Zhang out: 707c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 7082532386fSEric Paris xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); 709c8c05a8eSCatherine Zhang xfrm_state_put(x); 71026b15dadSJamal Hadi Salim return err; 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds 7131da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 7141da177e4SLinus Torvalds { 715f778a636SMathias Krause memset(p, 0, sizeof(*p)); 7161da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 7171da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 7181da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 7191da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 7201da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 72154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 7221da177e4SLinus Torvalds p->mode = x->props.mode; 7231da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 7241da177e4SLinus Torvalds p->reqid = x->props.reqid; 7251da177e4SLinus Torvalds p->family = x->props.family; 7261da177e4SLinus Torvalds p->flags = x->props.flags; 7271da177e4SLinus Torvalds p->seq = x->km.seq; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds struct xfrm_dump_info { 7311da177e4SLinus Torvalds struct sk_buff *in_skb; 7321da177e4SLinus Torvalds struct sk_buff *out_skb; 7331da177e4SLinus Torvalds u32 nlmsg_seq; 7341da177e4SLinus Torvalds u16 nlmsg_flags; 7351da177e4SLinus Torvalds }; 7361da177e4SLinus Torvalds 737c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 738c0144beaSThomas Graf { 739c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 740c0144beaSThomas Graf struct nlattr *attr; 74168325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 742c0144beaSThomas Graf 743c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 744c0144beaSThomas Graf if (attr == NULL) 745c0144beaSThomas Graf return -EMSGSIZE; 746c0144beaSThomas Graf 747c0144beaSThomas Graf uctx = nla_data(attr); 748c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 749c0144beaSThomas Graf uctx->len = ctx_size; 750c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 751c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 752c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 753c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 754c0144beaSThomas Graf 755c0144beaSThomas Graf return 0; 756c0144beaSThomas Graf } 757c0144beaSThomas Graf 7584447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 7594447bb33SMartin Willi { 7604447bb33SMartin Willi struct xfrm_algo *algo; 7614447bb33SMartin Willi struct nlattr *nla; 7624447bb33SMartin Willi 7634447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 7644447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 7654447bb33SMartin Willi if (!nla) 7664447bb33SMartin Willi return -EMSGSIZE; 7674447bb33SMartin Willi 7684447bb33SMartin Willi algo = nla_data(nla); 7694c87308bSMathias Krause strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name)); 7704447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 7714447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 7724447bb33SMartin Willi 7734447bb33SMartin Willi return 0; 7744447bb33SMartin Willi } 7754447bb33SMartin Willi 77668325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 77768325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 77868325d3bSHerbert Xu struct xfrm_usersa_info *p, 77968325d3bSHerbert Xu struct sk_buff *skb) 7801da177e4SLinus Torvalds { 7811d1e34ddSDavid S. Miller int ret = 0; 7821d1e34ddSDavid S. Miller 7831da177e4SLinus Torvalds copy_to_user_state(x, p); 7841da177e4SLinus Torvalds 785a947b0a9SNicolas Dichtel if (x->props.extra_flags) { 786a947b0a9SNicolas Dichtel ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS, 787a947b0a9SNicolas Dichtel x->props.extra_flags); 788a947b0a9SNicolas Dichtel if (ret) 789a947b0a9SNicolas Dichtel goto out; 790a947b0a9SNicolas Dichtel } 791a947b0a9SNicolas Dichtel 7921d1e34ddSDavid S. Miller if (x->coaddr) { 7931d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 7941d1e34ddSDavid S. Miller if (ret) 7951d1e34ddSDavid S. Miller goto out; 7961d1e34ddSDavid S. Miller } 7971d1e34ddSDavid S. Miller if (x->lastused) { 7981d1e34ddSDavid S. Miller ret = nla_put_u64(skb, XFRMA_LASTUSED, x->lastused); 7991d1e34ddSDavid S. Miller if (ret) 8001d1e34ddSDavid S. Miller goto out; 8011d1e34ddSDavid S. Miller } 8021d1e34ddSDavid S. Miller if (x->aead) { 8031d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 8041d1e34ddSDavid S. Miller if (ret) 8051d1e34ddSDavid S. Miller goto out; 8061d1e34ddSDavid S. Miller } 8071d1e34ddSDavid S. Miller if (x->aalg) { 8081d1e34ddSDavid S. Miller ret = copy_to_user_auth(x->aalg, skb); 8091d1e34ddSDavid S. Miller if (!ret) 8101d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC, 8111d1e34ddSDavid S. Miller xfrm_alg_auth_len(x->aalg), x->aalg); 8121d1e34ddSDavid S. Miller if (ret) 8131d1e34ddSDavid S. Miller goto out; 8141d1e34ddSDavid S. Miller } 8151d1e34ddSDavid S. Miller if (x->ealg) { 8161d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 8171d1e34ddSDavid S. Miller if (ret) 8181d1e34ddSDavid S. Miller goto out; 8191d1e34ddSDavid S. Miller } 8201d1e34ddSDavid S. Miller if (x->calg) { 8211d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 8221d1e34ddSDavid S. Miller if (ret) 8231d1e34ddSDavid S. Miller goto out; 8241d1e34ddSDavid S. Miller } 8251d1e34ddSDavid S. Miller if (x->encap) { 8261d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 8271d1e34ddSDavid S. Miller if (ret) 8281d1e34ddSDavid S. Miller goto out; 8291d1e34ddSDavid S. Miller } 8301d1e34ddSDavid S. Miller if (x->tfcpad) { 8311d1e34ddSDavid S. Miller ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad); 8321d1e34ddSDavid S. Miller if (ret) 8331d1e34ddSDavid S. Miller goto out; 8341d1e34ddSDavid S. Miller } 8351d1e34ddSDavid S. Miller ret = xfrm_mark_put(skb, &x->mark); 8361d1e34ddSDavid S. Miller if (ret) 8371d1e34ddSDavid S. Miller goto out; 8381d1e34ddSDavid S. Miller if (x->replay_esn) { 8391d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 840d0fde795SDavid S. Miller xfrm_replay_state_esn_len(x->replay_esn), 8411d1e34ddSDavid S. Miller x->replay_esn); 8421d1e34ddSDavid S. Miller if (ret) 8431d1e34ddSDavid S. Miller goto out; 8441d1e34ddSDavid S. Miller } 8451d1e34ddSDavid S. Miller if (x->security) 8461d1e34ddSDavid S. Miller ret = copy_sec_ctx(x->security, skb); 8471d1e34ddSDavid S. Miller out: 8481d1e34ddSDavid S. Miller return ret; 84968325d3bSHerbert Xu } 85068325d3bSHerbert Xu 85168325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 85268325d3bSHerbert Xu { 85368325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 85468325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 85568325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 85668325d3bSHerbert Xu struct xfrm_usersa_info *p; 85768325d3bSHerbert Xu struct nlmsghdr *nlh; 85868325d3bSHerbert Xu int err; 85968325d3bSHerbert Xu 86015e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, 86168325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 86268325d3bSHerbert Xu if (nlh == NULL) 86368325d3bSHerbert Xu return -EMSGSIZE; 86468325d3bSHerbert Xu 86568325d3bSHerbert Xu p = nlmsg_data(nlh); 86668325d3bSHerbert Xu 86768325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 8681d1e34ddSDavid S. Miller if (err) { 8699825069dSThomas Graf nlmsg_cancel(skb, nlh); 87068325d3bSHerbert Xu return err; 8711da177e4SLinus Torvalds } 8721d1e34ddSDavid S. Miller nlmsg_end(skb, nlh); 8731d1e34ddSDavid S. Miller return 0; 8741d1e34ddSDavid S. Miller } 8751da177e4SLinus Torvalds 8764c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 8774c563f76STimo Teras { 8784c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8794c563f76STimo Teras xfrm_state_walk_done(walk); 8804c563f76STimo Teras return 0; 8814c563f76STimo Teras } 8824c563f76STimo Teras 8831da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 8841da177e4SLinus Torvalds { 885fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 8864c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8871da177e4SLinus Torvalds struct xfrm_dump_info info; 8881da177e4SLinus Torvalds 8894c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 8904c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 8914c563f76STimo Teras 8921da177e4SLinus Torvalds info.in_skb = cb->skb; 8931da177e4SLinus Torvalds info.out_skb = skb; 8941da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 8951da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 8964c563f76STimo Teras 8974c563f76STimo Teras if (!cb->args[0]) { 8984c563f76STimo Teras cb->args[0] = 1; 8994c563f76STimo Teras xfrm_state_walk_init(walk, 0); 9004c563f76STimo Teras } 9014c563f76STimo Teras 902fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds return skb->len; 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 9081da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 9091da177e4SLinus Torvalds { 9101da177e4SLinus Torvalds struct xfrm_dump_info info; 9111da177e4SLinus Torvalds struct sk_buff *skb; 912864745d2SMathias Krause int err; 9131da177e4SLinus Torvalds 9147deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 9151da177e4SLinus Torvalds if (!skb) 9161da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds info.in_skb = in_skb; 9191da177e4SLinus Torvalds info.out_skb = skb; 9201da177e4SLinus Torvalds info.nlmsg_seq = seq; 9211da177e4SLinus Torvalds info.nlmsg_flags = 0; 9221da177e4SLinus Torvalds 923864745d2SMathias Krause err = dump_one_state(x, 0, &info); 924864745d2SMathias Krause if (err) { 9251da177e4SLinus Torvalds kfree_skb(skb); 926864745d2SMathias Krause return ERR_PTR(err); 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds 9291da177e4SLinus Torvalds return skb; 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds 9327deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 9337deb2264SThomas Graf { 9347deb2264SThomas Graf return NLMSG_ALIGN(4) 9357deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 9367deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 9377deb2264SThomas Graf } 9387deb2264SThomas Graf 939e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 94015e47304SEric W. Biederman u32 portid, u32 seq, u32 flags) 941ecfd6b18SJamal Hadi Salim { 9425a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 9435a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 9445a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 945ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 9461d1e34ddSDavid S. Miller int err; 947ecfd6b18SJamal Hadi Salim u32 *f; 948ecfd6b18SJamal Hadi Salim 94915e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 95025985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 951ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 952ecfd6b18SJamal Hadi Salim 953ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 954ecfd6b18SJamal Hadi Salim *f = flags; 955e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 9565a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 9575a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 9585a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 9595a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 9605a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 9615a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 9625a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 9635a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 964ecfd6b18SJamal Hadi Salim 9651d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 9661d1e34ddSDavid S. Miller if (!err) 9671d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 9681d1e34ddSDavid S. Miller if (err) { 9691d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 9701d1e34ddSDavid S. Miller return err; 9711d1e34ddSDavid S. Miller } 972ecfd6b18SJamal Hadi Salim 973ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 974ecfd6b18SJamal Hadi Salim } 975ecfd6b18SJamal Hadi Salim 976ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 9775424f32eSThomas Graf struct nlattr **attrs) 978ecfd6b18SJamal Hadi Salim { 979a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 980ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 9817b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 98215e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid; 983ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 984ecfd6b18SJamal Hadi Salim 9857deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 986ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 987ecfd6b18SJamal Hadi Salim return -ENOMEM; 988ecfd6b18SJamal Hadi Salim 98915e47304SEric W. Biederman if (build_spdinfo(r_skb, net, sportid, seq, *flags) < 0) 990ecfd6b18SJamal Hadi Salim BUG(); 991ecfd6b18SJamal Hadi Salim 99215e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); 993ecfd6b18SJamal Hadi Salim } 994ecfd6b18SJamal Hadi Salim 9957deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 9967deb2264SThomas Graf { 9977deb2264SThomas Graf return NLMSG_ALIGN(4) 9987deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 9997deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 10007deb2264SThomas Graf } 10017deb2264SThomas Graf 1002e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 100315e47304SEric W. Biederman u32 portid, u32 seq, u32 flags) 100428d8909bSJamal Hadi Salim { 1005af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 1006af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 100728d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 10081d1e34ddSDavid S. Miller int err; 100928d8909bSJamal Hadi Salim u32 *f; 101028d8909bSJamal Hadi Salim 101115e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 101225985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 101328d8909bSJamal Hadi Salim return -EMSGSIZE; 101428d8909bSJamal Hadi Salim 101528d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 101628d8909bSJamal Hadi Salim *f = flags; 1017e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 101828d8909bSJamal Hadi Salim 1019af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 1020af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 1021af11e316SJamal Hadi Salim 10221d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt); 10231d1e34ddSDavid S. Miller if (!err) 10241d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 10251d1e34ddSDavid S. Miller if (err) { 10261d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 10271d1e34ddSDavid S. Miller return err; 10281d1e34ddSDavid S. Miller } 102928d8909bSJamal Hadi Salim 103028d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 103128d8909bSJamal Hadi Salim } 103228d8909bSJamal Hadi Salim 103328d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 10345424f32eSThomas Graf struct nlattr **attrs) 103528d8909bSJamal Hadi Salim { 1036a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 103728d8909bSJamal Hadi Salim struct sk_buff *r_skb; 10387b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 103915e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid; 104028d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 104128d8909bSJamal Hadi Salim 10427deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 104328d8909bSJamal Hadi Salim if (r_skb == NULL) 104428d8909bSJamal Hadi Salim return -ENOMEM; 104528d8909bSJamal Hadi Salim 104615e47304SEric W. Biederman if (build_sadinfo(r_skb, net, sportid, seq, *flags) < 0) 104728d8909bSJamal Hadi Salim BUG(); 104828d8909bSJamal Hadi Salim 104915e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); 105028d8909bSJamal Hadi Salim } 105128d8909bSJamal Hadi Salim 105222e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 10535424f32eSThomas Graf struct nlattr **attrs) 10541da177e4SLinus Torvalds { 1055fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 10567b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 10571da177e4SLinus Torvalds struct xfrm_state *x; 10581da177e4SLinus Torvalds struct sk_buff *resp_skb; 1059eb2971b6SMasahide NAKAMURA int err = -ESRCH; 10601da177e4SLinus Torvalds 1061fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 10621da177e4SLinus Torvalds if (x == NULL) 10631da177e4SLinus Torvalds goto out_noput; 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 10661da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 10671da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 10681da177e4SLinus Torvalds } else { 106915e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds xfrm_state_put(x); 10721da177e4SLinus Torvalds out_noput: 10731da177e4SLinus Torvalds return err; 10741da177e4SLinus Torvalds } 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 10771da177e4SLinus Torvalds { 10781da177e4SLinus Torvalds switch (p->info.id.proto) { 10791da177e4SLinus Torvalds case IPPROTO_AH: 10801da177e4SLinus Torvalds case IPPROTO_ESP: 10811da177e4SLinus Torvalds break; 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds case IPPROTO_COMP: 10841da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 10851da177e4SLinus Torvalds if (p->max >= 0x10000) 10861da177e4SLinus Torvalds return -EINVAL; 10871da177e4SLinus Torvalds break; 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds default: 10901da177e4SLinus Torvalds return -EINVAL; 10913ff50b79SStephen Hemminger } 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds if (p->min > p->max) 10941da177e4SLinus Torvalds return -EINVAL; 10951da177e4SLinus Torvalds 10961da177e4SLinus Torvalds return 0; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 109922e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 11005424f32eSThomas Graf struct nlattr **attrs) 11011da177e4SLinus Torvalds { 1102fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 11031da177e4SLinus Torvalds struct xfrm_state *x; 11041da177e4SLinus Torvalds struct xfrm_userspi_info *p; 11051da177e4SLinus Torvalds struct sk_buff *resp_skb; 11061da177e4SLinus Torvalds xfrm_address_t *daddr; 11071da177e4SLinus Torvalds int family; 11081da177e4SLinus Torvalds int err; 11096f26b61eSJamal Hadi Salim u32 mark; 11106f26b61eSJamal Hadi Salim struct xfrm_mark m; 11111da177e4SLinus Torvalds 11127b67c857SThomas Graf p = nlmsg_data(nlh); 11131da177e4SLinus Torvalds err = verify_userspi_info(p); 11141da177e4SLinus Torvalds if (err) 11151da177e4SLinus Torvalds goto out_noput; 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds family = p->info.family; 11181da177e4SLinus Torvalds daddr = &p->info.id.daddr; 11191da177e4SLinus Torvalds 11201da177e4SLinus Torvalds x = NULL; 11216f26b61eSJamal Hadi Salim 11226f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 11231da177e4SLinus Torvalds if (p->info.seq) { 11246f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq); 112570e94e66SYOSHIFUJI Hideaki / 吉藤英明 if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) { 11261da177e4SLinus Torvalds xfrm_state_put(x); 11271da177e4SLinus Torvalds x = NULL; 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds if (!x) 11326f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid, 11331da177e4SLinus Torvalds p->info.id.proto, daddr, 11341da177e4SLinus Torvalds &p->info.saddr, 1, 11351da177e4SLinus Torvalds family); 11361da177e4SLinus Torvalds err = -ENOENT; 11371da177e4SLinus Torvalds if (x == NULL) 11381da177e4SLinus Torvalds goto out_noput; 11391da177e4SLinus Torvalds 1140658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 1141658b219eSHerbert Xu if (err) 1142658b219eSHerbert Xu goto out; 11431da177e4SLinus Torvalds 11441da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 11451da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 11461da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 11471da177e4SLinus Torvalds goto out; 11481da177e4SLinus Torvalds } 11491da177e4SLinus Torvalds 115015e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); 11511da177e4SLinus Torvalds 11521da177e4SLinus Torvalds out: 11531da177e4SLinus Torvalds xfrm_state_put(x); 11541da177e4SLinus Torvalds out_noput: 11551da177e4SLinus Torvalds return err; 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds 1158b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 11591da177e4SLinus Torvalds { 11601da177e4SLinus Torvalds switch (dir) { 11611da177e4SLinus Torvalds case XFRM_POLICY_IN: 11621da177e4SLinus Torvalds case XFRM_POLICY_OUT: 11631da177e4SLinus Torvalds case XFRM_POLICY_FWD: 11641da177e4SLinus Torvalds break; 11651da177e4SLinus Torvalds 11661da177e4SLinus Torvalds default: 11671da177e4SLinus Torvalds return -EINVAL; 11683ff50b79SStephen Hemminger } 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds return 0; 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds 1173b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1174f7b6983fSMasahide NAKAMURA { 1175f7b6983fSMasahide NAKAMURA switch (type) { 1176f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1177f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1178f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1179f7b6983fSMasahide NAKAMURA #endif 1180f7b6983fSMasahide NAKAMURA break; 1181f7b6983fSMasahide NAKAMURA 1182f7b6983fSMasahide NAKAMURA default: 1183f7b6983fSMasahide NAKAMURA return -EINVAL; 11843ff50b79SStephen Hemminger } 1185f7b6983fSMasahide NAKAMURA 1186f7b6983fSMasahide NAKAMURA return 0; 1187f7b6983fSMasahide NAKAMURA } 1188f7b6983fSMasahide NAKAMURA 11891da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 11901da177e4SLinus Torvalds { 11911da177e4SLinus Torvalds switch (p->share) { 11921da177e4SLinus Torvalds case XFRM_SHARE_ANY: 11931da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 11941da177e4SLinus Torvalds case XFRM_SHARE_USER: 11951da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 11961da177e4SLinus Torvalds break; 11971da177e4SLinus Torvalds 11981da177e4SLinus Torvalds default: 11991da177e4SLinus Torvalds return -EINVAL; 12003ff50b79SStephen Hemminger } 12011da177e4SLinus Torvalds 12021da177e4SLinus Torvalds switch (p->action) { 12031da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 12041da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 12051da177e4SLinus Torvalds break; 12061da177e4SLinus Torvalds 12071da177e4SLinus Torvalds default: 12081da177e4SLinus Torvalds return -EINVAL; 12093ff50b79SStephen Hemminger } 12101da177e4SLinus Torvalds 12111da177e4SLinus Torvalds switch (p->sel.family) { 12121da177e4SLinus Torvalds case AF_INET: 12131da177e4SLinus Torvalds break; 12141da177e4SLinus Torvalds 12151da177e4SLinus Torvalds case AF_INET6: 1216dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 12171da177e4SLinus Torvalds break; 12181da177e4SLinus Torvalds #else 12191da177e4SLinus Torvalds return -EAFNOSUPPORT; 12201da177e4SLinus Torvalds #endif 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds default: 12231da177e4SLinus Torvalds return -EINVAL; 12243ff50b79SStephen Hemminger } 12251da177e4SLinus Torvalds 12261da177e4SLinus Torvalds return verify_policy_dir(p->dir); 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds 12295424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1230df71837dSTrent Jaeger { 12315424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1232df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1233df71837dSTrent Jaeger 1234df71837dSTrent Jaeger if (!rt) 1235df71837dSTrent Jaeger return 0; 1236df71837dSTrent Jaeger 12375424f32eSThomas Graf uctx = nla_data(rt); 123803e1ad7bSPaul Moore return security_xfrm_policy_alloc(&pol->security, uctx); 1239df71837dSTrent Jaeger } 1240df71837dSTrent Jaeger 12411da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 12421da177e4SLinus Torvalds int nr) 12431da177e4SLinus Torvalds { 12441da177e4SLinus Torvalds int i; 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds xp->xfrm_nr = nr; 12471da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 12481da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 12491da177e4SLinus Torvalds 12501da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 12511da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 12521da177e4SLinus Torvalds sizeof(xfrm_address_t)); 12531da177e4SLinus Torvalds t->reqid = ut->reqid; 12541da177e4SLinus Torvalds t->mode = ut->mode; 12551da177e4SLinus Torvalds t->share = ut->share; 12561da177e4SLinus Torvalds t->optional = ut->optional; 12571da177e4SLinus Torvalds t->aalgos = ut->aalgos; 12581da177e4SLinus Torvalds t->ealgos = ut->ealgos; 12591da177e4SLinus Torvalds t->calgos = ut->calgos; 1260c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1261c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 12628511d01dSMiika Komu t->encap_family = ut->family; 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds 1266b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1267b4ad86bfSDavid S. Miller { 1268b4ad86bfSDavid S. Miller int i; 1269b4ad86bfSDavid S. Miller 1270b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1271b4ad86bfSDavid S. Miller return -EINVAL; 1272b4ad86bfSDavid S. Miller 1273b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1274b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1275b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1276b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1277b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1278b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1279b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1280b4ad86bfSDavid S. Miller */ 1281b4ad86bfSDavid S. Miller if (!ut[i].family) 1282b4ad86bfSDavid S. Miller ut[i].family = family; 1283b4ad86bfSDavid S. Miller 1284b4ad86bfSDavid S. Miller switch (ut[i].family) { 1285b4ad86bfSDavid S. Miller case AF_INET: 1286b4ad86bfSDavid S. Miller break; 1287dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1288b4ad86bfSDavid S. Miller case AF_INET6: 1289b4ad86bfSDavid S. Miller break; 1290b4ad86bfSDavid S. Miller #endif 1291b4ad86bfSDavid S. Miller default: 1292b4ad86bfSDavid S. Miller return -EINVAL; 12933ff50b79SStephen Hemminger } 1294b4ad86bfSDavid S. Miller } 1295b4ad86bfSDavid S. Miller 1296b4ad86bfSDavid S. Miller return 0; 1297b4ad86bfSDavid S. Miller } 1298b4ad86bfSDavid S. Miller 12995424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 13001da177e4SLinus Torvalds { 13015424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds if (!rt) { 13041da177e4SLinus Torvalds pol->xfrm_nr = 0; 13051da177e4SLinus Torvalds } else { 13065424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 13075424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1308b4ad86bfSDavid S. Miller int err; 13091da177e4SLinus Torvalds 1310b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1311b4ad86bfSDavid S. Miller if (err) 1312b4ad86bfSDavid S. Miller return err; 13131da177e4SLinus Torvalds 13145424f32eSThomas Graf copy_templates(pol, utmpl, nr); 13151da177e4SLinus Torvalds } 13161da177e4SLinus Torvalds return 0; 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds 13195424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1320f7b6983fSMasahide NAKAMURA { 13215424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1322f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1323b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1324f7b6983fSMasahide NAKAMURA int err; 1325f7b6983fSMasahide NAKAMURA 1326f7b6983fSMasahide NAKAMURA if (rt) { 13275424f32eSThomas Graf upt = nla_data(rt); 1328f7b6983fSMasahide NAKAMURA type = upt->type; 1329f7b6983fSMasahide NAKAMURA } 1330f7b6983fSMasahide NAKAMURA 1331f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1332f7b6983fSMasahide NAKAMURA if (err) 1333f7b6983fSMasahide NAKAMURA return err; 1334f7b6983fSMasahide NAKAMURA 1335f7b6983fSMasahide NAKAMURA *tp = type; 1336f7b6983fSMasahide NAKAMURA return 0; 1337f7b6983fSMasahide NAKAMURA } 1338f7b6983fSMasahide NAKAMURA 13391da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 13401da177e4SLinus Torvalds { 13411da177e4SLinus Torvalds xp->priority = p->priority; 13421da177e4SLinus Torvalds xp->index = p->index; 13431da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 13441da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 13451da177e4SLinus Torvalds xp->action = p->action; 13461da177e4SLinus Torvalds xp->flags = p->flags; 13471da177e4SLinus Torvalds xp->family = p->sel.family; 13481da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 13491da177e4SLinus Torvalds } 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 13521da177e4SLinus Torvalds { 13537b789836SMathias Krause memset(p, 0, sizeof(*p)); 13541da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 13551da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 13561da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 13571da177e4SLinus Torvalds p->priority = xp->priority; 13581da177e4SLinus Torvalds p->index = xp->index; 13591da177e4SLinus Torvalds p->sel.family = xp->family; 13601da177e4SLinus Torvalds p->dir = dir; 13611da177e4SLinus Torvalds p->action = xp->action; 13621da177e4SLinus Torvalds p->flags = xp->flags; 13631da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 13641da177e4SLinus Torvalds } 13651da177e4SLinus Torvalds 1366fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 13671da177e4SLinus Torvalds { 1368fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 13691da177e4SLinus Torvalds int err; 13701da177e4SLinus Torvalds 13711da177e4SLinus Torvalds if (!xp) { 13721da177e4SLinus Torvalds *errp = -ENOMEM; 13731da177e4SLinus Torvalds return NULL; 13741da177e4SLinus Torvalds } 13751da177e4SLinus Torvalds 13761da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1377df71837dSTrent Jaeger 137835a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1379f7b6983fSMasahide NAKAMURA if (err) 1380f7b6983fSMasahide NAKAMURA goto error; 1381f7b6983fSMasahide NAKAMURA 138235a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 138335a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1384f7b6983fSMasahide NAKAMURA if (err) 1385f7b6983fSMasahide NAKAMURA goto error; 13861da177e4SLinus Torvalds 1387295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark); 1388295fae56SJamal Hadi Salim 13891da177e4SLinus Torvalds return xp; 1390f7b6983fSMasahide NAKAMURA error: 1391f7b6983fSMasahide NAKAMURA *errp = err; 139212a169e7SHerbert Xu xp->walk.dead = 1; 139364c31b3fSWANG Cong xfrm_policy_destroy(xp); 1394f7b6983fSMasahide NAKAMURA return NULL; 13951da177e4SLinus Torvalds } 13961da177e4SLinus Torvalds 139722e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 13985424f32eSThomas Graf struct nlattr **attrs) 13991da177e4SLinus Torvalds { 1400fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 14017b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 14021da177e4SLinus Torvalds struct xfrm_policy *xp; 140326b15dadSJamal Hadi Salim struct km_event c; 14041da177e4SLinus Torvalds int err; 14051da177e4SLinus Torvalds int excl; 1406e1760bd5SEric W. Biederman kuid_t loginuid = audit_get_loginuid(current); 1407c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1408c53fa1edSPatrick McHardy u32 sid; 14091da177e4SLinus Torvalds 14101da177e4SLinus Torvalds err = verify_newpolicy_info(p); 14111da177e4SLinus Torvalds if (err) 14121da177e4SLinus Torvalds return err; 141335a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1414df71837dSTrent Jaeger if (err) 1415df71837dSTrent Jaeger return err; 14161da177e4SLinus Torvalds 1417fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 14181da177e4SLinus Torvalds if (!xp) 14191da177e4SLinus Torvalds return err; 14201da177e4SLinus Torvalds 142125985edcSLucas De Marchi /* shouldn't excl be based on nlh flags?? 142226b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 142326b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 142426b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 14251da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 14261da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 1427c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 14282532386fSEric Paris xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); 1429161a09e7SJoy Latten 14301da177e4SLinus Torvalds if (err) { 143103e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 14321da177e4SLinus Torvalds kfree(xp); 14331da177e4SLinus Torvalds return err; 14341da177e4SLinus Torvalds } 14351da177e4SLinus Torvalds 1436f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 143726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 143815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 143926b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 144026b15dadSJamal Hadi Salim 14411da177e4SLinus Torvalds xfrm_pol_put(xp); 14421da177e4SLinus Torvalds 14431da177e4SLinus Torvalds return 0; 14441da177e4SLinus Torvalds } 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 14471da177e4SLinus Torvalds { 14481da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 14491da177e4SLinus Torvalds int i; 14501da177e4SLinus Torvalds 14511da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 14521da177e4SLinus Torvalds return 0; 14531da177e4SLinus Torvalds 14541da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 14551da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 14561da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 14571da177e4SLinus Torvalds 14581f86840fSMathias Krause memset(up, 0, sizeof(*up)); 14591da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 14608511d01dSMiika Komu up->family = kp->encap_family; 14611da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 14621da177e4SLinus Torvalds up->reqid = kp->reqid; 14631da177e4SLinus Torvalds up->mode = kp->mode; 14641da177e4SLinus Torvalds up->share = kp->share; 14651da177e4SLinus Torvalds up->optional = kp->optional; 14661da177e4SLinus Torvalds up->aalgos = kp->aalgos; 14671da177e4SLinus Torvalds up->ealgos = kp->ealgos; 14681da177e4SLinus Torvalds up->calgos = kp->calgos; 14691da177e4SLinus Torvalds } 14701da177e4SLinus Torvalds 1471c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1472c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1473df71837dSTrent Jaeger } 1474df71837dSTrent Jaeger 14750d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 14760d681623SSerge Hallyn { 14770d681623SSerge Hallyn if (x->security) { 14780d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 14790d681623SSerge Hallyn } 14800d681623SSerge Hallyn return 0; 14810d681623SSerge Hallyn } 14820d681623SSerge Hallyn 14830d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 14840d681623SSerge Hallyn { 14851d1e34ddSDavid S. Miller if (xp->security) 14860d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 14870d681623SSerge Hallyn return 0; 14880d681623SSerge Hallyn } 1489cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1490cfbfd45aSThomas Graf { 1491cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1492cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1493cfbfd45aSThomas Graf #else 1494cfbfd45aSThomas Graf return 0; 1495cfbfd45aSThomas Graf #endif 1496cfbfd45aSThomas Graf } 14970d681623SSerge Hallyn 1498f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1499b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1500f7b6983fSMasahide NAKAMURA { 1501c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1502c0144beaSThomas Graf .type = type, 1503c0144beaSThomas Graf }; 1504f7b6983fSMasahide NAKAMURA 1505c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1506f7b6983fSMasahide NAKAMURA } 1507f7b6983fSMasahide NAKAMURA 1508f7b6983fSMasahide NAKAMURA #else 1509b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1510f7b6983fSMasahide NAKAMURA { 1511f7b6983fSMasahide NAKAMURA return 0; 1512f7b6983fSMasahide NAKAMURA } 1513f7b6983fSMasahide NAKAMURA #endif 1514f7b6983fSMasahide NAKAMURA 15151da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 15161da177e4SLinus Torvalds { 15171da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 15181da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 15191da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 15201da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 15211da177e4SLinus Torvalds struct nlmsghdr *nlh; 15221d1e34ddSDavid S. Miller int err; 15231da177e4SLinus Torvalds 152415e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, 152579b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 152679b8b7f4SThomas Graf if (nlh == NULL) 152779b8b7f4SThomas Graf return -EMSGSIZE; 15281da177e4SLinus Torvalds 15297b67c857SThomas Graf p = nlmsg_data(nlh); 15301da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 15311d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 15321d1e34ddSDavid S. Miller if (!err) 15331d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 15341d1e34ddSDavid S. Miller if (!err) 15351d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 15361d1e34ddSDavid S. Miller if (!err) 15371d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 15381d1e34ddSDavid S. Miller if (err) { 15391d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 15401d1e34ddSDavid S. Miller return err; 15411d1e34ddSDavid S. Miller } 15429825069dSThomas Graf nlmsg_end(skb, nlh); 15431da177e4SLinus Torvalds return 0; 15441da177e4SLinus Torvalds } 15451da177e4SLinus Torvalds 15464c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 15474c563f76STimo Teras { 15484c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 15494c563f76STimo Teras 15504c563f76STimo Teras xfrm_policy_walk_done(walk); 15514c563f76STimo Teras return 0; 15524c563f76STimo Teras } 15534c563f76STimo Teras 15541da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 15551da177e4SLinus Torvalds { 1556fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 15574c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 15581da177e4SLinus Torvalds struct xfrm_dump_info info; 15591da177e4SLinus Torvalds 15604c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 15614c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 15624c563f76STimo Teras 15631da177e4SLinus Torvalds info.in_skb = cb->skb; 15641da177e4SLinus Torvalds info.out_skb = skb; 15651da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 15661da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 15674c563f76STimo Teras 15684c563f76STimo Teras if (!cb->args[0]) { 15694c563f76STimo Teras cb->args[0] = 1; 15704c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 15714c563f76STimo Teras } 15724c563f76STimo Teras 1573fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds return skb->len; 15761da177e4SLinus Torvalds } 15771da177e4SLinus Torvalds 15781da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 15791da177e4SLinus Torvalds struct xfrm_policy *xp, 15801da177e4SLinus Torvalds int dir, u32 seq) 15811da177e4SLinus Torvalds { 15821da177e4SLinus Torvalds struct xfrm_dump_info info; 15831da177e4SLinus Torvalds struct sk_buff *skb; 1584c2546372SMathias Krause int err; 15851da177e4SLinus Torvalds 15867deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 15871da177e4SLinus Torvalds if (!skb) 15881da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 15891da177e4SLinus Torvalds 15901da177e4SLinus Torvalds info.in_skb = in_skb; 15911da177e4SLinus Torvalds info.out_skb = skb; 15921da177e4SLinus Torvalds info.nlmsg_seq = seq; 15931da177e4SLinus Torvalds info.nlmsg_flags = 0; 15941da177e4SLinus Torvalds 1595c2546372SMathias Krause err = dump_one_policy(xp, dir, 0, &info); 1596c2546372SMathias Krause if (err) { 15971da177e4SLinus Torvalds kfree_skb(skb); 1598c2546372SMathias Krause return ERR_PTR(err); 15991da177e4SLinus Torvalds } 16001da177e4SLinus Torvalds 16011da177e4SLinus Torvalds return skb; 16021da177e4SLinus Torvalds } 16031da177e4SLinus Torvalds 160422e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 16055424f32eSThomas Graf struct nlattr **attrs) 16061da177e4SLinus Torvalds { 1607fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 16081da177e4SLinus Torvalds struct xfrm_policy *xp; 16091da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1610b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 16111da177e4SLinus Torvalds int err; 161226b15dadSJamal Hadi Salim struct km_event c; 16131da177e4SLinus Torvalds int delete; 1614295fae56SJamal Hadi Salim struct xfrm_mark m; 1615295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 16161da177e4SLinus Torvalds 16177b67c857SThomas Graf p = nlmsg_data(nlh); 16181da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 16191da177e4SLinus Torvalds 162035a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1621f7b6983fSMasahide NAKAMURA if (err) 1622f7b6983fSMasahide NAKAMURA return err; 1623f7b6983fSMasahide NAKAMURA 16241da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 16251da177e4SLinus Torvalds if (err) 16261da177e4SLinus Torvalds return err; 16271da177e4SLinus Torvalds 16281da177e4SLinus Torvalds if (p->index) 1629295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err); 1630df71837dSTrent Jaeger else { 16315424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 163203e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1633df71837dSTrent Jaeger 163435a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1635df71837dSTrent Jaeger if (err) 1636df71837dSTrent Jaeger return err; 1637df71837dSTrent Jaeger 16382c8dd116SDenis V. Lunev ctx = NULL; 1639df71837dSTrent Jaeger if (rt) { 16405424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1641df71837dSTrent Jaeger 164203e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 164303e1ad7bSPaul Moore if (err) 1644df71837dSTrent Jaeger return err; 16452c8dd116SDenis V. Lunev } 1646295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel, 16476f26b61eSJamal Hadi Salim ctx, delete, &err); 164803e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1649df71837dSTrent Jaeger } 16501da177e4SLinus Torvalds if (xp == NULL) 16511da177e4SLinus Torvalds return -ENOENT; 16521da177e4SLinus Torvalds 16531da177e4SLinus Torvalds if (!delete) { 16541da177e4SLinus Torvalds struct sk_buff *resp_skb; 16551da177e4SLinus Torvalds 16561da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 16571da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 16581da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 16591da177e4SLinus Torvalds } else { 1660a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 166115e47304SEric W. Biederman NETLINK_CB(skb).portid); 16621da177e4SLinus Torvalds } 166326b15dadSJamal Hadi Salim } else { 1664e1760bd5SEric W. Biederman kuid_t loginuid = audit_get_loginuid(current); 1665c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1666c53fa1edSPatrick McHardy u32 sid; 16672532386fSEric Paris 1668c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 16692532386fSEric Paris xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, 16702532386fSEric Paris sid); 167113fcfbb0SDavid S. Miller 167213fcfbb0SDavid S. Miller if (err != 0) 1673c8c05a8eSCatherine Zhang goto out; 167413fcfbb0SDavid S. Miller 1675e7443892SHerbert Xu c.data.byid = p->index; 1676f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 167726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 167815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 167926b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 16801da177e4SLinus Torvalds } 16811da177e4SLinus Torvalds 1682c8c05a8eSCatherine Zhang out: 1683ef41aaa0SEric Paris xfrm_pol_put(xp); 16841da177e4SLinus Torvalds return err; 16851da177e4SLinus Torvalds } 16861da177e4SLinus Torvalds 168722e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 16885424f32eSThomas Graf struct nlattr **attrs) 16891da177e4SLinus Torvalds { 1690fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 169126b15dadSJamal Hadi Salim struct km_event c; 16927b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1693161a09e7SJoy Latten struct xfrm_audit audit_info; 16944aa2e62cSJoy Latten int err; 16951da177e4SLinus Torvalds 1696c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1697c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1698c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1699fc34acd3SAlexey Dobriyan err = xfrm_state_flush(net, p->proto, &audit_info); 17009e64cc95SJamal Hadi Salim if (err) { 17019e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */ 17029e64cc95SJamal Hadi Salim return 0; 1703069c474eSDavid S. Miller return err; 17049e64cc95SJamal Hadi Salim } 1705bf08867fSHerbert Xu c.data.proto = p->proto; 1706f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 170726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 170815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 17097067802eSAlexey Dobriyan c.net = net; 171026b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 171126b15dadSJamal Hadi Salim 17121da177e4SLinus Torvalds return 0; 17131da177e4SLinus Torvalds } 17141da177e4SLinus Torvalds 1715d8647b79SSteffen Klassert static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x) 17167deb2264SThomas Graf { 1717d8647b79SSteffen Klassert size_t replay_size = x->replay_esn ? 1718d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn) : 1719d8647b79SSteffen Klassert sizeof(struct xfrm_replay_state); 1720d8647b79SSteffen Klassert 17217deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 1722d8647b79SSteffen Klassert + nla_total_size(replay_size) 17237deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 17246f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 17257deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 17267deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 17277deb2264SThomas Graf } 1728d51d081dSJamal Hadi Salim 1729214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 1730d51d081dSJamal Hadi Salim { 1731d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1732d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 17331d1e34ddSDavid S. Miller int err; 1734d51d081dSJamal Hadi Salim 173515e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 173679b8b7f4SThomas Graf if (nlh == NULL) 173779b8b7f4SThomas Graf return -EMSGSIZE; 1738d51d081dSJamal Hadi Salim 17397b67c857SThomas Graf id = nlmsg_data(nlh); 17402b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1741d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1742d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1743d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 17442b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 17452b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1746d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1747d51d081dSJamal Hadi Salim 1748d0fde795SDavid S. Miller if (x->replay_esn) { 17491d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 1750d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn), 17511d1e34ddSDavid S. Miller x->replay_esn); 1752d0fde795SDavid S. Miller } else { 17531d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), 17541d1e34ddSDavid S. Miller &x->replay); 1755d0fde795SDavid S. Miller } 17561d1e34ddSDavid S. Miller if (err) 17571d1e34ddSDavid S. Miller goto out_cancel; 17581d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 17591d1e34ddSDavid S. Miller if (err) 17601d1e34ddSDavid S. Miller goto out_cancel; 1761d8647b79SSteffen Klassert 17621d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_RTHR) { 17631d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 17641d1e34ddSDavid S. Miller if (err) 17651d1e34ddSDavid S. Miller goto out_cancel; 17661d1e34ddSDavid S. Miller } 17671d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_ETHR) { 17681d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_ETIMER_THRESH, 17691d1e34ddSDavid S. Miller x->replay_maxage * 10 / HZ); 17701d1e34ddSDavid S. Miller if (err) 17711d1e34ddSDavid S. Miller goto out_cancel; 17721d1e34ddSDavid S. Miller } 17731d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 17741d1e34ddSDavid S. Miller if (err) 17751d1e34ddSDavid S. Miller goto out_cancel; 17766f26b61eSJamal Hadi Salim 17779825069dSThomas Graf return nlmsg_end(skb, nlh); 1778d51d081dSJamal Hadi Salim 17791d1e34ddSDavid S. Miller out_cancel: 17809825069dSThomas Graf nlmsg_cancel(skb, nlh); 17811d1e34ddSDavid S. Miller return err; 1782d51d081dSJamal Hadi Salim } 1783d51d081dSJamal Hadi Salim 178422e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 17855424f32eSThomas Graf struct nlattr **attrs) 1786d51d081dSJamal Hadi Salim { 1787fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1788d51d081dSJamal Hadi Salim struct xfrm_state *x; 1789d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1790d51d081dSJamal Hadi Salim int err; 1791d51d081dSJamal Hadi Salim struct km_event c; 17926f26b61eSJamal Hadi Salim u32 mark; 17936f26b61eSJamal Hadi Salim struct xfrm_mark m; 17947b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1795d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1796d51d081dSJamal Hadi Salim 17976f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 17986f26b61eSJamal Hadi Salim 17996f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); 1800d8647b79SSteffen Klassert if (x == NULL) 1801d51d081dSJamal Hadi Salim return -ESRCH; 1802d8647b79SSteffen Klassert 1803d8647b79SSteffen Klassert r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 1804d8647b79SSteffen Klassert if (r_skb == NULL) { 1805d8647b79SSteffen Klassert xfrm_state_put(x); 1806d8647b79SSteffen Klassert return -ENOMEM; 1807d51d081dSJamal Hadi Salim } 1808d51d081dSJamal Hadi Salim 1809d51d081dSJamal Hadi Salim /* 1810d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1811d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1812d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1813d51d081dSJamal Hadi Salim */ 1814d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1815d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1816d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 181715e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 1818d51d081dSJamal Hadi Salim 1819d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1820d51d081dSJamal Hadi Salim BUG(); 182115e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).portid); 1822d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1823d51d081dSJamal Hadi Salim xfrm_state_put(x); 1824d51d081dSJamal Hadi Salim return err; 1825d51d081dSJamal Hadi Salim } 1826d51d081dSJamal Hadi Salim 182722e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 18285424f32eSThomas Graf struct nlattr **attrs) 1829d51d081dSJamal Hadi Salim { 1830fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1831d51d081dSJamal Hadi Salim struct xfrm_state *x; 1832d51d081dSJamal Hadi Salim struct km_event c; 1833d51d081dSJamal Hadi Salim int err = - EINVAL; 18346f26b61eSJamal Hadi Salim u32 mark = 0; 18356f26b61eSJamal Hadi Salim struct xfrm_mark m; 18367b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 18375424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 1838d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 18395424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1840d51d081dSJamal Hadi Salim 1841d8647b79SSteffen Klassert if (!lt && !rp && !re) 1842d51d081dSJamal Hadi Salim return err; 1843d51d081dSJamal Hadi Salim 1844d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1845d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1846d51d081dSJamal Hadi Salim return err; 1847d51d081dSJamal Hadi Salim 18486f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 18496f26b61eSJamal Hadi Salim 18506f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1851d51d081dSJamal Hadi Salim if (x == NULL) 1852d51d081dSJamal Hadi Salim return -ESRCH; 1853d51d081dSJamal Hadi Salim 1854d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1855d51d081dSJamal Hadi Salim goto out; 1856d51d081dSJamal Hadi Salim 1857e2b19125SSteffen Klassert err = xfrm_replay_verify_len(x->replay_esn, rp); 1858e2b19125SSteffen Klassert if (err) 1859e2b19125SSteffen Klassert goto out; 1860e2b19125SSteffen Klassert 1861d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1862e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 1); 1863d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1864d51d081dSJamal Hadi Salim 1865d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1866d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 186715e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 1868d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1869d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1870d51d081dSJamal Hadi Salim err = 0; 1871d51d081dSJamal Hadi Salim out: 1872d51d081dSJamal Hadi Salim xfrm_state_put(x); 1873d51d081dSJamal Hadi Salim return err; 1874d51d081dSJamal Hadi Salim } 1875d51d081dSJamal Hadi Salim 187622e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 18775424f32eSThomas Graf struct nlattr **attrs) 18781da177e4SLinus Torvalds { 1879fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 188026b15dadSJamal Hadi Salim struct km_event c; 1881b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1882f7b6983fSMasahide NAKAMURA int err; 1883161a09e7SJoy Latten struct xfrm_audit audit_info; 188426b15dadSJamal Hadi Salim 188535a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1886f7b6983fSMasahide NAKAMURA if (err) 1887f7b6983fSMasahide NAKAMURA return err; 1888f7b6983fSMasahide NAKAMURA 1889c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1890c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1891c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1892fc34acd3SAlexey Dobriyan err = xfrm_policy_flush(net, type, &audit_info); 18932f1eb65fSJamal Hadi Salim if (err) { 18942f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */ 18952f1eb65fSJamal Hadi Salim return 0; 1896069c474eSDavid S. Miller return err; 18972f1eb65fSJamal Hadi Salim } 18982f1eb65fSJamal Hadi Salim 1899f7b6983fSMasahide NAKAMURA c.data.type = type; 1900f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 190126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 190215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 19037067802eSAlexey Dobriyan c.net = net; 190426b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 19051da177e4SLinus Torvalds return 0; 19061da177e4SLinus Torvalds } 19071da177e4SLinus Torvalds 190822e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 19095424f32eSThomas Graf struct nlattr **attrs) 19106c5c8ca7SJamal Hadi Salim { 1911fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 19126c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 19137b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 19146c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1915b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 19166c5c8ca7SJamal Hadi Salim int err = -ENOENT; 1917295fae56SJamal Hadi Salim struct xfrm_mark m; 1918295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 19196c5c8ca7SJamal Hadi Salim 192035a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1921f7b6983fSMasahide NAKAMURA if (err) 1922f7b6983fSMasahide NAKAMURA return err; 1923f7b6983fSMasahide NAKAMURA 1924c8bf4d04STimo Teräs err = verify_policy_dir(p->dir); 1925c8bf4d04STimo Teräs if (err) 1926c8bf4d04STimo Teräs return err; 1927c8bf4d04STimo Teräs 19286c5c8ca7SJamal Hadi Salim if (p->index) 1929295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err); 19306c5c8ca7SJamal Hadi Salim else { 19315424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 193203e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 19336c5c8ca7SJamal Hadi Salim 193435a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 19356c5c8ca7SJamal Hadi Salim if (err) 19366c5c8ca7SJamal Hadi Salim return err; 19376c5c8ca7SJamal Hadi Salim 19382c8dd116SDenis V. Lunev ctx = NULL; 19396c5c8ca7SJamal Hadi Salim if (rt) { 19405424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 19416c5c8ca7SJamal Hadi Salim 194203e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 194303e1ad7bSPaul Moore if (err) 19446c5c8ca7SJamal Hadi Salim return err; 19452c8dd116SDenis V. Lunev } 1946295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, 19476f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err); 194803e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 19496c5c8ca7SJamal Hadi Salim } 19506c5c8ca7SJamal Hadi Salim if (xp == NULL) 1951ef41aaa0SEric Paris return -ENOENT; 195203e1ad7bSPaul Moore 1953ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead)) 19546c5c8ca7SJamal Hadi Salim goto out; 19556c5c8ca7SJamal Hadi Salim 19566c5c8ca7SJamal Hadi Salim err = 0; 19576c5c8ca7SJamal Hadi Salim if (up->hard) { 1958e1760bd5SEric W. Biederman kuid_t loginuid = audit_get_loginuid(current); 1959c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1960c53fa1edSPatrick McHardy u32 sid; 1961c53fa1edSPatrick McHardy 1962c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 19636c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 19642532386fSEric Paris xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); 1965161a09e7SJoy Latten 19666c5c8ca7SJamal Hadi Salim } else { 19676c5c8ca7SJamal Hadi Salim // reset the timers here? 196862db5cfdSstephen hemminger WARN(1, "Dont know what to do with soft policy expire\n"); 19696c5c8ca7SJamal Hadi Salim } 1970c6bb8136SEric W. Biederman km_policy_expired(xp, p->dir, up->hard, nlh->nlmsg_pid); 19716c5c8ca7SJamal Hadi Salim 19726c5c8ca7SJamal Hadi Salim out: 19736c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 19746c5c8ca7SJamal Hadi Salim return err; 19756c5c8ca7SJamal Hadi Salim } 19766c5c8ca7SJamal Hadi Salim 197722e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 19785424f32eSThomas Graf struct nlattr **attrs) 197953bc6b4dSJamal Hadi Salim { 1980fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 198153bc6b4dSJamal Hadi Salim struct xfrm_state *x; 198253bc6b4dSJamal Hadi Salim int err; 19837b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 198453bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 19856f26b61eSJamal Hadi Salim struct xfrm_mark m; 1986928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m); 198753bc6b4dSJamal Hadi Salim 19886f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family); 198953bc6b4dSJamal Hadi Salim 19903a765aa5SDavid S. Miller err = -ENOENT; 199153bc6b4dSJamal Hadi Salim if (x == NULL) 199253bc6b4dSJamal Hadi Salim return err; 199353bc6b4dSJamal Hadi Salim 199453bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 19953a765aa5SDavid S. Miller err = -EINVAL; 199653bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 199753bc6b4dSJamal Hadi Salim goto out; 1998c6bb8136SEric W. Biederman km_state_expired(x, ue->hard, nlh->nlmsg_pid); 199953bc6b4dSJamal Hadi Salim 2000161a09e7SJoy Latten if (ue->hard) { 2001e1760bd5SEric W. Biederman kuid_t loginuid = audit_get_loginuid(current); 2002c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 2003c53fa1edSPatrick McHardy u32 sid; 2004c53fa1edSPatrick McHardy 2005c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 200653bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 20072532386fSEric Paris xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); 2008161a09e7SJoy Latten } 20093a765aa5SDavid S. Miller err = 0; 201053bc6b4dSJamal Hadi Salim out: 201153bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 201253bc6b4dSJamal Hadi Salim xfrm_state_put(x); 201353bc6b4dSJamal Hadi Salim return err; 201453bc6b4dSJamal Hadi Salim } 201553bc6b4dSJamal Hadi Salim 201622e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 20175424f32eSThomas Graf struct nlattr **attrs) 2018980ebd25SJamal Hadi Salim { 2019fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 2020980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 2021980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 2022980ebd25SJamal Hadi Salim int i; 20235424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 20246f26b61eSJamal Hadi Salim struct xfrm_mark mark; 2025980ebd25SJamal Hadi Salim 20267b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 2027fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 2028980ebd25SJamal Hadi Salim int err = -ENOMEM; 2029980ebd25SJamal Hadi Salim 2030980ebd25SJamal Hadi Salim if (!x) 2031d8eb9307SIlpo Järvinen goto nomem; 2032980ebd25SJamal Hadi Salim 20336f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark); 20346f26b61eSJamal Hadi Salim 2035980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 2036d8eb9307SIlpo Järvinen if (err) 2037d8eb9307SIlpo Järvinen goto bad_policy; 2038980ebd25SJamal Hadi Salim 2039980ebd25SJamal Hadi Salim /* build an XP */ 2040fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 2041d8eb9307SIlpo Järvinen if (!xp) 2042d8eb9307SIlpo Järvinen goto free_state; 2043980ebd25SJamal Hadi Salim 2044980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 2045980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 2046980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 20476f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m; 20486f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v; 20495424f32eSThomas Graf ut = nla_data(rt); 2050980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 2051980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 2052980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 2053980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 2054980ebd25SJamal Hadi Salim x->props.mode = t->mode; 2055980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 2056980ebd25SJamal Hadi Salim x->props.family = ut->family; 2057980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 2058980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 2059980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 2060980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 2061980ebd25SJamal Hadi Salim 2062980ebd25SJamal Hadi Salim } 2063980ebd25SJamal Hadi Salim 2064980ebd25SJamal Hadi Salim kfree(x); 2065980ebd25SJamal Hadi Salim kfree(xp); 2066980ebd25SJamal Hadi Salim 2067980ebd25SJamal Hadi Salim return 0; 2068d8eb9307SIlpo Järvinen 2069d8eb9307SIlpo Järvinen bad_policy: 207062db5cfdSstephen hemminger WARN(1, "BAD policy passed\n"); 2071d8eb9307SIlpo Järvinen free_state: 2072d8eb9307SIlpo Järvinen kfree(x); 2073d8eb9307SIlpo Järvinen nomem: 2074d8eb9307SIlpo Järvinen return err; 2075980ebd25SJamal Hadi Salim } 2076980ebd25SJamal Hadi Salim 20775c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 20785c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 207913c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 20805424f32eSThomas Graf struct nlattr **attrs, int *num) 20815c79de6eSShinta Sugimoto { 20825424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 20835c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 20845c79de6eSShinta Sugimoto int i, num_migrate; 20855c79de6eSShinta Sugimoto 208613c1d189SArnaud Ebalard if (k != NULL) { 208713c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 208813c1d189SArnaud Ebalard 208913c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 209013c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 209113c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 209213c1d189SArnaud Ebalard k->family = uk->family; 209313c1d189SArnaud Ebalard k->reserved = uk->reserved; 209413c1d189SArnaud Ebalard } 209513c1d189SArnaud Ebalard 20965424f32eSThomas Graf um = nla_data(rt); 20975424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 20985c79de6eSShinta Sugimoto 20995c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 21005c79de6eSShinta Sugimoto return -EINVAL; 21015c79de6eSShinta Sugimoto 21025c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 21035c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 21045c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 21055c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 21065c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 21075c79de6eSShinta Sugimoto 21085c79de6eSShinta Sugimoto ma->proto = um->proto; 21095c79de6eSShinta Sugimoto ma->mode = um->mode; 21105c79de6eSShinta Sugimoto ma->reqid = um->reqid; 21115c79de6eSShinta Sugimoto 21125c79de6eSShinta Sugimoto ma->old_family = um->old_family; 21135c79de6eSShinta Sugimoto ma->new_family = um->new_family; 21145c79de6eSShinta Sugimoto } 21155c79de6eSShinta Sugimoto 21165c79de6eSShinta Sugimoto *num = i; 21175c79de6eSShinta Sugimoto return 0; 21185c79de6eSShinta Sugimoto } 21195c79de6eSShinta Sugimoto 21205c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 21215424f32eSThomas Graf struct nlattr **attrs) 21225c79de6eSShinta Sugimoto { 21237b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 21245c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 212513c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 21265c79de6eSShinta Sugimoto u8 type; 21275c79de6eSShinta Sugimoto int err; 21285c79de6eSShinta Sugimoto int n = 0; 21295c79de6eSShinta Sugimoto 213035a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 2131cf5cb79fSThomas Graf return -EINVAL; 21325c79de6eSShinta Sugimoto 213313c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 213413c1d189SArnaud Ebalard 21355424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 21365c79de6eSShinta Sugimoto if (err) 21375c79de6eSShinta Sugimoto return err; 21385c79de6eSShinta Sugimoto 213913c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 21405c79de6eSShinta Sugimoto if (err) 21415c79de6eSShinta Sugimoto return err; 21425c79de6eSShinta Sugimoto 21435c79de6eSShinta Sugimoto if (!n) 21445c79de6eSShinta Sugimoto return 0; 21455c79de6eSShinta Sugimoto 214613c1d189SArnaud Ebalard xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); 21475c79de6eSShinta Sugimoto 21485c79de6eSShinta Sugimoto return 0; 21495c79de6eSShinta Sugimoto } 21505c79de6eSShinta Sugimoto #else 21515c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 21525424f32eSThomas Graf struct nlattr **attrs) 21535c79de6eSShinta Sugimoto { 21545c79de6eSShinta Sugimoto return -ENOPROTOOPT; 21555c79de6eSShinta Sugimoto } 21565c79de6eSShinta Sugimoto #endif 21575c79de6eSShinta Sugimoto 21585c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 2159183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) 21605c79de6eSShinta Sugimoto { 21615c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 21625c79de6eSShinta Sugimoto 21635c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 21645c79de6eSShinta Sugimoto um.proto = m->proto; 21655c79de6eSShinta Sugimoto um.mode = m->mode; 21665c79de6eSShinta Sugimoto um.reqid = m->reqid; 21675c79de6eSShinta Sugimoto um.old_family = m->old_family; 21685c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 21695c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 21705c79de6eSShinta Sugimoto um.new_family = m->new_family; 21715c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 21725c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 21735c79de6eSShinta Sugimoto 2174c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 21755c79de6eSShinta Sugimoto } 21765c79de6eSShinta Sugimoto 2177183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) 217813c1d189SArnaud Ebalard { 217913c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 218013c1d189SArnaud Ebalard 218113c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 218213c1d189SArnaud Ebalard uk.family = k->family; 218313c1d189SArnaud Ebalard uk.reserved = k->reserved; 218413c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 2185a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 218613c1d189SArnaud Ebalard 218713c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 218813c1d189SArnaud Ebalard } 218913c1d189SArnaud Ebalard 219013c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 21917deb2264SThomas Graf { 21927deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 219313c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 21947deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 21957deb2264SThomas Graf + userpolicy_type_attrsize(); 21967deb2264SThomas Graf } 21977deb2264SThomas Graf 2198183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, 2199183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k, 2200183cad12SDavid S. Miller const struct xfrm_selector *sel, u8 dir, u8 type) 22015c79de6eSShinta Sugimoto { 2202183cad12SDavid S. Miller const struct xfrm_migrate *mp; 22035c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 22045c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 22051d1e34ddSDavid S. Miller int i, err; 22065c79de6eSShinta Sugimoto 220779b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 220879b8b7f4SThomas Graf if (nlh == NULL) 220979b8b7f4SThomas Graf return -EMSGSIZE; 22105c79de6eSShinta Sugimoto 22117b67c857SThomas Graf pol_id = nlmsg_data(nlh); 22125c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 22135c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 22145c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 22155c79de6eSShinta Sugimoto pol_id->dir = dir; 22165c79de6eSShinta Sugimoto 22171d1e34ddSDavid S. Miller if (k != NULL) { 22181d1e34ddSDavid S. Miller err = copy_to_user_kmaddress(k, skb); 22191d1e34ddSDavid S. Miller if (err) 22201d1e34ddSDavid S. Miller goto out_cancel; 22211d1e34ddSDavid S. Miller } 22221d1e34ddSDavid S. Miller err = copy_to_user_policy_type(type, skb); 22231d1e34ddSDavid S. Miller if (err) 22241d1e34ddSDavid S. Miller goto out_cancel; 22255c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 22261d1e34ddSDavid S. Miller err = copy_to_user_migrate(mp, skb); 22271d1e34ddSDavid S. Miller if (err) 22281d1e34ddSDavid S. Miller goto out_cancel; 22295c79de6eSShinta Sugimoto } 22305c79de6eSShinta Sugimoto 22319825069dSThomas Graf return nlmsg_end(skb, nlh); 22321d1e34ddSDavid S. Miller 22331d1e34ddSDavid S. Miller out_cancel: 22349825069dSThomas Graf nlmsg_cancel(skb, nlh); 22351d1e34ddSDavid S. Miller return err; 22365c79de6eSShinta Sugimoto } 22375c79de6eSShinta Sugimoto 2238183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2239183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2240183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 22415c79de6eSShinta Sugimoto { 2242a6483b79SAlexey Dobriyan struct net *net = &init_net; 22435c79de6eSShinta Sugimoto struct sk_buff *skb; 22445c79de6eSShinta Sugimoto 224513c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 22465c79de6eSShinta Sugimoto if (skb == NULL) 22475c79de6eSShinta Sugimoto return -ENOMEM; 22485c79de6eSShinta Sugimoto 22495c79de6eSShinta Sugimoto /* build migrate */ 225013c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 22515c79de6eSShinta Sugimoto BUG(); 22525c79de6eSShinta Sugimoto 2253a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 22545c79de6eSShinta Sugimoto } 22555c79de6eSShinta Sugimoto #else 2256183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2257183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2258183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 22595c79de6eSShinta Sugimoto { 22605c79de6eSShinta Sugimoto return -ENOPROTOOPT; 22615c79de6eSShinta Sugimoto } 22625c79de6eSShinta Sugimoto #endif 2263d51d081dSJamal Hadi Salim 2264a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2265492b558bSThomas Graf 2266492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 226766f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2268492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2269492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2270492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2271492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2272492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2273492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2274980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 227553bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2276492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 227766f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 22786c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2279492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2280a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2281d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2282d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 228397a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 22845c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2285a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2286a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 22871da177e4SLinus Torvalds }; 22881da177e4SLinus Torvalds 2289492b558bSThomas Graf #undef XMSGSIZE 2290492b558bSThomas Graf 2291cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 2292c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)}, 2293c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)}, 2294c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64}, 2295c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 22961a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2297cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2298cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2299cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2300cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2301cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2302cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2303cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2304cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2305cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2306cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2307cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2308cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2309cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2310cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 231113c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 23126f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 231335d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2314d8647b79SSteffen Klassert [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 2315a947b0a9SNicolas Dichtel [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 2316cf5cb79fSThomas Graf }; 2317cf5cb79fSThomas Graf 2318*05600a79SMathias Krause static const struct xfrm_link { 23195424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 23201da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 23214c563f76STimo Teras int (*done)(struct netlink_callback *); 2322492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2323492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2324492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2325492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 23264c563f76STimo Teras .dump = xfrm_dump_sa, 23274c563f76STimo Teras .done = xfrm_dump_sa_done }, 2328492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2329492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2330492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 23314c563f76STimo Teras .dump = xfrm_dump_policy, 23324c563f76STimo Teras .done = xfrm_dump_policy_done }, 2333492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2334980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 233553bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2336492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2337492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 23386c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2339492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2340492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2341d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2342d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 23435c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 234428d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2345ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 23461da177e4SLinus Torvalds }; 23471da177e4SLinus Torvalds 23481d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 23491da177e4SLinus Torvalds { 2350a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 235135a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 2352*05600a79SMathias Krause const struct xfrm_link *link; 2353a7bd9a45SThomas Graf int type, err; 23541da177e4SLinus Torvalds 23551da177e4SLinus Torvalds type = nlh->nlmsg_type; 23561da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 23571d00a4ebSThomas Graf return -EINVAL; 23581da177e4SLinus Torvalds 23591da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 23601da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 23611da177e4SLinus Torvalds 23621da177e4SLinus Torvalds /* All operations require privileges, even GET */ 2363df008c91SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 23641d00a4ebSThomas Graf return -EPERM; 23651da177e4SLinus Torvalds 2366492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2367492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2368b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) { 23691da177e4SLinus Torvalds if (link->dump == NULL) 23701d00a4ebSThomas Graf return -EINVAL; 23711da177e4SLinus Torvalds 237280d326faSPablo Neira Ayuso { 237380d326faSPablo Neira Ayuso struct netlink_dump_control c = { 237480d326faSPablo Neira Ayuso .dump = link->dump, 237580d326faSPablo Neira Ayuso .done = link->done, 237680d326faSPablo Neira Ayuso }; 237780d326faSPablo Neira Ayuso return netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c); 237880d326faSPablo Neira Ayuso } 23791da177e4SLinus Torvalds } 23801da177e4SLinus Torvalds 238135a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2382cf5cb79fSThomas Graf xfrma_policy); 2383a7bd9a45SThomas Graf if (err < 0) 2384a7bd9a45SThomas Graf return err; 23851da177e4SLinus Torvalds 23861da177e4SLinus Torvalds if (link->doit == NULL) 23871d00a4ebSThomas Graf return -EINVAL; 23881da177e4SLinus Torvalds 23895424f32eSThomas Graf return link->doit(skb, nlh, attrs); 23901da177e4SLinus Torvalds } 23911da177e4SLinus Torvalds 2392cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 23931da177e4SLinus Torvalds { 23944a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 2395cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 23964a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 23971da177e4SLinus Torvalds } 23981da177e4SLinus Torvalds 23997deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 24007deb2264SThomas Graf { 24016f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) 24026f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)); 24037deb2264SThomas Graf } 24047deb2264SThomas Graf 2405214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 24061da177e4SLinus Torvalds { 24071da177e4SLinus Torvalds struct xfrm_user_expire *ue; 24081da177e4SLinus Torvalds struct nlmsghdr *nlh; 24091d1e34ddSDavid S. Miller int err; 24101da177e4SLinus Torvalds 241115e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 241279b8b7f4SThomas Graf if (nlh == NULL) 241379b8b7f4SThomas Graf return -EMSGSIZE; 24141da177e4SLinus Torvalds 24157b67c857SThomas Graf ue = nlmsg_data(nlh); 24161da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2417d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 24181da177e4SLinus Torvalds 24191d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 24201d1e34ddSDavid S. Miller if (err) 24211d1e34ddSDavid S. Miller return err; 24226f26b61eSJamal Hadi Salim 24239825069dSThomas Graf return nlmsg_end(skb, nlh); 24241da177e4SLinus Torvalds } 24251da177e4SLinus Torvalds 2426214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) 24271da177e4SLinus Torvalds { 2428fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 24291da177e4SLinus Torvalds struct sk_buff *skb; 24301da177e4SLinus Torvalds 24317deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 24321da177e4SLinus Torvalds if (skb == NULL) 24331da177e4SLinus Torvalds return -ENOMEM; 24341da177e4SLinus Torvalds 24356f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) { 24366f26b61eSJamal Hadi Salim kfree_skb(skb); 24376f26b61eSJamal Hadi Salim return -EMSGSIZE; 24386f26b61eSJamal Hadi Salim } 24391da177e4SLinus Torvalds 2440a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 24411da177e4SLinus Torvalds } 24421da177e4SLinus Torvalds 2443214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) 2444d51d081dSJamal Hadi Salim { 2445fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2446d51d081dSJamal Hadi Salim struct sk_buff *skb; 2447d51d081dSJamal Hadi Salim 2448d8647b79SSteffen Klassert skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 2449d51d081dSJamal Hadi Salim if (skb == NULL) 2450d51d081dSJamal Hadi Salim return -ENOMEM; 2451d51d081dSJamal Hadi Salim 2452d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2453d51d081dSJamal Hadi Salim BUG(); 2454d51d081dSJamal Hadi Salim 2455a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2456d51d081dSJamal Hadi Salim } 2457d51d081dSJamal Hadi Salim 2458214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c) 245926b15dadSJamal Hadi Salim { 24607067802eSAlexey Dobriyan struct net *net = c->net; 246126b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 246226b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 246326b15dadSJamal Hadi Salim struct sk_buff *skb; 24647deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 246526b15dadSJamal Hadi Salim 24667deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 246726b15dadSJamal Hadi Salim if (skb == NULL) 246826b15dadSJamal Hadi Salim return -ENOMEM; 246926b15dadSJamal Hadi Salim 247015e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 247179b8b7f4SThomas Graf if (nlh == NULL) { 247279b8b7f4SThomas Graf kfree_skb(skb); 247379b8b7f4SThomas Graf return -EMSGSIZE; 247479b8b7f4SThomas Graf } 247526b15dadSJamal Hadi Salim 24767b67c857SThomas Graf p = nlmsg_data(nlh); 2477bf08867fSHerbert Xu p->proto = c->data.proto; 247826b15dadSJamal Hadi Salim 24799825069dSThomas Graf nlmsg_end(skb, nlh); 248026b15dadSJamal Hadi Salim 2481a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 248226b15dadSJamal Hadi Salim } 248326b15dadSJamal Hadi Salim 24847deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 248526b15dadSJamal Hadi Salim { 24867deb2264SThomas Graf size_t l = 0; 24871a6509d9SHerbert Xu if (x->aead) 24881a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 24894447bb33SMartin Willi if (x->aalg) { 24904447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 24914447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 24924447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 24934447bb33SMartin Willi } 249426b15dadSJamal Hadi Salim if (x->ealg) 24950f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 249626b15dadSJamal Hadi Salim if (x->calg) 24977deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 249826b15dadSJamal Hadi Salim if (x->encap) 24997deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 250035d2856bSMartin Willi if (x->tfcpad) 250135d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad)); 2502d8647b79SSteffen Klassert if (x->replay_esn) 2503d8647b79SSteffen Klassert l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn)); 250468325d3bSHerbert Xu if (x->security) 250568325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 250668325d3bSHerbert Xu x->security->ctx_len); 250768325d3bSHerbert Xu if (x->coaddr) 250868325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 2509a947b0a9SNicolas Dichtel if (x->props.extra_flags) 2510a947b0a9SNicolas Dichtel l += nla_total_size(sizeof(x->props.extra_flags)); 251168325d3bSHerbert Xu 2512d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2513d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 251426b15dadSJamal Hadi Salim 251526b15dadSJamal Hadi Salim return l; 251626b15dadSJamal Hadi Salim } 251726b15dadSJamal Hadi Salim 2518214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) 251926b15dadSJamal Hadi Salim { 2520fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 252126b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 25220603eac0SHerbert Xu struct xfrm_usersa_id *id; 252326b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 252426b15dadSJamal Hadi Salim struct sk_buff *skb; 252526b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 25261d1e34ddSDavid S. Miller int headlen, err; 25270603eac0SHerbert Xu 25280603eac0SHerbert Xu headlen = sizeof(*p); 25290603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 25307deb2264SThomas Graf len += nla_total_size(headlen); 25310603eac0SHerbert Xu headlen = sizeof(*id); 25326f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 25330603eac0SHerbert Xu } 25347deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 253526b15dadSJamal Hadi Salim 25367deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 253726b15dadSJamal Hadi Salim if (skb == NULL) 253826b15dadSJamal Hadi Salim return -ENOMEM; 253926b15dadSJamal Hadi Salim 254015e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); 25411d1e34ddSDavid S. Miller err = -EMSGSIZE; 254279b8b7f4SThomas Graf if (nlh == NULL) 25431d1e34ddSDavid S. Miller goto out_free_skb; 254426b15dadSJamal Hadi Salim 25457b67c857SThomas Graf p = nlmsg_data(nlh); 25460603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2547c0144beaSThomas Graf struct nlattr *attr; 2548c0144beaSThomas Graf 25497b67c857SThomas Graf id = nlmsg_data(nlh); 25500603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 25510603eac0SHerbert Xu id->spi = x->id.spi; 25520603eac0SHerbert Xu id->family = x->props.family; 25530603eac0SHerbert Xu id->proto = x->id.proto; 25540603eac0SHerbert Xu 2555c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 25561d1e34ddSDavid S. Miller err = -EMSGSIZE; 2557c0144beaSThomas Graf if (attr == NULL) 25581d1e34ddSDavid S. Miller goto out_free_skb; 2559c0144beaSThomas Graf 2560c0144beaSThomas Graf p = nla_data(attr); 25610603eac0SHerbert Xu } 25621d1e34ddSDavid S. Miller err = copy_to_user_state_extra(x, p, skb); 25631d1e34ddSDavid S. Miller if (err) 25641d1e34ddSDavid S. Miller goto out_free_skb; 256526b15dadSJamal Hadi Salim 25669825069dSThomas Graf nlmsg_end(skb, nlh); 256726b15dadSJamal Hadi Salim 2568a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 256926b15dadSJamal Hadi Salim 25701d1e34ddSDavid S. Miller out_free_skb: 257126b15dadSJamal Hadi Salim kfree_skb(skb); 25721d1e34ddSDavid S. Miller return err; 257326b15dadSJamal Hadi Salim } 257426b15dadSJamal Hadi Salim 2575214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) 257626b15dadSJamal Hadi Salim { 257726b15dadSJamal Hadi Salim 257826b15dadSJamal Hadi Salim switch (c->event) { 2579f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 258026b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2581d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2582d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2583f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2584f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2585f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 258626b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2587f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 258826b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 258926b15dadSJamal Hadi Salim default: 259062db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n", 259162db5cfdSstephen hemminger c->event); 259226b15dadSJamal Hadi Salim break; 259326b15dadSJamal Hadi Salim } 259426b15dadSJamal Hadi Salim 259526b15dadSJamal Hadi Salim return 0; 259626b15dadSJamal Hadi Salim 259726b15dadSJamal Hadi Salim } 259826b15dadSJamal Hadi Salim 25997deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 26007deb2264SThomas Graf struct xfrm_policy *xp) 26017deb2264SThomas Graf { 26027deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 26037deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 26046f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 26057deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 26067deb2264SThomas Graf + userpolicy_type_attrsize(); 26077deb2264SThomas Graf } 26087deb2264SThomas Graf 26091da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 261065e0736bSFan Du struct xfrm_tmpl *xt, struct xfrm_policy *xp) 26111da177e4SLinus Torvalds { 26121d1e34ddSDavid S. Miller __u32 seq = xfrm_get_acqseq(); 26131da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 26141da177e4SLinus Torvalds struct nlmsghdr *nlh; 26151d1e34ddSDavid S. Miller int err; 26161da177e4SLinus Torvalds 261779b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 261879b8b7f4SThomas Graf if (nlh == NULL) 261979b8b7f4SThomas Graf return -EMSGSIZE; 26201da177e4SLinus Torvalds 26217b67c857SThomas Graf ua = nlmsg_data(nlh); 26221da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 26231da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 26241da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 262565e0736bSFan Du copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT); 26261da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 26271da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 26281da177e4SLinus Torvalds ua->calgos = xt->calgos; 26291da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 26301da177e4SLinus Torvalds 26311d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 26321d1e34ddSDavid S. Miller if (!err) 26331d1e34ddSDavid S. Miller err = copy_to_user_state_sec_ctx(x, skb); 26341d1e34ddSDavid S. Miller if (!err) 26351d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 26361d1e34ddSDavid S. Miller if (!err) 26371d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 26381d1e34ddSDavid S. Miller if (err) { 26391d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 26401d1e34ddSDavid S. Miller return err; 26411d1e34ddSDavid S. Miller } 26421da177e4SLinus Torvalds 26439825069dSThomas Graf return nlmsg_end(skb, nlh); 26441da177e4SLinus Torvalds } 26451da177e4SLinus Torvalds 26461da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 264765e0736bSFan Du struct xfrm_policy *xp) 26481da177e4SLinus Torvalds { 2649a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 26501da177e4SLinus Torvalds struct sk_buff *skb; 26511da177e4SLinus Torvalds 26527deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 26531da177e4SLinus Torvalds if (skb == NULL) 26541da177e4SLinus Torvalds return -ENOMEM; 26551da177e4SLinus Torvalds 265665e0736bSFan Du if (build_acquire(skb, x, xt, xp) < 0) 26571da177e4SLinus Torvalds BUG(); 26581da177e4SLinus Torvalds 2659a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 26601da177e4SLinus Torvalds } 26611da177e4SLinus Torvalds 26621da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 26631da177e4SLinus Torvalds * or more templates. 26641da177e4SLinus Torvalds */ 2665cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 26661da177e4SLinus Torvalds u8 *data, int len, int *dir) 26671da177e4SLinus Torvalds { 2668fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 26691da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 26701da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 26711da177e4SLinus Torvalds struct xfrm_policy *xp; 26721da177e4SLinus Torvalds int nr; 26731da177e4SLinus Torvalds 2674cb969f07SVenkat Yekkirala switch (sk->sk_family) { 26751da177e4SLinus Torvalds case AF_INET: 26761da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 26771da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 26781da177e4SLinus Torvalds return NULL; 26791da177e4SLinus Torvalds } 26801da177e4SLinus Torvalds break; 2681dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 26821da177e4SLinus Torvalds case AF_INET6: 26831da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 26841da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 26851da177e4SLinus Torvalds return NULL; 26861da177e4SLinus Torvalds } 26871da177e4SLinus Torvalds break; 26881da177e4SLinus Torvalds #endif 26891da177e4SLinus Torvalds default: 26901da177e4SLinus Torvalds *dir = -EINVAL; 26911da177e4SLinus Torvalds return NULL; 26921da177e4SLinus Torvalds } 26931da177e4SLinus Torvalds 26941da177e4SLinus Torvalds *dir = -EINVAL; 26951da177e4SLinus Torvalds 26961da177e4SLinus Torvalds if (len < sizeof(*p) || 26971da177e4SLinus Torvalds verify_newpolicy_info(p)) 26981da177e4SLinus Torvalds return NULL; 26991da177e4SLinus Torvalds 27001da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2701b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 27021da177e4SLinus Torvalds return NULL; 27031da177e4SLinus Torvalds 2704a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2705a4f1bac6SHerbert Xu return NULL; 2706a4f1bac6SHerbert Xu 27072f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC); 27081da177e4SLinus Torvalds if (xp == NULL) { 27091da177e4SLinus Torvalds *dir = -ENOBUFS; 27101da177e4SLinus Torvalds return NULL; 27111da177e4SLinus Torvalds } 27121da177e4SLinus Torvalds 27131da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2714f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 27151da177e4SLinus Torvalds copy_templates(xp, ut, nr); 27161da177e4SLinus Torvalds 27171da177e4SLinus Torvalds *dir = p->dir; 27181da177e4SLinus Torvalds 27191da177e4SLinus Torvalds return xp; 27201da177e4SLinus Torvalds } 27211da177e4SLinus Torvalds 27227deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 27237deb2264SThomas Graf { 27247deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 27257deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 27267deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 2727295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 27287deb2264SThomas Graf + userpolicy_type_attrsize(); 27297deb2264SThomas Graf } 27307deb2264SThomas Graf 27311da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2732214e005bSDavid S. Miller int dir, const struct km_event *c) 27331da177e4SLinus Torvalds { 27341da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 2735d51d081dSJamal Hadi Salim int hard = c->data.hard; 27361d1e34ddSDavid S. Miller struct nlmsghdr *nlh; 27371d1e34ddSDavid S. Miller int err; 27381da177e4SLinus Torvalds 273915e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 274079b8b7f4SThomas Graf if (nlh == NULL) 274179b8b7f4SThomas Graf return -EMSGSIZE; 27421da177e4SLinus Torvalds 27437b67c857SThomas Graf upe = nlmsg_data(nlh); 27441da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 27451d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 27461d1e34ddSDavid S. Miller if (!err) 27471d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 27481d1e34ddSDavid S. Miller if (!err) 27491d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 27501d1e34ddSDavid S. Miller if (!err) 27511d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 27521d1e34ddSDavid S. Miller if (err) { 27531d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 27541d1e34ddSDavid S. Miller return err; 27551d1e34ddSDavid S. Miller } 27561da177e4SLinus Torvalds upe->hard = !!hard; 27571da177e4SLinus Torvalds 27589825069dSThomas Graf return nlmsg_end(skb, nlh); 27591da177e4SLinus Torvalds } 27601da177e4SLinus Torvalds 2761214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 27621da177e4SLinus Torvalds { 2763fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 27641da177e4SLinus Torvalds struct sk_buff *skb; 27651da177e4SLinus Torvalds 27667deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 27671da177e4SLinus Torvalds if (skb == NULL) 27681da177e4SLinus Torvalds return -ENOMEM; 27691da177e4SLinus Torvalds 2770d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 27711da177e4SLinus Torvalds BUG(); 27721da177e4SLinus Torvalds 2773a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 27741da177e4SLinus Torvalds } 27751da177e4SLinus Torvalds 2776214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) 277726b15dadSJamal Hadi Salim { 27781d1e34ddSDavid S. Miller int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 2779fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 278026b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 27810603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 278226b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 278326b15dadSJamal Hadi Salim struct sk_buff *skb; 27841d1e34ddSDavid S. Miller int headlen, err; 27850603eac0SHerbert Xu 27860603eac0SHerbert Xu headlen = sizeof(*p); 27870603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 27887deb2264SThomas Graf len += nla_total_size(headlen); 27890603eac0SHerbert Xu headlen = sizeof(*id); 27900603eac0SHerbert Xu } 2791cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 2792295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 27937deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 279426b15dadSJamal Hadi Salim 27957deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 279626b15dadSJamal Hadi Salim if (skb == NULL) 279726b15dadSJamal Hadi Salim return -ENOMEM; 279826b15dadSJamal Hadi Salim 279915e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); 28001d1e34ddSDavid S. Miller err = -EMSGSIZE; 280179b8b7f4SThomas Graf if (nlh == NULL) 28021d1e34ddSDavid S. Miller goto out_free_skb; 280326b15dadSJamal Hadi Salim 28047b67c857SThomas Graf p = nlmsg_data(nlh); 28050603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2806c0144beaSThomas Graf struct nlattr *attr; 2807c0144beaSThomas Graf 28087b67c857SThomas Graf id = nlmsg_data(nlh); 28090603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 28100603eac0SHerbert Xu id->dir = dir; 28110603eac0SHerbert Xu if (c->data.byid) 28120603eac0SHerbert Xu id->index = xp->index; 28130603eac0SHerbert Xu else 28140603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 28150603eac0SHerbert Xu 2816c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 28171d1e34ddSDavid S. Miller err = -EMSGSIZE; 2818c0144beaSThomas Graf if (attr == NULL) 28191d1e34ddSDavid S. Miller goto out_free_skb; 2820c0144beaSThomas Graf 2821c0144beaSThomas Graf p = nla_data(attr); 28220603eac0SHerbert Xu } 282326b15dadSJamal Hadi Salim 282426b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 28251d1e34ddSDavid S. Miller err = copy_to_user_tmpl(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 goto out_free_skb; 2832295fae56SJamal Hadi Salim 28339825069dSThomas Graf nlmsg_end(skb, nlh); 283426b15dadSJamal Hadi Salim 2835a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 283626b15dadSJamal Hadi Salim 28371d1e34ddSDavid S. Miller out_free_skb: 283826b15dadSJamal Hadi Salim kfree_skb(skb); 28391d1e34ddSDavid S. Miller return err; 284026b15dadSJamal Hadi Salim } 284126b15dadSJamal Hadi Salim 2842214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c) 284326b15dadSJamal Hadi Salim { 28447067802eSAlexey Dobriyan struct net *net = c->net; 284526b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 284626b15dadSJamal Hadi Salim struct sk_buff *skb; 28471d1e34ddSDavid S. Miller int err; 284826b15dadSJamal Hadi Salim 28497deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 285026b15dadSJamal Hadi Salim if (skb == NULL) 285126b15dadSJamal Hadi Salim return -ENOMEM; 285226b15dadSJamal Hadi Salim 285315e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 28541d1e34ddSDavid S. Miller err = -EMSGSIZE; 285579b8b7f4SThomas Graf if (nlh == NULL) 28561d1e34ddSDavid S. Miller goto out_free_skb; 28571d1e34ddSDavid S. Miller err = copy_to_user_policy_type(c->data.type, skb); 28581d1e34ddSDavid S. Miller if (err) 28591d1e34ddSDavid S. Miller goto out_free_skb; 286026b15dadSJamal Hadi Salim 28619825069dSThomas Graf nlmsg_end(skb, nlh); 286226b15dadSJamal Hadi Salim 2863a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 286426b15dadSJamal Hadi Salim 28651d1e34ddSDavid S. Miller out_free_skb: 286626b15dadSJamal Hadi Salim kfree_skb(skb); 28671d1e34ddSDavid S. Miller return err; 286826b15dadSJamal Hadi Salim } 286926b15dadSJamal Hadi Salim 2870214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 287126b15dadSJamal Hadi Salim { 287226b15dadSJamal Hadi Salim 287326b15dadSJamal Hadi Salim switch (c->event) { 2874f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2875f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2876f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 287726b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2878f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 287926b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2880f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 288126b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 288226b15dadSJamal Hadi Salim default: 288362db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n", 288462db5cfdSstephen hemminger c->event); 288526b15dadSJamal Hadi Salim } 288626b15dadSJamal Hadi Salim 288726b15dadSJamal Hadi Salim return 0; 288826b15dadSJamal Hadi Salim 288926b15dadSJamal Hadi Salim } 289026b15dadSJamal Hadi Salim 28917deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 28927deb2264SThomas Graf { 28937deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 28947deb2264SThomas Graf } 28957deb2264SThomas Graf 289697a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 289797a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 289897a64b45SMasahide NAKAMURA { 289997a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 290097a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 290197a64b45SMasahide NAKAMURA 290279b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 290379b8b7f4SThomas Graf if (nlh == NULL) 290479b8b7f4SThomas Graf return -EMSGSIZE; 290597a64b45SMasahide NAKAMURA 29067b67c857SThomas Graf ur = nlmsg_data(nlh); 290797a64b45SMasahide NAKAMURA ur->proto = proto; 290897a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 290997a64b45SMasahide NAKAMURA 29101d1e34ddSDavid S. Miller if (addr) { 29111d1e34ddSDavid S. Miller int err = nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr); 29121d1e34ddSDavid S. Miller if (err) { 29139825069dSThomas Graf nlmsg_cancel(skb, nlh); 29141d1e34ddSDavid S. Miller return err; 29151d1e34ddSDavid S. Miller } 29161d1e34ddSDavid S. Miller } 29171d1e34ddSDavid S. Miller return nlmsg_end(skb, nlh); 291897a64b45SMasahide NAKAMURA } 291997a64b45SMasahide NAKAMURA 2920db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 2921db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 292297a64b45SMasahide NAKAMURA { 292397a64b45SMasahide NAKAMURA struct sk_buff *skb; 292497a64b45SMasahide NAKAMURA 29257deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 292697a64b45SMasahide NAKAMURA if (skb == NULL) 292797a64b45SMasahide NAKAMURA return -ENOMEM; 292897a64b45SMasahide NAKAMURA 292997a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 293097a64b45SMasahide NAKAMURA BUG(); 293197a64b45SMasahide NAKAMURA 2932a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 293397a64b45SMasahide NAKAMURA } 293497a64b45SMasahide NAKAMURA 29353a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 29363a2dfbe8SMartin Willi { 29373a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 29383a2dfbe8SMartin Willi } 29393a2dfbe8SMartin Willi 29403a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 29413a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 29423a2dfbe8SMartin Willi { 29433a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 29443a2dfbe8SMartin Willi struct nlmsghdr *nlh; 29453a2dfbe8SMartin Willi 29463a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 29473a2dfbe8SMartin Willi if (nlh == NULL) 29483a2dfbe8SMartin Willi return -EMSGSIZE; 29493a2dfbe8SMartin Willi 29503a2dfbe8SMartin Willi um = nlmsg_data(nlh); 29513a2dfbe8SMartin Willi 29523a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 29533a2dfbe8SMartin Willi um->id.spi = x->id.spi; 29543a2dfbe8SMartin Willi um->id.family = x->props.family; 29553a2dfbe8SMartin Willi um->id.proto = x->id.proto; 29563a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 29573a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 29583a2dfbe8SMartin Willi um->new_sport = new_sport; 29593a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 29603a2dfbe8SMartin Willi um->reqid = x->props.reqid; 29613a2dfbe8SMartin Willi 29623a2dfbe8SMartin Willi return nlmsg_end(skb, nlh); 29633a2dfbe8SMartin Willi } 29643a2dfbe8SMartin Willi 29653a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 29663a2dfbe8SMartin Willi __be16 sport) 29673a2dfbe8SMartin Willi { 2968a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 29693a2dfbe8SMartin Willi struct sk_buff *skb; 29703a2dfbe8SMartin Willi 29713a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 29723a2dfbe8SMartin Willi return -EINVAL; 29733a2dfbe8SMartin Willi 29743a2dfbe8SMartin Willi if (!x->encap) 29753a2dfbe8SMartin Willi return -EINVAL; 29763a2dfbe8SMartin Willi 29773a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 29783a2dfbe8SMartin Willi if (skb == NULL) 29793a2dfbe8SMartin Willi return -ENOMEM; 29803a2dfbe8SMartin Willi 29813a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 29823a2dfbe8SMartin Willi BUG(); 29833a2dfbe8SMartin Willi 2984a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 29853a2dfbe8SMartin Willi } 29863a2dfbe8SMartin Willi 29871da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 29881da177e4SLinus Torvalds .id = "netlink", 29891da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 29901da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 29911da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 29921da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 299397a64b45SMasahide NAKAMURA .report = xfrm_send_report, 29945c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 29953a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 29961da177e4SLinus Torvalds }; 29971da177e4SLinus Torvalds 2998a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 29991da177e4SLinus Torvalds { 3000be33690dSPatrick McHardy struct sock *nlsk; 3001a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = { 3002a31f2d17SPablo Neira Ayuso .groups = XFRMNLGRP_MAX, 3003a31f2d17SPablo Neira Ayuso .input = xfrm_netlink_rcv, 3004a31f2d17SPablo Neira Ayuso }; 3005be33690dSPatrick McHardy 30069f00d977SPablo Neira Ayuso nlsk = netlink_kernel_create(net, NETLINK_XFRM, &cfg); 3007be33690dSPatrick McHardy if (nlsk == NULL) 30081da177e4SLinus Torvalds return -ENOMEM; 3009d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 3010cf778b00SEric Dumazet rcu_assign_pointer(net->xfrm.nlsk, nlsk); 30111da177e4SLinus Torvalds return 0; 30121da177e4SLinus Torvalds } 30131da177e4SLinus Torvalds 3014d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 3015a6483b79SAlexey Dobriyan { 3016d79d792eSEric W. Biederman struct net *net; 3017d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 3018a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(net->xfrm.nlsk, NULL); 3019d79d792eSEric W. Biederman synchronize_net(); 3020d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 3021d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 3022a6483b79SAlexey Dobriyan } 3023a6483b79SAlexey Dobriyan 3024a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 3025a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 3026d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 3027a6483b79SAlexey Dobriyan }; 3028a6483b79SAlexey Dobriyan 3029a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 3030a6483b79SAlexey Dobriyan { 3031a6483b79SAlexey Dobriyan int rv; 3032a6483b79SAlexey Dobriyan 3033a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 3034a6483b79SAlexey Dobriyan 3035a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 3036a6483b79SAlexey Dobriyan if (rv < 0) 3037a6483b79SAlexey Dobriyan return rv; 3038a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 3039a6483b79SAlexey Dobriyan if (rv < 0) 3040a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 3041a6483b79SAlexey Dobriyan return rv; 3042a6483b79SAlexey Dobriyan } 3043a6483b79SAlexey Dobriyan 30441da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 30451da177e4SLinus Torvalds { 30461da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 3047a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 30481da177e4SLinus Torvalds } 30491da177e4SLinus Torvalds 30501da177e4SLinus Torvalds module_init(xfrm_user_init); 30511da177e4SLinus Torvalds module_exit(xfrm_user_exit); 30521da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 30534fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 3054f8cd5488SJamal Hadi Salim 3055