1 1.6 oster /* $NetBSD: wgconfig.c,v 1.6 2023/05/07 16:05:07 oster Exp $ */ 2 1.1 riastrad 3 1.1 riastrad /* 4 1.1 riastrad * Copyright (C) Ryota Ozaki <ozaki.ryota (at) gmail.com> 5 1.1 riastrad * All rights reserved. 6 1.1 riastrad * 7 1.1 riastrad * Redistribution and use in source and binary forms, with or without 8 1.1 riastrad * modification, are permitted provided that the following conditions 9 1.1 riastrad * are met: 10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright 11 1.1 riastrad * notice, this list of conditions and the following disclaimer. 12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the 14 1.1 riastrad * documentation and/or other materials provided with the distribution. 15 1.1 riastrad * 3. Neither the name of the project nor the names of its contributors 16 1.1 riastrad * may be used to endorse or promote products derived from this software 17 1.1 riastrad * without specific prior written permission. 18 1.1 riastrad * 19 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 1.1 riastrad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 riastrad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 riastrad * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 1.1 riastrad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 riastrad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 riastrad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 riastrad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 riastrad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 riastrad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 riastrad * SUCH DAMAGE. 30 1.1 riastrad */ 31 1.1 riastrad 32 1.1 riastrad #include <sys/cdefs.h> 33 1.6 oster __RCSID("$NetBSD: wgconfig.c,v 1.6 2023/05/07 16:05:07 oster Exp $"); 34 1.1 riastrad 35 1.1 riastrad #include <sys/ioctl.h> 36 1.1 riastrad 37 1.1 riastrad #include <net/if.h> 38 1.1 riastrad #include <net/if_wg.h> 39 1.1 riastrad 40 1.1 riastrad #include <arpa/inet.h> 41 1.1 riastrad 42 1.1 riastrad #include <stdio.h> 43 1.1 riastrad #include <stdlib.h> 44 1.1 riastrad #include <string.h> 45 1.1 riastrad #include <err.h> 46 1.1 riastrad #include <unistd.h> 47 1.1 riastrad #include <errno.h> 48 1.1 riastrad #include <resolv.h> 49 1.1 riastrad #include <util.h> 50 1.1 riastrad #include <netdb.h> 51 1.1 riastrad 52 1.1 riastrad #include <prop/proplib.h> 53 1.1 riastrad 54 1.1 riastrad #define PROP_BUFFER_LEN 4096 55 1.1 riastrad #define KEY_LEN 32 56 1.1 riastrad #define KEY_BASE64_LEN 44 57 1.1 riastrad 58 1.1 riastrad __dead static void 59 1.1 riastrad usage(void) 60 1.1 riastrad { 61 1.1 riastrad const char *progname = getprogname(); 62 1.1 riastrad #define P(str) fprintf(stderr, "\t%s <interface> %s\n", progname, str) 63 1.1 riastrad 64 1.1 riastrad fprintf(stderr, "Usage:\n"); 65 1.1 riastrad P("[show all]"); 66 1.1 riastrad P("show peer <peer name> [--show-preshared-key]"); 67 1.1 riastrad P("show private-key"); 68 1.1 riastrad P("set private-key <file path>"); 69 1.1 riastrad P("set listen-port <port>"); 70 1.1 riastrad P("add peer <peer name> <base64 public key>\n" 71 1.1 riastrad "\t [--preshared-key=<file path>] [--endpoint=<ip>:<port>]\n" 72 1.1 riastrad "\t [--allowed-ips=<ip1>/<cidr1>[,<ip2>/<cidr2>]...]"); 73 1.1 riastrad P("delete peer <peer name>"); 74 1.1 riastrad 75 1.1 riastrad exit(EXIT_FAILURE); 76 1.1 riastrad #undef P 77 1.1 riastrad } 78 1.1 riastrad 79 1.1 riastrad static const char * 80 1.1 riastrad format_key(prop_object_t key_prop) 81 1.1 riastrad { 82 1.1 riastrad int error; 83 1.3 riastrad const void *key; 84 1.1 riastrad size_t key_len; 85 1.1 riastrad static char key_b64[KEY_BASE64_LEN + 1]; 86 1.1 riastrad 87 1.1 riastrad if (key_prop == NULL) 88 1.3 riastrad return "(none)"; 89 1.3 riastrad if (prop_object_type(key_prop) != PROP_TYPE_DATA) 90 1.3 riastrad errx(EXIT_FAILURE, "invalid key"); 91 1.1 riastrad 92 1.3 riastrad key = prop_data_value(key_prop); 93 1.1 riastrad key_len = prop_data_size(key_prop); 94 1.1 riastrad if (key_len != KEY_LEN) 95 1.4 martin errx(EXIT_FAILURE, "invalid key len: %zu", key_len); 96 1.1 riastrad error = b64_ntop(key, key_len, key_b64, KEY_BASE64_LEN + 1); 97 1.1 riastrad if (error == -1) 98 1.1 riastrad errx(EXIT_FAILURE, "b64_ntop failed"); 99 1.1 riastrad key_b64[KEY_BASE64_LEN] = '\0'; /* just in case */ 100 1.1 riastrad 101 1.1 riastrad return key_b64; 102 1.1 riastrad } 103 1.1 riastrad 104 1.1 riastrad static const char * 105 1.1 riastrad format_endpoint(prop_object_t endpoint_prop) 106 1.1 riastrad { 107 1.1 riastrad int error; 108 1.1 riastrad static char buf[INET6_ADDRSTRLEN]; 109 1.1 riastrad struct sockaddr_storage sockaddr; 110 1.3 riastrad const void *addr; 111 1.1 riastrad size_t addr_len; 112 1.1 riastrad 113 1.3 riastrad if (prop_object_type(endpoint_prop) != PROP_TYPE_DATA) 114 1.3 riastrad errx(EXIT_FAILURE, "invalid endpoint"); 115 1.3 riastrad 116 1.3 riastrad addr = prop_data_value(endpoint_prop); 117 1.1 riastrad addr_len = prop_data_size(endpoint_prop); 118 1.1 riastrad memcpy(&sockaddr, addr, addr_len); 119 1.1 riastrad 120 1.1 riastrad error = sockaddr_snprintf(buf, sizeof(buf), "%a:%p", 121 1.1 riastrad (struct sockaddr *)&sockaddr); 122 1.1 riastrad if (error == -1) 123 1.1 riastrad err(EXIT_FAILURE, "sockaddr_snprintf failed"); 124 1.1 riastrad 125 1.1 riastrad return buf; 126 1.1 riastrad } 127 1.1 riastrad 128 1.1 riastrad static void 129 1.1 riastrad handle_allowed_ips(prop_dictionary_t peer, const char *prefix) 130 1.1 riastrad { 131 1.3 riastrad prop_object_t prop_obj; 132 1.1 riastrad prop_array_t allowedips; 133 1.1 riastrad prop_object_iterator_t it; 134 1.1 riastrad prop_dictionary_t allowedip; 135 1.1 riastrad bool first = true; 136 1.1 riastrad 137 1.3 riastrad prop_obj = prop_dictionary_get(peer, "allowedips"); 138 1.3 riastrad if (prop_obj == NULL) 139 1.1 riastrad return; 140 1.3 riastrad if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) 141 1.3 riastrad errx(EXIT_FAILURE, "invalid allowedips"); 142 1.3 riastrad allowedips = prop_obj; 143 1.1 riastrad 144 1.1 riastrad printf("%sallowed-ips: ", prefix); 145 1.1 riastrad 146 1.1 riastrad it = prop_array_iterator(allowedips); 147 1.3 riastrad while ((prop_obj = prop_object_iterator_next(it)) != NULL) { 148 1.1 riastrad uint8_t family; 149 1.1 riastrad uint8_t cidr; 150 1.3 riastrad const void *addr; 151 1.3 riastrad size_t addrlen, famaddrlen; 152 1.1 riastrad char ntopbuf[INET6_ADDRSTRLEN]; 153 1.1 riastrad const char *ntopret; 154 1.1 riastrad 155 1.3 riastrad if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) { 156 1.3 riastrad warnx("invalid allowedip"); 157 1.3 riastrad continue; 158 1.3 riastrad } 159 1.3 riastrad allowedip = prop_obj; 160 1.3 riastrad 161 1.3 riastrad if (!prop_dictionary_get_uint8(allowedip, "family", &family)) { 162 1.1 riastrad warnx("allowed-ip without family"); 163 1.1 riastrad continue; 164 1.1 riastrad } 165 1.1 riastrad 166 1.3 riastrad if (!prop_dictionary_get_uint8(allowedip, "cidr", &cidr)) { 167 1.1 riastrad warnx("allowed-ip without cidr"); 168 1.1 riastrad continue; 169 1.1 riastrad } 170 1.1 riastrad 171 1.3 riastrad if (!prop_dictionary_get_data(allowedip, "ip", 172 1.3 riastrad &addr, &addrlen)) { 173 1.1 riastrad warnx("allowed-ip without ip"); 174 1.1 riastrad continue; 175 1.1 riastrad } 176 1.1 riastrad 177 1.3 riastrad switch (family) { 178 1.3 riastrad case AF_INET: 179 1.3 riastrad famaddrlen = sizeof(struct in_addr); 180 1.3 riastrad break; 181 1.3 riastrad case AF_INET6: 182 1.3 riastrad famaddrlen = sizeof(struct in6_addr); 183 1.3 riastrad break; 184 1.3 riastrad default: 185 1.3 riastrad warnx("unknown family %d", family); 186 1.3 riastrad continue; 187 1.3 riastrad } 188 1.3 riastrad if (addrlen != famaddrlen) { 189 1.3 riastrad warnx("allowed-ip bad ip length"); 190 1.3 riastrad continue; 191 1.3 riastrad } 192 1.3 riastrad 193 1.1 riastrad ntopret = inet_ntop(family, addr, ntopbuf, sizeof(ntopbuf)); 194 1.1 riastrad if (ntopret == NULL) 195 1.1 riastrad errx(EXIT_FAILURE, "inet_ntop failed"); 196 1.1 riastrad printf("%s%s/%u", first ? "" : ",", ntopbuf, cidr); 197 1.1 riastrad first = false; 198 1.1 riastrad } 199 1.1 riastrad if (first) 200 1.1 riastrad printf("(none)\n"); 201 1.1 riastrad else 202 1.1 riastrad printf("\n"); 203 1.1 riastrad } 204 1.1 riastrad 205 1.1 riastrad static prop_dictionary_t 206 1.1 riastrad ioctl_get(const char *interface) 207 1.1 riastrad { 208 1.1 riastrad int error = 0; 209 1.1 riastrad struct ifdrv ifd; 210 1.1 riastrad int sock; 211 1.1 riastrad char *buf; 212 1.1 riastrad prop_dictionary_t prop_dict; 213 1.1 riastrad 214 1.1 riastrad sock = socket(AF_INET, SOCK_DGRAM, 0); 215 1.1 riastrad if (error == -1) 216 1.1 riastrad err(EXIT_FAILURE, "socket"); 217 1.1 riastrad buf = malloc(PROP_BUFFER_LEN); 218 1.1 riastrad if (buf == NULL) 219 1.1 riastrad errx(EXIT_FAILURE, "malloc failed"); 220 1.1 riastrad 221 1.1 riastrad strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); 222 1.1 riastrad ifd.ifd_cmd = 0; 223 1.1 riastrad ifd.ifd_data = buf; 224 1.1 riastrad ifd.ifd_len = PROP_BUFFER_LEN; 225 1.1 riastrad 226 1.1 riastrad error = ioctl(sock, SIOCGDRVSPEC, &ifd); 227 1.1 riastrad if (error == -1) 228 1.1 riastrad err(EXIT_FAILURE, "ioctl(SIOCGDRVSPEC)"); 229 1.1 riastrad 230 1.1 riastrad prop_dict = prop_dictionary_internalize(buf); 231 1.1 riastrad if (prop_dict == NULL) 232 1.1 riastrad errx(EXIT_FAILURE, "prop_dictionary_internalize failed"); 233 1.1 riastrad 234 1.1 riastrad free(buf); 235 1.1 riastrad close(sock); 236 1.1 riastrad 237 1.1 riastrad return prop_dict; 238 1.1 riastrad } 239 1.1 riastrad 240 1.1 riastrad static void 241 1.1 riastrad show_peer(prop_dictionary_t peer, const char *prefix, bool show_psk) 242 1.1 riastrad { 243 1.1 riastrad prop_object_t prop_obj; 244 1.5 tih time_t sec; 245 1.1 riastrad 246 1.1 riastrad prop_obj = prop_dictionary_get(peer, "public_key"); 247 1.1 riastrad if (prop_obj == NULL) { 248 1.1 riastrad warnx("peer without public-key"); 249 1.1 riastrad return; 250 1.1 riastrad } 251 1.1 riastrad printf("%spublic-key: %s\n", prefix, format_key(prop_obj)); 252 1.1 riastrad 253 1.1 riastrad prop_obj = prop_dictionary_get(peer, "endpoint"); 254 1.1 riastrad if (prop_obj == NULL) 255 1.1 riastrad printf("%sendpoint: (none)\n", prefix); 256 1.1 riastrad else 257 1.1 riastrad printf("%sendpoint: %s\n", prefix, format_endpoint(prop_obj)); 258 1.1 riastrad 259 1.1 riastrad if (show_psk) { 260 1.1 riastrad prop_obj = prop_dictionary_get(peer, "preshared_key"); 261 1.1 riastrad printf("%spreshared-key: %s\n", prefix, format_key(prop_obj)); 262 1.1 riastrad } else { 263 1.1 riastrad printf("%spreshared-key: (hidden)\n", prefix); 264 1.1 riastrad } 265 1.1 riastrad 266 1.1 riastrad handle_allowed_ips(peer, prefix); 267 1.1 riastrad 268 1.5 tih if (prop_dictionary_get_int64(peer, "last_handshake_time_sec", &sec)) { 269 1.5 tih if (sec > 0) 270 1.5 tih printf("%slatest-handshake: %s", prefix, ctime(&sec)); 271 1.5 tih else 272 1.5 tih printf("%slatest-handshake: (never)\n", prefix); 273 1.5 tih } else { 274 1.1 riastrad printf("%slatest-handshake: (none)\n", prefix); 275 1.5 tih } 276 1.1 riastrad } 277 1.1 riastrad 278 1.1 riastrad static int 279 1.1 riastrad cmd_show_all(const char *interface, int argc, char *argv[]) 280 1.1 riastrad { 281 1.1 riastrad prop_dictionary_t prop_dict; 282 1.1 riastrad prop_object_t prop_obj; 283 1.3 riastrad uint16_t port; 284 1.3 riastrad prop_array_t peers; 285 1.1 riastrad 286 1.1 riastrad prop_dict = ioctl_get(interface); 287 1.1 riastrad 288 1.1 riastrad printf("interface: %s\n", interface); 289 1.1 riastrad 290 1.1 riastrad #if 0 291 1.1 riastrad prop_obj = prop_dictionary_get(prop_dict, "private_key"); 292 1.1 riastrad printf("\tprivate-key: %s\n", format_key(prop_obj)); 293 1.1 riastrad #else 294 1.1 riastrad printf("\tprivate-key: (hidden)\n"); 295 1.1 riastrad #endif 296 1.1 riastrad 297 1.3 riastrad if (prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) { 298 1.3 riastrad printf("\tlisten-port: %u\n", port); 299 1.1 riastrad } else { 300 1.1 riastrad printf("\tlisten-port: (none)\n"); 301 1.1 riastrad } 302 1.1 riastrad 303 1.3 riastrad prop_obj = prop_dictionary_get(prop_dict, "peers"); 304 1.3 riastrad if (prop_obj == NULL) 305 1.1 riastrad return EXIT_SUCCESS; 306 1.3 riastrad if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) 307 1.3 riastrad errx(EXIT_FAILURE, "invalid peers"); 308 1.3 riastrad peers = prop_obj; 309 1.1 riastrad 310 1.1 riastrad prop_object_iterator_t it = prop_array_iterator(peers); 311 1.3 riastrad while ((prop_obj = prop_object_iterator_next(it)) != NULL) { 312 1.3 riastrad const char *name; 313 1.3 riastrad 314 1.3 riastrad if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) 315 1.3 riastrad errx(EXIT_FAILURE, "invalid peer"); 316 1.3 riastrad prop_dictionary_t peer = prop_obj; 317 1.3 riastrad 318 1.3 riastrad if (prop_dictionary_get_string(peer, "name", &name)) { 319 1.1 riastrad printf("\tpeer: %s\n", name); 320 1.1 riastrad } else 321 1.1 riastrad printf("\tpeer: (none)\n"); 322 1.1 riastrad 323 1.1 riastrad show_peer(peer, "\t\t", false); 324 1.1 riastrad } 325 1.1 riastrad 326 1.1 riastrad return EXIT_SUCCESS; 327 1.1 riastrad } 328 1.1 riastrad 329 1.1 riastrad static int 330 1.1 riastrad cmd_show_peer(const char *interface, int argc, char *argv[]) 331 1.1 riastrad { 332 1.1 riastrad prop_dictionary_t prop_dict; 333 1.3 riastrad prop_object_t prop_obj; 334 1.1 riastrad const char *target; 335 1.1 riastrad const char *opt = "--show-preshared-key"; 336 1.1 riastrad bool show_psk = false; 337 1.1 riastrad 338 1.1 riastrad if (argc != 1 && argc != 2) 339 1.1 riastrad usage(); 340 1.1 riastrad target = argv[0]; 341 1.1 riastrad if (argc == 2) { 342 1.1 riastrad if (strncmp(argv[1], opt, strlen(opt)) != 0) 343 1.1 riastrad usage(); 344 1.1 riastrad show_psk = true; 345 1.1 riastrad } 346 1.1 riastrad 347 1.1 riastrad prop_dict = ioctl_get(interface); 348 1.1 riastrad 349 1.3 riastrad prop_obj = prop_dictionary_get(prop_dict, "peers"); 350 1.3 riastrad if (prop_obj == NULL) 351 1.1 riastrad return EXIT_SUCCESS; 352 1.3 riastrad if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) 353 1.3 riastrad errx(EXIT_FAILURE, "invalid peers"); 354 1.1 riastrad 355 1.3 riastrad prop_array_t peers = prop_obj; 356 1.1 riastrad prop_object_iterator_t it = prop_array_iterator(peers); 357 1.3 riastrad while ((prop_obj = prop_object_iterator_next(it)) != NULL) { 358 1.3 riastrad const char *name; 359 1.3 riastrad 360 1.3 riastrad if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) 361 1.3 riastrad errx(EXIT_FAILURE, "invalid peer"); 362 1.3 riastrad prop_dictionary_t peer = prop_obj; 363 1.3 riastrad 364 1.3 riastrad if (!prop_dictionary_get_string(peer, "name", &name)) 365 1.1 riastrad continue; 366 1.1 riastrad if (strcmp(name, target) == 0) { 367 1.1 riastrad printf("peer: %s\n", name); 368 1.1 riastrad show_peer(peer, "\t", show_psk); 369 1.3 riastrad return EXIT_SUCCESS; 370 1.1 riastrad } 371 1.1 riastrad } 372 1.1 riastrad 373 1.3 riastrad return EXIT_FAILURE; 374 1.1 riastrad } 375 1.1 riastrad 376 1.1 riastrad static int 377 1.1 riastrad cmd_show_private_key(const char *interface, int argc, char *argv[]) 378 1.1 riastrad { 379 1.1 riastrad prop_dictionary_t prop_dict; 380 1.1 riastrad prop_object_t prop_obj; 381 1.1 riastrad 382 1.1 riastrad prop_dict = ioctl_get(interface); 383 1.1 riastrad 384 1.1 riastrad prop_obj = prop_dictionary_get(prop_dict, "private_key"); 385 1.1 riastrad printf("private-key: %s\n", format_key(prop_obj)); 386 1.1 riastrad 387 1.1 riastrad return EXIT_SUCCESS; 388 1.1 riastrad } 389 1.1 riastrad 390 1.1 riastrad static void 391 1.1 riastrad ioctl_set(const char *interface, int cmd, char *propstr) 392 1.1 riastrad { 393 1.1 riastrad int error; 394 1.1 riastrad struct ifdrv ifd; 395 1.1 riastrad int sock; 396 1.1 riastrad 397 1.1 riastrad strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); 398 1.1 riastrad ifd.ifd_cmd = cmd; 399 1.1 riastrad ifd.ifd_data = propstr; 400 1.1 riastrad ifd.ifd_len = strlen(propstr); 401 1.1 riastrad sock = socket(AF_INET, SOCK_DGRAM, 0); 402 1.1 riastrad error = ioctl(sock, SIOCSDRVSPEC, &ifd); 403 1.1 riastrad if (error == -1) 404 1.1 riastrad err(EXIT_FAILURE, "ioctl(SIOCSDRVSPEC): cmd=%d", cmd); 405 1.1 riastrad close(sock); 406 1.1 riastrad } 407 1.1 riastrad 408 1.1 riastrad static void 409 1.1 riastrad base64_decode(const char keyb64buf[KEY_BASE64_LEN + 1], 410 1.1 riastrad unsigned char keybuf[KEY_LEN]) 411 1.1 riastrad { 412 1.1 riastrad int ret; 413 1.1 riastrad 414 1.1 riastrad ret = b64_pton(keyb64buf, keybuf, KEY_LEN); 415 1.1 riastrad if (ret == -1) 416 1.1 riastrad errx(EXIT_FAILURE, "b64_pton failed"); 417 1.1 riastrad } 418 1.1 riastrad 419 1.1 riastrad static void 420 1.1 riastrad read_key(const char *path, unsigned char keybuf[KEY_LEN]) 421 1.1 riastrad { 422 1.1 riastrad FILE *fp; 423 1.1 riastrad char keyb64buf[KEY_BASE64_LEN + 1]; 424 1.1 riastrad size_t n; 425 1.1 riastrad 426 1.1 riastrad fp = fopen(path, "r"); 427 1.1 riastrad if (fp == NULL) 428 1.1 riastrad err(EXIT_FAILURE, "fopen"); 429 1.1 riastrad 430 1.1 riastrad n = fread(keyb64buf, 1, KEY_BASE64_LEN, fp); 431 1.1 riastrad if (n != KEY_BASE64_LEN) 432 1.4 martin errx(EXIT_FAILURE, "base64 key len is short: %zu", n); 433 1.1 riastrad keyb64buf[KEY_BASE64_LEN] = '\0'; 434 1.1 riastrad 435 1.1 riastrad base64_decode(keyb64buf, keybuf); 436 1.1 riastrad } 437 1.1 riastrad 438 1.1 riastrad static int 439 1.1 riastrad cmd_set_private_key(const char *interface, int argc, char *argv[]) 440 1.1 riastrad { 441 1.1 riastrad unsigned char keybuf[KEY_LEN]; 442 1.1 riastrad 443 1.1 riastrad if (argc != 1) 444 1.1 riastrad usage(); 445 1.1 riastrad 446 1.1 riastrad read_key(argv[0], keybuf); 447 1.1 riastrad 448 1.1 riastrad prop_dictionary_t prop_dict; 449 1.1 riastrad prop_dict = prop_dictionary_create(); 450 1.3 riastrad if (prop_dict == NULL) 451 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_create"); 452 1.3 riastrad 453 1.3 riastrad if (!prop_dictionary_set_data(prop_dict, "private_key", 454 1.3 riastrad keybuf, sizeof(keybuf))) 455 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_data"); 456 1.1 riastrad 457 1.1 riastrad char *buf = prop_dictionary_externalize(prop_dict); 458 1.1 riastrad if (buf == NULL) 459 1.1 riastrad err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 460 1.1 riastrad ioctl_set(interface, WG_IOCTL_SET_PRIVATE_KEY, buf); 461 1.1 riastrad 462 1.1 riastrad return EXIT_SUCCESS; 463 1.1 riastrad } 464 1.1 riastrad 465 1.1 riastrad static uint16_t 466 1.1 riastrad strtouint16(const char *str) 467 1.1 riastrad { 468 1.1 riastrad char *ep; 469 1.1 riastrad long val; 470 1.1 riastrad 471 1.1 riastrad errno = 0; 472 1.1 riastrad val = strtol(str, &ep, 10); 473 1.1 riastrad if (ep == str) 474 1.1 riastrad errx(EXIT_FAILURE, "strtol: not a number"); 475 1.1 riastrad if (*ep != '\0') 476 1.1 riastrad errx(EXIT_FAILURE, "strtol: trailing garbage"); 477 1.1 riastrad if (errno != 0) 478 1.1 riastrad err(EXIT_FAILURE, "strtol"); 479 1.1 riastrad if (val < 0 || val > USHRT_MAX) 480 1.1 riastrad errx(EXIT_FAILURE, "out of range"); 481 1.1 riastrad 482 1.1 riastrad return (uint16_t)val; 483 1.1 riastrad } 484 1.1 riastrad 485 1.1 riastrad static int 486 1.1 riastrad cmd_set_listen_port(const char *interface, int argc, char *argv[]) 487 1.1 riastrad { 488 1.1 riastrad uint16_t port; 489 1.1 riastrad 490 1.1 riastrad if (argc != 1) 491 1.1 riastrad usage(); 492 1.1 riastrad 493 1.1 riastrad port = strtouint16(argv[0]); 494 1.1 riastrad if (port == 0) 495 1.1 riastrad errx(EXIT_FAILURE, "port 0 is not allowed"); 496 1.1 riastrad 497 1.1 riastrad prop_dictionary_t prop_dict; 498 1.1 riastrad prop_dict = prop_dictionary_create(); 499 1.3 riastrad if (prop_dict == NULL) 500 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_create"); 501 1.3 riastrad 502 1.3 riastrad if (!prop_dictionary_set_uint16(prop_dict, "listen_port", port)) 503 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); 504 1.1 riastrad 505 1.1 riastrad char *buf = prop_dictionary_externalize(prop_dict); 506 1.1 riastrad if (buf == NULL) 507 1.1 riastrad err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 508 1.1 riastrad ioctl_set(interface, WG_IOCTL_SET_LISTEN_PORT, buf); 509 1.1 riastrad 510 1.1 riastrad return EXIT_SUCCESS; 511 1.1 riastrad } 512 1.1 riastrad 513 1.1 riastrad static void 514 1.1 riastrad handle_option_endpoint(const char *_addr_port, prop_dictionary_t prop_dict) 515 1.1 riastrad { 516 1.1 riastrad int error; 517 1.1 riastrad char *port; 518 1.1 riastrad struct addrinfo hints, *res; 519 1.1 riastrad char *addr_port, *addr; 520 1.1 riastrad 521 1.1 riastrad addr = addr_port = strdup(_addr_port); 522 1.1 riastrad 523 1.1 riastrad if (addr_port[0] == '[') { 524 1.1 riastrad /* [<ipv6>]:<port> */ 525 1.1 riastrad /* Accept [<ipv4>]:<port> too, but it's not a big deal. */ 526 1.1 riastrad char *bracket, *colon; 527 1.1 riastrad if (strlen(addr_port) < strlen("[::]:0")) 528 1.1 riastrad errx(EXIT_FAILURE, "invalid endpoint format"); 529 1.1 riastrad addr = addr_port + 1; 530 1.1 riastrad bracket = strchr(addr, ']'); 531 1.1 riastrad if (bracket == NULL) 532 1.1 riastrad errx(EXIT_FAILURE, "invalid endpoint format"); 533 1.1 riastrad *bracket = '\0'; 534 1.1 riastrad colon = bracket + 1; 535 1.1 riastrad if (*colon != ':') 536 1.1 riastrad errx(EXIT_FAILURE, "invalid endpoint format"); 537 1.1 riastrad *colon = '\0'; 538 1.1 riastrad port = colon + 1; 539 1.1 riastrad } else { 540 1.1 riastrad char *colon, *tmp; 541 1.1 riastrad colon = strchr(addr_port, ':'); 542 1.1 riastrad if (colon == NULL) 543 1.1 riastrad errx(EXIT_FAILURE, "no ':' found in endpoint"); 544 1.1 riastrad tmp = strchr(colon + 1, ':'); 545 1.1 riastrad if (tmp != NULL) { 546 1.1 riastrad /* <ipv6>:<port> */ 547 1.1 riastrad /* Assume the last colon is a separator */ 548 1.1 riastrad char *last_colon = tmp; 549 1.1 riastrad while ((tmp = strchr(tmp + 1, ':')) != NULL) 550 1.1 riastrad last_colon = tmp; 551 1.1 riastrad colon = last_colon; 552 1.1 riastrad *colon = '\0'; 553 1.1 riastrad port = colon + 1; 554 1.1 riastrad } else { 555 1.1 riastrad /* <ipv4>:<port> */ 556 1.1 riastrad *colon = '\0'; 557 1.1 riastrad port = colon + 1; 558 1.1 riastrad } 559 1.1 riastrad } 560 1.1 riastrad 561 1.1 riastrad memset(&hints, 0, sizeof(hints)); 562 1.1 riastrad hints.ai_family = AF_UNSPEC; 563 1.1 riastrad hints.ai_flags = AI_NUMERICHOST; 564 1.1 riastrad error = getaddrinfo(addr, port, &hints, &res); 565 1.3 riastrad if (error) 566 1.3 riastrad errx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(error)); 567 1.1 riastrad 568 1.3 riastrad if (!prop_dictionary_set_data(prop_dict, "endpoint", 569 1.3 riastrad res->ai_addr, res->ai_addrlen)) 570 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_data"); 571 1.1 riastrad 572 1.1 riastrad freeaddrinfo(res); 573 1.1 riastrad free(addr_port); 574 1.1 riastrad } 575 1.1 riastrad 576 1.1 riastrad static void 577 1.1 riastrad handle_option_allowed_ips(const char *_allowed_ips, prop_dictionary_t prop_dict) 578 1.1 riastrad { 579 1.1 riastrad prop_array_t allowedips; 580 1.1 riastrad int i; 581 1.1 riastrad char *allowed_ips, *ip; 582 1.1 riastrad 583 1.1 riastrad allowed_ips = strdup(_allowed_ips); 584 1.3 riastrad if (allowed_ips == NULL) 585 1.3 riastrad errx(EXIT_FAILURE, "strdup"); 586 1.1 riastrad 587 1.1 riastrad allowedips = prop_array_create(); 588 1.3 riastrad if (allowedips == NULL) 589 1.3 riastrad errx(EXIT_FAILURE, "prop_array_create"); 590 1.3 riastrad 591 1.3 riastrad for (i = 0; (ip = strsep(&allowed_ips, ",")) != NULL; i++) { 592 1.1 riastrad prop_dictionary_t prop_allowedip; 593 1.1 riastrad uint16_t cidr; 594 1.1 riastrad char *cidrp; 595 1.1 riastrad struct addrinfo hints, *res; 596 1.1 riastrad int error; 597 1.1 riastrad 598 1.3 riastrad prop_allowedip = prop_dictionary_create(); 599 1.3 riastrad if (prop_allowedip == NULL) 600 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_create"); 601 1.3 riastrad 602 1.1 riastrad cidrp = strchr(ip, '/'); 603 1.1 riastrad if (cidrp == NULL) 604 1.1 riastrad errx(EXIT_FAILURE, "no '/' found in allowed-ip"); 605 1.1 riastrad *cidrp = '\0'; 606 1.1 riastrad cidrp++; 607 1.1 riastrad 608 1.1 riastrad cidr = strtouint16(cidrp); 609 1.1 riastrad 610 1.1 riastrad memset(&hints, 0, sizeof(hints)); 611 1.1 riastrad hints.ai_family = AF_UNSPEC; 612 1.1 riastrad hints.ai_flags = AI_NUMERICHOST; 613 1.1 riastrad error = getaddrinfo(ip, 0, &hints, &res); 614 1.3 riastrad if (error) 615 1.3 riastrad errx(EXIT_FAILURE, "getaddrinfo: %s", 616 1.3 riastrad gai_strerror(errno)); 617 1.1 riastrad 618 1.1 riastrad sa_family_t family = res->ai_addr->sa_family; 619 1.3 riastrad if (!prop_dictionary_set_uint8(prop_allowedip, "family", 620 1.3 riastrad family)) 621 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_uint8"); 622 1.3 riastrad 623 1.3 riastrad const void *addr; 624 1.3 riastrad size_t addrlen; 625 1.3 riastrad switch (family) { 626 1.3 riastrad case AF_INET: { 627 1.3 riastrad const struct sockaddr_in *sin = 628 1.3 riastrad (const struct sockaddr_in *)res->ai_addr; 629 1.3 riastrad addr = &sin->sin_addr; 630 1.3 riastrad addrlen = sizeof(sin->sin_addr); 631 1.3 riastrad break; 632 1.3 riastrad } 633 1.3 riastrad case AF_INET6: { 634 1.3 riastrad const struct sockaddr_in6 *sin6 = 635 1.3 riastrad (const struct sockaddr_in6 *)res->ai_addr; 636 1.3 riastrad addr = &sin6->sin6_addr; 637 1.3 riastrad addrlen = sizeof(sin6->sin6_addr); 638 1.3 riastrad break; 639 1.3 riastrad } 640 1.3 riastrad default: 641 1.1 riastrad errx(EXIT_FAILURE, "invalid family: %d", family); 642 1.3 riastrad } 643 1.3 riastrad if (!prop_dictionary_set_data(prop_allowedip, "ip", 644 1.3 riastrad addr, addrlen)) 645 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_data"); 646 1.3 riastrad if (!prop_dictionary_set_uint16(prop_allowedip, "cidr", cidr)) 647 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); 648 1.1 riastrad 649 1.1 riastrad freeaddrinfo(res); 650 1.1 riastrad prop_array_set(allowedips, i, prop_allowedip); 651 1.1 riastrad } 652 1.1 riastrad prop_dictionary_set(prop_dict, "allowedips", allowedips); 653 1.3 riastrad prop_object_release(allowedips); 654 1.1 riastrad 655 1.1 riastrad free(allowed_ips); 656 1.1 riastrad } 657 1.1 riastrad 658 1.1 riastrad static void 659 1.1 riastrad handle_option_preshared_key(const char *path, prop_dictionary_t prop_dict) 660 1.1 riastrad { 661 1.1 riastrad unsigned char keybuf[KEY_LEN]; 662 1.1 riastrad 663 1.1 riastrad read_key(path, keybuf); 664 1.3 riastrad if (!prop_dictionary_set_data(prop_dict, "preshared_key", 665 1.3 riastrad keybuf, sizeof(keybuf))) 666 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_data"); 667 1.1 riastrad } 668 1.1 riastrad 669 1.1 riastrad static const struct option { 670 1.1 riastrad const char *option; 671 1.1 riastrad void (*func)(const char *, prop_dictionary_t); 672 1.1 riastrad } options[] = { 673 1.1 riastrad {"--endpoint=", handle_option_endpoint}, 674 1.1 riastrad {"--allowed-ips=", handle_option_allowed_ips}, 675 1.1 riastrad {"--preshared-key=", handle_option_preshared_key}, 676 1.1 riastrad }; 677 1.1 riastrad 678 1.1 riastrad static void 679 1.1 riastrad handle_options(int argc, char *argv[], prop_dictionary_t prop_dict) 680 1.1 riastrad { 681 1.1 riastrad 682 1.1 riastrad while (argc > 0) { 683 1.6 oster int found = 0; 684 1.1 riastrad for (size_t i = 0; i < __arraycount(options); i++) { 685 1.1 riastrad const struct option *opt = &options[i]; 686 1.1 riastrad size_t optlen = strlen(opt->option); 687 1.1 riastrad if (strncmp(argv[0], opt->option, optlen) == 0) { 688 1.1 riastrad opt->func(argv[0] + optlen, prop_dict); 689 1.6 oster found = 1; 690 1.1 riastrad break; 691 1.1 riastrad } 692 1.1 riastrad } 693 1.6 oster if (found == 0) 694 1.6 oster errx(EXIT_FAILURE, "invalid option: %s", argv[0]); 695 1.1 riastrad argc -= 1; 696 1.1 riastrad argv += 1; 697 1.1 riastrad } 698 1.1 riastrad 699 1.1 riastrad if (argc != 0) 700 1.1 riastrad usage(); 701 1.1 riastrad } 702 1.1 riastrad 703 1.1 riastrad static int 704 1.1 riastrad cmd_add_peer(const char *interface, int argc, char *argv[]) 705 1.1 riastrad { 706 1.1 riastrad const char *name; 707 1.1 riastrad unsigned char keybuf[KEY_LEN]; 708 1.1 riastrad 709 1.1 riastrad if (argc < 2) 710 1.1 riastrad usage(); 711 1.1 riastrad 712 1.1 riastrad prop_dictionary_t prop_dict; 713 1.1 riastrad prop_dict = prop_dictionary_create(); 714 1.3 riastrad if (prop_dict == NULL) 715 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_create"); 716 1.1 riastrad 717 1.1 riastrad name = argv[0]; 718 1.1 riastrad if (strlen(name) > WG_PEER_NAME_MAXLEN) 719 1.1 riastrad errx(EXIT_FAILURE, "peer name too long"); 720 1.1 riastrad if (strnlen(argv[1], KEY_BASE64_LEN + 1) != KEY_BASE64_LEN) 721 1.3 riastrad errx(EXIT_FAILURE, "invalid public-key length: %zu", 722 1.3 riastrad strlen(argv[1])); 723 1.1 riastrad base64_decode(argv[1], keybuf); 724 1.1 riastrad 725 1.3 riastrad if (!prop_dictionary_set_string(prop_dict, "name", name)) 726 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_string"); 727 1.3 riastrad if (!prop_dictionary_set_data(prop_dict, "public_key", 728 1.3 riastrad keybuf, sizeof(keybuf))) 729 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_data"); 730 1.1 riastrad 731 1.1 riastrad argc -= 2; 732 1.1 riastrad argv += 2; 733 1.1 riastrad 734 1.1 riastrad handle_options(argc, argv, prop_dict); 735 1.1 riastrad 736 1.1 riastrad char *buf = prop_dictionary_externalize(prop_dict); 737 1.1 riastrad if (buf == NULL) 738 1.1 riastrad err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 739 1.1 riastrad ioctl_set(interface, WG_IOCTL_ADD_PEER, buf); 740 1.1 riastrad 741 1.1 riastrad return EXIT_SUCCESS; 742 1.1 riastrad } 743 1.1 riastrad 744 1.1 riastrad static int 745 1.1 riastrad cmd_delete_peer(const char *interface, int argc, char *argv[]) 746 1.1 riastrad { 747 1.1 riastrad const char *name; 748 1.1 riastrad 749 1.1 riastrad if (argc != 1) 750 1.1 riastrad usage(); 751 1.1 riastrad 752 1.1 riastrad prop_dictionary_t prop_dict; 753 1.1 riastrad prop_dict = prop_dictionary_create(); 754 1.3 riastrad if (prop_dict == NULL) 755 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_create"); 756 1.1 riastrad 757 1.1 riastrad name = argv[0]; 758 1.1 riastrad if (strlen(name) > WG_PEER_NAME_MAXLEN) 759 1.1 riastrad errx(EXIT_FAILURE, "peer name too long"); 760 1.1 riastrad 761 1.3 riastrad if (!prop_dictionary_set_string(prop_dict, "name", name)) 762 1.3 riastrad errx(EXIT_FAILURE, "prop_dictionary_set_string"); 763 1.1 riastrad 764 1.1 riastrad char *buf = prop_dictionary_externalize(prop_dict); 765 1.1 riastrad if (buf == NULL) 766 1.1 riastrad err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 767 1.1 riastrad ioctl_set(interface, WG_IOCTL_DELETE_PEER, buf); 768 1.1 riastrad 769 1.1 riastrad return EXIT_SUCCESS; 770 1.1 riastrad } 771 1.1 riastrad 772 1.1 riastrad static const struct command { 773 1.1 riastrad const char *command; 774 1.1 riastrad const char *target; 775 1.1 riastrad int (*func)(const char *, int, char **); 776 1.1 riastrad } commands[] = { 777 1.1 riastrad {"show", "all", cmd_show_all}, 778 1.1 riastrad {"show", "peer", cmd_show_peer}, 779 1.1 riastrad {"show", "private-key", cmd_show_private_key}, 780 1.1 riastrad {"set", "private-key", cmd_set_private_key}, 781 1.1 riastrad {"set", "listen-port", cmd_set_listen_port}, 782 1.1 riastrad {"add", "peer", cmd_add_peer}, 783 1.1 riastrad {"delete", "peer", cmd_delete_peer}, 784 1.1 riastrad }; 785 1.1 riastrad 786 1.1 riastrad int 787 1.1 riastrad main(int argc, char *argv[]) 788 1.1 riastrad { 789 1.1 riastrad const char *interface; 790 1.1 riastrad const char *command; 791 1.1 riastrad const char *target; 792 1.1 riastrad 793 1.2 riastrad if (argc < 2 || 794 1.2 riastrad strcmp(argv[1], "-h") == 0 || 795 1.2 riastrad strcmp(argv[1], "-?") == 0 || 796 1.2 riastrad strcmp(argv[1], "--help") == 0) { 797 1.1 riastrad usage(); 798 1.1 riastrad } 799 1.1 riastrad 800 1.1 riastrad interface = argv[1]; 801 1.1 riastrad if (strlen(interface) > IFNAMSIZ) 802 1.1 riastrad errx(EXIT_FAILURE, "interface name too long"); 803 1.1 riastrad if (argc == 2) { 804 1.1 riastrad return cmd_show_all(interface, 0, NULL); 805 1.1 riastrad } 806 1.1 riastrad if (argc < 4) { 807 1.1 riastrad usage(); 808 1.1 riastrad } 809 1.1 riastrad command = argv[2]; 810 1.1 riastrad target = argv[3]; 811 1.1 riastrad 812 1.1 riastrad argc -= 4; 813 1.1 riastrad argv += 4; 814 1.1 riastrad 815 1.1 riastrad for (size_t i = 0; i < __arraycount(commands); i++) { 816 1.1 riastrad const struct command *cmd = &commands[i]; 817 1.1 riastrad if (strncmp(command, cmd->command, strlen(cmd->command)) == 0 && 818 1.1 riastrad strncmp(target, cmd->target, strlen(cmd->target)) == 0) { 819 1.1 riastrad return cmd->func(interface, argc, argv); 820 1.1 riastrad } 821 1.1 riastrad } 822 1.1 riastrad 823 1.1 riastrad usage(); 824 1.1 riastrad } 825