1 /* $OpenBSD: filter.c,v 1.21 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 <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22
23 #include <netinet/in.h>
24 #include <netinet/tcp.h>
25 #include <arpa/inet.h>
26 #include <net/if.h>
27 #include <net/pfvar.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 int add_addr(struct sockaddr *, struct pf_pool *);
43 int prepare_rule(u_int32_t, struct sockaddr *, struct sockaddr *,
44 u_int16_t);
45
46 static struct pfioc_rule pfr;
47 static struct pfioc_trans pft;
48 static struct pfioc_trans_e pfte;
49 static int dev, rule_log;
50 static char *qname, *tagname;
51
52 int
add_addr(struct sockaddr * addr,struct pf_pool * pfp)53 add_addr(struct sockaddr *addr, struct pf_pool *pfp)
54 {
55 if (addr->sa_family == AF_INET) {
56 memcpy(&pfp->addr.v.a.addr.v4,
57 &satosin(addr)->sin_addr.s_addr, 4);
58 memset(&pfp->addr.v.a.mask.addr8, 255, 4);
59 } else {
60 memcpy(&pfp->addr.v.a.addr.v6,
61 &satosin6(addr)->sin6_addr.s6_addr, 16);
62 memset(&pfp->addr.v.a.mask.addr8, 255, 16);
63 }
64 pfp->addr.type = PF_ADDR_ADDRMASK;
65 return (0);
66 }
67
68 int
add_nat(u_int32_t id,struct sockaddr * src,int s_rd,struct sockaddr * dst,u_int16_t d_port,struct sockaddr * nat,u_int16_t nat_range_low,u_int16_t nat_range_high)69 add_nat(u_int32_t id, struct sockaddr *src, int s_rd, struct sockaddr *dst,
70 u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
71 u_int16_t nat_range_high)
72 {
73 if (!src || !dst || !d_port || !nat || !nat_range_low ||
74 !nat_range_high || (src->sa_family != nat->sa_family)) {
75 errno = EINVAL;
76 return (-1);
77 }
78
79 if (prepare_rule(id, src, dst, d_port) == -1)
80 return (-1);
81
82 if (add_addr(nat, &pfr.rule.nat) == -1)
83 return (-1);
84
85 pfr.rule.direction = PF_OUT;
86 pfr.rule.onrdomain = s_rd;
87 pfr.rule.rtableid = -1;
88 pfr.rule.nat.proxy_port[0] = nat_range_low;
89 pfr.rule.nat.proxy_port[1] = nat_range_high;
90 if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
91 return (-1);
92
93 return (0);
94 }
95
96 int
add_rdr(u_int32_t id,struct sockaddr * src,int s_rd,struct sockaddr * dst,u_int16_t d_port,struct sockaddr * rdr,u_int16_t rdr_port,int d_rd)97 add_rdr(u_int32_t id, struct sockaddr *src, int s_rd, struct sockaddr *dst,
98 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, int d_rd)
99 {
100 if (!src || !dst || !d_port || !rdr || !rdr_port ||
101 (src->sa_family != rdr->sa_family)) {
102 errno = EINVAL;
103 return (-1);
104 }
105
106 if (prepare_rule(id, src, dst, d_port) == -1)
107 return (-1);
108
109 if (add_addr(rdr, &pfr.rule.rdr) == -1)
110 return (-1);
111
112 pfr.rule.direction = PF_IN;
113 pfr.rule.onrdomain = s_rd;
114 pfr.rule.rtableid = d_rd;
115 pfr.rule.rdr.proxy_port[0] = rdr_port;
116 if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
117 return (-1);
118
119 return (0);
120 }
121
122 int
do_commit(void)123 do_commit(void)
124 {
125 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
126 return (-1);
127
128 return (0);
129 }
130
131 int
do_rollback(void)132 do_rollback(void)
133 {
134 if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
135 return (-1);
136
137 return (0);
138 }
139
140 void
init_filter(char * opt_qname,char * opt_tagname,int opt_verbose)141 init_filter(char *opt_qname, char *opt_tagname, int opt_verbose)
142 {
143 struct pf_status status;
144
145 qname = opt_qname;
146 tagname = opt_tagname;
147
148 if (opt_verbose == 1)
149 rule_log = PF_LOG;
150 else if (opt_verbose == 2)
151 rule_log = PF_LOG_ALL;
152
153 dev = open("/dev/pf", O_RDWR);
154 if (dev == -1)
155 err(1, "open /dev/pf");
156 if (ioctl(dev, DIOCGETSTATUS, &status) == -1)
157 err(1, "DIOCGETSTATUS");
158 if (!status.running)
159 errx(1, "pf is disabled");
160 }
161
162 int
prepare_commit(u_int32_t id)163 prepare_commit(u_int32_t id)
164 {
165 char an[PF_ANCHOR_NAME_SIZE];
166
167 memset(&pft, 0, sizeof pft);
168 pft.size = 1;
169 pft.esize = sizeof pfte;
170 pft.array = &pfte;
171
172 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
173 getpid(), id);
174 memset(&pfte, 0, sizeof pfte);
175 strlcpy(pfte.anchor, an, PF_ANCHOR_NAME_SIZE);
176 pfte.type = PF_TRANS_RULESET;
177
178 if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
179 return (-1);
180
181 return (0);
182 }
183
184 int
prepare_rule(u_int32_t id,struct sockaddr * src,struct sockaddr * dst,u_int16_t d_port)185 prepare_rule(u_int32_t id, struct sockaddr *src,
186 struct sockaddr *dst, u_int16_t d_port)
187 {
188 char an[PF_ANCHOR_NAME_SIZE];
189
190 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
191 (src->sa_family != dst->sa_family)) {
192 errno = EPROTONOSUPPORT;
193 return (-1);
194 }
195
196 memset(&pfr, 0, sizeof pfr);
197 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
198 getpid(), id);
199 strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
200
201 pfr.ticket = pfte.ticket;
202
203 /* Generic for all rule types. */
204 pfr.rule.af = src->sa_family;
205 pfr.rule.proto = IPPROTO_TCP;
206 pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
207 pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
208 pfr.rule.nat.addr.type = PF_ADDR_NONE;
209 pfr.rule.rdr.addr.type = PF_ADDR_NONE;
210
211 if (src->sa_family == AF_INET) {
212 memcpy(&pfr.rule.src.addr.v.a.addr.v4,
213 &satosin(src)->sin_addr.s_addr, 4);
214 memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
215 memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
216 &satosin(dst)->sin_addr.s_addr, 4);
217 memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
218 } else {
219 memcpy(&pfr.rule.src.addr.v.a.addr.v6,
220 &satosin6(src)->sin6_addr.s6_addr, 16);
221 memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
222 memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
223 &satosin6(dst)->sin6_addr.s6_addr, 16);
224 memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
225 }
226 pfr.rule.dst.port_op = PF_OP_EQ;
227 pfr.rule.dst.port[0] = htons(d_port);
228
229 /*
230 * pass [quick] [log] inet[6] proto tcp \
231 * from $src to $dst port = $d_port flags S/SA keep state
232 * (max 1) [queue qname] [tag tagname]
233 */
234 if (tagname != NULL)
235 pfr.rule.action = PF_MATCH;
236 else
237 pfr.rule.action = PF_PASS;
238 pfr.rule.quick = 1;
239 pfr.rule.log = rule_log;
240 pfr.rule.keep_state = 1;
241 pfr.rule.flags = TH_SYN;
242 pfr.rule.flagset = (TH_SYN|TH_ACK);
243 pfr.rule.max_states = 1;
244 if (qname != NULL)
245 strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
246 if (tagname != NULL) {
247 pfr.rule.quick = 0;
248 strlcpy(pfr.rule.tagname, tagname,
249 sizeof pfr.rule.tagname);
250 }
251
252 return (0);
253 }
254