xref: /dragonfly/usr.sbin/ftp-proxy/filter.c (revision a14160bc65a8e6493cc912bd27de0a8a52d98af7)
1 /*        $OpenBSD: filter.c,v 1.6 2007/08/01 09:31:41 henning 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 <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 
23 #include <net/if.h>
24 #include <net/pf/pfvar.h>
25 #include <netinet/in.h>
26 #include <netinet/tcp.h>
27 #include <arpa/inet.h>
28 
29 #include <err.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "filter.h"
37 
38 /* From netinet/in.h, but only _KERNEL_ gets them. */
39 #define satosin(sa) ((struct sockaddr_in *)(sa))
40 #define satosin6(sa)          ((struct sockaddr_in6 *)(sa))
41 
42 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
43 
44 int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
45     u_int16_t);
46 int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
47     struct sockaddr_in *);
48 int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
49     struct sockaddr_in6 *);
50 
51 static struct pfioc_pooladdr  pfp;
52 static struct pfioc_rule      pfr;
53 static struct pfioc_trans     pft;
54 static struct pfioc_trans_e   pfte[TRANS_SIZE];
55 static int dev, rule_log;
56 static char *qname, *tagname;
57 
58 int
add_filter(u_int32_t id,u_int8_t dir,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port)59 add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
60     struct sockaddr *dst, u_int16_t d_port)
61 {
62           if (!src || !dst || !d_port) {
63                     errno = EINVAL;
64                     return (-1);
65           }
66 
67           if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1)
68                     return (-1);
69 
70           pfr.rule.direction = dir;
71           if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
72                     return (-1);
73 
74           return (0);
75 }
76 
77 int
add_nat(u_int32_t id,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port,struct sockaddr * nat,u_int16_t nat_range_low,u_int16_t nat_range_high)78 add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
79     u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
80     u_int16_t nat_range_high)
81 {
82           if (!src || !dst || !d_port || !nat || !nat_range_low ||
83               (src->sa_family != nat->sa_family)) {
84                     errno = EINVAL;
85                     return (-1);
86           }
87 
88           if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1)
89                     return (-1);
90 
91           if (nat->sa_family == AF_INET) {
92                     memcpy(&pfp.addr.addr.v.a.addr.v4,
93                         &satosin(nat)->sin_addr.s_addr, 4);
94                     memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
95           } else {
96                     memcpy(&pfp.addr.addr.v.a.addr.v6,
97                         &satosin6(nat)->sin6_addr.s6_addr, 16);
98                     memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
99           }
100           if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
101                     return (-1);
102 
103           pfr.rule.rpool.proxy_port[0] = nat_range_low;
104           pfr.rule.rpool.proxy_port[1] = nat_range_high;
105           if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
106                     return (-1);
107 
108           return (0);
109 }
110 
111 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)112 add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
113     u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port)
114 {
115           if (!src || !dst || !d_port || !rdr || !rdr_port ||
116               (src->sa_family != rdr->sa_family)) {
117                     errno = EINVAL;
118                     return (-1);
119           }
120 
121           if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1)
122                     return (-1);
123 
124           if (rdr->sa_family == AF_INET) {
125                     memcpy(&pfp.addr.addr.v.a.addr.v4,
126                         &satosin(rdr)->sin_addr.s_addr, 4);
127                     memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
128           } else {
129                     memcpy(&pfp.addr.addr.v.a.addr.v6,
130                         &satosin6(rdr)->sin6_addr.s6_addr, 16);
131                     memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
132           }
133           if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
134                     return (-1);
135 
136           pfr.rule.rpool.proxy_port[0] = rdr_port;
137           if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
138                     return (-1);
139 
140           return (0);
141 }
142 
143 int
do_commit(void)144 do_commit(void)
145 {
146           if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
147                     return (-1);
148 
149           return (0);
150 }
151 
152 int
do_rollback(void)153 do_rollback(void)
154 {
155           if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
156                     return (-1);
157 
158           return (0);
159 }
160 
161 void
init_filter(char * opt_qname,char * opt_tagname,int opt_verbose)162 init_filter(char *opt_qname, char *opt_tagname, int opt_verbose)
163 {
164           struct pf_status status;
165 
166           qname = opt_qname;
167           tagname = opt_tagname;
168 
169           if (opt_verbose == 1)
170                     rule_log = PF_LOG;
171           else if (opt_verbose == 2)
172                     rule_log = PF_LOG_ALL;
173 
174           dev = open("/dev/pf", O_RDWR);
175           if (dev == -1)
176                     err(1, "/dev/pf");
177           if (ioctl(dev, DIOCGETSTATUS, &status) == -1)
178                     err(1, "DIOCGETSTATUS");
179           if (!status.running)
180                     errx(1, "pf is disabled");
181 }
182 
183 int
prepare_commit(u_int32_t id)184 prepare_commit(u_int32_t id)
185 {
186           char an[PF_ANCHOR_NAME_SIZE];
187           int i;
188 
189           memset(&pft, 0, sizeof pft);
190           pft.size = TRANS_SIZE;
191           pft.esize = sizeof pfte[0];
192           pft.array = pfte;
193 
194           snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
195               getpid(), id);
196           for (i = 0; i < TRANS_SIZE; i++) {
197                     memset(&pfte[i], 0, sizeof pfte[0]);
198                     strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
199                     switch (i) {
200                     case TRANS_FILTER:
201                               pfte[i].rs_num = PF_RULESET_FILTER;
202                               break;
203                     case TRANS_NAT:
204                               pfte[i].rs_num = PF_RULESET_NAT;
205                               break;
206                     case TRANS_RDR:
207                               pfte[i].rs_num = PF_RULESET_RDR;
208                               break;
209                     default:
210                               errno = EINVAL;
211                               return (-1);
212                     }
213           }
214 
215           if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
216                     return (-1);
217 
218           return (0);
219 }
220 
221 int
prepare_rule(u_int32_t id,int rs_num,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port)222 prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
223     struct sockaddr *dst, u_int16_t d_port)
224 {
225           char an[PF_ANCHOR_NAME_SIZE];
226 
227           if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
228               (src->sa_family != dst->sa_family)) {
229                     errno = EPROTONOSUPPORT;
230                     return (-1);
231           }
232 
233           memset(&pfp, 0, sizeof pfp);
234           memset(&pfr, 0, sizeof pfr);
235           snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
236               getpid(), id);
237           strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
238           strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
239 
240           switch (rs_num) {
241           case PF_RULESET_FILTER:
242                     pfr.ticket = pfte[TRANS_FILTER].ticket;
243                     break;
244           case PF_RULESET_NAT:
245                     pfr.ticket = pfte[TRANS_NAT].ticket;
246                     break;
247           case PF_RULESET_RDR:
248                     pfr.ticket = pfte[TRANS_RDR].ticket;
249                     break;
250           default:
251                     errno = EINVAL;
252                     return (-1);
253           }
254           if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
255                     return (-1);
256           pfr.pool_ticket = pfp.ticket;
257 
258           /* Generic for all rule types. */
259           pfr.rule.af = src->sa_family;
260           pfr.rule.proto = IPPROTO_TCP;
261           pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
262           pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
263           if (src->sa_family == AF_INET) {
264                     memcpy(&pfr.rule.src.addr.v.a.addr.v4,
265                         &satosin(src)->sin_addr.s_addr, 4);
266                     memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
267                     memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
268                         &satosin(dst)->sin_addr.s_addr, 4);
269                     memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
270           } else {
271                     memcpy(&pfr.rule.src.addr.v.a.addr.v6,
272                         &satosin6(src)->sin6_addr.s6_addr, 16);
273                     memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
274                     memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
275                         &satosin6(dst)->sin6_addr.s6_addr, 16);
276                     memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
277           }
278           pfr.rule.dst.port_op = PF_OP_EQ;
279           pfr.rule.dst.port[0] = htons(d_port);
280           if (tagname != NULL)
281                     strlcpy(pfr.rule.tagname, tagname, sizeof pfr.rule.tagname);
282 
283           switch (rs_num) {
284           case PF_RULESET_FILTER:
285                     /*
286                      * pass quick [log] inet[6] proto tcp \
287                      *     from $src to $dst port = $d_port flags S/SA keep state
288                      *     (max 1) [queue qname]
289                      */
290                     pfr.rule.action = PF_PASS;
291                     pfr.rule.quick = 1;
292                     pfr.rule.log = rule_log;
293                     pfr.rule.keep_state = 1;
294                     pfr.rule.flags = TH_SYN;
295                     pfr.rule.flagset = (TH_SYN|TH_ACK);
296                     pfr.rule.max_states = 1;
297                     if (qname != NULL)
298                               strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
299                     break;
300           case PF_RULESET_NAT:
301                     /*
302                      * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
303                      */
304                     pfr.rule.action = PF_NAT;
305                     break;
306           case PF_RULESET_RDR:
307                     /*
308                      * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
309                      */
310                     pfr.rule.action = PF_RDR;
311                     break;
312           default:
313                     errno = EINVAL;
314                     return (-1);
315           }
316 
317           return (0);
318 }
319 
320 int
server_lookup(struct sockaddr * client,struct sockaddr * proxy,struct sockaddr * server)321 server_lookup(struct sockaddr *client, struct sockaddr *proxy,
322     struct sockaddr *server)
323 {
324           if (client->sa_family == AF_INET)
325                     return (server_lookup4(satosin(client), satosin(proxy),
326                         satosin(server)));
327 
328           if (client->sa_family == AF_INET6)
329                     return (server_lookup6(satosin6(client), satosin6(proxy),
330                         satosin6(server)));
331 
332           errno = EPROTONOSUPPORT;
333           return (-1);
334 }
335 
336 int
server_lookup4(struct sockaddr_in * client,struct sockaddr_in * proxy,struct sockaddr_in * server)337 server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
338     struct sockaddr_in *server)
339 {
340           struct pfioc_natlook pnl;
341 
342           memset(&pnl, 0, sizeof pnl);
343           pnl.direction = PF_OUT;
344           pnl.af = AF_INET;
345           pnl.proto = IPPROTO_TCP;
346           memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
347           memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
348           pnl.sport = client->sin_port;
349           pnl.dport = proxy->sin_port;
350 
351           if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
352                     return (-1);
353 
354           memset(server, 0, sizeof(struct sockaddr_in));
355           server->sin_len = sizeof(struct sockaddr_in);
356           server->sin_family = AF_INET;
357           memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
358               sizeof server->sin_addr.s_addr);
359           server->sin_port = pnl.rdport;
360 
361           return (0);
362 }
363 
364 int
server_lookup6(struct sockaddr_in6 * client,struct sockaddr_in6 * proxy,struct sockaddr_in6 * server)365 server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
366     struct sockaddr_in6 *server)
367 {
368           struct pfioc_natlook pnl;
369 
370           memset(&pnl, 0, sizeof pnl);
371           pnl.direction = PF_OUT;
372           pnl.af = AF_INET6;
373           pnl.proto = IPPROTO_TCP;
374           memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
375           memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
376           pnl.sport = client->sin6_port;
377           pnl.dport = proxy->sin6_port;
378 
379           if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
380                     return (-1);
381 
382           memset(server, 0, sizeof(struct sockaddr_in6));
383           server->sin6_len = sizeof(struct sockaddr_in6);
384           server->sin6_family = AF_INET6;
385           memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
386               sizeof server->sin6_addr);
387           server->sin6_port = pnl.rdport;
388 
389           return (0);
390 }
391