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
14static 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
22static 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
48out:
49	free(ifgr.ifgr_groups);
50	return ret;
51}
52
53static 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
234skip_peers:
235	free(wgd.wgd_data);
236	nvlist_destroy(nvl_device);
237	*device = dev;
238	return 0;
239
240err:
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
250static 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
352err:
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