firewall3: Fix some format string problems
[project/firewall3.git] / helpers.c
1 /*
2 * firewall3 - 3rd OpenWrt UCI firewall implementation
3 *
4 * Copyright (C) 2018 Jo-Philipp Wich <jo@mein.io>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "helpers.h"
20
21
22 const struct fw3_option fw3_cthelper_opts[] = {
23 FW3_OPT("enabled", bool, cthelper, enabled),
24 FW3_OPT("name", string, cthelper, name),
25 FW3_OPT("module", string, cthelper, module),
26 FW3_OPT("description", string, cthelper, description),
27 FW3_OPT("family", family, cthelper, family),
28 FW3_LIST("proto", protocol, cthelper, proto),
29 FW3_OPT("port", port, cthelper, port),
30
31 { }
32 };
33
34
35 static bool
36 test_module(struct fw3_cthelper *helper)
37 {
38 struct stat s;
39 char path[sizeof("/sys/module/nf_conntrack_xxxxxxxxxxxxxxxx")];
40
41 snprintf(path, sizeof(path), "/sys/module/%s", helper->module);
42
43 if (stat(path, &s) || !S_ISDIR(s.st_mode))
44 return false;
45
46 return true;
47 }
48
49 static bool
50 check_cthelper_proto(const struct fw3_cthelper *helper)
51 {
52 struct fw3_protocol *proto;
53
54 if (list_empty(&helper->proto))
55 return false;
56
57 list_for_each_entry(proto, &helper->proto, list)
58 {
59 if (!proto->protocol || proto->any || proto->invert)
60 return false;
61 }
62
63 return true;
64 }
65
66 static bool
67 check_cthelper(struct fw3_state *state, struct fw3_cthelper *helper, struct uci_element *e)
68 {
69 if (!helper->name || !*helper->name)
70 {
71 warn_section("helper", helper, e, "must have a name assigned");
72 }
73 else if (!helper->module || !*helper->module)
74 {
75 warn_section("helper", helper, e, "must have a module assigned");
76 }
77 else if (!check_cthelper_proto(helper))
78 {
79 warn_section("helper", helper, e, "must specify a protocol");
80 }
81 else if (helper->port.set && helper->port.invert)
82 {
83 warn_section("helper", helper, e, "must not specify negated ports");
84 }
85 else
86 {
87 return true;
88 }
89
90 return false;
91 }
92
93 static struct fw3_cthelper *
94 fw3_alloc_cthelper(struct fw3_state *state)
95 {
96 struct fw3_cthelper *helper;
97
98 helper = calloc(1, sizeof(*helper));
99 if (!helper)
100 return NULL;
101
102 helper->enabled = true;
103 helper->family = FW3_FAMILY_ANY;
104 INIT_LIST_HEAD(&helper->proto);
105
106 list_add_tail(&helper->list, &state->cthelpers);
107
108 return helper;
109 }
110
111 static void
112 load_cthelpers(struct fw3_state *state, struct uci_package *p)
113 {
114 struct fw3_cthelper *helper;
115 struct uci_section *s;
116 struct uci_element *e;
117
118 uci_foreach_element(&p->sections, e)
119 {
120 s = uci_to_section(e);
121
122 if (strcmp(s->type, "helper"))
123 continue;
124
125 helper = fw3_alloc_cthelper(state);
126
127 if (!helper)
128 continue;
129
130 if (!fw3_parse_options(helper, fw3_cthelper_opts, s))
131 warn_elem(e, "has invalid options");
132
133 if (!check_cthelper(state, helper, e))
134 fw3_free_cthelper(helper);
135 }
136 }
137
138 void
139 fw3_load_cthelpers(struct fw3_state *state, struct uci_package *p)
140 {
141 struct uci_package *hp = NULL;
142 FILE *fp;
143
144 INIT_LIST_HEAD(&state->cthelpers);
145
146 fp = fopen(FW3_HELPERCONF, "r");
147
148 if (fp) {
149 uci_import(state->uci, fp, "fw3_ct_helpers", &hp, true);
150 fclose(fp);
151
152 if (hp)
153 load_cthelpers(state, hp);
154 }
155
156 load_cthelpers(state, p);
157 }
158
159 struct fw3_cthelper *
160 fw3_lookup_cthelper(struct fw3_state *state, const char *name)
161 {
162 struct fw3_cthelper *h;
163
164 if (list_empty(&state->cthelpers))
165 return NULL;
166
167 list_for_each_entry(h, &state->cthelpers, list)
168 {
169 if (strcasecmp(h->name, name))
170 continue;
171
172 return h;
173 }
174
175 return NULL;
176 }
177
178 bool
179 fw3_cthelper_check_proto(const struct fw3_cthelper *h, const struct fw3_protocol *proto)
180 {
181 struct fw3_protocol *p;
182
183 list_for_each_entry(p, &h->proto, list)
184 {
185 if (p->protocol == proto->protocol)
186 return true;
187 }
188
189 return false;
190 }
191
192 struct fw3_cthelper *
193 fw3_lookup_cthelper_by_proto_port(struct fw3_state *state,
194 struct fw3_protocol *proto,
195 struct fw3_port *port)
196 {
197 struct fw3_cthelper *h;
198
199 if (list_empty(&state->cthelpers))
200 return NULL;
201
202 if (!proto || !proto->protocol || proto->any || proto->invert)
203 return NULL;
204
205 if (port && port->invert)
206 return NULL;
207
208 list_for_each_entry(h, &state->cthelpers, list)
209 {
210 if (!h->enabled)
211 continue;
212
213 if (!fw3_cthelper_check_proto(h, proto))
214 continue;
215
216 if (h->port.set && (!port || !port->set))
217 continue;
218
219 if (!h->port.set && (!port || !port->set))
220 return h;
221
222 if (h->port.set && port && port->set &&
223 h->port.port_min <= port->port_min &&
224 h->port.port_max >= port->port_max)
225 return h;
226 }
227
228 return NULL;
229 }
230
231 static void
232 print_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper,
233 struct fw3_zone *zone, struct fw3_protocol *proto)
234 {
235 struct fw3_ipt_rule *r;
236
237 r = fw3_ipt_rule_create(handle, proto, NULL, NULL, NULL, NULL);
238
239 if (helper->description && *helper->description)
240 fw3_ipt_rule_comment(r, helper->description);
241 else
242 fw3_ipt_rule_comment(r, helper->name);
243
244 fw3_ipt_rule_sport_dport(r, NULL, &helper->port);
245 fw3_ipt_rule_target(r, "CT");
246 fw3_ipt_rule_addarg(r, false, "--helper", helper->name);
247 fw3_ipt_rule_replace(r, "zone_%s_helper", zone->name);
248 }
249
250 static void
251 expand_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper,
252 struct fw3_zone *zone)
253 {
254 struct fw3_protocol *proto;
255
256 list_for_each_entry(proto, &helper->proto, list)
257 print_helper_rule(handle, helper, zone, proto);
258 }
259
260 void
261 fw3_print_cthelpers(struct fw3_ipt_handle *handle, struct fw3_state *state,
262 struct fw3_zone *zone)
263 {
264 struct fw3_cthelper *helper;
265 struct fw3_cthelpermatch *match;
266
267 if (handle->table != FW3_TABLE_RAW)
268 return;
269
270 if (!fw3_is_family(zone, handle->family))
271 return;
272
273 if (list_empty(&zone->cthelpers))
274 {
275 if (zone->masq || !zone->auto_helper)
276 return;
277
278 if (list_empty(&state->cthelpers))
279 return;
280
281 info(" - Using automatic conntrack helper attachment");
282
283 list_for_each_entry(helper, &state->cthelpers, list)
284 {
285 if (!helper || !helper->enabled)
286 continue;
287
288 if (!fw3_is_family(helper, handle->family))
289 continue;
290
291 if (!test_module(helper))
292 continue;
293
294 expand_helper_rule(handle, helper, zone);
295 }
296 }
297 else
298 {
299 list_for_each_entry(match, &zone->cthelpers, list)
300 {
301 helper = match->ptr;
302
303 if (!helper || !helper->enabled)
304 continue;
305
306 if (!fw3_is_family(helper, handle->family))
307 continue;
308
309 if (!test_module(helper))
310 {
311 info(" ! Conntrack module '%s' for helper '%s' is not loaded",
312 helper->module, helper->name);
313 continue;
314 }
315
316 expand_helper_rule(handle, helper, zone);
317 }
318 }
319 }