diff options
| -rw-r--r-- | include/net/xfrm.h | 10 | ||||
| -rw-r--r-- | net/xfrm/xfrm_device.c | 20 | ||||
| -rw-r--r-- | net/xfrm/xfrm_replay.c | 10 | ||||
| -rw-r--r-- | net/xfrm/xfrm_state.c | 28 | ||||
| -rw-r--r-- | net/xfrm/xfrm_user.c | 209 | 
5 files changed, 196 insertions, 81 deletions
| diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 28b988577ed2..c504d07bcb7c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -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_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);  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); -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_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);  int xfrm_input_resume(struct sk_buff *skb, int nexthdr); @@ -1886,7 +1887,8 @@ void xfrm_dev_resume(struct sk_buff *skb);  void xfrm_dev_backlog(struct softnet_data *sd);  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, -		       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);  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  	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;  } diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 637ca8838436..5f5aafd418af 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -207,7 +207,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur  EXPORT_SYMBOL_GPL(validate_xmit_xfrm);  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;  	struct dst_entry *dst; @@ -216,15 +217,21 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,  	xfrm_address_t *saddr;  	xfrm_address_t *daddr; -	if (!x->type_offload) +	if (!x->type_offload) { +		NL_SET_ERR_MSG(extack, "Type doesn't support offload");  		return -EINVAL; +	}  	/* 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; +	} -	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; +	}  	dev = dev_get_by_index(net, xuo->ifindex);  	if (!dev) { @@ -256,6 +263,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,  	if (x->props.flags & XFRM_STATE_ESN &&  	    !dev->xfrmdev_ops->xdo_dev_state_advance_esn) { +		NL_SET_ERR_MSG(extack, "Device doesn't support offload with ESN");  		xso->dev = NULL;  		dev_put(dev);  		return -EINVAL; @@ -277,8 +285,10 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,  		xso->real_dev = NULL;  		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 0; diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 9277d81b344c..9f4d42eb090f 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -766,18 +766,22 @@ int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)  }  #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;  	if (replay_esn) {  		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; +		}  		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; +			}  			x->repl_mode = XFRM_REPLAY_MODE_ESN;  		} else {  			x->repl_mode = XFRM_REPLAY_MODE_BMP; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 52e60e607f8a..0b59ff7985e6 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2610,7 +2610,8 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int 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 *outer_mode; @@ -2625,12 +2626,16 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)  	if (x->sel.family != AF_UNSPEC) {  		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; +		}  		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; +		}  		x->inner_mode = *inner_mode;  	} else { @@ -2638,11 +2643,15 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)  		int iafamily = AF_INET;  		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; +		} -		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; +		}  		x->inner_mode = *inner_mode; @@ -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); -	if (x->type == NULL) +	if (x->type == NULL) { +		NL_SET_ERR_MSG(extack, "Requested type not found");  		goto error; +	}  	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)  	outer_mode = xfrm_get_mode(x->props.mode, family);  	if (!outer_mode) { +		NL_SET_ERR_MSG(extack, "Requested mode not found");  		err = -EPROTONOSUPPORT;  		goto error;  	}  	x->outer_mode = *outer_mode;  	if (init_replay) { -		err = xfrm_init_replay(x); +		err = xfrm_init_replay(x, extack);  		if (err)  			goto error;  	} @@ -2689,7 +2701,7 @@ int xfrm_init_state(struct xfrm_state *x)  {  	int err; -	err = __xfrm_init_state(x, true, false); +	err = __xfrm_init_state(x, true, false, NULL);  	if (!err)  		x->km.state = XFRM_STATE_VALID; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 772a051feedb..e73f9efc54c1 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -35,7 +35,8 @@  #endif  #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 xfrm_algo *algp; @@ -44,8 +45,10 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)  		return 0;  	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; +	}  	switch (type) {  	case XFRMA_ALG_AUTH: @@ -54,6 +57,7 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)  		break;  	default: +		NL_SET_ERR_MSG(extack, "Invalid algorithm attribute type");  		return -EINVAL;  	} @@ -61,7 +65,8 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)  	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 xfrm_algo_auth *algp; @@ -70,14 +75,16 @@ static int verify_auth_trunc(struct nlattr **attrs)  		return 0;  	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; +	}  	algp->alg_name[sizeof(algp->alg_name) - 1] = '\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 xfrm_algo_aead *algp; @@ -86,8 +93,10 @@ static int verify_aead(struct nlattr **attrs)  		return 0;  	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; +	}  	algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';  	return 0; @@ -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, -				struct nlattr **attrs) +				struct nlattr **attrs, +				struct netlink_ext_ack *extack)  {  	struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];  	struct xfrm_replay_state_esn *rs; -	if (!rt) -		return (p->flags & XFRM_STATE_ESN) ? -EINVAL : 0; +	if (!rt) { +		if (p->flags & XFRM_STATE_ESN) { +			NL_SET_ERR_MSG(extack, "Missing required attribute for ESN"); +			return -EINVAL; +		} +		return 0; +	}  	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; +	}  	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; +	}  	/* 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; +	} -	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 0;  }  static int verify_newsa_info(struct xfrm_usersa_info *p, -			     struct nlattr **attrs) +			     struct nlattr **attrs, +			     struct netlink_ext_ack *extack)  {  	int err; @@ -163,10 +187,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,  		break;  #else  		err = -EAFNOSUPPORT; +		NL_SET_ERR_MSG(extack, "IPv6 support disabled");  		goto out;  #endif  	default: +		NL_SET_ERR_MSG(extack, "Invalid address family");  		goto out;  	} @@ -175,65 +201,98 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,  		break;  	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; +		}  		break;  	case AF_INET6:  #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; +		}  		break;  #else +		NL_SET_ERR_MSG(extack, "IPv6 support disabled");  		err = -EAFNOSUPPORT;  		goto out;  #endif  	default: +		NL_SET_ERR_MSG(extack, "Invalid address family in selector");  		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]	|| +		if (!attrs[XFRMA_ALG_AUTH]	&& +		    !attrs[XFRMA_ALG_AUTH_TRUNC]) { +			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_COMP]	|| -		    attrs[XFRMA_TFCPAD]) +		    attrs[XFRMA_TFCPAD]) { +			NL_SET_ERR_MSG(extack, "Invalid attributes for AH: AEAD, CRYPT, COMP, TFCPAD");  			goto out; +		}  		break;  	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; +		} +  		if (!attrs[XFRMA_ALG_AUTH] &&  		    !attrs[XFRMA_ALG_AUTH_TRUNC] &&  		    !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; +		} +  		if ((attrs[XFRMA_ALG_AUTH] ||  		     attrs[XFRMA_ALG_AUTH_TRUNC] ||  		     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; +		} +  		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; +		}  		break;  	case IPPROTO_COMP: -		if (!attrs[XFRMA_ALG_COMP]	|| -		    attrs[XFRMA_ALG_AEAD]	|| +		if (!attrs[XFRMA_ALG_COMP]) { +			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_TRUNC]	||  		    attrs[XFRMA_ALG_CRYPT]	|| -		    attrs[XFRMA_TFCPAD]		|| -		    (ntohl(p->id.spi) >= 0x10000)) +		    attrs[XFRMA_TFCPAD]) { +			NL_SET_ERR_MSG(extack, "Invalid attributes for COMP: AEAD, AUTH, AUTH_TRUNC, CRYPT, TFCPAD"); +			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;  #if IS_ENABLED(CONFIG_IPV6) @@ -246,29 +305,36 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,  		    attrs[XFRMA_ALG_CRYPT]	||  		    attrs[XFRMA_ENCAP]		||  		    attrs[XFRMA_SEC_CTX]	|| -		    attrs[XFRMA_TFCPAD]		|| -		    !attrs[XFRMA_COADDR]) +		    attrs[XFRMA_TFCPAD]) { +			NL_SET_ERR_MSG(extack, "Invalid attributes for DSTOPTS/ROUTING"); +			goto out; +		} + +		if (!attrs[XFRMA_COADDR]) { +			NL_SET_ERR_MSG(extack, "Missing required COADDR attribute for DSTOPTS/ROUTING");  			goto out; +		}  		break;  #endif  	default: +		NL_SET_ERR_MSG(extack, "Unsupported protocol");  		goto out;  	} -	if ((err = verify_aead(attrs))) +	if ((err = verify_aead(attrs, extack)))  		goto out; -	if ((err = verify_auth_trunc(attrs))) +	if ((err = verify_auth_trunc(attrs, extack)))  		goto out; -	if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) +	if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH, extack)))  		goto out; -	if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) +	if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT, extack)))  		goto out; -	if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) +	if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP, extack)))  		goto out; -	if ((err = verify_sec_ctx_len(attrs, NULL))) +	if ((err = verify_sec_ctx_len(attrs, extack)))  		goto out; -	if ((err = verify_replay(p, attrs))) +	if ((err = verify_replay(p, attrs, extack)))  		goto out;  	err = -EINVAL; @@ -280,14 +346,19 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,  		break;  	default: +		NL_SET_ERR_MSG(extack, "Unsupported mode");  		goto out;  	}  	err = 0; -	if (attrs[XFRMA_MTIMER_THRESH]) -		if (!attrs[XFRMA_ENCAP]) +	if (attrs[XFRMA_MTIMER_THRESH]) { +		if (!attrs[XFRMA_ENCAP]) { +			NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");  			err = -EINVAL; +			goto out; +		} +	}  out:  	return err; @@ -295,7 +366,7 @@ out:  static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,  			   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_desc *algo; @@ -306,8 +377,10 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,  	ualg = nla_data(rta);  	algo = get_byname(ualg->alg_name, 1); -	if (!algo) +	if (!algo) { +		NL_SET_ERR_MSG(extack, "Requested COMP algorithm not found");  		return -ENOSYS; +	}  	*props = algo->desc.sadb_alg_id;  	p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); @@ -319,7 +392,8 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,  	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_desc *algo; @@ -330,8 +404,10 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)  	ualg = nla_data(rta);  	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; +	}  	x->props.ealgo = algo->desc.sadb_alg_id;  	p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); @@ -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, -		       struct nlattr *rta) +		       struct nlattr *rta, struct netlink_ext_ack *extack)  {  	struct xfrm_algo *ualg;  	struct xfrm_algo_auth *p; @@ -357,8 +433,10 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,  	ualg = nla_data(rta);  	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; +	}  	*props = algo->desc.sadb_alg_id;  	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,  }  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_desc *algo; @@ -386,10 +464,14 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,  	ualg = nla_data(rta);  	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; -	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; +	}  	*props = algo->desc.sadb_alg_id;  	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,  	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_desc *algo; @@ -415,8 +498,10 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta)  	ualg = nla_data(rta);  	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; +	}  	x->props.ealgo = algo->desc.sadb_alg_id;  	p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); @@ -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,  					       struct xfrm_usersa_info *p,  					       struct nlattr **attrs, -					       int *errp) +					       int *errp, +					       struct netlink_ext_ack *extack)  {  	struct xfrm_state *x = xfrm_state_alloc(net);  	int err = -ENOMEM; @@ -608,21 +694,21 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,  	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]))) +	if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD], extack)))  		goto error;  	if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, -				     attrs[XFRMA_ALG_AUTH_TRUNC]))) +				     attrs[XFRMA_ALG_AUTH_TRUNC], extack)))  		goto error;  	if (!x->props.aalgo) {  		if ((err = attach_auth(&x->aalg, &x->props.aalgo, -				       attrs[XFRMA_ALG_AUTH]))) +				       attrs[XFRMA_ALG_AUTH], extack)))  			goto error;  	} -	if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT]))) +	if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT], extack)))  		goto error;  	if ((err = attach_one_algo(&x->calg, &x->props.calgo,  				   xfrm_calg_get_byname, -				   attrs[XFRMA_ALG_COMP]))) +				   attrs[XFRMA_ALG_COMP], extack)))  		goto error;  	if (attrs[XFRMA_TFCPAD]) @@ -635,7 +721,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,  	if (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)  		goto error; @@ -655,7 +741,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,  	/* sysctl_xfrm_aevent_etime is in 100ms units */  	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;  	/* override default values from above */ @@ -664,7 +750,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,  	/* configure the hardware if offload is requested */  	if (attrs[XFRMA_OFFLOAD_DEV]) {  		err = xfrm_dev_state_add(net, x, -					 nla_data(attrs[XFRMA_OFFLOAD_DEV])); +					 nla_data(attrs[XFRMA_OFFLOAD_DEV]), +					 extack);  		if (err)  			goto error;  	} @@ -688,11 +775,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,  	int err;  	struct km_event c; -	err = verify_newsa_info(p, attrs); +	err = verify_newsa_info(p, attrs, extack);  	if (err)  		return err; -	x = xfrm_state_construct(net, p, attrs, &err); +	x = xfrm_state_construct(net, p, attrs, &err, extack);  	if (!x)  		return err; | 
