xref: /openbsd-src/usr.sbin/tftp-proxy/filter.c (revision 68928c43a99c1507757b46fa476c5482a4d9e547)
1*68928c43Sderaadt /*	$OpenBSD: filter.c,v 1.2 2015/01/21 21:50:33 deraadt Exp $ */
2697c8096Sdlg 
3697c8096Sdlg /*
4697c8096Sdlg  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5697c8096Sdlg  *
6697c8096Sdlg  * Permission to use, copy, modify, and distribute this software for any
7697c8096Sdlg  * purpose with or without fee is hereby granted, provided that the above
8697c8096Sdlg  * copyright notice and this permission notice appear in all copies.
9697c8096Sdlg  *
10697c8096Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11697c8096Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12697c8096Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13697c8096Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14697c8096Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15697c8096Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16697c8096Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17697c8096Sdlg  */
18697c8096Sdlg 
19697c8096Sdlg #include <syslog.h>
20697c8096Sdlg 
21697c8096Sdlg #include <sys/ioctl.h>
22697c8096Sdlg #include <sys/types.h>
23697c8096Sdlg #include <sys/socket.h>
24697c8096Sdlg 
25697c8096Sdlg #include <netinet/in.h>
26697c8096Sdlg #include <netinet/tcp.h>
27697c8096Sdlg #include <arpa/inet.h>
28*68928c43Sderaadt #include <net/if.h>
29*68928c43Sderaadt #include <net/pfvar.h>
30697c8096Sdlg 
31697c8096Sdlg #include <err.h>
32697c8096Sdlg #include <errno.h>
33697c8096Sdlg #include <fcntl.h>
34697c8096Sdlg #include <stdio.h>
35697c8096Sdlg #include <stdlib.h>
36697c8096Sdlg #include <string.h>
37697c8096Sdlg #include <unistd.h>
38697c8096Sdlg 
39697c8096Sdlg #include "filter.h"
40697c8096Sdlg 
41697c8096Sdlg /* From netinet/in.h, but only _KERNEL_ gets them. */
42697c8096Sdlg #define satosin(sa)	((struct sockaddr_in *)(sa))
43697c8096Sdlg #define satosin6(sa)	((struct sockaddr_in6 *)(sa))
44697c8096Sdlg 
45697c8096Sdlg enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
46697c8096Sdlg 
47697c8096Sdlg int prepare_rule(u_int32_t, struct sockaddr *, struct sockaddr *,
48697c8096Sdlg     u_int16_t, u_int8_t);
49697c8096Sdlg 
50697c8096Sdlg static struct pfioc_rule	pfr;
51697c8096Sdlg static struct pfioc_trans	pft;
52697c8096Sdlg static struct pfioc_trans_e	pfte;
53697c8096Sdlg static int dev, rule_log;
54697c8096Sdlg static char *qname;
55697c8096Sdlg 
56697c8096Sdlg int
add_filter(u_int32_t id,u_int8_t dir,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port,u_int8_t proto)57697c8096Sdlg add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
58697c8096Sdlg     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
59697c8096Sdlg {
60697c8096Sdlg 	if (!src || !dst || !d_port || !proto) {
61697c8096Sdlg 		errno = EINVAL;
62697c8096Sdlg 		return (-1);
63697c8096Sdlg 	}
64697c8096Sdlg 
65697c8096Sdlg 	if (prepare_rule(id, src, dst, d_port, proto) == -1)
66697c8096Sdlg 		return (-1);
67697c8096Sdlg 
68697c8096Sdlg 	pfr.rule.direction = dir;
69697c8096Sdlg 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
70697c8096Sdlg 		return (-1);
71697c8096Sdlg 
72697c8096Sdlg 	return (0);
73697c8096Sdlg }
74697c8096Sdlg 
75697c8096Sdlg int
add_rdr(u_int32_t id,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port,struct sockaddr * rdr,u_int16_t rdr_port,u_int8_t proto)76697c8096Sdlg add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
77697c8096Sdlg     u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
78697c8096Sdlg {
79697c8096Sdlg 	if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
80697c8096Sdlg 	    (src->sa_family != rdr->sa_family)) {
81697c8096Sdlg 		errno = EINVAL;
82697c8096Sdlg 		return (-1);
83697c8096Sdlg 	}
84697c8096Sdlg 
85697c8096Sdlg 	if (prepare_rule(id, src, dst, d_port, proto) == -1)
86697c8096Sdlg 		return (-1);
87697c8096Sdlg 
88697c8096Sdlg 	pfr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
89697c8096Sdlg 	if (rdr->sa_family == AF_INET) {
90697c8096Sdlg 		memcpy(&pfr.rule.rdr.addr.v.a.addr.v4,
91697c8096Sdlg 		    &satosin(rdr)->sin_addr.s_addr, 4);
92697c8096Sdlg 		memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 4);
93697c8096Sdlg 	} else {
94697c8096Sdlg 		memcpy(&pfr.rule.rdr.addr.v.a.addr.v6,
95697c8096Sdlg 		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
96697c8096Sdlg 		memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 16);
97697c8096Sdlg 	}
98697c8096Sdlg 
99697c8096Sdlg 	pfr.rule.rdr.proxy_port[0] = rdr_port;
100697c8096Sdlg 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
101697c8096Sdlg 		return (-1);
102697c8096Sdlg 
103697c8096Sdlg 	return (0);
104697c8096Sdlg }
105697c8096Sdlg 
106697c8096Sdlg int
do_commit(void)107697c8096Sdlg do_commit(void)
108697c8096Sdlg {
109697c8096Sdlg 	if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
110697c8096Sdlg 		return (-1);
111697c8096Sdlg 
112697c8096Sdlg 	return (0);
113697c8096Sdlg }
114697c8096Sdlg 
115697c8096Sdlg int
do_rollback(void)116697c8096Sdlg do_rollback(void)
117697c8096Sdlg {
118697c8096Sdlg 	if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
119697c8096Sdlg 		return (-1);
120697c8096Sdlg 
121697c8096Sdlg 	return (0);
122697c8096Sdlg }
123697c8096Sdlg 
124697c8096Sdlg void
init_filter(char * opt_qname,int opt_verbose)125697c8096Sdlg init_filter(char *opt_qname, int opt_verbose)
126697c8096Sdlg {
127697c8096Sdlg 	struct pf_status status;
128697c8096Sdlg 
129697c8096Sdlg 	qname = opt_qname;
130697c8096Sdlg 
131697c8096Sdlg 	if (opt_verbose == 1)
132697c8096Sdlg 		rule_log = PF_LOG;
133697c8096Sdlg 	else if (opt_verbose == 2)
134697c8096Sdlg 		rule_log = PF_LOG_ALL;
135697c8096Sdlg 
136697c8096Sdlg 	dev = open("/dev/pf", O_RDWR);
137697c8096Sdlg 	if (dev == -1) {
138697c8096Sdlg 		syslog(LOG_ERR, "can't open /dev/pf");
139697c8096Sdlg 		exit(1);
140697c8096Sdlg 	}
141697c8096Sdlg 	if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
142697c8096Sdlg 		syslog(LOG_ERR, "DIOCGETSTATUS");
143697c8096Sdlg 		exit(1);
144697c8096Sdlg 	}
145697c8096Sdlg 	if (!status.running) {
146697c8096Sdlg 		syslog(LOG_ERR, "pf is disabled");
147697c8096Sdlg 		exit(1);
148697c8096Sdlg 	}
149697c8096Sdlg }
150697c8096Sdlg 
151697c8096Sdlg int
prepare_commit(u_int32_t id)152697c8096Sdlg prepare_commit(u_int32_t id)
153697c8096Sdlg {
154697c8096Sdlg 	memset(&pft, 0, sizeof pft);
155697c8096Sdlg 	memset(&pfte, 0, sizeof pfte);
156697c8096Sdlg 	pft.size = 1;
157697c8096Sdlg 	pft.esize = sizeof pfte;
158697c8096Sdlg 	pft.array = &pfte;
159697c8096Sdlg 
160697c8096Sdlg 	snprintf(pfte.anchor, PF_ANCHOR_NAME_SIZE,
161697c8096Sdlg 	    "%s/%d.%08x", FTP_PROXY_ANCHOR, getpid(), id);
162697c8096Sdlg 	pfte.type = PF_TRANS_RULESET;
163697c8096Sdlg 
164697c8096Sdlg 	if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
165697c8096Sdlg 		return (-1);
166697c8096Sdlg 
167697c8096Sdlg 	return (0);
168697c8096Sdlg }
169697c8096Sdlg 
170697c8096Sdlg int
prepare_rule(u_int32_t id,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port,u_int8_t proto)171697c8096Sdlg prepare_rule(u_int32_t id, struct sockaddr *src,
172697c8096Sdlg     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
173697c8096Sdlg {
174697c8096Sdlg 	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
175697c8096Sdlg 	    (src->sa_family != dst->sa_family)) {
176697c8096Sdlg 		errno = EPROTONOSUPPORT;
177697c8096Sdlg 		return (-1);
178697c8096Sdlg 	}
179697c8096Sdlg 
180697c8096Sdlg 	memset(&pfr, 0, sizeof pfr);
181697c8096Sdlg 	snprintf(pfr.anchor, PF_ANCHOR_NAME_SIZE,
182697c8096Sdlg 	    "%s/%d.%08x", FTP_PROXY_ANCHOR, getpid(), id);
183697c8096Sdlg 
184697c8096Sdlg 	pfr.ticket = pfte.ticket;
185697c8096Sdlg 
186697c8096Sdlg 	/* Generic for all rule types. */
187697c8096Sdlg 	pfr.rule.af = src->sa_family;
188697c8096Sdlg 	pfr.rule.proto = proto;
189697c8096Sdlg 	pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
190697c8096Sdlg 	pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
191697c8096Sdlg 	pfr.rule.rdr.addr.type = PF_ADDR_NONE;
192697c8096Sdlg 	pfr.rule.nat.addr.type = PF_ADDR_NONE;
193697c8096Sdlg 
194697c8096Sdlg 	if (src->sa_family == AF_INET) {
195697c8096Sdlg 		memcpy(&pfr.rule.src.addr.v.a.addr.v4,
196697c8096Sdlg 		    &satosin(src)->sin_addr.s_addr, 4);
197697c8096Sdlg 		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
198697c8096Sdlg 		memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
199697c8096Sdlg 		    &satosin(dst)->sin_addr.s_addr, 4);
200697c8096Sdlg 		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
201697c8096Sdlg 	} else {
202697c8096Sdlg 		memcpy(&pfr.rule.src.addr.v.a.addr.v6,
203697c8096Sdlg 		    &satosin6(src)->sin6_addr.s6_addr, 16);
204697c8096Sdlg 		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
205697c8096Sdlg 		memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
206697c8096Sdlg 		    &satosin6(dst)->sin6_addr.s6_addr, 16);
207697c8096Sdlg 		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
208697c8096Sdlg 	}
209697c8096Sdlg 	pfr.rule.dst.port_op = PF_OP_EQ;
210697c8096Sdlg 	pfr.rule.dst.port[0] = htons(d_port);
211697c8096Sdlg #ifdef notyet
212697c8096Sdlg 	pfr.rule.rule_flag = PFRULE_ONCE;
213697c8096Sdlg #endif
214697c8096Sdlg 	pfr.rule.action = PF_PASS;
215697c8096Sdlg 	pfr.rule.quick = 1;
216697c8096Sdlg 	pfr.rule.log = rule_log;
217697c8096Sdlg 	pfr.rule.keep_state = 1;
218697c8096Sdlg 	pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
219697c8096Sdlg 	pfr.rule.flagset = (proto == IPPROTO_TCP ?
220697c8096Sdlg 	    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
221697c8096Sdlg #ifdef notyet
222697c8096Sdlg 	pfr.rule.max_states = 1;
223697c8096Sdlg #endif
224697c8096Sdlg 	if (qname != NULL)
225697c8096Sdlg 		strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
226697c8096Sdlg 
227697c8096Sdlg 	return (0);
228697c8096Sdlg }
229