diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2012-01-17 04:39:05 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-01-17 04:52:46 -0500 |
commit | 088067f4f14d6ee5c6a196b015a560cbe7744224 (patch) | |
tree | 1be34b941c1671b70834212d2188f8ce6b1c1f45 /net | |
parent | 9bf04646b0b41c5438ed8a27c5f8dbe0ff40d756 (diff) |
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: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/ipset/ip_set_core.c | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 86137b558f45..0f8e5f2fa1ba 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c | |||
@@ -77,35 +77,42 @@ find_set_type(const char *name, u8 family, u8 revision) | |||
77 | } | 77 | } |
78 | 78 | ||
79 | /* Unlock, try to load a set type module and lock again */ | 79 | /* Unlock, try to load a set type module and lock again */ |
80 | static int | 80 | static bool |
81 | try_to_load_type(const char *name) | 81 | load_settype(const char *name) |
82 | { | 82 | { |
83 | nfnl_unlock(); | 83 | nfnl_unlock(); |
84 | pr_debug("try to load ip_set_%s\n", name); | 84 | pr_debug("try to load ip_set_%s\n", name); |
85 | if (request_module("ip_set_%s", name) < 0) { | 85 | if (request_module("ip_set_%s", name) < 0) { |
86 | pr_warning("Can't find ip_set type %s\n", name); | 86 | pr_warning("Can't find ip_set type %s\n", name); |
87 | nfnl_lock(); | 87 | nfnl_lock(); |
88 | return -IPSET_ERR_FIND_TYPE; | 88 | return false; |
89 | } | 89 | } |
90 | nfnl_lock(); | 90 | nfnl_lock(); |
91 | return -EAGAIN; | 91 | return true; |
92 | } | 92 | } |
93 | 93 | ||
94 | /* Find a set type and reference it */ | 94 | /* Find a set type and reference it */ |
95 | #define find_set_type_get(name, family, revision, found) \ | ||
96 | __find_set_type_get(name, family, revision, found, false) | ||
97 | |||
95 | static int | 98 | static int |
96 | find_set_type_get(const char *name, u8 family, u8 revision, | 99 | __find_set_type_get(const char *name, u8 family, u8 revision, |
97 | struct ip_set_type **found) | 100 | struct ip_set_type **found, bool retry) |
98 | { | 101 | { |
99 | struct ip_set_type *type; | 102 | struct ip_set_type *type; |
100 | int err; | 103 | int err; |
101 | 104 | ||
105 | if (retry && !load_settype(name)) | ||
106 | return -IPSET_ERR_FIND_TYPE; | ||
107 | |||
102 | rcu_read_lock(); | 108 | rcu_read_lock(); |
103 | *found = find_set_type(name, family, revision); | 109 | *found = find_set_type(name, family, revision); |
104 | if (*found) { | 110 | if (*found) { |
105 | err = !try_module_get((*found)->me) ? -EFAULT : 0; | 111 | err = !try_module_get((*found)->me) ? -EFAULT : 0; |
106 | goto unlock; | 112 | goto unlock; |
107 | } | 113 | } |
108 | /* Make sure the type is loaded but we don't support the revision */ | 114 | /* Make sure the type is already loaded |
115 | * but we don't support the revision */ | ||
109 | list_for_each_entry_rcu(type, &ip_set_type_list, list) | 116 | list_for_each_entry_rcu(type, &ip_set_type_list, list) |
110 | if (STREQ(type->name, name)) { | 117 | if (STREQ(type->name, name)) { |
111 | err = -IPSET_ERR_FIND_TYPE; | 118 | err = -IPSET_ERR_FIND_TYPE; |
@@ -113,7 +120,8 @@ find_set_type_get(const char *name, u8 family, u8 revision, | |||
113 | } | 120 | } |
114 | rcu_read_unlock(); | 121 | rcu_read_unlock(); |
115 | 122 | ||
116 | return try_to_load_type(name); | 123 | return retry ? -IPSET_ERR_FIND_TYPE : |
124 | __find_set_type_get(name, family, revision, found, true); | ||
117 | 125 | ||
118 | unlock: | 126 | unlock: |
119 | rcu_read_unlock(); | 127 | rcu_read_unlock(); |
@@ -124,12 +132,19 @@ unlock: | |||
124 | * If we succeeded, the supported minimal and maximum revisions are | 132 | * If we succeeded, the supported minimal and maximum revisions are |
125 | * filled out. | 133 | * filled out. |
126 | */ | 134 | */ |
135 | #define find_set_type_minmax(name, family, min, max) \ | ||
136 | __find_set_type_minmax(name, family, min, max, false) | ||
137 | |||
127 | static int | 138 | static int |
128 | find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) | 139 | __find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max, |
140 | bool retry) | ||
129 | { | 141 | { |
130 | struct ip_set_type *type; | 142 | struct ip_set_type *type; |
131 | bool found = false; | 143 | bool found = false; |
132 | 144 | ||
145 | if (retry && !load_settype(name)) | ||
146 | return -IPSET_ERR_FIND_TYPE; | ||
147 | |||
133 | *min = 255; *max = 0; | 148 | *min = 255; *max = 0; |
134 | rcu_read_lock(); | 149 | rcu_read_lock(); |
135 | list_for_each_entry_rcu(type, &ip_set_type_list, list) | 150 | 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 | if (found) | 160 | if (found) |
146 | return 0; | 161 | return 0; |
147 | 162 | ||
148 | return try_to_load_type(name); | 163 | return retry ? -IPSET_ERR_FIND_TYPE : |
164 | __find_set_type_minmax(name, family, min, max, true); | ||
149 | } | 165 | } |
150 | 166 | ||
151 | #define family_name(f) ((f) == AF_INET ? "inet" : \ | 167 | #define family_name(f) ((f) == AF_INET ? "inet" : \ |