]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/firewall-util.c
firewall-util: add an assert that we're not overwriting a buffer
[thirdparty/systemd.git] / src / shared / firewall-util.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2015 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 /* Temporary work-around for broken glibc vs. linux kernel header definitions
21 * This is already fixed upstream, remove this when distributions have updated.
22 */
23 #define _NET_IF_H 1
24
25 #include <alloca.h>
26 #include <arpa/inet.h>
27 #include <endian.h>
28 #include <errno.h>
29 #include <stddef.h>
30 #include <string.h>
31 #include <sys/socket.h>
32 #include <net/if.h>
33 #ifndef IFNAMSIZ
34 #define IFNAMSIZ 16
35 #endif
36 #include <linux/if.h>
37 #include <linux/netfilter_ipv4/ip_tables.h>
38 #include <linux/netfilter/nf_nat.h>
39 #include <linux/netfilter/xt_addrtype.h>
40 #include <libiptc/libiptc.h>
41
42 #include "alloc-util.h"
43 #include "firewall-util.h"
44 #include "in-addr-util.h"
45 #include "macro.h"
46 #include "socket-util.h"
47
48 DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
49
50 static int entry_fill_basics(
51 struct ipt_entry *entry,
52 int protocol,
53 const char *in_interface,
54 const union in_addr_union *source,
55 unsigned source_prefixlen,
56 const char *out_interface,
57 const union in_addr_union *destination,
58 unsigned destination_prefixlen) {
59
60 assert(entry);
61
62 if (out_interface && !ifname_valid(out_interface))
63 return -EINVAL;
64 if (in_interface && !ifname_valid(in_interface))
65 return -EINVAL;
66
67 entry->ip.proto = protocol;
68
69 if (in_interface) {
70 strcpy(entry->ip.iniface, in_interface);
71 memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1);
72 }
73 if (source) {
74 entry->ip.src = source->in;
75 in_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
76 }
77
78 if (out_interface) {
79 size_t l = strlen(out_interface);
80 assert(l < sizeof entry->ip.outiface && l < sizeof entry->ip.outiface_mask);
81
82 strcpy(entry->ip.outiface, out_interface);
83 memset(entry->ip.outiface_mask, 0xFF, l + 1);
84 }
85 if (destination) {
86 entry->ip.dst = destination->in;
87 in_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
88 }
89
90 return 0;
91 }
92
93 int fw_add_masquerade(
94 bool add,
95 int af,
96 int protocol,
97 const union in_addr_union *source,
98 unsigned source_prefixlen,
99 const char *out_interface,
100 const union in_addr_union *destination,
101 unsigned destination_prefixlen) {
102
103 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
104 struct ipt_entry *entry, *mask;
105 struct ipt_entry_target *t;
106 size_t sz;
107 struct nf_nat_ipv4_multi_range_compat *mr;
108 int r;
109
110 if (af != AF_INET)
111 return -EOPNOTSUPP;
112
113 if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
114 return -EOPNOTSUPP;
115
116 h = iptc_init("nat");
117 if (!h)
118 return -errno;
119
120 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
121 XT_ALIGN(sizeof(struct ipt_entry_target)) +
122 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
123
124 /* Put together the entry we want to add or remove */
125 entry = alloca0(sz);
126 entry->next_offset = sz;
127 entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
128 r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
129 if (r < 0)
130 return r;
131
132 /* Fill in target part */
133 t = ipt_get_target(entry);
134 t->u.target_size =
135 XT_ALIGN(sizeof(struct ipt_entry_target)) +
136 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
137 strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
138 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
139 mr->rangesize = 1;
140
141 /* Create a search mask entry */
142 mask = alloca(sz);
143 memset(mask, 0xFF, sz);
144
145 if (add) {
146 if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h))
147 return 0;
148 if (errno != ENOENT) /* if other error than not existing yet, fail */
149 return -errno;
150
151 if (!iptc_insert_entry("POSTROUTING", entry, 0, h))
152 return -errno;
153 } else {
154 if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) {
155 if (errno == ENOENT) /* if it's already gone, all is good! */
156 return 0;
157
158 return -errno;
159 }
160 }
161
162 if (!iptc_commit(h))
163 return -errno;
164
165 return 0;
166 }
167
168 int fw_add_local_dnat(
169 bool add,
170 int af,
171 int protocol,
172 const char *in_interface,
173 const union in_addr_union *source,
174 unsigned source_prefixlen,
175 const union in_addr_union *destination,
176 unsigned destination_prefixlen,
177 uint16_t local_port,
178 const union in_addr_union *remote,
179 uint16_t remote_port,
180 const union in_addr_union *previous_remote) {
181
182
183 _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
184 struct ipt_entry *entry, *mask;
185 struct ipt_entry_target *t;
186 struct ipt_entry_match *m;
187 struct xt_addrtype_info_v1 *at;
188 struct nf_nat_ipv4_multi_range_compat *mr;
189 size_t sz, msz;
190 int r;
191
192 assert(add || !previous_remote);
193
194 if (af != AF_INET)
195 return -EOPNOTSUPP;
196
197 if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
198 return -EOPNOTSUPP;
199
200 if (local_port <= 0)
201 return -EINVAL;
202
203 if (remote_port <= 0)
204 return -EINVAL;
205
206 h = iptc_init("nat");
207 if (!h)
208 return -errno;
209
210 sz = XT_ALIGN(sizeof(struct ipt_entry)) +
211 XT_ALIGN(sizeof(struct ipt_entry_match)) +
212 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
213 XT_ALIGN(sizeof(struct ipt_entry_target)) +
214 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
215
216 if (protocol == IPPROTO_TCP)
217 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
218 XT_ALIGN(sizeof(struct xt_tcp));
219 else
220 msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
221 XT_ALIGN(sizeof(struct xt_udp));
222
223 sz += msz;
224
225 /* Fill in basic part */
226 entry = alloca0(sz);
227 entry->next_offset = sz;
228 entry->target_offset =
229 XT_ALIGN(sizeof(struct ipt_entry)) +
230 XT_ALIGN(sizeof(struct ipt_entry_match)) +
231 XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
232 msz;
233 r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
234 if (r < 0)
235 return r;
236
237 /* Fill in first match */
238 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
239 m->u.match_size = msz;
240 if (protocol == IPPROTO_TCP) {
241 struct xt_tcp *tcp;
242
243 strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
244 tcp = (struct xt_tcp*) m->data;
245 tcp->dpts[0] = tcp->dpts[1] = local_port;
246 tcp->spts[0] = 0;
247 tcp->spts[1] = 0xFFFF;
248
249 } else {
250 struct xt_udp *udp;
251
252 strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
253 udp = (struct xt_udp*) m->data;
254 udp->dpts[0] = udp->dpts[1] = local_port;
255 udp->spts[0] = 0;
256 udp->spts[1] = 0xFFFF;
257 }
258
259 /* Fill in second match */
260 m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
261 m->u.match_size =
262 XT_ALIGN(sizeof(struct ipt_entry_match)) +
263 XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
264 strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
265 m->u.user.revision = 1;
266 at = (struct xt_addrtype_info_v1*) m->data;
267 at->dest = XT_ADDRTYPE_LOCAL;
268
269 /* Fill in target part */
270 t = ipt_get_target(entry);
271 t->u.target_size =
272 XT_ALIGN(sizeof(struct ipt_entry_target)) +
273 XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
274 strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
275 mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
276 mr->rangesize = 1;
277 mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
278 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
279 if (protocol == IPPROTO_TCP)
280 mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port);
281 else
282 mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port);
283
284 mask = alloca0(sz);
285 memset(mask, 0xFF, sz);
286
287 if (add) {
288 /* Add the PREROUTING rule, if it is missing so far */
289 if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
290 if (errno != ENOENT)
291 return -EINVAL;
292
293 if (!iptc_insert_entry("PREROUTING", entry, 0, h))
294 return -errno;
295 }
296
297 /* If a previous remote is set, remove its entry */
298 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
299 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
300
301 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
302 if (errno != ENOENT)
303 return -errno;
304 }
305
306 mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
307 }
308
309 /* Add the OUTPUT rule, if it is missing so far */
310 if (!in_interface) {
311
312 /* Don't apply onto loopback addresses */
313 if (!destination) {
314 entry->ip.dst.s_addr = htobe32(0x7F000000);
315 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
316 entry->ip.invflags = IPT_INV_DSTIP;
317 }
318
319 if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
320 if (errno != ENOENT)
321 return -errno;
322
323 if (!iptc_insert_entry("OUTPUT", entry, 0, h))
324 return -errno;
325 }
326
327 /* If a previous remote is set, remove its entry */
328 if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
329 mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
330
331 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
332 if (errno != ENOENT)
333 return -errno;
334 }
335 }
336 }
337 } else {
338 if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
339 if (errno != ENOENT)
340 return -errno;
341 }
342
343 if (!in_interface) {
344 if (!destination) {
345 entry->ip.dst.s_addr = htobe32(0x7F000000);
346 entry->ip.dmsk.s_addr = htobe32(0xFF000000);
347 entry->ip.invflags = IPT_INV_DSTIP;
348 }
349
350 if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
351 if (errno != ENOENT)
352 return -errno;
353 }
354 }
355 }
356
357 if (!iptc_commit(h))
358 return -errno;
359
360 return 0;
361 }