[NETLINK]: Improve string attribute validation
authorThomas Graf <tgraf@suug.ch>
Sun, 27 Aug 2006 03:11:47 +0000 (20:11 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Fri, 22 Sep 2006 22:18:24 +0000 (15:18 -0700)
Introduces a new attribute type NLA_NUL_STRING to support NUL
terminated strings. Attributes of this kind require to carry
a terminating NUL within the maximum specified in the policy.

The `old' NLA_STRING which is not required to be NUL terminated
is extended to provide means to specify a maximum length of the
string.

Aims at easing the pain with using nla_strlcpy() on temporary
buffers.

Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/netlink.h
net/netlink/attr.c

index bcb27e3..11dc2e7 100644 (file)
@@ -167,6 +167,7 @@ enum {
        NLA_FLAG,
        NLA_MSECS,
        NLA_NESTED,
+       NLA_NUL_STRING,
        __NLA_TYPE_MAX,
 };
 
@@ -175,21 +176,27 @@ enum {
 /**
  * struct nla_policy - attribute validation policy
  * @type: Type of attribute or NLA_UNSPEC
- * @minlen: Minimal length of payload required to be available
+ * @len: Type specific length of payload
  *
  * Policies are defined as arrays of this struct, the array must be
  * accessible by attribute type up to the highest identifier to be expected.
  *
+ * Meaning of `len' field:
+ *    NLA_STRING           Maximum length of string
+ *    NLA_NUL_STRING       Maximum length of string (excluding NUL)
+ *    NLA_FLAG             Unused
+ *    All other            Exact length of attribute payload
+ *
  * Example:
  * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = {
  *     [ATTR_FOO] = { .type = NLA_U16 },
- *     [ATTR_BAR] = { .type = NLA_STRING },
- *     [ATTR_BAZ] = { .minlen = sizeof(struct mystruct) },
+ *     [ATTR_BAR] = { .type = NLA_STRING, len = BARSIZ },
+ *     [ATTR_BAZ] = { .len = sizeof(struct mystruct) },
  * };
  */
 struct nla_policy {
        u16             type;
-       u16             minlen;
+       u16             len;
 };
 
 /**
index 136e529..0041395 100644 (file)
@@ -20,7 +20,6 @@ static u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = {
        [NLA_U16]       = sizeof(u16),
        [NLA_U32]       = sizeof(u32),
        [NLA_U64]       = sizeof(u64),
-       [NLA_STRING]    = 1,
        [NLA_NESTED]    = NLA_HDRLEN,
 };
 
@@ -28,7 +27,7 @@ static int validate_nla(struct nlattr *nla, int maxtype,
                        struct nla_policy *policy)
 {
        struct nla_policy *pt;
-       int minlen = 0;
+       int minlen = 0, attrlen = nla_len(nla);
 
        if (nla->nla_type <= 0 || nla->nla_type > maxtype)
                return 0;
@@ -37,16 +36,46 @@ static int validate_nla(struct nlattr *nla, int maxtype,
 
        BUG_ON(pt->type > NLA_TYPE_MAX);
 
-       if (pt->minlen)
-               minlen = pt->minlen;
-       else if (pt->type != NLA_UNSPEC)
-               minlen = nla_attr_minlen[pt->type];
+       switch (pt->type) {
+       case NLA_FLAG:
+               if (attrlen > 0)
+                       return -ERANGE;
+               break;
 
-       if (pt->type == NLA_FLAG && nla_len(nla) > 0)
-               return -ERANGE;
+       case NLA_NUL_STRING:
+               if (pt->len)
+                       minlen = min_t(int, attrlen, pt->len + 1);
+               else
+                       minlen = attrlen;
 
-       if (nla_len(nla) < minlen)
-               return -ERANGE;
+               if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
+                       return -EINVAL;
+               /* fall through */
+
+       case NLA_STRING:
+               if (attrlen < 1)
+                       return -ERANGE;
+
+               if (pt->len) {
+                       char *buf = nla_data(nla);
+
+                       if (buf[attrlen - 1] == '\0')
+                               attrlen--;
+
+                       if (attrlen > pt->len)
+                               return -ERANGE;
+               }
+               break;
+
+       default:
+               if (pt->len)
+                       minlen = pt->len;
+               else if (pt->type != NLA_UNSPEC)
+                       minlen = nla_attr_minlen[pt->type];
+
+               if (attrlen < minlen)
+                       return -ERANGE;
+       }
 
        return 0;
 }