Commit 31df0cff authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 1042caa7 1501d135
......@@ -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
......
......@@ -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);
......
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