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 122df71837dSTrent Jaeger 1231da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1245424f32eSThomas Graf struct nlattr **attrs) 1251da177e4SLinus Torvalds { 1261da177e4SLinus Torvalds int err; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds err = -EINVAL; 1291da177e4SLinus Torvalds switch (p->family) { 1301da177e4SLinus Torvalds case AF_INET: 1311da177e4SLinus Torvalds break; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds case AF_INET6: 1341da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1351da177e4SLinus Torvalds break; 1361da177e4SLinus Torvalds #else 1371da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1381da177e4SLinus Torvalds goto out; 1391da177e4SLinus Torvalds #endif 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds default: 1421da177e4SLinus Torvalds goto out; 1433ff50b79SStephen Hemminger } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds err = -EINVAL; 1461da177e4SLinus Torvalds switch (p->id.proto) { 1471da177e4SLinus Torvalds case IPPROTO_AH: 1484447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1494447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1501a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 15135a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 15235d2856bSMartin Willi attrs[XFRMA_ALG_COMP] || 15335d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 1541da177e4SLinus Torvalds goto out; 1551da177e4SLinus Torvalds break; 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds case IPPROTO_ESP: 1581a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1591a6509d9SHerbert Xu goto out; 1601a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1614447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1621a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1631a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1641a6509d9SHerbert Xu goto out; 1651a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1664447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1671a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1681a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1691da177e4SLinus Torvalds goto out; 17035d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] && 17135d2856bSMartin Willi p->mode != XFRM_MODE_TUNNEL) 17235d2856bSMartin Willi goto out; 1731da177e4SLinus Torvalds break; 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds case IPPROTO_COMP: 17635a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 1771a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 17835a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1794447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 18035d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] || 18135d2856bSMartin Willi attrs[XFRMA_TFCPAD]) 1821da177e4SLinus Torvalds goto out; 1831da177e4SLinus Torvalds break; 1841da177e4SLinus Torvalds 185e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 186e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 187e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 18835a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 18935a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1904447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1911a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 19235a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 19335a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 19435a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 19535d2856bSMartin Willi attrs[XFRMA_TFCPAD] || 19635a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 197e23c7194SMasahide NAKAMURA goto out; 198e23c7194SMasahide NAKAMURA break; 199e23c7194SMasahide NAKAMURA #endif 200e23c7194SMasahide NAKAMURA 2011da177e4SLinus Torvalds default: 2021da177e4SLinus Torvalds goto out; 2033ff50b79SStephen Hemminger } 2041da177e4SLinus Torvalds 2051a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 2061a6509d9SHerbert Xu goto out; 2074447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2084447bb33SMartin Willi goto out; 20935a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2101da177e4SLinus Torvalds goto out; 21135a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2121da177e4SLinus Torvalds goto out; 21335a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2141da177e4SLinus Torvalds goto out; 21535a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 216df71837dSTrent Jaeger goto out; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds err = -EINVAL; 2191da177e4SLinus Torvalds switch (p->mode) { 2207e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2217e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 222060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2230a69452cSDiego Beltrami case XFRM_MODE_BEET: 2241da177e4SLinus Torvalds break; 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds default: 2271da177e4SLinus Torvalds goto out; 2283ff50b79SStephen Hemminger } 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds err = 0; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds out: 2331da177e4SLinus Torvalds return err; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2376f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int), 2385424f32eSThomas Graf struct nlattr *rta) 2391da177e4SLinus Torvalds { 2401da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2411da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds if (!rta) 2441da177e4SLinus Torvalds return 0; 2451da177e4SLinus Torvalds 2465424f32eSThomas Graf ualg = nla_data(rta); 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2491da177e4SLinus Torvalds if (!algo) 2501da177e4SLinus Torvalds return -ENOSYS; 2511da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2521da177e4SLinus Torvalds 2530f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2541da177e4SLinus Torvalds if (!p) 2551da177e4SLinus Torvalds return -ENOMEM; 2561da177e4SLinus Torvalds 25704ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2581da177e4SLinus Torvalds *algpp = p; 2591da177e4SLinus Torvalds return 0; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2624447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 2634447bb33SMartin Willi struct nlattr *rta) 2644447bb33SMartin Willi { 2654447bb33SMartin Willi struct xfrm_algo *ualg; 2664447bb33SMartin Willi struct xfrm_algo_auth *p; 2674447bb33SMartin Willi struct xfrm_algo_desc *algo; 2684447bb33SMartin Willi 2694447bb33SMartin Willi if (!rta) 2704447bb33SMartin Willi return 0; 2714447bb33SMartin Willi 2724447bb33SMartin Willi ualg = nla_data(rta); 2734447bb33SMartin Willi 2744447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 2754447bb33SMartin Willi if (!algo) 2764447bb33SMartin Willi return -ENOSYS; 2774447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 2784447bb33SMartin Willi 2794447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 2804447bb33SMartin Willi if (!p) 2814447bb33SMartin Willi return -ENOMEM; 2824447bb33SMartin Willi 2834447bb33SMartin Willi strcpy(p->alg_name, algo->name); 2844447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 2854447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 2864447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 2874447bb33SMartin Willi 2884447bb33SMartin Willi *algpp = p; 2894447bb33SMartin Willi return 0; 2904447bb33SMartin Willi } 2914447bb33SMartin Willi 2924447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 2934447bb33SMartin Willi struct nlattr *rta) 2944447bb33SMartin Willi { 2954447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 2964447bb33SMartin Willi struct xfrm_algo_desc *algo; 2974447bb33SMartin Willi 2984447bb33SMartin Willi if (!rta) 2994447bb33SMartin Willi return 0; 3004447bb33SMartin Willi 3014447bb33SMartin Willi ualg = nla_data(rta); 3024447bb33SMartin Willi 3034447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3044447bb33SMartin Willi if (!algo) 3054447bb33SMartin Willi return -ENOSYS; 306fa6dd8a2SNicolas Dichtel if ((ualg->alg_trunc_len / 8) > MAX_AH_AUTH_LEN || 307fa6dd8a2SNicolas Dichtel ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3084447bb33SMartin Willi return -EINVAL; 3094447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3104447bb33SMartin Willi 3114447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3124447bb33SMartin Willi if (!p) 3134447bb33SMartin Willi return -ENOMEM; 3144447bb33SMartin Willi 3154447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3164447bb33SMartin Willi if (!p->alg_trunc_len) 3174447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3184447bb33SMartin Willi 3194447bb33SMartin Willi *algpp = p; 3204447bb33SMartin Willi return 0; 3214447bb33SMartin Willi } 3224447bb33SMartin Willi 3231a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 3241a6509d9SHerbert Xu struct nlattr *rta) 3251a6509d9SHerbert Xu { 3261a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3271a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3281a6509d9SHerbert Xu 3291a6509d9SHerbert Xu if (!rta) 3301a6509d9SHerbert Xu return 0; 3311a6509d9SHerbert Xu 3321a6509d9SHerbert Xu ualg = nla_data(rta); 3331a6509d9SHerbert Xu 3341a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3351a6509d9SHerbert Xu if (!algo) 3361a6509d9SHerbert Xu return -ENOSYS; 3371a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 3381a6509d9SHerbert Xu 3391a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3401a6509d9SHerbert Xu if (!p) 3411a6509d9SHerbert Xu return -ENOMEM; 3421a6509d9SHerbert Xu 3431a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 3441a6509d9SHerbert Xu *algpp = p; 3451a6509d9SHerbert Xu return 0; 3461a6509d9SHerbert Xu } 3471a6509d9SHerbert Xu 348661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 349df71837dSTrent Jaeger { 350df71837dSTrent Jaeger int len = 0; 351df71837dSTrent Jaeger 352df71837dSTrent Jaeger if (xfrm_ctx) { 353df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 354df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 355df71837dSTrent Jaeger } 356df71837dSTrent Jaeger return len; 357df71837dSTrent Jaeger } 358df71837dSTrent Jaeger 3591da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 3601da177e4SLinus Torvalds { 3611da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 3621da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 3631da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 3641da177e4SLinus Torvalds x->props.mode = p->mode; 3651da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 3661da177e4SLinus Torvalds x->props.reqid = p->reqid; 3671da177e4SLinus Torvalds x->props.family = p->family; 36854489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 3691da177e4SLinus Torvalds x->props.flags = p->flags; 370196b0036SHerbert Xu 371ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 372196b0036SHerbert Xu x->sel.family = p->family; 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds 375d51d081dSJamal Hadi Salim /* 376d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 377d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 378d51d081dSJamal Hadi Salim * 379d51d081dSJamal Hadi Salim */ 3805424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) 381d51d081dSJamal Hadi Salim { 3825424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 3835424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 3845424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 3855424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 386d51d081dSJamal Hadi Salim 387d51d081dSJamal Hadi Salim if (rp) { 388d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 3895424f32eSThomas Graf replay = nla_data(rp); 390d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 391d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 392d51d081dSJamal Hadi Salim } 393d51d081dSJamal Hadi Salim 394d51d081dSJamal Hadi Salim if (lt) { 395d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 3965424f32eSThomas Graf ltime = nla_data(lt); 397d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 398d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 399d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 400d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 401d51d081dSJamal Hadi Salim } 402d51d081dSJamal Hadi Salim 403cf5cb79fSThomas Graf if (et) 4045424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 405d51d081dSJamal Hadi Salim 406cf5cb79fSThomas Graf if (rt) 4075424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 408d51d081dSJamal Hadi Salim } 409d51d081dSJamal Hadi Salim 410fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 411fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 4125424f32eSThomas Graf struct nlattr **attrs, 4131da177e4SLinus Torvalds int *errp) 4141da177e4SLinus Torvalds { 415fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 4161da177e4SLinus Torvalds int err = -ENOMEM; 4171da177e4SLinus Torvalds 4181da177e4SLinus Torvalds if (!x) 4191da177e4SLinus Torvalds goto error_no_put; 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds copy_from_user_state(x, p); 4221da177e4SLinus Torvalds 4231a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 4241a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 4251a6509d9SHerbert Xu goto error; 4264447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 4274447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 4284447bb33SMartin Willi goto error; 4294447bb33SMartin Willi if (!x->props.aalgo) { 4304447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 43135a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 4321da177e4SLinus Torvalds goto error; 4334447bb33SMartin Willi } 4341da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 4351da177e4SLinus Torvalds xfrm_ealg_get_byname, 43635a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 4371da177e4SLinus Torvalds goto error; 4381da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 4391da177e4SLinus Torvalds xfrm_calg_get_byname, 44035a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 4411da177e4SLinus Torvalds goto error; 442fd21150aSThomas Graf 443fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 444fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 445fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 446fd21150aSThomas Graf if (x->encap == NULL) 4471da177e4SLinus Torvalds goto error; 448fd21150aSThomas Graf } 449fd21150aSThomas Graf 45035d2856bSMartin Willi if (attrs[XFRMA_TFCPAD]) 45135d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); 45235d2856bSMartin Willi 453fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 454fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 455fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 456fd21150aSThomas Graf if (x->coaddr == NULL) 457060f02a3SNoriaki TAKAMIYA goto error; 458fd21150aSThomas Graf } 459fd21150aSThomas Graf 4606f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark); 4616f26b61eSJamal Hadi Salim 46272cb6962SHerbert Xu err = xfrm_init_state(x); 4631da177e4SLinus Torvalds if (err) 4641da177e4SLinus Torvalds goto error; 4651da177e4SLinus Torvalds 466fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 467fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 468df71837dSTrent Jaeger goto error; 469df71837dSTrent Jaeger 4701da177e4SLinus Torvalds x->km.seq = p->seq; 471b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 472d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 473b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 474d51d081dSJamal Hadi Salim x->preplay.bitmap = 0; 475d51d081dSJamal Hadi Salim x->preplay.seq = x->replay.seq+x->replay_maxdiff; 476d51d081dSJamal Hadi Salim x->preplay.oseq = x->replay.oseq +x->replay_maxdiff; 477d51d081dSJamal Hadi Salim 478*9fdc4883SSteffen Klassert if ((err = xfrm_init_replay(x))) 479*9fdc4883SSteffen Klassert goto error; 480d51d081dSJamal Hadi Salim 481*9fdc4883SSteffen Klassert /* override default values from above */ 4825424f32eSThomas Graf xfrm_update_ae_params(x, attrs); 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds return x; 4851da177e4SLinus Torvalds 4861da177e4SLinus Torvalds error: 4871da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 4881da177e4SLinus Torvalds xfrm_state_put(x); 4891da177e4SLinus Torvalds error_no_put: 4901da177e4SLinus Torvalds *errp = err; 4911da177e4SLinus Torvalds return NULL; 4921da177e4SLinus Torvalds } 4931da177e4SLinus Torvalds 49422e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 4955424f32eSThomas Graf struct nlattr **attrs) 4961da177e4SLinus Torvalds { 497fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 4987b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 4991da177e4SLinus Torvalds struct xfrm_state *x; 5001da177e4SLinus Torvalds int err; 50126b15dadSJamal Hadi Salim struct km_event c; 502c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 503c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 504c53fa1edSPatrick McHardy u32 sid; 5051da177e4SLinus Torvalds 50635a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 5071da177e4SLinus Torvalds if (err) 5081da177e4SLinus Torvalds return err; 5091da177e4SLinus Torvalds 510fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 5111da177e4SLinus Torvalds if (!x) 5121da177e4SLinus Torvalds return err; 5131da177e4SLinus Torvalds 51426b15dadSJamal Hadi Salim xfrm_state_hold(x); 5151da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 5161da177e4SLinus Torvalds err = xfrm_state_add(x); 5171da177e4SLinus Torvalds else 5181da177e4SLinus Torvalds err = xfrm_state_update(x); 5191da177e4SLinus Torvalds 520c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 5212532386fSEric Paris xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); 522161a09e7SJoy Latten 5231da177e4SLinus Torvalds if (err < 0) { 5241da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 52521380b81SHerbert Xu __xfrm_state_put(x); 5267d6dfe1fSPatrick McHardy goto out; 5271da177e4SLinus Torvalds } 5281da177e4SLinus Torvalds 52926b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 53026b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 531f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 53226b15dadSJamal Hadi Salim 53326b15dadSJamal Hadi Salim km_state_notify(x, &c); 5347d6dfe1fSPatrick McHardy out: 53526b15dadSJamal Hadi Salim xfrm_state_put(x); 5361da177e4SLinus Torvalds return err; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds 539fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 540fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 5415424f32eSThomas Graf struct nlattr **attrs, 542eb2971b6SMasahide NAKAMURA int *errp) 543eb2971b6SMasahide NAKAMURA { 544eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 5456f26b61eSJamal Hadi Salim struct xfrm_mark m; 546eb2971b6SMasahide NAKAMURA int err; 5476f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 548eb2971b6SMasahide NAKAMURA 549eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 550eb2971b6SMasahide NAKAMURA err = -ESRCH; 5516f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family); 552eb2971b6SMasahide NAKAMURA } else { 553eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 554eb2971b6SMasahide NAKAMURA 55535a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 556eb2971b6SMasahide NAKAMURA if (!saddr) { 557eb2971b6SMasahide NAKAMURA err = -EINVAL; 558eb2971b6SMasahide NAKAMURA goto out; 559eb2971b6SMasahide NAKAMURA } 560eb2971b6SMasahide NAKAMURA 5619abbffeeSMasahide NAKAMURA err = -ESRCH; 5626f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark, 5636f26b61eSJamal Hadi Salim &p->daddr, saddr, 564221df1edSAlexey Dobriyan p->proto, p->family); 565eb2971b6SMasahide NAKAMURA } 566eb2971b6SMasahide NAKAMURA 567eb2971b6SMasahide NAKAMURA out: 568eb2971b6SMasahide NAKAMURA if (!x && errp) 569eb2971b6SMasahide NAKAMURA *errp = err; 570eb2971b6SMasahide NAKAMURA return x; 571eb2971b6SMasahide NAKAMURA } 572eb2971b6SMasahide NAKAMURA 57322e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 5745424f32eSThomas Graf struct nlattr **attrs) 5751da177e4SLinus Torvalds { 576fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 5771da177e4SLinus Torvalds struct xfrm_state *x; 578eb2971b6SMasahide NAKAMURA int err = -ESRCH; 57926b15dadSJamal Hadi Salim struct km_event c; 5807b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 581c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 582c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 583c53fa1edSPatrick McHardy u32 sid; 5841da177e4SLinus Torvalds 585fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 5861da177e4SLinus Torvalds if (x == NULL) 587eb2971b6SMasahide NAKAMURA return err; 5881da177e4SLinus Torvalds 5896f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 590c8c05a8eSCatherine Zhang goto out; 591c8c05a8eSCatherine Zhang 5921da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 593c8c05a8eSCatherine Zhang err = -EPERM; 594c8c05a8eSCatherine Zhang goto out; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds 59726b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 598161a09e7SJoy Latten 599c8c05a8eSCatherine Zhang if (err < 0) 600c8c05a8eSCatherine Zhang goto out; 60126b15dadSJamal Hadi Salim 60226b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 60326b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 604f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 60526b15dadSJamal Hadi Salim km_state_notify(x, &c); 6061da177e4SLinus Torvalds 607c8c05a8eSCatherine Zhang out: 608c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 6092532386fSEric Paris xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); 610c8c05a8eSCatherine Zhang xfrm_state_put(x); 61126b15dadSJamal Hadi Salim return err; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 6151da177e4SLinus Torvalds { 6161da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 6171da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 6181da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 6191da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 6201da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 62154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 6221da177e4SLinus Torvalds p->mode = x->props.mode; 6231da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 6241da177e4SLinus Torvalds p->reqid = x->props.reqid; 6251da177e4SLinus Torvalds p->family = x->props.family; 6261da177e4SLinus Torvalds p->flags = x->props.flags; 6271da177e4SLinus Torvalds p->seq = x->km.seq; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds struct xfrm_dump_info { 6311da177e4SLinus Torvalds struct sk_buff *in_skb; 6321da177e4SLinus Torvalds struct sk_buff *out_skb; 6331da177e4SLinus Torvalds u32 nlmsg_seq; 6341da177e4SLinus Torvalds u16 nlmsg_flags; 6351da177e4SLinus Torvalds }; 6361da177e4SLinus Torvalds 637c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 638c0144beaSThomas Graf { 639c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 640c0144beaSThomas Graf struct nlattr *attr; 64168325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 642c0144beaSThomas Graf 643c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 644c0144beaSThomas Graf if (attr == NULL) 645c0144beaSThomas Graf return -EMSGSIZE; 646c0144beaSThomas Graf 647c0144beaSThomas Graf uctx = nla_data(attr); 648c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 649c0144beaSThomas Graf uctx->len = ctx_size; 650c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 651c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 652c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 653c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 654c0144beaSThomas Graf 655c0144beaSThomas Graf return 0; 656c0144beaSThomas Graf } 657c0144beaSThomas Graf 6584447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 6594447bb33SMartin Willi { 6604447bb33SMartin Willi struct xfrm_algo *algo; 6614447bb33SMartin Willi struct nlattr *nla; 6624447bb33SMartin Willi 6634447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 6644447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 6654447bb33SMartin Willi if (!nla) 6664447bb33SMartin Willi return -EMSGSIZE; 6674447bb33SMartin Willi 6684447bb33SMartin Willi algo = nla_data(nla); 6694447bb33SMartin Willi strcpy(algo->alg_name, auth->alg_name); 6704447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 6714447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 6724447bb33SMartin Willi 6734447bb33SMartin Willi return 0; 6744447bb33SMartin Willi } 6754447bb33SMartin Willi 67668325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 67768325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 67868325d3bSHerbert Xu struct xfrm_usersa_info *p, 67968325d3bSHerbert Xu struct sk_buff *skb) 6801da177e4SLinus Torvalds { 6811da177e4SLinus Torvalds copy_to_user_state(x, p); 6821da177e4SLinus Torvalds 683050f009eSHerbert Xu if (x->coaddr) 684050f009eSHerbert Xu NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 685050f009eSHerbert Xu 686050f009eSHerbert Xu if (x->lastused) 687050f009eSHerbert Xu NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); 688050f009eSHerbert Xu 6891a6509d9SHerbert Xu if (x->aead) 6901a6509d9SHerbert Xu NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 6914447bb33SMartin Willi if (x->aalg) { 6924447bb33SMartin Willi if (copy_to_user_auth(x->aalg, skb)) 6934447bb33SMartin Willi goto nla_put_failure; 6944447bb33SMartin Willi 6954447bb33SMartin Willi NLA_PUT(skb, XFRMA_ALG_AUTH_TRUNC, 6964447bb33SMartin Willi xfrm_alg_auth_len(x->aalg), x->aalg); 6974447bb33SMartin Willi } 6981da177e4SLinus Torvalds if (x->ealg) 6990f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 7001da177e4SLinus Torvalds if (x->calg) 701c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds if (x->encap) 704c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 7051da177e4SLinus Torvalds 70635d2856bSMartin Willi if (x->tfcpad) 70735d2856bSMartin Willi NLA_PUT_U32(skb, XFRMA_TFCPAD, x->tfcpad); 70835d2856bSMartin Willi 7096f26b61eSJamal Hadi Salim if (xfrm_mark_put(skb, &x->mark)) 7106f26b61eSJamal Hadi Salim goto nla_put_failure; 7116f26b61eSJamal Hadi Salim 712c0144beaSThomas Graf if (x->security && copy_sec_ctx(x->security, skb) < 0) 713c0144beaSThomas Graf goto nla_put_failure; 714060f02a3SNoriaki TAKAMIYA 71568325d3bSHerbert Xu return 0; 71668325d3bSHerbert Xu 71768325d3bSHerbert Xu nla_put_failure: 71868325d3bSHerbert Xu return -EMSGSIZE; 71968325d3bSHerbert Xu } 72068325d3bSHerbert Xu 72168325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 72268325d3bSHerbert Xu { 72368325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 72468325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 72568325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 72668325d3bSHerbert Xu struct xfrm_usersa_info *p; 72768325d3bSHerbert Xu struct nlmsghdr *nlh; 72868325d3bSHerbert Xu int err; 72968325d3bSHerbert Xu 73068325d3bSHerbert Xu nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 73168325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 73268325d3bSHerbert Xu if (nlh == NULL) 73368325d3bSHerbert Xu return -EMSGSIZE; 73468325d3bSHerbert Xu 73568325d3bSHerbert Xu p = nlmsg_data(nlh); 73668325d3bSHerbert Xu 73768325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 73868325d3bSHerbert Xu if (err) 73968325d3bSHerbert Xu goto nla_put_failure; 74068325d3bSHerbert Xu 7419825069dSThomas Graf nlmsg_end(skb, nlh); 7421da177e4SLinus Torvalds return 0; 7431da177e4SLinus Torvalds 744c0144beaSThomas Graf nla_put_failure: 7459825069dSThomas Graf nlmsg_cancel(skb, nlh); 74668325d3bSHerbert Xu return err; 7471da177e4SLinus Torvalds } 7481da177e4SLinus Torvalds 7494c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 7504c563f76STimo Teras { 7514c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 7524c563f76STimo Teras xfrm_state_walk_done(walk); 7534c563f76STimo Teras return 0; 7544c563f76STimo Teras } 7554c563f76STimo Teras 7561da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 7571da177e4SLinus Torvalds { 758fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 7594c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 7601da177e4SLinus Torvalds struct xfrm_dump_info info; 7611da177e4SLinus Torvalds 7624c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 7634c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 7644c563f76STimo Teras 7651da177e4SLinus Torvalds info.in_skb = cb->skb; 7661da177e4SLinus Torvalds info.out_skb = skb; 7671da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 7681da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 7694c563f76STimo Teras 7704c563f76STimo Teras if (!cb->args[0]) { 7714c563f76STimo Teras cb->args[0] = 1; 7724c563f76STimo Teras xfrm_state_walk_init(walk, 0); 7734c563f76STimo Teras } 7744c563f76STimo Teras 775fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 7761da177e4SLinus Torvalds 7771da177e4SLinus Torvalds return skb->len; 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds 7801da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 7811da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 7821da177e4SLinus Torvalds { 7831da177e4SLinus Torvalds struct xfrm_dump_info info; 7841da177e4SLinus Torvalds struct sk_buff *skb; 7851da177e4SLinus Torvalds 7867deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 7871da177e4SLinus Torvalds if (!skb) 7881da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds info.in_skb = in_skb; 7911da177e4SLinus Torvalds info.out_skb = skb; 7921da177e4SLinus Torvalds info.nlmsg_seq = seq; 7931da177e4SLinus Torvalds info.nlmsg_flags = 0; 7941da177e4SLinus Torvalds 7951da177e4SLinus Torvalds if (dump_one_state(x, 0, &info)) { 7961da177e4SLinus Torvalds kfree_skb(skb); 7971da177e4SLinus Torvalds return NULL; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds 8001da177e4SLinus Torvalds return skb; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds 8037deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 8047deb2264SThomas Graf { 8057deb2264SThomas Graf return NLMSG_ALIGN(4) 8067deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 8077deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 8087deb2264SThomas Graf } 8097deb2264SThomas Graf 810e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 811e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 812ecfd6b18SJamal Hadi Salim { 8135a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 8145a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 8155a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 816ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 817ecfd6b18SJamal Hadi Salim u32 *f; 818ecfd6b18SJamal Hadi Salim 819ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 820ecfd6b18SJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 821ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 822ecfd6b18SJamal Hadi Salim 823ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 824ecfd6b18SJamal Hadi Salim *f = flags; 825e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 8265a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 8275a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 8285a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 8295a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 8305a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 8315a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 8325a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 8335a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 834ecfd6b18SJamal Hadi Salim 8355a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 8365a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 837ecfd6b18SJamal Hadi Salim 838ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 839ecfd6b18SJamal Hadi Salim 840ecfd6b18SJamal Hadi Salim nla_put_failure: 841ecfd6b18SJamal Hadi Salim nlmsg_cancel(skb, nlh); 842ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 843ecfd6b18SJamal Hadi Salim } 844ecfd6b18SJamal Hadi Salim 845ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 8465424f32eSThomas Graf struct nlattr **attrs) 847ecfd6b18SJamal Hadi Salim { 848a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 849ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 8507b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 851ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 852ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 853ecfd6b18SJamal Hadi Salim 8547deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 855ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 856ecfd6b18SJamal Hadi Salim return -ENOMEM; 857ecfd6b18SJamal Hadi Salim 858e071041bSAlexey Dobriyan if (build_spdinfo(r_skb, net, spid, seq, *flags) < 0) 859ecfd6b18SJamal Hadi Salim BUG(); 860ecfd6b18SJamal Hadi Salim 861a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 862ecfd6b18SJamal Hadi Salim } 863ecfd6b18SJamal Hadi Salim 8647deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 8657deb2264SThomas Graf { 8667deb2264SThomas Graf return NLMSG_ALIGN(4) 8677deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 8687deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 8697deb2264SThomas Graf } 8707deb2264SThomas Graf 871e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 872e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 87328d8909bSJamal Hadi Salim { 874af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 875af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 87628d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 87728d8909bSJamal Hadi Salim u32 *f; 87828d8909bSJamal Hadi Salim 87928d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 88028d8909bSJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 88128d8909bSJamal Hadi Salim return -EMSGSIZE; 88228d8909bSJamal Hadi Salim 88328d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 88428d8909bSJamal Hadi Salim *f = flags; 885e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 88628d8909bSJamal Hadi Salim 887af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 888af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 889af11e316SJamal Hadi Salim 890af11e316SJamal Hadi Salim NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); 891af11e316SJamal Hadi Salim NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 89228d8909bSJamal Hadi Salim 89328d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 89428d8909bSJamal Hadi Salim 89528d8909bSJamal Hadi Salim nla_put_failure: 89628d8909bSJamal Hadi Salim nlmsg_cancel(skb, nlh); 89728d8909bSJamal Hadi Salim return -EMSGSIZE; 89828d8909bSJamal Hadi Salim } 89928d8909bSJamal Hadi Salim 90028d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 9015424f32eSThomas Graf struct nlattr **attrs) 90228d8909bSJamal Hadi Salim { 903a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 90428d8909bSJamal Hadi Salim struct sk_buff *r_skb; 9057b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 90628d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 90728d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 90828d8909bSJamal Hadi Salim 9097deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 91028d8909bSJamal Hadi Salim if (r_skb == NULL) 91128d8909bSJamal Hadi Salim return -ENOMEM; 91228d8909bSJamal Hadi Salim 913e071041bSAlexey Dobriyan if (build_sadinfo(r_skb, net, spid, seq, *flags) < 0) 91428d8909bSJamal Hadi Salim BUG(); 91528d8909bSJamal Hadi Salim 916a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 91728d8909bSJamal Hadi Salim } 91828d8909bSJamal Hadi Salim 91922e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 9205424f32eSThomas Graf struct nlattr **attrs) 9211da177e4SLinus Torvalds { 922fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 9237b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 9241da177e4SLinus Torvalds struct xfrm_state *x; 9251da177e4SLinus Torvalds struct sk_buff *resp_skb; 926eb2971b6SMasahide NAKAMURA int err = -ESRCH; 9271da177e4SLinus Torvalds 928fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 9291da177e4SLinus Torvalds if (x == NULL) 9301da177e4SLinus Torvalds goto out_noput; 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 9331da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 9341da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 9351da177e4SLinus Torvalds } else { 936a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 9371da177e4SLinus Torvalds } 9381da177e4SLinus Torvalds xfrm_state_put(x); 9391da177e4SLinus Torvalds out_noput: 9401da177e4SLinus Torvalds return err; 9411da177e4SLinus Torvalds } 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 9441da177e4SLinus Torvalds { 9451da177e4SLinus Torvalds switch (p->info.id.proto) { 9461da177e4SLinus Torvalds case IPPROTO_AH: 9471da177e4SLinus Torvalds case IPPROTO_ESP: 9481da177e4SLinus Torvalds break; 9491da177e4SLinus Torvalds 9501da177e4SLinus Torvalds case IPPROTO_COMP: 9511da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 9521da177e4SLinus Torvalds if (p->max >= 0x10000) 9531da177e4SLinus Torvalds return -EINVAL; 9541da177e4SLinus Torvalds break; 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds default: 9571da177e4SLinus Torvalds return -EINVAL; 9583ff50b79SStephen Hemminger } 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds if (p->min > p->max) 9611da177e4SLinus Torvalds return -EINVAL; 9621da177e4SLinus Torvalds 9631da177e4SLinus Torvalds return 0; 9641da177e4SLinus Torvalds } 9651da177e4SLinus Torvalds 96622e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 9675424f32eSThomas Graf struct nlattr **attrs) 9681da177e4SLinus Torvalds { 969fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 9701da177e4SLinus Torvalds struct xfrm_state *x; 9711da177e4SLinus Torvalds struct xfrm_userspi_info *p; 9721da177e4SLinus Torvalds struct sk_buff *resp_skb; 9731da177e4SLinus Torvalds xfrm_address_t *daddr; 9741da177e4SLinus Torvalds int family; 9751da177e4SLinus Torvalds int err; 9766f26b61eSJamal Hadi Salim u32 mark; 9776f26b61eSJamal Hadi Salim struct xfrm_mark m; 9781da177e4SLinus Torvalds 9797b67c857SThomas Graf p = nlmsg_data(nlh); 9801da177e4SLinus Torvalds err = verify_userspi_info(p); 9811da177e4SLinus Torvalds if (err) 9821da177e4SLinus Torvalds goto out_noput; 9831da177e4SLinus Torvalds 9841da177e4SLinus Torvalds family = p->info.family; 9851da177e4SLinus Torvalds daddr = &p->info.id.daddr; 9861da177e4SLinus Torvalds 9871da177e4SLinus Torvalds x = NULL; 9886f26b61eSJamal Hadi Salim 9896f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 9901da177e4SLinus Torvalds if (p->info.seq) { 9916f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq); 9921da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 9931da177e4SLinus Torvalds xfrm_state_put(x); 9941da177e4SLinus Torvalds x = NULL; 9951da177e4SLinus Torvalds } 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds if (!x) 9996f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid, 10001da177e4SLinus Torvalds p->info.id.proto, daddr, 10011da177e4SLinus Torvalds &p->info.saddr, 1, 10021da177e4SLinus Torvalds family); 10031da177e4SLinus Torvalds err = -ENOENT; 10041da177e4SLinus Torvalds if (x == NULL) 10051da177e4SLinus Torvalds goto out_noput; 10061da177e4SLinus Torvalds 1007658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 1008658b219eSHerbert Xu if (err) 1009658b219eSHerbert Xu goto out; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 10121da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 10131da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 10141da177e4SLinus Torvalds goto out; 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds 1017a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds out: 10201da177e4SLinus Torvalds xfrm_state_put(x); 10211da177e4SLinus Torvalds out_noput: 10221da177e4SLinus Torvalds return err; 10231da177e4SLinus Torvalds } 10241da177e4SLinus Torvalds 1025b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 10261da177e4SLinus Torvalds { 10271da177e4SLinus Torvalds switch (dir) { 10281da177e4SLinus Torvalds case XFRM_POLICY_IN: 10291da177e4SLinus Torvalds case XFRM_POLICY_OUT: 10301da177e4SLinus Torvalds case XFRM_POLICY_FWD: 10311da177e4SLinus Torvalds break; 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds default: 10341da177e4SLinus Torvalds return -EINVAL; 10353ff50b79SStephen Hemminger } 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds return 0; 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds 1040b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1041f7b6983fSMasahide NAKAMURA { 1042f7b6983fSMasahide NAKAMURA switch (type) { 1043f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1044f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1045f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1046f7b6983fSMasahide NAKAMURA #endif 1047f7b6983fSMasahide NAKAMURA break; 1048f7b6983fSMasahide NAKAMURA 1049f7b6983fSMasahide NAKAMURA default: 1050f7b6983fSMasahide NAKAMURA return -EINVAL; 10513ff50b79SStephen Hemminger } 1052f7b6983fSMasahide NAKAMURA 1053f7b6983fSMasahide NAKAMURA return 0; 1054f7b6983fSMasahide NAKAMURA } 1055f7b6983fSMasahide NAKAMURA 10561da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 10571da177e4SLinus Torvalds { 10581da177e4SLinus Torvalds switch (p->share) { 10591da177e4SLinus Torvalds case XFRM_SHARE_ANY: 10601da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 10611da177e4SLinus Torvalds case XFRM_SHARE_USER: 10621da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 10631da177e4SLinus Torvalds break; 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds default: 10661da177e4SLinus Torvalds return -EINVAL; 10673ff50b79SStephen Hemminger } 10681da177e4SLinus Torvalds 10691da177e4SLinus Torvalds switch (p->action) { 10701da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 10711da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 10721da177e4SLinus Torvalds break; 10731da177e4SLinus Torvalds 10741da177e4SLinus Torvalds default: 10751da177e4SLinus Torvalds return -EINVAL; 10763ff50b79SStephen Hemminger } 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds switch (p->sel.family) { 10791da177e4SLinus Torvalds case AF_INET: 10801da177e4SLinus Torvalds break; 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds case AF_INET6: 10831da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 10841da177e4SLinus Torvalds break; 10851da177e4SLinus Torvalds #else 10861da177e4SLinus Torvalds return -EAFNOSUPPORT; 10871da177e4SLinus Torvalds #endif 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds default: 10901da177e4SLinus Torvalds return -EINVAL; 10913ff50b79SStephen Hemminger } 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds return verify_policy_dir(p->dir); 10941da177e4SLinus Torvalds } 10951da177e4SLinus Torvalds 10965424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1097df71837dSTrent Jaeger { 10985424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1099df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1100df71837dSTrent Jaeger 1101df71837dSTrent Jaeger if (!rt) 1102df71837dSTrent Jaeger return 0; 1103df71837dSTrent Jaeger 11045424f32eSThomas Graf uctx = nla_data(rt); 110503e1ad7bSPaul Moore return security_xfrm_policy_alloc(&pol->security, uctx); 1106df71837dSTrent Jaeger } 1107df71837dSTrent Jaeger 11081da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 11091da177e4SLinus Torvalds int nr) 11101da177e4SLinus Torvalds { 11111da177e4SLinus Torvalds int i; 11121da177e4SLinus Torvalds 11131da177e4SLinus Torvalds xp->xfrm_nr = nr; 11141da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 11151da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 11181da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 11191da177e4SLinus Torvalds sizeof(xfrm_address_t)); 11201da177e4SLinus Torvalds t->reqid = ut->reqid; 11211da177e4SLinus Torvalds t->mode = ut->mode; 11221da177e4SLinus Torvalds t->share = ut->share; 11231da177e4SLinus Torvalds t->optional = ut->optional; 11241da177e4SLinus Torvalds t->aalgos = ut->aalgos; 11251da177e4SLinus Torvalds t->ealgos = ut->ealgos; 11261da177e4SLinus Torvalds t->calgos = ut->calgos; 1127c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1128c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 11298511d01dSMiika Komu t->encap_family = ut->family; 11301da177e4SLinus Torvalds } 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds 1133b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1134b4ad86bfSDavid S. Miller { 1135b4ad86bfSDavid S. Miller int i; 1136b4ad86bfSDavid S. Miller 1137b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1138b4ad86bfSDavid S. Miller return -EINVAL; 1139b4ad86bfSDavid S. Miller 1140b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1141b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1142b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1143b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1144b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1145b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1146b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1147b4ad86bfSDavid S. Miller */ 1148b4ad86bfSDavid S. Miller if (!ut[i].family) 1149b4ad86bfSDavid S. Miller ut[i].family = family; 1150b4ad86bfSDavid S. Miller 1151b4ad86bfSDavid S. Miller switch (ut[i].family) { 1152b4ad86bfSDavid S. Miller case AF_INET: 1153b4ad86bfSDavid S. Miller break; 1154b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1155b4ad86bfSDavid S. Miller case AF_INET6: 1156b4ad86bfSDavid S. Miller break; 1157b4ad86bfSDavid S. Miller #endif 1158b4ad86bfSDavid S. Miller default: 1159b4ad86bfSDavid S. Miller return -EINVAL; 11603ff50b79SStephen Hemminger } 1161b4ad86bfSDavid S. Miller } 1162b4ad86bfSDavid S. Miller 1163b4ad86bfSDavid S. Miller return 0; 1164b4ad86bfSDavid S. Miller } 1165b4ad86bfSDavid S. Miller 11665424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 11671da177e4SLinus Torvalds { 11685424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds if (!rt) { 11711da177e4SLinus Torvalds pol->xfrm_nr = 0; 11721da177e4SLinus Torvalds } else { 11735424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 11745424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1175b4ad86bfSDavid S. Miller int err; 11761da177e4SLinus Torvalds 1177b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1178b4ad86bfSDavid S. Miller if (err) 1179b4ad86bfSDavid S. Miller return err; 11801da177e4SLinus Torvalds 11815424f32eSThomas Graf copy_templates(pol, utmpl, nr); 11821da177e4SLinus Torvalds } 11831da177e4SLinus Torvalds return 0; 11841da177e4SLinus Torvalds } 11851da177e4SLinus Torvalds 11865424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1187f7b6983fSMasahide NAKAMURA { 11885424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1189f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1190b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1191f7b6983fSMasahide NAKAMURA int err; 1192f7b6983fSMasahide NAKAMURA 1193f7b6983fSMasahide NAKAMURA if (rt) { 11945424f32eSThomas Graf upt = nla_data(rt); 1195f7b6983fSMasahide NAKAMURA type = upt->type; 1196f7b6983fSMasahide NAKAMURA } 1197f7b6983fSMasahide NAKAMURA 1198f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1199f7b6983fSMasahide NAKAMURA if (err) 1200f7b6983fSMasahide NAKAMURA return err; 1201f7b6983fSMasahide NAKAMURA 1202f7b6983fSMasahide NAKAMURA *tp = type; 1203f7b6983fSMasahide NAKAMURA return 0; 1204f7b6983fSMasahide NAKAMURA } 1205f7b6983fSMasahide NAKAMURA 12061da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 12071da177e4SLinus Torvalds { 12081da177e4SLinus Torvalds xp->priority = p->priority; 12091da177e4SLinus Torvalds xp->index = p->index; 12101da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 12111da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 12121da177e4SLinus Torvalds xp->action = p->action; 12131da177e4SLinus Torvalds xp->flags = p->flags; 12141da177e4SLinus Torvalds xp->family = p->sel.family; 12151da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 12161da177e4SLinus Torvalds } 12171da177e4SLinus Torvalds 12181da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 12191da177e4SLinus Torvalds { 12201da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 12211da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 12221da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 12231da177e4SLinus Torvalds p->priority = xp->priority; 12241da177e4SLinus Torvalds p->index = xp->index; 12251da177e4SLinus Torvalds p->sel.family = xp->family; 12261da177e4SLinus Torvalds p->dir = dir; 12271da177e4SLinus Torvalds p->action = xp->action; 12281da177e4SLinus Torvalds p->flags = xp->flags; 12291da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 12301da177e4SLinus Torvalds } 12311da177e4SLinus Torvalds 1232fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 12331da177e4SLinus Torvalds { 1234fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 12351da177e4SLinus Torvalds int err; 12361da177e4SLinus Torvalds 12371da177e4SLinus Torvalds if (!xp) { 12381da177e4SLinus Torvalds *errp = -ENOMEM; 12391da177e4SLinus Torvalds return NULL; 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds 12421da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1243df71837dSTrent Jaeger 124435a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1245f7b6983fSMasahide NAKAMURA if (err) 1246f7b6983fSMasahide NAKAMURA goto error; 1247f7b6983fSMasahide NAKAMURA 124835a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 124935a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1250f7b6983fSMasahide NAKAMURA if (err) 1251f7b6983fSMasahide NAKAMURA goto error; 12521da177e4SLinus Torvalds 1253295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark); 1254295fae56SJamal Hadi Salim 12551da177e4SLinus Torvalds return xp; 1256f7b6983fSMasahide NAKAMURA error: 1257f7b6983fSMasahide NAKAMURA *errp = err; 125812a169e7SHerbert Xu xp->walk.dead = 1; 125964c31b3fSWANG Cong xfrm_policy_destroy(xp); 1260f7b6983fSMasahide NAKAMURA return NULL; 12611da177e4SLinus Torvalds } 12621da177e4SLinus Torvalds 126322e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 12645424f32eSThomas Graf struct nlattr **attrs) 12651da177e4SLinus Torvalds { 1266fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 12677b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 12681da177e4SLinus Torvalds struct xfrm_policy *xp; 126926b15dadSJamal Hadi Salim struct km_event c; 12701da177e4SLinus Torvalds int err; 12711da177e4SLinus Torvalds int excl; 1272c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1273c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1274c53fa1edSPatrick McHardy u32 sid; 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds err = verify_newpolicy_info(p); 12771da177e4SLinus Torvalds if (err) 12781da177e4SLinus Torvalds return err; 127935a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1280df71837dSTrent Jaeger if (err) 1281df71837dSTrent Jaeger return err; 12821da177e4SLinus Torvalds 1283fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 12841da177e4SLinus Torvalds if (!xp) 12851da177e4SLinus Torvalds return err; 12861da177e4SLinus Torvalds 128726b15dadSJamal Hadi Salim /* shouldnt excl be based on nlh flags?? 128826b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 128926b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 129026b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 12911da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 12921da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 1293c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 12942532386fSEric Paris xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); 1295161a09e7SJoy Latten 12961da177e4SLinus Torvalds if (err) { 129703e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 12981da177e4SLinus Torvalds kfree(xp); 12991da177e4SLinus Torvalds return err; 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds 1302f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 130326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 130426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 130526b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 130626b15dadSJamal Hadi Salim 13071da177e4SLinus Torvalds xfrm_pol_put(xp); 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds return 0; 13101da177e4SLinus Torvalds } 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 13131da177e4SLinus Torvalds { 13141da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 13151da177e4SLinus Torvalds int i; 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 13181da177e4SLinus Torvalds return 0; 13191da177e4SLinus Torvalds 13201da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 13211da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 13221da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 13231da177e4SLinus Torvalds 13241da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 13258511d01dSMiika Komu up->family = kp->encap_family; 13261da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 13271da177e4SLinus Torvalds up->reqid = kp->reqid; 13281da177e4SLinus Torvalds up->mode = kp->mode; 13291da177e4SLinus Torvalds up->share = kp->share; 13301da177e4SLinus Torvalds up->optional = kp->optional; 13311da177e4SLinus Torvalds up->aalgos = kp->aalgos; 13321da177e4SLinus Torvalds up->ealgos = kp->ealgos; 13331da177e4SLinus Torvalds up->calgos = kp->calgos; 13341da177e4SLinus Torvalds } 13351da177e4SLinus Torvalds 1336c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1337c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1338df71837dSTrent Jaeger } 1339df71837dSTrent Jaeger 13400d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 13410d681623SSerge Hallyn { 13420d681623SSerge Hallyn if (x->security) { 13430d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 13440d681623SSerge Hallyn } 13450d681623SSerge Hallyn return 0; 13460d681623SSerge Hallyn } 13470d681623SSerge Hallyn 13480d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 13490d681623SSerge Hallyn { 13500d681623SSerge Hallyn if (xp->security) { 13510d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 13520d681623SSerge Hallyn } 13530d681623SSerge Hallyn return 0; 13540d681623SSerge Hallyn } 1355cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1356cfbfd45aSThomas Graf { 1357cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1358cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1359cfbfd45aSThomas Graf #else 1360cfbfd45aSThomas Graf return 0; 1361cfbfd45aSThomas Graf #endif 1362cfbfd45aSThomas Graf } 13630d681623SSerge Hallyn 1364f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1365b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1366f7b6983fSMasahide NAKAMURA { 1367c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1368c0144beaSThomas Graf .type = type, 1369c0144beaSThomas Graf }; 1370f7b6983fSMasahide NAKAMURA 1371c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1372f7b6983fSMasahide NAKAMURA } 1373f7b6983fSMasahide NAKAMURA 1374f7b6983fSMasahide NAKAMURA #else 1375b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1376f7b6983fSMasahide NAKAMURA { 1377f7b6983fSMasahide NAKAMURA return 0; 1378f7b6983fSMasahide NAKAMURA } 1379f7b6983fSMasahide NAKAMURA #endif 1380f7b6983fSMasahide NAKAMURA 13811da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 13821da177e4SLinus Torvalds { 13831da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 13841da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 13851da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 13861da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 13871da177e4SLinus Torvalds struct nlmsghdr *nlh; 13881da177e4SLinus Torvalds 138979b8b7f4SThomas Graf nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 139079b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 139179b8b7f4SThomas Graf if (nlh == NULL) 139279b8b7f4SThomas Graf return -EMSGSIZE; 13931da177e4SLinus Torvalds 13947b67c857SThomas Graf p = nlmsg_data(nlh); 13951da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 13961da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 13971da177e4SLinus Torvalds goto nlmsg_failure; 1398df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 1399df71837dSTrent Jaeger goto nlmsg_failure; 14001459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 1401f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 1402295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 1403295fae56SJamal Hadi Salim goto nla_put_failure; 14041da177e4SLinus Torvalds 14059825069dSThomas Graf nlmsg_end(skb, nlh); 14061da177e4SLinus Torvalds return 0; 14071da177e4SLinus Torvalds 1408295fae56SJamal Hadi Salim nla_put_failure: 14091da177e4SLinus Torvalds nlmsg_failure: 14109825069dSThomas Graf nlmsg_cancel(skb, nlh); 14119825069dSThomas Graf return -EMSGSIZE; 14121da177e4SLinus Torvalds } 14131da177e4SLinus Torvalds 14144c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 14154c563f76STimo Teras { 14164c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 14174c563f76STimo Teras 14184c563f76STimo Teras xfrm_policy_walk_done(walk); 14194c563f76STimo Teras return 0; 14204c563f76STimo Teras } 14214c563f76STimo Teras 14221da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 14231da177e4SLinus Torvalds { 1424fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 14254c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 14261da177e4SLinus Torvalds struct xfrm_dump_info info; 14271da177e4SLinus Torvalds 14284c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 14294c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 14304c563f76STimo Teras 14311da177e4SLinus Torvalds info.in_skb = cb->skb; 14321da177e4SLinus Torvalds info.out_skb = skb; 14331da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 14341da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 14354c563f76STimo Teras 14364c563f76STimo Teras if (!cb->args[0]) { 14374c563f76STimo Teras cb->args[0] = 1; 14384c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 14394c563f76STimo Teras } 14404c563f76STimo Teras 1441fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 14421da177e4SLinus Torvalds 14431da177e4SLinus Torvalds return skb->len; 14441da177e4SLinus Torvalds } 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 14471da177e4SLinus Torvalds struct xfrm_policy *xp, 14481da177e4SLinus Torvalds int dir, u32 seq) 14491da177e4SLinus Torvalds { 14501da177e4SLinus Torvalds struct xfrm_dump_info info; 14511da177e4SLinus Torvalds struct sk_buff *skb; 14521da177e4SLinus Torvalds 14537deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 14541da177e4SLinus Torvalds if (!skb) 14551da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 14561da177e4SLinus Torvalds 14571da177e4SLinus Torvalds info.in_skb = in_skb; 14581da177e4SLinus Torvalds info.out_skb = skb; 14591da177e4SLinus Torvalds info.nlmsg_seq = seq; 14601da177e4SLinus Torvalds info.nlmsg_flags = 0; 14611da177e4SLinus Torvalds 14621da177e4SLinus Torvalds if (dump_one_policy(xp, dir, 0, &info) < 0) { 14631da177e4SLinus Torvalds kfree_skb(skb); 14641da177e4SLinus Torvalds return NULL; 14651da177e4SLinus Torvalds } 14661da177e4SLinus Torvalds 14671da177e4SLinus Torvalds return skb; 14681da177e4SLinus Torvalds } 14691da177e4SLinus Torvalds 147022e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 14715424f32eSThomas Graf struct nlattr **attrs) 14721da177e4SLinus Torvalds { 1473fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 14741da177e4SLinus Torvalds struct xfrm_policy *xp; 14751da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1476b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 14771da177e4SLinus Torvalds int err; 147826b15dadSJamal Hadi Salim struct km_event c; 14791da177e4SLinus Torvalds int delete; 1480295fae56SJamal Hadi Salim struct xfrm_mark m; 1481295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 14821da177e4SLinus Torvalds 14837b67c857SThomas Graf p = nlmsg_data(nlh); 14841da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 14851da177e4SLinus Torvalds 148635a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1487f7b6983fSMasahide NAKAMURA if (err) 1488f7b6983fSMasahide NAKAMURA return err; 1489f7b6983fSMasahide NAKAMURA 14901da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 14911da177e4SLinus Torvalds if (err) 14921da177e4SLinus Torvalds return err; 14931da177e4SLinus Torvalds 14941da177e4SLinus Torvalds if (p->index) 1495295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err); 1496df71837dSTrent Jaeger else { 14975424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 149803e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1499df71837dSTrent Jaeger 150035a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1501df71837dSTrent Jaeger if (err) 1502df71837dSTrent Jaeger return err; 1503df71837dSTrent Jaeger 15042c8dd116SDenis V. Lunev ctx = NULL; 1505df71837dSTrent Jaeger if (rt) { 15065424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1507df71837dSTrent Jaeger 150803e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 150903e1ad7bSPaul Moore if (err) 1510df71837dSTrent Jaeger return err; 15112c8dd116SDenis V. Lunev } 1512295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel, 15136f26b61eSJamal Hadi Salim ctx, delete, &err); 151403e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1515df71837dSTrent Jaeger } 15161da177e4SLinus Torvalds if (xp == NULL) 15171da177e4SLinus Torvalds return -ENOENT; 15181da177e4SLinus Torvalds 15191da177e4SLinus Torvalds if (!delete) { 15201da177e4SLinus Torvalds struct sk_buff *resp_skb; 15211da177e4SLinus Torvalds 15221da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 15231da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 15241da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 15251da177e4SLinus Torvalds } else { 1526a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 1527082a1ad5SThomas Graf NETLINK_CB(skb).pid); 15281da177e4SLinus Torvalds } 152926b15dadSJamal Hadi Salim } else { 1530c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1531c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1532c53fa1edSPatrick McHardy u32 sid; 15332532386fSEric Paris 1534c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 15352532386fSEric Paris xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, 15362532386fSEric Paris sid); 153713fcfbb0SDavid S. Miller 153813fcfbb0SDavid S. Miller if (err != 0) 1539c8c05a8eSCatherine Zhang goto out; 154013fcfbb0SDavid S. Miller 1541e7443892SHerbert Xu c.data.byid = p->index; 1542f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 154326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 154426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 154526b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 15461da177e4SLinus Torvalds } 15471da177e4SLinus Torvalds 1548c8c05a8eSCatherine Zhang out: 1549ef41aaa0SEric Paris xfrm_pol_put(xp); 15501da177e4SLinus Torvalds return err; 15511da177e4SLinus Torvalds } 15521da177e4SLinus Torvalds 155322e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 15545424f32eSThomas Graf struct nlattr **attrs) 15551da177e4SLinus Torvalds { 1556fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 155726b15dadSJamal Hadi Salim struct km_event c; 15587b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1559161a09e7SJoy Latten struct xfrm_audit audit_info; 15604aa2e62cSJoy Latten int err; 15611da177e4SLinus Torvalds 1562c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1563c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1564c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1565fc34acd3SAlexey Dobriyan err = xfrm_state_flush(net, p->proto, &audit_info); 15669e64cc95SJamal Hadi Salim if (err) { 15679e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */ 15689e64cc95SJamal Hadi Salim return 0; 1569069c474eSDavid S. Miller return err; 15709e64cc95SJamal Hadi Salim } 1571bf08867fSHerbert Xu c.data.proto = p->proto; 1572f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 157326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 157426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 15757067802eSAlexey Dobriyan c.net = net; 157626b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 157726b15dadSJamal Hadi Salim 15781da177e4SLinus Torvalds return 0; 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds 15817deb2264SThomas Graf static inline size_t xfrm_aevent_msgsize(void) 15827deb2264SThomas Graf { 15837deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 15847deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_replay_state)) 15857deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 15866f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 15877deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 15887deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 15897deb2264SThomas Graf } 1590d51d081dSJamal Hadi Salim 1591214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 1592d51d081dSJamal Hadi Salim { 1593d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1594d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 1595d51d081dSJamal Hadi Salim 159679b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 159779b8b7f4SThomas Graf if (nlh == NULL) 159879b8b7f4SThomas Graf return -EMSGSIZE; 1599d51d081dSJamal Hadi Salim 16007b67c857SThomas Graf id = nlmsg_data(nlh); 16012b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1602d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1603d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1604d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 16052b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 16062b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1607d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1608d51d081dSJamal Hadi Salim 1609c0144beaSThomas Graf NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); 1610c0144beaSThomas Graf NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 1611d51d081dSJamal Hadi Salim 1612c0144beaSThomas Graf if (id->flags & XFRM_AE_RTHR) 1613c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 1614d51d081dSJamal Hadi Salim 1615c0144beaSThomas Graf if (id->flags & XFRM_AE_ETHR) 1616c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH, 1617c0144beaSThomas Graf x->replay_maxage * 10 / HZ); 1618d51d081dSJamal Hadi Salim 16196f26b61eSJamal Hadi Salim if (xfrm_mark_put(skb, &x->mark)) 16206f26b61eSJamal Hadi Salim goto nla_put_failure; 16216f26b61eSJamal Hadi Salim 16229825069dSThomas Graf return nlmsg_end(skb, nlh); 1623d51d081dSJamal Hadi Salim 1624c0144beaSThomas Graf nla_put_failure: 16259825069dSThomas Graf nlmsg_cancel(skb, nlh); 16269825069dSThomas Graf return -EMSGSIZE; 1627d51d081dSJamal Hadi Salim } 1628d51d081dSJamal Hadi Salim 162922e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 16305424f32eSThomas Graf struct nlattr **attrs) 1631d51d081dSJamal Hadi Salim { 1632fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1633d51d081dSJamal Hadi Salim struct xfrm_state *x; 1634d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1635d51d081dSJamal Hadi Salim int err; 1636d51d081dSJamal Hadi Salim struct km_event c; 16376f26b61eSJamal Hadi Salim u32 mark; 16386f26b61eSJamal Hadi Salim struct xfrm_mark m; 16397b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1640d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1641d51d081dSJamal Hadi Salim 16427deb2264SThomas Graf r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 1643d51d081dSJamal Hadi Salim if (r_skb == NULL) 1644d51d081dSJamal Hadi Salim return -ENOMEM; 1645d51d081dSJamal Hadi Salim 16466f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 16476f26b61eSJamal Hadi Salim 16486f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); 1649d51d081dSJamal Hadi Salim if (x == NULL) { 1650b08d5840SPatrick McHardy kfree_skb(r_skb); 1651d51d081dSJamal Hadi Salim return -ESRCH; 1652d51d081dSJamal Hadi Salim } 1653d51d081dSJamal Hadi Salim 1654d51d081dSJamal Hadi Salim /* 1655d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1656d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1657d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1658d51d081dSJamal Hadi Salim */ 1659d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1660d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1661d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1662d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1663d51d081dSJamal Hadi Salim 1664d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1665d51d081dSJamal Hadi Salim BUG(); 1666a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid); 1667d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1668d51d081dSJamal Hadi Salim xfrm_state_put(x); 1669d51d081dSJamal Hadi Salim return err; 1670d51d081dSJamal Hadi Salim } 1671d51d081dSJamal Hadi Salim 167222e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 16735424f32eSThomas Graf struct nlattr **attrs) 1674d51d081dSJamal Hadi Salim { 1675fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1676d51d081dSJamal Hadi Salim struct xfrm_state *x; 1677d51d081dSJamal Hadi Salim struct km_event c; 1678d51d081dSJamal Hadi Salim int err = - EINVAL; 16796f26b61eSJamal Hadi Salim u32 mark = 0; 16806f26b61eSJamal Hadi Salim struct xfrm_mark m; 16817b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 16825424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 16835424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1684d51d081dSJamal Hadi Salim 1685d51d081dSJamal Hadi Salim if (!lt && !rp) 1686d51d081dSJamal Hadi Salim return err; 1687d51d081dSJamal Hadi Salim 1688d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1689d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1690d51d081dSJamal Hadi Salim return err; 1691d51d081dSJamal Hadi Salim 16926f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 16936f26b61eSJamal Hadi Salim 16946f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1695d51d081dSJamal Hadi Salim if (x == NULL) 1696d51d081dSJamal Hadi Salim return -ESRCH; 1697d51d081dSJamal Hadi Salim 1698d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1699d51d081dSJamal Hadi Salim goto out; 1700d51d081dSJamal Hadi Salim 1701d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 170235a7aa08SThomas Graf xfrm_update_ae_params(x, attrs); 1703d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1704d51d081dSJamal Hadi Salim 1705d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1706d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1707d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1708d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1709d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1710d51d081dSJamal Hadi Salim err = 0; 1711d51d081dSJamal Hadi Salim out: 1712d51d081dSJamal Hadi Salim xfrm_state_put(x); 1713d51d081dSJamal Hadi Salim return err; 1714d51d081dSJamal Hadi Salim } 1715d51d081dSJamal Hadi Salim 171622e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 17175424f32eSThomas Graf struct nlattr **attrs) 17181da177e4SLinus Torvalds { 1719fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 172026b15dadSJamal Hadi Salim struct km_event c; 1721b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1722f7b6983fSMasahide NAKAMURA int err; 1723161a09e7SJoy Latten struct xfrm_audit audit_info; 172426b15dadSJamal Hadi Salim 172535a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1726f7b6983fSMasahide NAKAMURA if (err) 1727f7b6983fSMasahide NAKAMURA return err; 1728f7b6983fSMasahide NAKAMURA 1729c53fa1edSPatrick McHardy audit_info.loginuid = audit_get_loginuid(current); 1730c53fa1edSPatrick McHardy audit_info.sessionid = audit_get_sessionid(current); 1731c53fa1edSPatrick McHardy security_task_getsecid(current, &audit_info.secid); 1732fc34acd3SAlexey Dobriyan err = xfrm_policy_flush(net, type, &audit_info); 17332f1eb65fSJamal Hadi Salim if (err) { 17342f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */ 17352f1eb65fSJamal Hadi Salim return 0; 1736069c474eSDavid S. Miller return err; 17372f1eb65fSJamal Hadi Salim } 17382f1eb65fSJamal Hadi Salim 1739f7b6983fSMasahide NAKAMURA c.data.type = type; 1740f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 174126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 174226b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 17437067802eSAlexey Dobriyan c.net = net; 174426b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 17451da177e4SLinus Torvalds return 0; 17461da177e4SLinus Torvalds } 17471da177e4SLinus Torvalds 174822e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 17495424f32eSThomas Graf struct nlattr **attrs) 17506c5c8ca7SJamal Hadi Salim { 1751fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 17526c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 17537b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 17546c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1755b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 17566c5c8ca7SJamal Hadi Salim int err = -ENOENT; 1757295fae56SJamal Hadi Salim struct xfrm_mark m; 1758295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 17596c5c8ca7SJamal Hadi Salim 176035a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1761f7b6983fSMasahide NAKAMURA if (err) 1762f7b6983fSMasahide NAKAMURA return err; 1763f7b6983fSMasahide NAKAMURA 1764c8bf4d04STimo Teräs err = verify_policy_dir(p->dir); 1765c8bf4d04STimo Teräs if (err) 1766c8bf4d04STimo Teräs return err; 1767c8bf4d04STimo Teräs 17686c5c8ca7SJamal Hadi Salim if (p->index) 1769295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err); 17706c5c8ca7SJamal Hadi Salim else { 17715424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 177203e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 17736c5c8ca7SJamal Hadi Salim 177435a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 17756c5c8ca7SJamal Hadi Salim if (err) 17766c5c8ca7SJamal Hadi Salim return err; 17776c5c8ca7SJamal Hadi Salim 17782c8dd116SDenis V. Lunev ctx = NULL; 17796c5c8ca7SJamal Hadi Salim if (rt) { 17805424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 17816c5c8ca7SJamal Hadi Salim 178203e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 178303e1ad7bSPaul Moore if (err) 17846c5c8ca7SJamal Hadi Salim return err; 17852c8dd116SDenis V. Lunev } 1786295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, 17876f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err); 178803e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 17896c5c8ca7SJamal Hadi Salim } 17906c5c8ca7SJamal Hadi Salim if (xp == NULL) 1791ef41aaa0SEric Paris return -ENOENT; 179203e1ad7bSPaul Moore 1793ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead)) 17946c5c8ca7SJamal Hadi Salim goto out; 17956c5c8ca7SJamal Hadi Salim 17966c5c8ca7SJamal Hadi Salim err = 0; 17976c5c8ca7SJamal Hadi Salim if (up->hard) { 1798c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1799c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1800c53fa1edSPatrick McHardy u32 sid; 1801c53fa1edSPatrick McHardy 1802c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 18036c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 18042532386fSEric Paris xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); 1805161a09e7SJoy Latten 18066c5c8ca7SJamal Hadi Salim } else { 18076c5c8ca7SJamal Hadi Salim // reset the timers here? 180862db5cfdSstephen hemminger WARN(1, "Dont know what to do with soft policy expire\n"); 18096c5c8ca7SJamal Hadi Salim } 18106c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 18116c5c8ca7SJamal Hadi Salim 18126c5c8ca7SJamal Hadi Salim out: 18136c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 18146c5c8ca7SJamal Hadi Salim return err; 18156c5c8ca7SJamal Hadi Salim } 18166c5c8ca7SJamal Hadi Salim 181722e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 18185424f32eSThomas Graf struct nlattr **attrs) 181953bc6b4dSJamal Hadi Salim { 1820fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 182153bc6b4dSJamal Hadi Salim struct xfrm_state *x; 182253bc6b4dSJamal Hadi Salim int err; 18237b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 182453bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 18256f26b61eSJamal Hadi Salim struct xfrm_mark m; 1826928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m); 182753bc6b4dSJamal Hadi Salim 18286f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family); 182953bc6b4dSJamal Hadi Salim 18303a765aa5SDavid S. Miller err = -ENOENT; 183153bc6b4dSJamal Hadi Salim if (x == NULL) 183253bc6b4dSJamal Hadi Salim return err; 183353bc6b4dSJamal Hadi Salim 183453bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 18353a765aa5SDavid S. Miller err = -EINVAL; 183653bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 183753bc6b4dSJamal Hadi Salim goto out; 183853bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 183953bc6b4dSJamal Hadi Salim 1840161a09e7SJoy Latten if (ue->hard) { 1841c53fa1edSPatrick McHardy uid_t loginuid = audit_get_loginuid(current); 1842c53fa1edSPatrick McHardy u32 sessionid = audit_get_sessionid(current); 1843c53fa1edSPatrick McHardy u32 sid; 1844c53fa1edSPatrick McHardy 1845c53fa1edSPatrick McHardy security_task_getsecid(current, &sid); 184653bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 18472532386fSEric Paris xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); 1848161a09e7SJoy Latten } 18493a765aa5SDavid S. Miller err = 0; 185053bc6b4dSJamal Hadi Salim out: 185153bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 185253bc6b4dSJamal Hadi Salim xfrm_state_put(x); 185353bc6b4dSJamal Hadi Salim return err; 185453bc6b4dSJamal Hadi Salim } 185553bc6b4dSJamal Hadi Salim 185622e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 18575424f32eSThomas Graf struct nlattr **attrs) 1858980ebd25SJamal Hadi Salim { 1859fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1860980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1861980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1862980ebd25SJamal Hadi Salim int i; 18635424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 18646f26b61eSJamal Hadi Salim struct xfrm_mark mark; 1865980ebd25SJamal Hadi Salim 18667b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 1867fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 1868980ebd25SJamal Hadi Salim int err = -ENOMEM; 1869980ebd25SJamal Hadi Salim 1870980ebd25SJamal Hadi Salim if (!x) 1871d8eb9307SIlpo Järvinen goto nomem; 1872980ebd25SJamal Hadi Salim 18736f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark); 18746f26b61eSJamal Hadi Salim 1875980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 1876d8eb9307SIlpo Järvinen if (err) 1877d8eb9307SIlpo Järvinen goto bad_policy; 1878980ebd25SJamal Hadi Salim 1879980ebd25SJamal Hadi Salim /* build an XP */ 1880fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 1881d8eb9307SIlpo Järvinen if (!xp) 1882d8eb9307SIlpo Järvinen goto free_state; 1883980ebd25SJamal Hadi Salim 1884980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 1885980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 1886980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 18876f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m; 18886f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v; 18895424f32eSThomas Graf ut = nla_data(rt); 1890980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 1891980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 1892980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 1893980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 1894980ebd25SJamal Hadi Salim x->props.mode = t->mode; 1895980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 1896980ebd25SJamal Hadi Salim x->props.family = ut->family; 1897980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 1898980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 1899980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 1900980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 1901980ebd25SJamal Hadi Salim 1902980ebd25SJamal Hadi Salim } 1903980ebd25SJamal Hadi Salim 1904980ebd25SJamal Hadi Salim kfree(x); 1905980ebd25SJamal Hadi Salim kfree(xp); 1906980ebd25SJamal Hadi Salim 1907980ebd25SJamal Hadi Salim return 0; 1908d8eb9307SIlpo Järvinen 1909d8eb9307SIlpo Järvinen bad_policy: 191062db5cfdSstephen hemminger WARN(1, "BAD policy passed\n"); 1911d8eb9307SIlpo Järvinen free_state: 1912d8eb9307SIlpo Järvinen kfree(x); 1913d8eb9307SIlpo Järvinen nomem: 1914d8eb9307SIlpo Järvinen return err; 1915980ebd25SJamal Hadi Salim } 1916980ebd25SJamal Hadi Salim 19175c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 19185c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 191913c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 19205424f32eSThomas Graf struct nlattr **attrs, int *num) 19215c79de6eSShinta Sugimoto { 19225424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 19235c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 19245c79de6eSShinta Sugimoto int i, num_migrate; 19255c79de6eSShinta Sugimoto 192613c1d189SArnaud Ebalard if (k != NULL) { 192713c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 192813c1d189SArnaud Ebalard 192913c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 193013c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 193113c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 193213c1d189SArnaud Ebalard k->family = uk->family; 193313c1d189SArnaud Ebalard k->reserved = uk->reserved; 193413c1d189SArnaud Ebalard } 193513c1d189SArnaud Ebalard 19365424f32eSThomas Graf um = nla_data(rt); 19375424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 19385c79de6eSShinta Sugimoto 19395c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 19405c79de6eSShinta Sugimoto return -EINVAL; 19415c79de6eSShinta Sugimoto 19425c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 19435c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 19445c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 19455c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 19465c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 19475c79de6eSShinta Sugimoto 19485c79de6eSShinta Sugimoto ma->proto = um->proto; 19495c79de6eSShinta Sugimoto ma->mode = um->mode; 19505c79de6eSShinta Sugimoto ma->reqid = um->reqid; 19515c79de6eSShinta Sugimoto 19525c79de6eSShinta Sugimoto ma->old_family = um->old_family; 19535c79de6eSShinta Sugimoto ma->new_family = um->new_family; 19545c79de6eSShinta Sugimoto } 19555c79de6eSShinta Sugimoto 19565c79de6eSShinta Sugimoto *num = i; 19575c79de6eSShinta Sugimoto return 0; 19585c79de6eSShinta Sugimoto } 19595c79de6eSShinta Sugimoto 19605c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 19615424f32eSThomas Graf struct nlattr **attrs) 19625c79de6eSShinta Sugimoto { 19637b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 19645c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 196513c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 19665c79de6eSShinta Sugimoto u8 type; 19675c79de6eSShinta Sugimoto int err; 19685c79de6eSShinta Sugimoto int n = 0; 19695c79de6eSShinta Sugimoto 197035a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 1971cf5cb79fSThomas Graf return -EINVAL; 19725c79de6eSShinta Sugimoto 197313c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 197413c1d189SArnaud Ebalard 19755424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 19765c79de6eSShinta Sugimoto if (err) 19775c79de6eSShinta Sugimoto return err; 19785c79de6eSShinta Sugimoto 197913c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 19805c79de6eSShinta Sugimoto if (err) 19815c79de6eSShinta Sugimoto return err; 19825c79de6eSShinta Sugimoto 19835c79de6eSShinta Sugimoto if (!n) 19845c79de6eSShinta Sugimoto return 0; 19855c79de6eSShinta Sugimoto 198613c1d189SArnaud Ebalard xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); 19875c79de6eSShinta Sugimoto 19885c79de6eSShinta Sugimoto return 0; 19895c79de6eSShinta Sugimoto } 19905c79de6eSShinta Sugimoto #else 19915c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 19925424f32eSThomas Graf struct nlattr **attrs) 19935c79de6eSShinta Sugimoto { 19945c79de6eSShinta Sugimoto return -ENOPROTOOPT; 19955c79de6eSShinta Sugimoto } 19965c79de6eSShinta Sugimoto #endif 19975c79de6eSShinta Sugimoto 19985c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 1999183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) 20005c79de6eSShinta Sugimoto { 20015c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 20025c79de6eSShinta Sugimoto 20035c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 20045c79de6eSShinta Sugimoto um.proto = m->proto; 20055c79de6eSShinta Sugimoto um.mode = m->mode; 20065c79de6eSShinta Sugimoto um.reqid = m->reqid; 20075c79de6eSShinta Sugimoto um.old_family = m->old_family; 20085c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 20095c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 20105c79de6eSShinta Sugimoto um.new_family = m->new_family; 20115c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 20125c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 20135c79de6eSShinta Sugimoto 2014c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 20155c79de6eSShinta Sugimoto } 20165c79de6eSShinta Sugimoto 2017183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) 201813c1d189SArnaud Ebalard { 201913c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 202013c1d189SArnaud Ebalard 202113c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 202213c1d189SArnaud Ebalard uk.family = k->family; 202313c1d189SArnaud Ebalard uk.reserved = k->reserved; 202413c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 2025a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 202613c1d189SArnaud Ebalard 202713c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 202813c1d189SArnaud Ebalard } 202913c1d189SArnaud Ebalard 203013c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 20317deb2264SThomas Graf { 20327deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 203313c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 20347deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 20357deb2264SThomas Graf + userpolicy_type_attrsize(); 20367deb2264SThomas Graf } 20377deb2264SThomas Graf 2038183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, 2039183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k, 2040183cad12SDavid S. Miller const struct xfrm_selector *sel, u8 dir, u8 type) 20415c79de6eSShinta Sugimoto { 2042183cad12SDavid S. Miller const struct xfrm_migrate *mp; 20435c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 20445c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 20455c79de6eSShinta Sugimoto int i; 20465c79de6eSShinta Sugimoto 204779b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 204879b8b7f4SThomas Graf if (nlh == NULL) 204979b8b7f4SThomas Graf return -EMSGSIZE; 20505c79de6eSShinta Sugimoto 20517b67c857SThomas Graf pol_id = nlmsg_data(nlh); 20525c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 20535c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 20545c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 20555c79de6eSShinta Sugimoto pol_id->dir = dir; 20565c79de6eSShinta Sugimoto 205713c1d189SArnaud Ebalard if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0)) 205813c1d189SArnaud Ebalard goto nlmsg_failure; 205913c1d189SArnaud Ebalard 20605c79de6eSShinta Sugimoto if (copy_to_user_policy_type(type, skb) < 0) 20615c79de6eSShinta Sugimoto goto nlmsg_failure; 20625c79de6eSShinta Sugimoto 20635c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 20645c79de6eSShinta Sugimoto if (copy_to_user_migrate(mp, skb) < 0) 20655c79de6eSShinta Sugimoto goto nlmsg_failure; 20665c79de6eSShinta Sugimoto } 20675c79de6eSShinta Sugimoto 20689825069dSThomas Graf return nlmsg_end(skb, nlh); 20695c79de6eSShinta Sugimoto nlmsg_failure: 20709825069dSThomas Graf nlmsg_cancel(skb, nlh); 20719825069dSThomas Graf return -EMSGSIZE; 20725c79de6eSShinta Sugimoto } 20735c79de6eSShinta Sugimoto 2074183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2075183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2076183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 20775c79de6eSShinta Sugimoto { 2078a6483b79SAlexey Dobriyan struct net *net = &init_net; 20795c79de6eSShinta Sugimoto struct sk_buff *skb; 20805c79de6eSShinta Sugimoto 208113c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 20825c79de6eSShinta Sugimoto if (skb == NULL) 20835c79de6eSShinta Sugimoto return -ENOMEM; 20845c79de6eSShinta Sugimoto 20855c79de6eSShinta Sugimoto /* build migrate */ 208613c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 20875c79de6eSShinta Sugimoto BUG(); 20885c79de6eSShinta Sugimoto 2089a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 20905c79de6eSShinta Sugimoto } 20915c79de6eSShinta Sugimoto #else 2092183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2093183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2094183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 20955c79de6eSShinta Sugimoto { 20965c79de6eSShinta Sugimoto return -ENOPROTOOPT; 20975c79de6eSShinta Sugimoto } 20985c79de6eSShinta Sugimoto #endif 2099d51d081dSJamal Hadi Salim 2100a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2101492b558bSThomas Graf 2102492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 210366f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2104492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2105492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2106492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2107492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2108492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2109492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2110980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 211153bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2112492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 211366f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 21146c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2115492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2116a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2117d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2118d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 211997a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 21205c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2121a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2122a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 21231da177e4SLinus Torvalds }; 21241da177e4SLinus Torvalds 2125492b558bSThomas Graf #undef XMSGSIZE 2126492b558bSThomas Graf 2127cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 2128c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)}, 2129c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)}, 2130c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64}, 2131c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 21321a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2133cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2134cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2135cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2136cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2137cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2138cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2139cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2140cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2141cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2142cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2143cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2144cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2145cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2146cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 214713c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 21486f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 214935d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2150cf5cb79fSThomas Graf }; 2151cf5cb79fSThomas Graf 21521da177e4SLinus Torvalds static struct xfrm_link { 21535424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 21541da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 21554c563f76STimo Teras int (*done)(struct netlink_callback *); 2156492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2157492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2158492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2159492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 21604c563f76STimo Teras .dump = xfrm_dump_sa, 21614c563f76STimo Teras .done = xfrm_dump_sa_done }, 2162492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2163492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2164492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 21654c563f76STimo Teras .dump = xfrm_dump_policy, 21664c563f76STimo Teras .done = xfrm_dump_policy_done }, 2167492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2168980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 216953bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2170492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2171492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 21726c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2173492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2174492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2175d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2176d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 21775c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 217828d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2179ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 21801da177e4SLinus Torvalds }; 21811da177e4SLinus Torvalds 21821d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 21831da177e4SLinus Torvalds { 2184a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 218535a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 21861da177e4SLinus Torvalds struct xfrm_link *link; 2187a7bd9a45SThomas Graf int type, err; 21881da177e4SLinus Torvalds 21891da177e4SLinus Torvalds type = nlh->nlmsg_type; 21901da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 21911d00a4ebSThomas Graf return -EINVAL; 21921da177e4SLinus Torvalds 21931da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 21941da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 21951da177e4SLinus Torvalds 21961da177e4SLinus Torvalds /* All operations require privileges, even GET */ 21971d00a4ebSThomas Graf if (security_netlink_recv(skb, CAP_NET_ADMIN)) 21981d00a4ebSThomas Graf return -EPERM; 21991da177e4SLinus Torvalds 2200492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2201492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2202b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) { 22031da177e4SLinus Torvalds if (link->dump == NULL) 22041d00a4ebSThomas Graf return -EINVAL; 22051da177e4SLinus Torvalds 2206a6483b79SAlexey Dobriyan return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done); 22071da177e4SLinus Torvalds } 22081da177e4SLinus Torvalds 220935a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2210cf5cb79fSThomas Graf xfrma_policy); 2211a7bd9a45SThomas Graf if (err < 0) 2212a7bd9a45SThomas Graf return err; 22131da177e4SLinus Torvalds 22141da177e4SLinus Torvalds if (link->doit == NULL) 22151d00a4ebSThomas Graf return -EINVAL; 22161da177e4SLinus Torvalds 22175424f32eSThomas Graf return link->doit(skb, nlh, attrs); 22181da177e4SLinus Torvalds } 22191da177e4SLinus Torvalds 2220cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 22211da177e4SLinus Torvalds { 22224a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 2223cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 22244a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 22251da177e4SLinus Torvalds } 22261da177e4SLinus Torvalds 22277deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 22287deb2264SThomas Graf { 22296f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) 22306f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)); 22317deb2264SThomas Graf } 22327deb2264SThomas Graf 2233214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 22341da177e4SLinus Torvalds { 22351da177e4SLinus Torvalds struct xfrm_user_expire *ue; 22361da177e4SLinus Torvalds struct nlmsghdr *nlh; 22371da177e4SLinus Torvalds 223879b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 223979b8b7f4SThomas Graf if (nlh == NULL) 224079b8b7f4SThomas Graf return -EMSGSIZE; 22411da177e4SLinus Torvalds 22427b67c857SThomas Graf ue = nlmsg_data(nlh); 22431da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2244d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 22451da177e4SLinus Torvalds 22466f26b61eSJamal Hadi Salim if (xfrm_mark_put(skb, &x->mark)) 22476f26b61eSJamal Hadi Salim goto nla_put_failure; 22486f26b61eSJamal Hadi Salim 22499825069dSThomas Graf return nlmsg_end(skb, nlh); 22506f26b61eSJamal Hadi Salim 22516f26b61eSJamal Hadi Salim nla_put_failure: 22526f26b61eSJamal Hadi Salim return -EMSGSIZE; 22531da177e4SLinus Torvalds } 22541da177e4SLinus Torvalds 2255214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) 22561da177e4SLinus Torvalds { 2257fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 22581da177e4SLinus Torvalds struct sk_buff *skb; 22591da177e4SLinus Torvalds 22607deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 22611da177e4SLinus Torvalds if (skb == NULL) 22621da177e4SLinus Torvalds return -ENOMEM; 22631da177e4SLinus Torvalds 22646f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) { 22656f26b61eSJamal Hadi Salim kfree_skb(skb); 22666f26b61eSJamal Hadi Salim return -EMSGSIZE; 22676f26b61eSJamal Hadi Salim } 22681da177e4SLinus Torvalds 2269a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 22701da177e4SLinus Torvalds } 22711da177e4SLinus Torvalds 2272214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) 2273d51d081dSJamal Hadi Salim { 2274fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2275d51d081dSJamal Hadi Salim struct sk_buff *skb; 2276d51d081dSJamal Hadi Salim 22777deb2264SThomas Graf skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 2278d51d081dSJamal Hadi Salim if (skb == NULL) 2279d51d081dSJamal Hadi Salim return -ENOMEM; 2280d51d081dSJamal Hadi Salim 2281d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2282d51d081dSJamal Hadi Salim BUG(); 2283d51d081dSJamal Hadi Salim 2284a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2285d51d081dSJamal Hadi Salim } 2286d51d081dSJamal Hadi Salim 2287214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c) 228826b15dadSJamal Hadi Salim { 22897067802eSAlexey Dobriyan struct net *net = c->net; 229026b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 229126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 229226b15dadSJamal Hadi Salim struct sk_buff *skb; 22937deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 229426b15dadSJamal Hadi Salim 22957deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 229626b15dadSJamal Hadi Salim if (skb == NULL) 229726b15dadSJamal Hadi Salim return -ENOMEM; 229826b15dadSJamal Hadi Salim 229979b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 230079b8b7f4SThomas Graf if (nlh == NULL) { 230179b8b7f4SThomas Graf kfree_skb(skb); 230279b8b7f4SThomas Graf return -EMSGSIZE; 230379b8b7f4SThomas Graf } 230426b15dadSJamal Hadi Salim 23057b67c857SThomas Graf p = nlmsg_data(nlh); 2306bf08867fSHerbert Xu p->proto = c->data.proto; 230726b15dadSJamal Hadi Salim 23089825069dSThomas Graf nlmsg_end(skb, nlh); 230926b15dadSJamal Hadi Salim 2310a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 231126b15dadSJamal Hadi Salim } 231226b15dadSJamal Hadi Salim 23137deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 231426b15dadSJamal Hadi Salim { 23157deb2264SThomas Graf size_t l = 0; 23161a6509d9SHerbert Xu if (x->aead) 23171a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 23184447bb33SMartin Willi if (x->aalg) { 23194447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 23204447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 23214447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 23224447bb33SMartin Willi } 232326b15dadSJamal Hadi Salim if (x->ealg) 23240f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 232526b15dadSJamal Hadi Salim if (x->calg) 23267deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 232726b15dadSJamal Hadi Salim if (x->encap) 23287deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 232935d2856bSMartin Willi if (x->tfcpad) 233035d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad)); 233168325d3bSHerbert Xu if (x->security) 233268325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 233368325d3bSHerbert Xu x->security->ctx_len); 233468325d3bSHerbert Xu if (x->coaddr) 233568325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 233668325d3bSHerbert Xu 2337d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2338d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 233926b15dadSJamal Hadi Salim 234026b15dadSJamal Hadi Salim return l; 234126b15dadSJamal Hadi Salim } 234226b15dadSJamal Hadi Salim 2343214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) 234426b15dadSJamal Hadi Salim { 2345fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 234626b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 23470603eac0SHerbert Xu struct xfrm_usersa_id *id; 234826b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 234926b15dadSJamal Hadi Salim struct sk_buff *skb; 235026b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 23510603eac0SHerbert Xu int headlen; 23520603eac0SHerbert Xu 23530603eac0SHerbert Xu headlen = sizeof(*p); 23540603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 23557deb2264SThomas Graf len += nla_total_size(headlen); 23560603eac0SHerbert Xu headlen = sizeof(*id); 23576f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 23580603eac0SHerbert Xu } 23597deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 236026b15dadSJamal Hadi Salim 23617deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 236226b15dadSJamal Hadi Salim if (skb == NULL) 236326b15dadSJamal Hadi Salim return -ENOMEM; 236426b15dadSJamal Hadi Salim 236579b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 236679b8b7f4SThomas Graf if (nlh == NULL) 2367c0144beaSThomas Graf goto nla_put_failure; 236826b15dadSJamal Hadi Salim 23697b67c857SThomas Graf p = nlmsg_data(nlh); 23700603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2371c0144beaSThomas Graf struct nlattr *attr; 2372c0144beaSThomas Graf 23737b67c857SThomas Graf id = nlmsg_data(nlh); 23740603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 23750603eac0SHerbert Xu id->spi = x->id.spi; 23760603eac0SHerbert Xu id->family = x->props.family; 23770603eac0SHerbert Xu id->proto = x->id.proto; 23780603eac0SHerbert Xu 2379c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 2380c0144beaSThomas Graf if (attr == NULL) 2381c0144beaSThomas Graf goto nla_put_failure; 2382c0144beaSThomas Graf 2383c0144beaSThomas Graf p = nla_data(attr); 23840603eac0SHerbert Xu } 23850603eac0SHerbert Xu 238668325d3bSHerbert Xu if (copy_to_user_state_extra(x, p, skb)) 238768325d3bSHerbert Xu goto nla_put_failure; 238826b15dadSJamal Hadi Salim 23899825069dSThomas Graf nlmsg_end(skb, nlh); 239026b15dadSJamal Hadi Salim 2391a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 239226b15dadSJamal Hadi Salim 2393c0144beaSThomas Graf nla_put_failure: 239468325d3bSHerbert Xu /* Somebody screwed up with xfrm_sa_len! */ 239568325d3bSHerbert Xu WARN_ON(1); 239626b15dadSJamal Hadi Salim kfree_skb(skb); 239726b15dadSJamal Hadi Salim return -1; 239826b15dadSJamal Hadi Salim } 239926b15dadSJamal Hadi Salim 2400214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) 240126b15dadSJamal Hadi Salim { 240226b15dadSJamal Hadi Salim 240326b15dadSJamal Hadi Salim switch (c->event) { 2404f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 240526b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2406d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2407d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2408f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2409f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2410f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 241126b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2412f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 241326b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 241426b15dadSJamal Hadi Salim default: 241562db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n", 241662db5cfdSstephen hemminger c->event); 241726b15dadSJamal Hadi Salim break; 241826b15dadSJamal Hadi Salim } 241926b15dadSJamal Hadi Salim 242026b15dadSJamal Hadi Salim return 0; 242126b15dadSJamal Hadi Salim 242226b15dadSJamal Hadi Salim } 242326b15dadSJamal Hadi Salim 24247deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 24257deb2264SThomas Graf struct xfrm_policy *xp) 24267deb2264SThomas Graf { 24277deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 24287deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 24296f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 24307deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 24317deb2264SThomas Graf + userpolicy_type_attrsize(); 24327deb2264SThomas Graf } 24337deb2264SThomas Graf 24341da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 24351da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 24361da177e4SLinus Torvalds int dir) 24371da177e4SLinus Torvalds { 24381da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 24391da177e4SLinus Torvalds struct nlmsghdr *nlh; 24401da177e4SLinus Torvalds __u32 seq = xfrm_get_acqseq(); 24411da177e4SLinus Torvalds 244279b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 244379b8b7f4SThomas Graf if (nlh == NULL) 244479b8b7f4SThomas Graf return -EMSGSIZE; 24451da177e4SLinus Torvalds 24467b67c857SThomas Graf ua = nlmsg_data(nlh); 24471da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 24481da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 24491da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 24501da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 24511da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 24521da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 24531da177e4SLinus Torvalds ua->calgos = xt->calgos; 24541da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 24551da177e4SLinus Torvalds 24561da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 24571da177e4SLinus Torvalds goto nlmsg_failure; 24580d681623SSerge Hallyn if (copy_to_user_state_sec_ctx(x, skb)) 2459df71837dSTrent Jaeger goto nlmsg_failure; 24601459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2461f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 2462295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 2463295fae56SJamal Hadi Salim goto nla_put_failure; 24641da177e4SLinus Torvalds 24659825069dSThomas Graf return nlmsg_end(skb, nlh); 24661da177e4SLinus Torvalds 2467295fae56SJamal Hadi Salim nla_put_failure: 24681da177e4SLinus Torvalds nlmsg_failure: 24699825069dSThomas Graf nlmsg_cancel(skb, nlh); 24709825069dSThomas Graf return -EMSGSIZE; 24711da177e4SLinus Torvalds } 24721da177e4SLinus Torvalds 24731da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 24741da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 24751da177e4SLinus Torvalds { 2476a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 24771da177e4SLinus Torvalds struct sk_buff *skb; 24781da177e4SLinus Torvalds 24797deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 24801da177e4SLinus Torvalds if (skb == NULL) 24811da177e4SLinus Torvalds return -ENOMEM; 24821da177e4SLinus Torvalds 24831da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 24841da177e4SLinus Torvalds BUG(); 24851da177e4SLinus Torvalds 2486a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 24871da177e4SLinus Torvalds } 24881da177e4SLinus Torvalds 24891da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 24901da177e4SLinus Torvalds * or more templates. 24911da177e4SLinus Torvalds */ 2492cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 24931da177e4SLinus Torvalds u8 *data, int len, int *dir) 24941da177e4SLinus Torvalds { 2495fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 24961da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 24971da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 24981da177e4SLinus Torvalds struct xfrm_policy *xp; 24991da177e4SLinus Torvalds int nr; 25001da177e4SLinus Torvalds 2501cb969f07SVenkat Yekkirala switch (sk->sk_family) { 25021da177e4SLinus Torvalds case AF_INET: 25031da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 25041da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 25051da177e4SLinus Torvalds return NULL; 25061da177e4SLinus Torvalds } 25071da177e4SLinus Torvalds break; 25081da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 25091da177e4SLinus Torvalds case AF_INET6: 25101da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 25111da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 25121da177e4SLinus Torvalds return NULL; 25131da177e4SLinus Torvalds } 25141da177e4SLinus Torvalds break; 25151da177e4SLinus Torvalds #endif 25161da177e4SLinus Torvalds default: 25171da177e4SLinus Torvalds *dir = -EINVAL; 25181da177e4SLinus Torvalds return NULL; 25191da177e4SLinus Torvalds } 25201da177e4SLinus Torvalds 25211da177e4SLinus Torvalds *dir = -EINVAL; 25221da177e4SLinus Torvalds 25231da177e4SLinus Torvalds if (len < sizeof(*p) || 25241da177e4SLinus Torvalds verify_newpolicy_info(p)) 25251da177e4SLinus Torvalds return NULL; 25261da177e4SLinus Torvalds 25271da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2528b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 25291da177e4SLinus Torvalds return NULL; 25301da177e4SLinus Torvalds 2531a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2532a4f1bac6SHerbert Xu return NULL; 2533a4f1bac6SHerbert Xu 25342f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC); 25351da177e4SLinus Torvalds if (xp == NULL) { 25361da177e4SLinus Torvalds *dir = -ENOBUFS; 25371da177e4SLinus Torvalds return NULL; 25381da177e4SLinus Torvalds } 25391da177e4SLinus Torvalds 25401da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2541f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 25421da177e4SLinus Torvalds copy_templates(xp, ut, nr); 25431da177e4SLinus Torvalds 25441da177e4SLinus Torvalds *dir = p->dir; 25451da177e4SLinus Torvalds 25461da177e4SLinus Torvalds return xp; 25471da177e4SLinus Torvalds } 25481da177e4SLinus Torvalds 25497deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 25507deb2264SThomas Graf { 25517deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 25527deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 25537deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 2554295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 25557deb2264SThomas Graf + userpolicy_type_attrsize(); 25567deb2264SThomas Graf } 25577deb2264SThomas Graf 25581da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2559214e005bSDavid S. Miller int dir, const struct km_event *c) 25601da177e4SLinus Torvalds { 25611da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 25621da177e4SLinus Torvalds struct nlmsghdr *nlh; 2563d51d081dSJamal Hadi Salim int hard = c->data.hard; 25641da177e4SLinus Torvalds 256579b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 256679b8b7f4SThomas Graf if (nlh == NULL) 256779b8b7f4SThomas Graf return -EMSGSIZE; 25681da177e4SLinus Torvalds 25697b67c857SThomas Graf upe = nlmsg_data(nlh); 25701da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 25711da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 25721da177e4SLinus Torvalds goto nlmsg_failure; 2573df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 2574df71837dSTrent Jaeger goto nlmsg_failure; 25751459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2576f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 2577295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 2578295fae56SJamal Hadi Salim goto nla_put_failure; 25791da177e4SLinus Torvalds upe->hard = !!hard; 25801da177e4SLinus Torvalds 25819825069dSThomas Graf return nlmsg_end(skb, nlh); 25821da177e4SLinus Torvalds 2583295fae56SJamal Hadi Salim nla_put_failure: 25841da177e4SLinus Torvalds nlmsg_failure: 25859825069dSThomas Graf nlmsg_cancel(skb, nlh); 25869825069dSThomas Graf return -EMSGSIZE; 25871da177e4SLinus Torvalds } 25881da177e4SLinus Torvalds 2589214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 25901da177e4SLinus Torvalds { 2591fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 25921da177e4SLinus Torvalds struct sk_buff *skb; 25931da177e4SLinus Torvalds 25947deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 25951da177e4SLinus Torvalds if (skb == NULL) 25961da177e4SLinus Torvalds return -ENOMEM; 25971da177e4SLinus Torvalds 2598d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 25991da177e4SLinus Torvalds BUG(); 26001da177e4SLinus Torvalds 2601a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 26021da177e4SLinus Torvalds } 26031da177e4SLinus Torvalds 2604214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) 260526b15dadSJamal Hadi Salim { 2606fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 260726b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 26080603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 260926b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 261026b15dadSJamal Hadi Salim struct sk_buff *skb; 26117deb2264SThomas Graf int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 26120603eac0SHerbert Xu int headlen; 26130603eac0SHerbert Xu 26140603eac0SHerbert Xu headlen = sizeof(*p); 26150603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 26167deb2264SThomas Graf len += nla_total_size(headlen); 26170603eac0SHerbert Xu headlen = sizeof(*id); 26180603eac0SHerbert Xu } 2619cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 2620295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 26217deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 262226b15dadSJamal Hadi Salim 26237deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 262426b15dadSJamal Hadi Salim if (skb == NULL) 262526b15dadSJamal Hadi Salim return -ENOMEM; 262626b15dadSJamal Hadi Salim 262779b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 262879b8b7f4SThomas Graf if (nlh == NULL) 262979b8b7f4SThomas Graf goto nlmsg_failure; 263026b15dadSJamal Hadi Salim 26317b67c857SThomas Graf p = nlmsg_data(nlh); 26320603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2633c0144beaSThomas Graf struct nlattr *attr; 2634c0144beaSThomas Graf 26357b67c857SThomas Graf id = nlmsg_data(nlh); 26360603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 26370603eac0SHerbert Xu id->dir = dir; 26380603eac0SHerbert Xu if (c->data.byid) 26390603eac0SHerbert Xu id->index = xp->index; 26400603eac0SHerbert Xu else 26410603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 26420603eac0SHerbert Xu 2643c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 2644c0144beaSThomas Graf if (attr == NULL) 2645c0144beaSThomas Graf goto nlmsg_failure; 2646c0144beaSThomas Graf 2647c0144beaSThomas Graf p = nla_data(attr); 26480603eac0SHerbert Xu } 264926b15dadSJamal Hadi Salim 265026b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 265126b15dadSJamal Hadi Salim if (copy_to_user_tmpl(xp, skb) < 0) 265226b15dadSJamal Hadi Salim goto nlmsg_failure; 26531459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2654f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 265526b15dadSJamal Hadi Salim 2656295fae56SJamal Hadi Salim if (xfrm_mark_put(skb, &xp->mark)) 2657295fae56SJamal Hadi Salim goto nla_put_failure; 2658295fae56SJamal Hadi Salim 26599825069dSThomas Graf nlmsg_end(skb, nlh); 266026b15dadSJamal Hadi Salim 2661a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 266226b15dadSJamal Hadi Salim 2663295fae56SJamal Hadi Salim nla_put_failure: 266426b15dadSJamal Hadi Salim nlmsg_failure: 266526b15dadSJamal Hadi Salim kfree_skb(skb); 266626b15dadSJamal Hadi Salim return -1; 266726b15dadSJamal Hadi Salim } 266826b15dadSJamal Hadi Salim 2669214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c) 267026b15dadSJamal Hadi Salim { 26717067802eSAlexey Dobriyan struct net *net = c->net; 267226b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 267326b15dadSJamal Hadi Salim struct sk_buff *skb; 267426b15dadSJamal Hadi Salim 26757deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 267626b15dadSJamal Hadi Salim if (skb == NULL) 267726b15dadSJamal Hadi Salim return -ENOMEM; 267826b15dadSJamal Hadi Salim 267979b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 268079b8b7f4SThomas Graf if (nlh == NULL) 268179b8b7f4SThomas Graf goto nlmsg_failure; 26820c51f53cSJamal Hadi Salim if (copy_to_user_policy_type(c->data.type, skb) < 0) 26830c51f53cSJamal Hadi Salim goto nlmsg_failure; 268426b15dadSJamal Hadi Salim 26859825069dSThomas Graf nlmsg_end(skb, nlh); 268626b15dadSJamal Hadi Salim 2687a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 268826b15dadSJamal Hadi Salim 268926b15dadSJamal Hadi Salim nlmsg_failure: 269026b15dadSJamal Hadi Salim kfree_skb(skb); 269126b15dadSJamal Hadi Salim return -1; 269226b15dadSJamal Hadi Salim } 269326b15dadSJamal Hadi Salim 2694214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 269526b15dadSJamal Hadi Salim { 269626b15dadSJamal Hadi Salim 269726b15dadSJamal Hadi Salim switch (c->event) { 2698f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2699f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2700f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 270126b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2702f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 270326b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2704f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 270526b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 270626b15dadSJamal Hadi Salim default: 270762db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n", 270862db5cfdSstephen hemminger c->event); 270926b15dadSJamal Hadi Salim } 271026b15dadSJamal Hadi Salim 271126b15dadSJamal Hadi Salim return 0; 271226b15dadSJamal Hadi Salim 271326b15dadSJamal Hadi Salim } 271426b15dadSJamal Hadi Salim 27157deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 27167deb2264SThomas Graf { 27177deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 27187deb2264SThomas Graf } 27197deb2264SThomas Graf 272097a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 272197a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 272297a64b45SMasahide NAKAMURA { 272397a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 272497a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 272597a64b45SMasahide NAKAMURA 272679b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 272779b8b7f4SThomas Graf if (nlh == NULL) 272879b8b7f4SThomas Graf return -EMSGSIZE; 272997a64b45SMasahide NAKAMURA 27307b67c857SThomas Graf ur = nlmsg_data(nlh); 273197a64b45SMasahide NAKAMURA ur->proto = proto; 273297a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 273397a64b45SMasahide NAKAMURA 273497a64b45SMasahide NAKAMURA if (addr) 2735c0144beaSThomas Graf NLA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); 273697a64b45SMasahide NAKAMURA 27379825069dSThomas Graf return nlmsg_end(skb, nlh); 273897a64b45SMasahide NAKAMURA 2739c0144beaSThomas Graf nla_put_failure: 27409825069dSThomas Graf nlmsg_cancel(skb, nlh); 27419825069dSThomas Graf return -EMSGSIZE; 274297a64b45SMasahide NAKAMURA } 274397a64b45SMasahide NAKAMURA 2744db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 2745db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 274697a64b45SMasahide NAKAMURA { 274797a64b45SMasahide NAKAMURA struct sk_buff *skb; 274897a64b45SMasahide NAKAMURA 27497deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 275097a64b45SMasahide NAKAMURA if (skb == NULL) 275197a64b45SMasahide NAKAMURA return -ENOMEM; 275297a64b45SMasahide NAKAMURA 275397a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 275497a64b45SMasahide NAKAMURA BUG(); 275597a64b45SMasahide NAKAMURA 2756a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 275797a64b45SMasahide NAKAMURA } 275897a64b45SMasahide NAKAMURA 27593a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 27603a2dfbe8SMartin Willi { 27613a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 27623a2dfbe8SMartin Willi } 27633a2dfbe8SMartin Willi 27643a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 27653a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 27663a2dfbe8SMartin Willi { 27673a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 27683a2dfbe8SMartin Willi struct nlmsghdr *nlh; 27693a2dfbe8SMartin Willi 27703a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 27713a2dfbe8SMartin Willi if (nlh == NULL) 27723a2dfbe8SMartin Willi return -EMSGSIZE; 27733a2dfbe8SMartin Willi 27743a2dfbe8SMartin Willi um = nlmsg_data(nlh); 27753a2dfbe8SMartin Willi 27763a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 27773a2dfbe8SMartin Willi um->id.spi = x->id.spi; 27783a2dfbe8SMartin Willi um->id.family = x->props.family; 27793a2dfbe8SMartin Willi um->id.proto = x->id.proto; 27803a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 27813a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 27823a2dfbe8SMartin Willi um->new_sport = new_sport; 27833a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 27843a2dfbe8SMartin Willi um->reqid = x->props.reqid; 27853a2dfbe8SMartin Willi 27863a2dfbe8SMartin Willi return nlmsg_end(skb, nlh); 27873a2dfbe8SMartin Willi } 27883a2dfbe8SMartin Willi 27893a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 27903a2dfbe8SMartin Willi __be16 sport) 27913a2dfbe8SMartin Willi { 2792a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 27933a2dfbe8SMartin Willi struct sk_buff *skb; 27943a2dfbe8SMartin Willi 27953a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 27963a2dfbe8SMartin Willi return -EINVAL; 27973a2dfbe8SMartin Willi 27983a2dfbe8SMartin Willi if (!x->encap) 27993a2dfbe8SMartin Willi return -EINVAL; 28003a2dfbe8SMartin Willi 28013a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 28023a2dfbe8SMartin Willi if (skb == NULL) 28033a2dfbe8SMartin Willi return -ENOMEM; 28043a2dfbe8SMartin Willi 28053a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 28063a2dfbe8SMartin Willi BUG(); 28073a2dfbe8SMartin Willi 2808a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 28093a2dfbe8SMartin Willi } 28103a2dfbe8SMartin Willi 28111da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 28121da177e4SLinus Torvalds .id = "netlink", 28131da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 28141da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 28151da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 28161da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 281797a64b45SMasahide NAKAMURA .report = xfrm_send_report, 28185c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 28193a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 28201da177e4SLinus Torvalds }; 28211da177e4SLinus Torvalds 2822a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 28231da177e4SLinus Torvalds { 2824be33690dSPatrick McHardy struct sock *nlsk; 2825be33690dSPatrick McHardy 2826a6483b79SAlexey Dobriyan nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, 2827af65bdfcSPatrick McHardy xfrm_netlink_rcv, NULL, THIS_MODULE); 2828be33690dSPatrick McHardy if (nlsk == NULL) 28291da177e4SLinus Torvalds return -ENOMEM; 2830d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 2831a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, nlsk); 28321da177e4SLinus Torvalds return 0; 28331da177e4SLinus Torvalds } 28341da177e4SLinus Torvalds 2835d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 2836a6483b79SAlexey Dobriyan { 2837d79d792eSEric W. Biederman struct net *net; 2838d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2839a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, NULL); 2840d79d792eSEric W. Biederman synchronize_net(); 2841d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2842d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 2843a6483b79SAlexey Dobriyan } 2844a6483b79SAlexey Dobriyan 2845a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 2846a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 2847d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 2848a6483b79SAlexey Dobriyan }; 2849a6483b79SAlexey Dobriyan 2850a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 2851a6483b79SAlexey Dobriyan { 2852a6483b79SAlexey Dobriyan int rv; 2853a6483b79SAlexey Dobriyan 2854a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 2855a6483b79SAlexey Dobriyan 2856a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 2857a6483b79SAlexey Dobriyan if (rv < 0) 2858a6483b79SAlexey Dobriyan return rv; 2859a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 2860a6483b79SAlexey Dobriyan if (rv < 0) 2861a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 2862a6483b79SAlexey Dobriyan return rv; 2863a6483b79SAlexey Dobriyan } 2864a6483b79SAlexey Dobriyan 28651da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 28661da177e4SLinus Torvalds { 28671da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 2868a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 28691da177e4SLinus Torvalds } 28701da177e4SLinus Torvalds 28711da177e4SLinus Torvalds module_init(xfrm_user_init); 28721da177e4SLinus Torvalds module_exit(xfrm_user_exit); 28731da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 28744fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 2875f8cd5488SJamal Hadi Salim 2876