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#include <sys/mbuf.h>
45
46#include <netinet/in.h>
47#include <netinet/in_systm.h>
48#include <netinet/ip.h>
49#include <netinet/tcp.h>
50
51#include <net/if.h>
52#include <net/vnet.h>
53#include <net/pfvar.h>
54
55#ifdef INET6
56#include <netinet/ip6.h>
57#endif /* INET6 */
58
59#include <arpa/inet.h>
60#include <errno.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#define rs_malloc(x)		 calloc(1, x)
65#define rs_free(x)		 free(x)
66
67#include "pfctl.h"
68
69#ifdef PFDEBUG
70#include <sys/stdarg.h>
71#define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
72#else
73#define DPFPRINTF(format, x...)	((void)0)
74#endif /* PFDEBUG */
75
76struct pfctl_anchor_global	 pf_anchors;
77struct pfctl_anchor	 pf_main_anchor;
78#undef V_pf_anchors
79#define V_pf_anchors		 pf_anchors
80#undef pf_main_ruleset
81#define pf_main_ruleset		 pf_main_anchor.ruleset
82
83static __inline int		pf_anchor_compare(struct pfctl_anchor *,
84				    struct pfctl_anchor *);
85static struct pfctl_anchor	*pf_find_anchor(const char *);
86
87RB_GENERATE(pfctl_anchor_global, pfctl_anchor, entry_global,
88    pf_anchor_compare);
89RB_GENERATE(pfctl_anchor_node, pfctl_anchor, entry_node, pf_anchor_compare);
90
91static __inline int
92pf_anchor_compare(struct pfctl_anchor *a, struct pfctl_anchor *b)
93{
94	int c = strcmp(a->path, b->path);
95
96	return (c ? (c < 0 ? -1 : 1) : 0);
97}
98
99int
100pf_get_ruleset_number(u_int8_t action)
101{
102	switch (action) {
103	case PF_SCRUB:
104	case PF_NOSCRUB:
105		return (PF_RULESET_SCRUB);
106		break;
107	case PF_PASS:
108	case PF_DROP:
109		return (PF_RULESET_FILTER);
110		break;
111	case PF_NAT:
112	case PF_NONAT:
113		return (PF_RULESET_NAT);
114		break;
115	case PF_BINAT:
116	case PF_NOBINAT:
117		return (PF_RULESET_BINAT);
118		break;
119	case PF_RDR:
120	case PF_NORDR:
121		return (PF_RULESET_RDR);
122		break;
123	default:
124		return (PF_RULESET_MAX);
125		break;
126	}
127}
128
129void
130pf_init_ruleset(struct pfctl_ruleset *ruleset)
131{
132	int	i;
133
134	memset(ruleset, 0, sizeof(struct pfctl_ruleset));
135	for (i = 0; i < PF_RULESET_MAX; i++) {
136		TAILQ_INIT(&ruleset->rules[i].queues[0]);
137		TAILQ_INIT(&ruleset->rules[i].queues[1]);
138		ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
139		ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
140	}
141}
142
143static struct pfctl_anchor *
144pf_find_anchor(const char *path)
145{
146	struct pfctl_anchor	*key, *found;
147
148	key = (struct pfctl_anchor *)rs_malloc(sizeof(*key));
149	if (key == NULL)
150		return (NULL);
151	strlcpy(key->path, path, sizeof(key->path));
152	found = RB_FIND(pfctl_anchor_global, &V_pf_anchors, key);
153	rs_free(key);
154	return (found);
155}
156
157struct pfctl_ruleset *
158pf_find_ruleset(const char *path)
159{
160	struct pfctl_anchor	*anchor;
161
162	while (*path == '/')
163		path++;
164	if (!*path)
165		return (&pf_main_ruleset);
166	anchor = pf_find_anchor(path);
167	if (anchor == NULL)
168		return (NULL);
169	else
170		return (&anchor->ruleset);
171}
172
173struct pfctl_ruleset *
174pf_find_or_create_ruleset(const char *path)
175{
176	char			*p, *q, *r;
177	struct pfctl_ruleset	*ruleset;
178	struct pfctl_anchor	*anchor = NULL, *dup, *parent = NULL;
179
180	if (path[0] == 0)
181		return (&pf_main_ruleset);
182	while (*path == '/')
183		path++;
184	ruleset = pf_find_ruleset(path);
185	if (ruleset != NULL)
186		return (ruleset);
187	p = (char *)rs_malloc(MAXPATHLEN);
188	if (p == NULL)
189		return (NULL);
190	strlcpy(p, path, MAXPATHLEN);
191	while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
192		*q = 0;
193		if ((ruleset = pf_find_ruleset(p)) != NULL) {
194			parent = ruleset->anchor;
195			break;
196		}
197	}
198	if (q == NULL)
199		q = p;
200	else
201		q++;
202	strlcpy(p, path, MAXPATHLEN);
203	if (!*q) {
204		rs_free(p);
205		return (NULL);
206	}
207	while ((r = strchr(q, '/')) != NULL || *q) {
208		if (r != NULL)
209			*r = 0;
210		if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
211		    (parent != NULL && strlen(parent->path) >=
212		    MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
213			rs_free(p);
214			return (NULL);
215		}
216		anchor = (struct pfctl_anchor *)rs_malloc(sizeof(*anchor));
217		if (anchor == NULL) {
218			rs_free(p);
219			return (NULL);
220		}
221		RB_INIT(&anchor->children);
222		strlcpy(anchor->name, q, sizeof(anchor->name));
223		if (parent != NULL) {
224			strlcpy(anchor->path, parent->path,
225			    sizeof(anchor->path));
226			strlcat(anchor->path, "/", sizeof(anchor->path));
227		}
228		strlcat(anchor->path, anchor->name, sizeof(anchor->path));
229		if ((dup = RB_INSERT(pfctl_anchor_global, &V_pf_anchors, anchor)) !=
230		    NULL) {
231			printf("pf_find_or_create_ruleset: RB_INSERT1 "
232			    "'%s' '%s' collides with '%s' '%s'\n",
233			    anchor->path, anchor->name, dup->path, dup->name);
234			rs_free(anchor);
235			rs_free(p);
236			return (NULL);
237		}
238		if (parent != NULL) {
239			anchor->parent = parent;
240			if ((dup = RB_INSERT(pfctl_anchor_node, &parent->children,
241			    anchor)) != NULL) {
242				printf("pf_find_or_create_ruleset: "
243				    "RB_INSERT2 '%s' '%s' collides with "
244				    "'%s' '%s'\n", anchor->path, anchor->name,
245				    dup->path, dup->name);
246				RB_REMOVE(pfctl_anchor_global, &V_pf_anchors,
247				    anchor);
248				rs_free(anchor);
249				rs_free(p);
250				return (NULL);
251			}
252		}
253		pf_init_ruleset(&anchor->ruleset);
254		anchor->ruleset.anchor = anchor;
255		parent = anchor;
256		if (r != NULL)
257			q = r + 1;
258		else
259			*q = 0;
260	}
261	rs_free(p);
262	return (&anchor->ruleset);
263}
264
265void
266pf_remove_if_empty_ruleset(struct pfctl_ruleset *ruleset)
267{
268	struct pfctl_anchor	*parent;
269	int			 i;
270
271	while (ruleset != NULL) {
272		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
273		    !RB_EMPTY(&ruleset->anchor->children) ||
274		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
275		    ruleset->topen)
276			return;
277		for (i = 0; i < PF_RULESET_MAX; ++i)
278			if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
279			    !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
280			    ruleset->rules[i].inactive.open)
281				return;
282		RB_REMOVE(pfctl_anchor_global, &V_pf_anchors, ruleset->anchor);
283		if ((parent = ruleset->anchor->parent) != NULL)
284			RB_REMOVE(pfctl_anchor_node, &parent->children,
285			    ruleset->anchor);
286		rs_free(ruleset->anchor);
287		if (parent == NULL)
288			return;
289		ruleset = &parent->ruleset;
290	}
291}
292int
293pfctl_anchor_setup(struct pfctl_rule *r, const struct pfctl_ruleset *s,
294    const char *name)
295{
296	char			*p, *path;
297	struct pfctl_ruleset	*ruleset;
298
299	r->anchor = NULL;
300	r->anchor_relative = 0;
301	r->anchor_wildcard = 0;
302	if (!name[0])
303		return (0);
304	path = (char *)rs_malloc(MAXPATHLEN);
305	if (path == NULL)
306		return (1);
307	if (name[0] == '/')
308		strlcpy(path, name + 1, MAXPATHLEN);
309	else {
310		/* relative path */
311		r->anchor_relative = 1;
312		if (s->anchor == NULL || !s->anchor->path[0])
313			path[0] = 0;
314		else
315			strlcpy(path, s->anchor->path, MAXPATHLEN);
316		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
317			if (!path[0]) {
318				printf("pfctl_anchor_setup: .. beyond root\n");
319				rs_free(path);
320				return (1);
321			}
322			if ((p = strrchr(path, '/')) != NULL)
323				*p = 0;
324			else
325				path[0] = 0;
326			r->anchor_relative++;
327			name += 3;
328		}
329		if (path[0])
330			strlcat(path, "/", MAXPATHLEN);
331		strlcat(path, name, MAXPATHLEN);
332	}
333	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
334		r->anchor_wildcard = 1;
335		*p = 0;
336	}
337	ruleset = pf_find_or_create_ruleset(path);
338	rs_free(path);
339	if (ruleset == NULL || ruleset->anchor == NULL) {
340		printf("pfctl_anchor_setup: ruleset\n");
341		return (1);
342	}
343	r->anchor = ruleset->anchor;
344	r->anchor->refcnt++;
345	return (0);
346}
347