1 /* $OpenBSD: pf_ruleset.c,v 1.20 2023/04/28 14:08:38 sashan Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Daniel Hartmeier 5 * Copyright (c) 2002,2003 Henning Brauer 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * - Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * - Redistributions in binary form must reproduce the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer in the documentation and/or other materials provided 17 * with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 * 32 * Effort sponsored in part by the Defense Advanced Research Projects 33 * Agency (DARPA) and Air Force Research Laboratory, Air Force 34 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 35 * 36 */ 37 38 #include <sys/param.h> 39 #include <sys/socket.h> 40 #ifdef _KERNEL 41 #include <sys/systm.h> 42 #include <sys/mbuf.h> 43 #include <sys/pool.h> 44 #endif /* _KERNEL */ 45 #include <sys/syslog.h> 46 47 #include <netinet/in.h> 48 #include <netinet/ip.h> 49 #include <netinet/tcp.h> 50 51 #include <net/if.h> 52 #include <net/pfvar.h> 53 54 #ifdef INET6 55 #include <netinet/ip6.h> 56 #endif /* INET6 */ 57 58 59 #ifdef _KERNEL 60 #define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO) 61 #define rs_free(x, siz) free(x, M_TEMP, siz) 62 #define rs_pool_get_anchor() pool_get(&pf_anchor_pl, \ 63 PR_WAITOK|PR_LIMITFAIL|PR_ZERO) 64 #define rs_pool_put_anchor(x) pool_put(&pf_anchor_pl, x) 65 66 struct pool pf_anchor_pl; 67 68 #else /* !_KERNEL */ 69 /* Userland equivalents so we can lend code to pfctl et al. */ 70 71 #include <arpa/inet.h> 72 #include <errno.h> 73 #include <stdio.h> 74 #include <stdlib.h> 75 #include <string.h> 76 #define rs_malloc(x) calloc(1, x) 77 #define rs_free(x, siz) freezero(x, siz) 78 #define rs_pool_get_anchor() calloc(1, sizeof(struct pf_anchor)) 79 #define rs_pool_put_anchor(x) freezero(x, sizeof(struct pf_anchor)) 80 81 #ifdef PFDEBUG 82 #include <sys/stdarg.h> /* for DPFPRINTF() */ 83 #endif /* PFDEBUG */ 84 #endif /* _KERNEL */ 85 86 87 struct pf_anchor_global pf_anchors; 88 struct pf_anchor pf_main_anchor; 89 90 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); 91 92 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); 93 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); 94 95 static __inline int 96 pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) 97 { 98 int c = strcmp(a->path, b->path); 99 100 return (c ? (c < 0 ? -1 : 1) : 0); 101 } 102 103 void 104 pf_init_ruleset(struct pf_ruleset *ruleset) 105 { 106 memset(ruleset, 0, sizeof(struct pf_ruleset)); 107 TAILQ_INIT(&ruleset->rules.queues[0]); 108 TAILQ_INIT(&ruleset->rules.queues[1]); 109 ruleset->rules.active.ptr = &ruleset->rules.queues[0]; 110 ruleset->rules.inactive.ptr = &ruleset->rules.queues[1]; 111 } 112 113 struct pf_anchor * 114 pf_find_anchor(const char *path) 115 { 116 struct pf_anchor *key, *found; 117 118 key = rs_malloc(sizeof(*key)); 119 if (key == NULL) 120 return (NULL); 121 strlcpy(key->path, path, sizeof(key->path)); 122 found = RB_FIND(pf_anchor_global, &pf_anchors, key); 123 rs_free(key, sizeof(*key)); 124 return (found); 125 } 126 127 struct pf_ruleset * 128 pf_find_ruleset(const char *path) 129 { 130 struct pf_anchor *anchor; 131 132 while (*path == '/') 133 path++; 134 if (!*path) 135 return (&pf_main_ruleset); 136 anchor = pf_find_anchor(path); 137 if (anchor == NULL) 138 return (NULL); 139 else 140 return (&anchor->ruleset); 141 } 142 143 struct pf_ruleset * 144 pf_get_leaf_ruleset(char *path, char **path_remainder) 145 { 146 struct pf_ruleset *ruleset; 147 char *leaf, *p; 148 int i = 0; 149 150 p = path; 151 while (*p == '/') 152 p++; 153 154 ruleset = pf_find_ruleset(p); 155 leaf = p; 156 while (ruleset == NULL) { 157 leaf = strrchr(p, '/'); 158 if (leaf != NULL) { 159 *leaf = '\0'; 160 i++; 161 ruleset = pf_find_ruleset(p); 162 } else { 163 leaf = path; 164 /* 165 * if no path component exists, then main ruleset is 166 * our parent. 167 */ 168 ruleset = &pf_main_ruleset; 169 } 170 } 171 172 if (path_remainder != NULL) 173 *path_remainder = leaf; 174 175 /* restore slashes in path. */ 176 while (i != 0) { 177 while (*leaf != '\0') 178 leaf++; 179 *leaf = '/'; 180 i--; 181 } 182 183 return (ruleset); 184 } 185 186 struct pf_anchor * 187 pf_create_anchor(struct pf_anchor *parent, const char *aname) 188 { 189 struct pf_anchor *anchor, *dup; 190 191 if (!*aname || (strlen(aname) >= PF_ANCHOR_NAME_SIZE) || 192 ((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH))) 193 return (NULL); 194 195 anchor = rs_pool_get_anchor(); 196 if (anchor == NULL) 197 return (NULL); 198 199 RB_INIT(&anchor->children); 200 strlcpy(anchor->name, aname, sizeof(anchor->name)); 201 if (parent != NULL) { 202 /* 203 * Make sure path for levels 2, 3, ... is terminated by '/': 204 * 1/2/3/... 205 */ 206 strlcpy(anchor->path, parent->path, sizeof(anchor->path)); 207 strlcat(anchor->path, "/", sizeof(anchor->path)); 208 } 209 strlcat(anchor->path, anchor->name, sizeof(anchor->path)); 210 211 if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != NULL) { 212 DPFPRINTF(LOG_NOTICE, 213 "%s: RB_INSERT to global '%s' '%s' collides with '%s' '%s'", 214 __func__, anchor->path, anchor->name, dup->path, dup->name); 215 rs_pool_put_anchor(anchor); 216 return (NULL); 217 } 218 219 if (parent != NULL) { 220 anchor->parent = parent; 221 dup = RB_INSERT(pf_anchor_node, &parent->children, anchor); 222 if (dup != NULL) { 223 DPFPRINTF(LOG_NOTICE, 224 "%s: RB_INSERT to parent '%s' '%s' collides with " 225 "'%s' '%s'", __func__, anchor->path, anchor->name, 226 dup->path, dup->name); 227 RB_REMOVE(pf_anchor_global, &pf_anchors, 228 anchor); 229 rs_pool_put_anchor(anchor); 230 return (NULL); 231 } 232 } 233 234 pf_init_ruleset(&anchor->ruleset); 235 anchor->ruleset.anchor = anchor; 236 #ifdef _KERNEL 237 refcnt_init(&anchor->ref); 238 #endif 239 240 return (anchor); 241 } 242 243 struct pf_ruleset * 244 pf_find_or_create_ruleset(const char *path) 245 { 246 char *p, *aname, *r; 247 struct pf_ruleset *ruleset; 248 struct pf_anchor *anchor; 249 250 if (path[0] == 0) 251 return (&pf_main_ruleset); 252 253 while (*path == '/') 254 path++; 255 256 ruleset = pf_find_ruleset(path); 257 if (ruleset != NULL) 258 return (ruleset); 259 260 p = rs_malloc(MAXPATHLEN); 261 if (p == NULL) 262 return (NULL); 263 strlcpy(p, path, MAXPATHLEN); 264 265 ruleset = pf_get_leaf_ruleset(p, &aname); 266 anchor = ruleset->anchor; 267 268 while (*aname == '/') 269 aname++; 270 /* 271 * aname is a path remainder, which contains nodes we must create. We 272 * process the aname path from left to right, effectively descending 273 * from parents to children. 274 */ 275 while ((r = strchr(aname, '/')) != NULL || *aname) { 276 if (r != NULL) 277 *r = 0; 278 279 anchor = pf_create_anchor(anchor, aname); 280 if (anchor == NULL) { 281 rs_free(p, MAXPATHLEN); 282 return (NULL); 283 } 284 285 if (r == NULL) 286 break; 287 else 288 aname = r + 1; 289 } 290 291 rs_free(p, MAXPATHLEN); 292 return (&anchor->ruleset); 293 } 294 295 void 296 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 297 { 298 struct pf_anchor *parent; 299 300 while (ruleset != NULL) { 301 if (ruleset == &pf_main_ruleset || 302 !RB_EMPTY(&ruleset->anchor->children) || 303 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 304 ruleset->topen) 305 return; 306 if (!TAILQ_EMPTY(ruleset->rules.active.ptr) || 307 !TAILQ_EMPTY(ruleset->rules.inactive.ptr) || 308 ruleset->rules.inactive.open) 309 return; 310 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); 311 if ((parent = ruleset->anchor->parent) != NULL) 312 RB_REMOVE(pf_anchor_node, &parent->children, 313 ruleset->anchor); 314 pf_anchor_rele(ruleset->anchor); 315 if (parent == NULL) 316 return; 317 ruleset = &parent->ruleset; 318 } 319 } 320 321 int 322 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 323 const char *name) 324 { 325 char *p, *path; 326 struct pf_ruleset *ruleset; 327 328 r->anchor = NULL; 329 r->anchor_relative = 0; 330 r->anchor_wildcard = 0; 331 if (!name[0]) 332 return (0); 333 path = rs_malloc(MAXPATHLEN); 334 if (path == NULL) 335 return (1); 336 if (name[0] == '/') 337 strlcpy(path, name + 1, MAXPATHLEN); 338 else { 339 /* relative path */ 340 r->anchor_relative = 1; 341 if (s->anchor == NULL || !s->anchor->path[0]) 342 path[0] = 0; 343 else 344 strlcpy(path, s->anchor->path, MAXPATHLEN); 345 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 346 if (!path[0]) { 347 DPFPRINTF(LOG_NOTICE, 348 "pf_anchor_setup: .. beyond root"); 349 rs_free(path, MAXPATHLEN); 350 return (1); 351 } 352 if ((p = strrchr(path, '/')) != NULL) 353 *p = 0; 354 else 355 path[0] = 0; 356 r->anchor_relative++; 357 name += 3; 358 } 359 if (path[0]) 360 strlcat(path, "/", MAXPATHLEN); 361 strlcat(path, name, MAXPATHLEN); 362 } 363 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 364 r->anchor_wildcard = 1; 365 *p = 0; 366 } 367 ruleset = pf_find_or_create_ruleset(path); 368 rs_free(path, MAXPATHLEN); 369 if (ruleset == NULL || ruleset == &pf_main_ruleset) { 370 DPFPRINTF(LOG_NOTICE, 371 "pf_anchor_setup: ruleset"); 372 return (1); 373 } 374 r->anchor = ruleset->anchor; 375 r->anchor->refcnt++; 376 return (0); 377 } 378 379 int 380 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 381 struct pfioc_rule *pr) 382 { 383 pr->anchor_call[0] = 0; 384 if (r->anchor == NULL) 385 return (0); 386 if (!r->anchor_relative) { 387 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 388 strlcat(pr->anchor_call, r->anchor->path, 389 sizeof(pr->anchor_call)); 390 } else { 391 char *a, *p; 392 int i; 393 394 a = rs_malloc(MAXPATHLEN); 395 if (a == NULL) 396 return (1); 397 if (rs == &pf_main_ruleset) 398 a[0] = 0; 399 else 400 strlcpy(a, rs->anchor->path, MAXPATHLEN); 401 for (i = 1; i < r->anchor_relative; ++i) { 402 if ((p = strrchr(a, '/')) == NULL) 403 p = a; 404 *p = 0; 405 strlcat(pr->anchor_call, "../", 406 sizeof(pr->anchor_call)); 407 } 408 if (strncmp(a, r->anchor->path, strlen(a))) { 409 DPFPRINTF(LOG_NOTICE, 410 "pf_anchor_copyout: '%s' '%s'", a, 411 r->anchor->path); 412 rs_free(a, MAXPATHLEN); 413 return (1); 414 } 415 if (strlen(r->anchor->path) > strlen(a)) 416 strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 417 strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 418 rs_free(a, MAXPATHLEN); 419 } 420 if (r->anchor_wildcard) 421 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 422 sizeof(pr->anchor_call)); 423 return (0); 424 } 425 426 void 427 pf_remove_anchor(struct pf_rule *r) 428 { 429 if (r->anchor == NULL) 430 return; 431 if (r->anchor->refcnt <= 0) 432 DPFPRINTF(LOG_NOTICE, "pf_remove_anchor: broken refcount"); 433 else if (!--r->anchor->refcnt) 434 pf_remove_if_empty_ruleset(&r->anchor->ruleset); 435 r->anchor = NULL; 436 } 437 438 void 439 pf_anchor_rele(struct pf_anchor *anchor) 440 { 441 if ((anchor == NULL) || (anchor == &pf_main_anchor)) 442 return; 443 444 #ifdef _KERNEL 445 if (refcnt_rele(&anchor->ref)) 446 rs_pool_put_anchor(anchor); 447 #else 448 rs_pool_put_anchor(anchor); 449 #endif 450 } 451 452 struct pf_anchor * 453 pf_anchor_take(struct pf_anchor *anchor) 454 { 455 #ifdef _KERNEL 456 if (anchor != NULL && anchor != &pf_main_anchor) 457 refcnt_take(&anchor->ref); 458 #endif 459 return (anchor); 460 } 461