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