1*adf37648SKyle Evans // SPDX-License-Identifier: GPL-2.0 OR MIT
2*adf37648SKyle Evans /*
3*adf37648SKyle Evans * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4*adf37648SKyle Evans */
5*adf37648SKyle Evans
6*adf37648SKyle Evans #include <arpa/inet.h>
7*adf37648SKyle Evans #include <limits.h>
8*adf37648SKyle Evans #include <netdb.h>
9*adf37648SKyle Evans #include <stdio.h>
10*adf37648SKyle Evans #include <stdlib.h>
11*adf37648SKyle Evans #include <string.h>
12*adf37648SKyle Evans #include <unistd.h>
13*adf37648SKyle Evans #include <sys/socket.h>
14*adf37648SKyle Evans #include <sys/stat.h>
15*adf37648SKyle Evans #include <errno.h>
16*adf37648SKyle Evans
17*adf37648SKyle Evans #include "config.h"
18*adf37648SKyle Evans #include "containers.h"
19*adf37648SKyle Evans #include "ipc.h"
20*adf37648SKyle Evans #include "encoding.h"
21*adf37648SKyle Evans #include "ctype.h"
22*adf37648SKyle Evans
23*adf37648SKyle Evans #define COMMENT_CHAR '#'
24*adf37648SKyle Evans
get_value(const char * line,const char * key)25*adf37648SKyle Evans static const char *get_value(const char *line, const char *key)
26*adf37648SKyle Evans {
27*adf37648SKyle Evans size_t linelen = strlen(line);
28*adf37648SKyle Evans size_t keylen = strlen(key);
29*adf37648SKyle Evans
30*adf37648SKyle Evans if (keylen >= linelen)
31*adf37648SKyle Evans return NULL;
32*adf37648SKyle Evans
33*adf37648SKyle Evans if (strncasecmp(line, key, keylen))
34*adf37648SKyle Evans return NULL;
35*adf37648SKyle Evans
36*adf37648SKyle Evans return line + keylen;
37*adf37648SKyle Evans }
38*adf37648SKyle Evans
parse_port(uint16_t * port,uint32_t * flags,const char * value)39*adf37648SKyle Evans static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
40*adf37648SKyle Evans {
41*adf37648SKyle Evans int ret;
42*adf37648SKyle Evans struct addrinfo *resolved;
43*adf37648SKyle Evans struct addrinfo hints = {
44*adf37648SKyle Evans .ai_family = AF_UNSPEC,
45*adf37648SKyle Evans .ai_socktype = SOCK_DGRAM,
46*adf37648SKyle Evans .ai_protocol = IPPROTO_UDP,
47*adf37648SKyle Evans .ai_flags = AI_PASSIVE
48*adf37648SKyle Evans };
49*adf37648SKyle Evans
50*adf37648SKyle Evans if (!strlen(value)) {
51*adf37648SKyle Evans fprintf(stderr, "Unable to parse empty port\n");
52*adf37648SKyle Evans return false;
53*adf37648SKyle Evans }
54*adf37648SKyle Evans
55*adf37648SKyle Evans ret = getaddrinfo(NULL, value, &hints, &resolved);
56*adf37648SKyle Evans if (ret) {
57*adf37648SKyle Evans fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
58*adf37648SKyle Evans return false;
59*adf37648SKyle Evans }
60*adf37648SKyle Evans
61*adf37648SKyle Evans ret = -1;
62*adf37648SKyle Evans if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
63*adf37648SKyle Evans *port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
64*adf37648SKyle Evans ret = 0;
65*adf37648SKyle Evans } else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
66*adf37648SKyle Evans *port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
67*adf37648SKyle Evans ret = 0;
68*adf37648SKyle Evans } else
69*adf37648SKyle Evans fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
70*adf37648SKyle Evans
71*adf37648SKyle Evans freeaddrinfo(resolved);
72*adf37648SKyle Evans if (!ret)
73*adf37648SKyle Evans *flags |= WGDEVICE_HAS_LISTEN_PORT;
74*adf37648SKyle Evans return ret == 0;
75*adf37648SKyle Evans }
76*adf37648SKyle Evans
parse_fwmark(uint32_t * fwmark,uint32_t * flags,const char * value)77*adf37648SKyle Evans static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
78*adf37648SKyle Evans {
79*adf37648SKyle Evans unsigned long ret;
80*adf37648SKyle Evans char *end;
81*adf37648SKyle Evans int base = 10;
82*adf37648SKyle Evans
83*adf37648SKyle Evans if (!strcasecmp(value, "off")) {
84*adf37648SKyle Evans *fwmark = 0;
85*adf37648SKyle Evans *flags |= WGDEVICE_HAS_FWMARK;
86*adf37648SKyle Evans return true;
87*adf37648SKyle Evans }
88*adf37648SKyle Evans
89*adf37648SKyle Evans if (!char_is_digit(value[0]))
90*adf37648SKyle Evans goto err;
91*adf37648SKyle Evans
92*adf37648SKyle Evans if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
93*adf37648SKyle Evans base = 16;
94*adf37648SKyle Evans
95*adf37648SKyle Evans ret = strtoul(value, &end, base);
96*adf37648SKyle Evans if (*end || ret > UINT32_MAX)
97*adf37648SKyle Evans goto err;
98*adf37648SKyle Evans
99*adf37648SKyle Evans *fwmark = ret;
100*adf37648SKyle Evans *flags |= WGDEVICE_HAS_FWMARK;
101*adf37648SKyle Evans return true;
102*adf37648SKyle Evans err:
103*adf37648SKyle Evans fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
104*adf37648SKyle Evans return false;
105*adf37648SKyle Evans }
106*adf37648SKyle Evans
parse_key(uint8_t key[static WG_KEY_LEN],const char * value)107*adf37648SKyle Evans static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
108*adf37648SKyle Evans {
109*adf37648SKyle Evans if (!key_from_base64(key, value)) {
110*adf37648SKyle Evans fprintf(stderr, "Key is not the correct length or format: `%s'\n", value);
111*adf37648SKyle Evans memset(key, 0, WG_KEY_LEN);
112*adf37648SKyle Evans return false;
113*adf37648SKyle Evans }
114*adf37648SKyle Evans return true;
115*adf37648SKyle Evans }
116*adf37648SKyle Evans
parse_keyfile(uint8_t key[static WG_KEY_LEN],const char * path)117*adf37648SKyle Evans static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
118*adf37648SKyle Evans {
119*adf37648SKyle Evans FILE *f;
120*adf37648SKyle Evans int c;
121*adf37648SKyle Evans char dst[WG_KEY_LEN_BASE64];
122*adf37648SKyle Evans bool ret = false;
123*adf37648SKyle Evans
124*adf37648SKyle Evans f = fopen(path, "r");
125*adf37648SKyle Evans if (!f) {
126*adf37648SKyle Evans perror("fopen");
127*adf37648SKyle Evans return false;
128*adf37648SKyle Evans }
129*adf37648SKyle Evans
130*adf37648SKyle Evans if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
131*adf37648SKyle Evans /* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */
132*adf37648SKyle Evans if (!ferror(f) && feof(f) && !ftell(f)) {
133*adf37648SKyle Evans memset(key, 0, WG_KEY_LEN);
134*adf37648SKyle Evans ret = true;
135*adf37648SKyle Evans goto out;
136*adf37648SKyle Evans }
137*adf37648SKyle Evans
138*adf37648SKyle Evans fprintf(stderr, "Invalid length key in key file\n");
139*adf37648SKyle Evans goto out;
140*adf37648SKyle Evans }
141*adf37648SKyle Evans dst[WG_KEY_LEN_BASE64 - 1] = '\0';
142*adf37648SKyle Evans
143*adf37648SKyle Evans while ((c = getc(f)) != EOF) {
144*adf37648SKyle Evans if (!char_is_space(c)) {
145*adf37648SKyle Evans fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
146*adf37648SKyle Evans goto out;
147*adf37648SKyle Evans }
148*adf37648SKyle Evans }
149*adf37648SKyle Evans if (ferror(f) && errno) {
150*adf37648SKyle Evans perror("getc");
151*adf37648SKyle Evans goto out;
152*adf37648SKyle Evans }
153*adf37648SKyle Evans ret = parse_key(key, dst);
154*adf37648SKyle Evans
155*adf37648SKyle Evans out:
156*adf37648SKyle Evans fclose(f);
157*adf37648SKyle Evans return ret;
158*adf37648SKyle Evans }
159*adf37648SKyle Evans
parse_ip(struct wgallowedip * allowedip,const char * value)160*adf37648SKyle Evans static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
161*adf37648SKyle Evans {
162*adf37648SKyle Evans allowedip->family = AF_UNSPEC;
163*adf37648SKyle Evans if (strchr(value, ':')) {
164*adf37648SKyle Evans if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
165*adf37648SKyle Evans allowedip->family = AF_INET6;
166*adf37648SKyle Evans } else {
167*adf37648SKyle Evans if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
168*adf37648SKyle Evans allowedip->family = AF_INET;
169*adf37648SKyle Evans }
170*adf37648SKyle Evans if (allowedip->family == AF_UNSPEC) {
171*adf37648SKyle Evans fprintf(stderr, "Unable to parse IP address: `%s'\n", value);
172*adf37648SKyle Evans return false;
173*adf37648SKyle Evans }
174*adf37648SKyle Evans return true;
175*adf37648SKyle Evans }
176*adf37648SKyle Evans
parse_dns_retries(void)177*adf37648SKyle Evans static inline int parse_dns_retries(void)
178*adf37648SKyle Evans {
179*adf37648SKyle Evans unsigned long ret;
180*adf37648SKyle Evans char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
181*adf37648SKyle Evans
182*adf37648SKyle Evans if (!retries)
183*adf37648SKyle Evans return 15;
184*adf37648SKyle Evans if (!strcmp(retries, "infinity"))
185*adf37648SKyle Evans return -1;
186*adf37648SKyle Evans
187*adf37648SKyle Evans ret = strtoul(retries, &end, 10);
188*adf37648SKyle Evans if (*end || ret > INT_MAX) {
189*adf37648SKyle Evans fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
190*adf37648SKyle Evans exit(1);
191*adf37648SKyle Evans }
192*adf37648SKyle Evans return (int)ret;
193*adf37648SKyle Evans }
194*adf37648SKyle Evans
parse_endpoint(struct sockaddr * endpoint,const char * value)195*adf37648SKyle Evans static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
196*adf37648SKyle Evans {
197*adf37648SKyle Evans char *mutable = strdup(value);
198*adf37648SKyle Evans char *begin, *end;
199*adf37648SKyle Evans int ret, retries = parse_dns_retries();
200*adf37648SKyle Evans struct addrinfo *resolved;
201*adf37648SKyle Evans struct addrinfo hints = {
202*adf37648SKyle Evans .ai_family = AF_UNSPEC,
203*adf37648SKyle Evans .ai_socktype = SOCK_DGRAM,
204*adf37648SKyle Evans .ai_protocol = IPPROTO_UDP
205*adf37648SKyle Evans };
206*adf37648SKyle Evans if (!mutable) {
207*adf37648SKyle Evans perror("strdup");
208*adf37648SKyle Evans return false;
209*adf37648SKyle Evans }
210*adf37648SKyle Evans if (!strlen(value)) {
211*adf37648SKyle Evans free(mutable);
212*adf37648SKyle Evans fprintf(stderr, "Unable to parse empty endpoint\n");
213*adf37648SKyle Evans return false;
214*adf37648SKyle Evans }
215*adf37648SKyle Evans if (mutable[0] == '[') {
216*adf37648SKyle Evans begin = &mutable[1];
217*adf37648SKyle Evans end = strchr(mutable, ']');
218*adf37648SKyle Evans if (!end) {
219*adf37648SKyle Evans free(mutable);
220*adf37648SKyle Evans fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value);
221*adf37648SKyle Evans return false;
222*adf37648SKyle Evans }
223*adf37648SKyle Evans *end++ = '\0';
224*adf37648SKyle Evans if (*end++ != ':' || !*end) {
225*adf37648SKyle Evans free(mutable);
226*adf37648SKyle Evans fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
227*adf37648SKyle Evans return false;
228*adf37648SKyle Evans }
229*adf37648SKyle Evans } else {
230*adf37648SKyle Evans begin = mutable;
231*adf37648SKyle Evans end = strrchr(mutable, ':');
232*adf37648SKyle Evans if (!end || !*(end + 1)) {
233*adf37648SKyle Evans free(mutable);
234*adf37648SKyle Evans fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
235*adf37648SKyle Evans return false;
236*adf37648SKyle Evans }
237*adf37648SKyle Evans *end++ = '\0';
238*adf37648SKyle Evans }
239*adf37648SKyle Evans
240*adf37648SKyle Evans #define min(a, b) ((a) < (b) ? (a) : (b))
241*adf37648SKyle Evans for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
242*adf37648SKyle Evans ret = getaddrinfo(begin, end, &hints, &resolved);
243*adf37648SKyle Evans if (!ret)
244*adf37648SKyle Evans break;
245*adf37648SKyle Evans /* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
246*adf37648SKyle Evans *
247*adf37648SKyle Evans * This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
248*adf37648SKyle Evans * "From the perspective of the application that calls getaddrinfo() it perhaps
249*adf37648SKyle Evans * doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
250*adf37648SKyle Evans * permanent failure codes and the causes are all permanent failures in the
251*adf37648SKyle Evans * sense that there is no point in retrying later."
252*adf37648SKyle Evans *
253*adf37648SKyle Evans * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
254*adf37648SKyle Evans */
255*adf37648SKyle Evans if (ret == EAI_NONAME || ret == EAI_FAIL ||
256*adf37648SKyle Evans #ifdef EAI_NODATA
257*adf37648SKyle Evans ret == EAI_NODATA ||
258*adf37648SKyle Evans #endif
259*adf37648SKyle Evans (retries >= 0 && !retries--)) {
260*adf37648SKyle Evans free(mutable);
261*adf37648SKyle Evans fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
262*adf37648SKyle Evans return false;
263*adf37648SKyle Evans }
264*adf37648SKyle Evans fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);
265*adf37648SKyle Evans usleep(timeout);
266*adf37648SKyle Evans }
267*adf37648SKyle Evans
268*adf37648SKyle Evans if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
269*adf37648SKyle Evans (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
270*adf37648SKyle Evans memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
271*adf37648SKyle Evans else {
272*adf37648SKyle Evans freeaddrinfo(resolved);
273*adf37648SKyle Evans free(mutable);
274*adf37648SKyle Evans fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
275*adf37648SKyle Evans return false;
276*adf37648SKyle Evans }
277*adf37648SKyle Evans freeaddrinfo(resolved);
278*adf37648SKyle Evans free(mutable);
279*adf37648SKyle Evans return true;
280*adf37648SKyle Evans }
281*adf37648SKyle Evans
parse_persistent_keepalive(uint16_t * interval,uint32_t * flags,const char * value)282*adf37648SKyle Evans static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
283*adf37648SKyle Evans {
284*adf37648SKyle Evans unsigned long ret;
285*adf37648SKyle Evans char *end;
286*adf37648SKyle Evans
287*adf37648SKyle Evans if (!strcasecmp(value, "off")) {
288*adf37648SKyle Evans *interval = 0;
289*adf37648SKyle Evans *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
290*adf37648SKyle Evans return true;
291*adf37648SKyle Evans }
292*adf37648SKyle Evans
293*adf37648SKyle Evans if (!char_is_digit(value[0]))
294*adf37648SKyle Evans goto err;
295*adf37648SKyle Evans
296*adf37648SKyle Evans ret = strtoul(value, &end, 10);
297*adf37648SKyle Evans if (*end || ret > 65535)
298*adf37648SKyle Evans goto err;
299*adf37648SKyle Evans
300*adf37648SKyle Evans *interval = (uint16_t)ret;
301*adf37648SKyle Evans *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
302*adf37648SKyle Evans return true;
303*adf37648SKyle Evans err:
304*adf37648SKyle Evans fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
305*adf37648SKyle Evans return false;
306*adf37648SKyle Evans }
307*adf37648SKyle Evans
validate_netmask(struct wgallowedip * allowedip)308*adf37648SKyle Evans static bool validate_netmask(struct wgallowedip *allowedip)
309*adf37648SKyle Evans {
310*adf37648SKyle Evans uint32_t *ip;
311*adf37648SKyle Evans int last;
312*adf37648SKyle Evans
313*adf37648SKyle Evans switch (allowedip->family) {
314*adf37648SKyle Evans case AF_INET:
315*adf37648SKyle Evans last = 0;
316*adf37648SKyle Evans ip = (uint32_t *)&allowedip->ip4;
317*adf37648SKyle Evans break;
318*adf37648SKyle Evans case AF_INET6:
319*adf37648SKyle Evans last = 3;
320*adf37648SKyle Evans ip = (uint32_t *)&allowedip->ip6;
321*adf37648SKyle Evans break;
322*adf37648SKyle Evans default:
323*adf37648SKyle Evans return true; /* We don't know how to validate it, so say 'okay'. */
324*adf37648SKyle Evans }
325*adf37648SKyle Evans
326*adf37648SKyle Evans for (int i = last; i >= 0; --i) {
327*adf37648SKyle Evans uint32_t mask = ~0;
328*adf37648SKyle Evans
329*adf37648SKyle Evans if (allowedip->cidr >= 32 * (i + 1))
330*adf37648SKyle Evans break;
331*adf37648SKyle Evans if (allowedip->cidr > 32 * i)
332*adf37648SKyle Evans mask >>= (allowedip->cidr - 32 * i);
333*adf37648SKyle Evans if (ntohl(ip[i]) & mask)
334*adf37648SKyle Evans return false;
335*adf37648SKyle Evans }
336*adf37648SKyle Evans
337*adf37648SKyle Evans return true;
338*adf37648SKyle Evans }
339*adf37648SKyle Evans
parse_allowedips(struct wgpeer * peer,struct wgallowedip ** last_allowedip,const char * value)340*adf37648SKyle Evans static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
341*adf37648SKyle Evans {
342*adf37648SKyle Evans struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
343*adf37648SKyle Evans char *mask, *mutable = strdup(value), *sep, *saved_entry;
344*adf37648SKyle Evans
345*adf37648SKyle Evans if (!mutable) {
346*adf37648SKyle Evans perror("strdup");
347*adf37648SKyle Evans return false;
348*adf37648SKyle Evans }
349*adf37648SKyle Evans peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
350*adf37648SKyle Evans if (!strlen(value)) {
351*adf37648SKyle Evans free(mutable);
352*adf37648SKyle Evans return true;
353*adf37648SKyle Evans }
354*adf37648SKyle Evans sep = mutable;
355*adf37648SKyle Evans while ((mask = strsep(&sep, ","))) {
356*adf37648SKyle Evans unsigned long cidr;
357*adf37648SKyle Evans char *end, *ip;
358*adf37648SKyle Evans
359*adf37648SKyle Evans saved_entry = strdup(mask);
360*adf37648SKyle Evans ip = strsep(&mask, "/");
361*adf37648SKyle Evans
362*adf37648SKyle Evans new_allowedip = calloc(1, sizeof(*new_allowedip));
363*adf37648SKyle Evans if (!new_allowedip) {
364*adf37648SKyle Evans perror("calloc");
365*adf37648SKyle Evans free(saved_entry);
366*adf37648SKyle Evans free(mutable);
367*adf37648SKyle Evans return false;
368*adf37648SKyle Evans }
369*adf37648SKyle Evans
370*adf37648SKyle Evans if (!parse_ip(new_allowedip, ip)) {
371*adf37648SKyle Evans free(new_allowedip);
372*adf37648SKyle Evans free(saved_entry);
373*adf37648SKyle Evans free(mutable);
374*adf37648SKyle Evans return false;
375*adf37648SKyle Evans }
376*adf37648SKyle Evans
377*adf37648SKyle Evans if (mask) {
378*adf37648SKyle Evans if (!char_is_digit(mask[0]))
379*adf37648SKyle Evans goto err;
380*adf37648SKyle Evans cidr = strtoul(mask, &end, 10);
381*adf37648SKyle Evans if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
382*adf37648SKyle Evans goto err;
383*adf37648SKyle Evans } else if (new_allowedip->family == AF_INET)
384*adf37648SKyle Evans cidr = 32;
385*adf37648SKyle Evans else if (new_allowedip->family == AF_INET6)
386*adf37648SKyle Evans cidr = 128;
387*adf37648SKyle Evans else
388*adf37648SKyle Evans goto err;
389*adf37648SKyle Evans new_allowedip->cidr = cidr;
390*adf37648SKyle Evans
391*adf37648SKyle Evans if (!validate_netmask(new_allowedip))
392*adf37648SKyle Evans fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
393*adf37648SKyle Evans
394*adf37648SKyle Evans if (allowedip)
395*adf37648SKyle Evans allowedip->next_allowedip = new_allowedip;
396*adf37648SKyle Evans else
397*adf37648SKyle Evans peer->first_allowedip = new_allowedip;
398*adf37648SKyle Evans allowedip = new_allowedip;
399*adf37648SKyle Evans free(saved_entry);
400*adf37648SKyle Evans }
401*adf37648SKyle Evans free(mutable);
402*adf37648SKyle Evans *last_allowedip = allowedip;
403*adf37648SKyle Evans return true;
404*adf37648SKyle Evans
405*adf37648SKyle Evans err:
406*adf37648SKyle Evans free(new_allowedip);
407*adf37648SKyle Evans free(mutable);
408*adf37648SKyle Evans fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
409*adf37648SKyle Evans free(saved_entry);
410*adf37648SKyle Evans return false;
411*adf37648SKyle Evans }
412*adf37648SKyle Evans
process_line(struct config_ctx * ctx,const char * line)413*adf37648SKyle Evans static bool process_line(struct config_ctx *ctx, const char *line)
414*adf37648SKyle Evans {
415*adf37648SKyle Evans const char *value;
416*adf37648SKyle Evans bool ret = true;
417*adf37648SKyle Evans
418*adf37648SKyle Evans if (!strcasecmp(line, "[Interface]")) {
419*adf37648SKyle Evans ctx->is_peer_section = false;
420*adf37648SKyle Evans ctx->is_device_section = true;
421*adf37648SKyle Evans return true;
422*adf37648SKyle Evans }
423*adf37648SKyle Evans if (!strcasecmp(line, "[Peer]")) {
424*adf37648SKyle Evans struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
425*adf37648SKyle Evans
426*adf37648SKyle Evans if (!new_peer) {
427*adf37648SKyle Evans perror("calloc");
428*adf37648SKyle Evans return false;
429*adf37648SKyle Evans }
430*adf37648SKyle Evans ctx->last_allowedip = NULL;
431*adf37648SKyle Evans if (ctx->last_peer)
432*adf37648SKyle Evans ctx->last_peer->next_peer = new_peer;
433*adf37648SKyle Evans else
434*adf37648SKyle Evans ctx->device->first_peer = new_peer;
435*adf37648SKyle Evans ctx->last_peer = new_peer;
436*adf37648SKyle Evans ctx->is_peer_section = true;
437*adf37648SKyle Evans ctx->is_device_section = false;
438*adf37648SKyle Evans ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
439*adf37648SKyle Evans return true;
440*adf37648SKyle Evans }
441*adf37648SKyle Evans
442*adf37648SKyle Evans #define key_match(key) (value = get_value(line, key "="))
443*adf37648SKyle Evans
444*adf37648SKyle Evans if (ctx->is_device_section) {
445*adf37648SKyle Evans if (key_match("ListenPort"))
446*adf37648SKyle Evans ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
447*adf37648SKyle Evans else if (key_match("FwMark"))
448*adf37648SKyle Evans ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
449*adf37648SKyle Evans else if (key_match("PrivateKey")) {
450*adf37648SKyle Evans ret = parse_key(ctx->device->private_key, value);
451*adf37648SKyle Evans if (ret)
452*adf37648SKyle Evans ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
453*adf37648SKyle Evans } else
454*adf37648SKyle Evans goto error;
455*adf37648SKyle Evans } else if (ctx->is_peer_section) {
456*adf37648SKyle Evans if (key_match("Endpoint"))
457*adf37648SKyle Evans ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
458*adf37648SKyle Evans else if (key_match("PublicKey")) {
459*adf37648SKyle Evans ret = parse_key(ctx->last_peer->public_key, value);
460*adf37648SKyle Evans if (ret)
461*adf37648SKyle Evans ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
462*adf37648SKyle Evans } else if (key_match("AllowedIPs"))
463*adf37648SKyle Evans ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
464*adf37648SKyle Evans else if (key_match("PersistentKeepalive"))
465*adf37648SKyle Evans ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
466*adf37648SKyle Evans else if (key_match("PresharedKey")) {
467*adf37648SKyle Evans ret = parse_key(ctx->last_peer->preshared_key, value);
468*adf37648SKyle Evans if (ret)
469*adf37648SKyle Evans ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
470*adf37648SKyle Evans } else
471*adf37648SKyle Evans goto error;
472*adf37648SKyle Evans } else
473*adf37648SKyle Evans goto error;
474*adf37648SKyle Evans return ret;
475*adf37648SKyle Evans
476*adf37648SKyle Evans #undef key_match
477*adf37648SKyle Evans
478*adf37648SKyle Evans error:
479*adf37648SKyle Evans fprintf(stderr, "Line unrecognized: `%s'\n", line);
480*adf37648SKyle Evans return false;
481*adf37648SKyle Evans }
482*adf37648SKyle Evans
config_read_line(struct config_ctx * ctx,const char * input)483*adf37648SKyle Evans bool config_read_line(struct config_ctx *ctx, const char *input)
484*adf37648SKyle Evans {
485*adf37648SKyle Evans size_t len, cleaned_len = 0;
486*adf37648SKyle Evans char *line, *comment;
487*adf37648SKyle Evans bool ret = true;
488*adf37648SKyle Evans
489*adf37648SKyle Evans /* This is what strchrnul is for, but that isn't portable. */
490*adf37648SKyle Evans comment = strchr(input, COMMENT_CHAR);
491*adf37648SKyle Evans if (comment)
492*adf37648SKyle Evans len = comment - input;
493*adf37648SKyle Evans else
494*adf37648SKyle Evans len = strlen(input);
495*adf37648SKyle Evans
496*adf37648SKyle Evans line = calloc(len + 1, sizeof(char));
497*adf37648SKyle Evans if (!line) {
498*adf37648SKyle Evans perror("calloc");
499*adf37648SKyle Evans ret = false;
500*adf37648SKyle Evans goto out;
501*adf37648SKyle Evans }
502*adf37648SKyle Evans
503*adf37648SKyle Evans for (size_t i = 0; i < len; ++i) {
504*adf37648SKyle Evans if (!char_is_space(input[i]))
505*adf37648SKyle Evans line[cleaned_len++] = input[i];
506*adf37648SKyle Evans }
507*adf37648SKyle Evans if (!cleaned_len)
508*adf37648SKyle Evans goto out;
509*adf37648SKyle Evans ret = process_line(ctx, line);
510*adf37648SKyle Evans out:
511*adf37648SKyle Evans free(line);
512*adf37648SKyle Evans if (!ret)
513*adf37648SKyle Evans free_wgdevice(ctx->device);
514*adf37648SKyle Evans return ret;
515*adf37648SKyle Evans }
516*adf37648SKyle Evans
config_read_init(struct config_ctx * ctx,bool append)517*adf37648SKyle Evans bool config_read_init(struct config_ctx *ctx, bool append)
518*adf37648SKyle Evans {
519*adf37648SKyle Evans memset(ctx, 0, sizeof(*ctx));
520*adf37648SKyle Evans ctx->device = calloc(1, sizeof(*ctx->device));
521*adf37648SKyle Evans if (!ctx->device) {
522*adf37648SKyle Evans perror("calloc");
523*adf37648SKyle Evans return false;
524*adf37648SKyle Evans }
525*adf37648SKyle Evans if (!append)
526*adf37648SKyle Evans ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
527*adf37648SKyle Evans return true;
528*adf37648SKyle Evans }
529*adf37648SKyle Evans
config_read_finish(struct config_ctx * ctx)530*adf37648SKyle Evans struct wgdevice *config_read_finish(struct config_ctx *ctx)
531*adf37648SKyle Evans {
532*adf37648SKyle Evans struct wgpeer *peer;
533*adf37648SKyle Evans
534*adf37648SKyle Evans for_each_wgpeer(ctx->device, peer) {
535*adf37648SKyle Evans if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
536*adf37648SKyle Evans fprintf(stderr, "A peer is missing a public key\n");
537*adf37648SKyle Evans goto err;
538*adf37648SKyle Evans }
539*adf37648SKyle Evans }
540*adf37648SKyle Evans return ctx->device;
541*adf37648SKyle Evans err:
542*adf37648SKyle Evans free_wgdevice(ctx->device);
543*adf37648SKyle Evans return NULL;
544*adf37648SKyle Evans }
545*adf37648SKyle Evans
strip_spaces(const char * in)546*adf37648SKyle Evans static char *strip_spaces(const char *in)
547*adf37648SKyle Evans {
548*adf37648SKyle Evans char *out;
549*adf37648SKyle Evans size_t t, l, i;
550*adf37648SKyle Evans
551*adf37648SKyle Evans t = strlen(in);
552*adf37648SKyle Evans out = calloc(t + 1, sizeof(char));
553*adf37648SKyle Evans if (!out) {
554*adf37648SKyle Evans perror("calloc");
555*adf37648SKyle Evans return NULL;
556*adf37648SKyle Evans }
557*adf37648SKyle Evans for (i = 0, l = 0; i < t; ++i) {
558*adf37648SKyle Evans if (!char_is_space(in[i]))
559*adf37648SKyle Evans out[l++] = in[i];
560*adf37648SKyle Evans }
561*adf37648SKyle Evans return out;
562*adf37648SKyle Evans }
563*adf37648SKyle Evans
config_read_cmd(const char * argv[],int argc)564*adf37648SKyle Evans struct wgdevice *config_read_cmd(const char *argv[], int argc)
565*adf37648SKyle Evans {
566*adf37648SKyle Evans struct wgdevice *device = calloc(1, sizeof(*device));
567*adf37648SKyle Evans struct wgpeer *peer = NULL;
568*adf37648SKyle Evans struct wgallowedip *allowedip = NULL;
569*adf37648SKyle Evans
570*adf37648SKyle Evans if (!device) {
571*adf37648SKyle Evans perror("calloc");
572*adf37648SKyle Evans return false;
573*adf37648SKyle Evans }
574*adf37648SKyle Evans while (argc > 0) {
575*adf37648SKyle Evans if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
576*adf37648SKyle Evans if (!parse_port(&device->listen_port, &device->flags, argv[1]))
577*adf37648SKyle Evans goto error;
578*adf37648SKyle Evans argv += 2;
579*adf37648SKyle Evans argc -= 2;
580*adf37648SKyle Evans } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
581*adf37648SKyle Evans if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
582*adf37648SKyle Evans goto error;
583*adf37648SKyle Evans argv += 2;
584*adf37648SKyle Evans argc -= 2;
585*adf37648SKyle Evans } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
586*adf37648SKyle Evans if (!parse_keyfile(device->private_key, argv[1]))
587*adf37648SKyle Evans goto error;
588*adf37648SKyle Evans device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
589*adf37648SKyle Evans argv += 2;
590*adf37648SKyle Evans argc -= 2;
591*adf37648SKyle Evans } else if (!strcmp(argv[0], "peer") && argc >= 2) {
592*adf37648SKyle Evans struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
593*adf37648SKyle Evans
594*adf37648SKyle Evans allowedip = NULL;
595*adf37648SKyle Evans if (!new_peer) {
596*adf37648SKyle Evans perror("calloc");
597*adf37648SKyle Evans goto error;
598*adf37648SKyle Evans }
599*adf37648SKyle Evans if (peer)
600*adf37648SKyle Evans peer->next_peer = new_peer;
601*adf37648SKyle Evans else
602*adf37648SKyle Evans device->first_peer = new_peer;
603*adf37648SKyle Evans peer = new_peer;
604*adf37648SKyle Evans if (!parse_key(peer->public_key, argv[1]))
605*adf37648SKyle Evans goto error;
606*adf37648SKyle Evans peer->flags |= WGPEER_HAS_PUBLIC_KEY;
607*adf37648SKyle Evans argv += 2;
608*adf37648SKyle Evans argc -= 2;
609*adf37648SKyle Evans } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
610*adf37648SKyle Evans peer->flags |= WGPEER_REMOVE_ME;
611*adf37648SKyle Evans argv += 1;
612*adf37648SKyle Evans argc -= 1;
613*adf37648SKyle Evans } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
614*adf37648SKyle Evans if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
615*adf37648SKyle Evans goto error;
616*adf37648SKyle Evans argv += 2;
617*adf37648SKyle Evans argc -= 2;
618*adf37648SKyle Evans } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
619*adf37648SKyle Evans char *line = strip_spaces(argv[1]);
620*adf37648SKyle Evans
621*adf37648SKyle Evans if (!line)
622*adf37648SKyle Evans goto error;
623*adf37648SKyle Evans if (!parse_allowedips(peer, &allowedip, line)) {
624*adf37648SKyle Evans free(line);
625*adf37648SKyle Evans goto error;
626*adf37648SKyle Evans }
627*adf37648SKyle Evans free(line);
628*adf37648SKyle Evans argv += 2;
629*adf37648SKyle Evans argc -= 2;
630*adf37648SKyle Evans } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
631*adf37648SKyle Evans if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
632*adf37648SKyle Evans goto error;
633*adf37648SKyle Evans argv += 2;
634*adf37648SKyle Evans argc -= 2;
635*adf37648SKyle Evans } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
636*adf37648SKyle Evans if (!parse_keyfile(peer->preshared_key, argv[1]))
637*adf37648SKyle Evans goto error;
638*adf37648SKyle Evans peer->flags |= WGPEER_HAS_PRESHARED_KEY;
639*adf37648SKyle Evans argv += 2;
640*adf37648SKyle Evans argc -= 2;
641*adf37648SKyle Evans } else {
642*adf37648SKyle Evans fprintf(stderr, "Invalid argument: %s\n", argv[0]);
643*adf37648SKyle Evans goto error;
644*adf37648SKyle Evans }
645*adf37648SKyle Evans }
646*adf37648SKyle Evans return device;
647*adf37648SKyle Evans error:
648*adf37648SKyle Evans free_wgdevice(device);
649*adf37648SKyle Evans return false;
650*adf37648SKyle Evans }
651