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> 31e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 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 127d8647b79SSteffen Klassert if (!rt) 128d8647b79SSteffen Klassert return 0; 129d8647b79SSteffen Klassert 130d8647b79SSteffen Klassert if (p->replay_window != 0) 131d8647b79SSteffen Klassert return -EINVAL; 132d8647b79SSteffen Klassert 133d8647b79SSteffen Klassert return 0; 134d8647b79SSteffen Klassert } 135df71837dSTrent Jaeger 1361da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1375424f32eSThomas Graf struct nlattr **attrs) 1381da177e4SLinus Torvalds { 1391da177e4SLinus Torvalds int err; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds err = -EINVAL; 1421da177e4SLinus Torvalds switch (p->family) { 1431da177e4SLinus Torvalds case AF_INET: 1441da177e4SLinus Torvalds break; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds case AF_INET6: 1471da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1481da177e4SLinus Torvalds break; 1491da177e4SLinus Torvalds #else 1501da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1511da177e4SLinus Torvalds goto out; 1521da177e4SLinus Torvalds #endif 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds default: 1551da177e4SLinus Torvalds goto out; 1563ff50b79SStephen Hemminger } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds err = -EINVAL; 1591da177e4SLinus Torvalds switch (p->id.proto) { 1601da177e4SLinus Torvalds case IPPROTO_AH: 1614447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1624447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1631a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 16435a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 16535d2856bSMartin Willi attrs[XFRMA_ALG_COMP] || 16635d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 1671da177e4SLinus Torvalds goto out; 1681da177e4SLinus Torvalds break; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds case IPPROTO_ESP: 1711a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1721a6509d9SHerbert Xu goto out; 1731a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1744447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1751a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1761a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1771a6509d9SHerbert Xu goto out; 1781a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1794447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1801a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1811a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1821da177e4SLinus Torvalds goto out; 18335d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] && 18435d2856bSMartin Willi p->mode != XFRM_MODE_TUNNEL) 18535d2856bSMartin Willi goto out; 1861da177e4SLinus Torvalds break; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds case IPPROTO_COMP: 18935a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 1901a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 19135a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1924447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 19335d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] || 19435d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 1951da177e4SLinus Torvalds goto out; 1961da177e4SLinus Torvalds break; 1971da177e4SLinus Torvalds 198e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 199e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 200e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 20135a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 20235a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2034447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 2041a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 20535a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 20635a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 20735a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 20835d2856bSMartin Willi attrs[XFRMA_TFCPAD] || 20935a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 210e23c7194SMasahide NAKAMURA goto out; 211e23c7194SMasahide NAKAMURA break; 212e23c7194SMasahide NAKAMURA #endif 213e23c7194SMasahide NAKAMURA 2141da177e4SLinus Torvalds default: 2151da177e4SLinus Torvalds goto out; 2163ff50b79SStephen Hemminger } 2171da177e4SLinus Torvalds 2181a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 2191a6509d9SHerbert Xu goto out; 2204447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2214447bb33SMartin Willi goto out; 22235a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2231da177e4SLinus Torvalds goto out; 22435a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2251da177e4SLinus Torvalds goto out; 22635a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2271da177e4SLinus Torvalds goto out; 22835a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 229df71837dSTrent Jaeger goto out; 230d8647b79SSteffen Klassert if ((err = verify_replay(p, attrs))) 231d8647b79SSteffen Klassert goto out; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds err = -EINVAL; 2341da177e4SLinus Torvalds switch (p->mode) { 2357e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2367e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 237060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2380a69452cSDiego Beltrami case XFRM_MODE_BEET: 2391da177e4SLinus Torvalds break; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds default: 2421da177e4SLinus Torvalds goto out; 2433ff50b79SStephen Hemminger } 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds err = 0; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds out: 2481da177e4SLinus Torvalds return err; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2526f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int), 2535424f32eSThomas Graf struct nlattr *rta) 2541da177e4SLinus Torvalds { 2551da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2561da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds if (!rta) 2591da177e4SLinus Torvalds return 0; 2601da177e4SLinus Torvalds 2615424f32eSThomas Graf ualg = nla_data(rta); 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2641da177e4SLinus Torvalds if (!algo) 2651da177e4SLinus Torvalds return -ENOSYS; 2661da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2671da177e4SLinus Torvalds 2680f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2691da177e4SLinus Torvalds if (!p) 2701da177e4SLinus Torvalds return -ENOMEM; 2711da177e4SLinus Torvalds 27204ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2731da177e4SLinus Torvalds *algpp = p; 2741da177e4SLinus Torvalds return 0; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2774447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 2784447bb33SMartin Willi struct nlattr *rta) 2794447bb33SMartin Willi { 2804447bb33SMartin Willi struct xfrm_algo *ualg; 2814447bb33SMartin Willi struct xfrm_algo_auth *p; 2824447bb33SMartin Willi struct xfrm_algo_desc *algo; 2834447bb33SMartin Willi 2844447bb33SMartin Willi if (!rta) 2854447bb33SMartin Willi return 0; 2864447bb33SMartin Willi 2874447bb33SMartin Willi ualg = nla_data(rta); 2884447bb33SMartin Willi 2894447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 2904447bb33SMartin Willi if (!algo) 2914447bb33SMartin Willi return -ENOSYS; 2924447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 2934447bb33SMartin Willi 2944447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 2954447bb33SMartin Willi if (!p) 2964447bb33SMartin Willi return -ENOMEM; 2974447bb33SMartin Willi 2984447bb33SMartin Willi strcpy(p->alg_name, algo->name); 2994447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 3004447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3014447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 3024447bb33SMartin Willi 3034447bb33SMartin Willi *algpp = p; 3044447bb33SMartin Willi return 0; 3054447bb33SMartin Willi } 3064447bb33SMartin Willi 3074447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 3084447bb33SMartin Willi struct nlattr *rta) 3094447bb33SMartin Willi { 3104447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 3114447bb33SMartin Willi struct xfrm_algo_desc *algo; 3124447bb33SMartin Willi 3134447bb33SMartin Willi if (!rta) 3144447bb33SMartin Willi return 0; 3154447bb33SMartin Willi 3164447bb33SMartin Willi ualg = nla_data(rta); 3174447bb33SMartin Willi 3184447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3194447bb33SMartin Willi if (!algo) 3204447bb33SMartin Willi return -ENOSYS; 321fa6dd8a2SNicolas Dichtel if ((ualg->alg_trunc_len / 8) > MAX_AH_AUTH_LEN || 322fa6dd8a2SNicolas Dichtel ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3234447bb33SMartin Willi return -EINVAL; 3244447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3254447bb33SMartin Willi 3264447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3274447bb33SMartin Willi if (!p) 3284447bb33SMartin Willi return -ENOMEM; 3294447bb33SMartin Willi 3304447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3314447bb33SMartin Willi if (!p->alg_trunc_len) 3324447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3334447bb33SMartin Willi 3344447bb33SMartin Willi *algpp = p; 3354447bb33SMartin Willi return 0; 3364447bb33SMartin Willi } 3374447bb33SMartin Willi 3381a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 3391a6509d9SHerbert Xu struct nlattr *rta) 3401a6509d9SHerbert Xu { 3411a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3421a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3431a6509d9SHerbert Xu 3441a6509d9SHerbert Xu if (!rta) 3451a6509d9SHerbert Xu return 0; 3461a6509d9SHerbert Xu 3471a6509d9SHerbert Xu ualg = nla_data(rta); 3481a6509d9SHerbert Xu 3491a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3501a6509d9SHerbert Xu if (!algo) 3511a6509d9SHerbert Xu return -ENOSYS; 3521a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 3531a6509d9SHerbert Xu 3541a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3551a6509d9SHerbert Xu if (!p) 3561a6509d9SHerbert Xu return -ENOMEM; 3571a6509d9SHerbert Xu 3581a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 3591a6509d9SHerbert Xu *algpp = p; 3601a6509d9SHerbert Xu return 0; 3611a6509d9SHerbert Xu } 3621a6509d9SHerbert Xu 363*e2b19125SSteffen Klassert static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn, 364*e2b19125SSteffen Klassert struct nlattr *rp) 365*e2b19125SSteffen Klassert { 366*e2b19125SSteffen Klassert struct xfrm_replay_state_esn *up; 367*e2b19125SSteffen Klassert 368*e2b19125SSteffen Klassert if (!replay_esn || !rp) 369*e2b19125SSteffen Klassert return 0; 370*e2b19125SSteffen Klassert 371*e2b19125SSteffen Klassert up = nla_data(rp); 372*e2b19125SSteffen Klassert 373*e2b19125SSteffen Klassert if (xfrm_replay_state_esn_len(replay_esn) != 374*e2b19125SSteffen Klassert xfrm_replay_state_esn_len(up)) 375*e2b19125SSteffen Klassert return -EINVAL; 376*e2b19125SSteffen Klassert 377*e2b19125SSteffen Klassert return 0; 378*e2b19125SSteffen Klassert } 379*e2b19125SSteffen Klassert 380d8647b79SSteffen Klassert static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn, 381d8647b79SSteffen Klassert struct xfrm_replay_state_esn **preplay_esn, 382d8647b79SSteffen Klassert struct nlattr *rta) 383d8647b79SSteffen Klassert { 384d8647b79SSteffen Klassert struct xfrm_replay_state_esn *p, *pp, *up; 385d8647b79SSteffen Klassert 386d8647b79SSteffen Klassert if (!rta) 387d8647b79SSteffen Klassert return 0; 388d8647b79SSteffen Klassert 389d8647b79SSteffen Klassert up = nla_data(rta); 390d8647b79SSteffen Klassert 391d8647b79SSteffen Klassert p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL); 392d8647b79SSteffen Klassert if (!p) 393d8647b79SSteffen Klassert return -ENOMEM; 394d8647b79SSteffen Klassert 395d8647b79SSteffen Klassert pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL); 396d8647b79SSteffen Klassert if (!pp) { 397d8647b79SSteffen Klassert kfree(p); 398d8647b79SSteffen Klassert return -ENOMEM; 399d8647b79SSteffen Klassert } 400d8647b79SSteffen Klassert 401d8647b79SSteffen Klassert *replay_esn = p; 402d8647b79SSteffen Klassert *preplay_esn = pp; 403d8647b79SSteffen Klassert 404d8647b79SSteffen Klassert return 0; 405d8647b79SSteffen Klassert } 406d8647b79SSteffen Klassert 407661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 408df71837dSTrent Jaeger { 409df71837dSTrent Jaeger int len = 0; 410df71837dSTrent Jaeger 411df71837dSTrent Jaeger if (xfrm_ctx) { 412df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 413df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 414df71837dSTrent Jaeger } 415df71837dSTrent Jaeger return len; 416df71837dSTrent Jaeger } 417df71837dSTrent Jaeger 4181da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 4191da177e4SLinus Torvalds { 4201da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 4211da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 4221da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 4231da177e4SLinus Torvalds x->props.mode = p->mode; 4241da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 4251da177e4SLinus Torvalds x->props.reqid = p->reqid; 4261da177e4SLinus Torvalds x->props.family = p->family; 42754489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 4281da177e4SLinus Torvalds x->props.flags = p->flags; 429196b0036SHerbert Xu 430ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 431196b0036SHerbert Xu x->sel.family = p->family; 4321da177e4SLinus Torvalds } 4331da177e4SLinus Torvalds 434d51d081dSJamal Hadi Salim /* 435d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 436d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 437d51d081dSJamal Hadi Salim * 438d51d081dSJamal Hadi Salim */ 4395424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) 440d51d081dSJamal Hadi Salim { 4415424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 442d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 4435424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 4445424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 4455424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 446d51d081dSJamal Hadi Salim 447d8647b79SSteffen Klassert if (re) { 448d8647b79SSteffen Klassert struct xfrm_replay_state_esn *replay_esn; 449d8647b79SSteffen Klassert replay_esn = nla_data(re); 450d8647b79SSteffen Klassert memcpy(x->replay_esn, replay_esn, 451d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 452d8647b79SSteffen Klassert memcpy(x->preplay_esn, replay_esn, 453d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 454d8647b79SSteffen Klassert } 455d8647b79SSteffen Klassert 456d51d081dSJamal Hadi Salim if (rp) { 457d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 4585424f32eSThomas Graf replay = nla_data(rp); 459d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 460d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 461d51d081dSJamal Hadi Salim } 462d51d081dSJamal Hadi Salim 463d51d081dSJamal Hadi Salim if (lt) { 464d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 4655424f32eSThomas Graf ltime = nla_data(lt); 466d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 467d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 468d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 469d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 470d51d081dSJamal Hadi Salim } 471d51d081dSJamal Hadi Salim 472cf5cb79fSThomas Graf if (et) 4735424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 474d51d081dSJamal Hadi Salim 475cf5cb79fSThomas Graf if (rt) 4765424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 477d51d081dSJamal Hadi Salim } 478d51d081dSJamal Hadi Salim 479fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 480fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 4815424f32eSThomas Graf struct nlattr **attrs, 4821da177e4SLinus Torvalds int *errp) 4831da177e4SLinus Torvalds { 484fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 4851da177e4SLinus Torvalds int err = -ENOMEM; 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds if (!x) 4881da177e4SLinus Torvalds goto error_no_put; 4891da177e4SLinus Torvalds 4901da177e4SLinus Torvalds copy_from_user_state(x, p); 4911da177e4SLinus Torvalds 4921a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 4931a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 4941a6509d9SHerbert Xu goto error; 4954447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 4964447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 4974447bb33SMartin Willi goto error; 4984447bb33SMartin Willi if (!x->props.aalgo) { 4994447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 50035a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 5011da177e4SLinus Torvalds goto error; 5024447bb33SMartin Willi } 5031da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 5041da177e4SLinus Torvalds xfrm_ealg_get_byname, 50535a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 5061da177e4SLinus Torvalds goto error; 5071da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 5081da177e4SLinus Torvalds xfrm_calg_get_byname, 50935a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 5101da177e4SLinus Torvalds goto error; 511fd21150aSThomas Graf 512fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 513fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 514fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 515fd21150aSThomas Graf if (x->encap == NULL) 5161da177e4SLinus Torvalds goto error; 517fd21150aSThomas Graf } 518fd21150aSThomas Graf 51935d2856bSMartin Willi if (attrs[XFRMA_TFCPAD]) 52035d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); 52135d2856bSMartin Willi 522fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 523fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 524fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 525fd21150aSThomas Graf if (x->coaddr == NULL) 526060f02a3SNoriaki TAKAMIYA goto error; 527fd21150aSThomas Graf } 528fd21150aSThomas Graf 5296f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark); 5306f26b61eSJamal Hadi Salim 531a454f0ccSWei Yongjun err = __xfrm_init_state(x, false); 5321da177e4SLinus Torvalds if (err) 5331da177e4SLinus Torvalds goto error; 5341da177e4SLinus Torvalds 535fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 536fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 537df71837dSTrent Jaeger goto error; 538df71837dSTrent Jaeger 539d8647b79SSteffen Klassert if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, 540d8647b79SSteffen Klassert attrs[XFRMA_REPLAY_ESN_VAL]))) 541d8647b79SSteffen Klassert goto error; 542d8647b79SSteffen Klassert 5431da177e4SLinus Torvalds x->km.seq = p->seq; 544b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 545d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 546b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 547d51d081dSJamal Hadi Salim 5489fdc4883SSteffen Klassert if ((err = xfrm_init_replay(x))) 5499fdc4883SSteffen Klassert goto error; 550d51d081dSJamal Hadi Salim 5519fdc4883SSteffen Klassert /* override default values from above */ 5525424f32eSThomas Graf xfrm_update_ae_params(x, attrs); 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds return x; 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds error: 5571da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 5581da177e4SLinus Torvalds xfrm_state_put(x); 5591da177e4SLinus Torvalds error_no_put: 5601da177e4SLinus Torvalds *errp = err; 5611da177e4SLinus Torvalds return NULL; 5621da177e4SLinus Torvalds } 5631da177e4SLinus Torvalds 56422e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 5655424f32eSThomas Graf struct nlattr **attrs) 5661da177e4SLinus Torvalds { 567fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 5687b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 5691da177e4SLinus Torvalds struct xfrm_state *x; 5701da177e4SLinus Torvalds int err; 57126b15dadSJamal Hadi Salim struct km_event c; 572c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 573c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 574c53fa1edSPatrick McHardy u32 sid; 5751da177e4SLinus Torvalds 57635a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 5771da177e4SLinus Torvalds if (err) 5781da177e4SLinus Torvalds return err; 5791da177e4SLinus Torvalds 580fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 5811da177e4SLinus Torvalds if (!x) 5821da177e4SLinus Torvalds return err; 5831da177e4SLinus Torvalds 58426b15dadSJamal Hadi Salim xfrm_state_hold(x); 5851da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 5861da177e4SLinus Torvalds err = xfrm_state_add(x); 5871da177e4SLinus Torvalds else 5881da177e4SLinus Torvalds err = xfrm_state_update(x); 5891da177e4SLinus Torvalds 590c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 5912532386fSEric Paris xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); 592161a09e7SJoy Latten 5931da177e4SLinus Torvalds if (err < 0) { 5941da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 59521380b81SHerbert Xu __xfrm_state_put(x); 5967d6dfe1fSPatrick McHardy goto out; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 59926b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 60026b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 601f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 60226b15dadSJamal Hadi Salim 60326b15dadSJamal Hadi Salim km_state_notify(x, &c); 6047d6dfe1fSPatrick McHardy out: 60526b15dadSJamal Hadi Salim xfrm_state_put(x); 6061da177e4SLinus Torvalds return err; 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds 609fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 610fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 6115424f32eSThomas Graf struct nlattr **attrs, 612eb2971b6SMasahide NAKAMURA int *errp) 613eb2971b6SMasahide NAKAMURA { 614eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 6156f26b61eSJamal Hadi Salim struct xfrm_mark m; 616eb2971b6SMasahide NAKAMURA int err; 6176f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 618eb2971b6SMasahide NAKAMURA 619eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 620eb2971b6SMasahide NAKAMURA err = -ESRCH; 6216f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family); 622eb2971b6SMasahide NAKAMURA } else { 623eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 624eb2971b6SMasahide NAKAMURA 62535a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 626eb2971b6SMasahide NAKAMURA if (!saddr) { 627eb2971b6SMasahide NAKAMURA err = -EINVAL; 628eb2971b6SMasahide NAKAMURA goto out; 629eb2971b6SMasahide NAKAMURA } 630eb2971b6SMasahide NAKAMURA 6319abbffeeSMasahide NAKAMURA err = -ESRCH; 6326f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark, 6336f26b61eSJamal Hadi Salim &p->daddr, saddr, 634221df1edSAlexey Dobriyan p->proto, p->family); 635eb2971b6SMasahide NAKAMURA } 636eb2971b6SMasahide NAKAMURA 637eb2971b6SMasahide NAKAMURA out: 638eb2971b6SMasahide NAKAMURA if (!x && errp) 639eb2971b6SMasahide NAKAMURA *errp = err; 640eb2971b6SMasahide NAKAMURA return x; 641eb2971b6SMasahide NAKAMURA } 642eb2971b6SMasahide NAKAMURA 64322e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 6445424f32eSThomas Graf struct nlattr **attrs) 6451da177e4SLinus Torvalds { 646fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6471da177e4SLinus Torvalds struct xfrm_state *x; 648eb2971b6SMasahide NAKAMURA int err = -ESRCH; 64926b15dadSJamal Hadi Salim struct km_event c; 6507b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 651c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 652c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 653c53fa1edSPatrick McHardy u32 sid; 6541da177e4SLinus Torvalds 655fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 6561da177e4SLinus Torvalds if (x == NULL) 657eb2971b6SMasahide NAKAMURA return err; 6581da177e4SLinus Torvalds 6596f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 660c8c05a8eSCatherine Zhang goto out; 661c8c05a8eSCatherine Zhang 6621da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 663c8c05a8eSCatherine Zhang err = -EPERM; 664c8c05a8eSCatherine Zhang goto out; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 66726b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 668161a09e7SJoy Latten 669c8c05a8eSCatherine Zhang if (err < 0) 670c8c05a8eSCatherine Zhang goto out; 67126b15dadSJamal Hadi Salim 67226b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 67326b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 674f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 67526b15dadSJamal Hadi Salim km_state_notify(x, &c); 6761da177e4SLinus Torvalds 677c8c05a8eSCatherine Zhang out: 678c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 6792532386fSEric Paris xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); 680c8c05a8eSCatherine Zhang xfrm_state_put(x); 68126b15dadSJamal Hadi Salim return err; 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 6851da177e4SLinus Torvalds { 6861da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 6871da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 6881da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 6891da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 6901da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 69154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 6921da177e4SLinus Torvalds p->mode = x->props.mode; 6931da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 6941da177e4SLinus Torvalds p->reqid = x->props.reqid; 6951da177e4SLinus Torvalds p->family = x->props.family; 6961da177e4SLinus Torvalds p->flags = x->props.flags; 6971da177e4SLinus Torvalds p->seq = x->km.seq; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds struct xfrm_dump_info { 7011da177e4SLinus Torvalds struct sk_buff *in_skb; 7021da177e4SLinus Torvalds struct sk_buff *out_skb; 7031da177e4SLinus Torvalds u32 nlmsg_seq; 7041da177e4SLinus Torvalds u16 nlmsg_flags; 7051da177e4SLinus Torvalds }; 7061da177e4SLinus Torvalds 707c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 708c0144beaSThomas Graf { 709c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 710c0144beaSThomas Graf struct nlattr *attr; 71168325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 712c0144beaSThomas Graf 713c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 714c0144beaSThomas Graf if (attr == NULL) 715c0144beaSThomas Graf return -EMSGSIZE; 716c0144beaSThomas Graf 717c0144beaSThomas Graf uctx = nla_data(attr); 718c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 719c0144beaSThomas Graf uctx->len = ctx_size; 720c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 721c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 722c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 723c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 724c0144beaSThomas Graf 725c0144beaSThomas Graf return 0; 726c0144beaSThomas Graf } 727c0144beaSThomas Graf 7284447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 7294447bb33SMartin Willi { 7304447bb33SMartin Willi struct xfrm_algo *algo; 7314447bb33SMartin Willi struct nlattr *nla; 7324447bb33SMartin Willi 7334447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 7344447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 7354447bb33SMartin Willi if (!nla) 7364447bb33SMartin Willi return -EMSGSIZE; 7374447bb33SMartin Willi 7384447bb33SMartin Willi algo = nla_data(nla); 7394447bb33SMartin Willi strcpy(algo->alg_name, auth->alg_name); 7404447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 7414447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 7424447bb33SMartin Willi 7434447bb33SMartin Willi return 0; 7444447bb33SMartin Willi } 7454447bb33SMartin Willi 74668325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 74768325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 74868325d3bSHerbert Xu struct xfrm_usersa_info *p, 74968325d3bSHerbert Xu struct sk_buff *skb) 7501da177e4SLinus Torvalds { 7511da177e4SLinus Torvalds copy_to_user_state(x, p); 7521da177e4SLinus Torvalds 753050f009eSHerbert Xu if (x->coaddr) 754050f009eSHerbert Xu NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 755050f009eSHerbert Xu 756050f009eSHerbert Xu if (x->lastused) 757050f009eSHerbert Xu NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); 758050f009eSHerbert Xu 7591a6509d9SHerbert Xu if (x->aead) 7601a6509d9SHerbert Xu NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 7614447bb33SMartin Willi if (x->aalg) { 7624447bb33SMartin Willi if (copy_to_user_auth(x->aalg, skb)) 7634447bb33SMartin Willi goto nla_put_failure; 7644447bb33SMartin Willi 7654447bb33SMartin Willi NLA_PUT(skb, XFRMA_ALG_AUTH_TRUNC, 7664447bb33SMartin Willi xfrm_alg_auth_len(x->aalg), x->aalg); 7674447bb33SMartin Willi } 7681da177e4SLinus Torvalds if (x->ealg) 7690f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 7701da177e4SLinus Torvalds if (x->calg) 771c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 7721da177e4SLinus Torvalds 7731da177e4SLinus Torvalds if (x->encap) 774c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 7751da177e4SLinus Torvalds 77635d2856bSMartin Willi if (x->tfcpad) 77735d2856bSMartin Willi NLA_PUT_U32(skb, XFRMA_TFCPAD, x->tfcpad); 77835d2856bSMartin Willi 7796f26b61eSJamal Hadi Salim if (xfrm_mark_put(skb, &x->mark)) 7806f26b61eSJamal Hadi Salim goto nla_put_failure; 7816f26b61eSJamal Hadi Salim 782d8647b79SSteffen Klassert if (x->replay_esn) 783d8647b79SSteffen Klassert NLA_PUT(skb, XFRMA_REPLAY_ESN_VAL, 784d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn), x->replay_esn); 785d8647b79SSteffen Klassert 786c0144beaSThomas Graf if (x->security && copy_sec_ctx(x->security, skb) < 0) 787c0144beaSThomas Graf goto nla_put_failure; 788060f02a3SNoriaki TAKAMIYA 78968325d3bSHerbert Xu return 0; 79068325d3bSHerbert Xu 79168325d3bSHerbert Xu nla_put_failure: 79268325d3bSHerbert Xu return -EMSGSIZE; 79368325d3bSHerbert Xu } 79468325d3bSHerbert Xu 79568325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 79668325d3bSHerbert Xu { 79768325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 79868325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 79968325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 80068325d3bSHerbert Xu struct xfrm_usersa_info *p; 80168325d3bSHerbert Xu struct nlmsghdr *nlh; 80268325d3bSHerbert Xu int err; 80368325d3bSHerbert Xu 80468325d3bSHerbert Xu nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 80568325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 80668325d3bSHerbert Xu if (nlh == NULL) 80768325d3bSHerbert Xu return -EMSGSIZE; 80868325d3bSHerbert Xu 80968325d3bSHerbert Xu p = nlmsg_data(nlh); 81068325d3bSHerbert Xu 81168325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 81268325d3bSHerbert Xu if (err) 81368325d3bSHerbert Xu goto nla_put_failure; 81468325d3bSHerbert Xu 8159825069dSThomas Graf nlmsg_end(skb, nlh); 8161da177e4SLinus Torvalds return 0; 8171da177e4SLinus Torvalds 818c0144beaSThomas Graf nla_put_failure: 8199825069dSThomas Graf nlmsg_cancel(skb, nlh); 82068325d3bSHerbert Xu return err; 8211da177e4SLinus Torvalds } 8221da177e4SLinus Torvalds 8234c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 8244c563f76STimo Teras { 8254c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8264c563f76STimo Teras xfrm_state_walk_done(walk); 8274c563f76STimo Teras return 0; 8284c563f76STimo Teras } 8294c563f76STimo Teras 8301da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 8311da177e4SLinus Torvalds { 832fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 8334c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 8341da177e4SLinus Torvalds struct xfrm_dump_info info; 8351da177e4SLinus Torvalds 8364c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 8374c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 8384c563f76STimo Teras 8391da177e4SLinus Torvalds info.in_skb = cb->skb; 8401da177e4SLinus Torvalds info.out_skb = skb; 8411da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 8421da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 8434c563f76STimo Teras 8444c563f76STimo Teras if (!cb->args[0]) { 8454c563f76STimo Teras cb->args[0] = 1; 8464c563f76STimo Teras xfrm_state_walk_init(walk, 0); 8474c563f76STimo Teras } 8484c563f76STimo Teras 849fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 8501da177e4SLinus Torvalds 8511da177e4SLinus Torvalds return skb->len; 8521da177e4SLinus Torvalds } 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 8551da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 8561da177e4SLinus Torvalds { 8571da177e4SLinus Torvalds struct xfrm_dump_info info; 8581da177e4SLinus Torvalds struct sk_buff *skb; 8591da177e4SLinus Torvalds 8607deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 8611da177e4SLinus Torvalds if (!skb) 8621da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds info.in_skb = in_skb; 8651da177e4SLinus Torvalds info.out_skb = skb; 8661da177e4SLinus Torvalds info.nlmsg_seq = seq; 8671da177e4SLinus Torvalds info.nlmsg_flags = 0; 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds if (dump_one_state(x, 0, &info)) { 8701da177e4SLinus Torvalds kfree_skb(skb); 8711da177e4SLinus Torvalds return NULL; 8721da177e4SLinus Torvalds } 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds return skb; 8751da177e4SLinus Torvalds } 8761da177e4SLinus Torvalds 8777deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 8787deb2264SThomas Graf { 8797deb2264SThomas Graf return NLMSG_ALIGN(4) 8807deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 8817deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 8827deb2264SThomas Graf } 8837deb2264SThomas Graf 884e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 885e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 886ecfd6b18SJamal Hadi Salim { 8875a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 8885a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 8895a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 890ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 891ecfd6b18SJamal Hadi Salim u32 *f; 892ecfd6b18SJamal Hadi Salim 893ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 894ecfd6b18SJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 895ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 896ecfd6b18SJamal Hadi Salim 897ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 898ecfd6b18SJamal Hadi Salim *f = flags; 899e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 9005a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 9015a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 9025a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 9035a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 9045a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 9055a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 9065a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 9075a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 908ecfd6b18SJamal Hadi Salim 9095a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 9105a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 911ecfd6b18SJamal Hadi Salim 912ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 913ecfd6b18SJamal Hadi Salim 914ecfd6b18SJamal Hadi Salim nla_put_failure: 915ecfd6b18SJamal Hadi Salim nlmsg_cancel(skb, nlh); 916ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 917ecfd6b18SJamal Hadi Salim } 918ecfd6b18SJamal Hadi Salim 919ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 9205424f32eSThomas Graf struct nlattr **attrs) 921ecfd6b18SJamal Hadi Salim { 922a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 923ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 9247b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 925ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 926ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 927ecfd6b18SJamal Hadi Salim 9287deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 929ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 930ecfd6b18SJamal Hadi Salim return -ENOMEM; 931ecfd6b18SJamal Hadi Salim 932e071041bSAlexey Dobriyan if (build_spdinfo(r_skb, net, spid, seq, *flags) < 0) 933ecfd6b18SJamal Hadi Salim BUG(); 934ecfd6b18SJamal Hadi Salim 935a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 936ecfd6b18SJamal Hadi Salim } 937ecfd6b18SJamal Hadi Salim 9387deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 9397deb2264SThomas Graf { 9407deb2264SThomas Graf return NLMSG_ALIGN(4) 9417deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 9427deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 9437deb2264SThomas Graf } 9447deb2264SThomas Graf 945e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 946e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 94728d8909bSJamal Hadi Salim { 948af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 949af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 95028d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 95128d8909bSJamal Hadi Salim u32 *f; 95228d8909bSJamal Hadi Salim 95328d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 95428d8909bSJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 95528d8909bSJamal Hadi Salim return -EMSGSIZE; 95628d8909bSJamal Hadi Salim 95728d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 95828d8909bSJamal Hadi Salim *f = flags; 959e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 96028d8909bSJamal Hadi Salim 961af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 962af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 963af11e316SJamal Hadi Salim 964af11e316SJamal Hadi Salim NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); 965af11e316SJamal Hadi Salim NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 96628d8909bSJamal Hadi Salim 96728d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 96828d8909bSJamal Hadi Salim 96928d8909bSJamal Hadi Salim nla_put_failure: 97028d8909bSJamal Hadi Salim nlmsg_cancel(skb, nlh); 97128d8909bSJamal Hadi Salim return -EMSGSIZE; 97228d8909bSJamal Hadi Salim } 97328d8909bSJamal Hadi Salim 97428d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 9755424f32eSThomas Graf struct nlattr **attrs) 97628d8909bSJamal Hadi Salim { 977a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 97828d8909bSJamal Hadi Salim struct sk_buff *r_skb; 9797b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 98028d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 98128d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 98228d8909bSJamal Hadi Salim 9837deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 98428d8909bSJamal Hadi Salim if (r_skb == NULL) 98528d8909bSJamal Hadi Salim return -ENOMEM; 98628d8909bSJamal Hadi Salim 987e071041bSAlexey Dobriyan if (build_sadinfo(r_skb, net, spid, seq, *flags) < 0) 98828d8909bSJamal Hadi Salim BUG(); 98928d8909bSJamal Hadi Salim 990a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 99128d8909bSJamal Hadi Salim } 99228d8909bSJamal Hadi Salim 99322e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 9945424f32eSThomas Graf struct nlattr **attrs) 9951da177e4SLinus Torvalds { 996fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 9977b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 9981da177e4SLinus Torvalds struct xfrm_state *x; 9991da177e4SLinus Torvalds struct sk_buff *resp_skb; 1000eb2971b6SMasahide NAKAMURA int err = -ESRCH; 10011da177e4SLinus Torvalds 1002fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 10031da177e4SLinus Torvalds if (x == NULL) 10041da177e4SLinus Torvalds goto out_noput; 10051da177e4SLinus Torvalds 10061da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 10071da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 10081da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 10091da177e4SLinus Torvalds } else { 1010a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 10111da177e4SLinus Torvalds } 10121da177e4SLinus Torvalds xfrm_state_put(x); 10131da177e4SLinus Torvalds out_noput: 10141da177e4SLinus Torvalds return err; 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds 10171da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 10181da177e4SLinus Torvalds { 10191da177e4SLinus Torvalds switch (p->info.id.proto) { 10201da177e4SLinus Torvalds case IPPROTO_AH: 10211da177e4SLinus Torvalds case IPPROTO_ESP: 10221da177e4SLinus Torvalds break; 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds case IPPROTO_COMP: 10251da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 10261da177e4SLinus Torvalds if (p->max >= 0x10000) 10271da177e4SLinus Torvalds return -EINVAL; 10281da177e4SLinus Torvalds break; 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds default: 10311da177e4SLinus Torvalds return -EINVAL; 10323ff50b79SStephen Hemminger } 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds if (p->min > p->max) 10351da177e4SLinus Torvalds return -EINVAL; 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds return 0; 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds 104022e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 10415424f32eSThomas Graf struct nlattr **attrs) 10421da177e4SLinus Torvalds { 1043fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 10441da177e4SLinus Torvalds struct xfrm_state *x; 10451da177e4SLinus Torvalds struct xfrm_userspi_info *p; 10461da177e4SLinus Torvalds struct sk_buff *resp_skb; 10471da177e4SLinus Torvalds xfrm_address_t *daddr; 10481da177e4SLinus Torvalds int family; 10491da177e4SLinus Torvalds int err; 10506f26b61eSJamal Hadi Salim u32 mark; 10516f26b61eSJamal Hadi Salim struct xfrm_mark m; 10521da177e4SLinus Torvalds 10537b67c857SThomas Graf p = nlmsg_data(nlh); 10541da177e4SLinus Torvalds err = verify_userspi_info(p); 10551da177e4SLinus Torvalds if (err) 10561da177e4SLinus Torvalds goto out_noput; 10571da177e4SLinus Torvalds 10581da177e4SLinus Torvalds family = p->info.family; 10591da177e4SLinus Torvalds daddr = &p->info.id.daddr; 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds x = NULL; 10626f26b61eSJamal Hadi Salim 10636f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 10641da177e4SLinus Torvalds if (p->info.seq) { 10656f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq); 10661da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 10671da177e4SLinus Torvalds xfrm_state_put(x); 10681da177e4SLinus Torvalds x = NULL; 10691da177e4SLinus Torvalds } 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds 10721da177e4SLinus Torvalds if (!x) 10736f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid, 10741da177e4SLinus Torvalds p->info.id.proto, daddr, 10751da177e4SLinus Torvalds &p->info.saddr, 1, 10761da177e4SLinus Torvalds family); 10771da177e4SLinus Torvalds err = -ENOENT; 10781da177e4SLinus Torvalds if (x == NULL) 10791da177e4SLinus Torvalds goto out_noput; 10801da177e4SLinus Torvalds 1081658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 1082658b219eSHerbert Xu if (err) 1083658b219eSHerbert Xu goto out; 10841da177e4SLinus Torvalds 10851da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 10861da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 10871da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 10881da177e4SLinus Torvalds goto out; 10891da177e4SLinus Torvalds } 10901da177e4SLinus Torvalds 1091a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds out: 10941da177e4SLinus Torvalds xfrm_state_put(x); 10951da177e4SLinus Torvalds out_noput: 10961da177e4SLinus Torvalds return err; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 1099b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 11001da177e4SLinus Torvalds { 11011da177e4SLinus Torvalds switch (dir) { 11021da177e4SLinus Torvalds case XFRM_POLICY_IN: 11031da177e4SLinus Torvalds case XFRM_POLICY_OUT: 11041da177e4SLinus Torvalds case XFRM_POLICY_FWD: 11051da177e4SLinus Torvalds break; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds default: 11081da177e4SLinus Torvalds return -EINVAL; 11093ff50b79SStephen Hemminger } 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds return 0; 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds 1114b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1115f7b6983fSMasahide NAKAMURA { 1116f7b6983fSMasahide NAKAMURA switch (type) { 1117f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1118f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1119f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1120f7b6983fSMasahide NAKAMURA #endif 1121f7b6983fSMasahide NAKAMURA break; 1122f7b6983fSMasahide NAKAMURA 1123f7b6983fSMasahide NAKAMURA default: 1124f7b6983fSMasahide NAKAMURA return -EINVAL; 11253ff50b79SStephen Hemminger } 1126f7b6983fSMasahide NAKAMURA 1127f7b6983fSMasahide NAKAMURA return 0; 1128f7b6983fSMasahide NAKAMURA } 1129f7b6983fSMasahide NAKAMURA 11301da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 11311da177e4SLinus Torvalds { 11321da177e4SLinus Torvalds switch (p->share) { 11331da177e4SLinus Torvalds case XFRM_SHARE_ANY: 11341da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 11351da177e4SLinus Torvalds case XFRM_SHARE_USER: 11361da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 11371da177e4SLinus Torvalds break; 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds default: 11401da177e4SLinus Torvalds return -EINVAL; 11413ff50b79SStephen Hemminger } 11421da177e4SLinus Torvalds 11431da177e4SLinus Torvalds switch (p->action) { 11441da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 11451da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 11461da177e4SLinus Torvalds break; 11471da177e4SLinus Torvalds 11481da177e4SLinus Torvalds default: 11491da177e4SLinus Torvalds return -EINVAL; 11503ff50b79SStephen Hemminger } 11511da177e4SLinus Torvalds 11521da177e4SLinus Torvalds switch (p->sel.family) { 11531da177e4SLinus Torvalds case AF_INET: 11541da177e4SLinus Torvalds break; 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds case AF_INET6: 11571da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 11581da177e4SLinus Torvalds break; 11591da177e4SLinus Torvalds #else 11601da177e4SLinus Torvalds return -EAFNOSUPPORT; 11611da177e4SLinus Torvalds #endif 11621da177e4SLinus Torvalds 11631da177e4SLinus Torvalds default: 11641da177e4SLinus Torvalds return -EINVAL; 11653ff50b79SStephen Hemminger } 11661da177e4SLinus Torvalds 11671da177e4SLinus Torvalds return verify_policy_dir(p->dir); 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds 11705424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1171df71837dSTrent Jaeger { 11725424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1173df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1174df71837dSTrent Jaeger 1175df71837dSTrent Jaeger if (!rt) 1176df71837dSTrent Jaeger return 0; 1177df71837dSTrent Jaeger 11785424f32eSThomas Graf uctx = nla_data(rt); 117903e1ad7bSPaul Moore return security_xfrm_policy_alloc(&pol->security, uctx); 1180df71837dSTrent Jaeger } 1181df71837dSTrent Jaeger 11821da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 11831da177e4SLinus Torvalds int nr) 11841da177e4SLinus Torvalds { 11851da177e4SLinus Torvalds int i; 11861da177e4SLinus Torvalds 11871da177e4SLinus Torvalds xp->xfrm_nr = nr; 11881da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 11891da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 11901da177e4SLinus Torvalds 11911da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 11921da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 11931da177e4SLinus Torvalds sizeof(xfrm_address_t)); 11941da177e4SLinus Torvalds t->reqid = ut->reqid; 11951da177e4SLinus Torvalds t->mode = ut->mode; 11961da177e4SLinus Torvalds t->share = ut->share; 11971da177e4SLinus Torvalds t->optional = ut->optional; 11981da177e4SLinus Torvalds t->aalgos = ut->aalgos; 11991da177e4SLinus Torvalds t->ealgos = ut->ealgos; 12001da177e4SLinus Torvalds t->calgos = ut->calgos; 1201c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1202c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 12038511d01dSMiika Komu t->encap_family = ut->family; 12041da177e4SLinus Torvalds } 12051da177e4SLinus Torvalds } 12061da177e4SLinus Torvalds 1207b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1208b4ad86bfSDavid S. Miller { 1209b4ad86bfSDavid S. Miller int i; 1210b4ad86bfSDavid S. Miller 1211b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1212b4ad86bfSDavid S. Miller return -EINVAL; 1213b4ad86bfSDavid S. Miller 1214b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1215b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1216b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1217b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1218b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1219b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1220b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1221b4ad86bfSDavid S. Miller */ 1222b4ad86bfSDavid S. Miller if (!ut[i].family) 1223b4ad86bfSDavid S. Miller ut[i].family = family; 1224b4ad86bfSDavid S. Miller 1225b4ad86bfSDavid S. Miller switch (ut[i].family) { 1226b4ad86bfSDavid S. Miller case AF_INET: 1227b4ad86bfSDavid S. Miller break; 1228b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1229b4ad86bfSDavid S. Miller case AF_INET6: 1230b4ad86bfSDavid S. Miller break; 1231b4ad86bfSDavid S. Miller #endif 1232b4ad86bfSDavid S. Miller default: 1233b4ad86bfSDavid S. Miller return -EINVAL; 12343ff50b79SStephen Hemminger } 1235b4ad86bfSDavid S. Miller } 1236b4ad86bfSDavid S. Miller 1237b4ad86bfSDavid S. Miller return 0; 1238b4ad86bfSDavid S. Miller } 1239b4ad86bfSDavid S. Miller 12405424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 12411da177e4SLinus Torvalds { 12425424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 12431da177e4SLinus Torvalds 12441da177e4SLinus Torvalds if (!rt) { 12451da177e4SLinus Torvalds pol->xfrm_nr = 0; 12461da177e4SLinus Torvalds } else { 12475424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 12485424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1249b4ad86bfSDavid S. Miller int err; 12501da177e4SLinus Torvalds 1251b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1252b4ad86bfSDavid S. Miller if (err) 1253b4ad86bfSDavid S. Miller return err; 12541da177e4SLinus Torvalds 12555424f32eSThomas Graf copy_templates(pol, utmpl, nr); 12561da177e4SLinus Torvalds } 12571da177e4SLinus Torvalds return 0; 12581da177e4SLinus Torvalds } 12591da177e4SLinus Torvalds 12605424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1261f7b6983fSMasahide NAKAMURA { 12625424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1263f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1264b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1265f7b6983fSMasahide NAKAMURA int err; 1266f7b6983fSMasahide NAKAMURA 1267f7b6983fSMasahide NAKAMURA if (rt) { 12685424f32eSThomas Graf upt = nla_data(rt); 1269f7b6983fSMasahide NAKAMURA type = upt->type; 1270f7b6983fSMasahide NAKAMURA } 1271f7b6983fSMasahide NAKAMURA 1272f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1273f7b6983fSMasahide NAKAMURA if (err) 1274f7b6983fSMasahide NAKAMURA return err; 1275f7b6983fSMasahide NAKAMURA 1276f7b6983fSMasahide NAKAMURA *tp = type; 1277f7b6983fSMasahide NAKAMURA return 0; 1278f7b6983fSMasahide NAKAMURA } 1279f7b6983fSMasahide NAKAMURA 12801da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 12811da177e4SLinus Torvalds { 12821da177e4SLinus Torvalds xp->priority = p->priority; 12831da177e4SLinus Torvalds xp->index = p->index; 12841da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 12851da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 12861da177e4SLinus Torvalds xp->action = p->action; 12871da177e4SLinus Torvalds xp->flags = p->flags; 12881da177e4SLinus Torvalds xp->family = p->sel.family; 12891da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 12901da177e4SLinus Torvalds } 12911da177e4SLinus Torvalds 12921da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 12931da177e4SLinus Torvalds { 12941da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 12951da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 12961da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 12971da177e4SLinus Torvalds p->priority = xp->priority; 12981da177e4SLinus Torvalds p->index = xp->index; 12991da177e4SLinus Torvalds p->sel.family = xp->family; 13001da177e4SLinus Torvalds p->dir = dir; 13011da177e4SLinus Torvalds p->action = xp->action; 13021da177e4SLinus Torvalds p->flags = xp->flags; 13031da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 13041da177e4SLinus Torvalds } 13051da177e4SLinus Torvalds 1306fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 13071da177e4SLinus Torvalds { 1308fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 13091da177e4SLinus Torvalds int err; 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds if (!xp) { 13121da177e4SLinus Torvalds *errp = -ENOMEM; 13131da177e4SLinus Torvalds return NULL; 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds 13161da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1317df71837dSTrent Jaeger 131835a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1319f7b6983fSMasahide NAKAMURA if (err) 1320f7b6983fSMasahide NAKAMURA goto error; 1321f7b6983fSMasahide NAKAMURA 132235a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 132335a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1324f7b6983fSMasahide NAKAMURA if (err) 1325f7b6983fSMasahide NAKAMURA goto error; 13261da177e4SLinus Torvalds 1327295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark); 1328295fae56SJamal Hadi Salim 13291da177e4SLinus Torvalds return xp; 1330f7b6983fSMasahide NAKAMURA error: 1331f7b6983fSMasahide NAKAMURA *errp = err; 133212a169e7SHerbert Xu xp->walk.dead = 1; 133364c31b3fSWANG Cong xfrm_policy_destroy(xp); 1334f7b6983fSMasahide NAKAMURA return NULL; 13351da177e4SLinus Torvalds } 13361da177e4SLinus Torvalds 133722e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 13385424f32eSThomas Graf struct nlattr **attrs) 13391da177e4SLinus Torvalds { 1340fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 13417b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 13421da177e4SLinus Torvalds struct xfrm_policy *xp; 134326b15dadSJamal Hadi Salim struct km_event c; 13441da177e4SLinus Torvalds int err; 13451da177e4SLinus Torvalds int excl; 1346c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1347c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1348c53fa1edSPatrick McHardy u32 sid; 13491da177e4SLinus Torvalds 13501da177e4SLinus Torvalds err = verify_newpolicy_info(p); 13511da177e4SLinus Torvalds if (err) 13521da177e4SLinus Torvalds return err; 135335a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1354df71837dSTrent Jaeger if (err) 1355df71837dSTrent Jaeger return err; 13561da177e4SLinus Torvalds 1357fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 13581da177e4SLinus Torvalds if (!xp) 13591da177e4SLinus Torvalds return err; 13601da177e4SLinus Torvalds 136126b15dadSJamal Hadi Salim /* shouldnt excl be based on nlh flags?? 136226b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 136326b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 136426b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 13651da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 13661da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 1367c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 13682532386fSEric Paris xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); 1369161a09e7SJoy Latten 13701da177e4SLinus Torvalds if (err) { 137103e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 13721da177e4SLinus Torvalds kfree(xp); 13731da177e4SLinus Torvalds return err; 13741da177e4SLinus Torvalds } 13751da177e4SLinus Torvalds 1376f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 137726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 137826b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 137926b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 138026b15dadSJamal Hadi Salim 13811da177e4SLinus Torvalds xfrm_pol_put(xp); 13821da177e4SLinus Torvalds 13831da177e4SLinus Torvalds return 0; 13841da177e4SLinus Torvalds } 13851da177e4SLinus Torvalds 13861da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 13871da177e4SLinus Torvalds { 13881da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 13891da177e4SLinus Torvalds int i; 13901da177e4SLinus Torvalds 13911da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 13921da177e4SLinus Torvalds return 0; 13931da177e4SLinus Torvalds 13941da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 13951da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 13961da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 13971da177e4SLinus Torvalds 13981da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 13998511d01dSMiika Komu up->family = kp->encap_family; 14001da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 14011da177e4SLinus Torvalds up->reqid = kp->reqid; 14021da177e4SLinus Torvalds up->mode = kp->mode; 14031da177e4SLinus Torvalds up->share = kp->share; 14041da177e4SLinus Torvalds up->optional = kp->optional; 14051da177e4SLinus Torvalds up->aalgos = kp->aalgos; 14061da177e4SLinus Torvalds up->ealgos = kp->ealgos; 14071da177e4SLinus Torvalds up->calgos = kp->calgos; 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds 1410c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1411c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1412df71837dSTrent Jaeger } 1413df71837dSTrent Jaeger 14140d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 14150d681623SSerge Hallyn { 14160d681623SSerge Hallyn if (x->security) { 14170d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 14180d681623SSerge Hallyn } 14190d681623SSerge Hallyn return 0; 14200d681623SSerge Hallyn } 14210d681623SSerge Hallyn 14220d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 14230d681623SSerge Hallyn { 14240d681623SSerge Hallyn if (xp->security) { 14250d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 14260d681623SSerge Hallyn } 14270d681623SSerge Hallyn return 0; 14280d681623SSerge Hallyn } 1429cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1430cfbfd45aSThomas Graf { 1431cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1432cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1433cfbfd45aSThomas Graf #else 1434cfbfd45aSThomas Graf return 0; 1435cfbfd45aSThomas Graf #endif 1436cfbfd45aSThomas Graf } 14370d681623SSerge Hallyn 1438f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1439b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1440f7b6983fSMasahide NAKAMURA { 1441c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1442c0144beaSThomas Graf .type = type, 1443c0144beaSThomas Graf }; 1444f7b6983fSMasahide NAKAMURA 1445c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1446f7b6983fSMasahide NAKAMURA } 1447f7b6983fSMasahide NAKAMURA 1448f7b6983fSMasahide NAKAMURA #else 1449b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1450f7b6983fSMasahide NAKAMURA { 1451f7b6983fSMasahide NAKAMURA return 0; 1452f7b6983fSMasahide NAKAMURA } 1453f7b6983fSMasahide NAKAMURA #endif 1454f7b6983fSMasahide NAKAMURA 14551da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 14561da177e4SLinus Torvalds { 14571da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 14581da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 14591da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 14601da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 14611da177e4SLinus Torvalds struct nlmsghdr *nlh; 14621da177e4SLinus Torvalds 146379b8b7f4SThomas Graf nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 146479b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 146579b8b7f4SThomas Graf if (nlh == NULL) 146679b8b7f4SThomas Graf return -EMSGSIZE; 14671da177e4SLinus Torvalds 14687b67c857SThomas Graf p = nlmsg_data(nlh); 14691da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 14701da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 14711da177e4SLinus Torvalds goto nlmsg_failure; 1472df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 1473df71837dSTrent Jaeger goto nlmsg_failure; 14741459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 1475f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 1476295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 1477295fae56SJamal Hadi Salim goto nla_put_failure; 14781da177e4SLinus Torvalds 14799825069dSThomas Graf nlmsg_end(skb, nlh); 14801da177e4SLinus Torvalds return 0; 14811da177e4SLinus Torvalds 1482295fae56SJamal Hadi Salim nla_put_failure: 14831da177e4SLinus Torvalds nlmsg_failure: 14849825069dSThomas Graf nlmsg_cancel(skb, nlh); 14859825069dSThomas Graf return -EMSGSIZE; 14861da177e4SLinus Torvalds } 14871da177e4SLinus Torvalds 14884c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 14894c563f76STimo Teras { 14904c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 14914c563f76STimo Teras 14924c563f76STimo Teras xfrm_policy_walk_done(walk); 14934c563f76STimo Teras return 0; 14944c563f76STimo Teras } 14954c563f76STimo Teras 14961da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 14971da177e4SLinus Torvalds { 1498fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 14994c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 15001da177e4SLinus Torvalds struct xfrm_dump_info info; 15011da177e4SLinus Torvalds 15024c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 15034c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 15044c563f76STimo Teras 15051da177e4SLinus Torvalds info.in_skb = cb->skb; 15061da177e4SLinus Torvalds info.out_skb = skb; 15071da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 15081da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 15094c563f76STimo Teras 15104c563f76STimo Teras if (!cb->args[0]) { 15114c563f76STimo Teras cb->args[0] = 1; 15124c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 15134c563f76STimo Teras } 15144c563f76STimo Teras 1515fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 15161da177e4SLinus Torvalds 15171da177e4SLinus Torvalds return skb->len; 15181da177e4SLinus Torvalds } 15191da177e4SLinus Torvalds 15201da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 15211da177e4SLinus Torvalds struct xfrm_policy *xp, 15221da177e4SLinus Torvalds int dir, u32 seq) 15231da177e4SLinus Torvalds { 15241da177e4SLinus Torvalds struct xfrm_dump_info info; 15251da177e4SLinus Torvalds struct sk_buff *skb; 15261da177e4SLinus Torvalds 15277deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 15281da177e4SLinus Torvalds if (!skb) 15291da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds info.in_skb = in_skb; 15321da177e4SLinus Torvalds info.out_skb = skb; 15331da177e4SLinus Torvalds info.nlmsg_seq = seq; 15341da177e4SLinus Torvalds info.nlmsg_flags = 0; 15351da177e4SLinus Torvalds 15361da177e4SLinus Torvalds if (dump_one_policy(xp, dir, 0, &info) < 0) { 15371da177e4SLinus Torvalds kfree_skb(skb); 15381da177e4SLinus Torvalds return NULL; 15391da177e4SLinus Torvalds } 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds return skb; 15421da177e4SLinus Torvalds } 15431da177e4SLinus Torvalds 154422e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 15455424f32eSThomas Graf struct nlattr **attrs) 15461da177e4SLinus Torvalds { 1547fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 15481da177e4SLinus Torvalds struct xfrm_policy *xp; 15491da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1550b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 15511da177e4SLinus Torvalds int err; 155226b15dadSJamal Hadi Salim struct km_event c; 15531da177e4SLinus Torvalds int delete; 1554295fae56SJamal Hadi Salim struct xfrm_mark m; 1555295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 15561da177e4SLinus Torvalds 15577b67c857SThomas Graf p = nlmsg_data(nlh); 15581da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 15591da177e4SLinus Torvalds 156035a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1561f7b6983fSMasahide NAKAMURA if (err) 1562f7b6983fSMasahide NAKAMURA return err; 1563f7b6983fSMasahide NAKAMURA 15641da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 15651da177e4SLinus Torvalds if (err) 15661da177e4SLinus Torvalds return err; 15671da177e4SLinus Torvalds 15681da177e4SLinus Torvalds if (p->index) 1569295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err); 1570df71837dSTrent Jaeger else { 15715424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 157203e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1573df71837dSTrent Jaeger 157435a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1575df71837dSTrent Jaeger if (err) 1576df71837dSTrent Jaeger return err; 1577df71837dSTrent Jaeger 15782c8dd116SDenis V. Lunev ctx = NULL; 1579df71837dSTrent Jaeger if (rt) { 15805424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1581df71837dSTrent Jaeger 158203e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 158303e1ad7bSPaul Moore if (err) 1584df71837dSTrent Jaeger return err; 15852c8dd116SDenis V. Lunev } 1586295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel, 15876f26b61eSJamal Hadi Salim ctx, delete, &err); 158803e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1589df71837dSTrent Jaeger } 15901da177e4SLinus Torvalds if (xp == NULL) 15911da177e4SLinus Torvalds return -ENOENT; 15921da177e4SLinus Torvalds 15931da177e4SLinus Torvalds if (!delete) { 15941da177e4SLinus Torvalds struct sk_buff *resp_skb; 15951da177e4SLinus Torvalds 15961da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 15971da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 15981da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 15991da177e4SLinus Torvalds } else { 1600a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 1601082a1ad5SThomas Graf NETLINK_CB(skb).pid); 16021da177e4SLinus Torvalds } 160326b15dadSJamal Hadi Salim } else { 1604c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1605c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1606c53fa1edSPatrick McHardy u32 sid; 16072532386fSEric Paris 1608c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 16092532386fSEric Paris xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, 16102532386fSEric Paris sid); 161113fcfbb0SDavid S. Miller 161213fcfbb0SDavid S. Miller if (err != 0) 1613c8c05a8eSCatherine Zhang goto out; 161413fcfbb0SDavid S. Miller 1615e7443892SHerbert Xu c.data.byid = p->index; 1616f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 161726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 161826b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 161926b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 16201da177e4SLinus Torvalds } 16211da177e4SLinus Torvalds 1622c8c05a8eSCatherine Zhang out: 1623ef41aaa0SEric Paris xfrm_pol_put(xp); 16241da177e4SLinus Torvalds return err; 16251da177e4SLinus Torvalds } 16261da177e4SLinus Torvalds 162722e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 16285424f32eSThomas Graf struct nlattr **attrs) 16291da177e4SLinus Torvalds { 1630fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 163126b15dadSJamal Hadi Salim struct km_event c; 16327b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1633161a09e7SJoy Latten struct xfrm_audit audit_info; 16344aa2e62cSJoy Latten int err; 16351da177e4SLinus Torvalds 1636c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1637c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1638c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1639fc34acd3SAlexey Dobriyan err = xfrm_state_flush(net, p->proto, &audit_info); 16409e64cc95SJamal Hadi Salim if (err) { 16419e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */ 16429e64cc95SJamal Hadi Salim return 0; 1643069c474eSDavid S. Miller return err; 16449e64cc95SJamal Hadi Salim } 1645bf08867fSHerbert Xu c.data.proto = p->proto; 1646f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 164726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 164826b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 16497067802eSAlexey Dobriyan c.net = net; 165026b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 165126b15dadSJamal Hadi Salim 16521da177e4SLinus Torvalds return 0; 16531da177e4SLinus Torvalds } 16541da177e4SLinus Torvalds 1655d8647b79SSteffen Klassert static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x) 16567deb2264SThomas Graf { 1657d8647b79SSteffen Klassert size_t replay_size = x->replay_esn ? 1658d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn) : 1659d8647b79SSteffen Klassert sizeof(struct xfrm_replay_state); 1660d8647b79SSteffen Klassert 16617deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 1662d8647b79SSteffen Klassert + nla_total_size(replay_size) 16637deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 16646f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 16657deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 16667deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 16677deb2264SThomas Graf } 1668d51d081dSJamal Hadi Salim 1669214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 1670d51d081dSJamal Hadi Salim { 1671d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1672d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 1673d51d081dSJamal Hadi Salim 167479b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 167579b8b7f4SThomas Graf if (nlh == NULL) 167679b8b7f4SThomas Graf return -EMSGSIZE; 1677d51d081dSJamal Hadi Salim 16787b67c857SThomas Graf id = nlmsg_data(nlh); 16792b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1680d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1681d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1682d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 16832b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 16842b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1685d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1686d51d081dSJamal Hadi Salim 1687d8647b79SSteffen Klassert if (x->replay_esn) 1688d8647b79SSteffen Klassert NLA_PUT(skb, XFRMA_REPLAY_ESN_VAL, 1689d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn), 1690d8647b79SSteffen Klassert x->replay_esn); 1691d8647b79SSteffen Klassert else 1692c0144beaSThomas Graf NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); 1693d8647b79SSteffen Klassert 1694c0144beaSThomas Graf NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 1695d51d081dSJamal Hadi Salim 1696c0144beaSThomas Graf if (id->flags & XFRM_AE_RTHR) 1697c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 1698d51d081dSJamal Hadi Salim 1699c0144beaSThomas Graf if (id->flags & XFRM_AE_ETHR) 1700c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH, 1701c0144beaSThomas Graf x->replay_maxage * 10 / HZ); 1702d51d081dSJamal Hadi Salim 17036f26b61eSJamal Hadi Salim if (xfrm_mark_put(skb, &x->mark)) 17046f26b61eSJamal Hadi Salim goto nla_put_failure; 17056f26b61eSJamal Hadi Salim 17069825069dSThomas Graf return nlmsg_end(skb, nlh); 1707d51d081dSJamal Hadi Salim 1708c0144beaSThomas Graf nla_put_failure: 17099825069dSThomas Graf nlmsg_cancel(skb, nlh); 17109825069dSThomas Graf return -EMSGSIZE; 1711d51d081dSJamal Hadi Salim } 1712d51d081dSJamal Hadi Salim 171322e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 17145424f32eSThomas Graf struct nlattr **attrs) 1715d51d081dSJamal Hadi Salim { 1716fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1717d51d081dSJamal Hadi Salim struct xfrm_state *x; 1718d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1719d51d081dSJamal Hadi Salim int err; 1720d51d081dSJamal Hadi Salim struct km_event c; 17216f26b61eSJamal Hadi Salim u32 mark; 17226f26b61eSJamal Hadi Salim struct xfrm_mark m; 17237b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1724d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1725d51d081dSJamal Hadi Salim 17266f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 17276f26b61eSJamal Hadi Salim 17286f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); 1729d8647b79SSteffen Klassert if (x == NULL) 1730d51d081dSJamal Hadi Salim return -ESRCH; 1731d8647b79SSteffen Klassert 1732d8647b79SSteffen Klassert r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 1733d8647b79SSteffen Klassert if (r_skb == NULL) { 1734d8647b79SSteffen Klassert xfrm_state_put(x); 1735d8647b79SSteffen Klassert return -ENOMEM; 1736d51d081dSJamal Hadi Salim } 1737d51d081dSJamal Hadi Salim 1738d51d081dSJamal Hadi Salim /* 1739d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1740d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1741d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1742d51d081dSJamal Hadi Salim */ 1743d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1744d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1745d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1746d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1747d51d081dSJamal Hadi Salim 1748d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1749d51d081dSJamal Hadi Salim BUG(); 1750a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid); 1751d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1752d51d081dSJamal Hadi Salim xfrm_state_put(x); 1753d51d081dSJamal Hadi Salim return err; 1754d51d081dSJamal Hadi Salim } 1755d51d081dSJamal Hadi Salim 175622e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 17575424f32eSThomas Graf struct nlattr **attrs) 1758d51d081dSJamal Hadi Salim { 1759fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1760d51d081dSJamal Hadi Salim struct xfrm_state *x; 1761d51d081dSJamal Hadi Salim struct km_event c; 1762d51d081dSJamal Hadi Salim int err = - EINVAL; 17636f26b61eSJamal Hadi Salim u32 mark = 0; 17646f26b61eSJamal Hadi Salim struct xfrm_mark m; 17657b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 17665424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 1767d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 17685424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1769d51d081dSJamal Hadi Salim 1770d8647b79SSteffen Klassert if (!lt && !rp && !re) 1771d51d081dSJamal Hadi Salim return err; 1772d51d081dSJamal Hadi Salim 1773d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1774d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1775d51d081dSJamal Hadi Salim return err; 1776d51d081dSJamal Hadi Salim 17776f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 17786f26b61eSJamal Hadi Salim 17796f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1780d51d081dSJamal Hadi Salim if (x == NULL) 1781d51d081dSJamal Hadi Salim return -ESRCH; 1782d51d081dSJamal Hadi Salim 1783d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1784d51d081dSJamal Hadi Salim goto out; 1785d51d081dSJamal Hadi Salim 1786*e2b19125SSteffen Klassert err = xfrm_replay_verify_len(x->replay_esn, rp); 1787*e2b19125SSteffen Klassert if (err) 1788*e2b19125SSteffen Klassert goto out; 1789*e2b19125SSteffen Klassert 1790d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 179135a7aa08SThomas Graf xfrm_update_ae_params(x, attrs); 1792d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1793d51d081dSJamal Hadi Salim 1794d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1795d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1796d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1797d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1798d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1799d51d081dSJamal Hadi Salim err = 0; 1800d51d081dSJamal Hadi Salim out: 1801d51d081dSJamal Hadi Salim xfrm_state_put(x); 1802d51d081dSJamal Hadi Salim return err; 1803d51d081dSJamal Hadi Salim } 1804d51d081dSJamal Hadi Salim 180522e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 18065424f32eSThomas Graf struct nlattr **attrs) 18071da177e4SLinus Torvalds { 1808fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 180926b15dadSJamal Hadi Salim struct km_event c; 1810b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1811f7b6983fSMasahide NAKAMURA int err; 1812161a09e7SJoy Latten struct xfrm_audit audit_info; 181326b15dadSJamal Hadi Salim 181435a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1815f7b6983fSMasahide NAKAMURA if (err) 1816f7b6983fSMasahide NAKAMURA return err; 1817f7b6983fSMasahide NAKAMURA 1818c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1819c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1820c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1821fc34acd3SAlexey Dobriyan err = xfrm_policy_flush(net, type, &audit_info); 18222f1eb65fSJamal Hadi Salim if (err) { 18232f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */ 18242f1eb65fSJamal Hadi Salim return 0; 1825069c474eSDavid S. Miller return err; 18262f1eb65fSJamal Hadi Salim } 18272f1eb65fSJamal Hadi Salim 1828f7b6983fSMasahide NAKAMURA c.data.type = type; 1829f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 183026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 183126b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 18327067802eSAlexey Dobriyan c.net = net; 183326b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 18341da177e4SLinus Torvalds return 0; 18351da177e4SLinus Torvalds } 18361da177e4SLinus Torvalds 183722e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 18385424f32eSThomas Graf struct nlattr **attrs) 18396c5c8ca7SJamal Hadi Salim { 1840fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 18416c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 18427b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 18436c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1844b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 18456c5c8ca7SJamal Hadi Salim int err = -ENOENT; 1846295fae56SJamal Hadi Salim struct xfrm_mark m; 1847295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 18486c5c8ca7SJamal Hadi Salim 184935a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1850f7b6983fSMasahide NAKAMURA if (err) 1851f7b6983fSMasahide NAKAMURA return err; 1852f7b6983fSMasahide NAKAMURA 1853c8bf4d04STimo Teräs err = verify_policy_dir(p->dir); 1854c8bf4d04STimo Teräs if (err) 1855c8bf4d04STimo Teräs return err; 1856c8bf4d04STimo Teräs 18576c5c8ca7SJamal Hadi Salim if (p->index) 1858295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err); 18596c5c8ca7SJamal Hadi Salim else { 18605424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 186103e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 18626c5c8ca7SJamal Hadi Salim 186335a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 18646c5c8ca7SJamal Hadi Salim if (err) 18656c5c8ca7SJamal Hadi Salim return err; 18666c5c8ca7SJamal Hadi Salim 18672c8dd116SDenis V. Lunev ctx = NULL; 18686c5c8ca7SJamal Hadi Salim if (rt) { 18695424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 18706c5c8ca7SJamal Hadi Salim 187103e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 187203e1ad7bSPaul Moore if (err) 18736c5c8ca7SJamal Hadi Salim return err; 18742c8dd116SDenis V. Lunev } 1875295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, 18766f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err); 187703e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 18786c5c8ca7SJamal Hadi Salim } 18796c5c8ca7SJamal Hadi Salim if (xp == NULL) 1880ef41aaa0SEric Paris return -ENOENT; 188103e1ad7bSPaul Moore 1882ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead)) 18836c5c8ca7SJamal Hadi Salim goto out; 18846c5c8ca7SJamal Hadi Salim 18856c5c8ca7SJamal Hadi Salim err = 0; 18866c5c8ca7SJamal Hadi Salim if (up->hard) { 1887c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1888c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1889c53fa1edSPatrick McHardy u32 sid; 1890c53fa1edSPatrick McHardy 1891c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 18926c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 18932532386fSEric Paris xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); 1894161a09e7SJoy Latten 18956c5c8ca7SJamal Hadi Salim } else { 18966c5c8ca7SJamal Hadi Salim // reset the timers here? 189762db5cfdSstephen hemminger WARN(1, "Dont know what to do with soft policy expire\n"); 18986c5c8ca7SJamal Hadi Salim } 18996c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 19006c5c8ca7SJamal Hadi Salim 19016c5c8ca7SJamal Hadi Salim out: 19026c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 19036c5c8ca7SJamal Hadi Salim return err; 19046c5c8ca7SJamal Hadi Salim } 19056c5c8ca7SJamal Hadi Salim 190622e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 19075424f32eSThomas Graf struct nlattr **attrs) 190853bc6b4dSJamal Hadi Salim { 1909fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 191053bc6b4dSJamal Hadi Salim struct xfrm_state *x; 191153bc6b4dSJamal Hadi Salim int err; 19127b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 191353bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 19146f26b61eSJamal Hadi Salim struct xfrm_mark m; 1915928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m); 191653bc6b4dSJamal Hadi Salim 19176f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family); 191853bc6b4dSJamal Hadi Salim 19193a765aa5SDavid S. Miller err = -ENOENT; 192053bc6b4dSJamal Hadi Salim if (x == NULL) 192153bc6b4dSJamal Hadi Salim return err; 192253bc6b4dSJamal Hadi Salim 192353bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 19243a765aa5SDavid S. Miller err = -EINVAL; 192553bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 192653bc6b4dSJamal Hadi Salim goto out; 192753bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 192853bc6b4dSJamal Hadi Salim 1929161a09e7SJoy Latten if (ue->hard) { 1930c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1931c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1932c53fa1edSPatrick McHardy u32 sid; 1933c53fa1edSPatrick McHardy 1934c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 193553bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 19362532386fSEric Paris xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); 1937161a09e7SJoy Latten } 19383a765aa5SDavid S. Miller err = 0; 193953bc6b4dSJamal Hadi Salim out: 194053bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 194153bc6b4dSJamal Hadi Salim xfrm_state_put(x); 194253bc6b4dSJamal Hadi Salim return err; 194353bc6b4dSJamal Hadi Salim } 194453bc6b4dSJamal Hadi Salim 194522e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 19465424f32eSThomas Graf struct nlattr **attrs) 1947980ebd25SJamal Hadi Salim { 1948fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1949980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1950980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1951980ebd25SJamal Hadi Salim int i; 19525424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 19536f26b61eSJamal Hadi Salim struct xfrm_mark mark; 1954980ebd25SJamal Hadi Salim 19557b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 1956fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 1957980ebd25SJamal Hadi Salim int err = -ENOMEM; 1958980ebd25SJamal Hadi Salim 1959980ebd25SJamal Hadi Salim if (!x) 1960d8eb9307SIlpo Järvinen goto nomem; 1961980ebd25SJamal Hadi Salim 19626f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark); 19636f26b61eSJamal Hadi Salim 1964980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 1965d8eb9307SIlpo Järvinen if (err) 1966d8eb9307SIlpo Järvinen goto bad_policy; 1967980ebd25SJamal Hadi Salim 1968980ebd25SJamal Hadi Salim /* build an XP */ 1969fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 1970d8eb9307SIlpo Järvinen if (!xp) 1971d8eb9307SIlpo Järvinen goto free_state; 1972980ebd25SJamal Hadi Salim 1973980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 1974980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 1975980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 19766f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m; 19776f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v; 19785424f32eSThomas Graf ut = nla_data(rt); 1979980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 1980980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 1981980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 1982980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 1983980ebd25SJamal Hadi Salim x->props.mode = t->mode; 1984980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 1985980ebd25SJamal Hadi Salim x->props.family = ut->family; 1986980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 1987980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 1988980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 1989980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 1990980ebd25SJamal Hadi Salim 1991980ebd25SJamal Hadi Salim } 1992980ebd25SJamal Hadi Salim 1993980ebd25SJamal Hadi Salim kfree(x); 1994980ebd25SJamal Hadi Salim kfree(xp); 1995980ebd25SJamal Hadi Salim 1996980ebd25SJamal Hadi Salim return 0; 1997d8eb9307SIlpo Järvinen 1998d8eb9307SIlpo Järvinen bad_policy: 199962db5cfdSstephen hemminger WARN(1, "BAD policy passed\n"); 2000d8eb9307SIlpo Järvinen free_state: 2001d8eb9307SIlpo Järvinen kfree(x); 2002d8eb9307SIlpo Järvinen nomem: 2003d8eb9307SIlpo Järvinen return err; 2004980ebd25SJamal Hadi Salim } 2005980ebd25SJamal Hadi Salim 20065c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 20075c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 200813c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 20095424f32eSThomas Graf struct nlattr **attrs, int *num) 20105c79de6eSShinta Sugimoto { 20115424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 20125c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 20135c79de6eSShinta Sugimoto int i, num_migrate; 20145c79de6eSShinta Sugimoto 201513c1d189SArnaud Ebalard if (k != NULL) { 201613c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 201713c1d189SArnaud Ebalard 201813c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 201913c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 202013c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 202113c1d189SArnaud Ebalard k->family = uk->family; 202213c1d189SArnaud Ebalard k->reserved = uk->reserved; 202313c1d189SArnaud Ebalard } 202413c1d189SArnaud Ebalard 20255424f32eSThomas Graf um = nla_data(rt); 20265424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 20275c79de6eSShinta Sugimoto 20285c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 20295c79de6eSShinta Sugimoto return -EINVAL; 20305c79de6eSShinta Sugimoto 20315c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 20325c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 20335c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 20345c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 20355c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 20365c79de6eSShinta Sugimoto 20375c79de6eSShinta Sugimoto ma->proto = um->proto; 20385c79de6eSShinta Sugimoto ma->mode = um->mode; 20395c79de6eSShinta Sugimoto ma->reqid = um->reqid; 20405c79de6eSShinta Sugimoto 20415c79de6eSShinta Sugimoto ma->old_family = um->old_family; 20425c79de6eSShinta Sugimoto ma->new_family = um->new_family; 20435c79de6eSShinta Sugimoto } 20445c79de6eSShinta Sugimoto 20455c79de6eSShinta Sugimoto *num = i; 20465c79de6eSShinta Sugimoto return 0; 20475c79de6eSShinta Sugimoto } 20485c79de6eSShinta Sugimoto 20495c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 20505424f32eSThomas Graf struct nlattr **attrs) 20515c79de6eSShinta Sugimoto { 20527b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 20535c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 205413c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 20555c79de6eSShinta Sugimoto u8 type; 20565c79de6eSShinta Sugimoto int err; 20575c79de6eSShinta Sugimoto int n = 0; 20585c79de6eSShinta Sugimoto 205935a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 2060cf5cb79fSThomas Graf return -EINVAL; 20615c79de6eSShinta Sugimoto 206213c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 206313c1d189SArnaud Ebalard 20645424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 20655c79de6eSShinta Sugimoto if (err) 20665c79de6eSShinta Sugimoto return err; 20675c79de6eSShinta Sugimoto 206813c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 20695c79de6eSShinta Sugimoto if (err) 20705c79de6eSShinta Sugimoto return err; 20715c79de6eSShinta Sugimoto 20725c79de6eSShinta Sugimoto if (!n) 20735c79de6eSShinta Sugimoto return 0; 20745c79de6eSShinta Sugimoto 207513c1d189SArnaud Ebalard xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); 20765c79de6eSShinta Sugimoto 20775c79de6eSShinta Sugimoto return 0; 20785c79de6eSShinta Sugimoto } 20795c79de6eSShinta Sugimoto #else 20805c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 20815424f32eSThomas Graf struct nlattr **attrs) 20825c79de6eSShinta Sugimoto { 20835c79de6eSShinta Sugimoto return -ENOPROTOOPT; 20845c79de6eSShinta Sugimoto } 20855c79de6eSShinta Sugimoto #endif 20865c79de6eSShinta Sugimoto 20875c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 2088183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) 20895c79de6eSShinta Sugimoto { 20905c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 20915c79de6eSShinta Sugimoto 20925c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 20935c79de6eSShinta Sugimoto um.proto = m->proto; 20945c79de6eSShinta Sugimoto um.mode = m->mode; 20955c79de6eSShinta Sugimoto um.reqid = m->reqid; 20965c79de6eSShinta Sugimoto um.old_family = m->old_family; 20975c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 20985c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 20995c79de6eSShinta Sugimoto um.new_family = m->new_family; 21005c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 21015c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 21025c79de6eSShinta Sugimoto 2103c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 21045c79de6eSShinta Sugimoto } 21055c79de6eSShinta Sugimoto 2106183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) 210713c1d189SArnaud Ebalard { 210813c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 210913c1d189SArnaud Ebalard 211013c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 211113c1d189SArnaud Ebalard uk.family = k->family; 211213c1d189SArnaud Ebalard uk.reserved = k->reserved; 211313c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 2114a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 211513c1d189SArnaud Ebalard 211613c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 211713c1d189SArnaud Ebalard } 211813c1d189SArnaud Ebalard 211913c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 21207deb2264SThomas Graf { 21217deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 212213c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 21237deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 21247deb2264SThomas Graf + userpolicy_type_attrsize(); 21257deb2264SThomas Graf } 21267deb2264SThomas Graf 2127183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, 2128183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k, 2129183cad12SDavid S. Miller const struct xfrm_selector *sel, u8 dir, u8 type) 21305c79de6eSShinta Sugimoto { 2131183cad12SDavid S. Miller const struct xfrm_migrate *mp; 21325c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 21335c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 21345c79de6eSShinta Sugimoto int i; 21355c79de6eSShinta Sugimoto 213679b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 213779b8b7f4SThomas Graf if (nlh == NULL) 213879b8b7f4SThomas Graf return -EMSGSIZE; 21395c79de6eSShinta Sugimoto 21407b67c857SThomas Graf pol_id = nlmsg_data(nlh); 21415c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 21425c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 21435c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 21445c79de6eSShinta Sugimoto pol_id->dir = dir; 21455c79de6eSShinta Sugimoto 214613c1d189SArnaud Ebalard if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0)) 214713c1d189SArnaud Ebalard goto nlmsg_failure; 214813c1d189SArnaud Ebalard 21495c79de6eSShinta Sugimoto if (copy_to_user_policy_type(type, skb) < 0) 21505c79de6eSShinta Sugimoto goto nlmsg_failure; 21515c79de6eSShinta Sugimoto 21525c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 21535c79de6eSShinta Sugimoto if (copy_to_user_migrate(mp, skb) < 0) 21545c79de6eSShinta Sugimoto goto nlmsg_failure; 21555c79de6eSShinta Sugimoto } 21565c79de6eSShinta Sugimoto 21579825069dSThomas Graf return nlmsg_end(skb, nlh); 21585c79de6eSShinta Sugimoto nlmsg_failure: 21599825069dSThomas Graf nlmsg_cancel(skb, nlh); 21609825069dSThomas Graf return -EMSGSIZE; 21615c79de6eSShinta Sugimoto } 21625c79de6eSShinta Sugimoto 2163183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2164183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2165183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 21665c79de6eSShinta Sugimoto { 2167a6483b79SAlexey Dobriyan struct net *net = &init_net; 21685c79de6eSShinta Sugimoto struct sk_buff *skb; 21695c79de6eSShinta Sugimoto 217013c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 21715c79de6eSShinta Sugimoto if (skb == NULL) 21725c79de6eSShinta Sugimoto return -ENOMEM; 21735c79de6eSShinta Sugimoto 21745c79de6eSShinta Sugimoto /* build migrate */ 217513c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 21765c79de6eSShinta Sugimoto BUG(); 21775c79de6eSShinta Sugimoto 2178a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 21795c79de6eSShinta Sugimoto } 21805c79de6eSShinta Sugimoto #else 2181183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2182183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2183183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 21845c79de6eSShinta Sugimoto { 21855c79de6eSShinta Sugimoto return -ENOPROTOOPT; 21865c79de6eSShinta Sugimoto } 21875c79de6eSShinta Sugimoto #endif 2188d51d081dSJamal Hadi Salim 2189a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2190492b558bSThomas Graf 2191492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 219266f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2193492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2194492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2195492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2196492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2197492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2198492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2199980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 220053bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2201492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 220266f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 22036c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2204492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2205a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2206d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2207d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 220897a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 22095c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2210a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2211a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 22121da177e4SLinus Torvalds }; 22131da177e4SLinus Torvalds 2214492b558bSThomas Graf #undef XMSGSIZE 2215492b558bSThomas Graf 2216cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 2217c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)}, 2218c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)}, 2219c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64}, 2220c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 22211a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2222cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2223cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2224cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2225cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2226cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2227cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2228cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2229cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2230cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2231cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2232cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2233cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2234cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2235cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 223613c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 22376f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 223835d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2239d8647b79SSteffen Klassert [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 2240cf5cb79fSThomas Graf }; 2241cf5cb79fSThomas Graf 22421da177e4SLinus Torvalds static struct xfrm_link { 22435424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 22441da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 22454c563f76STimo Teras int (*done)(struct netlink_callback *); 2246492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2247492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2248492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2249492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 22504c563f76STimo Teras .dump = xfrm_dump_sa, 22514c563f76STimo Teras .done = xfrm_dump_sa_done }, 2252492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2253492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2254492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 22554c563f76STimo Teras .dump = xfrm_dump_policy, 22564c563f76STimo Teras .done = xfrm_dump_policy_done }, 2257492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2258980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 225953bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2260492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2261492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 22626c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2263492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2264492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2265d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2266d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 22675c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 226828d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2269ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 22701da177e4SLinus Torvalds }; 22711da177e4SLinus Torvalds 22721d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 22731da177e4SLinus Torvalds { 2274a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 227535a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 22761da177e4SLinus Torvalds struct xfrm_link *link; 2277a7bd9a45SThomas Graf int type, err; 22781da177e4SLinus Torvalds 22791da177e4SLinus Torvalds type = nlh->nlmsg_type; 22801da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 22811d00a4ebSThomas Graf return -EINVAL; 22821da177e4SLinus Torvalds 22831da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 22841da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 22851da177e4SLinus Torvalds 22861da177e4SLinus Torvalds /* All operations require privileges, even GET */ 22871d00a4ebSThomas Graf if (security_netlink_recv(skb, CAP_NET_ADMIN)) 22881d00a4ebSThomas Graf return -EPERM; 22891da177e4SLinus Torvalds 2290492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2291492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2292b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) { 22931da177e4SLinus Torvalds if (link->dump == NULL) 22941d00a4ebSThomas Graf return -EINVAL; 22951da177e4SLinus Torvalds 2296a6483b79SAlexey Dobriyan return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done); 22971da177e4SLinus Torvalds } 22981da177e4SLinus Torvalds 229935a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2300cf5cb79fSThomas Graf xfrma_policy); 2301a7bd9a45SThomas Graf if (err < 0) 2302a7bd9a45SThomas Graf return err; 23031da177e4SLinus Torvalds 23041da177e4SLinus Torvalds if (link->doit == NULL) 23051d00a4ebSThomas Graf return -EINVAL; 23061da177e4SLinus Torvalds 23075424f32eSThomas Graf return link->doit(skb, nlh, attrs); 23081da177e4SLinus Torvalds } 23091da177e4SLinus Torvalds 2310cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 23111da177e4SLinus Torvalds { 23124a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 2313cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 23144a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 23151da177e4SLinus Torvalds } 23161da177e4SLinus Torvalds 23177deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 23187deb2264SThomas Graf { 23196f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) 23206f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)); 23217deb2264SThomas Graf } 23227deb2264SThomas Graf 2323214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 23241da177e4SLinus Torvalds { 23251da177e4SLinus Torvalds struct xfrm_user_expire *ue; 23261da177e4SLinus Torvalds struct nlmsghdr *nlh; 23271da177e4SLinus Torvalds 232879b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 232979b8b7f4SThomas Graf if (nlh == NULL) 233079b8b7f4SThomas Graf return -EMSGSIZE; 23311da177e4SLinus Torvalds 23327b67c857SThomas Graf ue = nlmsg_data(nlh); 23331da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2334d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 23351da177e4SLinus Torvalds 23366f26b61eSJamal Hadi Salim if (xfrm_mark_put(skb, &x->mark)) 23376f26b61eSJamal Hadi Salim goto nla_put_failure; 23386f26b61eSJamal Hadi Salim 23399825069dSThomas Graf return nlmsg_end(skb, nlh); 23406f26b61eSJamal Hadi Salim 23416f26b61eSJamal Hadi Salim nla_put_failure: 23426f26b61eSJamal Hadi Salim return -EMSGSIZE; 23431da177e4SLinus Torvalds } 23441da177e4SLinus Torvalds 2345214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) 23461da177e4SLinus Torvalds { 2347fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 23481da177e4SLinus Torvalds struct sk_buff *skb; 23491da177e4SLinus Torvalds 23507deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 23511da177e4SLinus Torvalds if (skb == NULL) 23521da177e4SLinus Torvalds return -ENOMEM; 23531da177e4SLinus Torvalds 23546f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) { 23556f26b61eSJamal Hadi Salim kfree_skb(skb); 23566f26b61eSJamal Hadi Salim return -EMSGSIZE; 23576f26b61eSJamal Hadi Salim } 23581da177e4SLinus Torvalds 2359a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 23601da177e4SLinus Torvalds } 23611da177e4SLinus Torvalds 2362214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) 2363d51d081dSJamal Hadi Salim { 2364fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2365d51d081dSJamal Hadi Salim struct sk_buff *skb; 2366d51d081dSJamal Hadi Salim 2367d8647b79SSteffen Klassert skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 2368d51d081dSJamal Hadi Salim if (skb == NULL) 2369d51d081dSJamal Hadi Salim return -ENOMEM; 2370d51d081dSJamal Hadi Salim 2371d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2372d51d081dSJamal Hadi Salim BUG(); 2373d51d081dSJamal Hadi Salim 2374a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2375d51d081dSJamal Hadi Salim } 2376d51d081dSJamal Hadi Salim 2377214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c) 237826b15dadSJamal Hadi Salim { 23797067802eSAlexey Dobriyan struct net *net = c->net; 238026b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 238126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 238226b15dadSJamal Hadi Salim struct sk_buff *skb; 23837deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 238426b15dadSJamal Hadi Salim 23857deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 238626b15dadSJamal Hadi Salim if (skb == NULL) 238726b15dadSJamal Hadi Salim return -ENOMEM; 238826b15dadSJamal Hadi Salim 238979b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 239079b8b7f4SThomas Graf if (nlh == NULL) { 239179b8b7f4SThomas Graf kfree_skb(skb); 239279b8b7f4SThomas Graf return -EMSGSIZE; 239379b8b7f4SThomas Graf } 239426b15dadSJamal Hadi Salim 23957b67c857SThomas Graf p = nlmsg_data(nlh); 2396bf08867fSHerbert Xu p->proto = c->data.proto; 239726b15dadSJamal Hadi Salim 23989825069dSThomas Graf nlmsg_end(skb, nlh); 239926b15dadSJamal Hadi Salim 2400a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 240126b15dadSJamal Hadi Salim } 240226b15dadSJamal Hadi Salim 24037deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 240426b15dadSJamal Hadi Salim { 24057deb2264SThomas Graf size_t l = 0; 24061a6509d9SHerbert Xu if (x->aead) 24071a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 24084447bb33SMartin Willi if (x->aalg) { 24094447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 24104447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 24114447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 24124447bb33SMartin Willi } 241326b15dadSJamal Hadi Salim if (x->ealg) 24140f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 241526b15dadSJamal Hadi Salim if (x->calg) 24167deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 241726b15dadSJamal Hadi Salim if (x->encap) 24187deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 241935d2856bSMartin Willi if (x->tfcpad) 242035d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad)); 2421d8647b79SSteffen Klassert if (x->replay_esn) 2422d8647b79SSteffen Klassert l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn)); 242368325d3bSHerbert Xu if (x->security) 242468325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 242568325d3bSHerbert Xu x->security->ctx_len); 242668325d3bSHerbert Xu if (x->coaddr) 242768325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 242868325d3bSHerbert Xu 2429d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2430d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 243126b15dadSJamal Hadi Salim 243226b15dadSJamal Hadi Salim return l; 243326b15dadSJamal Hadi Salim } 243426b15dadSJamal Hadi Salim 2435214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) 243626b15dadSJamal Hadi Salim { 2437fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 243826b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 24390603eac0SHerbert Xu struct xfrm_usersa_id *id; 244026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 244126b15dadSJamal Hadi Salim struct sk_buff *skb; 244226b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 24430603eac0SHerbert Xu int headlen; 24440603eac0SHerbert Xu 24450603eac0SHerbert Xu headlen = sizeof(*p); 24460603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 24477deb2264SThomas Graf len += nla_total_size(headlen); 24480603eac0SHerbert Xu headlen = sizeof(*id); 24496f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 24500603eac0SHerbert Xu } 24517deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 245226b15dadSJamal Hadi Salim 24537deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 245426b15dadSJamal Hadi Salim if (skb == NULL) 245526b15dadSJamal Hadi Salim return -ENOMEM; 245626b15dadSJamal Hadi Salim 245779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 245879b8b7f4SThomas Graf if (nlh == NULL) 2459c0144beaSThomas Graf goto nla_put_failure; 246026b15dadSJamal Hadi Salim 24617b67c857SThomas Graf p = nlmsg_data(nlh); 24620603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2463c0144beaSThomas Graf struct nlattr *attr; 2464c0144beaSThomas Graf 24657b67c857SThomas Graf id = nlmsg_data(nlh); 24660603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 24670603eac0SHerbert Xu id->spi = x->id.spi; 24680603eac0SHerbert Xu id->family = x->props.family; 24690603eac0SHerbert Xu id->proto = x->id.proto; 24700603eac0SHerbert Xu 2471c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 2472c0144beaSThomas Graf if (attr == NULL) 2473c0144beaSThomas Graf goto nla_put_failure; 2474c0144beaSThomas Graf 2475c0144beaSThomas Graf p = nla_data(attr); 24760603eac0SHerbert Xu } 24770603eac0SHerbert Xu 247868325d3bSHerbert Xu if (copy_to_user_state_extra(x, p, skb)) 247968325d3bSHerbert Xu goto nla_put_failure; 248026b15dadSJamal Hadi Salim 24819825069dSThomas Graf nlmsg_end(skb, nlh); 248226b15dadSJamal Hadi Salim 2483a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 248426b15dadSJamal Hadi Salim 2485c0144beaSThomas Graf nla_put_failure: 248668325d3bSHerbert Xu /* Somebody screwed up with xfrm_sa_len! */ 248768325d3bSHerbert Xu WARN_ON(1); 248826b15dadSJamal Hadi Salim kfree_skb(skb); 248926b15dadSJamal Hadi Salim return -1; 249026b15dadSJamal Hadi Salim } 249126b15dadSJamal Hadi Salim 2492214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) 249326b15dadSJamal Hadi Salim { 249426b15dadSJamal Hadi Salim 249526b15dadSJamal Hadi Salim switch (c->event) { 2496f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 249726b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2498d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2499d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2500f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2501f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2502f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 250326b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2504f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 250526b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 250626b15dadSJamal Hadi Salim default: 250762db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n", 250862db5cfdSstephen hemminger c->event); 250926b15dadSJamal Hadi Salim break; 251026b15dadSJamal Hadi Salim } 251126b15dadSJamal Hadi Salim 251226b15dadSJamal Hadi Salim return 0; 251326b15dadSJamal Hadi Salim 251426b15dadSJamal Hadi Salim } 251526b15dadSJamal Hadi Salim 25167deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 25177deb2264SThomas Graf struct xfrm_policy *xp) 25187deb2264SThomas Graf { 25197deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 25207deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 25216f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 25227deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 25237deb2264SThomas Graf + userpolicy_type_attrsize(); 25247deb2264SThomas Graf } 25257deb2264SThomas Graf 25261da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 25271da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 25281da177e4SLinus Torvalds int dir) 25291da177e4SLinus Torvalds { 25301da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 25311da177e4SLinus Torvalds struct nlmsghdr *nlh; 25321da177e4SLinus Torvalds __u32 seq = xfrm_get_acqseq(); 25331da177e4SLinus Torvalds 253479b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 253579b8b7f4SThomas Graf if (nlh == NULL) 253679b8b7f4SThomas Graf return -EMSGSIZE; 25371da177e4SLinus Torvalds 25387b67c857SThomas Graf ua = nlmsg_data(nlh); 25391da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 25401da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 25411da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 25421da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 25431da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 25441da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 25451da177e4SLinus Torvalds ua->calgos = xt->calgos; 25461da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 25471da177e4SLinus Torvalds 25481da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 25491da177e4SLinus Torvalds goto nlmsg_failure; 25500d681623SSerge Hallyn if (copy_to_user_state_sec_ctx(x, skb)) 2551df71837dSTrent Jaeger goto nlmsg_failure; 25521459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2553f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 2554295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 2555295fae56SJamal Hadi Salim goto nla_put_failure; 25561da177e4SLinus Torvalds 25579825069dSThomas Graf return nlmsg_end(skb, nlh); 25581da177e4SLinus Torvalds 2559295fae56SJamal Hadi Salim nla_put_failure: 25601da177e4SLinus Torvalds nlmsg_failure: 25619825069dSThomas Graf nlmsg_cancel(skb, nlh); 25629825069dSThomas Graf return -EMSGSIZE; 25631da177e4SLinus Torvalds } 25641da177e4SLinus Torvalds 25651da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 25661da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 25671da177e4SLinus Torvalds { 2568a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 25691da177e4SLinus Torvalds struct sk_buff *skb; 25701da177e4SLinus Torvalds 25717deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 25721da177e4SLinus Torvalds if (skb == NULL) 25731da177e4SLinus Torvalds return -ENOMEM; 25741da177e4SLinus Torvalds 25751da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 25761da177e4SLinus Torvalds BUG(); 25771da177e4SLinus Torvalds 2578a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 25791da177e4SLinus Torvalds } 25801da177e4SLinus Torvalds 25811da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 25821da177e4SLinus Torvalds * or more templates. 25831da177e4SLinus Torvalds */ 2584cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 25851da177e4SLinus Torvalds u8 *data, int len, int *dir) 25861da177e4SLinus Torvalds { 2587fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 25881da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 25891da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 25901da177e4SLinus Torvalds struct xfrm_policy *xp; 25911da177e4SLinus Torvalds int nr; 25921da177e4SLinus Torvalds 2593cb969f07SVenkat Yekkirala switch (sk->sk_family) { 25941da177e4SLinus Torvalds case AF_INET: 25951da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 25961da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 25971da177e4SLinus Torvalds return NULL; 25981da177e4SLinus Torvalds } 25991da177e4SLinus Torvalds break; 26001da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 26011da177e4SLinus Torvalds case AF_INET6: 26021da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 26031da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 26041da177e4SLinus Torvalds return NULL; 26051da177e4SLinus Torvalds } 26061da177e4SLinus Torvalds break; 26071da177e4SLinus Torvalds #endif 26081da177e4SLinus Torvalds default: 26091da177e4SLinus Torvalds *dir = -EINVAL; 26101da177e4SLinus Torvalds return NULL; 26111da177e4SLinus Torvalds } 26121da177e4SLinus Torvalds 26131da177e4SLinus Torvalds *dir = -EINVAL; 26141da177e4SLinus Torvalds 26151da177e4SLinus Torvalds if (len < sizeof(*p) || 26161da177e4SLinus Torvalds verify_newpolicy_info(p)) 26171da177e4SLinus Torvalds return NULL; 26181da177e4SLinus Torvalds 26191da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2620b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 26211da177e4SLinus Torvalds return NULL; 26221da177e4SLinus Torvalds 2623a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2624a4f1bac6SHerbert Xu return NULL; 2625a4f1bac6SHerbert Xu 26262f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC); 26271da177e4SLinus Torvalds if (xp == NULL) { 26281da177e4SLinus Torvalds *dir = -ENOBUFS; 26291da177e4SLinus Torvalds return NULL; 26301da177e4SLinus Torvalds } 26311da177e4SLinus Torvalds 26321da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2633f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 26341da177e4SLinus Torvalds copy_templates(xp, ut, nr); 26351da177e4SLinus Torvalds 26361da177e4SLinus Torvalds *dir = p->dir; 26371da177e4SLinus Torvalds 26381da177e4SLinus Torvalds return xp; 26391da177e4SLinus Torvalds } 26401da177e4SLinus Torvalds 26417deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 26427deb2264SThomas Graf { 26437deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 26447deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 26457deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 2646295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 26477deb2264SThomas Graf + userpolicy_type_attrsize(); 26487deb2264SThomas Graf } 26497deb2264SThomas Graf 26501da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2651214e005bSDavid S. Miller int dir, const struct km_event *c) 26521da177e4SLinus Torvalds { 26531da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 26541da177e4SLinus Torvalds struct nlmsghdr *nlh; 2655d51d081dSJamal Hadi Salim int hard = c->data.hard; 26561da177e4SLinus Torvalds 265779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 265879b8b7f4SThomas Graf if (nlh == NULL) 265979b8b7f4SThomas Graf return -EMSGSIZE; 26601da177e4SLinus Torvalds 26617b67c857SThomas Graf upe = nlmsg_data(nlh); 26621da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 26631da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 26641da177e4SLinus Torvalds goto nlmsg_failure; 2665df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 2666df71837dSTrent Jaeger goto nlmsg_failure; 26671459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2668f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 2669295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 2670295fae56SJamal Hadi Salim goto nla_put_failure; 26711da177e4SLinus Torvalds upe->hard = !!hard; 26721da177e4SLinus Torvalds 26739825069dSThomas Graf return nlmsg_end(skb, nlh); 26741da177e4SLinus Torvalds 2675295fae56SJamal Hadi Salim nla_put_failure: 26761da177e4SLinus Torvalds nlmsg_failure: 26779825069dSThomas Graf nlmsg_cancel(skb, nlh); 26789825069dSThomas Graf return -EMSGSIZE; 26791da177e4SLinus Torvalds } 26801da177e4SLinus Torvalds 2681214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 26821da177e4SLinus Torvalds { 2683fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 26841da177e4SLinus Torvalds struct sk_buff *skb; 26851da177e4SLinus Torvalds 26867deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 26871da177e4SLinus Torvalds if (skb == NULL) 26881da177e4SLinus Torvalds return -ENOMEM; 26891da177e4SLinus Torvalds 2690d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 26911da177e4SLinus Torvalds BUG(); 26921da177e4SLinus Torvalds 2693a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 26941da177e4SLinus Torvalds } 26951da177e4SLinus Torvalds 2696214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) 269726b15dadSJamal Hadi Salim { 2698fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 269926b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 27000603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 270126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 270226b15dadSJamal Hadi Salim struct sk_buff *skb; 27037deb2264SThomas Graf int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 27040603eac0SHerbert Xu int headlen; 27050603eac0SHerbert Xu 27060603eac0SHerbert Xu headlen = sizeof(*p); 27070603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 27087deb2264SThomas Graf len += nla_total_size(headlen); 27090603eac0SHerbert Xu headlen = sizeof(*id); 27100603eac0SHerbert Xu } 2711cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 2712295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 27137deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 271426b15dadSJamal Hadi Salim 27157deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 271626b15dadSJamal Hadi Salim if (skb == NULL) 271726b15dadSJamal Hadi Salim return -ENOMEM; 271826b15dadSJamal Hadi Salim 271979b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 272079b8b7f4SThomas Graf if (nlh == NULL) 272179b8b7f4SThomas Graf goto nlmsg_failure; 272226b15dadSJamal Hadi Salim 27237b67c857SThomas Graf p = nlmsg_data(nlh); 27240603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2725c0144beaSThomas Graf struct nlattr *attr; 2726c0144beaSThomas Graf 27277b67c857SThomas Graf id = nlmsg_data(nlh); 27280603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 27290603eac0SHerbert Xu id->dir = dir; 27300603eac0SHerbert Xu if (c->data.byid) 27310603eac0SHerbert Xu id->index = xp->index; 27320603eac0SHerbert Xu else 27330603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 27340603eac0SHerbert Xu 2735c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 2736c0144beaSThomas Graf if (attr == NULL) 2737c0144beaSThomas Graf goto nlmsg_failure; 2738c0144beaSThomas Graf 2739c0144beaSThomas Graf p = nla_data(attr); 27400603eac0SHerbert Xu } 274126b15dadSJamal Hadi Salim 274226b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 274326b15dadSJamal Hadi Salim if (copy_to_user_tmpl(xp, skb) < 0) 274426b15dadSJamal Hadi Salim goto nlmsg_failure; 27451459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2746f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 274726b15dadSJamal Hadi Salim 2748295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 2749295fae56SJamal Hadi Salim goto nla_put_failure; 2750295fae56SJamal Hadi Salim 27519825069dSThomas Graf nlmsg_end(skb, nlh); 275226b15dadSJamal Hadi Salim 2753a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 275426b15dadSJamal Hadi Salim 2755295fae56SJamal Hadi Salim nla_put_failure: 275626b15dadSJamal Hadi Salim nlmsg_failure: 275726b15dadSJamal Hadi Salim kfree_skb(skb); 275826b15dadSJamal Hadi Salim return -1; 275926b15dadSJamal Hadi Salim } 276026b15dadSJamal Hadi Salim 2761214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c) 276226b15dadSJamal Hadi Salim { 27637067802eSAlexey Dobriyan struct net *net = c->net; 276426b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 276526b15dadSJamal Hadi Salim struct sk_buff *skb; 276626b15dadSJamal Hadi Salim 27677deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 276826b15dadSJamal Hadi Salim if (skb == NULL) 276926b15dadSJamal Hadi Salim return -ENOMEM; 277026b15dadSJamal Hadi Salim 277179b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 277279b8b7f4SThomas Graf if (nlh == NULL) 277379b8b7f4SThomas Graf goto nlmsg_failure; 27740c51f53cSJamal Hadi Salim if (copy_to_user_policy_type(c->data.type, skb) < 0) 27750c51f53cSJamal Hadi Salim goto nlmsg_failure; 277626b15dadSJamal Hadi Salim 27779825069dSThomas Graf nlmsg_end(skb, nlh); 277826b15dadSJamal Hadi Salim 2779a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 278026b15dadSJamal Hadi Salim 278126b15dadSJamal Hadi Salim nlmsg_failure: 278226b15dadSJamal Hadi Salim kfree_skb(skb); 278326b15dadSJamal Hadi Salim return -1; 278426b15dadSJamal Hadi Salim } 278526b15dadSJamal Hadi Salim 2786214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 278726b15dadSJamal Hadi Salim { 278826b15dadSJamal Hadi Salim 278926b15dadSJamal Hadi Salim switch (c->event) { 2790f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2791f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2792f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 279326b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2794f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 279526b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2796f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 279726b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 279826b15dadSJamal Hadi Salim default: 279962db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n", 280062db5cfdSstephen hemminger c->event); 280126b15dadSJamal Hadi Salim } 280226b15dadSJamal Hadi Salim 280326b15dadSJamal Hadi Salim return 0; 280426b15dadSJamal Hadi Salim 280526b15dadSJamal Hadi Salim } 280626b15dadSJamal Hadi Salim 28077deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 28087deb2264SThomas Graf { 28097deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 28107deb2264SThomas Graf } 28117deb2264SThomas Graf 281297a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 281397a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 281497a64b45SMasahide NAKAMURA { 281597a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 281697a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 281797a64b45SMasahide NAKAMURA 281879b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 281979b8b7f4SThomas Graf if (nlh == NULL) 282079b8b7f4SThomas Graf return -EMSGSIZE; 282197a64b45SMasahide NAKAMURA 28227b67c857SThomas Graf ur = nlmsg_data(nlh); 282397a64b45SMasahide NAKAMURA ur->proto = proto; 282497a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 282597a64b45SMasahide NAKAMURA 282697a64b45SMasahide NAKAMURA if (addr) 2827c0144beaSThomas Graf NLA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); 282897a64b45SMasahide NAKAMURA 28299825069dSThomas Graf return nlmsg_end(skb, nlh); 283097a64b45SMasahide NAKAMURA 2831c0144beaSThomas Graf nla_put_failure: 28329825069dSThomas Graf nlmsg_cancel(skb, nlh); 28339825069dSThomas Graf return -EMSGSIZE; 283497a64b45SMasahide NAKAMURA } 283597a64b45SMasahide NAKAMURA 2836db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 2837db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 283897a64b45SMasahide NAKAMURA { 283997a64b45SMasahide NAKAMURA struct sk_buff *skb; 284097a64b45SMasahide NAKAMURA 28417deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 284297a64b45SMasahide NAKAMURA if (skb == NULL) 284397a64b45SMasahide NAKAMURA return -ENOMEM; 284497a64b45SMasahide NAKAMURA 284597a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 284697a64b45SMasahide NAKAMURA BUG(); 284797a64b45SMasahide NAKAMURA 2848a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 284997a64b45SMasahide NAKAMURA } 285097a64b45SMasahide NAKAMURA 28513a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 28523a2dfbe8SMartin Willi { 28533a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 28543a2dfbe8SMartin Willi } 28553a2dfbe8SMartin Willi 28563a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 28573a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 28583a2dfbe8SMartin Willi { 28593a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 28603a2dfbe8SMartin Willi struct nlmsghdr *nlh; 28613a2dfbe8SMartin Willi 28623a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 28633a2dfbe8SMartin Willi if (nlh == NULL) 28643a2dfbe8SMartin Willi return -EMSGSIZE; 28653a2dfbe8SMartin Willi 28663a2dfbe8SMartin Willi um = nlmsg_data(nlh); 28673a2dfbe8SMartin Willi 28683a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 28693a2dfbe8SMartin Willi um->id.spi = x->id.spi; 28703a2dfbe8SMartin Willi um->id.family = x->props.family; 28713a2dfbe8SMartin Willi um->id.proto = x->id.proto; 28723a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 28733a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 28743a2dfbe8SMartin Willi um->new_sport = new_sport; 28753a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 28763a2dfbe8SMartin Willi um->reqid = x->props.reqid; 28773a2dfbe8SMartin Willi 28783a2dfbe8SMartin Willi return nlmsg_end(skb, nlh); 28793a2dfbe8SMartin Willi } 28803a2dfbe8SMartin Willi 28813a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 28823a2dfbe8SMartin Willi __be16 sport) 28833a2dfbe8SMartin Willi { 2884a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 28853a2dfbe8SMartin Willi struct sk_buff *skb; 28863a2dfbe8SMartin Willi 28873a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 28883a2dfbe8SMartin Willi return -EINVAL; 28893a2dfbe8SMartin Willi 28903a2dfbe8SMartin Willi if (!x->encap) 28913a2dfbe8SMartin Willi return -EINVAL; 28923a2dfbe8SMartin Willi 28933a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 28943a2dfbe8SMartin Willi if (skb == NULL) 28953a2dfbe8SMartin Willi return -ENOMEM; 28963a2dfbe8SMartin Willi 28973a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 28983a2dfbe8SMartin Willi BUG(); 28993a2dfbe8SMartin Willi 2900a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 29013a2dfbe8SMartin Willi } 29023a2dfbe8SMartin Willi 29031da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 29041da177e4SLinus Torvalds .id = "netlink", 29051da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 29061da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 29071da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 29081da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 290997a64b45SMasahide NAKAMURA .report = xfrm_send_report, 29105c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 29113a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 29121da177e4SLinus Torvalds }; 29131da177e4SLinus Torvalds 2914a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 29151da177e4SLinus Torvalds { 2916be33690dSPatrick McHardy struct sock *nlsk; 2917be33690dSPatrick McHardy 2918a6483b79SAlexey Dobriyan nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, 2919af65bdfcSPatrick McHardy xfrm_netlink_rcv, NULL, THIS_MODULE); 2920be33690dSPatrick McHardy if (nlsk == NULL) 29211da177e4SLinus Torvalds return -ENOMEM; 2922d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 2923a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, nlsk); 29241da177e4SLinus Torvalds return 0; 29251da177e4SLinus Torvalds } 29261da177e4SLinus Torvalds 2927d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 2928a6483b79SAlexey Dobriyan { 2929d79d792eSEric W. Biederman struct net *net; 2930d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2931a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, NULL); 2932d79d792eSEric W. Biederman synchronize_net(); 2933d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2934d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 2935a6483b79SAlexey Dobriyan } 2936a6483b79SAlexey Dobriyan 2937a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 2938a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 2939d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 2940a6483b79SAlexey Dobriyan }; 2941a6483b79SAlexey Dobriyan 2942a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 2943a6483b79SAlexey Dobriyan { 2944a6483b79SAlexey Dobriyan int rv; 2945a6483b79SAlexey Dobriyan 2946a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 2947a6483b79SAlexey Dobriyan 2948a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 2949a6483b79SAlexey Dobriyan if (rv < 0) 2950a6483b79SAlexey Dobriyan return rv; 2951a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 2952a6483b79SAlexey Dobriyan if (rv < 0) 2953a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 2954a6483b79SAlexey Dobriyan return rv; 2955a6483b79SAlexey Dobriyan } 2956a6483b79SAlexey Dobriyan 29571da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 29581da177e4SLinus Torvalds { 29591da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 2960a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 29611da177e4SLinus Torvalds } 29621da177e4SLinus Torvalds 29631da177e4SLinus Torvalds module_init(xfrm_user_init); 29641da177e4SLinus Torvalds module_exit(xfrm_user_exit); 29651da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 29664fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 2967f8cd5488SJamal Hadi Salim 2968