1169 lines · 27.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * NETLINK      Netlink attributes * * 		Authors:	Thomas Graf <tgraf@suug.ch> * 				Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> */ #include <linux/export.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/jiffies.h>#include <linux/nospec.h>#include <linux/skbuff.h>#include <linux/string.h>#include <linux/types.h>#include <net/netlink.h> /* For these data types, attribute length should be exactly the given * size. However, to maintain compatibility with broken commands, if the * attribute length does not match the expected size a warning is emitted * to the user that the command is sending invalid data and needs to be fixed. */static const u8 nla_attr_len[NLA_TYPE_MAX+1] = {	[NLA_U8]	= sizeof(u8),	[NLA_U16]	= sizeof(u16),	[NLA_U32]	= sizeof(u32),	[NLA_U64]	= sizeof(u64),	[NLA_S8]	= sizeof(s8),	[NLA_S16]	= sizeof(s16),	[NLA_S32]	= sizeof(s32),	[NLA_S64]	= sizeof(s64),	[NLA_BE16]	= sizeof(__be16),	[NLA_BE32]	= sizeof(__be32),}; static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {	[NLA_U8]	= sizeof(u8),	[NLA_U16]	= sizeof(u16),	[NLA_U32]	= sizeof(u32),	[NLA_U64]	= sizeof(u64),	[NLA_MSECS]	= sizeof(u64),	[NLA_NESTED]	= NLA_HDRLEN,	[NLA_S8]	= sizeof(s8),	[NLA_S16]	= sizeof(s16),	[NLA_S32]	= sizeof(s32),	[NLA_S64]	= sizeof(s64),	[NLA_BE16]	= sizeof(__be16),	[NLA_BE32]	= sizeof(__be32),}; /* * Nested policies might refer back to the original * policy in some cases, and userspace could try to * abuse that and recurse by nesting in the right * ways. Limit recursion to avoid this problem. */#define MAX_POLICY_RECURSION_DEPTH	10 static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,				const struct nla_policy *policy,				unsigned int validate,				struct netlink_ext_ack *extack,				struct nlattr **tb, unsigned int depth); static int validate_nla_bitfield32(const struct nlattr *nla,				   const u32 valid_flags_mask){	const struct nla_bitfield32 *bf = nla_data(nla); 	if (!valid_flags_mask)		return -EINVAL; 	/*disallow invalid bit selector */	if (bf->selector & ~valid_flags_mask)		return -EINVAL; 	/*disallow invalid bit values */	if (bf->value & ~valid_flags_mask)		return -EINVAL; 	/*disallow valid bit values that are not selected*/	if (bf->value & ~bf->selector)		return -EINVAL; 	return 0;} static int nla_validate_array(const struct nlattr *head, int len, int maxtype,			      const struct nla_policy *policy,			      struct netlink_ext_ack *extack,			      unsigned int validate, unsigned int depth){	const struct nlattr *entry;	int rem; 	nla_for_each_attr(entry, head, len, rem) {		int ret; 		if (nla_len(entry) == 0)			continue; 		if (nla_len(entry) < NLA_HDRLEN) {			NL_SET_ERR_MSG_ATTR_POL(extack, entry, policy,						"Array element too short");			return -ERANGE;		} 		ret = __nla_validate_parse(nla_data(entry), nla_len(entry),					   maxtype, policy, validate, extack,					   NULL, depth + 1);		if (ret < 0)			return ret;	} 	return 0;} void nla_get_range_unsigned(const struct nla_policy *pt,			    struct netlink_range_validation *range){	WARN_ON_ONCE(pt->validation_type != NLA_VALIDATE_RANGE_PTR &&		     (pt->min < 0 || pt->max < 0)); 	range->min = 0; 	switch (pt->type) {	case NLA_U8:		range->max = U8_MAX;		break;	case NLA_U16:	case NLA_BE16:	case NLA_BINARY:		range->max = U16_MAX;		break;	case NLA_U32:	case NLA_BE32:		range->max = U32_MAX;		break;	case NLA_U64:	case NLA_UINT:	case NLA_MSECS:		range->max = U64_MAX;		break;	default:		WARN_ON_ONCE(1);		return;	} 	switch (pt->validation_type) {	case NLA_VALIDATE_RANGE:	case NLA_VALIDATE_RANGE_WARN_TOO_LONG:		range->min = pt->min;		range->max = pt->max;		break;	case NLA_VALIDATE_RANGE_PTR:		*range = *pt->range;		break;	case NLA_VALIDATE_MIN:		range->min = pt->min;		break;	case NLA_VALIDATE_MAX:		range->max = pt->max;		break;	default:		break;	}} static int nla_validate_range_unsigned(const struct nla_policy *pt,				       const struct nlattr *nla,				       struct netlink_ext_ack *extack,				       unsigned int validate){	struct netlink_range_validation range;	u64 value; 	switch (pt->type) {	case NLA_U8:		value = nla_get_u8(nla);		break;	case NLA_U16:		value = nla_get_u16(nla);		break;	case NLA_U32:		value = nla_get_u32(nla);		break;	case NLA_U64:		value = nla_get_u64(nla);		break;	case NLA_UINT:		value = nla_get_uint(nla);		break;	case NLA_MSECS:		value = nla_get_u64(nla);		break;	case NLA_BINARY:		value = nla_len(nla);		break;	case NLA_BE16:		value = ntohs(nla_get_be16(nla));		break;	case NLA_BE32:		value = ntohl(nla_get_be32(nla));		break;	default:		return -EINVAL;	} 	nla_get_range_unsigned(pt, &range); 	if (pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG &&	    pt->type == NLA_BINARY && value > range.max) {		pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",				    current->comm, pt->type);		if (validate & NL_VALIDATE_STRICT_ATTRS) {			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"invalid attribute length");			return -EINVAL;		} 		/* this assumes min <= max (don't validate against min) */		return 0;	} 	if (value < range.min || value > range.max) {		bool binary = pt->type == NLA_BINARY; 		if (binary)			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"binary attribute size out of range");		else			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"integer out of range"); 		return -ERANGE;	} 	return 0;} void nla_get_range_signed(const struct nla_policy *pt,			  struct netlink_range_validation_signed *range){	switch (pt->type) {	case NLA_S8:		range->min = S8_MIN;		range->max = S8_MAX;		break;	case NLA_S16:		range->min = S16_MIN;		range->max = S16_MAX;		break;	case NLA_S32:		range->min = S32_MIN;		range->max = S32_MAX;		break;	case NLA_S64:	case NLA_SINT:		range->min = S64_MIN;		range->max = S64_MAX;		break;	default:		WARN_ON_ONCE(1);		return;	} 	switch (pt->validation_type) {	case NLA_VALIDATE_RANGE:		range->min = pt->min;		range->max = pt->max;		break;	case NLA_VALIDATE_RANGE_PTR:		*range = *pt->range_signed;		break;	case NLA_VALIDATE_MIN:		range->min = pt->min;		break;	case NLA_VALIDATE_MAX:		range->max = pt->max;		break;	default:		break;	}} static int nla_validate_int_range_signed(const struct nla_policy *pt,					 const struct nlattr *nla,					 struct netlink_ext_ack *extack){	struct netlink_range_validation_signed range;	s64 value; 	switch (pt->type) {	case NLA_S8:		value = nla_get_s8(nla);		break;	case NLA_S16:		value = nla_get_s16(nla);		break;	case NLA_S32:		value = nla_get_s32(nla);		break;	case NLA_S64:		value = nla_get_s64(nla);		break;	case NLA_SINT:		value = nla_get_sint(nla);		break;	default:		return -EINVAL;	} 	nla_get_range_signed(pt, &range); 	if (value < range.min || value > range.max) {		NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,					"integer out of range");		return -ERANGE;	} 	return 0;} static int nla_validate_int_range(const struct nla_policy *pt,				  const struct nlattr *nla,				  struct netlink_ext_ack *extack,				  unsigned int validate){	switch (pt->type) {	case NLA_U8:	case NLA_U16:	case NLA_U32:	case NLA_U64:	case NLA_UINT:	case NLA_MSECS:	case NLA_BINARY:	case NLA_BE16:	case NLA_BE32:		return nla_validate_range_unsigned(pt, nla, extack, validate);	case NLA_S8:	case NLA_S16:	case NLA_S32:	case NLA_S64:	case NLA_SINT:		return nla_validate_int_range_signed(pt, nla, extack);	default:		WARN_ON(1);		return -EINVAL;	}} static int nla_validate_mask(const struct nla_policy *pt,			     const struct nlattr *nla,			     struct netlink_ext_ack *extack){	u64 value; 	switch (pt->type) {	case NLA_U8:		value = nla_get_u8(nla);		break;	case NLA_U16:		value = nla_get_u16(nla);		break;	case NLA_U32:		value = nla_get_u32(nla);		break;	case NLA_U64:		value = nla_get_u64(nla);		break;	case NLA_UINT:		value = nla_get_uint(nla);		break;	case NLA_BE16:		value = ntohs(nla_get_be16(nla));		break;	case NLA_BE32:		value = ntohl(nla_get_be32(nla));		break;	default:		return -EINVAL;	} 	if (value & ~(u64)pt->mask) {		NL_SET_ERR_MSG_ATTR(extack, nla, "reserved bit set");		return -EINVAL;	} 	return 0;} static int validate_nla(const struct nlattr *nla, int maxtype,			const struct nla_policy *policy, unsigned int validate,			struct netlink_ext_ack *extack, unsigned int depth){	u16 strict_start_type = policy[0].strict_start_type;	const struct nla_policy *pt;	int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);	int err = -ERANGE; 	if (strict_start_type && type >= strict_start_type)		validate |= NL_VALIDATE_STRICT; 	if (type <= 0 || type > maxtype)		return 0; 	type = array_index_nospec(type, maxtype + 1);	pt = &policy[type]; 	BUG_ON(pt->type > NLA_TYPE_MAX); 	if (nla_attr_len[pt->type] && attrlen != nla_attr_len[pt->type]) {		pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",				    current->comm, type);		if (validate & NL_VALIDATE_STRICT_ATTRS) {			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"invalid attribute length");			return -EINVAL;		}	} 	if (validate & NL_VALIDATE_NESTED) {		if ((pt->type == NLA_NESTED || pt->type == NLA_NESTED_ARRAY) &&		    !(nla->nla_type & NLA_F_NESTED)) {			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"NLA_F_NESTED is missing");			return -EINVAL;		}		if (pt->type != NLA_NESTED && pt->type != NLA_NESTED_ARRAY &&		    pt->type != NLA_UNSPEC && (nla->nla_type & NLA_F_NESTED)) {			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"NLA_F_NESTED not expected");			return -EINVAL;		}	} 	switch (pt->type) {	case NLA_REJECT:		if (extack && pt->reject_message) {			NL_SET_BAD_ATTR(extack, nla);			extack->_msg = pt->reject_message;			return -EINVAL;		}		err = -EINVAL;		goto out_err; 	case NLA_FLAG:		if (attrlen > 0)			goto out_err;		break; 	case NLA_SINT:	case NLA_UINT:		if (attrlen != sizeof(u32) && attrlen != sizeof(u64)) {			NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,						"invalid attribute length");			return -EINVAL;		}		break; 	case NLA_BITFIELD32:		if (attrlen != sizeof(struct nla_bitfield32))			goto out_err; 		err = validate_nla_bitfield32(nla, pt->bitfield32_valid);		if (err)			goto out_err;		break; 	case NLA_NUL_STRING:		if (pt->len)			minlen = min_t(int, attrlen, pt->len + 1);		else			minlen = attrlen; 		if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) {			err = -EINVAL;			goto out_err;		}		fallthrough; 	case NLA_STRING:		if (attrlen < 1)			goto out_err; 		if (pt->len) {			char *buf = nla_data(nla); 			if (buf[attrlen - 1] == '\0')				attrlen--; 			if (attrlen > pt->len)				goto out_err;		}		break; 	case NLA_BINARY:		if (pt->len && attrlen > pt->len)			goto out_err;

Lines 1–500 of 1169.