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