Git init
[profile/ivi/iptables.git] / extensions / libxt_policy.c
1 /* Shared library add-on to iptables to add policy support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <syslog.h>
7 #include <getopt.h>
8 #include <netdb.h>
9 #include <errno.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <xtables.h>
14
15 #include <linux/netfilter/xt_policy.h>
16
17 /*
18  * HACK: global pointer to current matchinfo for making
19  * final checks and adjustments in final_check.
20  */
21 static struct xt_policy_info *policy_info;
22
23 static void policy_help(void)
24 {
25         printf(
26 "policy match options:\n"
27 "  --dir in|out                 match policy applied during decapsulation/\n"
28 "                               policy to be applied during encapsulation\n"
29 "  --pol none|ipsec             match policy\n"
30 "  --strict                     match entire policy instead of single element\n"
31 "                               at any position\n"
32 "[!] --reqid reqid              match reqid\n"
33 "[!] --spi spi                  match SPI\n"
34 "[!] --proto proto              match protocol (ah/esp/ipcomp)\n"
35 "[!] --mode mode                match mode (transport/tunnel)\n"
36 "[!] --tunnel-src addr/mask     match tunnel source\n"
37 "[!] --tunnel-dst addr/mask     match tunnel destination\n"
38 "  --next                       begin next element in policy\n");
39 }
40
41 static const struct option policy_opts[] =
42 {
43         {
44                 .name           = "dir",
45                 .has_arg        = 1,
46                 .val            = '1',
47         },
48         {
49                 .name           = "pol",
50                 .has_arg        = 1,
51                 .val            = '2',
52         },
53         {
54                 .name           = "strict",
55                 .val            = '3'
56         },
57         {
58                 .name           = "reqid",
59                 .has_arg        = 1,
60                 .val            = '4',
61         },
62         {
63                 .name           = "spi",
64                 .has_arg        = 1,
65                 .val            = '5'
66         },
67         {
68                 .name           = "tunnel-src",
69                 .has_arg        = 1,
70                 .val            = '6'
71         },
72         {
73                 .name           = "tunnel-dst",
74                 .has_arg        = 1,
75                 .val            = '7'
76         },
77         {
78                 .name           = "proto",
79                 .has_arg        = 1,
80                 .val            = '8'
81         },
82         {
83                 .name           = "mode",
84                 .has_arg        = 1,
85                 .val            = '9'
86         },
87         {
88                 .name           = "next",
89                 .val            = 'a'
90         },
91         { .name = NULL }
92 };
93
94 static int parse_direction(char *s)
95 {
96         if (strcmp(s, "in") == 0)
97                 return XT_POLICY_MATCH_IN;
98         if (strcmp(s, "out") == 0)
99                 return XT_POLICY_MATCH_OUT;
100         xtables_error(PARAMETER_PROBLEM, "policy_match: invalid dir \"%s\"", s);
101 }
102
103 static int parse_policy(char *s)
104 {
105         if (strcmp(s, "none") == 0)
106                 return XT_POLICY_MATCH_NONE;
107         if (strcmp(s, "ipsec") == 0)
108                 return 0;
109         xtables_error(PARAMETER_PROBLEM, "policy match: invalid policy \"%s\"", s);
110 }
111
112 static int parse_mode(char *s)
113 {
114         if (strcmp(s, "transport") == 0)
115                 return XT_POLICY_MODE_TRANSPORT;
116         if (strcmp(s, "tunnel") == 0)
117                 return XT_POLICY_MODE_TUNNEL;
118         xtables_error(PARAMETER_PROBLEM, "policy match: invalid mode \"%s\"", s);
119 }
120
121 static int policy_parse(int c, char **argv, int invert, unsigned int *flags,
122                         struct xt_policy_info *info, uint8_t family)
123 {
124         struct xt_policy_elem *e = &info->pol[info->len];
125         struct in_addr *addr = NULL, mask;
126         struct in6_addr *addr6 = NULL, mask6;
127         unsigned int naddr = 0, num;
128         int mode;
129
130         xtables_check_inverse(optarg, &invert, &optind, 0, argv);
131
132         switch (c) {
133         case '1':
134                 if (info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT))
135                         xtables_error(PARAMETER_PROBLEM,
136                                    "policy match: double --dir option");
137                 if (invert)
138                         xtables_error(PARAMETER_PROBLEM,
139                                    "policy match: can't invert --dir option");
140
141                 info->flags |= parse_direction(optarg);
142                 break;
143         case '2':
144                 if (invert)
145                         xtables_error(PARAMETER_PROBLEM,
146                                    "policy match: can't invert --policy option");
147
148                 info->flags |= parse_policy(optarg);
149                 break;
150         case '3':
151                 if (info->flags & XT_POLICY_MATCH_STRICT)
152                         xtables_error(PARAMETER_PROBLEM,
153                                    "policy match: double --strict option");
154
155                 if (invert)
156                         xtables_error(PARAMETER_PROBLEM,
157                                    "policy match: can't invert --strict option");
158
159                 info->flags |= XT_POLICY_MATCH_STRICT;
160                 break;
161         case '4':
162                 if (e->match.reqid)
163                         xtables_error(PARAMETER_PROBLEM,
164                                    "policy match: double --reqid option");
165
166                 e->match.reqid = 1;
167                 e->invert.reqid = invert;
168                 if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
169                         xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg);
170                 e->reqid = num;
171                 break;
172         case '5':
173                 if (e->match.spi)
174                         xtables_error(PARAMETER_PROBLEM,
175                                    "policy match: double --spi option");
176
177                 e->match.spi = 1;
178                 e->invert.spi = invert;
179                 if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
180                         xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg);
181                 e->spi = num;
182                 break;
183         case '6':
184                 if (e->match.saddr)
185                         xtables_error(PARAMETER_PROBLEM,
186                                    "policy match: double --tunnel-src option");
187
188                 if (family == NFPROTO_IPV6)
189                         xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr);
190                 else
191                         xtables_ipparse_any(optarg, &addr, &mask, &naddr);
192                 if (naddr > 1)
193                         xtables_error(PARAMETER_PROBLEM,
194                                    "policy match: name resolves to multiple IPs");
195
196                 e->match.saddr = 1;
197                 e->invert.saddr = invert;
198                 if (family == NFPROTO_IPV6) {
199                         memcpy(&e->saddr.a6, addr6, sizeof(*addr6));
200                         memcpy(&e->smask.a6, &mask6, sizeof(mask6));
201                 } else {
202                         e->saddr.a4 = addr[0];
203                         e->smask.a4 = mask;
204                 }
205                 break;
206         case '7':
207                 if (e->match.daddr)
208                         xtables_error(PARAMETER_PROBLEM,
209                                    "policy match: double --tunnel-dst option");
210
211                 if (family == NFPROTO_IPV6)
212                         xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr);
213                 else
214                         xtables_ipparse_any(optarg, &addr, &mask, &naddr);
215                 if (naddr > 1)
216                         xtables_error(PARAMETER_PROBLEM,
217                                    "policy match: name resolves to multiple IPs");
218
219                 e->match.daddr = 1;
220                 e->invert.daddr = invert;
221                 if (family == NFPROTO_IPV6) {
222                         memcpy(&e->daddr.a6, addr6, sizeof(*addr6));
223                         memcpy(&e->dmask.a6, &mask6, sizeof(mask6));
224                 } else {
225                         e->daddr.a4 = addr[0];
226                         e->dmask.a4 = mask;
227                 }
228                 break;
229         case '8':
230                 if (e->match.proto)
231                         xtables_error(PARAMETER_PROBLEM,
232                                    "policy match: double --proto option");
233
234                 e->proto = xtables_parse_protocol(optarg);
235                 if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
236                     e->proto != IPPROTO_COMP)
237                         xtables_error(PARAMETER_PROBLEM,
238                                    "policy match: protocol must ah/esp/ipcomp");
239                 e->match.proto = 1;
240                 e->invert.proto = invert;
241                 break;
242         case '9':
243                 if (e->match.mode)
244                         xtables_error(PARAMETER_PROBLEM,
245                                    "policy match: double --mode option");
246
247                 mode = parse_mode(optarg);
248                 e->match.mode = 1;
249                 e->invert.mode = invert;
250                 e->mode = mode;
251                 break;
252         case 'a':
253                 if (invert)
254                         xtables_error(PARAMETER_PROBLEM,
255                                    "policy match: can't invert --next option");
256
257                 if (++info->len == XT_POLICY_MAX_ELEM)
258                         xtables_error(PARAMETER_PROBLEM,
259                                    "policy match: maximum policy depth reached");
260                 break;
261         default:
262                 return 0;
263         }
264
265         policy_info = info;
266         return 1;
267 }
268
269 static int policy4_parse(int c, char **argv, int invert, unsigned int *flags,
270                          const void *entry, struct xt_entry_match **match)
271 {
272         return policy_parse(c, argv, invert, flags, (void *)(*match)->data,
273                NFPROTO_IPV4);
274 }
275
276 static int policy6_parse(int c, char **argv, int invert, unsigned int *flags,
277                         const void *entry, struct xt_entry_match **match)
278 {
279         return policy_parse(c, argv, invert, flags, (void *)(*match)->data,
280                NFPROTO_IPV6);
281 }
282
283 static void policy_check(unsigned int flags)
284 {
285         struct xt_policy_info *info = policy_info;
286         struct xt_policy_elem *e;
287         int i;
288
289         if (info == NULL)
290                 xtables_error(PARAMETER_PROBLEM,
291                            "policy match: no parameters given");
292
293         if (!(info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT)))
294                 xtables_error(PARAMETER_PROBLEM,
295                            "policy match: neither --dir in nor --dir out specified");
296
297         if (info->flags & XT_POLICY_MATCH_NONE) {
298                 if (info->flags & XT_POLICY_MATCH_STRICT)
299                         xtables_error(PARAMETER_PROBLEM,
300                                    "policy match: policy none but --strict given");
301
302                 if (info->len != 0)
303                         xtables_error(PARAMETER_PROBLEM,
304                                    "policy match: policy none but policy given");
305         } else
306                 info->len++;    /* increase len by 1, no --next after last element */
307
308         if (!(info->flags & XT_POLICY_MATCH_STRICT) && info->len > 1)
309                 xtables_error(PARAMETER_PROBLEM,
310                            "policy match: multiple elements but no --strict");
311
312         for (i = 0; i < info->len; i++) {
313                 e = &info->pol[i];
314
315                 if (info->flags & XT_POLICY_MATCH_STRICT &&
316                     !(e->match.reqid || e->match.spi || e->match.saddr ||
317                       e->match.daddr || e->match.proto || e->match.mode))
318                         xtables_error(PARAMETER_PROBLEM,
319                                    "policy match: empty policy element");
320
321                 if ((e->match.saddr || e->match.daddr)
322                     && ((e->mode == XT_POLICY_MODE_TUNNEL && e->invert.mode) ||
323                         (e->mode == XT_POLICY_MODE_TRANSPORT && !e->invert.mode)))
324                         xtables_error(PARAMETER_PROBLEM,
325                                    "policy match: --tunnel-src/--tunnel-dst "
326                                    "is only valid in tunnel mode");
327         }
328 }
329
330 static void print_mode(const char *prefix, u_int8_t mode, int numeric)
331 {
332         printf("%smode ", prefix);
333
334         switch (mode) {
335         case XT_POLICY_MODE_TRANSPORT:
336                 printf("transport ");
337                 break;
338         case XT_POLICY_MODE_TUNNEL:
339                 printf("tunnel ");
340                 break;
341         default:
342                 printf("??? ");
343                 break;
344         }
345 }
346
347 static void print_proto(const char *prefix, u_int8_t proto, int numeric)
348 {
349         struct protoent *p = NULL;
350
351         printf("%sproto ", prefix);
352         if (!numeric)
353                 p = getprotobynumber(proto);
354         if (p != NULL)
355                 printf("%s ", p->p_name);
356         else
357                 printf("%u ", proto);
358 }
359
360 #define PRINT_INVERT(x)         \
361 do {                            \
362         if (x)                  \
363                 printf("! ");   \
364 } while(0)
365
366 static void print_entry(const char *prefix, const struct xt_policy_elem *e,
367                         bool numeric, uint8_t family)
368 {
369         if (e->match.reqid) {
370                 PRINT_INVERT(e->invert.reqid);
371                 printf("%sreqid %u ", prefix, e->reqid);
372         }
373         if (e->match.spi) {
374                 PRINT_INVERT(e->invert.spi);
375                 printf("%sspi 0x%x ", prefix, e->spi);
376         }
377         if (e->match.proto) {
378                 PRINT_INVERT(e->invert.proto);
379                 print_proto(prefix, e->proto, numeric);
380         }
381         if (e->match.mode) {
382                 PRINT_INVERT(e->invert.mode);
383                 print_mode(prefix, e->mode, numeric);
384         }
385         if (e->match.daddr) {
386                 PRINT_INVERT(e->invert.daddr);
387                 if (family == NFPROTO_IPV6)
388                         printf("%stunnel-dst %s%s ", prefix,
389                                xtables_ip6addr_to_numeric(&e->daddr.a6),
390                                xtables_ip6mask_to_numeric(&e->dmask.a6));
391                 else
392                         printf("%stunnel-dst %s%s ", prefix,
393                                xtables_ipaddr_to_numeric(&e->daddr.a4),
394                                xtables_ipmask_to_numeric(&e->dmask.a4));
395         }
396         if (e->match.saddr) {
397                 PRINT_INVERT(e->invert.saddr);
398                 if (family == NFPROTO_IPV6)
399                         printf("%stunnel-src %s%s ", prefix,
400                                xtables_ip6addr_to_numeric(&e->saddr.a6),
401                                xtables_ip6mask_to_numeric(&e->smask.a6));
402                 else
403                         printf("%stunnel-src %s%s ", prefix,
404                                xtables_ipaddr_to_numeric(&e->saddr.a4),
405                                xtables_ipmask_to_numeric(&e->smask.a4));
406         }
407 }
408
409 static void print_flags(char *prefix, const struct xt_policy_info *info)
410 {
411         if (info->flags & XT_POLICY_MATCH_IN)
412                 printf("%sdir in ", prefix);
413         else
414                 printf("%sdir out ", prefix);
415
416         if (info->flags & XT_POLICY_MATCH_NONE)
417                 printf("%spol none ", prefix);
418         else
419                 printf("%spol ipsec ", prefix);
420
421         if (info->flags & XT_POLICY_MATCH_STRICT)
422                 printf("%sstrict ", prefix);
423 }
424
425 static void policy4_print(const void *ip, const struct xt_entry_match *match,
426                           int numeric)
427 {
428         const struct xt_policy_info *info = (void *)match->data;
429         unsigned int i;
430
431         printf("policy match ");
432         print_flags("", info);
433         for (i = 0; i < info->len; i++) {
434                 if (info->len > 1)
435                         printf("[%u] ", i);
436                 print_entry("", &info->pol[i], numeric, NFPROTO_IPV4);
437         }
438 }
439
440 static void policy6_print(const void *ip, const struct xt_entry_match *match,
441                           int numeric)
442 {
443         const struct xt_policy_info *info = (void *)match->data;
444         unsigned int i;
445
446         printf("policy match ");
447         print_flags("", info);
448         for (i = 0; i < info->len; i++) {
449                 if (info->len > 1)
450                         printf("[%u] ", i);
451                 print_entry("", &info->pol[i], numeric, NFPROTO_IPV6);
452         }
453 }
454
455 static void policy4_save(const void *ip, const struct xt_entry_match *match)
456 {
457         const struct xt_policy_info *info = (void *)match->data;
458         unsigned int i;
459
460         print_flags("--", info);
461         for (i = 0; i < info->len; i++) {
462                 print_entry("--", &info->pol[i], false, NFPROTO_IPV4);
463                 if (i + 1 < info->len)
464                         printf("--next ");
465         }
466 }
467
468 static void policy6_save(const void *ip, const struct xt_entry_match *match)
469 {
470         const struct xt_policy_info *info = (void *)match->data;
471         unsigned int i;
472
473         print_flags("--", info);
474         for (i = 0; i < info->len; i++) {
475                 print_entry("--", &info->pol[i], false, NFPROTO_IPV6);
476                 if (i + 1 < info->len)
477                         printf("--next ");
478         }
479 }
480
481 static struct xtables_match policy_mt_reg[] = {
482         {
483                 .name          = "policy",
484                 .version       = XTABLES_VERSION,
485                 .family        = NFPROTO_IPV4,
486                 .size          = XT_ALIGN(sizeof(struct xt_policy_info)),
487                 .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)),
488                 .help          = policy_help,
489                 .parse         = policy4_parse,
490                 .final_check   = policy_check,
491                 .print         = policy4_print,
492                 .save          = policy4_save,
493                 .extra_opts    = policy_opts,
494         },
495         {
496                 .name          = "policy",
497                 .version       = XTABLES_VERSION,
498                 .family        = NFPROTO_IPV6,
499                 .size          = XT_ALIGN(sizeof(struct xt_policy_info)),
500                 .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)),
501                 .help          = policy_help,
502                 .parse         = policy6_parse,
503                 .final_check   = policy_check,
504                 .print         = policy6_print,
505                 .save          = policy6_save,
506                 .extra_opts    = policy_opts,
507         },
508 };
509
510 void _init(void)
511 {
512         xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg));
513 }