GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/pfctl/../../sys/net/pf_ruleset.c Lines: 122 185 65.9 %
Date: 2017-11-07 Branches: 164 500 32.8 %

Line Branch Exec Source
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













































10643
RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
85













































975
RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
86
87
static __inline int
88
pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
89
{
90
3022
	int c = strcmp(a->path, b->path);
91
92
4461
	return (c ? (c < 0 ? -1 : 1) : 0);
93
}
94
95
void
96
pf_init_ruleset(struct pf_ruleset *ruleset)
97
{
98
2252
	memset(ruleset, 0, sizeof(struct pf_ruleset));
99
1126
	TAILQ_INIT(&ruleset->rules.queues[0]);
100
1126
	TAILQ_INIT(&ruleset->rules.queues[1]);
101
1126
	ruleset->rules.active.ptr = &ruleset->rules.queues[0];
102
1126
	ruleset->rules.inactive.ptr = &ruleset->rules.queues[1];
103
1126
}
104
105
struct pf_anchor *
106
pf_find_anchor(const char *path)
107
{
108
	struct pf_anchor	*key, *found;
109
110
808
	key = rs_malloc(sizeof(*key));
111
404
	if (key == NULL)
112
		return (NULL);
113
404
	strlcpy(key->path, path, sizeof(key->path));
114
404
	found = RB_FIND(pf_anchor_global, &pf_anchors, key);
115
404
	rs_free(key, sizeof(*key));
116
404
	return (found);
117
404
}
118
119
struct pf_ruleset *
120
pf_find_ruleset(const char *path)
121
{
122
	struct pf_anchor	*anchor;
123
124
1212
	while (*path == '/')
125
		path++;
126
404
	if (!*path)
127
		return (&pf_main_ruleset);
128
404
	anchor = pf_find_anchor(path);
129
404
	if (anchor == NULL)
130
332
		return (NULL);
131
	else
132
72
		return (&anchor->ruleset);
133
404
}
134
135
struct pf_ruleset *
136
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
486
	while (*p == '/')
144
		p++;
145
146
162
	ruleset = pf_find_ruleset(p);
147
	leaf = p;
148
494
	while (ruleset == NULL) {
149
170
		leaf = strrchr(p, '/');
150
170
		if (leaf != NULL) {
151
76
			*leaf = '\0';
152
76
			i++;
153
76
			ruleset = pf_find_ruleset(p);
154
76
		} 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
162
	if (path_remainder != NULL)
165
162
		*path_remainder = leaf;
166
167
	/* restore slashes in path.  */
168
314
	while (i != 0) {
169
190
		while (*leaf != '\0')
170
57
			leaf++;
171
76
		*leaf = '/';
172
76
		i--;
173
	}
174
175
162
	return (ruleset);
176
}
177
178
struct pf_anchor *
179
pf_create_anchor(struct pf_anchor *parent, const char *aname)
180
{
181
	struct pf_anchor	*anchor, *dup;
182
183

586
	if (!*aname || (strlen(aname) >= PF_ANCHOR_NAME_SIZE) ||
184
246
	    ((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH)))
185
		return (NULL);
186
187
170
	anchor = rs_malloc(sizeof(*anchor));
188
170
	if (anchor == NULL)
189
		return (NULL);
190
191
170
	RB_INIT(&anchor->children);
192
170
	strlcpy(anchor->name, aname, sizeof(anchor->name));
193
170
	if (parent != NULL) {
194
		/*
195
		 * Make sure path for levels 2, 3, ... is terminated by '/':
196
		 *	1/2/3/...
197
		 */
198
76
		strlcpy(anchor->path, parent->path, sizeof(anchor->path));
199
76
		strlcat(anchor->path, "/", sizeof(anchor->path));
200
76
	}
201
170
	strlcat(anchor->path, anchor->name, sizeof(anchor->path));
202
203
170
	if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != NULL) {
204
		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
		rs_free(anchor, sizeof(*anchor));
208
		return (NULL);
209
	}
210
211
170
	if (parent != NULL) {
212
76
		anchor->parent = parent;
213
76
		dup = RB_INSERT(pf_anchor_node, &parent->children, anchor);
214
76
		if (dup != NULL) {
215
			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
			RB_REMOVE(pf_anchor_global, &pf_anchors,
220
			    anchor);
221
			rs_free(anchor, sizeof(*anchor));
222
			return (NULL);
223
		}
224
	}
225
226
170
	pf_init_ruleset(&anchor->ruleset);
227
170
	anchor->ruleset.anchor = anchor;
228
229
170
	return (anchor);
230
170
}
231
232
struct pf_ruleset *
233
pf_find_or_create_ruleset(const char *path)
234
{
235
332
	char			*p, *aname, *r;
236
	struct pf_ruleset	*ruleset;
237
	struct pf_anchor	*anchor;
238
239
166
	if (path[0] == 0)
240
		return (&pf_main_ruleset);
241
242
166
	while (*path == '/')
243
		path++;
244
245
166
	ruleset = pf_find_ruleset(path);
246
166
	if (ruleset != NULL)
247
4
		return (ruleset);
248
249
162
	p = rs_malloc(MAXPATHLEN);
250
162
	if (p == NULL)
251
		return (NULL);
252
162
	strlcpy(p, path, MAXPATHLEN);
253
254
162
	ruleset = pf_get_leaf_ruleset(p, &aname);
255
162
	anchor = ruleset->anchor;
256
257
460
	while (*aname == '/')
258
68
		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

340
	while ((r = strchr(aname, '/')) != NULL || *aname) {
265
170
		if (r != NULL)
266
8
			*r = 0;
267
268
170
		anchor = pf_create_anchor(anchor, aname);
269
170
		if (anchor == NULL) {
270
			rs_free(p, MAXPATHLEN);
271
			return (NULL);
272
		}
273
274
170
		if (r == NULL)
275
			break;
276
		else
277
8
			aname = r + 1;
278
	}
279
280
162
	rs_free(p, MAXPATHLEN);
281
162
	return (&anchor->ruleset);
282
166
}
283
284
void
285
pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
286
{
287
	struct pf_anchor	*parent;
288
289
249
	while (ruleset != NULL) {
290

197
		if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
291
83
		    !RB_EMPTY(&ruleset->anchor->children) ||
292

62
		    ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
293
31
		    ruleset->topen)
294
52
			return;
295

62
		if (!TAILQ_EMPTY(ruleset->rules.active.ptr) ||
296
31
		    !TAILQ_EMPTY(ruleset->rules.inactive.ptr) ||
297
31
		    ruleset->rules.inactive.open)
298
			return;
299
31
		RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
300
31
		if ((parent = ruleset->anchor->parent) != NULL)
301
			RB_REMOVE(pf_anchor_node, &parent->children,
302
			    ruleset->anchor);
303
31
		rs_free(ruleset->anchor, sizeof(*(ruleset->anchor)));
304
31
		if (parent == NULL)
305
31
			return;
306
		ruleset = &parent->ruleset;
307
	}
308
83
}
309
310
int
311
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
166
	r->anchor = NULL;
318
83
	r->anchor_relative = 0;
319
83
	r->anchor_wildcard = 0;
320
83
	if (!name[0])
321
		return (0);
322
83
	path = rs_malloc(MAXPATHLEN);
323
83
	if (path == NULL)
324
		return (1);
325
83
	if (name[0] == '/')
326
		strlcpy(path, name + 1, MAXPATHLEN);
327
	else {
328
		/* relative path */
329
83
		r->anchor_relative = 1;
330

166
		if (s->anchor == NULL || !s->anchor->path[0])
331
8
			path[0] = 0;
332
		else
333
75
			strlcpy(path, s->anchor->path, MAXPATHLEN);
334

166
		while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
335
			if (!path[0]) {
336
				DPFPRINTF(LOG_NOTICE,
337
				    "pf_anchor_setup: .. beyond root");
338
				rs_free(path, MAXPATHLEN);
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
83
		if (path[0])
349
75
			strlcat(path, "/", MAXPATHLEN);
350
83
		strlcat(path, name, MAXPATHLEN);
351
	}
352

158
	if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
353
		r->anchor_wildcard = 1;
354
		*p = 0;
355
	}
356
83
	ruleset = pf_find_or_create_ruleset(path);
357
83
	rs_free(path, MAXPATHLEN);
358

166
	if (ruleset == NULL || ruleset->anchor == NULL) {
359
		DPFPRINTF(LOG_NOTICE,
360
		    "pf_anchor_setup: ruleset");
361
		return (1);
362
	}
363
83
	r->anchor = ruleset->anchor;
364
83
	r->anchor->refcnt++;
365
83
	return (0);
366
83
}
367
368
int
369
pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
370
    struct pfioc_rule *pr)
371
{
372
	pr->anchor_call[0] = 0;
373
	if (r->anchor == NULL)
374
		return (0);
375
	if (!r->anchor_relative) {
376
		strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
377
		strlcat(pr->anchor_call, r->anchor->path,
378
		    sizeof(pr->anchor_call));
379
	} else {
380
		char	*a, *p;
381
		int	 i;
382
383
		a = rs_malloc(MAXPATHLEN);
384
		if (a == NULL)
385
			return (1);
386
		if (rs->anchor == NULL)
387
			a[0] = 0;
388
		else
389
			strlcpy(a, rs->anchor->path, MAXPATHLEN);
390
		for (i = 1; i < r->anchor_relative; ++i) {
391
			if ((p = strrchr(a, '/')) == NULL)
392
				p = a;
393
			*p = 0;
394
			strlcat(pr->anchor_call, "../",
395
			    sizeof(pr->anchor_call));
396
		}
397
		if (strncmp(a, r->anchor->path, strlen(a))) {
398
			DPFPRINTF(LOG_NOTICE,
399
			    "pf_anchor_copyout: '%s' '%s'", a,
400
			    r->anchor->path);
401
			rs_free(a, MAXPATHLEN);
402
			return (1);
403
		}
404
		if (strlen(r->anchor->path) > strlen(a))
405
			strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
406
			    strlen(a) + 1 : 0), sizeof(pr->anchor_call));
407
		rs_free(a, MAXPATHLEN);
408
	}
409
	if (r->anchor_wildcard)
410
		strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
411
		    sizeof(pr->anchor_call));
412
	return (0);
413
}
414
415
void
416
pf_anchor_remove(struct pf_rule *r)
417
{
418
	if (r->anchor == NULL)
419
		return;
420
	if (r->anchor->refcnt <= 0) {
421
		DPFPRINTF(LOG_NOTICE,
422
		    "pf_anchor_remove: broken refcount");
423
		r->anchor = NULL;
424
		return;
425
	}
426
	if (!--r->anchor->refcnt)
427
		pf_remove_if_empty_ruleset(&r->anchor->ruleset);
428
	r->anchor = NULL;
429
}