mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-01 19:34:35 +08:00
Merge branch 'netlink-nested-policy-validation'
Johannes Berg says: ==================== netlink: nested policy validation This adds nested policy validation, which lets you specify the nested attribute type, e.g. NLA_NESTED with sub-policy, or the new NLA_NESTED_ARRAY with sub-sub-policy. Changes in v2: * move setting the bad attr pointer/message into validate_nla() * remove the recursion patch since that's no longer needed * simply skip the generic bad attr pointer/message setting in case of nested nla_validate() failing since that could fail only due to validate_nla() failing inside, which already sets the extack information Changes in v3: * fix NLA_REJECT to have an error message if none is in policy ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
31df0cffa9
@ -172,7 +172,7 @@ enum {
|
||||
NLA_FLAG,
|
||||
NLA_MSECS,
|
||||
NLA_NESTED,
|
||||
NLA_NESTED_COMPAT,
|
||||
NLA_NESTED_ARRAY,
|
||||
NLA_NUL_STRING,
|
||||
NLA_BINARY,
|
||||
NLA_S8,
|
||||
@ -201,9 +201,11 @@ enum {
|
||||
* NLA_NUL_STRING Maximum length of string (excluding NUL)
|
||||
* NLA_FLAG Unused
|
||||
* NLA_BINARY Maximum length of attribute payload
|
||||
* NLA_NESTED Don't use `len' field -- length verification is
|
||||
* done by checking len of nested header (or empty)
|
||||
* NLA_NESTED_COMPAT Minimum length of structure payload
|
||||
* NLA_NESTED,
|
||||
* NLA_NESTED_ARRAY Length verification is done by checking len of
|
||||
* nested header (or empty); len field is used if
|
||||
* validation_data is also used, for the max attr
|
||||
* number in the nested policy.
|
||||
* NLA_U8, NLA_U16,
|
||||
* NLA_U32, NLA_U64,
|
||||
* NLA_S8, NLA_S16,
|
||||
@ -226,6 +228,16 @@ enum {
|
||||
* NLA_REJECT This attribute is always rejected and validation data
|
||||
* may point to a string to report as the error instead
|
||||
* of the generic one in extended ACK.
|
||||
* NLA_NESTED Points to a nested policy to validate, must also set
|
||||
* `len' to the max attribute number.
|
||||
* Note that nla_parse() will validate, but of course not
|
||||
* parse, the nested sub-policies.
|
||||
* NLA_NESTED_ARRAY Points to a nested policy to validate, must also set
|
||||
* `len' to the max attribute number. The difference to
|
||||
* NLA_NESTED is the structure - NLA_NESTED has the
|
||||
* nested attributes directly inside, while an array has
|
||||
* the nested attributes at another level down and the
|
||||
* attributes directly in the nesting don't matter.
|
||||
* All other Unused
|
||||
*
|
||||
* Example:
|
||||
@ -239,7 +251,7 @@ enum {
|
||||
struct nla_policy {
|
||||
u16 type;
|
||||
u16 len;
|
||||
void *validation_data;
|
||||
const void *validation_data;
|
||||
};
|
||||
|
||||
#define NLA_POLICY_EXACT_LEN(_len) { .type = NLA_EXACT_LEN, .len = _len }
|
||||
@ -249,6 +261,11 @@ struct nla_policy {
|
||||
#define NLA_POLICY_ETH_ADDR NLA_POLICY_EXACT_LEN(ETH_ALEN)
|
||||
#define NLA_POLICY_ETH_ADDR_COMPAT NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
|
||||
|
||||
#define NLA_POLICY_NESTED(maxattr, policy) \
|
||||
{ .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
|
||||
#define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
|
||||
{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
|
||||
|
||||
/**
|
||||
* struct nl_info - netlink source information
|
||||
* @nlh: Netlink message header of original request
|
||||
|
149
lib/nlattr.c
149
lib/nlattr.c
@ -45,12 +45,11 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
|
||||
};
|
||||
|
||||
static int validate_nla_bitfield32(const struct nlattr *nla,
|
||||
u32 *valid_flags_allowed)
|
||||
const u32 *valid_flags_mask)
|
||||
{
|
||||
const struct nla_bitfield32 *bf = nla_data(nla);
|
||||
u32 *valid_flags_mask = valid_flags_allowed;
|
||||
|
||||
if (!valid_flags_allowed)
|
||||
if (!valid_flags_mask)
|
||||
return -EINVAL;
|
||||
|
||||
/*disallow invalid bit selector */
|
||||
@ -68,12 +67,41 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
|
||||
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)
|
||||
{
|
||||
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(extack, entry,
|
||||
"Array element too short");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
ret = nla_validate(nla_data(entry), nla_len(entry),
|
||||
maxtype, policy, extack);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_nla(const struct nlattr *nla, int maxtype,
|
||||
const struct nla_policy *policy,
|
||||
const char **error_msg)
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct nla_policy *pt;
|
||||
int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla);
|
||||
int err = -ERANGE;
|
||||
|
||||
if (type <= 0 || type > maxtype)
|
||||
return 0;
|
||||
@ -91,24 +119,31 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
||||
switch (pt->type) {
|
||||
case NLA_EXACT_LEN:
|
||||
if (attrlen != pt->len)
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
break;
|
||||
|
||||
case NLA_REJECT:
|
||||
if (pt->validation_data && error_msg)
|
||||
*error_msg = pt->validation_data;
|
||||
return -EINVAL;
|
||||
if (extack && pt->validation_data) {
|
||||
NL_SET_BAD_ATTR(extack, nla);
|
||||
extack->_msg = pt->validation_data;
|
||||
return -EINVAL;
|
||||
}
|
||||
err = -EINVAL;
|
||||
goto out_err;
|
||||
|
||||
case NLA_FLAG:
|
||||
if (attrlen > 0)
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
break;
|
||||
|
||||
case NLA_BITFIELD32:
|
||||
if (attrlen != sizeof(struct nla_bitfield32))
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
|
||||
return validate_nla_bitfield32(nla, pt->validation_data);
|
||||
err = validate_nla_bitfield32(nla, pt->validation_data);
|
||||
if (err)
|
||||
goto out_err;
|
||||
break;
|
||||
|
||||
case NLA_NUL_STRING:
|
||||
if (pt->len)
|
||||
@ -116,13 +151,15 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
||||
else
|
||||
minlen = attrlen;
|
||||
|
||||
if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
|
||||
return -EINVAL;
|
||||
if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) {
|
||||
err = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case NLA_STRING:
|
||||
if (attrlen < 1)
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
|
||||
if (pt->len) {
|
||||
char *buf = nla_data(nla);
|
||||
@ -131,32 +168,58 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
||||
attrlen--;
|
||||
|
||||
if (attrlen > pt->len)
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
}
|
||||
break;
|
||||
|
||||
case NLA_BINARY:
|
||||
if (pt->len && attrlen > pt->len)
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
break;
|
||||
|
||||
case NLA_NESTED_COMPAT:
|
||||
if (attrlen < pt->len)
|
||||
return -ERANGE;
|
||||
if (attrlen < NLA_ALIGN(pt->len))
|
||||
break;
|
||||
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
|
||||
return -ERANGE;
|
||||
nla = nla_data(nla) + NLA_ALIGN(pt->len);
|
||||
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
|
||||
return -ERANGE;
|
||||
break;
|
||||
case NLA_NESTED:
|
||||
/* a nested attributes is allowed to be empty; if its not,
|
||||
* it must have a size of at least NLA_HDRLEN.
|
||||
*/
|
||||
if (attrlen == 0)
|
||||
break;
|
||||
if (attrlen < NLA_HDRLEN)
|
||||
goto out_err;
|
||||
if (pt->validation_data) {
|
||||
err = nla_validate(nla_data(nla), nla_len(nla), pt->len,
|
||||
pt->validation_data, extack);
|
||||
if (err < 0) {
|
||||
/*
|
||||
* return directly to preserve the inner
|
||||
* error message/attribute pointer
|
||||
*/
|
||||
return err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NLA_NESTED_ARRAY:
|
||||
/* a nested array attribute is allowed to be empty; if its not,
|
||||
* it must have a size of at least NLA_HDRLEN.
|
||||
*/
|
||||
if (attrlen == 0)
|
||||
break;
|
||||
if (attrlen < NLA_HDRLEN)
|
||||
goto out_err;
|
||||
if (pt->validation_data) {
|
||||
int err;
|
||||
|
||||
err = nla_validate_array(nla_data(nla), nla_len(nla),
|
||||
pt->len, pt->validation_data,
|
||||
extack);
|
||||
if (err < 0) {
|
||||
/*
|
||||
* return directly to preserve the inner
|
||||
* error message/attribute pointer
|
||||
*/
|
||||
return err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (pt->len)
|
||||
minlen = pt->len;
|
||||
@ -164,10 +227,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
|
||||
minlen = nla_attr_minlen[pt->type];
|
||||
|
||||
if (attrlen < minlen)
|
||||
return -ERANGE;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_err:
|
||||
NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -192,12 +258,10 @@ int nla_validate(const struct nlattr *head, int len, int maxtype,
|
||||
int rem;
|
||||
|
||||
nla_for_each_attr(nla, head, len, rem) {
|
||||
int err = validate_nla(nla, maxtype, policy, NULL);
|
||||
int err = validate_nla(nla, maxtype, policy, extack);
|
||||
|
||||
if (err < 0) {
|
||||
NL_SET_BAD_ATTR(extack, nla);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -253,7 +317,7 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct nlattr *nla;
|
||||
int rem, err;
|
||||
int rem;
|
||||
|
||||
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
|
||||
|
||||
@ -261,17 +325,12 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
|
||||
u16 type = nla_type(nla);
|
||||
|
||||
if (type > 0 && type <= maxtype) {
|
||||
static const char _msg[] = "Attribute failed policy validation";
|
||||
const char *msg = _msg;
|
||||
|
||||
if (policy) {
|
||||
err = validate_nla(nla, maxtype, policy, &msg);
|
||||
if (err < 0) {
|
||||
NL_SET_BAD_ATTR(extack, nla);
|
||||
if (extack)
|
||||
extack->_msg = msg;
|
||||
goto errout;
|
||||
}
|
||||
int err = validate_nla(nla, maxtype, policy,
|
||||
extack);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
tb[type] = (struct nlattr *)nla;
|
||||
@ -282,9 +341,7 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
|
||||
pr_warn_ratelimited("netlink: %d bytes leftover after parsing attributes in process `%s'.\n",
|
||||
rem, current->comm);
|
||||
|
||||
err = 0;
|
||||
errout:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nla_parse);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user