1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4 *
5 */
6
7 #include <assert.h>
8 #include <sys/nv.h>
9 #include <sys/sockio.h>
10 #include <dev/wg/if_wg.h>
11
12 #define IPC_SUPPORTS_KERNEL_INTERFACE
13
get_dgram_socket(void)14 static int get_dgram_socket(void)
15 {
16 static int sock = -1;
17 if (sock < 0)
18 sock = socket(AF_INET, SOCK_DGRAM, 0);
19 return sock;
20 }
21
kernel_get_wireguard_interfaces(struct string_list * list)22 static int kernel_get_wireguard_interfaces(struct string_list *list)
23 {
24 struct ifgroupreq ifgr = { .ifgr_name = "wg" };
25 struct ifg_req *ifg;
26 int s = get_dgram_socket(), ret = 0;
27
28 if (s < 0)
29 return -errno;
30
31 if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
32 return errno == ENOENT ? 0 : -errno;
33
34 ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
35 if (!ifgr.ifgr_groups)
36 return -errno;
37 if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
38 ret = -errno;
39 goto out;
40 }
41
42 for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
43 if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
44 goto out;
45 ifgr.ifgr_len -= sizeof(struct ifg_req);
46 }
47
48 out:
49 free(ifgr.ifgr_groups);
50 return ret;
51 }
52
kernel_get_device(struct wgdevice ** device,const char * ifname)53 static int kernel_get_device(struct wgdevice **device, const char *ifname)
54 {
55 struct wg_data_io wgd = { 0 };
56 nvlist_t *nvl_device = NULL;
57 const nvlist_t *const *nvl_peers;
58 struct wgdevice *dev = NULL;
59 size_t size, peer_count, i;
60 uint64_t number;
61 const void *binary;
62 int ret = 0, s;
63
64 *device = NULL;
65 s = get_dgram_socket();
66 if (s < 0)
67 goto err;
68
69 strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));
70 if (ioctl(s, SIOCGWG, &wgd) < 0)
71 goto err;
72
73 wgd.wgd_data = malloc(wgd.wgd_size);
74 if (!wgd.wgd_data)
75 goto err;
76 if (ioctl(s, SIOCGWG, &wgd) < 0)
77 goto err;
78
79 dev = calloc(1, sizeof(*dev));
80 if (!dev)
81 goto err;
82 strlcpy(dev->name, ifname, sizeof(dev->name));
83 nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);
84 if (!nvl_device)
85 goto err;
86
87 if (nvlist_exists_number(nvl_device, "listen-port")) {
88 number = nvlist_get_number(nvl_device, "listen-port");
89 if (number <= UINT16_MAX) {
90 dev->listen_port = number;
91 dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
92 }
93 }
94 if (nvlist_exists_number(nvl_device, "user-cookie")) {
95 number = nvlist_get_number(nvl_device, "user-cookie");
96 if (number <= UINT32_MAX) {
97 dev->fwmark = number;
98 dev->flags |= WGDEVICE_HAS_FWMARK;
99 }
100 }
101 if (nvlist_exists_binary(nvl_device, "public-key")) {
102 binary = nvlist_get_binary(nvl_device, "public-key", &size);
103 if (binary && size == sizeof(dev->public_key)) {
104 memcpy(dev->public_key, binary, sizeof(dev->public_key));
105 dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
106 }
107 }
108 if (nvlist_exists_binary(nvl_device, "private-key")) {
109 binary = nvlist_get_binary(nvl_device, "private-key", &size);
110 if (binary && size == sizeof(dev->private_key)) {
111 memcpy(dev->private_key, binary, sizeof(dev->private_key));
112 dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
113 }
114 }
115 if (!nvlist_exists_nvlist_array(nvl_device, "peers"))
116 goto skip_peers;
117 nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count);
118 if (!nvl_peers)
119 goto skip_peers;
120 for (i = 0; i < peer_count; ++i) {
121 struct wgpeer *peer;
122 struct wgallowedip *aip = NULL;
123 const nvlist_t *const *nvl_aips;
124 size_t aip_count, j;
125
126 peer = calloc(1, sizeof(*peer));
127 if (!peer)
128 goto err_peer;
129 if (nvlist_exists_binary(nvl_peers[i], "public-key")) {
130 binary = nvlist_get_binary(nvl_peers[i], "public-key", &size);
131 if (binary && size == sizeof(peer->public_key)) {
132 memcpy(peer->public_key, binary, sizeof(peer->public_key));
133 peer->flags |= WGPEER_HAS_PUBLIC_KEY;
134 }
135 }
136 if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) {
137 binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size);
138 if (binary && size == sizeof(peer->preshared_key)) {
139 memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));
140 if (!key_is_zero(peer->preshared_key))
141 peer->flags |= WGPEER_HAS_PRESHARED_KEY;
142 }
143 }
144 if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) {
145 number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval");
146 if (number <= UINT16_MAX) {
147 peer->persistent_keepalive_interval = number;
148 peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
149 }
150 }
151 if (nvlist_exists_binary(nvl_peers[i], "endpoint")) {
152 const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size);
153 if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) &&
154 (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))
155 memcpy(&peer->endpoint.addr, endpoint, size);
156 }
157 if (nvlist_exists_number(nvl_peers[i], "rx-bytes"))
158 peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes");
159 if (nvlist_exists_number(nvl_peers[i], "tx-bytes"))
160 peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes");
161 if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) {
162 binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size);
163 if (binary && size == sizeof(peer->last_handshake_time))
164 memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time));
165 }
166
167 if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips"))
168 goto skip_allowed_ips;
169 nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count);
170 if (!aip_count || !nvl_aips)
171 goto skip_allowed_ips;
172 for (j = 0; j < aip_count; ++j) {
173 if (!nvlist_exists_number(nvl_aips[j], "cidr"))
174 continue;
175 if (!nvlist_exists_binary(nvl_aips[j], "ipv4") && !nvlist_exists_binary(nvl_aips[j], "ipv6"))
176 continue;
177 aip = calloc(1, sizeof(*aip));
178 if (!aip)
179 goto err_allowed_ips;
180 number = nvlist_get_number(nvl_aips[j], "cidr");
181 if (nvlist_exists_binary(nvl_aips[j], "ipv4")) {
182 binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size);
183 if (!binary || number > 32) {
184 ret = EINVAL;
185 goto err_allowed_ips;
186 }
187 aip->family = AF_INET;
188 aip->cidr = number;
189 memcpy(&aip->ip4, binary, sizeof(aip->ip4));
190 } else {
191 assert(nvlist_exists_binary(nvl_aips[j], "ipv6"));
192 binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size);
193 if (!binary || number > 128) {
194 ret = EINVAL;
195 goto err_allowed_ips;
196 }
197 aip->family = AF_INET6;
198 aip->cidr = number;
199 memcpy(&aip->ip6, binary, sizeof(aip->ip6));
200 }
201
202 if (!peer->first_allowedip)
203 peer->first_allowedip = aip;
204 else
205 peer->last_allowedip->next_allowedip = aip;
206 peer->last_allowedip = aip;
207 aip = NULL;
208 continue;
209
210 err_allowed_ips:
211 if (!ret)
212 ret = -errno;
213 free(aip);
214 goto err_peer;
215 }
216
217 /* Nothing leaked, hopefully -- ownership transferred or aip freed. */
218 assert(aip == NULL);
219 skip_allowed_ips:
220 if (!dev->first_peer)
221 dev->first_peer = peer;
222 else
223 dev->last_peer->next_peer = peer;
224 dev->last_peer = peer;
225 continue;
226
227 err_peer:
228 if (!ret)
229 ret = -errno;
230 free(peer);
231 goto err;
232 }
233
234 skip_peers:
235 free(wgd.wgd_data);
236 nvlist_destroy(nvl_device);
237 *device = dev;
238 return 0;
239
240 err:
241 if (!ret)
242 ret = -errno;
243 free(wgd.wgd_data);
244 nvlist_destroy(nvl_device);
245 free(dev);
246 return ret;
247 }
248
249
kernel_set_device(struct wgdevice * dev)250 static int kernel_set_device(struct wgdevice *dev)
251 {
252 struct wg_data_io wgd = { 0 };
253 nvlist_t *nvl_device = NULL, **nvl_peers = NULL;
254 size_t peer_count = 0, i = 0;
255 struct wgpeer *peer;
256 int ret = 0, s;
257
258 strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));
259
260 nvl_device = nvlist_create(0);
261 if (!nvl_device)
262 goto err;
263
264 for_each_wgpeer(dev, peer)
265 ++peer_count;
266 if (peer_count) {
267 nvl_peers = calloc(peer_count, sizeof(*nvl_peers));
268 if (!nvl_peers)
269 goto err;
270 }
271 if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
272 nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key));
273 if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
274 nvlist_add_number(nvl_device, "listen-port", dev->listen_port);
275 if (dev->flags & WGDEVICE_HAS_FWMARK)
276 nvlist_add_number(nvl_device, "user-cookie", dev->fwmark);
277 if (dev->flags & WGDEVICE_REPLACE_PEERS)
278 nvlist_add_bool(nvl_device, "replace-peers", true);
279
280 for_each_wgpeer(dev, peer) {
281 size_t aip_count = 0, j = 0;
282 nvlist_t **nvl_aips = NULL;
283 struct wgallowedip *aip;
284
285 nvl_peers[i] = nvlist_create(0);
286 if (!nvl_peers[i])
287 goto err_peer;
288 for_each_wgallowedip(peer, aip)
289 ++aip_count;
290 if (aip_count) {
291 nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
292 if (!nvl_aips)
293 goto err_peer;
294 }
295 nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
296 if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
297 nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key));
298 if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
299 nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval);
300 if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
301 nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len);
302 if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
303 nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
304 if (peer->flags & WGPEER_REMOVE_ME)
305 nvlist_add_bool(nvl_peers[i], "remove", true);
306 for_each_wgallowedip(peer, aip) {
307 nvl_aips[j] = nvlist_create(0);
308 if (!nvl_aips[j])
309 goto err_peer;
310 nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
311 if (aip->family == AF_INET)
312 nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
313 else if (aip->family == AF_INET6)
314 nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
315 ++j;
316 }
317 if (j) {
318 nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j);
319 for (j = 0; j < aip_count; ++j)
320 nvlist_destroy(nvl_aips[j]);
321 free(nvl_aips);
322 }
323 ++i;
324 continue;
325
326 err_peer:
327 ret = -errno;
328 for (j = 0; j < aip_count && nvl_aips; ++j)
329 nvlist_destroy(nvl_aips[j]);
330 free(nvl_aips);
331 nvlist_destroy(nvl_peers[i]);
332 nvl_peers[i] = NULL;
333 goto err;
334 }
335 if (i) {
336 nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i);
337 for (i = 0; i < peer_count; ++i)
338 nvlist_destroy(nvl_peers[i]);
339 free(nvl_peers);
340 nvl_peers = NULL;
341 }
342 wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);
343 nvlist_destroy(nvl_device);
344 nvl_device = NULL;
345 if (!wgd.wgd_data)
346 goto err;
347 s = get_dgram_socket();
348 if (s < 0)
349 return -errno;
350 return ioctl(s, SIOCSWG, &wgd);
351
352 err:
353 if (!ret)
354 ret = -errno;
355 for (i = 0; i < peer_count && nvl_peers; ++i)
356 nvlist_destroy(nvl_peers[i]);
357 free(nvl_peers);
358 nvlist_destroy(nvl_device);
359 return ret;
360 }
361