libxt_policy.c revision 028ad9ec6d5c27c107c9a7a316617cbe366abb0f
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 */ 21static struct xt_policy_info *policy_info; 22 23static 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 41static 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 94static 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 103static 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 112static 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 121static 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 269static 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 276static 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 283static 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 330static 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 347static 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) \ 361do { \ 362 if (x) \ 363 printf("! "); \ 364} while(0) 365 366static 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 409static 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 425static 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 440static 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 455static 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 468static 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 481static 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 510void _init(void) 511{ 512 xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg)); 513} 514