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]; 126d8647b79SSteffen Klassert 1277833aa05SSteffen Klassert if ((p->flags & XFRM_STATE_ESN) && !rt) 1287833aa05SSteffen Klassert return -EINVAL; 1297833aa05SSteffen Klassert 130d8647b79SSteffen Klassert if (!rt) 131d8647b79SSteffen Klassert return 0; 132d8647b79SSteffen Klassert 13302aadf72SSteffen Klassert if (p->id.proto != IPPROTO_ESP) 13402aadf72SSteffen Klassert return -EINVAL; 13502aadf72SSteffen Klassert 136d8647b79SSteffen Klassert if (p->replay_window != 0) 137d8647b79SSteffen Klassert return -EINVAL; 138d8647b79SSteffen Klassert 139d8647b79SSteffen Klassert return 0; 140d8647b79SSteffen Klassert } 141df71837dSTrent Jaeger 1421da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1435424f32eSThomas Graf struct nlattr **attrs) 1441da177e4SLinus Torvalds { 1451da177e4SLinus Torvalds int err; 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds err = -EINVAL; 1481da177e4SLinus Torvalds switch (p->family) { 1491da177e4SLinus Torvalds case AF_INET: 1501da177e4SLinus Torvalds break; 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds case AF_INET6: 153dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1541da177e4SLinus Torvalds break; 1551da177e4SLinus Torvalds #else 1561da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1571da177e4SLinus Torvalds goto out; 1581da177e4SLinus Torvalds #endif 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds default: 1611da177e4SLinus Torvalds goto out; 1623ff50b79SStephen Hemminger } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds err = -EINVAL; 1651da177e4SLinus Torvalds switch (p->id.proto) { 1661da177e4SLinus Torvalds case IPPROTO_AH: 1674447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1684447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1691a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 17035a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 17135d2856bSMartin Willi attrs[XFRMA_ALG_COMP] || 17235d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 1731da177e4SLinus Torvalds goto out; 1741da177e4SLinus Torvalds break; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds case IPPROTO_ESP: 1771a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1781a6509d9SHerbert Xu goto out; 1791a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1804447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1811a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1821a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1831a6509d9SHerbert Xu goto out; 1841a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1854447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1861a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1871a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1881da177e4SLinus Torvalds goto out; 18935d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] && 19035d2856bSMartin Willi p->mode != XFRM_MODE_TUNNEL) 19135d2856bSMartin Willi goto out; 1921da177e4SLinus Torvalds break; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds case IPPROTO_COMP: 19535a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 1961a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 19735a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1984447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 19935d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] || 20035d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 2011da177e4SLinus Torvalds goto out; 2021da177e4SLinus Torvalds break; 2031da177e4SLinus Torvalds 204dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 205e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 206e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 20735a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 20835a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2094447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 2101a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 21135a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 21235a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 21335a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 21435d2856bSMartin Willi attrs[XFRMA_TFCPAD] || 21535a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 216e23c7194SMasahide NAKAMURA goto out; 217e23c7194SMasahide NAKAMURA break; 218e23c7194SMasahide NAKAMURA #endif 219e23c7194SMasahide NAKAMURA 2201da177e4SLinus Torvalds default: 2211da177e4SLinus Torvalds goto out; 2223ff50b79SStephen Hemminger } 2231da177e4SLinus Torvalds 2241a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 2251a6509d9SHerbert Xu goto out; 2264447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2274447bb33SMartin Willi goto out; 22835a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2291da177e4SLinus Torvalds goto out; 23035a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2311da177e4SLinus Torvalds goto out; 23235a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2331da177e4SLinus Torvalds goto out; 23435a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 235df71837dSTrent Jaeger goto out; 236d8647b79SSteffen Klassert if ((err = verify_replay(p, attrs))) 237d8647b79SSteffen Klassert goto out; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds err = -EINVAL; 2401da177e4SLinus Torvalds switch (p->mode) { 2417e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2427e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 243060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2440a69452cSDiego Beltrami case XFRM_MODE_BEET: 2451da177e4SLinus Torvalds break; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds default: 2481da177e4SLinus Torvalds goto out; 2493ff50b79SStephen Hemminger } 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds err = 0; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds out: 2541da177e4SLinus Torvalds return err; 2551da177e4SLinus Torvalds } 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2586f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int), 2595424f32eSThomas Graf struct nlattr *rta) 2601da177e4SLinus Torvalds { 2611da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2621da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds if (!rta) 2651da177e4SLinus Torvalds return 0; 2661da177e4SLinus Torvalds 2675424f32eSThomas Graf ualg = nla_data(rta); 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2701da177e4SLinus Torvalds if (!algo) 2711da177e4SLinus Torvalds return -ENOSYS; 2721da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2731da177e4SLinus Torvalds 2740f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2751da177e4SLinus Torvalds if (!p) 2761da177e4SLinus Torvalds return -ENOMEM; 2771da177e4SLinus Torvalds 27804ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2791da177e4SLinus Torvalds *algpp = p; 2801da177e4SLinus Torvalds return 0; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 2834447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 2844447bb33SMartin Willi struct nlattr *rta) 2854447bb33SMartin Willi { 2864447bb33SMartin Willi struct xfrm_algo *ualg; 2874447bb33SMartin Willi struct xfrm_algo_auth *p; 2884447bb33SMartin Willi struct xfrm_algo_desc *algo; 2894447bb33SMartin Willi 2904447bb33SMartin Willi if (!rta) 2914447bb33SMartin Willi return 0; 2924447bb33SMartin Willi 2934447bb33SMartin Willi ualg = nla_data(rta); 2944447bb33SMartin Willi 2954447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 2964447bb33SMartin Willi if (!algo) 2974447bb33SMartin Willi return -ENOSYS; 2984447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 2994447bb33SMartin Willi 3004447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 3014447bb33SMartin Willi if (!p) 3024447bb33SMartin Willi return -ENOMEM; 3034447bb33SMartin Willi 3044447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3054447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 3064447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3074447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 3084447bb33SMartin Willi 3094447bb33SMartin Willi *algpp = p; 3104447bb33SMartin Willi return 0; 3114447bb33SMartin Willi } 3124447bb33SMartin Willi 3134447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 3144447bb33SMartin Willi struct nlattr *rta) 3154447bb33SMartin Willi { 3164447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 3174447bb33SMartin Willi struct xfrm_algo_desc *algo; 3184447bb33SMartin Willi 3194447bb33SMartin Willi if (!rta) 3204447bb33SMartin Willi return 0; 3214447bb33SMartin Willi 3224447bb33SMartin Willi ualg = nla_data(rta); 3234447bb33SMartin Willi 3244447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3254447bb33SMartin Willi if (!algo) 3264447bb33SMartin Willi return -ENOSYS; 327fa6dd8a2SNicolas Dichtel if ((ualg->alg_trunc_len / 8) > MAX_AH_AUTH_LEN || 328fa6dd8a2SNicolas Dichtel ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3294447bb33SMartin Willi return -EINVAL; 3304447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3314447bb33SMartin Willi 3324447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3334447bb33SMartin Willi if (!p) 3344447bb33SMartin Willi return -ENOMEM; 3354447bb33SMartin Willi 3364447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3374447bb33SMartin Willi if (!p->alg_trunc_len) 3384447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3394447bb33SMartin Willi 3404447bb33SMartin Willi *algpp = p; 3414447bb33SMartin Willi return 0; 3424447bb33SMartin Willi } 3434447bb33SMartin Willi 3441a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 3451a6509d9SHerbert Xu struct nlattr *rta) 3461a6509d9SHerbert Xu { 3471a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3481a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3491a6509d9SHerbert Xu 3501a6509d9SHerbert Xu if (!rta) 3511a6509d9SHerbert Xu return 0; 3521a6509d9SHerbert Xu 3531a6509d9SHerbert Xu ualg = nla_data(rta); 3541a6509d9SHerbert Xu 3551a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3561a6509d9SHerbert Xu if (!algo) 3571a6509d9SHerbert Xu return -ENOSYS; 3581a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 3591a6509d9SHerbert Xu 3601a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3611a6509d9SHerbert Xu if (!p) 3621a6509d9SHerbert Xu return -ENOMEM; 3631a6509d9SHerbert Xu 3641a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 3651a6509d9SHerbert Xu *algpp = p; 3661a6509d9SHerbert Xu return 0; 3671a6509d9SHerbert Xu } 3681a6509d9SHerbert Xu 369e2b19125SSteffen Klassert static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn, 370e2b19125SSteffen Klassert struct nlattr *rp) 371e2b19125SSteffen Klassert { 372e2b19125SSteffen Klassert struct xfrm_replay_state_esn *up; 373e2b19125SSteffen Klassert 374e2b19125SSteffen Klassert if (!replay_esn || !rp) 375e2b19125SSteffen Klassert return 0; 376e2b19125SSteffen Klassert 377e2b19125SSteffen Klassert up = nla_data(rp); 378e2b19125SSteffen Klassert 379e2b19125SSteffen Klassert if (xfrm_replay_state_esn_len(replay_esn) != 380e2b19125SSteffen Klassert xfrm_replay_state_esn_len(up)) 381e2b19125SSteffen Klassert return -EINVAL; 382e2b19125SSteffen Klassert 383e2b19125SSteffen Klassert return 0; 384e2b19125SSteffen Klassert } 385e2b19125SSteffen Klassert 386d8647b79SSteffen Klassert static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn, 387d8647b79SSteffen Klassert struct xfrm_replay_state_esn **preplay_esn, 388d8647b79SSteffen Klassert struct nlattr *rta) 389d8647b79SSteffen Klassert { 390d8647b79SSteffen Klassert struct xfrm_replay_state_esn *p, *pp, *up; 391d8647b79SSteffen Klassert 392d8647b79SSteffen Klassert if (!rta) 393d8647b79SSteffen Klassert return 0; 394d8647b79SSteffen Klassert 395d8647b79SSteffen Klassert up = nla_data(rta); 396d8647b79SSteffen Klassert 397d8647b79SSteffen Klassert p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL); 398d8647b79SSteffen Klassert if (!p) 399d8647b79SSteffen Klassert return -ENOMEM; 400d8647b79SSteffen Klassert 401d8647b79SSteffen Klassert pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL); 402d8647b79SSteffen Klassert if (!pp) { 403d8647b79SSteffen Klassert kfree(p); 404d8647b79SSteffen Klassert return -ENOMEM; 405d8647b79SSteffen Klassert } 406d8647b79SSteffen Klassert 407d8647b79SSteffen Klassert *replay_esn = p; 408d8647b79SSteffen Klassert *preplay_esn = pp; 409d8647b79SSteffen Klassert 410d8647b79SSteffen Klassert return 0; 411d8647b79SSteffen Klassert } 412d8647b79SSteffen Klassert 413661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 414df71837dSTrent Jaeger { 415df71837dSTrent Jaeger int len = 0; 416df71837dSTrent Jaeger 417df71837dSTrent Jaeger if (xfrm_ctx) { 418df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 419df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 420df71837dSTrent Jaeger } 421df71837dSTrent Jaeger return len; 422df71837dSTrent Jaeger } 423df71837dSTrent Jaeger 4241da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 4251da177e4SLinus Torvalds { 4261da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 4271da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 4281da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 4291da177e4SLinus Torvalds x->props.mode = p->mode; 4301da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 4311da177e4SLinus Torvalds x->props.reqid = p->reqid; 4321da177e4SLinus Torvalds x->props.family = p->family; 43354489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 4341da177e4SLinus Torvalds x->props.flags = p->flags; 435196b0036SHerbert Xu 436ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 437196b0036SHerbert Xu x->sel.family = p->family; 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds 440d51d081dSJamal Hadi Salim /* 441d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 442d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 443d51d081dSJamal Hadi Salim * 444d51d081dSJamal Hadi Salim */ 4455424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) 446d51d081dSJamal Hadi Salim { 4475424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 448d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 4495424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 4505424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 4515424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 452d51d081dSJamal Hadi Salim 453d8647b79SSteffen Klassert if (re) { 454d8647b79SSteffen Klassert struct xfrm_replay_state_esn *replay_esn; 455d8647b79SSteffen Klassert replay_esn = nla_data(re); 456d8647b79SSteffen Klassert memcpy(x->replay_esn, replay_esn, 457d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 458d8647b79SSteffen Klassert memcpy(x->preplay_esn, replay_esn, 459d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 460d8647b79SSteffen Klassert } 461d8647b79SSteffen Klassert 462d51d081dSJamal Hadi Salim if (rp) { 463d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 4645424f32eSThomas Graf replay = nla_data(rp); 465d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 466d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 467d51d081dSJamal Hadi Salim } 468d51d081dSJamal Hadi Salim 469d51d081dSJamal Hadi Salim if (lt) { 470d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 4715424f32eSThomas Graf ltime = nla_data(lt); 472d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 473d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 474d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 475d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 476d51d081dSJamal Hadi Salim } 477d51d081dSJamal Hadi Salim 478cf5cb79fSThomas Graf if (et) 4795424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 480d51d081dSJamal Hadi Salim 481cf5cb79fSThomas Graf if (rt) 4825424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 483d51d081dSJamal Hadi Salim } 484d51d081dSJamal Hadi Salim 485fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 486fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 4875424f32eSThomas Graf struct nlattr **attrs, 4881da177e4SLinus Torvalds int *errp) 4891da177e4SLinus Torvalds { 490fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 4911da177e4SLinus Torvalds int err = -ENOMEM; 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds if (!x) 4941da177e4SLinus Torvalds goto error_no_put; 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds copy_from_user_state(x, p); 4971da177e4SLinus Torvalds 4981a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 4991a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 5001a6509d9SHerbert Xu goto error; 5014447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 5024447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 5034447bb33SMartin Willi goto error; 5044447bb33SMartin Willi if (!x->props.aalgo) { 5054447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 50635a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 5071da177e4SLinus Torvalds goto error; 5084447bb33SMartin Willi } 5091da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 5101da177e4SLinus Torvalds xfrm_ealg_get_byname, 51135a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 5121da177e4SLinus Torvalds goto error; 5131da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 5141da177e4SLinus Torvalds xfrm_calg_get_byname, 51535a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 5161da177e4SLinus Torvalds goto error; 517fd21150aSThomas Graf 518fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 519fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 520fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 521fd21150aSThomas Graf if (x->encap == NULL) 5221da177e4SLinus Torvalds goto error; 523fd21150aSThomas Graf } 524fd21150aSThomas Graf 52535d2856bSMartin Willi if (attrs[XFRMA_TFCPAD]) 52635d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); 52735d2856bSMartin Willi 528fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 529fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 530fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 531fd21150aSThomas Graf if (x->coaddr == NULL) 532060f02a3SNoriaki TAKAMIYA goto error; 533fd21150aSThomas Graf } 534fd21150aSThomas Graf 5356f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark); 5366f26b61eSJamal Hadi Salim 537a454f0ccSWei Yongjun err = __xfrm_init_state(x, false); 5381da177e4SLinus Torvalds if (err) 5391da177e4SLinus Torvalds goto error; 5401da177e4SLinus Torvalds 541fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 542fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 543df71837dSTrent Jaeger goto error; 544df71837dSTrent Jaeger 545d8647b79SSteffen Klassert if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, 546d8647b79SSteffen Klassert attrs[XFRMA_REPLAY_ESN_VAL]))) 547d8647b79SSteffen Klassert goto error; 548d8647b79SSteffen Klassert 5491da177e4SLinus Torvalds x->km.seq = p->seq; 550b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 551d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 552b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 553d51d081dSJamal Hadi Salim 5549fdc4883SSteffen Klassert if ((err = xfrm_init_replay(x))) 5559fdc4883SSteffen Klassert goto error; 556d51d081dSJamal Hadi Salim 5579fdc4883SSteffen Klassert /* override default values from above */ 5585424f32eSThomas Graf xfrm_update_ae_params(x, attrs); 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds return x; 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds error: 5631da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 5641da177e4SLinus Torvalds xfrm_state_put(x); 5651da177e4SLinus Torvalds error_no_put: 5661da177e4SLinus Torvalds *errp = err; 5671da177e4SLinus Torvalds return NULL; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds 57022e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 5715424f32eSThomas Graf struct nlattr **attrs) 5721da177e4SLinus Torvalds { 573fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 5747b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 5751da177e4SLinus Torvalds struct xfrm_state *x; 5761da177e4SLinus Torvalds int err; 57726b15dadSJamal Hadi Salim struct km_event c; 578c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 579c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 580c53fa1edSPatrick McHardy u32 sid; 5811da177e4SLinus Torvalds 58235a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 5831da177e4SLinus Torvalds if (err) 5841da177e4SLinus Torvalds return err; 5851da177e4SLinus Torvalds 586fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 5871da177e4SLinus Torvalds if (!x) 5881da177e4SLinus Torvalds return err; 5891da177e4SLinus Torvalds 59026b15dadSJamal Hadi Salim xfrm_state_hold(x); 5911da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 5921da177e4SLinus Torvalds err = xfrm_state_add(x); 5931da177e4SLinus Torvalds else 5941da177e4SLinus Torvalds err = xfrm_state_update(x); 5951da177e4SLinus Torvalds 596c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 5972532386fSEric Paris xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); 598161a09e7SJoy Latten 5991da177e4SLinus Torvalds if (err < 0) { 6001da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 60121380b81SHerbert Xu __xfrm_state_put(x); 6027d6dfe1fSPatrick McHardy goto out; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds 60526b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 60626b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 607f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 60826b15dadSJamal Hadi Salim 60926b15dadSJamal Hadi Salim km_state_notify(x, &c); 6107d6dfe1fSPatrick McHardy out: 61126b15dadSJamal Hadi Salim xfrm_state_put(x); 6121da177e4SLinus Torvalds return err; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds 615fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 616fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 6175424f32eSThomas Graf struct nlattr **attrs, 618eb2971b6SMasahide NAKAMURA int *errp) 619eb2971b6SMasahide NAKAMURA { 620eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 6216f26b61eSJamal Hadi Salim struct xfrm_mark m; 622eb2971b6SMasahide NAKAMURA int err; 6236f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 624eb2971b6SMasahide NAKAMURA 625eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 626eb2971b6SMasahide NAKAMURA err = -ESRCH; 6276f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family); 628eb2971b6SMasahide NAKAMURA } else { 629eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 630eb2971b6SMasahide NAKAMURA 63135a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 632eb2971b6SMasahide NAKAMURA if (!saddr) { 633eb2971b6SMasahide NAKAMURA err = -EINVAL; 634eb2971b6SMasahide NAKAMURA goto out; 635eb2971b6SMasahide NAKAMURA } 636eb2971b6SMasahide NAKAMURA 6379abbffeeSMasahide NAKAMURA err = -ESRCH; 6386f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark, 6396f26b61eSJamal Hadi Salim &p->daddr, saddr, 640221df1edSAlexey Dobriyan p->proto, p->family); 641eb2971b6SMasahide NAKAMURA } 642eb2971b6SMasahide NAKAMURA 643eb2971b6SMasahide NAKAMURA out: 644eb2971b6SMasahide NAKAMURA if (!x && errp) 645eb2971b6SMasahide NAKAMURA *errp = err; 646eb2971b6SMasahide NAKAMURA return x; 647eb2971b6SMasahide NAKAMURA } 648eb2971b6SMasahide NAKAMURA 64922e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 6505424f32eSThomas Graf struct nlattr **attrs) 6511da177e4SLinus Torvalds { 652fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6531da177e4SLinus Torvalds struct xfrm_state *x; 654eb2971b6SMasahide NAKAMURA int err = -ESRCH; 65526b15dadSJamal Hadi Salim struct km_event c; 6567b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 657c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 658c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 659c53fa1edSPatrick McHardy u32 sid; 6601da177e4SLinus Torvalds 661fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 6621da177e4SLinus Torvalds if (x == NULL) 663eb2971b6SMasahide NAKAMURA return err; 6641da177e4SLinus Torvalds 6656f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 666c8c05a8eSCatherine Zhang goto out; 667c8c05a8eSCatherine Zhang 6681da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 669c8c05a8eSCatherine Zhang err = -EPERM; 670c8c05a8eSCatherine Zhang goto out; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds 67326b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 674161a09e7SJoy Latten 675c8c05a8eSCatherine Zhang if (err < 0) 676c8c05a8eSCatherine Zhang goto out; 67726b15dadSJamal Hadi Salim 67826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 67926b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 680f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 68126b15dadSJamal Hadi Salim km_state_notify(x, &c); 6821da177e4SLinus Torvalds 683c8c05a8eSCatherine Zhang out: 684c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 6852532386fSEric Paris xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); 686c8c05a8eSCatherine Zhang xfrm_state_put(x); 68726b15dadSJamal Hadi Salim return err; 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds 6901da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 6911da177e4SLinus Torvalds { 6921da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 6931da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 6941da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 6951da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 6961da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 69754489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 6981da177e4SLinus Torvalds p->mode = x->props.mode; 6991da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 7001da177e4SLinus Torvalds p->reqid = x->props.reqid; 7011da177e4SLinus Torvalds p->family = x->props.family; 7021da177e4SLinus Torvalds p->flags = x->props.flags; 7031da177e4SLinus Torvalds p->seq = x->km.seq; 7041da177e4SLinus Torvalds } 7051da177e4SLinus Torvalds 7061da177e4SLinus Torvalds struct xfrm_dump_info { 7071da177e4SLinus Torvalds struct sk_buff *in_skb; 7081da177e4SLinus Torvalds struct sk_buff *out_skb; 7091da177e4SLinus Torvalds u32 nlmsg_seq; 7101da177e4SLinus Torvalds u16 nlmsg_flags; 7111da177e4SLinus Torvalds }; 7121da177e4SLinus Torvalds 713c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 714c0144beaSThomas Graf { 715c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 716c0144beaSThomas Graf struct nlattr *attr; 71768325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 718c0144beaSThomas Graf 719c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 720c0144beaSThomas Graf if (attr == NULL) 721c0144beaSThomas Graf return -EMSGSIZE; 722c0144beaSThomas Graf 723c0144beaSThomas Graf uctx = nla_data(attr); 724c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 725c0144beaSThomas Graf uctx->len = ctx_size; 726c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 727c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 728c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 729c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 730c0144beaSThomas Graf 731c0144beaSThomas Graf return 0; 732c0144beaSThomas Graf } 733c0144beaSThomas Graf 7344447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 7354447bb33SMartin Willi { 7364447bb33SMartin Willi struct xfrm_algo *algo; 7374447bb33SMartin Willi struct nlattr *nla; 7384447bb33SMartin Willi 7394447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 7404447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 7414447bb33SMartin Willi if (!nla) 7424447bb33SMartin Willi return -EMSGSIZE; 7434447bb33SMartin Willi 7444447bb33SMartin Willi algo = nla_data(nla); 7454447bb33SMartin Willi strcpy(algo->alg_name, auth->alg_name); 7464447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 7474447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 7484447bb33SMartin Willi 7494447bb33SMartin Willi return 0; 7504447bb33SMartin Willi } 7514447bb33SMartin Willi 75268325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 75368325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 75468325d3bSHerbert Xu struct xfrm_usersa_info *p, 75568325d3bSHerbert Xu struct sk_buff *skb) 7561da177e4SLinus Torvalds { 7571d1e34ddSDavid S. Miller int ret = 0; 7581d1e34ddSDavid S. Miller 7591da177e4SLinus Torvalds copy_to_user_state(x, p); 7601da177e4SLinus Torvalds 7611d1e34ddSDavid S. Miller if (x->coaddr) { 7621d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 7631d1e34ddSDavid S. Miller if (ret) 7641d1e34ddSDavid S. Miller goto out; 7651d1e34ddSDavid S. Miller } 7661d1e34ddSDavid S. Miller if (x->lastused) { 7671d1e34ddSDavid S. Miller ret = nla_put_u64(skb, XFRMA_LASTUSED, x->lastused); 7681d1e34ddSDavid S. Miller if (ret) 7691d1e34ddSDavid S. Miller goto out; 7701d1e34ddSDavid S. Miller } 7711d1e34ddSDavid S. Miller if (x->aead) { 7721d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 7731d1e34ddSDavid S. Miller if (ret) 7741d1e34ddSDavid S. Miller goto out; 7751d1e34ddSDavid S. Miller } 7761d1e34ddSDavid S. Miller if (x->aalg) { 7771d1e34ddSDavid S. Miller ret = copy_to_user_auth(x->aalg, skb); 7781d1e34ddSDavid S. Miller if (!ret) 7791d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC, 7801d1e34ddSDavid S. Miller xfrm_alg_auth_len(x->aalg), x->aalg); 7811d1e34ddSDavid S. Miller if (ret) 7821d1e34ddSDavid S. Miller goto out; 7831d1e34ddSDavid S. Miller } 7841d1e34ddSDavid S. Miller if (x->ealg) { 7851d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 7861d1e34ddSDavid S. Miller if (ret) 7871d1e34ddSDavid S. Miller goto out; 7881d1e34ddSDavid S. Miller } 7891d1e34ddSDavid S. Miller if (x->calg) { 7901d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 7911d1e34ddSDavid S. Miller if (ret) 7921d1e34ddSDavid S. Miller goto out; 7931d1e34ddSDavid S. Miller } 7941d1e34ddSDavid S. Miller if (x->encap) { 7951d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 7961d1e34ddSDavid S. Miller if (ret) 7971d1e34ddSDavid S. Miller goto out; 7981d1e34ddSDavid S. Miller } 7991d1e34ddSDavid S. Miller if (x->tfcpad) { 8001d1e34ddSDavid S. Miller ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad); 8011d1e34ddSDavid S. Miller if (ret) 8021d1e34ddSDavid S. Miller goto out; 8031d1e34ddSDavid S. Miller } 8041d1e34ddSDavid S. Miller ret = xfrm_mark_put(skb, &x->mark); 8051d1e34ddSDavid S. Miller if (ret) 8061d1e34ddSDavid S. Miller goto out; 8071d1e34ddSDavid S. Miller if (x->replay_esn) { 8081d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 809d0fde795SDavid S. Miller xfrm_replay_state_esn_len(x->replay_esn), 8101d1e34ddSDavid S. Miller x->replay_esn); 8111d1e34ddSDavid S. Miller if (ret) 8121d1e34ddSDavid S. Miller goto out; 8131d1e34ddSDavid S. Miller } 8141d1e34ddSDavid S. Miller if (x->security) 8151d1e34ddSDavid S. Miller ret = copy_sec_ctx(x->security, skb); 8161d1e34ddSDavid S. Miller out: 8171d1e34ddSDavid S. Miller return ret; 81868325d3bSHerbert Xu } 81968325d3bSHerbert Xu 82068325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 82168325d3bSHerbert Xu { 82268325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 82368325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 82468325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 82568325d3bSHerbert Xu struct xfrm_usersa_info *p; 82668325d3bSHerbert Xu struct nlmsghdr *nlh; 82768325d3bSHerbert Xu int err; 82868325d3bSHerbert Xu 82968325d3bSHerbert Xu nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 83068325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 83168325d3bSHerbert Xu if (nlh == NULL) 83268325d3bSHerbert Xu return -EMSGSIZE; 83368325d3bSHerbert Xu 83468325d3bSHerbert Xu p = nlmsg_data(nlh); 83568325d3bSHerbert Xu 83668325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 8371d1e34ddSDavid S. Miller if (err) { 8389825069dSThomas Graf nlmsg_cancel(skb, nlh); 83968325d3bSHerbert Xu return err; 8401da177e4SLinus Torvalds } 8411d1e34ddSDavid S. Miller nlmsg_end(skb, nlh); 8421d1e34ddSDavid S. Miller return 0; 8431d1e34ddSDavid S. Miller } 8441da177e4SLinus Torvalds 8454c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 8464c563f76STimo Teras { 8474c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8484c563f76STimo Teras xfrm_state_walk_done(walk); 8494c563f76STimo Teras return 0; 8504c563f76STimo Teras } 8514c563f76STimo Teras 8521da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 8531da177e4SLinus Torvalds { 854fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 8554c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8561da177e4SLinus Torvalds struct xfrm_dump_info info; 8571da177e4SLinus Torvalds 8584c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 8594c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 8604c563f76STimo Teras 8611da177e4SLinus Torvalds info.in_skb = cb->skb; 8621da177e4SLinus Torvalds info.out_skb = skb; 8631da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 8641da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 8654c563f76STimo Teras 8664c563f76STimo Teras if (!cb->args[0]) { 8674c563f76STimo Teras cb->args[0] = 1; 8684c563f76STimo Teras xfrm_state_walk_init(walk, 0); 8694c563f76STimo Teras } 8704c563f76STimo Teras 871fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 8721da177e4SLinus Torvalds 8731da177e4SLinus Torvalds return skb->len; 8741da177e4SLinus Torvalds } 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 8771da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 8781da177e4SLinus Torvalds { 8791da177e4SLinus Torvalds struct xfrm_dump_info info; 8801da177e4SLinus Torvalds struct sk_buff *skb; 881864745d2SMathias Krause int err; 8821da177e4SLinus Torvalds 8837deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 8841da177e4SLinus Torvalds if (!skb) 8851da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds info.in_skb = in_skb; 8881da177e4SLinus Torvalds info.out_skb = skb; 8891da177e4SLinus Torvalds info.nlmsg_seq = seq; 8901da177e4SLinus Torvalds info.nlmsg_flags = 0; 8911da177e4SLinus Torvalds 892864745d2SMathias Krause err = dump_one_state(x, 0, &info); 893864745d2SMathias Krause if (err) { 8941da177e4SLinus Torvalds kfree_skb(skb); 895864745d2SMathias Krause return ERR_PTR(err); 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds return skb; 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds 9017deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 9027deb2264SThomas Graf { 9037deb2264SThomas Graf return NLMSG_ALIGN(4) 9047deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 9057deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 9067deb2264SThomas Graf } 9077deb2264SThomas Graf 908e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 909e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 910ecfd6b18SJamal Hadi Salim { 9115a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 9125a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 9135a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 914ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 9151d1e34ddSDavid S. Miller int err; 916ecfd6b18SJamal Hadi Salim u32 *f; 917ecfd6b18SJamal Hadi Salim 918ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 91925985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 920ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 921ecfd6b18SJamal Hadi Salim 922ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 923ecfd6b18SJamal Hadi Salim *f = flags; 924e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 9255a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 9265a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 9275a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 9285a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 9295a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 9305a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 9315a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 9325a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 933ecfd6b18SJamal Hadi Salim 9341d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 9351d1e34ddSDavid S. Miller if (!err) 9361d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 9371d1e34ddSDavid S. Miller if (err) { 9381d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 9391d1e34ddSDavid S. Miller return err; 9401d1e34ddSDavid S. Miller } 941ecfd6b18SJamal Hadi Salim 942ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 943ecfd6b18SJamal Hadi Salim } 944ecfd6b18SJamal Hadi Salim 945ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 9465424f32eSThomas Graf struct nlattr **attrs) 947ecfd6b18SJamal Hadi Salim { 948a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 949ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 9507b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 951ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 952ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 953ecfd6b18SJamal Hadi Salim 9547deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 955ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 956ecfd6b18SJamal Hadi Salim return -ENOMEM; 957ecfd6b18SJamal Hadi Salim 958e071041bSAlexey Dobriyan if (build_spdinfo(r_skb, net, spid, seq, *flags) < 0) 959ecfd6b18SJamal Hadi Salim BUG(); 960ecfd6b18SJamal Hadi Salim 961a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 962ecfd6b18SJamal Hadi Salim } 963ecfd6b18SJamal Hadi Salim 9647deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 9657deb2264SThomas Graf { 9667deb2264SThomas Graf return NLMSG_ALIGN(4) 9677deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 9687deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 9697deb2264SThomas Graf } 9707deb2264SThomas Graf 971e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 972e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 97328d8909bSJamal Hadi Salim { 974af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 975af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 97628d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 9771d1e34ddSDavid S. Miller int err; 97828d8909bSJamal Hadi Salim u32 *f; 97928d8909bSJamal Hadi Salim 98028d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 98125985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 98228d8909bSJamal Hadi Salim return -EMSGSIZE; 98328d8909bSJamal Hadi Salim 98428d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 98528d8909bSJamal Hadi Salim *f = flags; 986e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 98728d8909bSJamal Hadi Salim 988af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 989af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 990af11e316SJamal Hadi Salim 9911d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt); 9921d1e34ddSDavid S. Miller if (!err) 9931d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 9941d1e34ddSDavid S. Miller if (err) { 9951d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 9961d1e34ddSDavid S. Miller return err; 9971d1e34ddSDavid S. Miller } 99828d8909bSJamal Hadi Salim 99928d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 100028d8909bSJamal Hadi Salim } 100128d8909bSJamal Hadi Salim 100228d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 10035424f32eSThomas Graf struct nlattr **attrs) 100428d8909bSJamal Hadi Salim { 1005a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 100628d8909bSJamal Hadi Salim struct sk_buff *r_skb; 10077b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 100828d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 100928d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 101028d8909bSJamal Hadi Salim 10117deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 101228d8909bSJamal Hadi Salim if (r_skb == NULL) 101328d8909bSJamal Hadi Salim return -ENOMEM; 101428d8909bSJamal Hadi Salim 1015e071041bSAlexey Dobriyan if (build_sadinfo(r_skb, net, spid, seq, *flags) < 0) 101628d8909bSJamal Hadi Salim BUG(); 101728d8909bSJamal Hadi Salim 1018a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 101928d8909bSJamal Hadi Salim } 102028d8909bSJamal Hadi Salim 102122e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 10225424f32eSThomas Graf struct nlattr **attrs) 10231da177e4SLinus Torvalds { 1024fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 10257b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 10261da177e4SLinus Torvalds struct xfrm_state *x; 10271da177e4SLinus Torvalds struct sk_buff *resp_skb; 1028eb2971b6SMasahide NAKAMURA int err = -ESRCH; 10291da177e4SLinus Torvalds 1030fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 10311da177e4SLinus Torvalds if (x == NULL) 10321da177e4SLinus Torvalds goto out_noput; 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 10351da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 10361da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 10371da177e4SLinus Torvalds } else { 1038a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds xfrm_state_put(x); 10411da177e4SLinus Torvalds out_noput: 10421da177e4SLinus Torvalds return err; 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 10461da177e4SLinus Torvalds { 10471da177e4SLinus Torvalds switch (p->info.id.proto) { 10481da177e4SLinus Torvalds case IPPROTO_AH: 10491da177e4SLinus Torvalds case IPPROTO_ESP: 10501da177e4SLinus Torvalds break; 10511da177e4SLinus Torvalds 10521da177e4SLinus Torvalds case IPPROTO_COMP: 10531da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 10541da177e4SLinus Torvalds if (p->max >= 0x10000) 10551da177e4SLinus Torvalds return -EINVAL; 10561da177e4SLinus Torvalds break; 10571da177e4SLinus Torvalds 10581da177e4SLinus Torvalds default: 10591da177e4SLinus Torvalds return -EINVAL; 10603ff50b79SStephen Hemminger } 10611da177e4SLinus Torvalds 10621da177e4SLinus Torvalds if (p->min > p->max) 10631da177e4SLinus Torvalds return -EINVAL; 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds return 0; 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 106822e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 10695424f32eSThomas Graf struct nlattr **attrs) 10701da177e4SLinus Torvalds { 1071fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 10721da177e4SLinus Torvalds struct xfrm_state *x; 10731da177e4SLinus Torvalds struct xfrm_userspi_info *p; 10741da177e4SLinus Torvalds struct sk_buff *resp_skb; 10751da177e4SLinus Torvalds xfrm_address_t *daddr; 10761da177e4SLinus Torvalds int family; 10771da177e4SLinus Torvalds int err; 10786f26b61eSJamal Hadi Salim u32 mark; 10796f26b61eSJamal Hadi Salim struct xfrm_mark m; 10801da177e4SLinus Torvalds 10817b67c857SThomas Graf p = nlmsg_data(nlh); 10821da177e4SLinus Torvalds err = verify_userspi_info(p); 10831da177e4SLinus Torvalds if (err) 10841da177e4SLinus Torvalds goto out_noput; 10851da177e4SLinus Torvalds 10861da177e4SLinus Torvalds family = p->info.family; 10871da177e4SLinus Torvalds daddr = &p->info.id.daddr; 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds x = NULL; 10906f26b61eSJamal Hadi Salim 10916f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 10921da177e4SLinus Torvalds if (p->info.seq) { 10936f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq); 10941da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 10951da177e4SLinus Torvalds xfrm_state_put(x); 10961da177e4SLinus Torvalds x = NULL; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds 11001da177e4SLinus Torvalds if (!x) 11016f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid, 11021da177e4SLinus Torvalds p->info.id.proto, daddr, 11031da177e4SLinus Torvalds &p->info.saddr, 1, 11041da177e4SLinus Torvalds family); 11051da177e4SLinus Torvalds err = -ENOENT; 11061da177e4SLinus Torvalds if (x == NULL) 11071da177e4SLinus Torvalds goto out_noput; 11081da177e4SLinus Torvalds 1109658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 1110658b219eSHerbert Xu if (err) 1111658b219eSHerbert Xu goto out; 11121da177e4SLinus Torvalds 11131da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 11141da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 11151da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 11161da177e4SLinus Torvalds goto out; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds 1119a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds out: 11221da177e4SLinus Torvalds xfrm_state_put(x); 11231da177e4SLinus Torvalds out_noput: 11241da177e4SLinus Torvalds return err; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds 1127b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 11281da177e4SLinus Torvalds { 11291da177e4SLinus Torvalds switch (dir) { 11301da177e4SLinus Torvalds case XFRM_POLICY_IN: 11311da177e4SLinus Torvalds case XFRM_POLICY_OUT: 11321da177e4SLinus Torvalds case XFRM_POLICY_FWD: 11331da177e4SLinus Torvalds break; 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds default: 11361da177e4SLinus Torvalds return -EINVAL; 11373ff50b79SStephen Hemminger } 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds return 0; 11401da177e4SLinus Torvalds } 11411da177e4SLinus Torvalds 1142b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1143f7b6983fSMasahide NAKAMURA { 1144f7b6983fSMasahide NAKAMURA switch (type) { 1145f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1146f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1147f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1148f7b6983fSMasahide NAKAMURA #endif 1149f7b6983fSMasahide NAKAMURA break; 1150f7b6983fSMasahide NAKAMURA 1151f7b6983fSMasahide NAKAMURA default: 1152f7b6983fSMasahide NAKAMURA return -EINVAL; 11533ff50b79SStephen Hemminger } 1154f7b6983fSMasahide NAKAMURA 1155f7b6983fSMasahide NAKAMURA return 0; 1156f7b6983fSMasahide NAKAMURA } 1157f7b6983fSMasahide NAKAMURA 11581da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 11591da177e4SLinus Torvalds { 11601da177e4SLinus Torvalds switch (p->share) { 11611da177e4SLinus Torvalds case XFRM_SHARE_ANY: 11621da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 11631da177e4SLinus Torvalds case XFRM_SHARE_USER: 11641da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 11651da177e4SLinus Torvalds break; 11661da177e4SLinus Torvalds 11671da177e4SLinus Torvalds default: 11681da177e4SLinus Torvalds return -EINVAL; 11693ff50b79SStephen Hemminger } 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds switch (p->action) { 11721da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 11731da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 11741da177e4SLinus Torvalds break; 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds default: 11771da177e4SLinus Torvalds return -EINVAL; 11783ff50b79SStephen Hemminger } 11791da177e4SLinus Torvalds 11801da177e4SLinus Torvalds switch (p->sel.family) { 11811da177e4SLinus Torvalds case AF_INET: 11821da177e4SLinus Torvalds break; 11831da177e4SLinus Torvalds 11841da177e4SLinus Torvalds case AF_INET6: 1185dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 11861da177e4SLinus Torvalds break; 11871da177e4SLinus Torvalds #else 11881da177e4SLinus Torvalds return -EAFNOSUPPORT; 11891da177e4SLinus Torvalds #endif 11901da177e4SLinus Torvalds 11911da177e4SLinus Torvalds default: 11921da177e4SLinus Torvalds return -EINVAL; 11933ff50b79SStephen Hemminger } 11941da177e4SLinus Torvalds 11951da177e4SLinus Torvalds return verify_policy_dir(p->dir); 11961da177e4SLinus Torvalds } 11971da177e4SLinus Torvalds 11985424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1199df71837dSTrent Jaeger { 12005424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1201df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1202df71837dSTrent Jaeger 1203df71837dSTrent Jaeger if (!rt) 1204df71837dSTrent Jaeger return 0; 1205df71837dSTrent Jaeger 12065424f32eSThomas Graf uctx = nla_data(rt); 120703e1ad7bSPaul Moore return security_xfrm_policy_alloc(&pol->security, uctx); 1208df71837dSTrent Jaeger } 1209df71837dSTrent Jaeger 12101da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 12111da177e4SLinus Torvalds int nr) 12121da177e4SLinus Torvalds { 12131da177e4SLinus Torvalds int i; 12141da177e4SLinus Torvalds 12151da177e4SLinus Torvalds xp->xfrm_nr = nr; 12161da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 12171da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 12181da177e4SLinus Torvalds 12191da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 12201da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 12211da177e4SLinus Torvalds sizeof(xfrm_address_t)); 12221da177e4SLinus Torvalds t->reqid = ut->reqid; 12231da177e4SLinus Torvalds t->mode = ut->mode; 12241da177e4SLinus Torvalds t->share = ut->share; 12251da177e4SLinus Torvalds t->optional = ut->optional; 12261da177e4SLinus Torvalds t->aalgos = ut->aalgos; 12271da177e4SLinus Torvalds t->ealgos = ut->ealgos; 12281da177e4SLinus Torvalds t->calgos = ut->calgos; 1229c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1230c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 12318511d01dSMiika Komu t->encap_family = ut->family; 12321da177e4SLinus Torvalds } 12331da177e4SLinus Torvalds } 12341da177e4SLinus Torvalds 1235b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1236b4ad86bfSDavid S. Miller { 1237b4ad86bfSDavid S. Miller int i; 1238b4ad86bfSDavid S. Miller 1239b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1240b4ad86bfSDavid S. Miller return -EINVAL; 1241b4ad86bfSDavid S. Miller 1242b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1243b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1244b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1245b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1246b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1247b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1248b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1249b4ad86bfSDavid S. Miller */ 1250b4ad86bfSDavid S. Miller if (!ut[i].family) 1251b4ad86bfSDavid S. Miller ut[i].family = family; 1252b4ad86bfSDavid S. Miller 1253b4ad86bfSDavid S. Miller switch (ut[i].family) { 1254b4ad86bfSDavid S. Miller case AF_INET: 1255b4ad86bfSDavid S. Miller break; 1256dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1257b4ad86bfSDavid S. Miller case AF_INET6: 1258b4ad86bfSDavid S. Miller break; 1259b4ad86bfSDavid S. Miller #endif 1260b4ad86bfSDavid S. Miller default: 1261b4ad86bfSDavid S. Miller return -EINVAL; 12623ff50b79SStephen Hemminger } 1263b4ad86bfSDavid S. Miller } 1264b4ad86bfSDavid S. Miller 1265b4ad86bfSDavid S. Miller return 0; 1266b4ad86bfSDavid S. Miller } 1267b4ad86bfSDavid S. Miller 12685424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 12691da177e4SLinus Torvalds { 12705424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 12711da177e4SLinus Torvalds 12721da177e4SLinus Torvalds if (!rt) { 12731da177e4SLinus Torvalds pol->xfrm_nr = 0; 12741da177e4SLinus Torvalds } else { 12755424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 12765424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1277b4ad86bfSDavid S. Miller int err; 12781da177e4SLinus Torvalds 1279b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1280b4ad86bfSDavid S. Miller if (err) 1281b4ad86bfSDavid S. Miller return err; 12821da177e4SLinus Torvalds 12835424f32eSThomas Graf copy_templates(pol, utmpl, nr); 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds return 0; 12861da177e4SLinus Torvalds } 12871da177e4SLinus Torvalds 12885424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1289f7b6983fSMasahide NAKAMURA { 12905424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1291f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1292b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1293f7b6983fSMasahide NAKAMURA int err; 1294f7b6983fSMasahide NAKAMURA 1295f7b6983fSMasahide NAKAMURA if (rt) { 12965424f32eSThomas Graf upt = nla_data(rt); 1297f7b6983fSMasahide NAKAMURA type = upt->type; 1298f7b6983fSMasahide NAKAMURA } 1299f7b6983fSMasahide NAKAMURA 1300f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1301f7b6983fSMasahide NAKAMURA if (err) 1302f7b6983fSMasahide NAKAMURA return err; 1303f7b6983fSMasahide NAKAMURA 1304f7b6983fSMasahide NAKAMURA *tp = type; 1305f7b6983fSMasahide NAKAMURA return 0; 1306f7b6983fSMasahide NAKAMURA } 1307f7b6983fSMasahide NAKAMURA 13081da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 13091da177e4SLinus Torvalds { 13101da177e4SLinus Torvalds xp->priority = p->priority; 13111da177e4SLinus Torvalds xp->index = p->index; 13121da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 13131da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 13141da177e4SLinus Torvalds xp->action = p->action; 13151da177e4SLinus Torvalds xp->flags = p->flags; 13161da177e4SLinus Torvalds xp->family = p->sel.family; 13171da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds 13201da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 13211da177e4SLinus Torvalds { 13221da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 13231da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 13241da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 13251da177e4SLinus Torvalds p->priority = xp->priority; 13261da177e4SLinus Torvalds p->index = xp->index; 13271da177e4SLinus Torvalds p->sel.family = xp->family; 13281da177e4SLinus Torvalds p->dir = dir; 13291da177e4SLinus Torvalds p->action = xp->action; 13301da177e4SLinus Torvalds p->flags = xp->flags; 13311da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 13321da177e4SLinus Torvalds } 13331da177e4SLinus Torvalds 1334fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 13351da177e4SLinus Torvalds { 1336fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 13371da177e4SLinus Torvalds int err; 13381da177e4SLinus Torvalds 13391da177e4SLinus Torvalds if (!xp) { 13401da177e4SLinus Torvalds *errp = -ENOMEM; 13411da177e4SLinus Torvalds return NULL; 13421da177e4SLinus Torvalds } 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1345df71837dSTrent Jaeger 134635a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1347f7b6983fSMasahide NAKAMURA if (err) 1348f7b6983fSMasahide NAKAMURA goto error; 1349f7b6983fSMasahide NAKAMURA 135035a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 135135a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1352f7b6983fSMasahide NAKAMURA if (err) 1353f7b6983fSMasahide NAKAMURA goto error; 13541da177e4SLinus Torvalds 1355295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark); 1356295fae56SJamal Hadi Salim 13571da177e4SLinus Torvalds return xp; 1358f7b6983fSMasahide NAKAMURA error: 1359f7b6983fSMasahide NAKAMURA *errp = err; 136012a169e7SHerbert Xu xp->walk.dead = 1; 136164c31b3fSWANG Cong xfrm_policy_destroy(xp); 1362f7b6983fSMasahide NAKAMURA return NULL; 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds 136522e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 13665424f32eSThomas Graf struct nlattr **attrs) 13671da177e4SLinus Torvalds { 1368fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 13697b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 13701da177e4SLinus Torvalds struct xfrm_policy *xp; 137126b15dadSJamal Hadi Salim struct km_event c; 13721da177e4SLinus Torvalds int err; 13731da177e4SLinus Torvalds int excl; 1374c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1375c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1376c53fa1edSPatrick McHardy u32 sid; 13771da177e4SLinus Torvalds 13781da177e4SLinus Torvalds err = verify_newpolicy_info(p); 13791da177e4SLinus Torvalds if (err) 13801da177e4SLinus Torvalds return err; 138135a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1382df71837dSTrent Jaeger if (err) 1383df71837dSTrent Jaeger return err; 13841da177e4SLinus Torvalds 1385fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 13861da177e4SLinus Torvalds if (!xp) 13871da177e4SLinus Torvalds return err; 13881da177e4SLinus Torvalds 138925985edcSLucas De Marchi /* shouldn't excl be based on nlh flags?? 139026b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 139126b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 139226b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 13931da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 13941da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 1395c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 13962532386fSEric Paris xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); 1397161a09e7SJoy Latten 13981da177e4SLinus Torvalds if (err) { 139903e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 14001da177e4SLinus Torvalds kfree(xp); 14011da177e4SLinus Torvalds return err; 14021da177e4SLinus Torvalds } 14031da177e4SLinus Torvalds 1404f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 140526b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 140626b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 140726b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 140826b15dadSJamal Hadi Salim 14091da177e4SLinus Torvalds xfrm_pol_put(xp); 14101da177e4SLinus Torvalds 14111da177e4SLinus Torvalds return 0; 14121da177e4SLinus Torvalds } 14131da177e4SLinus Torvalds 14141da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 14151da177e4SLinus Torvalds { 14161da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 14171da177e4SLinus Torvalds int i; 14181da177e4SLinus Torvalds 14191da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 14201da177e4SLinus Torvalds return 0; 14211da177e4SLinus Torvalds 14221da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 14231da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 14241da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 14251da177e4SLinus Torvalds 14261da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 14278511d01dSMiika Komu up->family = kp->encap_family; 14281da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 14291da177e4SLinus Torvalds up->reqid = kp->reqid; 14301da177e4SLinus Torvalds up->mode = kp->mode; 14311da177e4SLinus Torvalds up->share = kp->share; 14321da177e4SLinus Torvalds up->optional = kp->optional; 14331da177e4SLinus Torvalds up->aalgos = kp->aalgos; 14341da177e4SLinus Torvalds up->ealgos = kp->ealgos; 14351da177e4SLinus Torvalds up->calgos = kp->calgos; 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds 1438c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1439c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1440df71837dSTrent Jaeger } 1441df71837dSTrent Jaeger 14420d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 14430d681623SSerge Hallyn { 14440d681623SSerge Hallyn if (x->security) { 14450d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 14460d681623SSerge Hallyn } 14470d681623SSerge Hallyn return 0; 14480d681623SSerge Hallyn } 14490d681623SSerge Hallyn 14500d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 14510d681623SSerge Hallyn { 14521d1e34ddSDavid S. Miller if (xp->security) 14530d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 14540d681623SSerge Hallyn return 0; 14550d681623SSerge Hallyn } 1456cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1457cfbfd45aSThomas Graf { 1458cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1459cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1460cfbfd45aSThomas Graf #else 1461cfbfd45aSThomas Graf return 0; 1462cfbfd45aSThomas Graf #endif 1463cfbfd45aSThomas Graf } 14640d681623SSerge Hallyn 1465f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1466b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1467f7b6983fSMasahide NAKAMURA { 1468c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1469c0144beaSThomas Graf .type = type, 1470c0144beaSThomas Graf }; 1471f7b6983fSMasahide NAKAMURA 1472c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1473f7b6983fSMasahide NAKAMURA } 1474f7b6983fSMasahide NAKAMURA 1475f7b6983fSMasahide NAKAMURA #else 1476b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1477f7b6983fSMasahide NAKAMURA { 1478f7b6983fSMasahide NAKAMURA return 0; 1479f7b6983fSMasahide NAKAMURA } 1480f7b6983fSMasahide NAKAMURA #endif 1481f7b6983fSMasahide NAKAMURA 14821da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 14831da177e4SLinus Torvalds { 14841da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 14851da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 14861da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 14871da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 14881da177e4SLinus Torvalds struct nlmsghdr *nlh; 14891d1e34ddSDavid S. Miller int err; 14901da177e4SLinus Torvalds 149179b8b7f4SThomas Graf nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 149279b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 149379b8b7f4SThomas Graf if (nlh == NULL) 149479b8b7f4SThomas Graf return -EMSGSIZE; 14951da177e4SLinus Torvalds 14967b67c857SThomas Graf p = nlmsg_data(nlh); 14971da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 14981d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 14991d1e34ddSDavid S. Miller if (!err) 15001d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 15011d1e34ddSDavid S. Miller if (!err) 15021d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 15031d1e34ddSDavid S. Miller if (!err) 15041d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 15051d1e34ddSDavid S. Miller if (err) { 15061d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 15071d1e34ddSDavid S. Miller return err; 15081d1e34ddSDavid S. Miller } 15099825069dSThomas Graf nlmsg_end(skb, nlh); 15101da177e4SLinus Torvalds return 0; 15111da177e4SLinus Torvalds } 15121da177e4SLinus Torvalds 15134c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 15144c563f76STimo Teras { 15154c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 15164c563f76STimo Teras 15174c563f76STimo Teras xfrm_policy_walk_done(walk); 15184c563f76STimo Teras return 0; 15194c563f76STimo Teras } 15204c563f76STimo Teras 15211da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 15221da177e4SLinus Torvalds { 1523fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 15244c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 15251da177e4SLinus Torvalds struct xfrm_dump_info info; 15261da177e4SLinus Torvalds 15274c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 15284c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 15294c563f76STimo Teras 15301da177e4SLinus Torvalds info.in_skb = cb->skb; 15311da177e4SLinus Torvalds info.out_skb = skb; 15321da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 15331da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 15344c563f76STimo Teras 15354c563f76STimo Teras if (!cb->args[0]) { 15364c563f76STimo Teras cb->args[0] = 1; 15374c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 15384c563f76STimo Teras } 15394c563f76STimo Teras 1540fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 15411da177e4SLinus Torvalds 15421da177e4SLinus Torvalds return skb->len; 15431da177e4SLinus Torvalds } 15441da177e4SLinus Torvalds 15451da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 15461da177e4SLinus Torvalds struct xfrm_policy *xp, 15471da177e4SLinus Torvalds int dir, u32 seq) 15481da177e4SLinus Torvalds { 15491da177e4SLinus Torvalds struct xfrm_dump_info info; 15501da177e4SLinus Torvalds struct sk_buff *skb; 1551*c2546372SMathias Krause int err; 15521da177e4SLinus Torvalds 15537deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 15541da177e4SLinus Torvalds if (!skb) 15551da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 15561da177e4SLinus Torvalds 15571da177e4SLinus Torvalds info.in_skb = in_skb; 15581da177e4SLinus Torvalds info.out_skb = skb; 15591da177e4SLinus Torvalds info.nlmsg_seq = seq; 15601da177e4SLinus Torvalds info.nlmsg_flags = 0; 15611da177e4SLinus Torvalds 1562*c2546372SMathias Krause err = dump_one_policy(xp, dir, 0, &info); 1563*c2546372SMathias Krause if (err) { 15641da177e4SLinus Torvalds kfree_skb(skb); 1565*c2546372SMathias Krause return ERR_PTR(err); 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds 15681da177e4SLinus Torvalds return skb; 15691da177e4SLinus Torvalds } 15701da177e4SLinus Torvalds 157122e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 15725424f32eSThomas Graf struct nlattr **attrs) 15731da177e4SLinus Torvalds { 1574fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 15751da177e4SLinus Torvalds struct xfrm_policy *xp; 15761da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1577b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 15781da177e4SLinus Torvalds int err; 157926b15dadSJamal Hadi Salim struct km_event c; 15801da177e4SLinus Torvalds int delete; 1581295fae56SJamal Hadi Salim struct xfrm_mark m; 1582295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 15831da177e4SLinus Torvalds 15847b67c857SThomas Graf p = nlmsg_data(nlh); 15851da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 15861da177e4SLinus Torvalds 158735a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1588f7b6983fSMasahide NAKAMURA if (err) 1589f7b6983fSMasahide NAKAMURA return err; 1590f7b6983fSMasahide NAKAMURA 15911da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 15921da177e4SLinus Torvalds if (err) 15931da177e4SLinus Torvalds return err; 15941da177e4SLinus Torvalds 15951da177e4SLinus Torvalds if (p->index) 1596295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err); 1597df71837dSTrent Jaeger else { 15985424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 159903e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1600df71837dSTrent Jaeger 160135a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1602df71837dSTrent Jaeger if (err) 1603df71837dSTrent Jaeger return err; 1604df71837dSTrent Jaeger 16052c8dd116SDenis V. Lunev ctx = NULL; 1606df71837dSTrent Jaeger if (rt) { 16075424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1608df71837dSTrent Jaeger 160903e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 161003e1ad7bSPaul Moore if (err) 1611df71837dSTrent Jaeger return err; 16122c8dd116SDenis V. Lunev } 1613295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel, 16146f26b61eSJamal Hadi Salim ctx, delete, &err); 161503e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1616df71837dSTrent Jaeger } 16171da177e4SLinus Torvalds if (xp == NULL) 16181da177e4SLinus Torvalds return -ENOENT; 16191da177e4SLinus Torvalds 16201da177e4SLinus Torvalds if (!delete) { 16211da177e4SLinus Torvalds struct sk_buff *resp_skb; 16221da177e4SLinus Torvalds 16231da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 16241da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 16251da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 16261da177e4SLinus Torvalds } else { 1627a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 1628082a1ad5SThomas Graf NETLINK_CB(skb).pid); 16291da177e4SLinus Torvalds } 163026b15dadSJamal Hadi Salim } else { 1631c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1632c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1633c53fa1edSPatrick McHardy u32 sid; 16342532386fSEric Paris 1635c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 16362532386fSEric Paris xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, 16372532386fSEric Paris sid); 163813fcfbb0SDavid S. Miller 163913fcfbb0SDavid S. Miller if (err != 0) 1640c8c05a8eSCatherine Zhang goto out; 164113fcfbb0SDavid S. Miller 1642e7443892SHerbert Xu c.data.byid = p->index; 1643f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 164426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 164526b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 164626b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds 1649c8c05a8eSCatherine Zhang out: 1650ef41aaa0SEric Paris xfrm_pol_put(xp); 16511da177e4SLinus Torvalds return err; 16521da177e4SLinus Torvalds } 16531da177e4SLinus Torvalds 165422e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 16555424f32eSThomas Graf struct nlattr **attrs) 16561da177e4SLinus Torvalds { 1657fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 165826b15dadSJamal Hadi Salim struct km_event c; 16597b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1660161a09e7SJoy Latten struct xfrm_audit audit_info; 16614aa2e62cSJoy Latten int err; 16621da177e4SLinus Torvalds 1663c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1664c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1665c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1666fc34acd3SAlexey Dobriyan err = xfrm_state_flush(net, p->proto, &audit_info); 16679e64cc95SJamal Hadi Salim if (err) { 16689e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */ 16699e64cc95SJamal Hadi Salim return 0; 1670069c474eSDavid S. Miller return err; 16719e64cc95SJamal Hadi Salim } 1672bf08867fSHerbert Xu c.data.proto = p->proto; 1673f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 167426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 167526b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 16767067802eSAlexey Dobriyan c.net = net; 167726b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 167826b15dadSJamal Hadi Salim 16791da177e4SLinus Torvalds return 0; 16801da177e4SLinus Torvalds } 16811da177e4SLinus Torvalds 1682d8647b79SSteffen Klassert static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x) 16837deb2264SThomas Graf { 1684d8647b79SSteffen Klassert size_t replay_size = x->replay_esn ? 1685d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn) : 1686d8647b79SSteffen Klassert sizeof(struct xfrm_replay_state); 1687d8647b79SSteffen Klassert 16887deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 1689d8647b79SSteffen Klassert + nla_total_size(replay_size) 16907deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 16916f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 16927deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 16937deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 16947deb2264SThomas Graf } 1695d51d081dSJamal Hadi Salim 1696214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 1697d51d081dSJamal Hadi Salim { 1698d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1699d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 17001d1e34ddSDavid S. Miller int err; 1701d51d081dSJamal Hadi Salim 170279b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 170379b8b7f4SThomas Graf if (nlh == NULL) 170479b8b7f4SThomas Graf return -EMSGSIZE; 1705d51d081dSJamal Hadi Salim 17067b67c857SThomas Graf id = nlmsg_data(nlh); 17072b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1708d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1709d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1710d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 17112b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 17122b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1713d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1714d51d081dSJamal Hadi Salim 1715d0fde795SDavid S. Miller if (x->replay_esn) { 17161d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 1717d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn), 17181d1e34ddSDavid S. Miller x->replay_esn); 1719d0fde795SDavid S. Miller } else { 17201d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), 17211d1e34ddSDavid S. Miller &x->replay); 1722d0fde795SDavid S. Miller } 17231d1e34ddSDavid S. Miller if (err) 17241d1e34ddSDavid S. Miller goto out_cancel; 17251d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 17261d1e34ddSDavid S. Miller if (err) 17271d1e34ddSDavid S. Miller goto out_cancel; 1728d8647b79SSteffen Klassert 17291d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_RTHR) { 17301d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 17311d1e34ddSDavid S. Miller if (err) 17321d1e34ddSDavid S. Miller goto out_cancel; 17331d1e34ddSDavid S. Miller } 17341d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_ETHR) { 17351d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_ETIMER_THRESH, 17361d1e34ddSDavid S. Miller x->replay_maxage * 10 / HZ); 17371d1e34ddSDavid S. Miller if (err) 17381d1e34ddSDavid S. Miller goto out_cancel; 17391d1e34ddSDavid S. Miller } 17401d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 17411d1e34ddSDavid S. Miller if (err) 17421d1e34ddSDavid S. Miller goto out_cancel; 17436f26b61eSJamal Hadi Salim 17449825069dSThomas Graf return nlmsg_end(skb, nlh); 1745d51d081dSJamal Hadi Salim 17461d1e34ddSDavid S. Miller out_cancel: 17479825069dSThomas Graf nlmsg_cancel(skb, nlh); 17481d1e34ddSDavid S. Miller return err; 1749d51d081dSJamal Hadi Salim } 1750d51d081dSJamal Hadi Salim 175122e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 17525424f32eSThomas Graf struct nlattr **attrs) 1753d51d081dSJamal Hadi Salim { 1754fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1755d51d081dSJamal Hadi Salim struct xfrm_state *x; 1756d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1757d51d081dSJamal Hadi Salim int err; 1758d51d081dSJamal Hadi Salim struct km_event c; 17596f26b61eSJamal Hadi Salim u32 mark; 17606f26b61eSJamal Hadi Salim struct xfrm_mark m; 17617b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1762d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1763d51d081dSJamal Hadi Salim 17646f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 17656f26b61eSJamal Hadi Salim 17666f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); 1767d8647b79SSteffen Klassert if (x == NULL) 1768d51d081dSJamal Hadi Salim return -ESRCH; 1769d8647b79SSteffen Klassert 1770d8647b79SSteffen Klassert r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 1771d8647b79SSteffen Klassert if (r_skb == NULL) { 1772d8647b79SSteffen Klassert xfrm_state_put(x); 1773d8647b79SSteffen Klassert return -ENOMEM; 1774d51d081dSJamal Hadi Salim } 1775d51d081dSJamal Hadi Salim 1776d51d081dSJamal Hadi Salim /* 1777d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1778d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1779d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1780d51d081dSJamal Hadi Salim */ 1781d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1782d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1783d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1784d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1785d51d081dSJamal Hadi Salim 1786d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1787d51d081dSJamal Hadi Salim BUG(); 1788a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid); 1789d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1790d51d081dSJamal Hadi Salim xfrm_state_put(x); 1791d51d081dSJamal Hadi Salim return err; 1792d51d081dSJamal Hadi Salim } 1793d51d081dSJamal Hadi Salim 179422e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 17955424f32eSThomas Graf struct nlattr **attrs) 1796d51d081dSJamal Hadi Salim { 1797fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1798d51d081dSJamal Hadi Salim struct xfrm_state *x; 1799d51d081dSJamal Hadi Salim struct km_event c; 1800d51d081dSJamal Hadi Salim int err = - EINVAL; 18016f26b61eSJamal Hadi Salim u32 mark = 0; 18026f26b61eSJamal Hadi Salim struct xfrm_mark m; 18037b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 18045424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 1805d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 18065424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1807d51d081dSJamal Hadi Salim 1808d8647b79SSteffen Klassert if (!lt && !rp && !re) 1809d51d081dSJamal Hadi Salim return err; 1810d51d081dSJamal Hadi Salim 1811d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1812d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1813d51d081dSJamal Hadi Salim return err; 1814d51d081dSJamal Hadi Salim 18156f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 18166f26b61eSJamal Hadi Salim 18176f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1818d51d081dSJamal Hadi Salim if (x == NULL) 1819d51d081dSJamal Hadi Salim return -ESRCH; 1820d51d081dSJamal Hadi Salim 1821d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1822d51d081dSJamal Hadi Salim goto out; 1823d51d081dSJamal Hadi Salim 1824e2b19125SSteffen Klassert err = xfrm_replay_verify_len(x->replay_esn, rp); 1825e2b19125SSteffen Klassert if (err) 1826e2b19125SSteffen Klassert goto out; 1827e2b19125SSteffen Klassert 1828d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 182935a7aa08SThomas Graf xfrm_update_ae_params(x, attrs); 1830d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1831d51d081dSJamal Hadi Salim 1832d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1833d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1834d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1835d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1836d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1837d51d081dSJamal Hadi Salim err = 0; 1838d51d081dSJamal Hadi Salim out: 1839d51d081dSJamal Hadi Salim xfrm_state_put(x); 1840d51d081dSJamal Hadi Salim return err; 1841d51d081dSJamal Hadi Salim } 1842d51d081dSJamal Hadi Salim 184322e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 18445424f32eSThomas Graf struct nlattr **attrs) 18451da177e4SLinus Torvalds { 1846fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 184726b15dadSJamal Hadi Salim struct km_event c; 1848b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1849f7b6983fSMasahide NAKAMURA int err; 1850161a09e7SJoy Latten struct xfrm_audit audit_info; 185126b15dadSJamal Hadi Salim 185235a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1853f7b6983fSMasahide NAKAMURA if (err) 1854f7b6983fSMasahide NAKAMURA return err; 1855f7b6983fSMasahide NAKAMURA 1856c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1857c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1858c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1859fc34acd3SAlexey Dobriyan err = xfrm_policy_flush(net, type, &audit_info); 18602f1eb65fSJamal Hadi Salim if (err) { 18612f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */ 18622f1eb65fSJamal Hadi Salim return 0; 1863069c474eSDavid S. Miller return err; 18642f1eb65fSJamal Hadi Salim } 18652f1eb65fSJamal Hadi Salim 1866f7b6983fSMasahide NAKAMURA c.data.type = type; 1867f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 186826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 186926b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 18707067802eSAlexey Dobriyan c.net = net; 187126b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 18721da177e4SLinus Torvalds return 0; 18731da177e4SLinus Torvalds } 18741da177e4SLinus Torvalds 187522e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 18765424f32eSThomas Graf struct nlattr **attrs) 18776c5c8ca7SJamal Hadi Salim { 1878fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 18796c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 18807b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 18816c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1882b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 18836c5c8ca7SJamal Hadi Salim int err = -ENOENT; 1884295fae56SJamal Hadi Salim struct xfrm_mark m; 1885295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 18866c5c8ca7SJamal Hadi Salim 188735a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1888f7b6983fSMasahide NAKAMURA if (err) 1889f7b6983fSMasahide NAKAMURA return err; 1890f7b6983fSMasahide NAKAMURA 1891c8bf4d04STimo Teräs err = verify_policy_dir(p->dir); 1892c8bf4d04STimo Teräs if (err) 1893c8bf4d04STimo Teräs return err; 1894c8bf4d04STimo Teräs 18956c5c8ca7SJamal Hadi Salim if (p->index) 1896295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err); 18976c5c8ca7SJamal Hadi Salim else { 18985424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 189903e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 19006c5c8ca7SJamal Hadi Salim 190135a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 19026c5c8ca7SJamal Hadi Salim if (err) 19036c5c8ca7SJamal Hadi Salim return err; 19046c5c8ca7SJamal Hadi Salim 19052c8dd116SDenis V. Lunev ctx = NULL; 19066c5c8ca7SJamal Hadi Salim if (rt) { 19075424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 19086c5c8ca7SJamal Hadi Salim 190903e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 191003e1ad7bSPaul Moore if (err) 19116c5c8ca7SJamal Hadi Salim return err; 19122c8dd116SDenis V. Lunev } 1913295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, 19146f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err); 191503e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 19166c5c8ca7SJamal Hadi Salim } 19176c5c8ca7SJamal Hadi Salim if (xp == NULL) 1918ef41aaa0SEric Paris return -ENOENT; 191903e1ad7bSPaul Moore 1920ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead)) 19216c5c8ca7SJamal Hadi Salim goto out; 19226c5c8ca7SJamal Hadi Salim 19236c5c8ca7SJamal Hadi Salim err = 0; 19246c5c8ca7SJamal Hadi Salim if (up->hard) { 1925c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1926c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1927c53fa1edSPatrick McHardy u32 sid; 1928c53fa1edSPatrick McHardy 1929c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 19306c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 19312532386fSEric Paris xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); 1932161a09e7SJoy Latten 19336c5c8ca7SJamal Hadi Salim } else { 19346c5c8ca7SJamal Hadi Salim // reset the timers here? 193562db5cfdSstephen hemminger WARN(1, "Dont know what to do with soft policy expire\n"); 19366c5c8ca7SJamal Hadi Salim } 19376c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 19386c5c8ca7SJamal Hadi Salim 19396c5c8ca7SJamal Hadi Salim out: 19406c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 19416c5c8ca7SJamal Hadi Salim return err; 19426c5c8ca7SJamal Hadi Salim } 19436c5c8ca7SJamal Hadi Salim 194422e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 19455424f32eSThomas Graf struct nlattr **attrs) 194653bc6b4dSJamal Hadi Salim { 1947fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 194853bc6b4dSJamal Hadi Salim struct xfrm_state *x; 194953bc6b4dSJamal Hadi Salim int err; 19507b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 195153bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 19526f26b61eSJamal Hadi Salim struct xfrm_mark m; 1953928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m); 195453bc6b4dSJamal Hadi Salim 19556f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family); 195653bc6b4dSJamal Hadi Salim 19573a765aa5SDavid S. Miller err = -ENOENT; 195853bc6b4dSJamal Hadi Salim if (x == NULL) 195953bc6b4dSJamal Hadi Salim return err; 196053bc6b4dSJamal Hadi Salim 196153bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 19623a765aa5SDavid S. Miller err = -EINVAL; 196353bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 196453bc6b4dSJamal Hadi Salim goto out; 196553bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 196653bc6b4dSJamal Hadi Salim 1967161a09e7SJoy Latten if (ue->hard) { 1968c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1969c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1970c53fa1edSPatrick McHardy u32 sid; 1971c53fa1edSPatrick McHardy 1972c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 197353bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 19742532386fSEric Paris xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); 1975161a09e7SJoy Latten } 19763a765aa5SDavid S. Miller err = 0; 197753bc6b4dSJamal Hadi Salim out: 197853bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 197953bc6b4dSJamal Hadi Salim xfrm_state_put(x); 198053bc6b4dSJamal Hadi Salim return err; 198153bc6b4dSJamal Hadi Salim } 198253bc6b4dSJamal Hadi Salim 198322e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 19845424f32eSThomas Graf struct nlattr **attrs) 1985980ebd25SJamal Hadi Salim { 1986fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1987980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1988980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1989980ebd25SJamal Hadi Salim int i; 19905424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 19916f26b61eSJamal Hadi Salim struct xfrm_mark mark; 1992980ebd25SJamal Hadi Salim 19937b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 1994fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 1995980ebd25SJamal Hadi Salim int err = -ENOMEM; 1996980ebd25SJamal Hadi Salim 1997980ebd25SJamal Hadi Salim if (!x) 1998d8eb9307SIlpo Järvinen goto nomem; 1999980ebd25SJamal Hadi Salim 20006f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark); 20016f26b61eSJamal Hadi Salim 2002980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 2003d8eb9307SIlpo Järvinen if (err) 2004d8eb9307SIlpo Järvinen goto bad_policy; 2005980ebd25SJamal Hadi Salim 2006980ebd25SJamal Hadi Salim /* build an XP */ 2007fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 2008d8eb9307SIlpo Järvinen if (!xp) 2009d8eb9307SIlpo Järvinen goto free_state; 2010980ebd25SJamal Hadi Salim 2011980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 2012980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 2013980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 20146f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m; 20156f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v; 20165424f32eSThomas Graf ut = nla_data(rt); 2017980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 2018980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 2019980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 2020980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 2021980ebd25SJamal Hadi Salim x->props.mode = t->mode; 2022980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 2023980ebd25SJamal Hadi Salim x->props.family = ut->family; 2024980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 2025980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 2026980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 2027980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 2028980ebd25SJamal Hadi Salim 2029980ebd25SJamal Hadi Salim } 2030980ebd25SJamal Hadi Salim 2031980ebd25SJamal Hadi Salim kfree(x); 2032980ebd25SJamal Hadi Salim kfree(xp); 2033980ebd25SJamal Hadi Salim 2034980ebd25SJamal Hadi Salim return 0; 2035d8eb9307SIlpo Järvinen 2036d8eb9307SIlpo Järvinen bad_policy: 203762db5cfdSstephen hemminger WARN(1, "BAD policy passed\n"); 2038d8eb9307SIlpo Järvinen free_state: 2039d8eb9307SIlpo Järvinen kfree(x); 2040d8eb9307SIlpo Järvinen nomem: 2041d8eb9307SIlpo Järvinen return err; 2042980ebd25SJamal Hadi Salim } 2043980ebd25SJamal Hadi Salim 20445c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 20455c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 204613c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 20475424f32eSThomas Graf struct nlattr **attrs, int *num) 20485c79de6eSShinta Sugimoto { 20495424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 20505c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 20515c79de6eSShinta Sugimoto int i, num_migrate; 20525c79de6eSShinta Sugimoto 205313c1d189SArnaud Ebalard if (k != NULL) { 205413c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 205513c1d189SArnaud Ebalard 205613c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 205713c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 205813c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 205913c1d189SArnaud Ebalard k->family = uk->family; 206013c1d189SArnaud Ebalard k->reserved = uk->reserved; 206113c1d189SArnaud Ebalard } 206213c1d189SArnaud Ebalard 20635424f32eSThomas Graf um = nla_data(rt); 20645424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 20655c79de6eSShinta Sugimoto 20665c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 20675c79de6eSShinta Sugimoto return -EINVAL; 20685c79de6eSShinta Sugimoto 20695c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 20705c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 20715c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 20725c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 20735c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 20745c79de6eSShinta Sugimoto 20755c79de6eSShinta Sugimoto ma->proto = um->proto; 20765c79de6eSShinta Sugimoto ma->mode = um->mode; 20775c79de6eSShinta Sugimoto ma->reqid = um->reqid; 20785c79de6eSShinta Sugimoto 20795c79de6eSShinta Sugimoto ma->old_family = um->old_family; 20805c79de6eSShinta Sugimoto ma->new_family = um->new_family; 20815c79de6eSShinta Sugimoto } 20825c79de6eSShinta Sugimoto 20835c79de6eSShinta Sugimoto *num = i; 20845c79de6eSShinta Sugimoto return 0; 20855c79de6eSShinta Sugimoto } 20865c79de6eSShinta Sugimoto 20875c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 20885424f32eSThomas Graf struct nlattr **attrs) 20895c79de6eSShinta Sugimoto { 20907b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 20915c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 209213c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 20935c79de6eSShinta Sugimoto u8 type; 20945c79de6eSShinta Sugimoto int err; 20955c79de6eSShinta Sugimoto int n = 0; 20965c79de6eSShinta Sugimoto 209735a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 2098cf5cb79fSThomas Graf return -EINVAL; 20995c79de6eSShinta Sugimoto 210013c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 210113c1d189SArnaud Ebalard 21025424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 21035c79de6eSShinta Sugimoto if (err) 21045c79de6eSShinta Sugimoto return err; 21055c79de6eSShinta Sugimoto 210613c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 21075c79de6eSShinta Sugimoto if (err) 21085c79de6eSShinta Sugimoto return err; 21095c79de6eSShinta Sugimoto 21105c79de6eSShinta Sugimoto if (!n) 21115c79de6eSShinta Sugimoto return 0; 21125c79de6eSShinta Sugimoto 211313c1d189SArnaud Ebalard xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); 21145c79de6eSShinta Sugimoto 21155c79de6eSShinta Sugimoto return 0; 21165c79de6eSShinta Sugimoto } 21175c79de6eSShinta Sugimoto #else 21185c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 21195424f32eSThomas Graf struct nlattr **attrs) 21205c79de6eSShinta Sugimoto { 21215c79de6eSShinta Sugimoto return -ENOPROTOOPT; 21225c79de6eSShinta Sugimoto } 21235c79de6eSShinta Sugimoto #endif 21245c79de6eSShinta Sugimoto 21255c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 2126183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) 21275c79de6eSShinta Sugimoto { 21285c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 21295c79de6eSShinta Sugimoto 21305c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 21315c79de6eSShinta Sugimoto um.proto = m->proto; 21325c79de6eSShinta Sugimoto um.mode = m->mode; 21335c79de6eSShinta Sugimoto um.reqid = m->reqid; 21345c79de6eSShinta Sugimoto um.old_family = m->old_family; 21355c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 21365c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 21375c79de6eSShinta Sugimoto um.new_family = m->new_family; 21385c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 21395c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 21405c79de6eSShinta Sugimoto 2141c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 21425c79de6eSShinta Sugimoto } 21435c79de6eSShinta Sugimoto 2144183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) 214513c1d189SArnaud Ebalard { 214613c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 214713c1d189SArnaud Ebalard 214813c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 214913c1d189SArnaud Ebalard uk.family = k->family; 215013c1d189SArnaud Ebalard uk.reserved = k->reserved; 215113c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 2152a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 215313c1d189SArnaud Ebalard 215413c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 215513c1d189SArnaud Ebalard } 215613c1d189SArnaud Ebalard 215713c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 21587deb2264SThomas Graf { 21597deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 216013c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 21617deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 21627deb2264SThomas Graf + userpolicy_type_attrsize(); 21637deb2264SThomas Graf } 21647deb2264SThomas Graf 2165183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, 2166183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k, 2167183cad12SDavid S. Miller const struct xfrm_selector *sel, u8 dir, u8 type) 21685c79de6eSShinta Sugimoto { 2169183cad12SDavid S. Miller const struct xfrm_migrate *mp; 21705c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 21715c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 21721d1e34ddSDavid S. Miller int i, err; 21735c79de6eSShinta Sugimoto 217479b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 217579b8b7f4SThomas Graf if (nlh == NULL) 217679b8b7f4SThomas Graf return -EMSGSIZE; 21775c79de6eSShinta Sugimoto 21787b67c857SThomas Graf pol_id = nlmsg_data(nlh); 21795c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 21805c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 21815c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 21825c79de6eSShinta Sugimoto pol_id->dir = dir; 21835c79de6eSShinta Sugimoto 21841d1e34ddSDavid S. Miller if (k != NULL) { 21851d1e34ddSDavid S. Miller err = copy_to_user_kmaddress(k, skb); 21861d1e34ddSDavid S. Miller if (err) 21871d1e34ddSDavid S. Miller goto out_cancel; 21881d1e34ddSDavid S. Miller } 21891d1e34ddSDavid S. Miller err = copy_to_user_policy_type(type, skb); 21901d1e34ddSDavid S. Miller if (err) 21911d1e34ddSDavid S. Miller goto out_cancel; 21925c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 21931d1e34ddSDavid S. Miller err = copy_to_user_migrate(mp, skb); 21941d1e34ddSDavid S. Miller if (err) 21951d1e34ddSDavid S. Miller goto out_cancel; 21965c79de6eSShinta Sugimoto } 21975c79de6eSShinta Sugimoto 21989825069dSThomas Graf return nlmsg_end(skb, nlh); 21991d1e34ddSDavid S. Miller 22001d1e34ddSDavid S. Miller out_cancel: 22019825069dSThomas Graf nlmsg_cancel(skb, nlh); 22021d1e34ddSDavid S. Miller return err; 22035c79de6eSShinta Sugimoto } 22045c79de6eSShinta Sugimoto 2205183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2206183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2207183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 22085c79de6eSShinta Sugimoto { 2209a6483b79SAlexey Dobriyan struct net *net = &init_net; 22105c79de6eSShinta Sugimoto struct sk_buff *skb; 22115c79de6eSShinta Sugimoto 221213c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 22135c79de6eSShinta Sugimoto if (skb == NULL) 22145c79de6eSShinta Sugimoto return -ENOMEM; 22155c79de6eSShinta Sugimoto 22165c79de6eSShinta Sugimoto /* build migrate */ 221713c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 22185c79de6eSShinta Sugimoto BUG(); 22195c79de6eSShinta Sugimoto 2220a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 22215c79de6eSShinta Sugimoto } 22225c79de6eSShinta Sugimoto #else 2223183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2224183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2225183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 22265c79de6eSShinta Sugimoto { 22275c79de6eSShinta Sugimoto return -ENOPROTOOPT; 22285c79de6eSShinta Sugimoto } 22295c79de6eSShinta Sugimoto #endif 2230d51d081dSJamal Hadi Salim 2231a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2232492b558bSThomas Graf 2233492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 223466f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2235492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2236492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2237492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2238492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2239492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2240492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2241980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 224253bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2243492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 224466f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 22456c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2246492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2247a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2248d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2249d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 225097a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 22515c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2252a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2253a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 22541da177e4SLinus Torvalds }; 22551da177e4SLinus Torvalds 2256492b558bSThomas Graf #undef XMSGSIZE 2257492b558bSThomas Graf 2258cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 2259c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)}, 2260c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)}, 2261c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64}, 2262c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 22631a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2264cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2265cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2266cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2267cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2268cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2269cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2270cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2271cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2272cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2273cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2274cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2275cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2276cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2277cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 227813c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 22796f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 228035d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2281d8647b79SSteffen Klassert [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 2282cf5cb79fSThomas Graf }; 2283cf5cb79fSThomas Graf 22841da177e4SLinus Torvalds static struct xfrm_link { 22855424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 22861da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 22874c563f76STimo Teras int (*done)(struct netlink_callback *); 2288492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2289492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2290492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2291492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 22924c563f76STimo Teras .dump = xfrm_dump_sa, 22934c563f76STimo Teras .done = xfrm_dump_sa_done }, 2294492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2295492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2296492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 22974c563f76STimo Teras .dump = xfrm_dump_policy, 22984c563f76STimo Teras .done = xfrm_dump_policy_done }, 2299492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2300980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 230153bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2302492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2303492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 23046c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2305492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2306492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2307d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2308d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 23095c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 231028d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2311ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 23121da177e4SLinus Torvalds }; 23131da177e4SLinus Torvalds 23141d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 23151da177e4SLinus Torvalds { 2316a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 231735a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 23181da177e4SLinus Torvalds struct xfrm_link *link; 2319a7bd9a45SThomas Graf int type, err; 23201da177e4SLinus Torvalds 23211da177e4SLinus Torvalds type = nlh->nlmsg_type; 23221da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 23231d00a4ebSThomas Graf return -EINVAL; 23241da177e4SLinus Torvalds 23251da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 23261da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 23271da177e4SLinus Torvalds 23281da177e4SLinus Torvalds /* All operations require privileges, even GET */ 2329fd778461SEric Paris if (!capable(CAP_NET_ADMIN)) 23301d00a4ebSThomas Graf return -EPERM; 23311da177e4SLinus Torvalds 2332492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2333492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2334b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) { 23351da177e4SLinus Torvalds if (link->dump == NULL) 23361d00a4ebSThomas Graf return -EINVAL; 23371da177e4SLinus Torvalds 233880d326faSPablo Neira Ayuso { 233980d326faSPablo Neira Ayuso struct netlink_dump_control c = { 234080d326faSPablo Neira Ayuso .dump = link->dump, 234180d326faSPablo Neira Ayuso .done = link->done, 234280d326faSPablo Neira Ayuso }; 234380d326faSPablo Neira Ayuso return netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c); 234480d326faSPablo Neira Ayuso } 23451da177e4SLinus Torvalds } 23461da177e4SLinus Torvalds 234735a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2348cf5cb79fSThomas Graf xfrma_policy); 2349a7bd9a45SThomas Graf if (err < 0) 2350a7bd9a45SThomas Graf return err; 23511da177e4SLinus Torvalds 23521da177e4SLinus Torvalds if (link->doit == NULL) 23531d00a4ebSThomas Graf return -EINVAL; 23541da177e4SLinus Torvalds 23555424f32eSThomas Graf return link->doit(skb, nlh, attrs); 23561da177e4SLinus Torvalds } 23571da177e4SLinus Torvalds 2358cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 23591da177e4SLinus Torvalds { 23604a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 2361cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 23624a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 23631da177e4SLinus Torvalds } 23641da177e4SLinus Torvalds 23657deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 23667deb2264SThomas Graf { 23676f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) 23686f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)); 23697deb2264SThomas Graf } 23707deb2264SThomas Graf 2371214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 23721da177e4SLinus Torvalds { 23731da177e4SLinus Torvalds struct xfrm_user_expire *ue; 23741da177e4SLinus Torvalds struct nlmsghdr *nlh; 23751d1e34ddSDavid S. Miller int err; 23761da177e4SLinus Torvalds 237779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 237879b8b7f4SThomas Graf if (nlh == NULL) 237979b8b7f4SThomas Graf return -EMSGSIZE; 23801da177e4SLinus Torvalds 23817b67c857SThomas Graf ue = nlmsg_data(nlh); 23821da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2383d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 23841da177e4SLinus Torvalds 23851d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 23861d1e34ddSDavid S. Miller if (err) 23871d1e34ddSDavid S. Miller return err; 23886f26b61eSJamal Hadi Salim 23899825069dSThomas Graf return nlmsg_end(skb, nlh); 23901da177e4SLinus Torvalds } 23911da177e4SLinus Torvalds 2392214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) 23931da177e4SLinus Torvalds { 2394fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 23951da177e4SLinus Torvalds struct sk_buff *skb; 23961da177e4SLinus Torvalds 23977deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 23981da177e4SLinus Torvalds if (skb == NULL) 23991da177e4SLinus Torvalds return -ENOMEM; 24001da177e4SLinus Torvalds 24016f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) { 24026f26b61eSJamal Hadi Salim kfree_skb(skb); 24036f26b61eSJamal Hadi Salim return -EMSGSIZE; 24046f26b61eSJamal Hadi Salim } 24051da177e4SLinus Torvalds 2406a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 24071da177e4SLinus Torvalds } 24081da177e4SLinus Torvalds 2409214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) 2410d51d081dSJamal Hadi Salim { 2411fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2412d51d081dSJamal Hadi Salim struct sk_buff *skb; 2413d51d081dSJamal Hadi Salim 2414d8647b79SSteffen Klassert skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 2415d51d081dSJamal Hadi Salim if (skb == NULL) 2416d51d081dSJamal Hadi Salim return -ENOMEM; 2417d51d081dSJamal Hadi Salim 2418d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2419d51d081dSJamal Hadi Salim BUG(); 2420d51d081dSJamal Hadi Salim 2421a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2422d51d081dSJamal Hadi Salim } 2423d51d081dSJamal Hadi Salim 2424214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c) 242526b15dadSJamal Hadi Salim { 24267067802eSAlexey Dobriyan struct net *net = c->net; 242726b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 242826b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 242926b15dadSJamal Hadi Salim struct sk_buff *skb; 24307deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 243126b15dadSJamal Hadi Salim 24327deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 243326b15dadSJamal Hadi Salim if (skb == NULL) 243426b15dadSJamal Hadi Salim return -ENOMEM; 243526b15dadSJamal Hadi Salim 243679b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 243779b8b7f4SThomas Graf if (nlh == NULL) { 243879b8b7f4SThomas Graf kfree_skb(skb); 243979b8b7f4SThomas Graf return -EMSGSIZE; 244079b8b7f4SThomas Graf } 244126b15dadSJamal Hadi Salim 24427b67c857SThomas Graf p = nlmsg_data(nlh); 2443bf08867fSHerbert Xu p->proto = c->data.proto; 244426b15dadSJamal Hadi Salim 24459825069dSThomas Graf nlmsg_end(skb, nlh); 244626b15dadSJamal Hadi Salim 2447a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 244826b15dadSJamal Hadi Salim } 244926b15dadSJamal Hadi Salim 24507deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 245126b15dadSJamal Hadi Salim { 24527deb2264SThomas Graf size_t l = 0; 24531a6509d9SHerbert Xu if (x->aead) 24541a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 24554447bb33SMartin Willi if (x->aalg) { 24564447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 24574447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 24584447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 24594447bb33SMartin Willi } 246026b15dadSJamal Hadi Salim if (x->ealg) 24610f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 246226b15dadSJamal Hadi Salim if (x->calg) 24637deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 246426b15dadSJamal Hadi Salim if (x->encap) 24657deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 246635d2856bSMartin Willi if (x->tfcpad) 246735d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad)); 2468d8647b79SSteffen Klassert if (x->replay_esn) 2469d8647b79SSteffen Klassert l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn)); 247068325d3bSHerbert Xu if (x->security) 247168325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 247268325d3bSHerbert Xu x->security->ctx_len); 247368325d3bSHerbert Xu if (x->coaddr) 247468325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 247568325d3bSHerbert Xu 2476d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2477d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 247826b15dadSJamal Hadi Salim 247926b15dadSJamal Hadi Salim return l; 248026b15dadSJamal Hadi Salim } 248126b15dadSJamal Hadi Salim 2482214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) 248326b15dadSJamal Hadi Salim { 2484fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 248526b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 24860603eac0SHerbert Xu struct xfrm_usersa_id *id; 248726b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 248826b15dadSJamal Hadi Salim struct sk_buff *skb; 248926b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 24901d1e34ddSDavid S. Miller int headlen, err; 24910603eac0SHerbert Xu 24920603eac0SHerbert Xu headlen = sizeof(*p); 24930603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 24947deb2264SThomas Graf len += nla_total_size(headlen); 24950603eac0SHerbert Xu headlen = sizeof(*id); 24966f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 24970603eac0SHerbert Xu } 24987deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 249926b15dadSJamal Hadi Salim 25007deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 250126b15dadSJamal Hadi Salim if (skb == NULL) 250226b15dadSJamal Hadi Salim return -ENOMEM; 250326b15dadSJamal Hadi Salim 250479b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 25051d1e34ddSDavid S. Miller err = -EMSGSIZE; 250679b8b7f4SThomas Graf if (nlh == NULL) 25071d1e34ddSDavid S. Miller goto out_free_skb; 250826b15dadSJamal Hadi Salim 25097b67c857SThomas Graf p = nlmsg_data(nlh); 25100603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2511c0144beaSThomas Graf struct nlattr *attr; 2512c0144beaSThomas Graf 25137b67c857SThomas Graf id = nlmsg_data(nlh); 25140603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 25150603eac0SHerbert Xu id->spi = x->id.spi; 25160603eac0SHerbert Xu id->family = x->props.family; 25170603eac0SHerbert Xu id->proto = x->id.proto; 25180603eac0SHerbert Xu 2519c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 25201d1e34ddSDavid S. Miller err = -EMSGSIZE; 2521c0144beaSThomas Graf if (attr == NULL) 25221d1e34ddSDavid S. Miller goto out_free_skb; 2523c0144beaSThomas Graf 2524c0144beaSThomas Graf p = nla_data(attr); 25250603eac0SHerbert Xu } 25261d1e34ddSDavid S. Miller err = copy_to_user_state_extra(x, p, skb); 25271d1e34ddSDavid S. Miller if (err) 25281d1e34ddSDavid S. Miller goto out_free_skb; 252926b15dadSJamal Hadi Salim 25309825069dSThomas Graf nlmsg_end(skb, nlh); 253126b15dadSJamal Hadi Salim 2532a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 253326b15dadSJamal Hadi Salim 25341d1e34ddSDavid S. Miller out_free_skb: 253526b15dadSJamal Hadi Salim kfree_skb(skb); 25361d1e34ddSDavid S. Miller return err; 253726b15dadSJamal Hadi Salim } 253826b15dadSJamal Hadi Salim 2539214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) 254026b15dadSJamal Hadi Salim { 254126b15dadSJamal Hadi Salim 254226b15dadSJamal Hadi Salim switch (c->event) { 2543f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 254426b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2545d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2546d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2547f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2548f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2549f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 255026b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2551f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 255226b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 255326b15dadSJamal Hadi Salim default: 255462db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n", 255562db5cfdSstephen hemminger c->event); 255626b15dadSJamal Hadi Salim break; 255726b15dadSJamal Hadi Salim } 255826b15dadSJamal Hadi Salim 255926b15dadSJamal Hadi Salim return 0; 256026b15dadSJamal Hadi Salim 256126b15dadSJamal Hadi Salim } 256226b15dadSJamal Hadi Salim 25637deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 25647deb2264SThomas Graf struct xfrm_policy *xp) 25657deb2264SThomas Graf { 25667deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 25677deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 25686f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 25697deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 25707deb2264SThomas Graf + userpolicy_type_attrsize(); 25717deb2264SThomas Graf } 25727deb2264SThomas Graf 25731da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 25741da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 25751da177e4SLinus Torvalds int dir) 25761da177e4SLinus Torvalds { 25771d1e34ddSDavid S. Miller __u32 seq = xfrm_get_acqseq(); 25781da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 25791da177e4SLinus Torvalds struct nlmsghdr *nlh; 25801d1e34ddSDavid S. Miller int err; 25811da177e4SLinus Torvalds 258279b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 258379b8b7f4SThomas Graf if (nlh == NULL) 258479b8b7f4SThomas Graf return -EMSGSIZE; 25851da177e4SLinus Torvalds 25867b67c857SThomas Graf ua = nlmsg_data(nlh); 25871da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 25881da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 25891da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 25901da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 25911da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 25921da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 25931da177e4SLinus Torvalds ua->calgos = xt->calgos; 25941da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 25951da177e4SLinus Torvalds 25961d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 25971d1e34ddSDavid S. Miller if (!err) 25981d1e34ddSDavid S. Miller err = copy_to_user_state_sec_ctx(x, skb); 25991d1e34ddSDavid S. Miller if (!err) 26001d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 26011d1e34ddSDavid S. Miller if (!err) 26021d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 26031d1e34ddSDavid S. Miller if (err) { 26041d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 26051d1e34ddSDavid S. Miller return err; 26061d1e34ddSDavid S. Miller } 26071da177e4SLinus Torvalds 26089825069dSThomas Graf return nlmsg_end(skb, nlh); 26091da177e4SLinus Torvalds } 26101da177e4SLinus Torvalds 26111da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 26121da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 26131da177e4SLinus Torvalds { 2614a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 26151da177e4SLinus Torvalds struct sk_buff *skb; 26161da177e4SLinus Torvalds 26177deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 26181da177e4SLinus Torvalds if (skb == NULL) 26191da177e4SLinus Torvalds return -ENOMEM; 26201da177e4SLinus Torvalds 26211da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 26221da177e4SLinus Torvalds BUG(); 26231da177e4SLinus Torvalds 2624a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 26251da177e4SLinus Torvalds } 26261da177e4SLinus Torvalds 26271da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 26281da177e4SLinus Torvalds * or more templates. 26291da177e4SLinus Torvalds */ 2630cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 26311da177e4SLinus Torvalds u8 *data, int len, int *dir) 26321da177e4SLinus Torvalds { 2633fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 26341da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 26351da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 26361da177e4SLinus Torvalds struct xfrm_policy *xp; 26371da177e4SLinus Torvalds int nr; 26381da177e4SLinus Torvalds 2639cb969f07SVenkat Yekkirala switch (sk->sk_family) { 26401da177e4SLinus Torvalds case AF_INET: 26411da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 26421da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 26431da177e4SLinus Torvalds return NULL; 26441da177e4SLinus Torvalds } 26451da177e4SLinus Torvalds break; 2646dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 26471da177e4SLinus Torvalds case AF_INET6: 26481da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 26491da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 26501da177e4SLinus Torvalds return NULL; 26511da177e4SLinus Torvalds } 26521da177e4SLinus Torvalds break; 26531da177e4SLinus Torvalds #endif 26541da177e4SLinus Torvalds default: 26551da177e4SLinus Torvalds *dir = -EINVAL; 26561da177e4SLinus Torvalds return NULL; 26571da177e4SLinus Torvalds } 26581da177e4SLinus Torvalds 26591da177e4SLinus Torvalds *dir = -EINVAL; 26601da177e4SLinus Torvalds 26611da177e4SLinus Torvalds if (len < sizeof(*p) || 26621da177e4SLinus Torvalds verify_newpolicy_info(p)) 26631da177e4SLinus Torvalds return NULL; 26641da177e4SLinus Torvalds 26651da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2666b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 26671da177e4SLinus Torvalds return NULL; 26681da177e4SLinus Torvalds 2669a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2670a4f1bac6SHerbert Xu return NULL; 2671a4f1bac6SHerbert Xu 26722f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC); 26731da177e4SLinus Torvalds if (xp == NULL) { 26741da177e4SLinus Torvalds *dir = -ENOBUFS; 26751da177e4SLinus Torvalds return NULL; 26761da177e4SLinus Torvalds } 26771da177e4SLinus Torvalds 26781da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2679f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 26801da177e4SLinus Torvalds copy_templates(xp, ut, nr); 26811da177e4SLinus Torvalds 26821da177e4SLinus Torvalds *dir = p->dir; 26831da177e4SLinus Torvalds 26841da177e4SLinus Torvalds return xp; 26851da177e4SLinus Torvalds } 26861da177e4SLinus Torvalds 26877deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 26887deb2264SThomas Graf { 26897deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 26907deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 26917deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 2692295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 26937deb2264SThomas Graf + userpolicy_type_attrsize(); 26947deb2264SThomas Graf } 26957deb2264SThomas Graf 26961da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2697214e005bSDavid S. Miller int dir, const struct km_event *c) 26981da177e4SLinus Torvalds { 26991da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 2700d51d081dSJamal Hadi Salim int hard = c->data.hard; 27011d1e34ddSDavid S. Miller struct nlmsghdr *nlh; 27021d1e34ddSDavid S. Miller int err; 27031da177e4SLinus Torvalds 270479b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 270579b8b7f4SThomas Graf if (nlh == NULL) 270679b8b7f4SThomas Graf return -EMSGSIZE; 27071da177e4SLinus Torvalds 27087b67c857SThomas Graf upe = nlmsg_data(nlh); 27091da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 27101d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 27111d1e34ddSDavid S. Miller if (!err) 27121d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 27131d1e34ddSDavid S. Miller if (!err) 27141d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 27151d1e34ddSDavid S. Miller if (!err) 27161d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 27171d1e34ddSDavid S. Miller if (err) { 27181d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 27191d1e34ddSDavid S. Miller return err; 27201d1e34ddSDavid S. Miller } 27211da177e4SLinus Torvalds upe->hard = !!hard; 27221da177e4SLinus Torvalds 27239825069dSThomas Graf return nlmsg_end(skb, nlh); 27241da177e4SLinus Torvalds } 27251da177e4SLinus Torvalds 2726214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 27271da177e4SLinus Torvalds { 2728fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 27291da177e4SLinus Torvalds struct sk_buff *skb; 27301da177e4SLinus Torvalds 27317deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 27321da177e4SLinus Torvalds if (skb == NULL) 27331da177e4SLinus Torvalds return -ENOMEM; 27341da177e4SLinus Torvalds 2735d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 27361da177e4SLinus Torvalds BUG(); 27371da177e4SLinus Torvalds 2738a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 27391da177e4SLinus Torvalds } 27401da177e4SLinus Torvalds 2741214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) 274226b15dadSJamal Hadi Salim { 27431d1e34ddSDavid S. Miller int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 2744fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 274526b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 27460603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 274726b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 274826b15dadSJamal Hadi Salim struct sk_buff *skb; 27491d1e34ddSDavid S. Miller int headlen, err; 27500603eac0SHerbert Xu 27510603eac0SHerbert Xu headlen = sizeof(*p); 27520603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 27537deb2264SThomas Graf len += nla_total_size(headlen); 27540603eac0SHerbert Xu headlen = sizeof(*id); 27550603eac0SHerbert Xu } 2756cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 2757295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 27587deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 275926b15dadSJamal Hadi Salim 27607deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 276126b15dadSJamal Hadi Salim if (skb == NULL) 276226b15dadSJamal Hadi Salim return -ENOMEM; 276326b15dadSJamal Hadi Salim 276479b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 27651d1e34ddSDavid S. Miller err = -EMSGSIZE; 276679b8b7f4SThomas Graf if (nlh == NULL) 27671d1e34ddSDavid S. Miller goto out_free_skb; 276826b15dadSJamal Hadi Salim 27697b67c857SThomas Graf p = nlmsg_data(nlh); 27700603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2771c0144beaSThomas Graf struct nlattr *attr; 2772c0144beaSThomas Graf 27737b67c857SThomas Graf id = nlmsg_data(nlh); 27740603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 27750603eac0SHerbert Xu id->dir = dir; 27760603eac0SHerbert Xu if (c->data.byid) 27770603eac0SHerbert Xu id->index = xp->index; 27780603eac0SHerbert Xu else 27790603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 27800603eac0SHerbert Xu 2781c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 27821d1e34ddSDavid S. Miller err = -EMSGSIZE; 2783c0144beaSThomas Graf if (attr == NULL) 27841d1e34ddSDavid S. Miller goto out_free_skb; 2785c0144beaSThomas Graf 2786c0144beaSThomas Graf p = nla_data(attr); 27870603eac0SHerbert Xu } 278826b15dadSJamal Hadi Salim 278926b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 27901d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 27911d1e34ddSDavid S. Miller if (!err) 27921d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 27931d1e34ddSDavid S. Miller if (!err) 27941d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 27951d1e34ddSDavid S. Miller if (err) 27961d1e34ddSDavid S. Miller goto out_free_skb; 2797295fae56SJamal Hadi Salim 27989825069dSThomas Graf nlmsg_end(skb, nlh); 279926b15dadSJamal Hadi Salim 2800a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 280126b15dadSJamal Hadi Salim 28021d1e34ddSDavid S. Miller out_free_skb: 280326b15dadSJamal Hadi Salim kfree_skb(skb); 28041d1e34ddSDavid S. Miller return err; 280526b15dadSJamal Hadi Salim } 280626b15dadSJamal Hadi Salim 2807214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c) 280826b15dadSJamal Hadi Salim { 28097067802eSAlexey Dobriyan struct net *net = c->net; 281026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 281126b15dadSJamal Hadi Salim struct sk_buff *skb; 28121d1e34ddSDavid S. Miller int err; 281326b15dadSJamal Hadi Salim 28147deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 281526b15dadSJamal Hadi Salim if (skb == NULL) 281626b15dadSJamal Hadi Salim return -ENOMEM; 281726b15dadSJamal Hadi Salim 281879b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 28191d1e34ddSDavid S. Miller err = -EMSGSIZE; 282079b8b7f4SThomas Graf if (nlh == NULL) 28211d1e34ddSDavid S. Miller goto out_free_skb; 28221d1e34ddSDavid S. Miller err = copy_to_user_policy_type(c->data.type, skb); 28231d1e34ddSDavid S. Miller if (err) 28241d1e34ddSDavid S. Miller goto out_free_skb; 282526b15dadSJamal Hadi Salim 28269825069dSThomas Graf nlmsg_end(skb, nlh); 282726b15dadSJamal Hadi Salim 2828a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 282926b15dadSJamal Hadi Salim 28301d1e34ddSDavid S. Miller out_free_skb: 283126b15dadSJamal Hadi Salim kfree_skb(skb); 28321d1e34ddSDavid S. Miller return err; 283326b15dadSJamal Hadi Salim } 283426b15dadSJamal Hadi Salim 2835214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 283626b15dadSJamal Hadi Salim { 283726b15dadSJamal Hadi Salim 283826b15dadSJamal Hadi Salim switch (c->event) { 2839f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2840f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2841f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 284226b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2843f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 284426b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2845f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 284626b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 284726b15dadSJamal Hadi Salim default: 284862db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n", 284962db5cfdSstephen hemminger c->event); 285026b15dadSJamal Hadi Salim } 285126b15dadSJamal Hadi Salim 285226b15dadSJamal Hadi Salim return 0; 285326b15dadSJamal Hadi Salim 285426b15dadSJamal Hadi Salim } 285526b15dadSJamal Hadi Salim 28567deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 28577deb2264SThomas Graf { 28587deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 28597deb2264SThomas Graf } 28607deb2264SThomas Graf 286197a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 286297a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 286397a64b45SMasahide NAKAMURA { 286497a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 286597a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 286697a64b45SMasahide NAKAMURA 286779b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 286879b8b7f4SThomas Graf if (nlh == NULL) 286979b8b7f4SThomas Graf return -EMSGSIZE; 287097a64b45SMasahide NAKAMURA 28717b67c857SThomas Graf ur = nlmsg_data(nlh); 287297a64b45SMasahide NAKAMURA ur->proto = proto; 287397a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 287497a64b45SMasahide NAKAMURA 28751d1e34ddSDavid S. Miller if (addr) { 28761d1e34ddSDavid S. Miller int err = nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr); 28771d1e34ddSDavid S. Miller if (err) { 28789825069dSThomas Graf nlmsg_cancel(skb, nlh); 28791d1e34ddSDavid S. Miller return err; 28801d1e34ddSDavid S. Miller } 28811d1e34ddSDavid S. Miller } 28821d1e34ddSDavid S. Miller return nlmsg_end(skb, nlh); 288397a64b45SMasahide NAKAMURA } 288497a64b45SMasahide NAKAMURA 2885db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 2886db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 288797a64b45SMasahide NAKAMURA { 288897a64b45SMasahide NAKAMURA struct sk_buff *skb; 288997a64b45SMasahide NAKAMURA 28907deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 289197a64b45SMasahide NAKAMURA if (skb == NULL) 289297a64b45SMasahide NAKAMURA return -ENOMEM; 289397a64b45SMasahide NAKAMURA 289497a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 289597a64b45SMasahide NAKAMURA BUG(); 289697a64b45SMasahide NAKAMURA 2897a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 289897a64b45SMasahide NAKAMURA } 289997a64b45SMasahide NAKAMURA 29003a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 29013a2dfbe8SMartin Willi { 29023a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 29033a2dfbe8SMartin Willi } 29043a2dfbe8SMartin Willi 29053a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 29063a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 29073a2dfbe8SMartin Willi { 29083a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 29093a2dfbe8SMartin Willi struct nlmsghdr *nlh; 29103a2dfbe8SMartin Willi 29113a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 29123a2dfbe8SMartin Willi if (nlh == NULL) 29133a2dfbe8SMartin Willi return -EMSGSIZE; 29143a2dfbe8SMartin Willi 29153a2dfbe8SMartin Willi um = nlmsg_data(nlh); 29163a2dfbe8SMartin Willi 29173a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 29183a2dfbe8SMartin Willi um->id.spi = x->id.spi; 29193a2dfbe8SMartin Willi um->id.family = x->props.family; 29203a2dfbe8SMartin Willi um->id.proto = x->id.proto; 29213a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 29223a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 29233a2dfbe8SMartin Willi um->new_sport = new_sport; 29243a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 29253a2dfbe8SMartin Willi um->reqid = x->props.reqid; 29263a2dfbe8SMartin Willi 29273a2dfbe8SMartin Willi return nlmsg_end(skb, nlh); 29283a2dfbe8SMartin Willi } 29293a2dfbe8SMartin Willi 29303a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 29313a2dfbe8SMartin Willi __be16 sport) 29323a2dfbe8SMartin Willi { 2933a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 29343a2dfbe8SMartin Willi struct sk_buff *skb; 29353a2dfbe8SMartin Willi 29363a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 29373a2dfbe8SMartin Willi return -EINVAL; 29383a2dfbe8SMartin Willi 29393a2dfbe8SMartin Willi if (!x->encap) 29403a2dfbe8SMartin Willi return -EINVAL; 29413a2dfbe8SMartin Willi 29423a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 29433a2dfbe8SMartin Willi if (skb == NULL) 29443a2dfbe8SMartin Willi return -ENOMEM; 29453a2dfbe8SMartin Willi 29463a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 29473a2dfbe8SMartin Willi BUG(); 29483a2dfbe8SMartin Willi 2949a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 29503a2dfbe8SMartin Willi } 29513a2dfbe8SMartin Willi 29521da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 29531da177e4SLinus Torvalds .id = "netlink", 29541da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 29551da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 29561da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 29571da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 295897a64b45SMasahide NAKAMURA .report = xfrm_send_report, 29595c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 29603a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 29611da177e4SLinus Torvalds }; 29621da177e4SLinus Torvalds 2963a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 29641da177e4SLinus Torvalds { 2965be33690dSPatrick McHardy struct sock *nlsk; 2966a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = { 2967a31f2d17SPablo Neira Ayuso .groups = XFRMNLGRP_MAX, 2968a31f2d17SPablo Neira Ayuso .input = xfrm_netlink_rcv, 2969a31f2d17SPablo Neira Ayuso }; 2970be33690dSPatrick McHardy 2971a31f2d17SPablo Neira Ayuso nlsk = netlink_kernel_create(net, NETLINK_XFRM, THIS_MODULE, &cfg); 2972be33690dSPatrick McHardy if (nlsk == NULL) 29731da177e4SLinus Torvalds return -ENOMEM; 2974d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 2975cf778b00SEric Dumazet rcu_assign_pointer(net->xfrm.nlsk, nlsk); 29761da177e4SLinus Torvalds return 0; 29771da177e4SLinus Torvalds } 29781da177e4SLinus Torvalds 2979d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 2980a6483b79SAlexey Dobriyan { 2981d79d792eSEric W. Biederman struct net *net; 2982d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2983a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(net->xfrm.nlsk, NULL); 2984d79d792eSEric W. Biederman synchronize_net(); 2985d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2986d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 2987a6483b79SAlexey Dobriyan } 2988a6483b79SAlexey Dobriyan 2989a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 2990a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 2991d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 2992a6483b79SAlexey Dobriyan }; 2993a6483b79SAlexey Dobriyan 2994a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 2995a6483b79SAlexey Dobriyan { 2996a6483b79SAlexey Dobriyan int rv; 2997a6483b79SAlexey Dobriyan 2998a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 2999a6483b79SAlexey Dobriyan 3000a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 3001a6483b79SAlexey Dobriyan if (rv < 0) 3002a6483b79SAlexey Dobriyan return rv; 3003a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 3004a6483b79SAlexey Dobriyan if (rv < 0) 3005a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 3006a6483b79SAlexey Dobriyan return rv; 3007a6483b79SAlexey Dobriyan } 3008a6483b79SAlexey Dobriyan 30091da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 30101da177e4SLinus Torvalds { 30111da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 3012a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 30131da177e4SLinus Torvalds } 30141da177e4SLinus Torvalds 30151da177e4SLinus Torvalds module_init(xfrm_user_init); 30161da177e4SLinus Torvalds module_exit(xfrm_user_exit); 30171da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 30184fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 3019f8cd5488SJamal Hadi Salim 3020