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> 291da177e4SLinus Torvalds #include <asm/uaccess.h> 30e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 31e23c7194SMasahide NAKAMURA #include <linux/in6.h> 32e23c7194SMasahide NAKAMURA #endif 331da177e4SLinus Torvalds 341a6509d9SHerbert Xu static inline int aead_len(struct xfrm_algo_aead *alg) 351a6509d9SHerbert Xu { 361a6509d9SHerbert Xu return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); 371a6509d9SHerbert Xu } 381a6509d9SHerbert Xu 395424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) 401da177e4SLinus Torvalds { 415424f32eSThomas Graf struct nlattr *rt = attrs[type]; 421da177e4SLinus Torvalds struct xfrm_algo *algp; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds if (!rt) 451da177e4SLinus Torvalds return 0; 461da177e4SLinus Torvalds 475424f32eSThomas Graf algp = nla_data(rt); 480f99be0dSEric Dumazet if (nla_len(rt) < xfrm_alg_len(algp)) 4931c26852SHerbert Xu return -EINVAL; 5031c26852SHerbert Xu 511da177e4SLinus Torvalds switch (type) { 521da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 531da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 541da177e4SLinus Torvalds case XFRMA_ALG_COMP: 551da177e4SLinus Torvalds break; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds default: 581da177e4SLinus Torvalds return -EINVAL; 593ff50b79SStephen Hemminger } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 621da177e4SLinus Torvalds return 0; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 654447bb33SMartin Willi static int verify_auth_trunc(struct nlattr **attrs) 664447bb33SMartin Willi { 674447bb33SMartin Willi struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; 684447bb33SMartin Willi struct xfrm_algo_auth *algp; 694447bb33SMartin Willi 704447bb33SMartin Willi if (!rt) 714447bb33SMartin Willi return 0; 724447bb33SMartin Willi 734447bb33SMartin Willi algp = nla_data(rt); 744447bb33SMartin Willi if (nla_len(rt) < xfrm_alg_auth_len(algp)) 754447bb33SMartin Willi return -EINVAL; 764447bb33SMartin Willi 774447bb33SMartin Willi algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 784447bb33SMartin Willi return 0; 794447bb33SMartin Willi } 804447bb33SMartin Willi 811a6509d9SHerbert Xu static int verify_aead(struct nlattr **attrs) 821a6509d9SHerbert Xu { 831a6509d9SHerbert Xu struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; 841a6509d9SHerbert Xu struct xfrm_algo_aead *algp; 851a6509d9SHerbert Xu 861a6509d9SHerbert Xu if (!rt) 871a6509d9SHerbert Xu return 0; 881a6509d9SHerbert Xu 891a6509d9SHerbert Xu algp = nla_data(rt); 901a6509d9SHerbert Xu if (nla_len(rt) < aead_len(algp)) 911a6509d9SHerbert Xu return -EINVAL; 921a6509d9SHerbert Xu 931a6509d9SHerbert Xu algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 941a6509d9SHerbert Xu return 0; 951a6509d9SHerbert Xu } 961a6509d9SHerbert Xu 975424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, 98eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 99eb2971b6SMasahide NAKAMURA { 1005424f32eSThomas Graf struct nlattr *rt = attrs[type]; 101eb2971b6SMasahide NAKAMURA 102cf5cb79fSThomas Graf if (rt && addrp) 1035424f32eSThomas Graf *addrp = nla_data(rt); 104eb2971b6SMasahide NAKAMURA } 105df71837dSTrent Jaeger 1065424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs) 107df71837dSTrent Jaeger { 1085424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 109df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 110df71837dSTrent Jaeger 111df71837dSTrent Jaeger if (!rt) 112df71837dSTrent Jaeger return 0; 113df71837dSTrent Jaeger 1145424f32eSThomas Graf uctx = nla_data(rt); 115cf5cb79fSThomas Graf if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) 116df71837dSTrent Jaeger return -EINVAL; 117df71837dSTrent Jaeger 118df71837dSTrent Jaeger return 0; 119df71837dSTrent Jaeger } 120df71837dSTrent Jaeger 121df71837dSTrent Jaeger 1221da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1235424f32eSThomas Graf struct nlattr **attrs) 1241da177e4SLinus Torvalds { 1251da177e4SLinus Torvalds int err; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds err = -EINVAL; 1281da177e4SLinus Torvalds switch (p->family) { 1291da177e4SLinus Torvalds case AF_INET: 1301da177e4SLinus Torvalds break; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds case AF_INET6: 1331da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1341da177e4SLinus Torvalds break; 1351da177e4SLinus Torvalds #else 1361da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1371da177e4SLinus Torvalds goto out; 1381da177e4SLinus Torvalds #endif 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds default: 1411da177e4SLinus Torvalds goto out; 1423ff50b79SStephen Hemminger } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds err = -EINVAL; 1451da177e4SLinus Torvalds switch (p->id.proto) { 1461da177e4SLinus Torvalds case IPPROTO_AH: 1474447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1484447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1491a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 15035a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 15135a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]) 1521da177e4SLinus Torvalds goto out; 1531da177e4SLinus Torvalds break; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds case IPPROTO_ESP: 1561a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1571a6509d9SHerbert Xu goto out; 1581a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1594447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1601a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1611a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1621a6509d9SHerbert Xu goto out; 1631a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1644447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1651a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1661a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1671da177e4SLinus Torvalds goto out; 1681da177e4SLinus Torvalds break; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds case IPPROTO_COMP: 17135a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 1721a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 17335a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1744447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 17535a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]) 1761da177e4SLinus Torvalds goto out; 1771da177e4SLinus Torvalds break; 1781da177e4SLinus Torvalds 179e23c7194SMasahide NAKAMURA #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 180e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 181e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 18235a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 18335a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 1844447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1851a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 18635a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 18735a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 18835a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 18935a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 190e23c7194SMasahide NAKAMURA goto out; 191e23c7194SMasahide NAKAMURA break; 192e23c7194SMasahide NAKAMURA #endif 193e23c7194SMasahide NAKAMURA 1941da177e4SLinus Torvalds default: 1951da177e4SLinus Torvalds goto out; 1963ff50b79SStephen Hemminger } 1971da177e4SLinus Torvalds 1981a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 1991a6509d9SHerbert Xu goto out; 2004447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2014447bb33SMartin Willi goto out; 20235a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2031da177e4SLinus Torvalds goto out; 20435a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2051da177e4SLinus Torvalds goto out; 20635a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2071da177e4SLinus Torvalds goto out; 20835a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 209df71837dSTrent Jaeger goto out; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds err = -EINVAL; 2121da177e4SLinus Torvalds switch (p->mode) { 2137e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2147e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 215060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2160a69452cSDiego Beltrami case XFRM_MODE_BEET: 2171da177e4SLinus Torvalds break; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds default: 2201da177e4SLinus Torvalds goto out; 2213ff50b79SStephen Hemminger } 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds err = 0; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds out: 2261da177e4SLinus Torvalds return err; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2301da177e4SLinus Torvalds struct xfrm_algo_desc *(*get_byname)(char *, int), 2315424f32eSThomas Graf struct nlattr *rta) 2321da177e4SLinus Torvalds { 2331da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2341da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds if (!rta) 2371da177e4SLinus Torvalds return 0; 2381da177e4SLinus Torvalds 2395424f32eSThomas Graf ualg = nla_data(rta); 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2421da177e4SLinus Torvalds if (!algo) 2431da177e4SLinus Torvalds return -ENOSYS; 2441da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2451da177e4SLinus Torvalds 2460f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2471da177e4SLinus Torvalds if (!p) 2481da177e4SLinus Torvalds return -ENOMEM; 2491da177e4SLinus Torvalds 25004ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2511da177e4SLinus Torvalds *algpp = p; 2521da177e4SLinus Torvalds return 0; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 2554447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 2564447bb33SMartin Willi struct nlattr *rta) 2574447bb33SMartin Willi { 2584447bb33SMartin Willi struct xfrm_algo *ualg; 2594447bb33SMartin Willi struct xfrm_algo_auth *p; 2604447bb33SMartin Willi struct xfrm_algo_desc *algo; 2614447bb33SMartin Willi 2624447bb33SMartin Willi if (!rta) 2634447bb33SMartin Willi return 0; 2644447bb33SMartin Willi 2654447bb33SMartin Willi ualg = nla_data(rta); 2664447bb33SMartin Willi 2674447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 2684447bb33SMartin Willi if (!algo) 2694447bb33SMartin Willi return -ENOSYS; 2704447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 2714447bb33SMartin Willi 2724447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 2734447bb33SMartin Willi if (!p) 2744447bb33SMartin Willi return -ENOMEM; 2754447bb33SMartin Willi 2764447bb33SMartin Willi strcpy(p->alg_name, algo->name); 2774447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 2784447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 2794447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 2804447bb33SMartin Willi 2814447bb33SMartin Willi *algpp = p; 2824447bb33SMartin Willi return 0; 2834447bb33SMartin Willi } 2844447bb33SMartin Willi 2854447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 2864447bb33SMartin Willi struct nlattr *rta) 2874447bb33SMartin Willi { 2884447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 2894447bb33SMartin Willi struct xfrm_algo_desc *algo; 2904447bb33SMartin Willi 2914447bb33SMartin Willi if (!rta) 2924447bb33SMartin Willi return 0; 2934447bb33SMartin Willi 2944447bb33SMartin Willi ualg = nla_data(rta); 2954447bb33SMartin Willi 2964447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 2974447bb33SMartin Willi if (!algo) 2984447bb33SMartin Willi return -ENOSYS; 2994447bb33SMartin Willi if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3004447bb33SMartin Willi return -EINVAL; 3014447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3024447bb33SMartin Willi 3034447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3044447bb33SMartin Willi if (!p) 3054447bb33SMartin Willi return -ENOMEM; 3064447bb33SMartin Willi 3074447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3084447bb33SMartin Willi if (!p->alg_trunc_len) 3094447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3104447bb33SMartin Willi 3114447bb33SMartin Willi *algpp = p; 3124447bb33SMartin Willi return 0; 3134447bb33SMartin Willi } 3144447bb33SMartin Willi 3151a6509d9SHerbert Xu static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props, 3161a6509d9SHerbert Xu struct nlattr *rta) 3171a6509d9SHerbert Xu { 3181a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3191a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3201a6509d9SHerbert Xu 3211a6509d9SHerbert Xu if (!rta) 3221a6509d9SHerbert Xu return 0; 3231a6509d9SHerbert Xu 3241a6509d9SHerbert Xu ualg = nla_data(rta); 3251a6509d9SHerbert Xu 3261a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3271a6509d9SHerbert Xu if (!algo) 3281a6509d9SHerbert Xu return -ENOSYS; 3291a6509d9SHerbert Xu *props = algo->desc.sadb_alg_id; 3301a6509d9SHerbert Xu 3311a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3321a6509d9SHerbert Xu if (!p) 3331a6509d9SHerbert Xu return -ENOMEM; 3341a6509d9SHerbert Xu 3351a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 3361a6509d9SHerbert Xu *algpp = p; 3371a6509d9SHerbert Xu return 0; 3381a6509d9SHerbert Xu } 3391a6509d9SHerbert Xu 340661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 341df71837dSTrent Jaeger { 342df71837dSTrent Jaeger int len = 0; 343df71837dSTrent Jaeger 344df71837dSTrent Jaeger if (xfrm_ctx) { 345df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 346df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 347df71837dSTrent Jaeger } 348df71837dSTrent Jaeger return len; 349df71837dSTrent Jaeger } 350df71837dSTrent Jaeger 3511da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 3521da177e4SLinus Torvalds { 3531da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 3541da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 3551da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 3561da177e4SLinus Torvalds x->props.mode = p->mode; 3571da177e4SLinus Torvalds x->props.replay_window = p->replay_window; 3581da177e4SLinus Torvalds x->props.reqid = p->reqid; 3591da177e4SLinus Torvalds x->props.family = p->family; 36054489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 3611da177e4SLinus Torvalds x->props.flags = p->flags; 362196b0036SHerbert Xu 363ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 364196b0036SHerbert Xu x->sel.family = p->family; 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 367d51d081dSJamal Hadi Salim /* 368d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 369d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 370d51d081dSJamal Hadi Salim * 371d51d081dSJamal Hadi Salim */ 3725424f32eSThomas Graf static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) 373d51d081dSJamal Hadi Salim { 3745424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 3755424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 3765424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 3775424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 378d51d081dSJamal Hadi Salim 379d51d081dSJamal Hadi Salim if (rp) { 380d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 3815424f32eSThomas Graf replay = nla_data(rp); 382d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 383d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 384d51d081dSJamal Hadi Salim } 385d51d081dSJamal Hadi Salim 386d51d081dSJamal Hadi Salim if (lt) { 387d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 3885424f32eSThomas Graf ltime = nla_data(lt); 389d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 390d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 391d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 392d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 393d51d081dSJamal Hadi Salim } 394d51d081dSJamal Hadi Salim 395cf5cb79fSThomas Graf if (et) 3965424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 397d51d081dSJamal Hadi Salim 398cf5cb79fSThomas Graf if (rt) 3995424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 400d51d081dSJamal Hadi Salim } 401d51d081dSJamal Hadi Salim 402fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 403fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 4045424f32eSThomas Graf struct nlattr **attrs, 4051da177e4SLinus Torvalds int *errp) 4061da177e4SLinus Torvalds { 407fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 4081da177e4SLinus Torvalds int err = -ENOMEM; 4091da177e4SLinus Torvalds 4101da177e4SLinus Torvalds if (!x) 4111da177e4SLinus Torvalds goto error_no_put; 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds copy_from_user_state(x, p); 4141da177e4SLinus Torvalds 4151a6509d9SHerbert Xu if ((err = attach_aead(&x->aead, &x->props.ealgo, 4161a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]))) 4171a6509d9SHerbert Xu goto error; 4184447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 4194447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 4204447bb33SMartin Willi goto error; 4214447bb33SMartin Willi if (!x->props.aalgo) { 4224447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 42335a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 4241da177e4SLinus Torvalds goto error; 4254447bb33SMartin Willi } 4261da177e4SLinus Torvalds if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, 4271da177e4SLinus Torvalds xfrm_ealg_get_byname, 42835a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT]))) 4291da177e4SLinus Torvalds goto error; 4301da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 4311da177e4SLinus Torvalds xfrm_calg_get_byname, 43235a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 4331da177e4SLinus Torvalds goto error; 434fd21150aSThomas Graf 435fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 436fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 437fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 438fd21150aSThomas Graf if (x->encap == NULL) 4391da177e4SLinus Torvalds goto error; 440fd21150aSThomas Graf } 441fd21150aSThomas Graf 442fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 443fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 444fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 445fd21150aSThomas Graf if (x->coaddr == NULL) 446060f02a3SNoriaki TAKAMIYA goto error; 447fd21150aSThomas Graf } 448fd21150aSThomas Graf 44972cb6962SHerbert Xu err = xfrm_init_state(x); 4501da177e4SLinus Torvalds if (err) 4511da177e4SLinus Torvalds goto error; 4521da177e4SLinus Torvalds 453fd21150aSThomas Graf if (attrs[XFRMA_SEC_CTX] && 454fd21150aSThomas Graf security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) 455df71837dSTrent Jaeger goto error; 456df71837dSTrent Jaeger 4571da177e4SLinus Torvalds x->km.seq = p->seq; 458b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 459d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 460b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 461d51d081dSJamal Hadi Salim x->preplay.bitmap = 0; 462d51d081dSJamal Hadi Salim x->preplay.seq = x->replay.seq+x->replay_maxdiff; 463d51d081dSJamal Hadi Salim x->preplay.oseq = x->replay.oseq +x->replay_maxdiff; 464d51d081dSJamal Hadi Salim 465d51d081dSJamal Hadi Salim /* override default values from above */ 466d51d081dSJamal Hadi Salim 4675424f32eSThomas Graf xfrm_update_ae_params(x, attrs); 4681da177e4SLinus Torvalds 4691da177e4SLinus Torvalds return x; 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds error: 4721da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 4731da177e4SLinus Torvalds xfrm_state_put(x); 4741da177e4SLinus Torvalds error_no_put: 4751da177e4SLinus Torvalds *errp = err; 4761da177e4SLinus Torvalds return NULL; 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds 47922e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 4805424f32eSThomas Graf struct nlattr **attrs) 4811da177e4SLinus Torvalds { 482fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 4837b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 4841da177e4SLinus Torvalds struct xfrm_state *x; 4851da177e4SLinus Torvalds int err; 48626b15dadSJamal Hadi Salim struct km_event c; 4872532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 4882532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 4892532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 4901da177e4SLinus Torvalds 49135a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 4921da177e4SLinus Torvalds if (err) 4931da177e4SLinus Torvalds return err; 4941da177e4SLinus Torvalds 495fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 4961da177e4SLinus Torvalds if (!x) 4971da177e4SLinus Torvalds return err; 4981da177e4SLinus Torvalds 49926b15dadSJamal Hadi Salim xfrm_state_hold(x); 5001da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 5011da177e4SLinus Torvalds err = xfrm_state_add(x); 5021da177e4SLinus Torvalds else 5031da177e4SLinus Torvalds err = xfrm_state_update(x); 5041da177e4SLinus Torvalds 5052532386fSEric Paris xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); 506161a09e7SJoy Latten 5071da177e4SLinus Torvalds if (err < 0) { 5081da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 50921380b81SHerbert Xu __xfrm_state_put(x); 5107d6dfe1fSPatrick McHardy goto out; 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds 51326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 51426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 515f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 51626b15dadSJamal Hadi Salim 51726b15dadSJamal Hadi Salim km_state_notify(x, &c); 5187d6dfe1fSPatrick McHardy out: 51926b15dadSJamal Hadi Salim xfrm_state_put(x); 5201da177e4SLinus Torvalds return err; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 523fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 524fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 5255424f32eSThomas Graf struct nlattr **attrs, 526eb2971b6SMasahide NAKAMURA int *errp) 527eb2971b6SMasahide NAKAMURA { 528eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 529eb2971b6SMasahide NAKAMURA int err; 530eb2971b6SMasahide NAKAMURA 531eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 532eb2971b6SMasahide NAKAMURA err = -ESRCH; 533fc34acd3SAlexey Dobriyan x = xfrm_state_lookup(net, &p->daddr, p->spi, p->proto, p->family); 534eb2971b6SMasahide NAKAMURA } else { 535eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 536eb2971b6SMasahide NAKAMURA 53735a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 538eb2971b6SMasahide NAKAMURA if (!saddr) { 539eb2971b6SMasahide NAKAMURA err = -EINVAL; 540eb2971b6SMasahide NAKAMURA goto out; 541eb2971b6SMasahide NAKAMURA } 542eb2971b6SMasahide NAKAMURA 5439abbffeeSMasahide NAKAMURA err = -ESRCH; 544fc34acd3SAlexey Dobriyan x = xfrm_state_lookup_byaddr(net, &p->daddr, saddr, 545221df1edSAlexey Dobriyan p->proto, p->family); 546eb2971b6SMasahide NAKAMURA } 547eb2971b6SMasahide NAKAMURA 548eb2971b6SMasahide NAKAMURA out: 549eb2971b6SMasahide NAKAMURA if (!x && errp) 550eb2971b6SMasahide NAKAMURA *errp = err; 551eb2971b6SMasahide NAKAMURA return x; 552eb2971b6SMasahide NAKAMURA } 553eb2971b6SMasahide NAKAMURA 55422e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 5555424f32eSThomas Graf struct nlattr **attrs) 5561da177e4SLinus Torvalds { 557fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 5581da177e4SLinus Torvalds struct xfrm_state *x; 559eb2971b6SMasahide NAKAMURA int err = -ESRCH; 56026b15dadSJamal Hadi Salim struct km_event c; 5617b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 5622532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 5632532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 5642532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 5651da177e4SLinus Torvalds 566fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 5671da177e4SLinus Torvalds if (x == NULL) 568eb2971b6SMasahide NAKAMURA return err; 5691da177e4SLinus Torvalds 5706f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 571c8c05a8eSCatherine Zhang goto out; 572c8c05a8eSCatherine Zhang 5731da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 574c8c05a8eSCatherine Zhang err = -EPERM; 575c8c05a8eSCatherine Zhang goto out; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 57826b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 579161a09e7SJoy Latten 580c8c05a8eSCatherine Zhang if (err < 0) 581c8c05a8eSCatherine Zhang goto out; 58226b15dadSJamal Hadi Salim 58326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 58426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 585f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 58626b15dadSJamal Hadi Salim km_state_notify(x, &c); 5871da177e4SLinus Torvalds 588c8c05a8eSCatherine Zhang out: 5892532386fSEric Paris xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); 590c8c05a8eSCatherine Zhang xfrm_state_put(x); 59126b15dadSJamal Hadi Salim return err; 5921da177e4SLinus Torvalds } 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 5951da177e4SLinus Torvalds { 5961da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 5971da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 5981da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 5991da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 6001da177e4SLinus Torvalds memcpy(&p->stats, &x->stats, sizeof(p->stats)); 60154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 6021da177e4SLinus Torvalds p->mode = x->props.mode; 6031da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 6041da177e4SLinus Torvalds p->reqid = x->props.reqid; 6051da177e4SLinus Torvalds p->family = x->props.family; 6061da177e4SLinus Torvalds p->flags = x->props.flags; 6071da177e4SLinus Torvalds p->seq = x->km.seq; 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds struct xfrm_dump_info { 6111da177e4SLinus Torvalds struct sk_buff *in_skb; 6121da177e4SLinus Torvalds struct sk_buff *out_skb; 6131da177e4SLinus Torvalds u32 nlmsg_seq; 6141da177e4SLinus Torvalds u16 nlmsg_flags; 6151da177e4SLinus Torvalds }; 6161da177e4SLinus Torvalds 617c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 618c0144beaSThomas Graf { 619c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 620c0144beaSThomas Graf struct nlattr *attr; 62168325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 622c0144beaSThomas Graf 623c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 624c0144beaSThomas Graf if (attr == NULL) 625c0144beaSThomas Graf return -EMSGSIZE; 626c0144beaSThomas Graf 627c0144beaSThomas Graf uctx = nla_data(attr); 628c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 629c0144beaSThomas Graf uctx->len = ctx_size; 630c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 631c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 632c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 633c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 634c0144beaSThomas Graf 635c0144beaSThomas Graf return 0; 636c0144beaSThomas Graf } 637c0144beaSThomas Graf 6384447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 6394447bb33SMartin Willi { 6404447bb33SMartin Willi struct xfrm_algo *algo; 6414447bb33SMartin Willi struct nlattr *nla; 6424447bb33SMartin Willi 6434447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 6444447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 6454447bb33SMartin Willi if (!nla) 6464447bb33SMartin Willi return -EMSGSIZE; 6474447bb33SMartin Willi 6484447bb33SMartin Willi algo = nla_data(nla); 6494447bb33SMartin Willi strcpy(algo->alg_name, auth->alg_name); 6504447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 6514447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 6524447bb33SMartin Willi 6534447bb33SMartin Willi return 0; 6544447bb33SMartin Willi } 6554447bb33SMartin Willi 65668325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 65768325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 65868325d3bSHerbert Xu struct xfrm_usersa_info *p, 65968325d3bSHerbert Xu struct sk_buff *skb) 6601da177e4SLinus Torvalds { 6611da177e4SLinus Torvalds copy_to_user_state(x, p); 6621da177e4SLinus Torvalds 663050f009eSHerbert Xu if (x->coaddr) 664050f009eSHerbert Xu NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 665050f009eSHerbert Xu 666050f009eSHerbert Xu if (x->lastused) 667050f009eSHerbert Xu NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused); 668050f009eSHerbert Xu 6691a6509d9SHerbert Xu if (x->aead) 6701a6509d9SHerbert Xu NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 6714447bb33SMartin Willi if (x->aalg) { 6724447bb33SMartin Willi if (copy_to_user_auth(x->aalg, skb)) 6734447bb33SMartin Willi goto nla_put_failure; 6744447bb33SMartin Willi 6754447bb33SMartin Willi NLA_PUT(skb, XFRMA_ALG_AUTH_TRUNC, 6764447bb33SMartin Willi xfrm_alg_auth_len(x->aalg), x->aalg); 6774447bb33SMartin Willi } 6781da177e4SLinus Torvalds if (x->ealg) 6790f99be0dSEric Dumazet NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 6801da177e4SLinus Torvalds if (x->calg) 681c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds if (x->encap) 684c0144beaSThomas Graf NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 6851da177e4SLinus Torvalds 686c0144beaSThomas Graf if (x->security && copy_sec_ctx(x->security, skb) < 0) 687c0144beaSThomas Graf goto nla_put_failure; 688060f02a3SNoriaki TAKAMIYA 68968325d3bSHerbert Xu return 0; 69068325d3bSHerbert Xu 69168325d3bSHerbert Xu nla_put_failure: 69268325d3bSHerbert Xu return -EMSGSIZE; 69368325d3bSHerbert Xu } 69468325d3bSHerbert Xu 69568325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 69668325d3bSHerbert Xu { 69768325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 69868325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 69968325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 70068325d3bSHerbert Xu struct xfrm_usersa_info *p; 70168325d3bSHerbert Xu struct nlmsghdr *nlh; 70268325d3bSHerbert Xu int err; 70368325d3bSHerbert Xu 70468325d3bSHerbert Xu nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 70568325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 70668325d3bSHerbert Xu if (nlh == NULL) 70768325d3bSHerbert Xu return -EMSGSIZE; 70868325d3bSHerbert Xu 70968325d3bSHerbert Xu p = nlmsg_data(nlh); 71068325d3bSHerbert Xu 71168325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 71268325d3bSHerbert Xu if (err) 71368325d3bSHerbert Xu goto nla_put_failure; 71468325d3bSHerbert Xu 7159825069dSThomas Graf nlmsg_end(skb, nlh); 7161da177e4SLinus Torvalds return 0; 7171da177e4SLinus Torvalds 718c0144beaSThomas Graf nla_put_failure: 7199825069dSThomas Graf nlmsg_cancel(skb, nlh); 72068325d3bSHerbert Xu return err; 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 7234c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 7244c563f76STimo Teras { 7254c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 7264c563f76STimo Teras xfrm_state_walk_done(walk); 7274c563f76STimo Teras return 0; 7284c563f76STimo Teras } 7294c563f76STimo Teras 7301da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 7311da177e4SLinus Torvalds { 732fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 7334c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 7341da177e4SLinus Torvalds struct xfrm_dump_info info; 7351da177e4SLinus Torvalds 7364c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 7374c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 7384c563f76STimo Teras 7391da177e4SLinus Torvalds info.in_skb = cb->skb; 7401da177e4SLinus Torvalds info.out_skb = skb; 7411da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 7421da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 7434c563f76STimo Teras 7444c563f76STimo Teras if (!cb->args[0]) { 7454c563f76STimo Teras cb->args[0] = 1; 7464c563f76STimo Teras xfrm_state_walk_init(walk, 0); 7474c563f76STimo Teras } 7484c563f76STimo Teras 749fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 7501da177e4SLinus Torvalds 7511da177e4SLinus Torvalds return skb->len; 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds 7541da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 7551da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 7561da177e4SLinus Torvalds { 7571da177e4SLinus Torvalds struct xfrm_dump_info info; 7581da177e4SLinus Torvalds struct sk_buff *skb; 7591da177e4SLinus Torvalds 7607deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 7611da177e4SLinus Torvalds if (!skb) 7621da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds info.in_skb = in_skb; 7651da177e4SLinus Torvalds info.out_skb = skb; 7661da177e4SLinus Torvalds info.nlmsg_seq = seq; 7671da177e4SLinus Torvalds info.nlmsg_flags = 0; 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds if (dump_one_state(x, 0, &info)) { 7701da177e4SLinus Torvalds kfree_skb(skb); 7711da177e4SLinus Torvalds return NULL; 7721da177e4SLinus Torvalds } 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds return skb; 7751da177e4SLinus Torvalds } 7761da177e4SLinus Torvalds 7777deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 7787deb2264SThomas Graf { 7797deb2264SThomas Graf return NLMSG_ALIGN(4) 7807deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 7817deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 7827deb2264SThomas Graf } 7837deb2264SThomas Graf 784*e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 785*e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 786ecfd6b18SJamal Hadi Salim { 7875a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 7885a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 7895a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 790ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 791ecfd6b18SJamal Hadi Salim u32 *f; 792ecfd6b18SJamal Hadi Salim 793ecfd6b18SJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 794ecfd6b18SJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 795ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 796ecfd6b18SJamal Hadi Salim 797ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 798ecfd6b18SJamal Hadi Salim *f = flags; 799*e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 8005a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 8015a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 8025a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 8035a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 8045a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 8055a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 8065a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 8075a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 808ecfd6b18SJamal Hadi Salim 8095a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 8105a6d3416SJamal Hadi Salim NLA_PUT(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 811ecfd6b18SJamal Hadi Salim 812ecfd6b18SJamal Hadi Salim return nlmsg_end(skb, nlh); 813ecfd6b18SJamal Hadi Salim 814ecfd6b18SJamal Hadi Salim nla_put_failure: 815ecfd6b18SJamal Hadi Salim nlmsg_cancel(skb, nlh); 816ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 817ecfd6b18SJamal Hadi Salim } 818ecfd6b18SJamal Hadi Salim 819ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 8205424f32eSThomas Graf struct nlattr **attrs) 821ecfd6b18SJamal Hadi Salim { 822a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 823ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 8247b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 825ecfd6b18SJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 826ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 827ecfd6b18SJamal Hadi Salim 8287deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 829ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 830ecfd6b18SJamal Hadi Salim return -ENOMEM; 831ecfd6b18SJamal Hadi Salim 832*e071041bSAlexey Dobriyan if (build_spdinfo(r_skb, net, spid, seq, *flags) < 0) 833ecfd6b18SJamal Hadi Salim BUG(); 834ecfd6b18SJamal Hadi Salim 835a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 836ecfd6b18SJamal Hadi Salim } 837ecfd6b18SJamal Hadi Salim 8387deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 8397deb2264SThomas Graf { 8407deb2264SThomas Graf return NLMSG_ALIGN(4) 8417deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 8427deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 8437deb2264SThomas Graf } 8447deb2264SThomas Graf 845*e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 846*e071041bSAlexey Dobriyan u32 pid, u32 seq, u32 flags) 84728d8909bSJamal Hadi Salim { 848af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 849af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 85028d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 85128d8909bSJamal Hadi Salim u32 *f; 85228d8909bSJamal Hadi Salim 85328d8909bSJamal Hadi Salim nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 85428d8909bSJamal Hadi Salim if (nlh == NULL) /* shouldnt really happen ... */ 85528d8909bSJamal Hadi Salim return -EMSGSIZE; 85628d8909bSJamal Hadi Salim 85728d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 85828d8909bSJamal Hadi Salim *f = flags; 859*e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 86028d8909bSJamal Hadi Salim 861af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 862af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 863af11e316SJamal Hadi Salim 864af11e316SJamal Hadi Salim NLA_PUT_U32(skb, XFRMA_SAD_CNT, si.sadcnt); 865af11e316SJamal Hadi Salim NLA_PUT(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 86628d8909bSJamal Hadi Salim 86728d8909bSJamal Hadi Salim return nlmsg_end(skb, nlh); 86828d8909bSJamal Hadi Salim 86928d8909bSJamal Hadi Salim nla_put_failure: 87028d8909bSJamal Hadi Salim nlmsg_cancel(skb, nlh); 87128d8909bSJamal Hadi Salim return -EMSGSIZE; 87228d8909bSJamal Hadi Salim } 87328d8909bSJamal Hadi Salim 87428d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 8755424f32eSThomas Graf struct nlattr **attrs) 87628d8909bSJamal Hadi Salim { 877a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 87828d8909bSJamal Hadi Salim struct sk_buff *r_skb; 8797b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 88028d8909bSJamal Hadi Salim u32 spid = NETLINK_CB(skb).pid; 88128d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 88228d8909bSJamal Hadi Salim 8837deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 88428d8909bSJamal Hadi Salim if (r_skb == NULL) 88528d8909bSJamal Hadi Salim return -ENOMEM; 88628d8909bSJamal Hadi Salim 887*e071041bSAlexey Dobriyan if (build_sadinfo(r_skb, net, spid, seq, *flags) < 0) 88828d8909bSJamal Hadi Salim BUG(); 88928d8909bSJamal Hadi Salim 890a6483b79SAlexey Dobriyan return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid); 89128d8909bSJamal Hadi Salim } 89228d8909bSJamal Hadi Salim 89322e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 8945424f32eSThomas Graf struct nlattr **attrs) 8951da177e4SLinus Torvalds { 896fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 8977b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 8981da177e4SLinus Torvalds struct xfrm_state *x; 8991da177e4SLinus Torvalds struct sk_buff *resp_skb; 900eb2971b6SMasahide NAKAMURA int err = -ESRCH; 9011da177e4SLinus Torvalds 902fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 9031da177e4SLinus Torvalds if (x == NULL) 9041da177e4SLinus Torvalds goto out_noput; 9051da177e4SLinus Torvalds 9061da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 9071da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 9081da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 9091da177e4SLinus Torvalds } else { 910a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds xfrm_state_put(x); 9131da177e4SLinus Torvalds out_noput: 9141da177e4SLinus Torvalds return err; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds 9171da177e4SLinus Torvalds static int verify_userspi_info(struct xfrm_userspi_info *p) 9181da177e4SLinus Torvalds { 9191da177e4SLinus Torvalds switch (p->info.id.proto) { 9201da177e4SLinus Torvalds case IPPROTO_AH: 9211da177e4SLinus Torvalds case IPPROTO_ESP: 9221da177e4SLinus Torvalds break; 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds case IPPROTO_COMP: 9251da177e4SLinus Torvalds /* IPCOMP spi is 16-bits. */ 9261da177e4SLinus Torvalds if (p->max >= 0x10000) 9271da177e4SLinus Torvalds return -EINVAL; 9281da177e4SLinus Torvalds break; 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds default: 9311da177e4SLinus Torvalds return -EINVAL; 9323ff50b79SStephen Hemminger } 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds if (p->min > p->max) 9351da177e4SLinus Torvalds return -EINVAL; 9361da177e4SLinus Torvalds 9371da177e4SLinus Torvalds return 0; 9381da177e4SLinus Torvalds } 9391da177e4SLinus Torvalds 94022e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 9415424f32eSThomas Graf struct nlattr **attrs) 9421da177e4SLinus Torvalds { 943fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 9441da177e4SLinus Torvalds struct xfrm_state *x; 9451da177e4SLinus Torvalds struct xfrm_userspi_info *p; 9461da177e4SLinus Torvalds struct sk_buff *resp_skb; 9471da177e4SLinus Torvalds xfrm_address_t *daddr; 9481da177e4SLinus Torvalds int family; 9491da177e4SLinus Torvalds int err; 9501da177e4SLinus Torvalds 9517b67c857SThomas Graf p = nlmsg_data(nlh); 9521da177e4SLinus Torvalds err = verify_userspi_info(p); 9531da177e4SLinus Torvalds if (err) 9541da177e4SLinus Torvalds goto out_noput; 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds family = p->info.family; 9571da177e4SLinus Torvalds daddr = &p->info.id.daddr; 9581da177e4SLinus Torvalds 9591da177e4SLinus Torvalds x = NULL; 9601da177e4SLinus Torvalds if (p->info.seq) { 961a6483b79SAlexey Dobriyan x = xfrm_find_acq_byseq(net, p->info.seq); 9621da177e4SLinus Torvalds if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { 9631da177e4SLinus Torvalds xfrm_state_put(x); 9641da177e4SLinus Torvalds x = NULL; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds 9681da177e4SLinus Torvalds if (!x) 969a6483b79SAlexey Dobriyan x = xfrm_find_acq(net, p->info.mode, p->info.reqid, 9701da177e4SLinus Torvalds p->info.id.proto, daddr, 9711da177e4SLinus Torvalds &p->info.saddr, 1, 9721da177e4SLinus Torvalds family); 9731da177e4SLinus Torvalds err = -ENOENT; 9741da177e4SLinus Torvalds if (x == NULL) 9751da177e4SLinus Torvalds goto out_noput; 9761da177e4SLinus Torvalds 977658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 978658b219eSHerbert Xu if (err) 979658b219eSHerbert Xu goto out; 9801da177e4SLinus Torvalds 9811da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 9821da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 9831da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 9841da177e4SLinus Torvalds goto out; 9851da177e4SLinus Torvalds } 9861da177e4SLinus Torvalds 987a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid); 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds out: 9901da177e4SLinus Torvalds xfrm_state_put(x); 9911da177e4SLinus Torvalds out_noput: 9921da177e4SLinus Torvalds return err; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 995b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 9961da177e4SLinus Torvalds { 9971da177e4SLinus Torvalds switch (dir) { 9981da177e4SLinus Torvalds case XFRM_POLICY_IN: 9991da177e4SLinus Torvalds case XFRM_POLICY_OUT: 10001da177e4SLinus Torvalds case XFRM_POLICY_FWD: 10011da177e4SLinus Torvalds break; 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds default: 10041da177e4SLinus Torvalds return -EINVAL; 10053ff50b79SStephen Hemminger } 10061da177e4SLinus Torvalds 10071da177e4SLinus Torvalds return 0; 10081da177e4SLinus Torvalds } 10091da177e4SLinus Torvalds 1010b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1011f7b6983fSMasahide NAKAMURA { 1012f7b6983fSMasahide NAKAMURA switch (type) { 1013f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1014f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1015f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1016f7b6983fSMasahide NAKAMURA #endif 1017f7b6983fSMasahide NAKAMURA break; 1018f7b6983fSMasahide NAKAMURA 1019f7b6983fSMasahide NAKAMURA default: 1020f7b6983fSMasahide NAKAMURA return -EINVAL; 10213ff50b79SStephen Hemminger } 1022f7b6983fSMasahide NAKAMURA 1023f7b6983fSMasahide NAKAMURA return 0; 1024f7b6983fSMasahide NAKAMURA } 1025f7b6983fSMasahide NAKAMURA 10261da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 10271da177e4SLinus Torvalds { 10281da177e4SLinus Torvalds switch (p->share) { 10291da177e4SLinus Torvalds case XFRM_SHARE_ANY: 10301da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 10311da177e4SLinus Torvalds case XFRM_SHARE_USER: 10321da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 10331da177e4SLinus Torvalds break; 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds default: 10361da177e4SLinus Torvalds return -EINVAL; 10373ff50b79SStephen Hemminger } 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds switch (p->action) { 10401da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 10411da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 10421da177e4SLinus Torvalds break; 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds default: 10451da177e4SLinus Torvalds return -EINVAL; 10463ff50b79SStephen Hemminger } 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds switch (p->sel.family) { 10491da177e4SLinus Torvalds case AF_INET: 10501da177e4SLinus Torvalds break; 10511da177e4SLinus Torvalds 10521da177e4SLinus Torvalds case AF_INET6: 10531da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 10541da177e4SLinus Torvalds break; 10551da177e4SLinus Torvalds #else 10561da177e4SLinus Torvalds return -EAFNOSUPPORT; 10571da177e4SLinus Torvalds #endif 10581da177e4SLinus Torvalds 10591da177e4SLinus Torvalds default: 10601da177e4SLinus Torvalds return -EINVAL; 10613ff50b79SStephen Hemminger } 10621da177e4SLinus Torvalds 10631da177e4SLinus Torvalds return verify_policy_dir(p->dir); 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds 10665424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1067df71837dSTrent Jaeger { 10685424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1069df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1070df71837dSTrent Jaeger 1071df71837dSTrent Jaeger if (!rt) 1072df71837dSTrent Jaeger return 0; 1073df71837dSTrent Jaeger 10745424f32eSThomas Graf uctx = nla_data(rt); 107503e1ad7bSPaul Moore return security_xfrm_policy_alloc(&pol->security, uctx); 1076df71837dSTrent Jaeger } 1077df71837dSTrent Jaeger 10781da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 10791da177e4SLinus Torvalds int nr) 10801da177e4SLinus Torvalds { 10811da177e4SLinus Torvalds int i; 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds xp->xfrm_nr = nr; 10841da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 10851da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 10881da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 10891da177e4SLinus Torvalds sizeof(xfrm_address_t)); 10901da177e4SLinus Torvalds t->reqid = ut->reqid; 10911da177e4SLinus Torvalds t->mode = ut->mode; 10921da177e4SLinus Torvalds t->share = ut->share; 10931da177e4SLinus Torvalds t->optional = ut->optional; 10941da177e4SLinus Torvalds t->aalgos = ut->aalgos; 10951da177e4SLinus Torvalds t->ealgos = ut->ealgos; 10961da177e4SLinus Torvalds t->calgos = ut->calgos; 1097c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1098c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 10998511d01dSMiika Komu t->encap_family = ut->family; 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds 1103b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1104b4ad86bfSDavid S. Miller { 1105b4ad86bfSDavid S. Miller int i; 1106b4ad86bfSDavid S. Miller 1107b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1108b4ad86bfSDavid S. Miller return -EINVAL; 1109b4ad86bfSDavid S. Miller 1110b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1111b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1112b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1113b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1114b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1115b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1116b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1117b4ad86bfSDavid S. Miller */ 1118b4ad86bfSDavid S. Miller if (!ut[i].family) 1119b4ad86bfSDavid S. Miller ut[i].family = family; 1120b4ad86bfSDavid S. Miller 1121b4ad86bfSDavid S. Miller switch (ut[i].family) { 1122b4ad86bfSDavid S. Miller case AF_INET: 1123b4ad86bfSDavid S. Miller break; 1124b4ad86bfSDavid S. Miller #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 1125b4ad86bfSDavid S. Miller case AF_INET6: 1126b4ad86bfSDavid S. Miller break; 1127b4ad86bfSDavid S. Miller #endif 1128b4ad86bfSDavid S. Miller default: 1129b4ad86bfSDavid S. Miller return -EINVAL; 11303ff50b79SStephen Hemminger } 1131b4ad86bfSDavid S. Miller } 1132b4ad86bfSDavid S. Miller 1133b4ad86bfSDavid S. Miller return 0; 1134b4ad86bfSDavid S. Miller } 1135b4ad86bfSDavid S. Miller 11365424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 11371da177e4SLinus Torvalds { 11385424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 11391da177e4SLinus Torvalds 11401da177e4SLinus Torvalds if (!rt) { 11411da177e4SLinus Torvalds pol->xfrm_nr = 0; 11421da177e4SLinus Torvalds } else { 11435424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 11445424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1145b4ad86bfSDavid S. Miller int err; 11461da177e4SLinus Torvalds 1147b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1148b4ad86bfSDavid S. Miller if (err) 1149b4ad86bfSDavid S. Miller return err; 11501da177e4SLinus Torvalds 11515424f32eSThomas Graf copy_templates(pol, utmpl, nr); 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds return 0; 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds 11565424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1157f7b6983fSMasahide NAKAMURA { 11585424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1159f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1160b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1161f7b6983fSMasahide NAKAMURA int err; 1162f7b6983fSMasahide NAKAMURA 1163f7b6983fSMasahide NAKAMURA if (rt) { 11645424f32eSThomas Graf upt = nla_data(rt); 1165f7b6983fSMasahide NAKAMURA type = upt->type; 1166f7b6983fSMasahide NAKAMURA } 1167f7b6983fSMasahide NAKAMURA 1168f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1169f7b6983fSMasahide NAKAMURA if (err) 1170f7b6983fSMasahide NAKAMURA return err; 1171f7b6983fSMasahide NAKAMURA 1172f7b6983fSMasahide NAKAMURA *tp = type; 1173f7b6983fSMasahide NAKAMURA return 0; 1174f7b6983fSMasahide NAKAMURA } 1175f7b6983fSMasahide NAKAMURA 11761da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 11771da177e4SLinus Torvalds { 11781da177e4SLinus Torvalds xp->priority = p->priority; 11791da177e4SLinus Torvalds xp->index = p->index; 11801da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 11811da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 11821da177e4SLinus Torvalds xp->action = p->action; 11831da177e4SLinus Torvalds xp->flags = p->flags; 11841da177e4SLinus Torvalds xp->family = p->sel.family; 11851da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 11861da177e4SLinus Torvalds } 11871da177e4SLinus Torvalds 11881da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 11891da177e4SLinus Torvalds { 11901da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 11911da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 11921da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 11931da177e4SLinus Torvalds p->priority = xp->priority; 11941da177e4SLinus Torvalds p->index = xp->index; 11951da177e4SLinus Torvalds p->sel.family = xp->family; 11961da177e4SLinus Torvalds p->dir = dir; 11971da177e4SLinus Torvalds p->action = xp->action; 11981da177e4SLinus Torvalds p->flags = xp->flags; 11991da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 12001da177e4SLinus Torvalds } 12011da177e4SLinus Torvalds 1202fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 12031da177e4SLinus Torvalds { 1204fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 12051da177e4SLinus Torvalds int err; 12061da177e4SLinus Torvalds 12071da177e4SLinus Torvalds if (!xp) { 12081da177e4SLinus Torvalds *errp = -ENOMEM; 12091da177e4SLinus Torvalds return NULL; 12101da177e4SLinus Torvalds } 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1213df71837dSTrent Jaeger 121435a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1215f7b6983fSMasahide NAKAMURA if (err) 1216f7b6983fSMasahide NAKAMURA goto error; 1217f7b6983fSMasahide NAKAMURA 121835a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 121935a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1220f7b6983fSMasahide NAKAMURA if (err) 1221f7b6983fSMasahide NAKAMURA goto error; 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds return xp; 1224f7b6983fSMasahide NAKAMURA error: 1225f7b6983fSMasahide NAKAMURA *errp = err; 122612a169e7SHerbert Xu xp->walk.dead = 1; 122764c31b3fSWANG Cong xfrm_policy_destroy(xp); 1228f7b6983fSMasahide NAKAMURA return NULL; 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds 123122e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 12325424f32eSThomas Graf struct nlattr **attrs) 12331da177e4SLinus Torvalds { 1234fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 12357b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 12361da177e4SLinus Torvalds struct xfrm_policy *xp; 123726b15dadSJamal Hadi Salim struct km_event c; 12381da177e4SLinus Torvalds int err; 12391da177e4SLinus Torvalds int excl; 12402532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 12412532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 12422532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 12431da177e4SLinus Torvalds 12441da177e4SLinus Torvalds err = verify_newpolicy_info(p); 12451da177e4SLinus Torvalds if (err) 12461da177e4SLinus Torvalds return err; 124735a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1248df71837dSTrent Jaeger if (err) 1249df71837dSTrent Jaeger return err; 12501da177e4SLinus Torvalds 1251fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 12521da177e4SLinus Torvalds if (!xp) 12531da177e4SLinus Torvalds return err; 12541da177e4SLinus Torvalds 125526b15dadSJamal Hadi Salim /* shouldnt excl be based on nlh flags?? 125626b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 125726b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 125826b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 12591da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 12601da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 12612532386fSEric Paris xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); 1262161a09e7SJoy Latten 12631da177e4SLinus Torvalds if (err) { 126403e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 12651da177e4SLinus Torvalds kfree(xp); 12661da177e4SLinus Torvalds return err; 12671da177e4SLinus Torvalds } 12681da177e4SLinus Torvalds 1269f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 127026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 127126b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 127226b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 127326b15dadSJamal Hadi Salim 12741da177e4SLinus Torvalds xfrm_pol_put(xp); 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds return 0; 12771da177e4SLinus Torvalds } 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 12801da177e4SLinus Torvalds { 12811da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 12821da177e4SLinus Torvalds int i; 12831da177e4SLinus Torvalds 12841da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 12851da177e4SLinus Torvalds return 0; 12861da177e4SLinus Torvalds 12871da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 12881da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 12891da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 12928511d01dSMiika Komu up->family = kp->encap_family; 12931da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 12941da177e4SLinus Torvalds up->reqid = kp->reqid; 12951da177e4SLinus Torvalds up->mode = kp->mode; 12961da177e4SLinus Torvalds up->share = kp->share; 12971da177e4SLinus Torvalds up->optional = kp->optional; 12981da177e4SLinus Torvalds up->aalgos = kp->aalgos; 12991da177e4SLinus Torvalds up->ealgos = kp->ealgos; 13001da177e4SLinus Torvalds up->calgos = kp->calgos; 13011da177e4SLinus Torvalds } 13021da177e4SLinus Torvalds 1303c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1304c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1305df71837dSTrent Jaeger } 1306df71837dSTrent Jaeger 13070d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 13080d681623SSerge Hallyn { 13090d681623SSerge Hallyn if (x->security) { 13100d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 13110d681623SSerge Hallyn } 13120d681623SSerge Hallyn return 0; 13130d681623SSerge Hallyn } 13140d681623SSerge Hallyn 13150d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 13160d681623SSerge Hallyn { 13170d681623SSerge Hallyn if (xp->security) { 13180d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 13190d681623SSerge Hallyn } 13200d681623SSerge Hallyn return 0; 13210d681623SSerge Hallyn } 1322cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1323cfbfd45aSThomas Graf { 1324cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1325cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1326cfbfd45aSThomas Graf #else 1327cfbfd45aSThomas Graf return 0; 1328cfbfd45aSThomas Graf #endif 1329cfbfd45aSThomas Graf } 13300d681623SSerge Hallyn 1331f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1332b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1333f7b6983fSMasahide NAKAMURA { 1334c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1335c0144beaSThomas Graf .type = type, 1336c0144beaSThomas Graf }; 1337f7b6983fSMasahide NAKAMURA 1338c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1339f7b6983fSMasahide NAKAMURA } 1340f7b6983fSMasahide NAKAMURA 1341f7b6983fSMasahide NAKAMURA #else 1342b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1343f7b6983fSMasahide NAKAMURA { 1344f7b6983fSMasahide NAKAMURA return 0; 1345f7b6983fSMasahide NAKAMURA } 1346f7b6983fSMasahide NAKAMURA #endif 1347f7b6983fSMasahide NAKAMURA 13481da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 13491da177e4SLinus Torvalds { 13501da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 13511da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 13521da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 13531da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 13541da177e4SLinus Torvalds struct nlmsghdr *nlh; 13551da177e4SLinus Torvalds 135679b8b7f4SThomas Graf nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, 135779b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 135879b8b7f4SThomas Graf if (nlh == NULL) 135979b8b7f4SThomas Graf return -EMSGSIZE; 13601da177e4SLinus Torvalds 13617b67c857SThomas Graf p = nlmsg_data(nlh); 13621da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 13631da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 13641da177e4SLinus Torvalds goto nlmsg_failure; 1365df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 1366df71837dSTrent Jaeger goto nlmsg_failure; 13671459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 1368f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 13691da177e4SLinus Torvalds 13709825069dSThomas Graf nlmsg_end(skb, nlh); 13711da177e4SLinus Torvalds return 0; 13721da177e4SLinus Torvalds 13731da177e4SLinus Torvalds nlmsg_failure: 13749825069dSThomas Graf nlmsg_cancel(skb, nlh); 13759825069dSThomas Graf return -EMSGSIZE; 13761da177e4SLinus Torvalds } 13771da177e4SLinus Torvalds 13784c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 13794c563f76STimo Teras { 13804c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 13814c563f76STimo Teras 13824c563f76STimo Teras xfrm_policy_walk_done(walk); 13834c563f76STimo Teras return 0; 13844c563f76STimo Teras } 13854c563f76STimo Teras 13861da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 13871da177e4SLinus Torvalds { 1388fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 13894c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 13901da177e4SLinus Torvalds struct xfrm_dump_info info; 13911da177e4SLinus Torvalds 13924c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 13934c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 13944c563f76STimo Teras 13951da177e4SLinus Torvalds info.in_skb = cb->skb; 13961da177e4SLinus Torvalds info.out_skb = skb; 13971da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 13981da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 13994c563f76STimo Teras 14004c563f76STimo Teras if (!cb->args[0]) { 14014c563f76STimo Teras cb->args[0] = 1; 14024c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 14034c563f76STimo Teras } 14044c563f76STimo Teras 1405fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 14061da177e4SLinus Torvalds 14071da177e4SLinus Torvalds return skb->len; 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds 14101da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 14111da177e4SLinus Torvalds struct xfrm_policy *xp, 14121da177e4SLinus Torvalds int dir, u32 seq) 14131da177e4SLinus Torvalds { 14141da177e4SLinus Torvalds struct xfrm_dump_info info; 14151da177e4SLinus Torvalds struct sk_buff *skb; 14161da177e4SLinus Torvalds 14177deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 14181da177e4SLinus Torvalds if (!skb) 14191da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 14201da177e4SLinus Torvalds 14211da177e4SLinus Torvalds info.in_skb = in_skb; 14221da177e4SLinus Torvalds info.out_skb = skb; 14231da177e4SLinus Torvalds info.nlmsg_seq = seq; 14241da177e4SLinus Torvalds info.nlmsg_flags = 0; 14251da177e4SLinus Torvalds 14261da177e4SLinus Torvalds if (dump_one_policy(xp, dir, 0, &info) < 0) { 14271da177e4SLinus Torvalds kfree_skb(skb); 14281da177e4SLinus Torvalds return NULL; 14291da177e4SLinus Torvalds } 14301da177e4SLinus Torvalds 14311da177e4SLinus Torvalds return skb; 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds 143422e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 14355424f32eSThomas Graf struct nlattr **attrs) 14361da177e4SLinus Torvalds { 1437fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 14381da177e4SLinus Torvalds struct xfrm_policy *xp; 14391da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1440b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 14411da177e4SLinus Torvalds int err; 144226b15dadSJamal Hadi Salim struct km_event c; 14431da177e4SLinus Torvalds int delete; 14441da177e4SLinus Torvalds 14457b67c857SThomas Graf p = nlmsg_data(nlh); 14461da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 14471da177e4SLinus Torvalds 144835a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1449f7b6983fSMasahide NAKAMURA if (err) 1450f7b6983fSMasahide NAKAMURA return err; 1451f7b6983fSMasahide NAKAMURA 14521da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 14531da177e4SLinus Torvalds if (err) 14541da177e4SLinus Torvalds return err; 14551da177e4SLinus Torvalds 14561da177e4SLinus Torvalds if (p->index) 1457a6483b79SAlexey Dobriyan xp = xfrm_policy_byid(net, type, p->dir, p->index, delete, &err); 1458df71837dSTrent Jaeger else { 14595424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 146003e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1461df71837dSTrent Jaeger 146235a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1463df71837dSTrent Jaeger if (err) 1464df71837dSTrent Jaeger return err; 1465df71837dSTrent Jaeger 14662c8dd116SDenis V. Lunev ctx = NULL; 1467df71837dSTrent Jaeger if (rt) { 14685424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1469df71837dSTrent Jaeger 147003e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 147103e1ad7bSPaul Moore if (err) 1472df71837dSTrent Jaeger return err; 14732c8dd116SDenis V. Lunev } 1474a6483b79SAlexey Dobriyan xp = xfrm_policy_bysel_ctx(net, type, p->dir, &p->sel, ctx, 1475ef41aaa0SEric Paris delete, &err); 147603e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1477df71837dSTrent Jaeger } 14781da177e4SLinus Torvalds if (xp == NULL) 14791da177e4SLinus Torvalds return -ENOENT; 14801da177e4SLinus Torvalds 14811da177e4SLinus Torvalds if (!delete) { 14821da177e4SLinus Torvalds struct sk_buff *resp_skb; 14831da177e4SLinus Torvalds 14841da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 14851da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 14861da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 14871da177e4SLinus Torvalds } else { 1488a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 1489082a1ad5SThomas Graf NETLINK_CB(skb).pid); 14901da177e4SLinus Torvalds } 149126b15dadSJamal Hadi Salim } else { 14922532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 14932532386fSEric Paris u32 sessionid = NETLINK_CB(skb).sessionid; 14942532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 14952532386fSEric Paris 14962532386fSEric Paris xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, 14972532386fSEric Paris sid); 149813fcfbb0SDavid S. Miller 149913fcfbb0SDavid S. Miller if (err != 0) 1500c8c05a8eSCatherine Zhang goto out; 150113fcfbb0SDavid S. Miller 1502e7443892SHerbert Xu c.data.byid = p->index; 1503f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 150426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 150526b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 150626b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 15071da177e4SLinus Torvalds } 15081da177e4SLinus Torvalds 1509c8c05a8eSCatherine Zhang out: 1510ef41aaa0SEric Paris xfrm_pol_put(xp); 15111da177e4SLinus Torvalds return err; 15121da177e4SLinus Torvalds } 15131da177e4SLinus Torvalds 151422e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 15155424f32eSThomas Graf struct nlattr **attrs) 15161da177e4SLinus Torvalds { 1517fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 151826b15dadSJamal Hadi Salim struct km_event c; 15197b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 1520161a09e7SJoy Latten struct xfrm_audit audit_info; 15214aa2e62cSJoy Latten int err; 15221da177e4SLinus Torvalds 1523161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 15242532386fSEric Paris audit_info.sessionid = NETLINK_CB(skb).sessionid; 1525161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 1526fc34acd3SAlexey Dobriyan err = xfrm_state_flush(net, p->proto, &audit_info); 15274aa2e62cSJoy Latten if (err) 15284aa2e62cSJoy Latten return err; 1529bf08867fSHerbert Xu c.data.proto = p->proto; 1530f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 153126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 153226b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 15337067802eSAlexey Dobriyan c.net = net; 153426b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 153526b15dadSJamal Hadi Salim 15361da177e4SLinus Torvalds return 0; 15371da177e4SLinus Torvalds } 15381da177e4SLinus Torvalds 15397deb2264SThomas Graf static inline size_t xfrm_aevent_msgsize(void) 15407deb2264SThomas Graf { 15417deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 15427deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_replay_state)) 15437deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_lifetime_cur)) 15447deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 15457deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 15467deb2264SThomas Graf } 1547d51d081dSJamal Hadi Salim 1548d51d081dSJamal Hadi Salim static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 1549d51d081dSJamal Hadi Salim { 1550d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1551d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 1552d51d081dSJamal Hadi Salim 155379b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 155479b8b7f4SThomas Graf if (nlh == NULL) 155579b8b7f4SThomas Graf return -EMSGSIZE; 1556d51d081dSJamal Hadi Salim 15577b67c857SThomas Graf id = nlmsg_data(nlh); 15582b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr,sizeof(x->id.daddr)); 1559d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1560d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1561d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 15622b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr,sizeof(x->props.saddr)); 15632b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1564d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1565d51d081dSJamal Hadi Salim 1566c0144beaSThomas Graf NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); 1567c0144beaSThomas Graf NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); 1568d51d081dSJamal Hadi Salim 1569c0144beaSThomas Graf if (id->flags & XFRM_AE_RTHR) 1570c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 1571d51d081dSJamal Hadi Salim 1572c0144beaSThomas Graf if (id->flags & XFRM_AE_ETHR) 1573c0144beaSThomas Graf NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH, 1574c0144beaSThomas Graf x->replay_maxage * 10 / HZ); 1575d51d081dSJamal Hadi Salim 15769825069dSThomas Graf return nlmsg_end(skb, nlh); 1577d51d081dSJamal Hadi Salim 1578c0144beaSThomas Graf nla_put_failure: 15799825069dSThomas Graf nlmsg_cancel(skb, nlh); 15809825069dSThomas Graf return -EMSGSIZE; 1581d51d081dSJamal Hadi Salim } 1582d51d081dSJamal Hadi Salim 158322e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 15845424f32eSThomas Graf struct nlattr **attrs) 1585d51d081dSJamal Hadi Salim { 1586fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1587d51d081dSJamal Hadi Salim struct xfrm_state *x; 1588d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1589d51d081dSJamal Hadi Salim int err; 1590d51d081dSJamal Hadi Salim struct km_event c; 15917b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1592d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1593d51d081dSJamal Hadi Salim 15947deb2264SThomas Graf r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 1595d51d081dSJamal Hadi Salim if (r_skb == NULL) 1596d51d081dSJamal Hadi Salim return -ENOMEM; 1597d51d081dSJamal Hadi Salim 1598a6483b79SAlexey Dobriyan x = xfrm_state_lookup(net, &id->daddr, id->spi, id->proto, id->family); 1599d51d081dSJamal Hadi Salim if (x == NULL) { 1600b08d5840SPatrick McHardy kfree_skb(r_skb); 1601d51d081dSJamal Hadi Salim return -ESRCH; 1602d51d081dSJamal Hadi Salim } 1603d51d081dSJamal Hadi Salim 1604d51d081dSJamal Hadi Salim /* 1605d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1606d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1607d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1608d51d081dSJamal Hadi Salim */ 1609d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1610d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1611d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1612d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1613d51d081dSJamal Hadi Salim 1614d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1615d51d081dSJamal Hadi Salim BUG(); 1616a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid); 1617d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1618d51d081dSJamal Hadi Salim xfrm_state_put(x); 1619d51d081dSJamal Hadi Salim return err; 1620d51d081dSJamal Hadi Salim } 1621d51d081dSJamal Hadi Salim 162222e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 16235424f32eSThomas Graf struct nlattr **attrs) 1624d51d081dSJamal Hadi Salim { 1625fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1626d51d081dSJamal Hadi Salim struct xfrm_state *x; 1627d51d081dSJamal Hadi Salim struct km_event c; 1628d51d081dSJamal Hadi Salim int err = - EINVAL; 16297b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 16305424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 16315424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 1632d51d081dSJamal Hadi Salim 1633d51d081dSJamal Hadi Salim if (!lt && !rp) 1634d51d081dSJamal Hadi Salim return err; 1635d51d081dSJamal Hadi Salim 1636d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1637d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1638d51d081dSJamal Hadi Salim return err; 1639d51d081dSJamal Hadi Salim 1640fc34acd3SAlexey Dobriyan x = xfrm_state_lookup(net, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1641d51d081dSJamal Hadi Salim if (x == NULL) 1642d51d081dSJamal Hadi Salim return -ESRCH; 1643d51d081dSJamal Hadi Salim 1644d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1645d51d081dSJamal Hadi Salim goto out; 1646d51d081dSJamal Hadi Salim 1647d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 164835a7aa08SThomas Graf xfrm_update_ae_params(x, attrs); 1649d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1650d51d081dSJamal Hadi Salim 1651d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1652d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 1653d51d081dSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 1654d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1655d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1656d51d081dSJamal Hadi Salim err = 0; 1657d51d081dSJamal Hadi Salim out: 1658d51d081dSJamal Hadi Salim xfrm_state_put(x); 1659d51d081dSJamal Hadi Salim return err; 1660d51d081dSJamal Hadi Salim } 1661d51d081dSJamal Hadi Salim 166222e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 16635424f32eSThomas Graf struct nlattr **attrs) 16641da177e4SLinus Torvalds { 1665fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 166626b15dadSJamal Hadi Salim struct km_event c; 1667b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1668f7b6983fSMasahide NAKAMURA int err; 1669161a09e7SJoy Latten struct xfrm_audit audit_info; 167026b15dadSJamal Hadi Salim 167135a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1672f7b6983fSMasahide NAKAMURA if (err) 1673f7b6983fSMasahide NAKAMURA return err; 1674f7b6983fSMasahide NAKAMURA 1675161a09e7SJoy Latten audit_info.loginuid = NETLINK_CB(skb).loginuid; 16762532386fSEric Paris audit_info.sessionid = NETLINK_CB(skb).sessionid; 1677161a09e7SJoy Latten audit_info.secid = NETLINK_CB(skb).sid; 1678fc34acd3SAlexey Dobriyan err = xfrm_policy_flush(net, type, &audit_info); 16794aa2e62cSJoy Latten if (err) 16804aa2e62cSJoy Latten return err; 1681f7b6983fSMasahide NAKAMURA c.data.type = type; 1682f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 168326b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 168426b15dadSJamal Hadi Salim c.pid = nlh->nlmsg_pid; 16857067802eSAlexey Dobriyan c.net = net; 168626b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 16871da177e4SLinus Torvalds return 0; 16881da177e4SLinus Torvalds } 16891da177e4SLinus Torvalds 169022e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 16915424f32eSThomas Graf struct nlattr **attrs) 16926c5c8ca7SJamal Hadi Salim { 1693fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 16946c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 16957b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 16966c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 1697b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 16986c5c8ca7SJamal Hadi Salim int err = -ENOENT; 16996c5c8ca7SJamal Hadi Salim 170035a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1701f7b6983fSMasahide NAKAMURA if (err) 1702f7b6983fSMasahide NAKAMURA return err; 1703f7b6983fSMasahide NAKAMURA 17046c5c8ca7SJamal Hadi Salim if (p->index) 1705fc34acd3SAlexey Dobriyan xp = xfrm_policy_byid(net, type, p->dir, p->index, 0, &err); 17066c5c8ca7SJamal Hadi Salim else { 17075424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 170803e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 17096c5c8ca7SJamal Hadi Salim 171035a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 17116c5c8ca7SJamal Hadi Salim if (err) 17126c5c8ca7SJamal Hadi Salim return err; 17136c5c8ca7SJamal Hadi Salim 17142c8dd116SDenis V. Lunev ctx = NULL; 17156c5c8ca7SJamal Hadi Salim if (rt) { 17165424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 17176c5c8ca7SJamal Hadi Salim 171803e1ad7bSPaul Moore err = security_xfrm_policy_alloc(&ctx, uctx); 171903e1ad7bSPaul Moore if (err) 17206c5c8ca7SJamal Hadi Salim return err; 17212c8dd116SDenis V. Lunev } 1722fc34acd3SAlexey Dobriyan xp = xfrm_policy_bysel_ctx(net, type, p->dir, &p->sel, ctx, 0, &err); 172303e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 17246c5c8ca7SJamal Hadi Salim } 17256c5c8ca7SJamal Hadi Salim if (xp == NULL) 1726ef41aaa0SEric Paris return -ENOENT; 172703e1ad7bSPaul Moore 17286c5c8ca7SJamal Hadi Salim read_lock(&xp->lock); 172912a169e7SHerbert Xu if (xp->walk.dead) { 17306c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 17316c5c8ca7SJamal Hadi Salim goto out; 17326c5c8ca7SJamal Hadi Salim } 17336c5c8ca7SJamal Hadi Salim 17346c5c8ca7SJamal Hadi Salim read_unlock(&xp->lock); 17356c5c8ca7SJamal Hadi Salim err = 0; 17366c5c8ca7SJamal Hadi Salim if (up->hard) { 17372532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 17382532386fSEric Paris uid_t sessionid = NETLINK_CB(skb).sessionid; 17392532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 17406c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 17412532386fSEric Paris xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); 1742161a09e7SJoy Latten 17436c5c8ca7SJamal Hadi Salim } else { 17446c5c8ca7SJamal Hadi Salim // reset the timers here? 17456c5c8ca7SJamal Hadi Salim printk("Dont know what to do with soft policy expire\n"); 17466c5c8ca7SJamal Hadi Salim } 17476c5c8ca7SJamal Hadi Salim km_policy_expired(xp, p->dir, up->hard, current->pid); 17486c5c8ca7SJamal Hadi Salim 17496c5c8ca7SJamal Hadi Salim out: 17506c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 17516c5c8ca7SJamal Hadi Salim return err; 17526c5c8ca7SJamal Hadi Salim } 17536c5c8ca7SJamal Hadi Salim 175422e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 17555424f32eSThomas Graf struct nlattr **attrs) 175653bc6b4dSJamal Hadi Salim { 1757fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 175853bc6b4dSJamal Hadi Salim struct xfrm_state *x; 175953bc6b4dSJamal Hadi Salim int err; 17607b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 176153bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 176253bc6b4dSJamal Hadi Salim 1763fc34acd3SAlexey Dobriyan x = xfrm_state_lookup(net, &p->id.daddr, p->id.spi, p->id.proto, p->family); 176453bc6b4dSJamal Hadi Salim 17653a765aa5SDavid S. Miller err = -ENOENT; 176653bc6b4dSJamal Hadi Salim if (x == NULL) 176753bc6b4dSJamal Hadi Salim return err; 176853bc6b4dSJamal Hadi Salim 176953bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 17703a765aa5SDavid S. Miller err = -EINVAL; 177153bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 177253bc6b4dSJamal Hadi Salim goto out; 177353bc6b4dSJamal Hadi Salim km_state_expired(x, ue->hard, current->pid); 177453bc6b4dSJamal Hadi Salim 1775161a09e7SJoy Latten if (ue->hard) { 17762532386fSEric Paris uid_t loginuid = NETLINK_CB(skb).loginuid; 17772532386fSEric Paris uid_t sessionid = NETLINK_CB(skb).sessionid; 17782532386fSEric Paris u32 sid = NETLINK_CB(skb).sid; 177953bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 17802532386fSEric Paris xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); 1781161a09e7SJoy Latten } 17823a765aa5SDavid S. Miller err = 0; 178353bc6b4dSJamal Hadi Salim out: 178453bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 178553bc6b4dSJamal Hadi Salim xfrm_state_put(x); 178653bc6b4dSJamal Hadi Salim return err; 178753bc6b4dSJamal Hadi Salim } 178853bc6b4dSJamal Hadi Salim 178922e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 17905424f32eSThomas Graf struct nlattr **attrs) 1791980ebd25SJamal Hadi Salim { 1792fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1793980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 1794980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 1795980ebd25SJamal Hadi Salim int i; 17965424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 1797980ebd25SJamal Hadi Salim 17987b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 1799fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 1800980ebd25SJamal Hadi Salim int err = -ENOMEM; 1801980ebd25SJamal Hadi Salim 1802980ebd25SJamal Hadi Salim if (!x) 1803d8eb9307SIlpo Järvinen goto nomem; 1804980ebd25SJamal Hadi Salim 1805980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 1806d8eb9307SIlpo Järvinen if (err) 1807d8eb9307SIlpo Järvinen goto bad_policy; 1808980ebd25SJamal Hadi Salim 1809980ebd25SJamal Hadi Salim /* build an XP */ 1810fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 1811d8eb9307SIlpo Järvinen if (!xp) 1812d8eb9307SIlpo Järvinen goto free_state; 1813980ebd25SJamal Hadi Salim 1814980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 1815980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 1816980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 1817980ebd25SJamal Hadi Salim 18185424f32eSThomas Graf ut = nla_data(rt); 1819980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 1820980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 1821980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 1822980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 1823980ebd25SJamal Hadi Salim x->props.mode = t->mode; 1824980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 1825980ebd25SJamal Hadi Salim x->props.family = ut->family; 1826980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 1827980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 1828980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 1829980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 1830980ebd25SJamal Hadi Salim 1831980ebd25SJamal Hadi Salim } 1832980ebd25SJamal Hadi Salim 1833980ebd25SJamal Hadi Salim kfree(x); 1834980ebd25SJamal Hadi Salim kfree(xp); 1835980ebd25SJamal Hadi Salim 1836980ebd25SJamal Hadi Salim return 0; 1837d8eb9307SIlpo Järvinen 1838d8eb9307SIlpo Järvinen bad_policy: 1839d8eb9307SIlpo Järvinen printk("BAD policy passed\n"); 1840d8eb9307SIlpo Järvinen free_state: 1841d8eb9307SIlpo Järvinen kfree(x); 1842d8eb9307SIlpo Järvinen nomem: 1843d8eb9307SIlpo Järvinen return err; 1844980ebd25SJamal Hadi Salim } 1845980ebd25SJamal Hadi Salim 18465c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 18475c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 184813c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 18495424f32eSThomas Graf struct nlattr **attrs, int *num) 18505c79de6eSShinta Sugimoto { 18515424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 18525c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 18535c79de6eSShinta Sugimoto int i, num_migrate; 18545c79de6eSShinta Sugimoto 185513c1d189SArnaud Ebalard if (k != NULL) { 185613c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 185713c1d189SArnaud Ebalard 185813c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 185913c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 186013c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 186113c1d189SArnaud Ebalard k->family = uk->family; 186213c1d189SArnaud Ebalard k->reserved = uk->reserved; 186313c1d189SArnaud Ebalard } 186413c1d189SArnaud Ebalard 18655424f32eSThomas Graf um = nla_data(rt); 18665424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 18675c79de6eSShinta Sugimoto 18685c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 18695c79de6eSShinta Sugimoto return -EINVAL; 18705c79de6eSShinta Sugimoto 18715c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 18725c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 18735c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 18745c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 18755c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 18765c79de6eSShinta Sugimoto 18775c79de6eSShinta Sugimoto ma->proto = um->proto; 18785c79de6eSShinta Sugimoto ma->mode = um->mode; 18795c79de6eSShinta Sugimoto ma->reqid = um->reqid; 18805c79de6eSShinta Sugimoto 18815c79de6eSShinta Sugimoto ma->old_family = um->old_family; 18825c79de6eSShinta Sugimoto ma->new_family = um->new_family; 18835c79de6eSShinta Sugimoto } 18845c79de6eSShinta Sugimoto 18855c79de6eSShinta Sugimoto *num = i; 18865c79de6eSShinta Sugimoto return 0; 18875c79de6eSShinta Sugimoto } 18885c79de6eSShinta Sugimoto 18895c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 18905424f32eSThomas Graf struct nlattr **attrs) 18915c79de6eSShinta Sugimoto { 18927b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 18935c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 189413c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 18955c79de6eSShinta Sugimoto u8 type; 18965c79de6eSShinta Sugimoto int err; 18975c79de6eSShinta Sugimoto int n = 0; 18985c79de6eSShinta Sugimoto 189935a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 1900cf5cb79fSThomas Graf return -EINVAL; 19015c79de6eSShinta Sugimoto 190213c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 190313c1d189SArnaud Ebalard 19045424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 19055c79de6eSShinta Sugimoto if (err) 19065c79de6eSShinta Sugimoto return err; 19075c79de6eSShinta Sugimoto 190813c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 19095c79de6eSShinta Sugimoto if (err) 19105c79de6eSShinta Sugimoto return err; 19115c79de6eSShinta Sugimoto 19125c79de6eSShinta Sugimoto if (!n) 19135c79de6eSShinta Sugimoto return 0; 19145c79de6eSShinta Sugimoto 191513c1d189SArnaud Ebalard xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); 19165c79de6eSShinta Sugimoto 19175c79de6eSShinta Sugimoto return 0; 19185c79de6eSShinta Sugimoto } 19195c79de6eSShinta Sugimoto #else 19205c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 19215424f32eSThomas Graf struct nlattr **attrs) 19225c79de6eSShinta Sugimoto { 19235c79de6eSShinta Sugimoto return -ENOPROTOOPT; 19245c79de6eSShinta Sugimoto } 19255c79de6eSShinta Sugimoto #endif 19265c79de6eSShinta Sugimoto 19275c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 19285c79de6eSShinta Sugimoto static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) 19295c79de6eSShinta Sugimoto { 19305c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 19315c79de6eSShinta Sugimoto 19325c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 19335c79de6eSShinta Sugimoto um.proto = m->proto; 19345c79de6eSShinta Sugimoto um.mode = m->mode; 19355c79de6eSShinta Sugimoto um.reqid = m->reqid; 19365c79de6eSShinta Sugimoto um.old_family = m->old_family; 19375c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 19385c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 19395c79de6eSShinta Sugimoto um.new_family = m->new_family; 19405c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 19415c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 19425c79de6eSShinta Sugimoto 1943c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 19445c79de6eSShinta Sugimoto } 19455c79de6eSShinta Sugimoto 194613c1d189SArnaud Ebalard static int copy_to_user_kmaddress(struct xfrm_kmaddress *k, struct sk_buff *skb) 194713c1d189SArnaud Ebalard { 194813c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 194913c1d189SArnaud Ebalard 195013c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 195113c1d189SArnaud Ebalard uk.family = k->family; 195213c1d189SArnaud Ebalard uk.reserved = k->reserved; 195313c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 1954a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 195513c1d189SArnaud Ebalard 195613c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 195713c1d189SArnaud Ebalard } 195813c1d189SArnaud Ebalard 195913c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 19607deb2264SThomas Graf { 19617deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 196213c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 19637deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 19647deb2264SThomas Graf + userpolicy_type_attrsize(); 19657deb2264SThomas Graf } 19667deb2264SThomas Graf 19675c79de6eSShinta Sugimoto static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, 196813c1d189SArnaud Ebalard int num_migrate, struct xfrm_kmaddress *k, 196913c1d189SArnaud Ebalard struct xfrm_selector *sel, u8 dir, u8 type) 19705c79de6eSShinta Sugimoto { 19715c79de6eSShinta Sugimoto struct xfrm_migrate *mp; 19725c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 19735c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 19745c79de6eSShinta Sugimoto int i; 19755c79de6eSShinta Sugimoto 197679b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 197779b8b7f4SThomas Graf if (nlh == NULL) 197879b8b7f4SThomas Graf return -EMSGSIZE; 19795c79de6eSShinta Sugimoto 19807b67c857SThomas Graf pol_id = nlmsg_data(nlh); 19815c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 19825c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 19835c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 19845c79de6eSShinta Sugimoto pol_id->dir = dir; 19855c79de6eSShinta Sugimoto 198613c1d189SArnaud Ebalard if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0)) 198713c1d189SArnaud Ebalard goto nlmsg_failure; 198813c1d189SArnaud Ebalard 19895c79de6eSShinta Sugimoto if (copy_to_user_policy_type(type, skb) < 0) 19905c79de6eSShinta Sugimoto goto nlmsg_failure; 19915c79de6eSShinta Sugimoto 19925c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 19935c79de6eSShinta Sugimoto if (copy_to_user_migrate(mp, skb) < 0) 19945c79de6eSShinta Sugimoto goto nlmsg_failure; 19955c79de6eSShinta Sugimoto } 19965c79de6eSShinta Sugimoto 19979825069dSThomas Graf return nlmsg_end(skb, nlh); 19985c79de6eSShinta Sugimoto nlmsg_failure: 19999825069dSThomas Graf nlmsg_cancel(skb, nlh); 20009825069dSThomas Graf return -EMSGSIZE; 20015c79de6eSShinta Sugimoto } 20025c79de6eSShinta Sugimoto 20035c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 200413c1d189SArnaud Ebalard struct xfrm_migrate *m, int num_migrate, 200513c1d189SArnaud Ebalard struct xfrm_kmaddress *k) 20065c79de6eSShinta Sugimoto { 2007a6483b79SAlexey Dobriyan struct net *net = &init_net; 20085c79de6eSShinta Sugimoto struct sk_buff *skb; 20095c79de6eSShinta Sugimoto 201013c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 20115c79de6eSShinta Sugimoto if (skb == NULL) 20125c79de6eSShinta Sugimoto return -ENOMEM; 20135c79de6eSShinta Sugimoto 20145c79de6eSShinta Sugimoto /* build migrate */ 201513c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 20165c79de6eSShinta Sugimoto BUG(); 20175c79de6eSShinta Sugimoto 2018a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); 20195c79de6eSShinta Sugimoto } 20205c79de6eSShinta Sugimoto #else 20215c79de6eSShinta Sugimoto static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, 202213c1d189SArnaud Ebalard struct xfrm_migrate *m, int num_migrate, 202313c1d189SArnaud Ebalard struct xfrm_kmaddress *k) 20245c79de6eSShinta Sugimoto { 20255c79de6eSShinta Sugimoto return -ENOPROTOOPT; 20265c79de6eSShinta Sugimoto } 20275c79de6eSShinta Sugimoto #endif 2028d51d081dSJamal Hadi Salim 2029a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2030492b558bSThomas Graf 2031492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 203266f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2033492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2034492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2035492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2036492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2037492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2038492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2039980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 204053bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2041492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 204266f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 20436c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2044492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2045a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2046d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2047d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 204897a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 20495c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2050a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2051a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 20521da177e4SLinus Torvalds }; 20531da177e4SLinus Torvalds 2054492b558bSThomas Graf #undef XMSGSIZE 2055492b558bSThomas Graf 2056cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 20571a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2058cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2059cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2060cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2061cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2062cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2063cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2064cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2065cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2066cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2067cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2068cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2069cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2070cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2071cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 207213c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 2073cf5cb79fSThomas Graf }; 2074cf5cb79fSThomas Graf 20751da177e4SLinus Torvalds static struct xfrm_link { 20765424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 20771da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 20784c563f76STimo Teras int (*done)(struct netlink_callback *); 2079492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2080492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2081492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2082492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 20834c563f76STimo Teras .dump = xfrm_dump_sa, 20844c563f76STimo Teras .done = xfrm_dump_sa_done }, 2085492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2086492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2087492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 20884c563f76STimo Teras .dump = xfrm_dump_policy, 20894c563f76STimo Teras .done = xfrm_dump_policy_done }, 2090492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2091980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 209253bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2093492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2094492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 20956c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2096492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2097492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2098d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2099d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 21005c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 210128d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2102ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 21031da177e4SLinus Torvalds }; 21041da177e4SLinus Torvalds 21051d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 21061da177e4SLinus Torvalds { 2107a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 210835a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 21091da177e4SLinus Torvalds struct xfrm_link *link; 2110a7bd9a45SThomas Graf int type, err; 21111da177e4SLinus Torvalds 21121da177e4SLinus Torvalds type = nlh->nlmsg_type; 21131da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 21141d00a4ebSThomas Graf return -EINVAL; 21151da177e4SLinus Torvalds 21161da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 21171da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 21181da177e4SLinus Torvalds 21191da177e4SLinus Torvalds /* All operations require privileges, even GET */ 21201d00a4ebSThomas Graf if (security_netlink_recv(skb, CAP_NET_ADMIN)) 21211d00a4ebSThomas Graf return -EPERM; 21221da177e4SLinus Torvalds 2123492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2124492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2125492b558bSThomas Graf (nlh->nlmsg_flags & NLM_F_DUMP)) { 21261da177e4SLinus Torvalds if (link->dump == NULL) 21271d00a4ebSThomas Graf return -EINVAL; 21281da177e4SLinus Torvalds 2129a6483b79SAlexey Dobriyan return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done); 21301da177e4SLinus Torvalds } 21311da177e4SLinus Torvalds 213235a7aa08SThomas Graf err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2133cf5cb79fSThomas Graf xfrma_policy); 2134a7bd9a45SThomas Graf if (err < 0) 2135a7bd9a45SThomas Graf return err; 21361da177e4SLinus Torvalds 21371da177e4SLinus Torvalds if (link->doit == NULL) 21381d00a4ebSThomas Graf return -EINVAL; 21391da177e4SLinus Torvalds 21405424f32eSThomas Graf return link->doit(skb, nlh, attrs); 21411da177e4SLinus Torvalds } 21421da177e4SLinus Torvalds 2143cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 21441da177e4SLinus Torvalds { 21454a3e2f71SArjan van de Ven mutex_lock(&xfrm_cfg_mutex); 2146cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 21474a3e2f71SArjan van de Ven mutex_unlock(&xfrm_cfg_mutex); 21481da177e4SLinus Torvalds } 21491da177e4SLinus Torvalds 21507deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 21517deb2264SThomas Graf { 21527deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)); 21537deb2264SThomas Graf } 21547deb2264SThomas Graf 2155d51d081dSJamal Hadi Salim static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) 21561da177e4SLinus Torvalds { 21571da177e4SLinus Torvalds struct xfrm_user_expire *ue; 21581da177e4SLinus Torvalds struct nlmsghdr *nlh; 21591da177e4SLinus Torvalds 216079b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 216179b8b7f4SThomas Graf if (nlh == NULL) 216279b8b7f4SThomas Graf return -EMSGSIZE; 21631da177e4SLinus Torvalds 21647b67c857SThomas Graf ue = nlmsg_data(nlh); 21651da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2166d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 21671da177e4SLinus Torvalds 21689825069dSThomas Graf return nlmsg_end(skb, nlh); 21691da177e4SLinus Torvalds } 21701da177e4SLinus Torvalds 217126b15dadSJamal Hadi Salim static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c) 21721da177e4SLinus Torvalds { 2173fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 21741da177e4SLinus Torvalds struct sk_buff *skb; 21751da177e4SLinus Torvalds 21767deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 21771da177e4SLinus Torvalds if (skb == NULL) 21781da177e4SLinus Torvalds return -ENOMEM; 21791da177e4SLinus Torvalds 2180d51d081dSJamal Hadi Salim if (build_expire(skb, x, c) < 0) 21811da177e4SLinus Torvalds BUG(); 21821da177e4SLinus Torvalds 2183a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds 2186d51d081dSJamal Hadi Salim static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c) 2187d51d081dSJamal Hadi Salim { 2188fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2189d51d081dSJamal Hadi Salim struct sk_buff *skb; 2190d51d081dSJamal Hadi Salim 21917deb2264SThomas Graf skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); 2192d51d081dSJamal Hadi Salim if (skb == NULL) 2193d51d081dSJamal Hadi Salim return -ENOMEM; 2194d51d081dSJamal Hadi Salim 2195d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2196d51d081dSJamal Hadi Salim BUG(); 2197d51d081dSJamal Hadi Salim 2198a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); 2199d51d081dSJamal Hadi Salim } 2200d51d081dSJamal Hadi Salim 220126b15dadSJamal Hadi Salim static int xfrm_notify_sa_flush(struct km_event *c) 220226b15dadSJamal Hadi Salim { 22037067802eSAlexey Dobriyan struct net *net = c->net; 220426b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 220526b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 220626b15dadSJamal Hadi Salim struct sk_buff *skb; 22077deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 220826b15dadSJamal Hadi Salim 22097deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 221026b15dadSJamal Hadi Salim if (skb == NULL) 221126b15dadSJamal Hadi Salim return -ENOMEM; 221226b15dadSJamal Hadi Salim 221379b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 221479b8b7f4SThomas Graf if (nlh == NULL) { 221579b8b7f4SThomas Graf kfree_skb(skb); 221679b8b7f4SThomas Graf return -EMSGSIZE; 221779b8b7f4SThomas Graf } 221826b15dadSJamal Hadi Salim 22197b67c857SThomas Graf p = nlmsg_data(nlh); 2220bf08867fSHerbert Xu p->proto = c->data.proto; 222126b15dadSJamal Hadi Salim 22229825069dSThomas Graf nlmsg_end(skb, nlh); 222326b15dadSJamal Hadi Salim 2224a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 222526b15dadSJamal Hadi Salim } 222626b15dadSJamal Hadi Salim 22277deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 222826b15dadSJamal Hadi Salim { 22297deb2264SThomas Graf size_t l = 0; 22301a6509d9SHerbert Xu if (x->aead) 22311a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 22324447bb33SMartin Willi if (x->aalg) { 22334447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 22344447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 22354447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 22364447bb33SMartin Willi } 223726b15dadSJamal Hadi Salim if (x->ealg) 22380f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 223926b15dadSJamal Hadi Salim if (x->calg) 22407deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 224126b15dadSJamal Hadi Salim if (x->encap) 22427deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 224368325d3bSHerbert Xu if (x->security) 224468325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 224568325d3bSHerbert Xu x->security->ctx_len); 224668325d3bSHerbert Xu if (x->coaddr) 224768325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 224868325d3bSHerbert Xu 2249d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2250d26f3984SHerbert Xu l += nla_total_size(sizeof(u64)); 225126b15dadSJamal Hadi Salim 225226b15dadSJamal Hadi Salim return l; 225326b15dadSJamal Hadi Salim } 225426b15dadSJamal Hadi Salim 225526b15dadSJamal Hadi Salim static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c) 225626b15dadSJamal Hadi Salim { 2257fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 225826b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 22590603eac0SHerbert Xu struct xfrm_usersa_id *id; 226026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 226126b15dadSJamal Hadi Salim struct sk_buff *skb; 226226b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 22630603eac0SHerbert Xu int headlen; 22640603eac0SHerbert Xu 22650603eac0SHerbert Xu headlen = sizeof(*p); 22660603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 22677deb2264SThomas Graf len += nla_total_size(headlen); 22680603eac0SHerbert Xu headlen = sizeof(*id); 22690603eac0SHerbert Xu } 22707deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 227126b15dadSJamal Hadi Salim 22727deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 227326b15dadSJamal Hadi Salim if (skb == NULL) 227426b15dadSJamal Hadi Salim return -ENOMEM; 227526b15dadSJamal Hadi Salim 227679b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 227779b8b7f4SThomas Graf if (nlh == NULL) 2278c0144beaSThomas Graf goto nla_put_failure; 227926b15dadSJamal Hadi Salim 22807b67c857SThomas Graf p = nlmsg_data(nlh); 22810603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2282c0144beaSThomas Graf struct nlattr *attr; 2283c0144beaSThomas Graf 22847b67c857SThomas Graf id = nlmsg_data(nlh); 22850603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 22860603eac0SHerbert Xu id->spi = x->id.spi; 22870603eac0SHerbert Xu id->family = x->props.family; 22880603eac0SHerbert Xu id->proto = x->id.proto; 22890603eac0SHerbert Xu 2290c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 2291c0144beaSThomas Graf if (attr == NULL) 2292c0144beaSThomas Graf goto nla_put_failure; 2293c0144beaSThomas Graf 2294c0144beaSThomas Graf p = nla_data(attr); 22950603eac0SHerbert Xu } 22960603eac0SHerbert Xu 229768325d3bSHerbert Xu if (copy_to_user_state_extra(x, p, skb)) 229868325d3bSHerbert Xu goto nla_put_failure; 229926b15dadSJamal Hadi Salim 23009825069dSThomas Graf nlmsg_end(skb, nlh); 230126b15dadSJamal Hadi Salim 2302a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); 230326b15dadSJamal Hadi Salim 2304c0144beaSThomas Graf nla_put_failure: 230568325d3bSHerbert Xu /* Somebody screwed up with xfrm_sa_len! */ 230668325d3bSHerbert Xu WARN_ON(1); 230726b15dadSJamal Hadi Salim kfree_skb(skb); 230826b15dadSJamal Hadi Salim return -1; 230926b15dadSJamal Hadi Salim } 231026b15dadSJamal Hadi Salim 231126b15dadSJamal Hadi Salim static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c) 231226b15dadSJamal Hadi Salim { 231326b15dadSJamal Hadi Salim 231426b15dadSJamal Hadi Salim switch (c->event) { 2315f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 231626b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2317d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2318d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2319f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2320f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2321f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 232226b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2323f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 232426b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 232526b15dadSJamal Hadi Salim default: 232626b15dadSJamal Hadi Salim printk("xfrm_user: Unknown SA event %d\n", c->event); 232726b15dadSJamal Hadi Salim break; 232826b15dadSJamal Hadi Salim } 232926b15dadSJamal Hadi Salim 233026b15dadSJamal Hadi Salim return 0; 233126b15dadSJamal Hadi Salim 233226b15dadSJamal Hadi Salim } 233326b15dadSJamal Hadi Salim 23347deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 23357deb2264SThomas Graf struct xfrm_policy *xp) 23367deb2264SThomas Graf { 23377deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 23387deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 23397deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 23407deb2264SThomas Graf + userpolicy_type_attrsize(); 23417deb2264SThomas Graf } 23427deb2264SThomas Graf 23431da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 23441da177e4SLinus Torvalds struct xfrm_tmpl *xt, struct xfrm_policy *xp, 23451da177e4SLinus Torvalds int dir) 23461da177e4SLinus Torvalds { 23471da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 23481da177e4SLinus Torvalds struct nlmsghdr *nlh; 23491da177e4SLinus Torvalds __u32 seq = xfrm_get_acqseq(); 23501da177e4SLinus Torvalds 235179b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 235279b8b7f4SThomas Graf if (nlh == NULL) 235379b8b7f4SThomas Graf return -EMSGSIZE; 23541da177e4SLinus Torvalds 23557b67c857SThomas Graf ua = nlmsg_data(nlh); 23561da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 23571da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 23581da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 23591da177e4SLinus Torvalds copy_to_user_policy(xp, &ua->policy, dir); 23601da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 23611da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 23621da177e4SLinus Torvalds ua->calgos = xt->calgos; 23631da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 23641da177e4SLinus Torvalds 23651da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 23661da177e4SLinus Torvalds goto nlmsg_failure; 23670d681623SSerge Hallyn if (copy_to_user_state_sec_ctx(x, skb)) 2368df71837dSTrent Jaeger goto nlmsg_failure; 23691459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2370f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 23711da177e4SLinus Torvalds 23729825069dSThomas Graf return nlmsg_end(skb, nlh); 23731da177e4SLinus Torvalds 23741da177e4SLinus Torvalds nlmsg_failure: 23759825069dSThomas Graf nlmsg_cancel(skb, nlh); 23769825069dSThomas Graf return -EMSGSIZE; 23771da177e4SLinus Torvalds } 23781da177e4SLinus Torvalds 23791da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 23801da177e4SLinus Torvalds struct xfrm_policy *xp, int dir) 23811da177e4SLinus Torvalds { 2382a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 23831da177e4SLinus Torvalds struct sk_buff *skb; 23841da177e4SLinus Torvalds 23857deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 23861da177e4SLinus Torvalds if (skb == NULL) 23871da177e4SLinus Torvalds return -ENOMEM; 23881da177e4SLinus Torvalds 23891da177e4SLinus Torvalds if (build_acquire(skb, x, xt, xp, dir) < 0) 23901da177e4SLinus Torvalds BUG(); 23911da177e4SLinus Torvalds 2392a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); 23931da177e4SLinus Torvalds } 23941da177e4SLinus Torvalds 23951da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 23961da177e4SLinus Torvalds * or more templates. 23971da177e4SLinus Torvalds */ 2398cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 23991da177e4SLinus Torvalds u8 *data, int len, int *dir) 24001da177e4SLinus Torvalds { 2401fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 24021da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 24031da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 24041da177e4SLinus Torvalds struct xfrm_policy *xp; 24051da177e4SLinus Torvalds int nr; 24061da177e4SLinus Torvalds 2407cb969f07SVenkat Yekkirala switch (sk->sk_family) { 24081da177e4SLinus Torvalds case AF_INET: 24091da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 24101da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 24111da177e4SLinus Torvalds return NULL; 24121da177e4SLinus Torvalds } 24131da177e4SLinus Torvalds break; 24141da177e4SLinus Torvalds #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 24151da177e4SLinus Torvalds case AF_INET6: 24161da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 24171da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 24181da177e4SLinus Torvalds return NULL; 24191da177e4SLinus Torvalds } 24201da177e4SLinus Torvalds break; 24211da177e4SLinus Torvalds #endif 24221da177e4SLinus Torvalds default: 24231da177e4SLinus Torvalds *dir = -EINVAL; 24241da177e4SLinus Torvalds return NULL; 24251da177e4SLinus Torvalds } 24261da177e4SLinus Torvalds 24271da177e4SLinus Torvalds *dir = -EINVAL; 24281da177e4SLinus Torvalds 24291da177e4SLinus Torvalds if (len < sizeof(*p) || 24301da177e4SLinus Torvalds verify_newpolicy_info(p)) 24311da177e4SLinus Torvalds return NULL; 24321da177e4SLinus Torvalds 24331da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2434b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 24351da177e4SLinus Torvalds return NULL; 24361da177e4SLinus Torvalds 2437a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2438a4f1bac6SHerbert Xu return NULL; 2439a4f1bac6SHerbert Xu 2440fc34acd3SAlexey Dobriyan xp = xfrm_policy_alloc(net, GFP_KERNEL); 24411da177e4SLinus Torvalds if (xp == NULL) { 24421da177e4SLinus Torvalds *dir = -ENOBUFS; 24431da177e4SLinus Torvalds return NULL; 24441da177e4SLinus Torvalds } 24451da177e4SLinus Torvalds 24461da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2447f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 24481da177e4SLinus Torvalds copy_templates(xp, ut, nr); 24491da177e4SLinus Torvalds 24501da177e4SLinus Torvalds *dir = p->dir; 24511da177e4SLinus Torvalds 24521da177e4SLinus Torvalds return xp; 24531da177e4SLinus Torvalds } 24541da177e4SLinus Torvalds 24557deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 24567deb2264SThomas Graf { 24577deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 24587deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 24597deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 24607deb2264SThomas Graf + userpolicy_type_attrsize(); 24617deb2264SThomas Graf } 24627deb2264SThomas Graf 24631da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2464d51d081dSJamal Hadi Salim int dir, struct km_event *c) 24651da177e4SLinus Torvalds { 24661da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 24671da177e4SLinus Torvalds struct nlmsghdr *nlh; 2468d51d081dSJamal Hadi Salim int hard = c->data.hard; 24691da177e4SLinus Torvalds 247079b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 247179b8b7f4SThomas Graf if (nlh == NULL) 247279b8b7f4SThomas Graf return -EMSGSIZE; 24731da177e4SLinus Torvalds 24747b67c857SThomas Graf upe = nlmsg_data(nlh); 24751da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 24761da177e4SLinus Torvalds if (copy_to_user_tmpl(xp, skb) < 0) 24771da177e4SLinus Torvalds goto nlmsg_failure; 2478df71837dSTrent Jaeger if (copy_to_user_sec_ctx(xp, skb)) 2479df71837dSTrent Jaeger goto nlmsg_failure; 24801459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2481f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 24821da177e4SLinus Torvalds upe->hard = !!hard; 24831da177e4SLinus Torvalds 24849825069dSThomas Graf return nlmsg_end(skb, nlh); 24851da177e4SLinus Torvalds 24861da177e4SLinus Torvalds nlmsg_failure: 24879825069dSThomas Graf nlmsg_cancel(skb, nlh); 24889825069dSThomas Graf return -EMSGSIZE; 24891da177e4SLinus Torvalds } 24901da177e4SLinus Torvalds 249126b15dadSJamal Hadi Salim static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 24921da177e4SLinus Torvalds { 2493fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 24941da177e4SLinus Torvalds struct sk_buff *skb; 24951da177e4SLinus Torvalds 24967deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 24971da177e4SLinus Torvalds if (skb == NULL) 24981da177e4SLinus Torvalds return -ENOMEM; 24991da177e4SLinus Torvalds 2500d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 25011da177e4SLinus Torvalds BUG(); 25021da177e4SLinus Torvalds 2503a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); 25041da177e4SLinus Torvalds } 25051da177e4SLinus Torvalds 250626b15dadSJamal Hadi Salim static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c) 250726b15dadSJamal Hadi Salim { 2508fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 250926b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 25100603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 251126b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 251226b15dadSJamal Hadi Salim struct sk_buff *skb; 25137deb2264SThomas Graf int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 25140603eac0SHerbert Xu int headlen; 25150603eac0SHerbert Xu 25160603eac0SHerbert Xu headlen = sizeof(*p); 25170603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 25187deb2264SThomas Graf len += nla_total_size(headlen); 25190603eac0SHerbert Xu headlen = sizeof(*id); 25200603eac0SHerbert Xu } 2521cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 25227deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 252326b15dadSJamal Hadi Salim 25247deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 252526b15dadSJamal Hadi Salim if (skb == NULL) 252626b15dadSJamal Hadi Salim return -ENOMEM; 252726b15dadSJamal Hadi Salim 252879b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); 252979b8b7f4SThomas Graf if (nlh == NULL) 253079b8b7f4SThomas Graf goto nlmsg_failure; 253126b15dadSJamal Hadi Salim 25327b67c857SThomas Graf p = nlmsg_data(nlh); 25330603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2534c0144beaSThomas Graf struct nlattr *attr; 2535c0144beaSThomas Graf 25367b67c857SThomas Graf id = nlmsg_data(nlh); 25370603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 25380603eac0SHerbert Xu id->dir = dir; 25390603eac0SHerbert Xu if (c->data.byid) 25400603eac0SHerbert Xu id->index = xp->index; 25410603eac0SHerbert Xu else 25420603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 25430603eac0SHerbert Xu 2544c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 2545c0144beaSThomas Graf if (attr == NULL) 2546c0144beaSThomas Graf goto nlmsg_failure; 2547c0144beaSThomas Graf 2548c0144beaSThomas Graf p = nla_data(attr); 25490603eac0SHerbert Xu } 255026b15dadSJamal Hadi Salim 255126b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 255226b15dadSJamal Hadi Salim if (copy_to_user_tmpl(xp, skb) < 0) 255326b15dadSJamal Hadi Salim goto nlmsg_failure; 25541459bb36SJamal Hadi Salim if (copy_to_user_policy_type(xp->type, skb) < 0) 2555f7b6983fSMasahide NAKAMURA goto nlmsg_failure; 255626b15dadSJamal Hadi Salim 25579825069dSThomas Graf nlmsg_end(skb, nlh); 255826b15dadSJamal Hadi Salim 2559a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 256026b15dadSJamal Hadi Salim 256126b15dadSJamal Hadi Salim nlmsg_failure: 256226b15dadSJamal Hadi Salim kfree_skb(skb); 256326b15dadSJamal Hadi Salim return -1; 256426b15dadSJamal Hadi Salim } 256526b15dadSJamal Hadi Salim 256626b15dadSJamal Hadi Salim static int xfrm_notify_policy_flush(struct km_event *c) 256726b15dadSJamal Hadi Salim { 25687067802eSAlexey Dobriyan struct net *net = c->net; 256926b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 257026b15dadSJamal Hadi Salim struct sk_buff *skb; 257126b15dadSJamal Hadi Salim 25727deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 257326b15dadSJamal Hadi Salim if (skb == NULL) 257426b15dadSJamal Hadi Salim return -ENOMEM; 257526b15dadSJamal Hadi Salim 257679b8b7f4SThomas Graf nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 257779b8b7f4SThomas Graf if (nlh == NULL) 257879b8b7f4SThomas Graf goto nlmsg_failure; 25790c51f53cSJamal Hadi Salim if (copy_to_user_policy_type(c->data.type, skb) < 0) 25800c51f53cSJamal Hadi Salim goto nlmsg_failure; 258126b15dadSJamal Hadi Salim 25829825069dSThomas Graf nlmsg_end(skb, nlh); 258326b15dadSJamal Hadi Salim 2584a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); 258526b15dadSJamal Hadi Salim 258626b15dadSJamal Hadi Salim nlmsg_failure: 258726b15dadSJamal Hadi Salim kfree_skb(skb); 258826b15dadSJamal Hadi Salim return -1; 258926b15dadSJamal Hadi Salim } 259026b15dadSJamal Hadi Salim 259126b15dadSJamal Hadi Salim static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) 259226b15dadSJamal Hadi Salim { 259326b15dadSJamal Hadi Salim 259426b15dadSJamal Hadi Salim switch (c->event) { 2595f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2596f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2597f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 259826b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2599f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 260026b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2601f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 260226b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 260326b15dadSJamal Hadi Salim default: 260426b15dadSJamal Hadi Salim printk("xfrm_user: Unknown Policy event %d\n", c->event); 260526b15dadSJamal Hadi Salim } 260626b15dadSJamal Hadi Salim 260726b15dadSJamal Hadi Salim return 0; 260826b15dadSJamal Hadi Salim 260926b15dadSJamal Hadi Salim } 261026b15dadSJamal Hadi Salim 26117deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 26127deb2264SThomas Graf { 26137deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 26147deb2264SThomas Graf } 26157deb2264SThomas Graf 261697a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 261797a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 261897a64b45SMasahide NAKAMURA { 261997a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 262097a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 262197a64b45SMasahide NAKAMURA 262279b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 262379b8b7f4SThomas Graf if (nlh == NULL) 262479b8b7f4SThomas Graf return -EMSGSIZE; 262597a64b45SMasahide NAKAMURA 26267b67c857SThomas Graf ur = nlmsg_data(nlh); 262797a64b45SMasahide NAKAMURA ur->proto = proto; 262897a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 262997a64b45SMasahide NAKAMURA 263097a64b45SMasahide NAKAMURA if (addr) 2631c0144beaSThomas Graf NLA_PUT(skb, XFRMA_COADDR, sizeof(*addr), addr); 263297a64b45SMasahide NAKAMURA 26339825069dSThomas Graf return nlmsg_end(skb, nlh); 263497a64b45SMasahide NAKAMURA 2635c0144beaSThomas Graf nla_put_failure: 26369825069dSThomas Graf nlmsg_cancel(skb, nlh); 26379825069dSThomas Graf return -EMSGSIZE; 263897a64b45SMasahide NAKAMURA } 263997a64b45SMasahide NAKAMURA 2640db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 2641db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 264297a64b45SMasahide NAKAMURA { 264397a64b45SMasahide NAKAMURA struct sk_buff *skb; 264497a64b45SMasahide NAKAMURA 26457deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 264697a64b45SMasahide NAKAMURA if (skb == NULL) 264797a64b45SMasahide NAKAMURA return -ENOMEM; 264897a64b45SMasahide NAKAMURA 264997a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 265097a64b45SMasahide NAKAMURA BUG(); 265197a64b45SMasahide NAKAMURA 2652a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); 265397a64b45SMasahide NAKAMURA } 265497a64b45SMasahide NAKAMURA 26553a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 26563a2dfbe8SMartin Willi { 26573a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 26583a2dfbe8SMartin Willi } 26593a2dfbe8SMartin Willi 26603a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 26613a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 26623a2dfbe8SMartin Willi { 26633a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 26643a2dfbe8SMartin Willi struct nlmsghdr *nlh; 26653a2dfbe8SMartin Willi 26663a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 26673a2dfbe8SMartin Willi if (nlh == NULL) 26683a2dfbe8SMartin Willi return -EMSGSIZE; 26693a2dfbe8SMartin Willi 26703a2dfbe8SMartin Willi um = nlmsg_data(nlh); 26713a2dfbe8SMartin Willi 26723a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 26733a2dfbe8SMartin Willi um->id.spi = x->id.spi; 26743a2dfbe8SMartin Willi um->id.family = x->props.family; 26753a2dfbe8SMartin Willi um->id.proto = x->id.proto; 26763a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 26773a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 26783a2dfbe8SMartin Willi um->new_sport = new_sport; 26793a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 26803a2dfbe8SMartin Willi um->reqid = x->props.reqid; 26813a2dfbe8SMartin Willi 26823a2dfbe8SMartin Willi return nlmsg_end(skb, nlh); 26833a2dfbe8SMartin Willi } 26843a2dfbe8SMartin Willi 26853a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 26863a2dfbe8SMartin Willi __be16 sport) 26873a2dfbe8SMartin Willi { 2688a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 26893a2dfbe8SMartin Willi struct sk_buff *skb; 26903a2dfbe8SMartin Willi 26913a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 26923a2dfbe8SMartin Willi return -EINVAL; 26933a2dfbe8SMartin Willi 26943a2dfbe8SMartin Willi if (!x->encap) 26953a2dfbe8SMartin Willi return -EINVAL; 26963a2dfbe8SMartin Willi 26973a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 26983a2dfbe8SMartin Willi if (skb == NULL) 26993a2dfbe8SMartin Willi return -ENOMEM; 27003a2dfbe8SMartin Willi 27013a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 27023a2dfbe8SMartin Willi BUG(); 27033a2dfbe8SMartin Willi 2704a6483b79SAlexey Dobriyan return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 27053a2dfbe8SMartin Willi } 27063a2dfbe8SMartin Willi 27071da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 27081da177e4SLinus Torvalds .id = "netlink", 27091da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 27101da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 27111da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 27121da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 271397a64b45SMasahide NAKAMURA .report = xfrm_send_report, 27145c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 27153a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 27161da177e4SLinus Torvalds }; 27171da177e4SLinus Torvalds 2718a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 27191da177e4SLinus Torvalds { 2720be33690dSPatrick McHardy struct sock *nlsk; 2721be33690dSPatrick McHardy 2722a6483b79SAlexey Dobriyan nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, 2723af65bdfcSPatrick McHardy xfrm_netlink_rcv, NULL, THIS_MODULE); 2724be33690dSPatrick McHardy if (nlsk == NULL) 27251da177e4SLinus Torvalds return -ENOMEM; 2726d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 2727a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, nlsk); 27281da177e4SLinus Torvalds return 0; 27291da177e4SLinus Torvalds } 27301da177e4SLinus Torvalds 2731d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 2732a6483b79SAlexey Dobriyan { 2733d79d792eSEric W. Biederman struct net *net; 2734d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2735a6483b79SAlexey Dobriyan rcu_assign_pointer(net->xfrm.nlsk, NULL); 2736d79d792eSEric W. Biederman synchronize_net(); 2737d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 2738d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 2739a6483b79SAlexey Dobriyan } 2740a6483b79SAlexey Dobriyan 2741a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 2742a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 2743d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 2744a6483b79SAlexey Dobriyan }; 2745a6483b79SAlexey Dobriyan 2746a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 2747a6483b79SAlexey Dobriyan { 2748a6483b79SAlexey Dobriyan int rv; 2749a6483b79SAlexey Dobriyan 2750a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 2751a6483b79SAlexey Dobriyan 2752a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 2753a6483b79SAlexey Dobriyan if (rv < 0) 2754a6483b79SAlexey Dobriyan return rv; 2755a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 2756a6483b79SAlexey Dobriyan if (rv < 0) 2757a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 2758a6483b79SAlexey Dobriyan return rv; 2759a6483b79SAlexey Dobriyan } 2760a6483b79SAlexey Dobriyan 27611da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 27621da177e4SLinus Torvalds { 27631da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 2764a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 27651da177e4SLinus Torvalds } 27661da177e4SLinus Torvalds 27671da177e4SLinus Torvalds module_init(xfrm_user_init); 27681da177e4SLinus Torvalds module_exit(xfrm_user_exit); 27691da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 27704fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 2771f8cd5488SJamal Hadi Salim 2772