libxt_policy.c revision 7ac405297ec38449b30e3b05fd6bf2082fd3d803
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 default: 265 return 0; 266 } 267 268 policy_info = info; 269 return 1; 270} 271 272static int policy4_parse(int c, char **argv, int invert, unsigned int *flags, 273 const void *entry, struct xt_entry_match **match) 274{ 275 return policy_parse(c, argv, invert, flags, (void *)(*match)->data, 276 NFPROTO_IPV4); 277} 278 279static int policy6_parse(int c, char **argv, int invert, unsigned int *flags, 280 const void *entry, struct xt_entry_match **match) 281{ 282 return policy_parse(c, argv, invert, flags, (void *)(*match)->data, 283 NFPROTO_IPV6); 284} 285 286static void policy_check(unsigned int flags) 287{ 288 struct xt_policy_info *info = policy_info; 289 struct xt_policy_elem *e; 290 int i; 291 292 if (info == NULL) 293 xtables_error(PARAMETER_PROBLEM, 294 "policy match: no parameters given"); 295 296 if (!(info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT))) 297 xtables_error(PARAMETER_PROBLEM, 298 "policy match: neither --dir in nor --dir out specified"); 299 300 if (info->flags & XT_POLICY_MATCH_NONE) { 301 if (info->flags & XT_POLICY_MATCH_STRICT) 302 xtables_error(PARAMETER_PROBLEM, 303 "policy match: policy none but --strict given"); 304 305 if (info->len != 0) 306 xtables_error(PARAMETER_PROBLEM, 307 "policy match: policy none but policy given"); 308 } else 309 info->len++; /* increase len by 1, no --next after last element */ 310 311 if (!(info->flags & XT_POLICY_MATCH_STRICT) && info->len > 1) 312 xtables_error(PARAMETER_PROBLEM, 313 "policy match: multiple elements but no --strict"); 314 315 for (i = 0; i < info->len; i++) { 316 e = &info->pol[i]; 317 318 if (info->flags & XT_POLICY_MATCH_STRICT && 319 !(e->match.reqid || e->match.spi || e->match.saddr || 320 e->match.daddr || e->match.proto || e->match.mode)) 321 xtables_error(PARAMETER_PROBLEM, 322 "policy match: empty policy element"); 323 324 if ((e->match.saddr || e->match.daddr) 325 && ((e->mode == XT_POLICY_MODE_TUNNEL && e->invert.mode) || 326 (e->mode == XT_POLICY_MODE_TRANSPORT && !e->invert.mode))) 327 xtables_error(PARAMETER_PROBLEM, 328 "policy match: --tunnel-src/--tunnel-dst " 329 "is only valid in tunnel mode"); 330 } 331} 332 333static void print_mode(const char *prefix, uint8_t mode, int numeric) 334{ 335 printf("%smode ", prefix); 336 337 switch (mode) { 338 case XT_POLICY_MODE_TRANSPORT: 339 printf("transport "); 340 break; 341 case XT_POLICY_MODE_TUNNEL: 342 printf("tunnel "); 343 break; 344 default: 345 printf("??? "); 346 break; 347 } 348} 349 350static void print_proto(const char *prefix, uint8_t proto, int numeric) 351{ 352 struct protoent *p = NULL; 353 354 printf("%sproto ", prefix); 355 if (!numeric) 356 p = getprotobynumber(proto); 357 if (p != NULL) 358 printf("%s ", p->p_name); 359 else 360 printf("%u ", proto); 361} 362 363#define PRINT_INVERT(x) \ 364do { \ 365 if (x) \ 366 printf("! "); \ 367} while(0) 368 369static void print_entry(const char *prefix, const struct xt_policy_elem *e, 370 bool numeric, uint8_t family) 371{ 372 if (e->match.reqid) { 373 PRINT_INVERT(e->invert.reqid); 374 printf("%sreqid %u ", prefix, e->reqid); 375 } 376 if (e->match.spi) { 377 PRINT_INVERT(e->invert.spi); 378 printf("%sspi 0x%x ", prefix, e->spi); 379 } 380 if (e->match.proto) { 381 PRINT_INVERT(e->invert.proto); 382 print_proto(prefix, e->proto, numeric); 383 } 384 if (e->match.mode) { 385 PRINT_INVERT(e->invert.mode); 386 print_mode(prefix, e->mode, numeric); 387 } 388 if (e->match.daddr) { 389 PRINT_INVERT(e->invert.daddr); 390 if (family == NFPROTO_IPV6) 391 printf("%stunnel-dst %s%s ", prefix, 392 xtables_ip6addr_to_numeric(&e->daddr.a6), 393 xtables_ip6mask_to_numeric(&e->dmask.a6)); 394 else 395 printf("%stunnel-dst %s%s ", prefix, 396 xtables_ipaddr_to_numeric(&e->daddr.a4), 397 xtables_ipmask_to_numeric(&e->dmask.a4)); 398 } 399 if (e->match.saddr) { 400 PRINT_INVERT(e->invert.saddr); 401 if (family == NFPROTO_IPV6) 402 printf("%stunnel-src %s%s ", prefix, 403 xtables_ip6addr_to_numeric(&e->saddr.a6), 404 xtables_ip6mask_to_numeric(&e->smask.a6)); 405 else 406 printf("%stunnel-src %s%s ", prefix, 407 xtables_ipaddr_to_numeric(&e->saddr.a4), 408 xtables_ipmask_to_numeric(&e->smask.a4)); 409 } 410} 411 412static void print_flags(char *prefix, const struct xt_policy_info *info) 413{ 414 if (info->flags & XT_POLICY_MATCH_IN) 415 printf("%sdir in ", prefix); 416 else 417 printf("%sdir out ", prefix); 418 419 if (info->flags & XT_POLICY_MATCH_NONE) 420 printf("%spol none ", prefix); 421 else 422 printf("%spol ipsec ", prefix); 423 424 if (info->flags & XT_POLICY_MATCH_STRICT) 425 printf("%sstrict ", prefix); 426} 427 428static void policy4_print(const void *ip, const struct xt_entry_match *match, 429 int numeric) 430{ 431 const struct xt_policy_info *info = (void *)match->data; 432 unsigned int i; 433 434 printf("policy match "); 435 print_flags("", info); 436 for (i = 0; i < info->len; i++) { 437 if (info->len > 1) 438 printf("[%u] ", i); 439 print_entry("", &info->pol[i], numeric, NFPROTO_IPV4); 440 } 441} 442 443static void policy6_print(const void *ip, const struct xt_entry_match *match, 444 int numeric) 445{ 446 const struct xt_policy_info *info = (void *)match->data; 447 unsigned int i; 448 449 printf("policy match "); 450 print_flags("", info); 451 for (i = 0; i < info->len; i++) { 452 if (info->len > 1) 453 printf("[%u] ", i); 454 print_entry("", &info->pol[i], numeric, NFPROTO_IPV6); 455 } 456} 457 458static void policy4_save(const void *ip, const struct xt_entry_match *match) 459{ 460 const struct xt_policy_info *info = (void *)match->data; 461 unsigned int i; 462 463 print_flags("--", info); 464 for (i = 0; i < info->len; i++) { 465 print_entry("--", &info->pol[i], false, NFPROTO_IPV4); 466 if (i + 1 < info->len) 467 printf("--next "); 468 } 469} 470 471static void policy6_save(const void *ip, const struct xt_entry_match *match) 472{ 473 const struct xt_policy_info *info = (void *)match->data; 474 unsigned int i; 475 476 print_flags("--", info); 477 for (i = 0; i < info->len; i++) { 478 print_entry("--", &info->pol[i], false, NFPROTO_IPV6); 479 if (i + 1 < info->len) 480 printf("--next "); 481 } 482} 483 484static struct xtables_match policy_mt_reg[] = { 485 { 486 .name = "policy", 487 .version = XTABLES_VERSION, 488 .family = NFPROTO_IPV4, 489 .size = XT_ALIGN(sizeof(struct xt_policy_info)), 490 .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)), 491 .help = policy_help, 492 .parse = policy4_parse, 493 .final_check = policy_check, 494 .print = policy4_print, 495 .save = policy4_save, 496 .extra_opts = policy_opts, 497 }, 498 { 499 .name = "policy", 500 .version = XTABLES_VERSION, 501 .family = NFPROTO_IPV6, 502 .size = XT_ALIGN(sizeof(struct xt_policy_info)), 503 .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)), 504 .help = policy_help, 505 .parse = policy6_parse, 506 .final_check = policy_check, 507 .print = policy6_print, 508 .save = policy6_save, 509 .extra_opts = policy_opts, 510 }, 511}; 512 513void _init(void) 514{ 515 xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg)); 516} 517