Release 4.11 net/xfrm/xfrm_user.c
/* xfrm_user.c: User interface to configure xfrm engine.
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*
* Changes:
* Mitsuru KANDA @USAGI
* Kazunori MIYAZAWA @USAGI
* Kunihiro Ishiguro <kunihiro@ipinfusion.com>
* IPv6 support
*
*/
#include <linux/crypto.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include <linux/string.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
#include <linux/init.h>
#include <linux/security.h>
#include <net/sock.h>
#include <net/xfrm.h>
#include <net/netlink.h>
#include <net/ah.h>
#include <linux/uaccess.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <linux/in6.h>
#endif
#include <asm/unaligned.h>
static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
{
struct nlattr *rt = attrs[type];
struct xfrm_algo *algp;
if (!rt)
return 0;
algp = nla_data(rt);
if (nla_len(rt) < xfrm_alg_len(algp))
return -EINVAL;
switch (type) {
case XFRMA_ALG_AUTH:
case XFRMA_ALG_CRYPT:
case XFRMA_ALG_COMP:
break;
default:
return -EINVAL;
}
algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David S. Miller | 76 | 78.35% | 1 | 16.67% |
Herbert Xu | 10 | 10.31% | 1 | 16.67% |
Thomas Graf | 10 | 10.31% | 3 | 50.00% |
Eric Dumazet | 1 | 1.03% | 1 | 16.67% |
Total | 97 | 100.00% | 6 | 100.00% |
static int verify_auth_trunc(struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC];
struct xfrm_algo_auth *algp;
if (!rt)
return 0;
algp = nla_data(rt);
if (nla_len(rt) < xfrm_alg_auth_len(algp))
return -EINVAL;
algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Martin Willi | 72 | 100.00% | 1 | 100.00% |
Total | 72 | 100.00% | 1 | 100.00% |
static int verify_aead(struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_ALG_AEAD];
struct xfrm_algo_aead *algp;
if (!rt)
return 0;
algp = nla_data(rt);
if (nla_len(rt) < aead_len(algp))
return -EINVAL;
algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Herbert Xu | 72 | 100.00% | 1 | 100.00% |
Total | 72 | 100.00% | 1 | 100.00% |
static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,
xfrm_address_t **addrp)
{
struct nlattr *rt = attrs[type];
if (rt && addrp)
*addrp = nla_data(rt);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Masahide Nakamura | 38 | 84.44% | 1 | 25.00% |
Thomas Graf | 7 | 15.56% | 3 | 75.00% |
Total | 45 | 100.00% | 4 | 100.00% |
static inline int verify_sec_ctx_len(struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_SEC_CTX];
struct xfrm_user_sec_ctx *uctx;
if (!rt)
return 0;
uctx = nla_data(rt);
if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len))
return -EINVAL;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Trent Jaeger | 55 | 80.88% | 1 | 25.00% |
Thomas Graf | 13 | 19.12% | 3 | 75.00% |
Total | 68 | 100.00% | 4 | 100.00% |
static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
struct xfrm_replay_state_esn *rs;
if (p->flags & XFRM_STATE_ESN) {
if (!rt)
return -EINVAL;
rs = nla_data(rt);
if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
return -EINVAL;
if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
nla_len(rt) != sizeof(*rs))
return -EINVAL;
}
if (!rt)
return 0;
/* As only ESP and AH support ESN feature. */
if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH))
return -EINVAL;
if (p->replay_window != 0)
return -EINVAL;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Steffen Klassert | 79 | 49.38% | 3 | 60.00% |
Mathias Krause | 68 | 42.50% | 1 | 20.00% |
Fan Du | 13 | 8.12% | 1 | 20.00% |
Total | 160 | 100.00% | 5 | 100.00% |
static int verify_newsa_info(struct xfrm_usersa_info *p,
struct nlattr **attrs)
{
int err;
err = -EINVAL;
switch (p->family) {
case AF_INET:
break;
case AF_INET6:
#if IS_ENABLED(CONFIG_IPV6)
break;
#else
err = -EAFNOSUPPORT;
goto out;
#endif
default:
goto out;
}
err = -EINVAL;
switch (p->id.proto) {
case IPPROTO_AH:
if ((!attrs[XFRMA_ALG_AUTH] &&
!attrs[XFRMA_ALG_AUTH_TRUNC]) ||
attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_ALG_COMP] ||
attrs[XFRMA_TFCPAD])
goto out;
break;
case IPPROTO_ESP:
if (attrs[XFRMA_ALG_COMP])
goto out;
if (!attrs[XFRMA_ALG_AUTH] &&
!attrs[XFRMA_ALG_AUTH_TRUNC] &&
!attrs[XFRMA_ALG_CRYPT] &&
!attrs[XFRMA_ALG_AEAD])
goto out;
if ((attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_CRYPT]) &&
attrs[XFRMA_ALG_AEAD])
goto out;
if (attrs[XFRMA_TFCPAD] &&
p->mode != XFRM_MODE_TUNNEL)
goto out;
break;
case IPPROTO_COMP:
if (!attrs[XFRMA_ALG_COMP] ||
attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_TFCPAD] ||
(ntohl(p->id.spi) >= 0x10000))
goto out;
break;
#if IS_ENABLED(CONFIG_IPV6)
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
if (attrs[XFRMA_ALG_COMP] ||
attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_ENCAP] ||
attrs[XFRMA_SEC_CTX] ||
attrs[XFRMA_TFCPAD] ||
!attrs[XFRMA_COADDR])
goto out;
break;
#endif
default:
goto out;
}
if ((err = verify_aead(attrs)))
goto out;
if ((err = verify_auth_trunc(attrs)))
goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH)))
goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT)))
goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP)))
goto out;
if ((err = verify_sec_ctx_len(attrs)))
goto out;
if ((err = verify_replay(p, attrs)))
goto out;
err = -EINVAL;
switch (p->mode) {
case XFRM_MODE_TRANSPORT:
case XFRM_MODE_TUNNEL:
case XFRM_MODE_ROUTEOPTIMIZATION:
case XFRM_MODE_BEET:
break;
default:
goto out;
}
err = 0;
out:
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David S. Miller | 210 | 44.21% | 2 | 12.50% |
Martin Willi | 77 | 16.21% | 2 | 12.50% |
Herbert Xu | 63 | 13.26% | 1 | 6.25% |
Masahide Nakamura | 46 | 9.68% | 2 | 12.50% |
Thomas Graf | 21 | 4.42% | 2 | 12.50% |
Steffen Klassert | 16 | 3.37% | 1 | 6.25% |
Kunihiro Ishiguro | 13 | 2.74% | 1 | 6.25% |
Tobias Brunner | 13 | 2.74% | 1 | 6.25% |
Trent Jaeger | 8 | 1.68% | 1 | 6.25% |
Noriaki Takamiya | 3 | 0.63% | 1 | 6.25% |
Diego Beltrami | 3 | 0.63% | 1 | 6.25% |
Eric Dumazet | 2 | 0.42% | 1 | 6.25% |
Total | 475 | 100.00% | 16 | 100.00% |
static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
struct xfrm_algo_desc *(*get_byname)(const char *, int),
struct nlattr *rta)
{
struct xfrm_algo *p, *ualg;
struct xfrm_algo_desc *algo;
if (!rta)
return 0;
ualg = nla_data(rta);
algo = get_byname(ualg->alg_name, 1);
if (!algo)
return -ENOSYS;
*props = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
if (!p)
return -ENOMEM;
strcpy(p->alg_name, algo->name);
*algpp = p;
return 0;
}
static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
{
struct xfrm_algo *p, *ualg;
struct xfrm_algo_desc *algo;
if (!rta)
return 0;
ualg = nla_data(rta);
algo = xfrm_ealg_get_byname(ualg->alg_name, 1);
if (!algo)
return -ENOSYS;
x->props.ealgo = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
if (!p)
return -ENOMEM;
strcpy(p->alg_name, algo->name);
x->ealg = p;
x->geniv = algo->uinfo.encr.geniv;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Herbert Xu | 131 | 100.00% | 1 | 100.00% |
Total | 131 | 100.00% | 1 | 100.00% |
static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
struct nlattr *rta)
{
struct xfrm_algo *ualg;
struct xfrm_algo_auth *p;
struct xfrm_algo_desc *algo;
if (!rta)
return 0;
ualg = nla_data(rta);
algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
if (!algo)
return -ENOSYS;
*props = algo->desc.sadb_alg_id;
p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL);
if (!p)
return -ENOMEM;
strcpy(p->alg_name, algo->name);
p->alg_key_len = ualg->alg_key_len;
p->alg_trunc_len = algo->uinfo.auth.icv_truncbits;
memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8);
*algpp = p;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Martin Willi | 170 | 98.84% | 1 | 50.00% |
David S. Miller | 2 | 1.16% | 1 | 50.00% |
Total | 172 | 100.00% | 2 | 100.00% |
static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
struct nlattr *rta)
{
struct xfrm_algo_auth *p, *ualg;
struct xfrm_algo_desc *algo;
if (!rta)
return 0;
ualg = nla_data(rta);
algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
if (!algo)
return -ENOSYS;
if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits)
return -EINVAL;
*props = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL);
if (!p)
return -ENOMEM;
strcpy(p->alg_name, algo->name);
if (!p->alg_trunc_len)
p->alg_trunc_len = algo->uinfo.auth.icv_truncbits;
*algpp = p;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Martin Willi | 52 | 33.12% | 1 | 11.11% |
David S. Miller | 48 | 30.57% | 1 | 11.11% |
Herbert Xu | 48 | 30.57% | 4 | 44.44% |
Thomas Graf | 6 | 3.82% | 2 | 22.22% |
Arnaldo Carvalho de Melo | 3 | 1.91% | 1 | 11.11% |
Total | 157 | 100.00% | 9 | 100.00% |
static int attach_aead(struct xfrm_state *x, struct nlattr *rta)
{
struct xfrm_algo_aead *p, *ualg;
struct xfrm_algo_desc *algo;
if (!rta)
return 0;
ualg = nla_data(rta);
algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
if (!algo)
return -ENOSYS;
x->props.ealgo = algo->desc.sadb_alg_id;
p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
if (!p)
return -ENOMEM;
strcpy(p->alg_name, algo->name);
x->aead = p;
x->geniv = algo->uinfo.aead.geniv;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Herbert Xu | 135 | 100.00% | 2 | 100.00% |
Total | 135 | 100.00% | 2 | 100.00% |
static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn,
struct nlattr *rp)
{
struct xfrm_replay_state_esn *up;
int ulen;
if (!replay_esn || !rp)
return 0;
up = nla_data(rp);
ulen = xfrm_replay_state_esn_len(up);
/* Check the overall length and the internal bitmap length to avoid
* potential overflow. */
if (nla_len(rp) < ulen ||
xfrm_replay_state_esn_len(replay_esn) != ulen ||
replay_esn->bmp_len != up->bmp_len)
return -EINVAL;
if (up->replay_window > up->bmp_len * sizeof(__u32) * 8)
return -EINVAL;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Steffen Klassert | 55 | 53.40% | 1 | 25.00% |
Andy Whitcroft | 30 | 29.13% | 2 | 50.00% |
Mathias Krause | 18 | 17.48% | 1 | 25.00% |
Total | 103 | 100.00% | 4 | 100.00% |
static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn,
struct xfrm_replay_state_esn **preplay_esn,
struct nlattr *rta)
{
struct xfrm_replay_state_esn *p, *pp, *up;
int klen, ulen;
if (!rta)
return 0;
up = nla_data(rta);
klen = xfrm_replay_state_esn_len(up);
ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
p = kzalloc(klen, GFP_KERNEL);
if (!p)
return -ENOMEM;
pp = kzalloc(klen, GFP_KERNEL);
if (!pp) {
kfree(p);
return -ENOMEM;
}
memcpy(p, up, ulen);
memcpy(pp, up, ulen);
*replay_esn = p;
*preplay_esn = pp;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Steffen Klassert | 104 | 68.42% | 1 | 50.00% |
Mathias Krause | 48 | 31.58% | 1 | 50.00% |
Total | 152 | 100.00% | 2 | 100.00% |
static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
{
int len = 0;
if (xfrm_ctx) {
len += sizeof(struct xfrm_user_sec_ctx);
len += xfrm_ctx->ctx_len;
}
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Trent Jaeger | 38 | 95.00% | 1 | 50.00% |
Joy Latten | 2 | 5.00% | 1 | 50.00% |
Total | 40 | 100.00% | 2 | 100.00% |
static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
{
memcpy(&x->id, &p->id, sizeof(x->id));
memcpy(&x->sel, &p->sel, sizeof(x->sel));
memcpy(&x->lft, &p->lft, sizeof(x->lft));
x->props.mode = p->mode;
x->props.replay_window = min_t(unsigned int, p->replay_window,
sizeof(x->replay.bitmap) * 8);
x->props.reqid = p->reqid;
x->props.family = p->family;
memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
x->props.flags = p->flags;
if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC))
x->sel.family = p->family;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David S. Miller | 136 | 73.91% | 3 | 33.33% |
Herbert Xu | 27 | 14.67% | 3 | 33.33% |
Fan Du | 9 | 4.89% | 1 | 11.11% |
Steffen Klassert | 9 | 4.89% | 1 | 11.11% |
Patrick McHardy | 3 | 1.63% | 1 | 11.11% |
Total | 184 | 100.00% | 9 | 100.00% |
/*
* someday when pfkey also has support, we could have the code
* somehow made shareable and move it to xfrm_state.c - JHS
*
*/
static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
int update_esn)
{
struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
if (re) {
struct xfrm_replay_state_esn *replay_esn;
replay_esn = nla_data(re);
memcpy(x->replay_esn, replay_esn,
xfrm_replay_state_esn_len(replay_esn));
memcpy(x->preplay_esn, replay_esn,
xfrm_replay_state_esn_len(replay_esn));
}
if (rp) {
struct xfrm_replay_state *replay;
replay = nla_data(rp);
memcpy(&x->replay, replay, sizeof(*replay));
memcpy(&x->preplay, replay, sizeof(*replay));
}
if (lt) {
struct xfrm_lifetime_cur *ltime;
ltime = nla_data(lt);
x->curlft.bytes = ltime->bytes;
x->curlft.packets = ltime->packets;
x->curlft.add_time = ltime->add_time;
x->curlft.use_time = ltime->use_time;
}
if (et)
x->replay_maxage = nla_get_u32(et);
if (rt)
x->replay_maxdiff = nla_get_u32(rt);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jamal Hadi Salim | 176 | 69.29% | 1 | 16.67% |
Steffen Klassert | 56 | 22.05% | 1 | 16.67% |
Thomas Graf | 15 | 5.91% | 3 | 50.00% |
Mathias Krause | 7 | 2.76% | 1 | 16.67% |
Total | 254 | 100.00% | 6 | 100.00% |
static struct xfrm_state *xfrm_state_construct(struct net *net,
struct xfrm_usersa_info *p,
struct nlattr **attrs,
int *errp)
{
struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM;
if (!x)
goto error_no_put;
copy_from_user_state(x, p);
if (attrs[XFRMA_SA_EXTRA_FLAGS])
x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]);
if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD])))
goto error;
if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH_TRUNC])))
goto error;
if (!x->props.aalgo) {
if ((err = attach_auth(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH])))
goto error;
}
if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT])))
goto error;
if ((err = attach_one_algo(&x->calg, &x->props.calgo,
xfrm_calg_get_byname,
attrs[XFRMA_ALG_COMP])))
goto error;
if (attrs[XFRMA_ENCAP]) {
x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]),
sizeof(*x->encap), GFP_KERNEL);
if (x->encap == NULL)
goto error;
}
if (attrs[XFRMA_TFCPAD])
x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]);
if (attrs[XFRMA_COADDR]) {
x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]),
sizeof(*x->coaddr), GFP_KERNEL);
if (x->coaddr == NULL)
goto error;
}
xfrm_mark_get(attrs, &x->mark);
err = __xfrm_init_state(x, false);
if (err)
goto error;
if (attrs[XFRMA_SEC_CTX]) {
err = security_xfrm_state_alloc(x,
nla_data(attrs[XFRMA_SEC_CTX]));
if (err)
goto error;
}
if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn,
attrs[XFRMA_REPLAY_ESN_VAL])))
goto error;
x->km.seq = p->seq;
x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth;
/* sysctl_xfrm_aevent_etime is in 100ms units */
x->replay_maxage = (net->xfrm.sysctl_aevent_etime