xref: /openbsd/usr.sbin/tftp-proxy/filter.c (revision 68928c43)
1 /*	$OpenBSD: filter.c,v 1.2 2015/01/21 21:50:33 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <syslog.h>
20 
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 
25 #include <netinet/in.h>
26 #include <netinet/tcp.h>
27 #include <arpa/inet.h>
28 #include <net/if.h>
29 #include <net/pfvar.h>
30 
31 #include <err.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "filter.h"
40 
41 /* From netinet/in.h, but only _KERNEL_ gets them. */
42 #define satosin(sa)	((struct sockaddr_in *)(sa))
43 #define satosin6(sa)	((struct sockaddr_in6 *)(sa))
44 
45 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
46 
47 int prepare_rule(u_int32_t, struct sockaddr *, struct sockaddr *,
48     u_int16_t, u_int8_t);
49 
50 static struct pfioc_rule	pfr;
51 static struct pfioc_trans	pft;
52 static struct pfioc_trans_e	pfte;
53 static int dev, rule_log;
54 static char *qname;
55 
56 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)57 add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
58     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
59 {
60 	if (!src || !dst || !d_port || !proto) {
61 		errno = EINVAL;
62 		return (-1);
63 	}
64 
65 	if (prepare_rule(id, src, dst, d_port, proto) == -1)
66 		return (-1);
67 
68 	pfr.rule.direction = dir;
69 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
70 		return (-1);
71 
72 	return (0);
73 }
74 
75 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)76 add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
77     u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
78 {
79 	if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
80 	    (src->sa_family != rdr->sa_family)) {
81 		errno = EINVAL;
82 		return (-1);
83 	}
84 
85 	if (prepare_rule(id, src, dst, d_port, proto) == -1)
86 		return (-1);
87 
88 	pfr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
89 	if (rdr->sa_family == AF_INET) {
90 		memcpy(&pfr.rule.rdr.addr.v.a.addr.v4,
91 		    &satosin(rdr)->sin_addr.s_addr, 4);
92 		memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 4);
93 	} else {
94 		memcpy(&pfr.rule.rdr.addr.v.a.addr.v6,
95 		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
96 		memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 16);
97 	}
98 
99 	pfr.rule.rdr.proxy_port[0] = rdr_port;
100 	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
101 		return (-1);
102 
103 	return (0);
104 }
105 
106 int
do_commit(void)107 do_commit(void)
108 {
109 	if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
110 		return (-1);
111 
112 	return (0);
113 }
114 
115 int
do_rollback(void)116 do_rollback(void)
117 {
118 	if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
119 		return (-1);
120 
121 	return (0);
122 }
123 
124 void
init_filter(char * opt_qname,int opt_verbose)125 init_filter(char *opt_qname, int opt_verbose)
126 {
127 	struct pf_status status;
128 
129 	qname = opt_qname;
130 
131 	if (opt_verbose == 1)
132 		rule_log = PF_LOG;
133 	else if (opt_verbose == 2)
134 		rule_log = PF_LOG_ALL;
135 
136 	dev = open("/dev/pf", O_RDWR);
137 	if (dev == -1) {
138 		syslog(LOG_ERR, "can't open /dev/pf");
139 		exit(1);
140 	}
141 	if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
142 		syslog(LOG_ERR, "DIOCGETSTATUS");
143 		exit(1);
144 	}
145 	if (!status.running) {
146 		syslog(LOG_ERR, "pf is disabled");
147 		exit(1);
148 	}
149 }
150 
151 int
prepare_commit(u_int32_t id)152 prepare_commit(u_int32_t id)
153 {
154 	memset(&pft, 0, sizeof pft);
155 	memset(&pfte, 0, sizeof pfte);
156 	pft.size = 1;
157 	pft.esize = sizeof pfte;
158 	pft.array = &pfte;
159 
160 	snprintf(pfte.anchor, PF_ANCHOR_NAME_SIZE,
161 	    "%s/%d.%08x", FTP_PROXY_ANCHOR, getpid(), id);
162 	pfte.type = PF_TRANS_RULESET;
163 
164 	if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
165 		return (-1);
166 
167 	return (0);
168 }
169 
170 int
prepare_rule(u_int32_t id,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port,u_int8_t proto)171 prepare_rule(u_int32_t id, struct sockaddr *src,
172     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
173 {
174 	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
175 	    (src->sa_family != dst->sa_family)) {
176 		errno = EPROTONOSUPPORT;
177 		return (-1);
178 	}
179 
180 	memset(&pfr, 0, sizeof pfr);
181 	snprintf(pfr.anchor, PF_ANCHOR_NAME_SIZE,
182 	    "%s/%d.%08x", FTP_PROXY_ANCHOR, getpid(), id);
183 
184 	pfr.ticket = pfte.ticket;
185 
186 	/* Generic for all rule types. */
187 	pfr.rule.af = src->sa_family;
188 	pfr.rule.proto = proto;
189 	pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
190 	pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
191 	pfr.rule.rdr.addr.type = PF_ADDR_NONE;
192 	pfr.rule.nat.addr.type = PF_ADDR_NONE;
193 
194 	if (src->sa_family == AF_INET) {
195 		memcpy(&pfr.rule.src.addr.v.a.addr.v4,
196 		    &satosin(src)->sin_addr.s_addr, 4);
197 		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
198 		memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
199 		    &satosin(dst)->sin_addr.s_addr, 4);
200 		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
201 	} else {
202 		memcpy(&pfr.rule.src.addr.v.a.addr.v6,
203 		    &satosin6(src)->sin6_addr.s6_addr, 16);
204 		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
205 		memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
206 		    &satosin6(dst)->sin6_addr.s6_addr, 16);
207 		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
208 	}
209 	pfr.rule.dst.port_op = PF_OP_EQ;
210 	pfr.rule.dst.port[0] = htons(d_port);
211 #ifdef notyet
212 	pfr.rule.rule_flag = PFRULE_ONCE;
213 #endif
214 	pfr.rule.action = PF_PASS;
215 	pfr.rule.quick = 1;
216 	pfr.rule.log = rule_log;
217 	pfr.rule.keep_state = 1;
218 	pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
219 	pfr.rule.flagset = (proto == IPPROTO_TCP ?
220 	    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
221 #ifdef notyet
222 	pfr.rule.max_states = 1;
223 #endif
224 	if (qname != NULL)
225 		strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
226 
227 	return (0);
228 }
229