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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 8 Apr 2018 12:26:29 +0000 (14:26 +0200)
commit b1d0a5d0cba4597c0394997b2d5fced3e3841b4e upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/netfilter/x_tables.h
net/netfilter/x_tables.c
net/netfilter/xt_hashlimit.c
net/netfilter/xt_recent.c

index 33f7530f96b9d710f6a6bee0d06c6612437e220e..8e46c35d654b50265704caebb881e6fd32b33b24 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 8a4947ff2ebf6e002a7f33ae21bb20ab9d6eb016..a450a1c8804b4d3f87a766443ff6a193d02bef31 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 b8a3e740ffd4eefec9483e09220b8b630a5e8f73..0c034597b9b870b1f45d2ce4da44a9b5a432157a 100644 (file)
@@ -915,8 +915,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);
 
@@ -933,8 +934,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);
 
@@ -948,9 +950,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 245fa350a7a85390e6767c4a0c5862f4213000fe..cf96d230e5a3c3655ce11671295b056121f7163a 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;