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 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 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 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 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