Commit 088067f4 authored by Jozsef Kadlecsik's avatar Jozsef Kadlecsik Committed by Pablo Neira Ayuso

netfilter: ipset: autoload set type modules safely

Jan Engelhardt noticed when userspace requests a set type unknown
to the kernel, it can lead to a loop due to the unsafe type module
loading. The issue is fixed in this patch.
Signed-off-by: default avatarJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 9bf04646
...@@ -77,35 +77,42 @@ find_set_type(const char *name, u8 family, u8 revision) ...@@ -77,35 +77,42 @@ find_set_type(const char *name, u8 family, u8 revision)
} }
/* Unlock, try to load a set type module and lock again */ /* Unlock, try to load a set type module and lock again */
static int static bool
try_to_load_type(const char *name) load_settype(const char *name)
{ {
nfnl_unlock(); nfnl_unlock();
pr_debug("try to load ip_set_%s\n", name); pr_debug("try to load ip_set_%s\n", name);
if (request_module("ip_set_%s", name) < 0) { if (request_module("ip_set_%s", name) < 0) {
pr_warning("Can't find ip_set type %s\n", name); pr_warning("Can't find ip_set type %s\n", name);
nfnl_lock(); nfnl_lock();
return -IPSET_ERR_FIND_TYPE; return false;
} }
nfnl_lock(); nfnl_lock();
return -EAGAIN; return true;
} }
/* Find a set type and reference it */ /* Find a set type and reference it */
#define find_set_type_get(name, family, revision, found) \
__find_set_type_get(name, family, revision, found, false)
static int static int
find_set_type_get(const char *name, u8 family, u8 revision, __find_set_type_get(const char *name, u8 family, u8 revision,
struct ip_set_type **found) struct ip_set_type **found, bool retry)
{ {
struct ip_set_type *type; struct ip_set_type *type;
int err; int err;
if (retry && !load_settype(name))
return -IPSET_ERR_FIND_TYPE;
rcu_read_lock(); rcu_read_lock();
*found = find_set_type(name, family, revision); *found = find_set_type(name, family, revision);
if (*found) { if (*found) {
err = !try_module_get((*found)->me) ? -EFAULT : 0; err = !try_module_get((*found)->me) ? -EFAULT : 0;
goto unlock; goto unlock;
} }
/* Make sure the type is loaded but we don't support the revision */ /* Make sure the type is already loaded
* but we don't support the revision */
list_for_each_entry_rcu(type, &ip_set_type_list, list) list_for_each_entry_rcu(type, &ip_set_type_list, list)
if (STREQ(type->name, name)) { if (STREQ(type->name, name)) {
err = -IPSET_ERR_FIND_TYPE; err = -IPSET_ERR_FIND_TYPE;
...@@ -113,7 +120,8 @@ find_set_type_get(const char *name, u8 family, u8 revision, ...@@ -113,7 +120,8 @@ find_set_type_get(const char *name, u8 family, u8 revision,
} }
rcu_read_unlock(); rcu_read_unlock();
return try_to_load_type(name); return retry ? -IPSET_ERR_FIND_TYPE :
__find_set_type_get(name, family, revision, found, true);
unlock: unlock:
rcu_read_unlock(); rcu_read_unlock();
...@@ -124,12 +132,19 @@ find_set_type_get(const char *name, u8 family, u8 revision, ...@@ -124,12 +132,19 @@ find_set_type_get(const char *name, u8 family, u8 revision,
* If we succeeded, the supported minimal and maximum revisions are * If we succeeded, the supported minimal and maximum revisions are
* filled out. * filled out.
*/ */
#define find_set_type_minmax(name, family, min, max) \
__find_set_type_minmax(name, family, min, max, false)
static int static int
find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) __find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max,
bool retry)
{ {
struct ip_set_type *type; struct ip_set_type *type;
bool found = false; bool found = false;
if (retry && !load_settype(name))
return -IPSET_ERR_FIND_TYPE;
*min = 255; *max = 0; *min = 255; *max = 0;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(type, &ip_set_type_list, list) list_for_each_entry_rcu(type, &ip_set_type_list, list)
...@@ -145,7 +160,8 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) ...@@ -145,7 +160,8 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max)
if (found) if (found)
return 0; return 0;
return try_to_load_type(name); return retry ? -IPSET_ERR_FIND_TYPE :
__find_set_type_minmax(name, family, min, max, true);
} }
#define family_name(f) ((f) == AF_INET ? "inet" : \ #define family_name(f) ((f) == AF_INET ? "inet" : \
......
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