netfilter: x_tables: add and use xt_check_proc_name
authorFlorian Westphal <fw@strlen.de>
Sat, 10 Mar 2018 00:15:45 +0000 (01:15 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sun, 11 Mar 2018 20:24:29 +0000 (21:24 +0100)
recent and hashlimit both create /proc files, but only check that
name is 0 terminated.

This can trigger WARN() from procfs when name is "" or "/".
Add helper for this and then use it for both.

Cc: Eric Dumazet <eric.dumazet@gmail.com>
Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
Reported-by: <syzbot+0502b00edac2a0680b61@syzkaller.appspotmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/x_tables.h
net/netfilter/x_tables.c
net/netfilter/xt_hashlimit.c
net/netfilter/xt_recent.c

index 1313b35c3ab7914a26c463aa9113756dd4e4ceae..14529511c4b8466ab81986ee9d874538bbe0e09a 100644 (file)
@@ -285,6 +285,8 @@ unsigned int *xt_alloc_entry_offsets(unsigned int size);
 bool xt_find_jump_offset(const unsigned int *offsets,
                         unsigned int target, unsigned int size);
 
+int xt_check_proc_name(const char *name, unsigned int size);
+
 int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
                   bool inv_proto);
 int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
index fa1655aff8d3febbf9983719cd8d5d7eda126891..4aa01c90e9d1edf24a9fef99f46351bceae2d5c0 100644 (file)
@@ -423,6 +423,36 @@ textify_hooks(char *buf, size_t size, unsigned int mask, uint8_t nfproto)
        return buf;
 }
 
+/**
+ * xt_check_proc_name - check that name is suitable for /proc file creation
+ *
+ * @name: file name candidate
+ * @size: length of buffer
+ *
+ * some x_tables modules wish to create a file in /proc.
+ * This function makes sure that the name is suitable for this
+ * purpose, it checks that name is NUL terminated and isn't a 'special'
+ * name, like "..".
+ *
+ * returns negative number on error or 0 if name is useable.
+ */
+int xt_check_proc_name(const char *name, unsigned int size)
+{
+       if (name[0] == '\0')
+               return -EINVAL;
+
+       if (strnlen(name, size) == size)
+               return -ENAMETOOLONG;
+
+       if (strcmp(name, ".") == 0 ||
+           strcmp(name, "..") == 0 ||
+           strchr(name, '/'))
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL(xt_check_proc_name);
+
 int xt_check_match(struct xt_mtchk_param *par,
                   unsigned int size, u_int8_t proto, bool inv_proto)
 {
index 66f5aca62a087ee816b6c18e0c385af535cf4ae7..3360f13dc208b6af1ca238f0018ee8ad6df3924c 100644 (file)
@@ -917,8 +917,9 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)
        struct hashlimit_cfg3 cfg = {};
        int ret;
 
-       if (info->name[sizeof(info->name) - 1] != '\0')
-               return -EINVAL;
+       ret = xt_check_proc_name(info->name, sizeof(info->name));
+       if (ret)
+               return ret;
 
        ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
 
@@ -935,8 +936,9 @@ static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par)
        struct hashlimit_cfg3 cfg = {};
        int ret;
 
-       if (info->name[sizeof(info->name) - 1] != '\0')
-               return -EINVAL;
+       ret = xt_check_proc_name(info->name, sizeof(info->name));
+       if (ret)
+               return ret;
 
        ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
 
@@ -950,9 +952,11 @@ static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par)
 static int hashlimit_mt_check(const struct xt_mtchk_param *par)
 {
        struct xt_hashlimit_mtinfo3 *info = par->matchinfo;
+       int ret;
 
-       if (info->name[sizeof(info->name) - 1] != '\0')
-               return -EINVAL;
+       ret = xt_check_proc_name(info->name, sizeof(info->name));
+       if (ret)
+               return ret;
 
        return hashlimit_mt_check_common(par, &info->hinfo, &info->cfg,
                                         info->name, 3);
index 6d232d18faff72ec8c26bd82dc7685fec52c5c6a..81ee1d6543b2696a2345b00d134399ab1d999e90 100644 (file)
@@ -361,9 +361,9 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
                                    info->hit_count, XT_RECENT_MAX_NSTAMPS - 1);
                return -EINVAL;
        }
-       if (info->name[0] == '\0' ||
-           strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN)
-               return -EINVAL;
+       ret = xt_check_proc_name(info->name, sizeof(info->name));
+       if (ret)
+               return ret;
 
        if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot)
                nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1;