1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 * $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $
37 */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #ifdef _KERNEL
45 # include <sys/systm.h>
46 # include <sys/refcount.h>
47 #endif /* _KERNEL */
48 #include <sys/mbuf.h>
49
50 #include <netinet/in.h>
51 #include <netinet/in_systm.h>
52 #include <netinet/ip.h>
53 #include <netinet/tcp.h>
54
55 #include <net/if.h>
56 #include <net/vnet.h>
57 #include <net/pfvar.h>
58
59 #ifdef INET6
60 #include <netinet/ip6.h>
61 #endif /* INET6 */
62
63
64 #ifdef _KERNEL
65 #define DPFPRINTF(format, x...) \
66 if (V_pf_status.debug >= PF_DEBUG_NOISY) \
67 printf(format , ##x)
68 #define rs_malloc(x) malloc(x, M_TEMP, M_NOWAIT|M_ZERO)
69 #define rs_free(x) free(x, M_TEMP)
70
71 #else
72 /* Userland equivalents so we can lend code to pfctl et al. */
73
74 #include <arpa/inet.h>
75 #include <errno.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #define rs_malloc(x) calloc(1, x)
80 #define rs_free(x) free(x)
81
82 #ifdef PFDEBUG
83 #include <sys/stdarg.h>
84 #define DPFPRINTF(format, x...) fprintf(stderr, format , ##x)
85 #else
86 #define DPFPRINTF(format, x...) ((void)0)
87 #endif /* PFDEBUG */
88 #endif /* _KERNEL */
89
90 #ifdef _KERNEL
91 VNET_DEFINE(struct pf_anchor_global, pf_anchors);
92 VNET_DEFINE(struct pf_anchor, pf_main_anchor);
93 #else /* ! _KERNEL */
94 struct pf_anchor_global pf_anchors;
95 struct pf_anchor pf_main_anchor;
96 #undef V_pf_anchors
97 #define V_pf_anchors pf_anchors
98 #undef pf_main_ruleset
99 #define pf_main_ruleset pf_main_anchor.ruleset
100 #endif /* _KERNEL */
101
102 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
103
104 static struct pf_anchor *pf_find_anchor(const char *);
105
106 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
107 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
108
109 static __inline int
pf_anchor_compare(struct pf_anchor * a,struct pf_anchor * b)110 pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
111 {
112 int c = strcmp(a->path, b->path);
113
114 return (c ? (c < 0 ? -1 : 1) : 0);
115 }
116
117 int
pf_get_ruleset_number(u_int8_t action)118 pf_get_ruleset_number(u_int8_t action)
119 {
120 switch (action) {
121 case PF_SCRUB:
122 case PF_NOSCRUB:
123 return (PF_RULESET_SCRUB);
124 break;
125 case PF_PASS:
126 case PF_DROP:
127 return (PF_RULESET_FILTER);
128 break;
129 case PF_NAT:
130 case PF_NONAT:
131 return (PF_RULESET_NAT);
132 break;
133 case PF_BINAT:
134 case PF_NOBINAT:
135 return (PF_RULESET_BINAT);
136 break;
137 case PF_RDR:
138 case PF_NORDR:
139 return (PF_RULESET_RDR);
140 break;
141 default:
142 return (PF_RULESET_MAX);
143 break;
144 }
145 }
146
147 void
pf_init_ruleset(struct pf_ruleset * ruleset)148 pf_init_ruleset(struct pf_ruleset *ruleset)
149 {
150 int i;
151
152 memset(ruleset, 0, sizeof(struct pf_ruleset));
153 for (i = 0; i < PF_RULESET_MAX; i++) {
154 TAILQ_INIT(&ruleset->rules[i].queues[0]);
155 TAILQ_INIT(&ruleset->rules[i].queues[1]);
156 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
157 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
158 }
159 }
160
161 static struct pf_anchor *
pf_find_anchor(const char * path)162 pf_find_anchor(const char *path)
163 {
164 struct pf_anchor *key, *found;
165
166 key = (struct pf_anchor *)rs_malloc(sizeof(*key));
167 if (key == NULL)
168 return (NULL);
169 strlcpy(key->path, path, sizeof(key->path));
170 found = RB_FIND(pf_anchor_global, &V_pf_anchors, key);
171 rs_free(key);
172 return (found);
173 }
174
175 struct pf_ruleset *
pf_find_ruleset(const char * path)176 pf_find_ruleset(const char *path)
177 {
178 struct pf_anchor *anchor;
179
180 while (*path == '/')
181 path++;
182 if (!*path)
183 return (&pf_main_ruleset);
184 anchor = pf_find_anchor(path);
185 if (anchor == NULL)
186 return (NULL);
187 else
188 return (&anchor->ruleset);
189 }
190
191 struct pf_ruleset *
pf_find_or_create_ruleset(const char * path)192 pf_find_or_create_ruleset(const char *path)
193 {
194 char *p, *q, *r;
195 struct pf_ruleset *ruleset;
196 struct pf_anchor *anchor = NULL, *dup, *parent = NULL;
197
198 if (path[0] == 0)
199 return (&pf_main_ruleset);
200 while (*path == '/')
201 path++;
202 ruleset = pf_find_ruleset(path);
203 if (ruleset != NULL)
204 return (ruleset);
205 p = (char *)rs_malloc(MAXPATHLEN);
206 if (p == NULL)
207 return (NULL);
208 strlcpy(p, path, MAXPATHLEN);
209 while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
210 *q = 0;
211 if ((ruleset = pf_find_ruleset(p)) != NULL) {
212 parent = ruleset->anchor;
213 break;
214 }
215 }
216 if (q == NULL)
217 q = p;
218 else
219 q++;
220 strlcpy(p, path, MAXPATHLEN);
221 if (!*q) {
222 rs_free(p);
223 return (NULL);
224 }
225 while ((r = strchr(q, '/')) != NULL || *q) {
226 if (r != NULL)
227 *r = 0;
228 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
229 (parent != NULL && strlen(parent->path) >=
230 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
231 rs_free(p);
232 return (NULL);
233 }
234 anchor = (struct pf_anchor *)rs_malloc(sizeof(*anchor));
235 if (anchor == NULL) {
236 rs_free(p);
237 return (NULL);
238 }
239 RB_INIT(&anchor->children);
240 strlcpy(anchor->name, q, sizeof(anchor->name));
241 if (parent != NULL) {
242 strlcpy(anchor->path, parent->path,
243 sizeof(anchor->path));
244 strlcat(anchor->path, "/", sizeof(anchor->path));
245 }
246 strlcat(anchor->path, anchor->name, sizeof(anchor->path));
247 if ((dup = RB_INSERT(pf_anchor_global, &V_pf_anchors, anchor)) !=
248 NULL) {
249 printf("pf_find_or_create_ruleset: RB_INSERT1 "
250 "'%s' '%s' collides with '%s' '%s'\n",
251 anchor->path, anchor->name, dup->path, dup->name);
252 rs_free(anchor);
253 rs_free(p);
254 return (NULL);
255 }
256 if (parent != NULL) {
257 anchor->parent = parent;
258 if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
259 anchor)) != NULL) {
260 printf("pf_find_or_create_ruleset: "
261 "RB_INSERT2 '%s' '%s' collides with "
262 "'%s' '%s'\n", anchor->path, anchor->name,
263 dup->path, dup->name);
264 RB_REMOVE(pf_anchor_global, &V_pf_anchors,
265 anchor);
266 rs_free(anchor);
267 rs_free(p);
268 return (NULL);
269 }
270 }
271 pf_init_ruleset(&anchor->ruleset);
272 anchor->ruleset.anchor = anchor;
273 parent = anchor;
274 if (r != NULL)
275 q = r + 1;
276 else
277 *q = 0;
278 }
279 rs_free(p);
280 return (&anchor->ruleset);
281 }
282
283 void
pf_remove_if_empty_ruleset(struct pf_ruleset * ruleset)284 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
285 {
286 struct pf_anchor *parent;
287 int i;
288
289 while (ruleset != NULL) {
290 if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
291 !RB_EMPTY(&ruleset->anchor->children) ||
292 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
293 ruleset->topen)
294 return;
295 for (i = 0; i < PF_RULESET_MAX; ++i)
296 if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
297 !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
298 ruleset->rules[i].inactive.open)
299 return;
300 RB_REMOVE(pf_anchor_global, &V_pf_anchors, ruleset->anchor);
301 if ((parent = ruleset->anchor->parent) != NULL)
302 RB_REMOVE(pf_anchor_node, &parent->children,
303 ruleset->anchor);
304 rs_free(ruleset->anchor);
305 if (parent == NULL)
306 return;
307 ruleset = &parent->ruleset;
308 }
309 }
310
311 int
pf_anchor_setup(struct pf_rule * r,const struct pf_ruleset * s,const char * name)312 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
313 const char *name)
314 {
315 char *p, *path;
316 struct pf_ruleset *ruleset;
317
318 r->anchor = NULL;
319 r->anchor_relative = 0;
320 r->anchor_wildcard = 0;
321 if (!name[0])
322 return (0);
323 path = (char *)rs_malloc(MAXPATHLEN);
324 if (path == NULL)
325 return (1);
326 if (name[0] == '/')
327 strlcpy(path, name + 1, MAXPATHLEN);
328 else {
329 /* relative path */
330 r->anchor_relative = 1;
331 if (s->anchor == NULL || !s->anchor->path[0])
332 path[0] = 0;
333 else
334 strlcpy(path, s->anchor->path, MAXPATHLEN);
335 while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
336 if (!path[0]) {
337 printf("pf_anchor_setup: .. beyond root\n");
338 rs_free(path);
339 return (1);
340 }
341 if ((p = strrchr(path, '/')) != NULL)
342 *p = 0;
343 else
344 path[0] = 0;
345 r->anchor_relative++;
346 name += 3;
347 }
348 if (path[0])
349 strlcat(path, "/", MAXPATHLEN);
350 strlcat(path, name, MAXPATHLEN);
351 }
352 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
353 r->anchor_wildcard = 1;
354 *p = 0;
355 }
356 ruleset = pf_find_or_create_ruleset(path);
357 rs_free(path);
358 if (ruleset == NULL || ruleset->anchor == NULL) {
359 printf("pf_anchor_setup: ruleset\n");
360 return (1);
361 }
362 r->anchor = ruleset->anchor;
363 r->anchor->refcnt++;
364 return (0);
365 }
366
367 int
pf_anchor_copyout(const struct pf_ruleset * rs,const struct pf_rule * r,struct pfioc_rule * pr)368 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
369 struct pfioc_rule *pr)
370 {
371 pr->anchor_call[0] = 0;
372 if (r->anchor == NULL)
373 return (0);
374 if (!r->anchor_relative) {
375 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
376 strlcat(pr->anchor_call, r->anchor->path,
377 sizeof(pr->anchor_call));
378 } else {
379 char *a, *p;
380 int i;
381
382 a = (char *)rs_malloc(MAXPATHLEN);
383 if (a == NULL)
384 return (1);
385 if (rs->anchor == NULL)
386 a[0] = 0;
387 else
388 strlcpy(a, rs->anchor->path, MAXPATHLEN);
389 for (i = 1; i < r->anchor_relative; ++i) {
390 if ((p = strrchr(a, '/')) == NULL)
391 p = a;
392 *p = 0;
393 strlcat(pr->anchor_call, "../",
394 sizeof(pr->anchor_call));
395 }
396 if (strncmp(a, r->anchor->path, strlen(a))) {
397 printf("pf_anchor_copyout: '%s' '%s'\n", a,
398 r->anchor->path);
399 rs_free(a);
400 return (1);
401 }
402 if (strlen(r->anchor->path) > strlen(a))
403 strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
404 strlen(a) + 1 : 0), sizeof(pr->anchor_call));
405 rs_free(a);
406 }
407 if (r->anchor_wildcard)
408 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
409 sizeof(pr->anchor_call));
410 return (0);
411 }
412
413 void
pf_anchor_remove(struct pf_rule * r)414 pf_anchor_remove(struct pf_rule *r)
415 {
416 if (r->anchor == NULL)
417 return;
418 if (r->anchor->refcnt <= 0) {
419 printf("pf_anchor_remove: broken refcount\n");
420 r->anchor = NULL;
421 return;
422 }
423 if (!--r->anchor->refcnt)
424 pf_remove_if_empty_ruleset(&r->anchor->ruleset);
425 r->anchor = NULL;
426 }
427