Commit 48ff45da authored by Steffen Klassert's avatar Steffen Klassert

Merge branch 'xfrm: add netlink extack for state creation'

Sabrina Dubroca says:

============
This is the second part of my work adding extended acks to XFRM, now
taking care of state creation. Policies were handled in the previous
series [1].
To keep this series at a reasonable size, x->type->init_state will be
handled separately.

[1] https://lkml.kernel.org/r/cover.1661162395.git.sd@queasysnail.net
============
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parents 50c448bb 1cf9a3ae
...@@ -1580,9 +1580,10 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali ...@@ -1580,9 +1580,10 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
int xfrm_init_replay(struct xfrm_state *x); int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack);
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu); u32 xfrm_state_mtu(struct xfrm_state *x, int mtu);
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload); int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
struct netlink_ext_ack *extack);
int xfrm_init_state(struct xfrm_state *x); int xfrm_init_state(struct xfrm_state *x);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr); int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
...@@ -1886,7 +1887,8 @@ void xfrm_dev_resume(struct sk_buff *skb); ...@@ -1886,7 +1887,8 @@ void xfrm_dev_resume(struct sk_buff *skb);
void xfrm_dev_backlog(struct softnet_data *sd); void xfrm_dev_backlog(struct softnet_data *sd);
struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again); struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
struct xfrm_user_offload *xuo); struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack);
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x) static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
...@@ -1949,7 +1951,7 @@ static inline struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_fea ...@@ -1949,7 +1951,7 @@ static inline struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_fea
return skb; return skb;
} }
static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo) static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack)
{ {
return 0; return 0;
} }
......
...@@ -207,7 +207,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur ...@@ -207,7 +207,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
EXPORT_SYMBOL_GPL(validate_xmit_xfrm); EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
struct xfrm_user_offload *xuo) struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{ {
int err; int err;
struct dst_entry *dst; struct dst_entry *dst;
...@@ -216,15 +217,21 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, ...@@ -216,15 +217,21 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
xfrm_address_t *saddr; xfrm_address_t *saddr;
xfrm_address_t *daddr; xfrm_address_t *daddr;
if (!x->type_offload) if (!x->type_offload) {
NL_SET_ERR_MSG(extack, "Type doesn't support offload");
return -EINVAL; return -EINVAL;
}
/* We don't yet support UDP encapsulation and TFC padding. */ /* We don't yet support UDP encapsulation and TFC padding. */
if (x->encap || x->tfcpad) if (x->encap || x->tfcpad) {
NL_SET_ERR_MSG(extack, "Encapsulation and TFC padding can't be offloaded");
return -EINVAL; return -EINVAL;
}
if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) {
NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request");
return -EINVAL; return -EINVAL;
}
dev = dev_get_by_index(net, xuo->ifindex); dev = dev_get_by_index(net, xuo->ifindex);
if (!dev) { if (!dev) {
...@@ -256,6 +263,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, ...@@ -256,6 +263,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
if (x->props.flags & XFRM_STATE_ESN && if (x->props.flags & XFRM_STATE_ESN &&
!dev->xfrmdev_ops->xdo_dev_state_advance_esn) { !dev->xfrmdev_ops->xdo_dev_state_advance_esn) {
NL_SET_ERR_MSG(extack, "Device doesn't support offload with ESN");
xso->dev = NULL; xso->dev = NULL;
dev_put(dev); dev_put(dev);
return -EINVAL; return -EINVAL;
...@@ -277,9 +285,11 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, ...@@ -277,9 +285,11 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
xso->real_dev = NULL; xso->real_dev = NULL;
netdev_put(dev, &xso->dev_tracker); netdev_put(dev, &xso->dev_tracker);
if (err != -EOPNOTSUPP) if (err != -EOPNOTSUPP) {
NL_SET_ERR_MSG(extack, "Device failed to offload this state");
return err; return err;
} }
}
return 0; return 0;
} }
......
...@@ -766,18 +766,22 @@ int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) ...@@ -766,18 +766,22 @@ int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
} }
#endif #endif
int xfrm_init_replay(struct xfrm_state *x) int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
struct xfrm_replay_state_esn *replay_esn = x->replay_esn; struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
if (replay_esn) { if (replay_esn) {
if (replay_esn->replay_window > if (replay_esn->replay_window >
replay_esn->bmp_len * sizeof(__u32) * 8) replay_esn->bmp_len * sizeof(__u32) * 8) {
NL_SET_ERR_MSG(extack, "ESN replay window is too large for the chosen bitmap size");
return -EINVAL; return -EINVAL;
}
if (x->props.flags & XFRM_STATE_ESN) { if (x->props.flags & XFRM_STATE_ESN) {
if (replay_esn->replay_window == 0) if (replay_esn->replay_window == 0) {
NL_SET_ERR_MSG(extack, "ESN replay window must be > 0");
return -EINVAL; return -EINVAL;
}
x->repl_mode = XFRM_REPLAY_MODE_ESN; x->repl_mode = XFRM_REPLAY_MODE_ESN;
} else { } else {
x->repl_mode = XFRM_REPLAY_MODE_BMP; x->repl_mode = XFRM_REPLAY_MODE_BMP;
......
...@@ -2610,7 +2610,8 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) ...@@ -2610,7 +2610,8 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
} }
EXPORT_SYMBOL_GPL(xfrm_state_mtu); EXPORT_SYMBOL_GPL(xfrm_state_mtu);
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
struct netlink_ext_ack *extack)
{ {
const struct xfrm_mode *inner_mode; const struct xfrm_mode *inner_mode;
const struct xfrm_mode *outer_mode; const struct xfrm_mode *outer_mode;
...@@ -2625,12 +2626,16 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) ...@@ -2625,12 +2626,16 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
if (x->sel.family != AF_UNSPEC) { if (x->sel.family != AF_UNSPEC) {
inner_mode = xfrm_get_mode(x->props.mode, x->sel.family); inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
if (inner_mode == NULL) if (inner_mode == NULL) {
NL_SET_ERR_MSG(extack, "Requested mode not found");
goto error; goto error;
}
if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) && if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
family != x->sel.family) family != x->sel.family) {
NL_SET_ERR_MSG(extack, "Only tunnel modes can accommodate a change of family");
goto error; goto error;
}
x->inner_mode = *inner_mode; x->inner_mode = *inner_mode;
} else { } else {
...@@ -2638,11 +2643,15 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) ...@@ -2638,11 +2643,15 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
int iafamily = AF_INET; int iafamily = AF_INET;
inner_mode = xfrm_get_mode(x->props.mode, x->props.family); inner_mode = xfrm_get_mode(x->props.mode, x->props.family);
if (inner_mode == NULL) if (inner_mode == NULL) {
NL_SET_ERR_MSG(extack, "Requested mode not found");
goto error; goto error;
}
if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
NL_SET_ERR_MSG(extack, "Only tunnel modes can accommodate an AF_UNSPEC selector");
goto error; goto error;
}
x->inner_mode = *inner_mode; x->inner_mode = *inner_mode;
...@@ -2657,8 +2666,10 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) ...@@ -2657,8 +2666,10 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
} }
x->type = xfrm_get_type(x->id.proto, family); x->type = xfrm_get_type(x->id.proto, family);
if (x->type == NULL) if (x->type == NULL) {
NL_SET_ERR_MSG(extack, "Requested type not found");
goto error; goto error;
}
x->type_offload = xfrm_get_type_offload(x->id.proto, family, offload); x->type_offload = xfrm_get_type_offload(x->id.proto, family, offload);
...@@ -2668,13 +2679,14 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) ...@@ -2668,13 +2679,14 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
outer_mode = xfrm_get_mode(x->props.mode, family); outer_mode = xfrm_get_mode(x->props.mode, family);
if (!outer_mode) { if (!outer_mode) {
NL_SET_ERR_MSG(extack, "Requested mode not found");
err = -EPROTONOSUPPORT; err = -EPROTONOSUPPORT;
goto error; goto error;
} }
x->outer_mode = *outer_mode; x->outer_mode = *outer_mode;
if (init_replay) { if (init_replay) {
err = xfrm_init_replay(x); err = xfrm_init_replay(x, extack);
if (err) if (err)
goto error; goto error;
} }
...@@ -2689,7 +2701,7 @@ int xfrm_init_state(struct xfrm_state *x) ...@@ -2689,7 +2701,7 @@ int xfrm_init_state(struct xfrm_state *x)
{ {
int err; int err;
err = __xfrm_init_state(x, true, false); err = __xfrm_init_state(x, true, false, NULL);
if (!err) if (!err)
x->km.state = XFRM_STATE_VALID; x->km.state = XFRM_STATE_VALID;
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
#endif #endif
#include <asm/unaligned.h> #include <asm/unaligned.h>
static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[type]; struct nlattr *rt = attrs[type];
struct xfrm_algo *algp; struct xfrm_algo *algp;
...@@ -44,8 +45,10 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) ...@@ -44,8 +45,10 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
return 0; return 0;
algp = nla_data(rt); algp = nla_data(rt);
if (nla_len(rt) < (int)xfrm_alg_len(algp)) if (nla_len(rt) < (int)xfrm_alg_len(algp)) {
NL_SET_ERR_MSG(extack, "Invalid AUTH/CRYPT/COMP attribute length");
return -EINVAL; return -EINVAL;
}
switch (type) { switch (type) {
case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH:
...@@ -54,6 +57,7 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) ...@@ -54,6 +57,7 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid algorithm attribute type");
return -EINVAL; return -EINVAL;
} }
...@@ -61,7 +65,8 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) ...@@ -61,7 +65,8 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
return 0; return 0;
} }
static int verify_auth_trunc(struct nlattr **attrs) static int verify_auth_trunc(struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC];
struct xfrm_algo_auth *algp; struct xfrm_algo_auth *algp;
...@@ -70,14 +75,16 @@ static int verify_auth_trunc(struct nlattr **attrs) ...@@ -70,14 +75,16 @@ static int verify_auth_trunc(struct nlattr **attrs)
return 0; return 0;
algp = nla_data(rt); algp = nla_data(rt);
if (nla_len(rt) < (int)xfrm_alg_auth_len(algp)) if (nla_len(rt) < (int)xfrm_alg_auth_len(algp)) {
NL_SET_ERR_MSG(extack, "Invalid AUTH_TRUNC attribute length");
return -EINVAL; return -EINVAL;
}
algp->alg_name[sizeof(algp->alg_name) - 1] = '\0'; algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
return 0; return 0;
} }
static int verify_aead(struct nlattr **attrs) static int verify_aead(struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; struct nlattr *rt = attrs[XFRMA_ALG_AEAD];
struct xfrm_algo_aead *algp; struct xfrm_algo_aead *algp;
...@@ -86,8 +93,10 @@ static int verify_aead(struct nlattr **attrs) ...@@ -86,8 +93,10 @@ static int verify_aead(struct nlattr **attrs)
return 0; return 0;
algp = nla_data(rt); algp = nla_data(rt);
if (nla_len(rt) < (int)aead_len(algp)) if (nla_len(rt) < (int)aead_len(algp)) {
NL_SET_ERR_MSG(extack, "Invalid AEAD attribute length");
return -EINVAL; return -EINVAL;
}
algp->alg_name[sizeof(algp->alg_name) - 1] = '\0'; algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
return 0; return 0;
...@@ -121,35 +130,50 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs, struct netlink_ext_a ...@@ -121,35 +130,50 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs, struct netlink_ext_a
} }
static inline int verify_replay(struct xfrm_usersa_info *p, static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
struct xfrm_replay_state_esn *rs; struct xfrm_replay_state_esn *rs;
if (!rt) if (!rt) {
return (p->flags & XFRM_STATE_ESN) ? -EINVAL : 0; if (p->flags & XFRM_STATE_ESN) {
NL_SET_ERR_MSG(extack, "Missing required attribute for ESN");
return -EINVAL;
}
return 0;
}
rs = nla_data(rt); rs = nla_data(rt);
if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) {
NL_SET_ERR_MSG(extack, "ESN bitmap length must be <= 128");
return -EINVAL; return -EINVAL;
}
if (nla_len(rt) < (int)xfrm_replay_state_esn_len(rs) && if (nla_len(rt) < (int)xfrm_replay_state_esn_len(rs) &&
nla_len(rt) != sizeof(*rs)) nla_len(rt) != sizeof(*rs)) {
NL_SET_ERR_MSG(extack, "ESN attribute is too short to fit the full bitmap length");
return -EINVAL; return -EINVAL;
}
/* As only ESP and AH support ESN feature. */ /* As only ESP and AH support ESN feature. */
if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) {
NL_SET_ERR_MSG(extack, "ESN only supported for ESP and AH");
return -EINVAL; return -EINVAL;
}
if (p->replay_window != 0) if (p->replay_window != 0) {
NL_SET_ERR_MSG(extack, "ESN not compatible with legacy replay_window");
return -EINVAL; return -EINVAL;
}
return 0; return 0;
} }
static int verify_newsa_info(struct xfrm_usersa_info *p, static int verify_newsa_info(struct xfrm_usersa_info *p,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
int err; int err;
...@@ -163,10 +187,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -163,10 +187,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
break; break;
#else #else
err = -EAFNOSUPPORT; err = -EAFNOSUPPORT;
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
goto out; goto out;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Invalid address family");
goto out; goto out;
} }
...@@ -175,65 +201,98 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -175,65 +201,98 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
break; break;
case AF_INET: case AF_INET:
if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
goto out; goto out;
}
break; break;
case AF_INET6: case AF_INET6:
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
goto out; goto out;
}
break; break;
#else #else
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
err = -EAFNOSUPPORT; err = -EAFNOSUPPORT;
goto out; goto out;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Invalid address family in selector");
goto out; goto out;
} }
err = -EINVAL; err = -EINVAL;
switch (p->id.proto) { switch (p->id.proto) {
case IPPROTO_AH: case IPPROTO_AH:
if ((!attrs[XFRMA_ALG_AUTH] && if (!attrs[XFRMA_ALG_AUTH] &&
!attrs[XFRMA_ALG_AUTH_TRUNC]) || !attrs[XFRMA_ALG_AUTH_TRUNC]) {
attrs[XFRMA_ALG_AEAD] || NL_SET_ERR_MSG(extack, "Missing required attribute for AH: AUTH_TRUNC or AUTH");
goto out;
}
if (attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_CRYPT] || attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_ALG_COMP] || attrs[XFRMA_ALG_COMP] ||
attrs[XFRMA_TFCPAD]) attrs[XFRMA_TFCPAD]) {
NL_SET_ERR_MSG(extack, "Invalid attributes for AH: AEAD, CRYPT, COMP, TFCPAD");
goto out; goto out;
}
break; break;
case IPPROTO_ESP: case IPPROTO_ESP:
if (attrs[XFRMA_ALG_COMP]) if (attrs[XFRMA_ALG_COMP]) {
NL_SET_ERR_MSG(extack, "Invalid attribute for ESP: COMP");
goto out; goto out;
}
if (!attrs[XFRMA_ALG_AUTH] && if (!attrs[XFRMA_ALG_AUTH] &&
!attrs[XFRMA_ALG_AUTH_TRUNC] && !attrs[XFRMA_ALG_AUTH_TRUNC] &&
!attrs[XFRMA_ALG_CRYPT] && !attrs[XFRMA_ALG_CRYPT] &&
!attrs[XFRMA_ALG_AEAD]) !attrs[XFRMA_ALG_AEAD]) {
NL_SET_ERR_MSG(extack, "Missing required attribute for ESP: at least one of AUTH, AUTH_TRUNC, CRYPT, AEAD");
goto out; goto out;
}
if ((attrs[XFRMA_ALG_AUTH] || if ((attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] || attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_CRYPT]) && attrs[XFRMA_ALG_CRYPT]) &&
attrs[XFRMA_ALG_AEAD]) attrs[XFRMA_ALG_AEAD]) {
NL_SET_ERR_MSG(extack, "Invalid attribute combination for ESP: AEAD can't be used with AUTH, AUTH_TRUNC, CRYPT");
goto out; goto out;
}
if (attrs[XFRMA_TFCPAD] && if (attrs[XFRMA_TFCPAD] &&
p->mode != XFRM_MODE_TUNNEL) p->mode != XFRM_MODE_TUNNEL) {
NL_SET_ERR_MSG(extack, "TFC padding can only be used in tunnel mode");
goto out; goto out;
}
break; break;
case IPPROTO_COMP: case IPPROTO_COMP:
if (!attrs[XFRMA_ALG_COMP] || if (!attrs[XFRMA_ALG_COMP]) {
attrs[XFRMA_ALG_AEAD] || NL_SET_ERR_MSG(extack, "Missing required attribute for COMP: COMP");
goto out;
}
if (attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_AUTH] || attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] || attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_CRYPT] || attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_TFCPAD] || attrs[XFRMA_TFCPAD]) {
(ntohl(p->id.spi) >= 0x10000)) NL_SET_ERR_MSG(extack, "Invalid attributes for COMP: AEAD, AUTH, AUTH_TRUNC, CRYPT, TFCPAD");
goto out; goto out;
}
if (ntohl(p->id.spi) >= 0x10000) {
NL_SET_ERR_MSG(extack, "SPI is too large for COMP (must be < 0x10000)");
goto out;
}
break; break;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
...@@ -246,29 +305,36 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -246,29 +305,36 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
attrs[XFRMA_ALG_CRYPT] || attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_ENCAP] || attrs[XFRMA_ENCAP] ||
attrs[XFRMA_SEC_CTX] || attrs[XFRMA_SEC_CTX] ||
attrs[XFRMA_TFCPAD] || attrs[XFRMA_TFCPAD]) {
!attrs[XFRMA_COADDR]) NL_SET_ERR_MSG(extack, "Invalid attributes for DSTOPTS/ROUTING");
goto out; goto out;
}
if (!attrs[XFRMA_COADDR]) {
NL_SET_ERR_MSG(extack, "Missing required COADDR attribute for DSTOPTS/ROUTING");
goto out;
}
break; break;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Unsupported protocol");
goto out; goto out;
} }
if ((err = verify_aead(attrs))) if ((err = verify_aead(attrs, extack)))
goto out; goto out;
if ((err = verify_auth_trunc(attrs))) if ((err = verify_auth_trunc(attrs, extack)))
goto out; goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH, extack)))
goto out; goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT, extack)))
goto out; goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP, extack)))
goto out; goto out;
if ((err = verify_sec_ctx_len(attrs, NULL))) if ((err = verify_sec_ctx_len(attrs, extack)))
goto out; goto out;
if ((err = verify_replay(p, attrs))) if ((err = verify_replay(p, attrs, extack)))
goto out; goto out;
err = -EINVAL; err = -EINVAL;
...@@ -280,14 +346,19 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -280,14 +346,19 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Unsupported mode");
goto out; goto out;
} }
err = 0; err = 0;
if (attrs[XFRMA_MTIMER_THRESH]) if (attrs[XFRMA_MTIMER_THRESH]) {
if (!attrs[XFRMA_ENCAP]) if (!attrs[XFRMA_ENCAP]) {
NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");
err = -EINVAL; err = -EINVAL;
goto out;
}
}
out: out:
return err; return err;
...@@ -295,7 +366,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -295,7 +366,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
struct xfrm_algo_desc *(*get_byname)(const char *, int), struct xfrm_algo_desc *(*get_byname)(const char *, int),
struct nlattr *rta) struct nlattr *rta, struct netlink_ext_ack *extack)
{ {
struct xfrm_algo *p, *ualg; struct xfrm_algo *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -306,8 +377,10 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, ...@@ -306,8 +377,10 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
ualg = nla_data(rta); ualg = nla_data(rta);
algo = get_byname(ualg->alg_name, 1); algo = get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested COMP algorithm not found");
return -ENOSYS; return -ENOSYS;
}
*props = algo->desc.sadb_alg_id; *props = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
...@@ -319,7 +392,8 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, ...@@ -319,7 +392,8 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
return 0; return 0;
} }
static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) static int attach_crypt(struct xfrm_state *x, struct nlattr *rta,
struct netlink_ext_ack *extack)
{ {
struct xfrm_algo *p, *ualg; struct xfrm_algo *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -330,8 +404,10 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) ...@@ -330,8 +404,10 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_ealg_get_byname(ualg->alg_name, 1); algo = xfrm_ealg_get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested CRYPT algorithm not found");
return -ENOSYS; return -ENOSYS;
}
x->props.ealgo = algo->desc.sadb_alg_id; x->props.ealgo = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
...@@ -345,7 +421,7 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) ...@@ -345,7 +421,7 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
} }
static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
struct nlattr *rta) struct nlattr *rta, struct netlink_ext_ack *extack)
{ {
struct xfrm_algo *ualg; struct xfrm_algo *ualg;
struct xfrm_algo_auth *p; struct xfrm_algo_auth *p;
...@@ -357,8 +433,10 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -357,8 +433,10 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_aalg_get_byname(ualg->alg_name, 1); algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested AUTH algorithm not found");
return -ENOSYS; return -ENOSYS;
}
*props = algo->desc.sadb_alg_id; *props = algo->desc.sadb_alg_id;
p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL);
...@@ -375,7 +453,7 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -375,7 +453,7 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
} }
static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
struct nlattr *rta) struct nlattr *rta, struct netlink_ext_ack *extack)
{ {
struct xfrm_algo_auth *p, *ualg; struct xfrm_algo_auth *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -386,10 +464,14 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -386,10 +464,14 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_aalg_get_byname(ualg->alg_name, 1); algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested AUTH_TRUNC algorithm not found");
return -ENOSYS; return -ENOSYS;
if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) }
if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) {
NL_SET_ERR_MSG(extack, "Invalid length requested for truncated ICV");
return -EINVAL; return -EINVAL;
}
*props = algo->desc.sadb_alg_id; *props = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL);
...@@ -404,7 +486,8 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -404,7 +486,8 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
return 0; return 0;
} }
static int attach_aead(struct xfrm_state *x, struct nlattr *rta) static int attach_aead(struct xfrm_state *x, struct nlattr *rta,
struct netlink_ext_ack *extack)
{ {
struct xfrm_algo_aead *p, *ualg; struct xfrm_algo_aead *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -415,8 +498,10 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta) ...@@ -415,8 +498,10 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta)
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested AEAD algorithm not found");
return -ENOSYS; return -ENOSYS;
}
x->props.ealgo = algo->desc.sadb_alg_id; x->props.ealgo = algo->desc.sadb_alg_id;
p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
...@@ -581,7 +666,8 @@ static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m) ...@@ -581,7 +666,8 @@ static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m)
static struct xfrm_state *xfrm_state_construct(struct net *net, static struct xfrm_state *xfrm_state_construct(struct net *net,
struct xfrm_usersa_info *p, struct xfrm_usersa_info *p,
struct nlattr **attrs, struct nlattr **attrs,
int *errp) int *errp,
struct netlink_ext_ack *extack)
{ {
struct xfrm_state *x = xfrm_state_alloc(net); struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM; int err = -ENOMEM;
...@@ -608,21 +694,21 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -608,21 +694,21 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (attrs[XFRMA_SA_EXTRA_FLAGS]) if (attrs[XFRMA_SA_EXTRA_FLAGS])
x->props.extra_flags = nla_get_u32(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]))) if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD], extack)))
goto error; goto error;
if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH_TRUNC]))) attrs[XFRMA_ALG_AUTH_TRUNC], extack)))
goto error; goto error;
if (!x->props.aalgo) { if (!x->props.aalgo) {
if ((err = attach_auth(&x->aalg, &x->props.aalgo, if ((err = attach_auth(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH]))) attrs[XFRMA_ALG_AUTH], extack)))
goto error; goto error;
} }
if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT]))) if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT], extack)))
goto error; goto error;
if ((err = attach_one_algo(&x->calg, &x->props.calgo, if ((err = attach_one_algo(&x->calg, &x->props.calgo,
xfrm_calg_get_byname, xfrm_calg_get_byname,
attrs[XFRMA_ALG_COMP]))) attrs[XFRMA_ALG_COMP], extack)))
goto error; goto error;
if (attrs[XFRMA_TFCPAD]) if (attrs[XFRMA_TFCPAD])
...@@ -635,7 +721,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -635,7 +721,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (attrs[XFRMA_IF_ID]) if (attrs[XFRMA_IF_ID])
x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]); err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
if (err) if (err)
goto error; goto error;
...@@ -655,7 +741,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -655,7 +741,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
/* sysctl_xfrm_aevent_etime is in 100ms units */ /* sysctl_xfrm_aevent_etime is in 100ms units */
x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M;
if ((err = xfrm_init_replay(x))) if ((err = xfrm_init_replay(x, extack)))
goto error; goto error;
/* override default values from above */ /* override default values from above */
...@@ -664,7 +750,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -664,7 +750,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
/* configure the hardware if offload is requested */ /* configure the hardware if offload is requested */
if (attrs[XFRMA_OFFLOAD_DEV]) { if (attrs[XFRMA_OFFLOAD_DEV]) {
err = xfrm_dev_state_add(net, x, err = xfrm_dev_state_add(net, x,
nla_data(attrs[XFRMA_OFFLOAD_DEV])); nla_data(attrs[XFRMA_OFFLOAD_DEV]),
extack);
if (err) if (err)
goto error; goto error;
} }
...@@ -688,11 +775,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -688,11 +775,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
int err; int err;
struct km_event c; struct km_event c;
err = verify_newsa_info(p, attrs); err = verify_newsa_info(p, attrs, extack);
if (err) if (err)
return err; return err;
x = xfrm_state_construct(net, p, attrs, &err); x = xfrm_state_construct(net, p, attrs, &err, extack);
if (!x) if (!x)
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment