redirects: fix segmentation fault
[oweals/firewall3.git] / rules.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013-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 "rules.h"
20
21
22 const struct fw3_option fw3_rule_opts[] = {
23         FW3_OPT("enabled",             bool,      rule,     enabled),
24
25         FW3_OPT("name",                string,    rule,     name),
26         FW3_OPT("family",              family,    rule,     family),
27
28         FW3_OPT("src",                 device,    rule,     src),
29         FW3_OPT("dest",                device,    rule,     dest),
30
31         FW3_OPT("device",              string,    rule,     device),
32         FW3_OPT("direction",           direction, rule,     direction_out),
33
34         FW3_OPT("ipset",               setmatch,  rule,     ipset),
35         FW3_OPT("helper",              cthelper,  rule,     helper),
36         FW3_OPT("set_helper",          cthelper,  rule,     set_helper),
37
38         FW3_LIST("proto",              protocol,  rule,     proto),
39
40         FW3_LIST("src_ip",             network,   rule,     ip_src),
41         FW3_LIST("src_mac",            mac,       rule,     mac_src),
42         FW3_LIST("src_port",           port,      rule,     port_src),
43
44         FW3_LIST("dest_ip",            network,   rule,     ip_dest),
45         FW3_LIST("dest_port",          port,      rule,     port_dest),
46
47         FW3_LIST("icmp_type",          icmptype,  rule,     icmp_type),
48         FW3_OPT("extra",               string,    rule,     extra),
49
50         FW3_OPT("limit",               limit,     rule,     limit),
51         FW3_OPT("limit_burst",         int,       rule,     limit.burst),
52
53         FW3_OPT("utc_time",            bool,      rule,     time.utc),
54         FW3_OPT("start_date",          date,      rule,     time.datestart),
55         FW3_OPT("stop_date",           date,      rule,     time.datestop),
56         FW3_OPT("start_time",          time,      rule,     time.timestart),
57         FW3_OPT("stop_time",           time,      rule,     time.timestop),
58         FW3_OPT("weekdays",            weekdays,  rule,     time.weekdays),
59         FW3_OPT("monthdays",           monthdays, rule,     time.monthdays),
60
61         FW3_OPT("mark",                mark,      rule,     mark),
62         FW3_OPT("set_mark",            mark,      rule,     set_mark),
63         FW3_OPT("set_xmark",           mark,      rule,     set_xmark),
64
65         FW3_OPT("dscp",                dscp,      rule,     dscp),
66         FW3_OPT("set_dscp",            dscp,      rule,     set_dscp),
67
68         FW3_OPT("target",              target,    rule,     target),
69
70         { }
71 };
72
73
74 static bool
75 need_src_action_chain(struct fw3_rule *r)
76 {
77         return (r->_src && r->_src->log && (r->target > FW3_FLAG_ACCEPT));
78 }
79
80 static struct fw3_rule*
81 alloc_rule(struct fw3_state *state)
82 {
83         struct fw3_rule *rule = calloc(1, sizeof(*rule));
84
85         if (rule) {
86                 INIT_LIST_HEAD(&rule->proto);
87
88                 INIT_LIST_HEAD(&rule->ip_src);
89                 INIT_LIST_HEAD(&rule->mac_src);
90                 INIT_LIST_HEAD(&rule->port_src);
91
92                 INIT_LIST_HEAD(&rule->ip_dest);
93                 INIT_LIST_HEAD(&rule->port_dest);
94
95                 INIT_LIST_HEAD(&rule->icmp_type);
96
97                 list_add_tail(&rule->list, &state->rules);
98                 rule->enabled = true;
99         }
100
101         return rule;
102 }
103
104 static bool
105 check_rule(struct fw3_state *state, struct fw3_rule *r, struct uci_element *e)
106 {
107         if (!r->enabled)
108                 return false;
109
110         if (r->src.invert || r->dest.invert)
111         {
112                 warn_section("rule", r, e, "must not have inverted 'src' or 'dest' options");
113                 return false;
114         }
115         else if (r->src.set && !r->src.any &&
116                  !(r->_src = fw3_lookup_zone(state, r->src.name)))
117         {
118                 warn_section("rule", r, e, "refers to not existing zone '%s'", r->src.name);
119                 return false;
120         }
121         else if (r->dest.set && !r->dest.any &&
122                  !(r->_dest = fw3_lookup_zone(state, r->dest.name)))
123         {
124                 warn_section("rule", r, e, "refers to not existing zone '%s'", r->dest.name);
125                 return false;
126         }
127         else if (r->ipset.set && state->disable_ipsets)
128         {
129                 warn_section("rule", r, e, "skipped due to disabled ipset support");
130                 return false;
131         }
132         else if (r->ipset.set &&
133                  !(r->ipset.ptr = fw3_lookup_ipset(state, r->ipset.name)))
134         {
135                 warn_section("rule", r, e, "refers to unknown ipset '%s'", r->ipset.name);
136                 return false;
137         }
138         else if (r->helper.set &&
139                  !(r->helper.ptr = fw3_lookup_cthelper(state, r->helper.name)))
140         {
141                 warn_section("rule", r, e, "refers to unknown CT helper '%s'", r->helper.name);
142                 return false;
143         }
144         else if (r->set_helper.set &&
145                  !(r->set_helper.ptr = fw3_lookup_cthelper(state, r->set_helper.name)))
146         {
147                 warn_section("rule", r, e, "refers to unknown CT helper '%s'", r->set_helper.name);
148                 return false;
149         }
150
151         if (!r->_src && (r->target == FW3_FLAG_NOTRACK || r->target == FW3_FLAG_HELPER))
152         {
153                 warn_section("rule", r, e, "is set to target %s but has no source assigned",
154                              fw3_flag_names[r->target]);
155                 return false;
156         }
157
158         if (!r->set_mark.set && !r->set_xmark.set &&
159             r->target == FW3_FLAG_MARK)
160         {
161                 warn_section("rule", r, e, "is set to target MARK but specifies neither "
162                                            "'set_mark' nor 'set_xmark' option");
163                 return false;
164         }
165
166         if (!r->set_dscp.set && r->target == FW3_FLAG_DSCP)
167         {
168                 warn_section("rule", r, e, "is set to target DSCP but specifies no "
169                                            "'set_dscp' option");
170                 return false;
171         }
172
173         if (r->_dest && (r->target == FW3_FLAG_MARK || r->target == FW3_FLAG_DSCP))
174         {
175                 warn_section("rule", r, e, "must not specify 'dest' for %s target",
176                              fw3_flag_names[r->target]);
177                 return false;
178         }
179
180         if (r->set_mark.invert || r->set_xmark.invert || r->set_dscp.invert)
181         {
182                 warn_section("rule", r, e, "must not have inverted 'set_mark', "
183                                            "'set_xmark' or 'set_dscp'");
184                 return false;
185         }
186
187         if (!r->set_helper.set && r->target == FW3_FLAG_HELPER)
188         {
189                 warn_section("rule", r, e, "is set to target HELPER but specifies "
190                                            "no 'set_helper' option");
191                 return false;
192         }
193
194         if (r->set_helper.invert && r->target == FW3_FLAG_HELPER)
195         {
196                 warn_section("rule", r, e, "must not have inverted 'set_helper' option");
197                 return false;
198         }
199
200         if (!r->_src && !r->_dest && !r->src.any && !r->dest.any)
201         {
202                 warn_section("rule", r, e, "has neither a source nor a destination zone assigned "
203                                 "- assuming an output rule");
204         }
205
206         if (list_empty(&r->proto))
207         {
208                 warn_section("rule", r, e, "does not specify a protocol, assuming TCP+UDP");
209                 fw3_parse_protocol(&r->proto, "tcpudp", true);
210         }
211
212         if (r->target == FW3_FLAG_UNSPEC)
213         {
214                 warn_section("rule", r, e, "has no target specified, defaulting to REJECT");
215                 r->target = FW3_FLAG_REJECT;
216         }
217         else if (r->target > FW3_FLAG_DSCP)
218         {
219                 warn_section("rule", r, e, "has invalid target specified, defaulting to REJECT");
220                 r->target = FW3_FLAG_REJECT;
221         }
222
223         /* NB: r family... */
224         if (r->_dest)
225         {
226                 fw3_setbit(r->_dest->flags[0], r->target);
227                 fw3_setbit(r->_dest->flags[1], r->target);
228         }
229         else if (need_src_action_chain(r))
230         {
231                 fw3_setbit(r->_src->flags[0], fw3_to_src_target(r->target));
232                 fw3_setbit(r->_src->flags[1], fw3_to_src_target(r->target));
233         }
234
235         return true;
236 }
237
238 void
239 fw3_load_rules(struct fw3_state *state, struct uci_package *p,
240                 struct blob_attr *a)
241 {
242         struct uci_section *s;
243         struct uci_element *e;
244         struct fw3_rule *rule;
245         struct blob_attr *entry;
246         unsigned rem;
247
248         INIT_LIST_HEAD(&state->rules);
249
250         blob_for_each_attr(entry, a, rem) {
251                 const char *type;
252                 const char *name = "ubus rule";
253
254                 if (!fw3_attr_parse_name_type(entry, &name, &type))
255                         continue;
256
257                 if (strcmp(type, "rule"))
258                         continue;
259
260                 if (!(rule = alloc_rule(state)))
261                         continue;
262
263                 if (!fw3_parse_blob_options(rule, fw3_rule_opts, entry, name))
264                 {
265                         warn_section("rule", rule, NULL, "skipped due to invalid options");
266                         fw3_free_rule(rule);
267                         continue;
268                 }
269
270                 if (!check_rule(state, rule, NULL))
271                         fw3_free_rule(rule);
272         }
273
274         uci_foreach_element(&p->sections, e)
275         {
276                 s = uci_to_section(e);
277
278                 if (strcmp(s->type, "rule"))
279                         continue;
280
281                 if (!(rule = alloc_rule(state)))
282                         continue;
283
284                 if (!fw3_parse_options(rule, fw3_rule_opts, s))
285                 {
286                         warn_elem(e, "skipped due to invalid options");
287                         fw3_free_rule(rule);
288                         continue;
289                 }
290
291                 if (!check_rule(state, rule, e))
292                         fw3_free_rule(rule);
293         }
294 }
295
296
297 static void
298 append_chain(struct fw3_ipt_rule *r, struct fw3_rule *rule)
299 {
300         char chain[32];
301
302         snprintf(chain, sizeof(chain), "OUTPUT");
303
304         if (rule->target == FW3_FLAG_NOTRACK)
305         {
306                 snprintf(chain, sizeof(chain), "zone_%s_notrack", rule->src.name);
307         }
308         else if (rule->target == FW3_FLAG_HELPER)
309         {
310                 snprintf(chain, sizeof(chain), "zone_%s_helper", rule->src.name);
311         }
312         else if ((rule->target == FW3_FLAG_MARK || rule->target == FW3_FLAG_DSCP) &&
313                  (rule->_src || rule->src.any))
314         {
315                 snprintf(chain, sizeof(chain), "PREROUTING");
316         }
317         else
318         {
319                 if (rule->src.set)
320                 {
321                         if (!rule->src.any)
322                         {
323                                 if (rule->dest.set)
324                                         snprintf(chain, sizeof(chain), "zone_%s_forward",
325                                                  rule->src.name);
326                                 else
327                                         snprintf(chain, sizeof(chain), "zone_%s_input",
328                                                  rule->src.name);
329                         }
330                         else
331                         {
332                                 if (rule->dest.set)
333                                         snprintf(chain, sizeof(chain), "FORWARD");
334                                 else
335                                         snprintf(chain, sizeof(chain), "INPUT");
336                         }
337                 }
338
339                 if (rule->dest.set && !rule->src.set)
340                 {
341                         if (rule->dest.any)
342                                 snprintf(chain, sizeof(chain), "OUTPUT");
343                         else
344                                 snprintf(chain, sizeof(chain), "zone_%s_output",
345                                          rule->dest.name);
346                 }
347         }
348
349         fw3_ipt_rule_append(r, chain);
350 }
351
352 static void set_target(struct fw3_ipt_rule *r, struct fw3_rule *rule)
353 {
354         const char *name;
355         struct fw3_mark *mark;
356         char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF")];
357
358         switch(rule->target)
359         {
360         case FW3_FLAG_MARK:
361                 name = rule->set_mark.set ? "--set-mark" : "--set-xmark";
362                 mark = rule->set_mark.set ? &rule->set_mark : &rule->set_xmark;
363                 snprintf(buf, sizeof(buf), "0x%x/0x%x", mark->mark, mark->mask);
364
365                 fw3_ipt_rule_target(r, "MARK");
366                 fw3_ipt_rule_addarg(r, false, name, buf);
367                 return;
368
369         case FW3_FLAG_DSCP:
370                 snprintf(buf, sizeof(buf), "0x%x", rule->set_dscp.dscp);
371
372                 fw3_ipt_rule_target(r, "DSCP");
373                 fw3_ipt_rule_addarg(r, false, "--set-dscp", buf);
374                 return;
375
376         case FW3_FLAG_NOTRACK:
377                 fw3_ipt_rule_target(r, "CT");
378                 fw3_ipt_rule_addarg(r, false, "--notrack", NULL);
379                 return;
380
381         case FW3_FLAG_HELPER:
382                 fw3_ipt_rule_target(r, "CT");
383                 fw3_ipt_rule_addarg(r, false, "--helper", rule->set_helper.ptr->name);
384                 return;
385
386         case FW3_FLAG_ACCEPT:
387         case FW3_FLAG_DROP:
388                 name = fw3_flag_names[rule->target];
389                 break;
390
391         default:
392                 name = fw3_flag_names[FW3_FLAG_REJECT];
393                 break;
394         }
395
396         if (rule->dest.set && !rule->dest.any)
397                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", rule->dest.name, name);
398         else if (need_src_action_chain(rule))
399                 fw3_ipt_rule_target(r, "zone_%s_src_%s", rule->src.name, name);
400         else if (strcmp(name, "REJECT"))
401                 fw3_ipt_rule_target(r, name);
402         else
403                 fw3_ipt_rule_target(r, "reject");
404 }
405
406 static void
407 set_comment(struct fw3_ipt_rule *r, const char *name, int num)
408 {
409         if (name)
410                 fw3_ipt_rule_comment(r, name);
411         else
412                 fw3_ipt_rule_comment(r, "@rule[%u]", num);
413 }
414
415 static void
416 print_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
417            struct fw3_rule *rule, int num, struct fw3_protocol *proto,
418            struct fw3_address *sip, struct fw3_address *dip,
419            struct fw3_port *sport, struct fw3_port *dport,
420            struct fw3_mac *mac, struct fw3_icmptype *icmptype)
421 {
422         struct fw3_ipt_rule *r;
423
424         if (!fw3_is_family(sip, handle->family) ||
425             !fw3_is_family(dip, handle->family))
426         {
427                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
428                         info("     ! Skipping due to different family of ip address");
429
430                 return;
431         }
432
433         if (!fw3_is_family(sip, handle->family) ||
434             !fw3_is_family(dip, handle->family))
435         {
436                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
437                         info("     ! Skipping due to different family of ip address");
438
439                 return;
440         }
441
442         if (!fw3_is_family(sip, handle->family) ||
443             !fw3_is_family(dip, handle->family))
444         {
445                 if ((sip && !sip->resolved) || (dip && !dip->resolved))
446                         info("     ! Skipping due to different family of ip address");
447
448                 return;
449         }
450
451         if (proto->protocol == 58 && handle->family == FW3_FAMILY_V4)
452         {
453                 info("     ! Skipping protocol %s due to different family",
454                      fw3_protoname(proto));
455                 return;
456         }
457
458         if (rule->helper.ptr &&
459             !fw3_cthelper_check_proto(rule->helper.ptr, proto))
460         {
461                 info("     ! Skipping protocol %s since helper '%s' does not support it",
462                      fw3_protoname(proto), rule->helper.ptr->name);
463                 return;
464         }
465
466         if (rule->set_helper.ptr &&
467             !fw3_cthelper_check_proto(rule->set_helper.ptr, proto))
468         {
469                 info("     ! Skipping protocol %s since helper '%s' does not support it",
470                      fw3_protoname(proto), rule->helper.ptr->name);
471                 return;
472         }
473
474         r = fw3_ipt_rule_create(handle, proto, NULL, NULL, sip, dip);
475         fw3_ipt_rule_sport_dport(r, sport, dport);
476         fw3_ipt_rule_device(r, rule->device, rule->direction_out);
477         fw3_ipt_rule_icmptype(r, icmptype);
478         fw3_ipt_rule_mac(r, mac);
479         fw3_ipt_rule_ipset(r, &rule->ipset);
480         fw3_ipt_rule_helper(r, &rule->helper);
481         fw3_ipt_rule_limit(r, &rule->limit);
482         fw3_ipt_rule_time(r, &rule->time);
483         fw3_ipt_rule_mark(r, &rule->mark);
484         fw3_ipt_rule_dscp(r, &rule->dscp);
485         set_target(r, rule);
486         fw3_ipt_rule_extra(r, rule->extra);
487         set_comment(r, rule->name, num);
488         append_chain(r, rule);
489 }
490
491 static void
492 expand_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
493             struct fw3_rule *rule, int num)
494 {
495         struct fw3_protocol *proto;
496         struct fw3_address *sip;
497         struct fw3_address *dip;
498         struct fw3_port *sport;
499         struct fw3_port *dport;
500         struct fw3_mac *mac;
501         struct fw3_icmptype *icmptype;
502
503         struct list_head *sports = NULL;
504         struct list_head *dports = NULL;
505         struct list_head *icmptypes = NULL;
506
507         struct list_head empty;
508         INIT_LIST_HEAD(&empty);
509
510         if (!fw3_is_family(rule, handle->family))
511                 return;
512
513         if ((rule->target == FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_RAW) ||
514             (rule->target == FW3_FLAG_HELPER && handle->table != FW3_TABLE_RAW)  ||
515             (rule->target == FW3_FLAG_MARK && handle->table != FW3_TABLE_MANGLE) ||
516             (rule->target == FW3_FLAG_DSCP && handle->table != FW3_TABLE_MANGLE) ||
517                 (rule->target < FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_FILTER))
518                 return;
519
520         if (rule->name)
521                 info("   * Rule '%s'", rule->name);
522         else
523                 info("   * Rule #%u", num);
524
525         if (!fw3_is_family(rule->_src, handle->family) ||
526             !fw3_is_family(rule->_dest, handle->family))
527         {
528                 info("     ! Skipping due to different family of zone");
529                 return;
530         }
531
532         if (rule->ipset.ptr)
533         {
534                 if (!fw3_is_family(rule->ipset.ptr, handle->family))
535                 {
536                         info("     ! Skipping due to different family in ipset");
537                         return;
538                 }
539
540                 if (!fw3_check_ipset(rule->ipset.ptr))
541                 {
542                         info("     ! Skipping due to missing ipset '%s'",
543                              rule->ipset.ptr->external
544                                         ? rule->ipset.ptr->external : rule->ipset.ptr->name);
545                         return;
546                 }
547
548                 set(rule->ipset.ptr->flags, handle->family, handle->family);
549         }
550
551         if (rule->helper.ptr && !fw3_is_family(rule->helper.ptr, handle->family))
552         {
553                 info("     ! Skipping due to unsupported family of CT helper");
554                 return;
555         }
556
557         if (rule->set_helper.ptr && !fw3_is_family(rule->set_helper.ptr, handle->family))
558         {
559                 info("     ! Skipping due to unsupported family of CT helper");
560                 return;
561         }
562
563         list_for_each_entry(proto, &rule->proto, list)
564         {
565                 /* icmp / ipv6-icmp */
566                 if (proto->protocol == 1 || proto->protocol == 58)
567                 {
568                         sports = &empty;
569                         dports = &empty;
570                         icmptypes = &rule->icmp_type;
571                 }
572                 else
573                 {
574                         sports = &rule->port_src;
575                         dports = &rule->port_dest;
576                         icmptypes = &empty;
577                 }
578
579                 fw3_foreach(sip, &rule->ip_src)
580                 fw3_foreach(dip, &rule->ip_dest)
581                 fw3_foreach(sport, sports)
582                 fw3_foreach(dport, dports)
583                 fw3_foreach(mac, &rule->mac_src)
584                 fw3_foreach(icmptype, icmptypes)
585                         print_rule(handle, state, rule, num, proto, sip, dip,
586                                    sport, dport, mac, icmptype);
587         }
588 }
589
590 void
591 fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state)
592 {
593         int num = 0;
594         struct fw3_rule *rule;
595
596         list_for_each_entry(rule, &state->rules, list)
597                 expand_rule(handle, state, rule, num++);
598 }