xref: /freebsd/contrib/wireguard-tools/config.c (revision adf376485712c8fffbf3be330d505a969647f479)
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