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