1/* 2 * Copyright (c) 2007-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* $apfw: pf_ruleset.c,v 1.2 2007/08/10 03:00:16 jhw Exp $ */ 30/* $OpenBSD: pf_ruleset.c,v 1.1 2006/10/27 13:56:51 mcbride Exp $ */ 31 32/* 33 * Copyright (c) 2001 Daniel Hartmeier 34 * Copyright (c) 2002,2003 Henning Brauer 35 * All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 41 * - Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * - Redistributions in binary form must reproduce the above 44 * copyright notice, this list of conditions and the following 45 * disclaimer in the documentation and/or other materials provided 46 * with the distribution. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 51 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 52 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 53 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 54 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 55 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 56 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 58 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 59 * POSSIBILITY OF SUCH DAMAGE. 60 * 61 * Effort sponsored in part by the Defense Advanced Research Projects 62 * Agency (DARPA) and Air Force Research Laboratory, Air Force 63 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 64 * 65 */ 66 67#include <sys/param.h> 68#include <sys/socket.h> 69#ifdef KERNEL 70#include <sys/systm.h> 71#include <sys/malloc.h> 72#include <libkern/libkern.h> 73#endif /* KERNEL */ 74#include <sys/mbuf.h> 75 76#include <netinet/in.h> 77#include <netinet/in_systm.h> 78#include <netinet/ip.h> 79#include <netinet/tcp.h> 80 81#include <net/if.h> 82#include <net/pfvar.h> 83 84#if INET6 85#include <netinet/ip6.h> 86#endif /* INET6 */ 87 88 89#ifdef KERNEL 90#define DPFPRINTF(format, x...) \ 91 if (pf_status.debug >= PF_DEBUG_NOISY) \ 92 printf(format, ##x) 93#define rs_malloc(x) _MALLOC(x, M_TEMP, M_WAITOK) 94#define rs_free(x) _FREE(x, M_TEMP) 95#define strrchr _strrchr 96 97static char * 98_strrchr(const char *c, int ch) 99{ 100 char *p = (char *)(size_t)c, *save; 101 102 for (save = NULL; ; ++p) { 103 if (*p == ch) 104 save = (char *)p; 105 if (*p == '\0') 106 return (save); 107 } 108 /* NOTREACHED */ 109} 110 111#else 112/* Userland equivalents so we can lend code to pfctl et al. */ 113 114#include <arpa/inet.h> 115#include <errno.h> 116#include <stdio.h> 117#include <stdlib.h> 118#include <string.h> 119#define rs_malloc(x) malloc(x) 120#define rs_free(x) free(x) 121 122#ifdef PFDEBUG 123#include <sys/stdarg.h> 124#define DPFPRINTF(format, x...) fprintf(stderr, format, ##x) 125#else 126#define DPFPRINTF(format, x...) ((void)0) 127#endif /* PFDEBUG */ 128#endif /* KERNEL */ 129 130 131struct pf_anchor_global pf_anchors; 132struct pf_anchor pf_main_anchor; 133 134static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 135 136RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 137RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 138 139static __inline int 140pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) 141{ 142 int c = strcmp(a->path, b->path); 143 144 return (c ? (c < 0 ? -1 : 1) : 0); 145} 146 147int 148pf_get_ruleset_number(u_int8_t action) 149{ 150 switch (action) { 151 case PF_SCRUB: 152 case PF_NOSCRUB: 153 return (PF_RULESET_SCRUB); 154 break; 155 case PF_PASS: 156 case PF_DROP: 157 return (PF_RULESET_FILTER); 158 break; 159 case PF_NAT: 160 case PF_NONAT: 161 return (PF_RULESET_NAT); 162 break; 163 case PF_BINAT: 164 case PF_NOBINAT: 165 return (PF_RULESET_BINAT); 166 break; 167 case PF_RDR: 168 case PF_NORDR: 169 return (PF_RULESET_RDR); 170 break; 171#if DUMMYNET 172 case PF_DUMMYNET: 173 case PF_NODUMMYNET: 174 return (PF_RULESET_DUMMYNET); 175#endif /* DUMMYNET */ 176 default: 177 return (PF_RULESET_MAX); 178 break; 179 } 180} 181 182void 183pf_init_ruleset(struct pf_ruleset *ruleset) 184{ 185 int i; 186 187 memset(ruleset, 0, sizeof (struct pf_ruleset)); 188 for (i = 0; i < PF_RULESET_MAX; i++) { 189 TAILQ_INIT(&ruleset->rules[i].queues[0]); 190 TAILQ_INIT(&ruleset->rules[i].queues[1]); 191 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 192 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 193 } 194} 195 196struct pf_anchor * 197pf_find_anchor(const char *path) 198{ 199 struct pf_anchor *key, *found; 200 201 key = (struct pf_anchor *)rs_malloc(sizeof (*key)); 202 memset(key, 0, sizeof (*key)); 203 strlcpy(key->path, path, sizeof (key->path)); 204 found = RB_FIND(pf_anchor_global, &pf_anchors, key); 205 rs_free(key); 206 return (found); 207} 208 209struct pf_ruleset * 210pf_find_ruleset(const char *path) 211{ 212 struct pf_anchor *anchor; 213 214 while (*path == '/') 215 path++; 216 if (!*path) 217 return (&pf_main_ruleset); 218 anchor = pf_find_anchor(path); 219 if (anchor == NULL) 220 return (NULL); 221 else 222 return (&anchor->ruleset); 223} 224 225struct pf_ruleset * 226pf_find_ruleset_with_owner(const char *path, const char *owner, int is_anchor, 227 int *error) 228{ 229 struct pf_anchor *anchor; 230 231 while (*path == '/') 232 path++; 233 if (!*path) 234 return (&pf_main_ruleset); 235 anchor = pf_find_anchor(path); 236 if (anchor == NULL) { 237 *error = EINVAL; 238 return (NULL); 239 } else { 240 if ((owner && anchor->owner && (!strcmp(owner, anchor->owner))) 241 || (is_anchor && !strcmp(anchor->owner, ""))) 242 return (&anchor->ruleset); 243 *error = EPERM; 244 return NULL; 245 } 246} 247 248struct pf_ruleset * 249pf_find_or_create_ruleset(const char *path) 250{ 251 char *p, *q, *r; 252 struct pf_ruleset *ruleset; 253 struct pf_anchor *anchor = 0, *dup, *parent = NULL; 254 255 if (path[0] == 0) 256 return (&pf_main_ruleset); 257 while (*path == '/') 258 path++; 259 ruleset = pf_find_ruleset(path); 260 if (ruleset != NULL) 261 return (ruleset); 262 p = (char *)rs_malloc(MAXPATHLEN); 263 bzero(p, MAXPATHLEN); 264 strlcpy(p, path, MAXPATHLEN); 265 while (parent == NULL && (q = strrchr(p, '/')) != NULL) { 266 *q = 0; 267 if ((ruleset = pf_find_ruleset(p)) != NULL) { 268 parent = ruleset->anchor; 269 break; 270 } 271 } 272 if (q == NULL) 273 q = p; 274 else 275 q++; 276 strlcpy(p, path, MAXPATHLEN); 277 if (!*q) { 278 rs_free(p); 279 return (NULL); 280 } 281 while ((r = strchr(q, '/')) != NULL || *q) { 282 if (r != NULL) 283 *r = 0; 284 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE || 285 (parent != NULL && strlen(parent->path) >= 286 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) { 287 rs_free(p); 288 return (NULL); 289 } 290 anchor = (struct pf_anchor *)rs_malloc(sizeof (*anchor)); 291 if (anchor == NULL) { 292 rs_free(p); 293 return (NULL); 294 } 295 memset(anchor, 0, sizeof (*anchor)); 296 RB_INIT(&anchor->children); 297 strlcpy(anchor->name, q, sizeof (anchor->name)); 298 if (parent != NULL) { 299 strlcpy(anchor->path, parent->path, 300 sizeof (anchor->path)); 301 strlcat(anchor->path, "/", sizeof (anchor->path)); 302 } 303 strlcat(anchor->path, anchor->name, sizeof (anchor->path)); 304 if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != 305 NULL) { 306 printf("pf_find_or_create_ruleset: RB_INSERT1 " 307 "'%s' '%s' collides with '%s' '%s'\n", 308 anchor->path, anchor->name, dup->path, dup->name); 309 rs_free(anchor); 310 rs_free(p); 311 return (NULL); 312 } 313 if (parent != NULL) { 314 anchor->parent = parent; 315 if ((dup = RB_INSERT(pf_anchor_node, &parent->children, 316 anchor)) != NULL) { 317 printf("pf_find_or_create_ruleset: " 318 "RB_INSERT2 '%s' '%s' collides with " 319 "'%s' '%s'\n", anchor->path, anchor->name, 320 dup->path, dup->name); 321 RB_REMOVE(pf_anchor_global, &pf_anchors, 322 anchor); 323 rs_free(anchor); 324 rs_free(p); 325 return (NULL); 326 } 327 } 328 pf_init_ruleset(&anchor->ruleset); 329 anchor->ruleset.anchor = anchor; 330 parent = anchor; 331 if (r != NULL) 332 q = r + 1; 333 else 334 *q = 0; 335 } 336 rs_free(p); 337 return (anchor ? &anchor->ruleset : 0); 338} 339 340void 341pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 342{ 343 struct pf_anchor *parent; 344 int i; 345 346 while (ruleset != NULL) { 347 if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL || 348 !RB_EMPTY(&ruleset->anchor->children) || 349 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 350 ruleset->topen) 351 return; 352 for (i = 0; i < PF_RULESET_MAX; ++i) 353 if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 354 !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || 355 ruleset->rules[i].inactive.open) 356 return; 357 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); 358 if ((parent = ruleset->anchor->parent) != NULL) 359 RB_REMOVE(pf_anchor_node, &parent->children, 360 ruleset->anchor); 361 rs_free(ruleset->anchor); 362 if (parent == NULL) 363 return; 364 ruleset = &parent->ruleset; 365 } 366} 367 368int 369pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 370 const char *name) 371{ 372 char *p, *path; 373 struct pf_ruleset *ruleset; 374 375 r->anchor = NULL; 376 r->anchor_relative = 0; 377 r->anchor_wildcard = 0; 378 if (!name[0]) 379 return (0); 380 path = (char *)rs_malloc(MAXPATHLEN); 381 bzero(path, MAXPATHLEN); 382 if (name[0] == '/') 383 strlcpy(path, name + 1, MAXPATHLEN); 384 else { 385 /* relative path */ 386 r->anchor_relative = 1; 387 if (s->anchor == NULL || !s->anchor->path[0]) 388 path[0] = 0; 389 else 390 strlcpy(path, s->anchor->path, MAXPATHLEN); 391 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 392 if (!path[0]) { 393 printf("pf_anchor_setup: .. beyond root\n"); 394 rs_free(path); 395 return (1); 396 } 397 if ((p = strrchr(path, '/')) != NULL) 398 *p = 0; 399 else 400 path[0] = 0; 401 r->anchor_relative++; 402 name += 3; 403 } 404 if (path[0]) 405 strlcat(path, "/", MAXPATHLEN); 406 strlcat(path, name, MAXPATHLEN); 407 } 408 if ((p = strrchr(path, '/')) != NULL && strcmp(p, "/*") == 0) { 409 r->anchor_wildcard = 1; 410 *p = 0; 411 } 412 ruleset = pf_find_or_create_ruleset(path); 413 rs_free(path); 414 if (ruleset == NULL || ruleset->anchor == NULL) { 415 printf("pf_anchor_setup: ruleset\n"); 416 return (1); 417 } 418 r->anchor = ruleset->anchor; 419 r->anchor->refcnt++; 420 return (0); 421} 422 423int 424pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 425 struct pfioc_rule *pr) 426{ 427 pr->anchor_call[0] = 0; 428 if (r->anchor == NULL) 429 return (0); 430 if (!r->anchor_relative) { 431 strlcpy(pr->anchor_call, "/", sizeof (pr->anchor_call)); 432 strlcat(pr->anchor_call, r->anchor->path, 433 sizeof (pr->anchor_call)); 434 } else { 435 char *a, *p; 436 int i; 437 438 a = (char *)rs_malloc(MAXPATHLEN); 439 bzero(a, MAXPATHLEN); 440 if (rs->anchor == NULL) 441 a[0] = 0; 442 else 443 strlcpy(a, rs->anchor->path, MAXPATHLEN); 444 for (i = 1; i < r->anchor_relative; ++i) { 445 if ((p = strrchr(a, '/')) == NULL) 446 p = a; 447 *p = 0; 448 strlcat(pr->anchor_call, "../", 449 sizeof (pr->anchor_call)); 450 } 451 if (strncmp(a, r->anchor->path, strlen(a))) { 452 printf("pf_anchor_copyout: '%s' '%s'\n", a, 453 r->anchor->path); 454 rs_free(a); 455 return (1); 456 } 457 if (strlen(r->anchor->path) > strlen(a)) 458 strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 459 strlen(a) + 1 : 0), sizeof (pr->anchor_call)); 460 rs_free(a); 461 } 462 if (r->anchor_wildcard) 463 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 464 sizeof (pr->anchor_call)); 465 return (0); 466} 467 468void 469pf_anchor_remove(struct pf_rule *r) 470{ 471 if (r->anchor == NULL) 472 return; 473 if (r->anchor->refcnt <= 0) { 474 printf("pf_anchor_remove: broken refcount\n"); 475 r->anchor = NULL; 476 return; 477 } 478 if (!--r->anchor->refcnt) 479 pf_remove_if_empty_ruleset(&r->anchor->ruleset); 480 r->anchor = NULL; 481} 482