1 /* $OpenBSD: pf_ruleset.c,v 1.19 2022/07/20 09:33:11 mbuhl 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 237 return (anchor); 238 } 239 240 struct pf_ruleset * 241 pf_find_or_create_ruleset(const char *path) 242 { 243 char *p, *aname, *r; 244 struct pf_ruleset *ruleset; 245 struct pf_anchor *anchor; 246 247 if (path[0] == 0) 248 return (&pf_main_ruleset); 249 250 while (*path == '/') 251 path++; 252 253 ruleset = pf_find_ruleset(path); 254 if (ruleset != NULL) 255 return (ruleset); 256 257 p = rs_malloc(MAXPATHLEN); 258 if (p == NULL) 259 return (NULL); 260 strlcpy(p, path, MAXPATHLEN); 261 262 ruleset = pf_get_leaf_ruleset(p, &aname); 263 anchor = ruleset->anchor; 264 265 while (*aname == '/') 266 aname++; 267 /* 268 * aname is a path remainder, which contains nodes we must create. We 269 * process the aname path from left to right, effectively descending 270 * from parents to children. 271 */ 272 while ((r = strchr(aname, '/')) != NULL || *aname) { 273 if (r != NULL) 274 *r = 0; 275 276 anchor = pf_create_anchor(anchor, aname); 277 if (anchor == NULL) { 278 rs_free(p, MAXPATHLEN); 279 return (NULL); 280 } 281 282 if (r == NULL) 283 break; 284 else 285 aname = r + 1; 286 } 287 288 rs_free(p, MAXPATHLEN); 289 return (&anchor->ruleset); 290 } 291 292 void 293 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 294 { 295 struct pf_anchor *parent; 296 297 while (ruleset != NULL) { 298 if (ruleset == &pf_main_ruleset || 299 !RB_EMPTY(&ruleset->anchor->children) || 300 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 || 301 ruleset->topen) 302 return; 303 if (!TAILQ_EMPTY(ruleset->rules.active.ptr) || 304 !TAILQ_EMPTY(ruleset->rules.inactive.ptr) || 305 ruleset->rules.inactive.open) 306 return; 307 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor); 308 if ((parent = ruleset->anchor->parent) != NULL) 309 RB_REMOVE(pf_anchor_node, &parent->children, 310 ruleset->anchor); 311 rs_pool_put_anchor(ruleset->anchor); 312 if (parent == NULL) 313 return; 314 ruleset = &parent->ruleset; 315 } 316 } 317 318 int 319 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s, 320 const char *name) 321 { 322 char *p, *path; 323 struct pf_ruleset *ruleset; 324 325 r->anchor = NULL; 326 r->anchor_relative = 0; 327 r->anchor_wildcard = 0; 328 if (!name[0]) 329 return (0); 330 path = rs_malloc(MAXPATHLEN); 331 if (path == NULL) 332 return (1); 333 if (name[0] == '/') 334 strlcpy(path, name + 1, MAXPATHLEN); 335 else { 336 /* relative path */ 337 r->anchor_relative = 1; 338 if (s->anchor == NULL || !s->anchor->path[0]) 339 path[0] = 0; 340 else 341 strlcpy(path, s->anchor->path, MAXPATHLEN); 342 while (name[0] == '.' && name[1] == '.' && name[2] == '/') { 343 if (!path[0]) { 344 DPFPRINTF(LOG_NOTICE, 345 "pf_anchor_setup: .. beyond root"); 346 rs_free(path, MAXPATHLEN); 347 return (1); 348 } 349 if ((p = strrchr(path, '/')) != NULL) 350 *p = 0; 351 else 352 path[0] = 0; 353 r->anchor_relative++; 354 name += 3; 355 } 356 if (path[0]) 357 strlcat(path, "/", MAXPATHLEN); 358 strlcat(path, name, MAXPATHLEN); 359 } 360 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) { 361 r->anchor_wildcard = 1; 362 *p = 0; 363 } 364 ruleset = pf_find_or_create_ruleset(path); 365 rs_free(path, MAXPATHLEN); 366 if (ruleset == NULL || ruleset == &pf_main_ruleset) { 367 DPFPRINTF(LOG_NOTICE, 368 "pf_anchor_setup: ruleset"); 369 return (1); 370 } 371 r->anchor = ruleset->anchor; 372 r->anchor->refcnt++; 373 return (0); 374 } 375 376 int 377 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r, 378 struct pfioc_rule *pr) 379 { 380 pr->anchor_call[0] = 0; 381 if (r->anchor == NULL) 382 return (0); 383 if (!r->anchor_relative) { 384 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call)); 385 strlcat(pr->anchor_call, r->anchor->path, 386 sizeof(pr->anchor_call)); 387 } else { 388 char *a, *p; 389 int i; 390 391 a = rs_malloc(MAXPATHLEN); 392 if (a == NULL) 393 return (1); 394 if (rs == &pf_main_ruleset) 395 a[0] = 0; 396 else 397 strlcpy(a, rs->anchor->path, MAXPATHLEN); 398 for (i = 1; i < r->anchor_relative; ++i) { 399 if ((p = strrchr(a, '/')) == NULL) 400 p = a; 401 *p = 0; 402 strlcat(pr->anchor_call, "../", 403 sizeof(pr->anchor_call)); 404 } 405 if (strncmp(a, r->anchor->path, strlen(a))) { 406 DPFPRINTF(LOG_NOTICE, 407 "pf_anchor_copyout: '%s' '%s'", a, 408 r->anchor->path); 409 rs_free(a, MAXPATHLEN); 410 return (1); 411 } 412 if (strlen(r->anchor->path) > strlen(a)) 413 strlcat(pr->anchor_call, r->anchor->path + (a[0] ? 414 strlen(a) + 1 : 0), sizeof(pr->anchor_call)); 415 rs_free(a, MAXPATHLEN); 416 } 417 if (r->anchor_wildcard) 418 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*", 419 sizeof(pr->anchor_call)); 420 return (0); 421 } 422 423 void 424 pf_remove_anchor(struct pf_rule *r) 425 { 426 if (r->anchor == NULL) 427 return; 428 if (r->anchor->refcnt <= 0) 429 DPFPRINTF(LOG_NOTICE, "pf_remove_anchor: broken refcount"); 430 else if (!--r->anchor->refcnt) 431 pf_remove_if_empty_ruleset(&r->anchor->ruleset); 432 r->anchor = NULL; 433 } 434