2017-01-10 06:36:19 +01:00
|
|
|
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
2015-06-05 15:58:00 +02:00
|
|
|
|
|
|
|
#include <arpa/inet.h>
|
2016-11-05 21:35:33 +01:00
|
|
|
#include <limits.h>
|
2015-06-05 15:58:00 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2017-04-24 03:45:40 +02:00
|
|
|
#include <unistd.h>
|
2015-06-05 15:58:00 +02:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
2017-09-25 04:22:09 +02:00
|
|
|
#include "containers.h"
|
2016-07-20 21:24:27 +02:00
|
|
|
#include "ipc.h"
|
2017-05-15 23:24:48 +02:00
|
|
|
#include "encoding.h"
|
2015-06-05 15:58:00 +02:00
|
|
|
|
|
|
|
#define COMMENT_CHAR '#'
|
|
|
|
|
|
|
|
static const char *get_value(const char *line, const char *key)
|
|
|
|
{
|
|
|
|
size_t linelen = strlen(line);
|
|
|
|
size_t keylen = strlen(key);
|
|
|
|
|
|
|
|
if (keylen >= linelen)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (strncasecmp(line, key, keylen))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return line + keylen;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct addrinfo *resolved;
|
|
|
|
struct addrinfo hints = {
|
|
|
|
.ai_family = AF_UNSPEC,
|
|
|
|
.ai_socktype = SOCK_DGRAM,
|
|
|
|
.ai_protocol = IPPROTO_UDP,
|
2017-03-28 10:46:31 +02:00
|
|
|
.ai_flags = AI_PASSIVE
|
2015-06-05 15:58:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!strlen(value)) {
|
|
|
|
fprintf(stderr, "Unable to parse empty port\n");
|
2017-09-25 04:22:09 +02:00
|
|
|
return false;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = getaddrinfo(NULL, value, &hints, &resolved);
|
2017-09-25 04:22:09 +02:00
|
|
|
if (ret) {
|
2015-06-05 15:58:00 +02:00
|
|
|
fprintf(stderr, "%s: `%s`\n", gai_strerror(ret), value);
|
2017-09-25 04:22:09 +02:00
|
|
|
return false;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = -1;
|
|
|
|
if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
|
|
|
|
*port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
|
|
|
|
ret = 0;
|
|
|
|
} else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
|
|
|
|
*port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
|
|
|
|
ret = 0;
|
|
|
|
} else
|
2015-06-05 15:58:00 +02:00
|
|
|
fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s`\n", value);
|
|
|
|
|
|
|
|
freeaddrinfo(resolved);
|
2017-09-25 04:22:09 +02:00
|
|
|
if (!ret)
|
|
|
|
*flags |= WGDEVICE_HAS_LISTEN_PORT;
|
|
|
|
return ret == 0;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
|
2017-01-24 17:43:35 +01:00
|
|
|
{
|
|
|
|
unsigned long ret;
|
|
|
|
char *end;
|
|
|
|
int base = 10;
|
|
|
|
|
2017-02-22 21:45:03 +01:00
|
|
|
if (!strcasecmp(value, "off")) {
|
|
|
|
*fwmark = 0;
|
2017-09-25 04:22:09 +02:00
|
|
|
*flags |= WGDEVICE_HAS_FWMARK;
|
2017-02-22 21:45:03 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-01-24 17:43:35 +01:00
|
|
|
if (value[0] == '0' && value[1] == 'x') {
|
|
|
|
value += 2;
|
|
|
|
base = 16;
|
|
|
|
}
|
|
|
|
ret = strtoul(value, &end, base);
|
|
|
|
if (!*value || *end || ret > UINT32_MAX)
|
|
|
|
return false;
|
|
|
|
*fwmark = ret;
|
2017-09-25 04:22:09 +02:00
|
|
|
*flags |= WGDEVICE_HAS_FWMARK;
|
2017-01-24 17:43:35 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-27 11:30:05 +02:00
|
|
|
static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
2017-04-16 01:20:43 +02:00
|
|
|
if (!key_from_base64(key, value)) {
|
2016-07-03 20:06:33 +02:00
|
|
|
fprintf(stderr, "Key is not the correct length or format: `%s`\n", value);
|
2015-06-05 15:58:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
2017-09-25 04:22:09 +02:00
|
|
|
allowedip->family = AF_UNSPEC;
|
2015-06-05 15:58:00 +02:00
|
|
|
if (strchr(value, ':')) {
|
2017-09-25 04:22:09 +02:00
|
|
|
if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
|
|
|
|
allowedip->family = AF_INET6;
|
2015-06-05 15:58:00 +02:00
|
|
|
} else {
|
2017-09-25 04:22:09 +02:00
|
|
|
if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
|
|
|
|
allowedip->family = AF_INET;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
if (allowedip->family == AF_UNSPEC) {
|
2015-06-05 15:58:00 +02:00
|
|
|
fprintf(stderr, "Unable to parse IP address: `%s`\n", value);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-01-10 05:52:06 +01:00
|
|
|
static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
|
|
|
char *mutable = strdup(value);
|
|
|
|
char *begin, *end;
|
|
|
|
int ret;
|
|
|
|
struct addrinfo *resolved;
|
|
|
|
struct addrinfo hints = {
|
|
|
|
.ai_family = AF_UNSPEC,
|
|
|
|
.ai_socktype = SOCK_DGRAM,
|
2016-12-20 21:50:29 +01:00
|
|
|
.ai_protocol = IPPROTO_UDP
|
2015-06-05 15:58:00 +02:00
|
|
|
};
|
2017-04-19 16:51:24 +02:00
|
|
|
if (!mutable) {
|
|
|
|
perror("strdup");
|
|
|
|
return false;
|
|
|
|
}
|
2015-06-05 15:58:00 +02:00
|
|
|
if (!strlen(value)) {
|
|
|
|
free(mutable);
|
|
|
|
fprintf(stderr, "Unable to parse empty endpoint\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (mutable[0] == '[') {
|
|
|
|
begin = &mutable[1];
|
|
|
|
end = strchr(mutable, ']');
|
|
|
|
if (!end) {
|
|
|
|
free(mutable);
|
|
|
|
fprintf(stderr, "Unable to find matching brace of endpoint: `%s`\n", value);
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-15 23:24:48 +02:00
|
|
|
*end++ = '\0';
|
|
|
|
if (*end++ != ':' || !*end) {
|
2015-06-05 15:58:00 +02:00
|
|
|
free(mutable);
|
|
|
|
fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
begin = mutable;
|
|
|
|
end = strrchr(mutable, ':');
|
|
|
|
if (!end || !*(end + 1)) {
|
|
|
|
free(mutable);
|
|
|
|
fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-15 23:24:48 +02:00
|
|
|
*end++ = '\0';
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
2017-04-24 03:45:40 +02:00
|
|
|
|
|
|
|
for (unsigned int timeout = 1000000; timeout < 90000000; timeout = timeout * 3 / 2) {
|
|
|
|
ret = getaddrinfo(begin, end, &hints, &resolved);
|
|
|
|
if (ret != EAI_AGAIN)
|
|
|
|
break;
|
|
|
|
fprintf(stderr, "%s: `%s`. Trying again in %.2f seconds...\n", gai_strerror(ret), value, timeout / 1000000.0);
|
|
|
|
usleep(timeout);
|
|
|
|
}
|
|
|
|
|
2015-06-05 15:58:00 +02:00
|
|
|
if (ret != 0) {
|
|
|
|
free(mutable);
|
|
|
|
fprintf(stderr, "%s: `%s`\n", gai_strerror(ret), value);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
|
|
|
|
(resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
|
|
|
|
memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
|
|
|
|
else {
|
|
|
|
freeaddrinfo(resolved);
|
|
|
|
free(mutable);
|
|
|
|
fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s`\n", value);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
freeaddrinfo(resolved);
|
|
|
|
free(mutable);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
|
2016-07-08 02:30:03 +02:00
|
|
|
{
|
|
|
|
unsigned long ret;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
if (!strcasecmp(value, "off")) {
|
|
|
|
*interval = 0;
|
2017-09-25 04:22:09 +02:00
|
|
|
*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
|
2016-07-08 02:30:03 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = strtoul(value, &end, 10);
|
2016-08-08 13:53:00 +02:00
|
|
|
if (!*value || *value == '-' || *end || ret > 65535) {
|
|
|
|
fprintf(stderr, "The persistent keepalive interval must be 0/off or 1-65535. Found: `%s`\n", value);
|
2016-07-08 02:30:03 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
*interval = (uint16_t)ret;
|
|
|
|
*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
|
2016-07-08 02:30:03 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
2017-09-25 04:22:09 +02:00
|
|
|
struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
|
2015-06-05 15:58:00 +02:00
|
|
|
char *mask, *mutable = strdup(value), *sep;
|
|
|
|
if (!mutable) {
|
|
|
|
perror("strdup");
|
|
|
|
return false;
|
|
|
|
};
|
2017-09-25 04:22:09 +02:00
|
|
|
peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
|
2015-06-05 15:58:00 +02:00
|
|
|
if (!strlen(value)) {
|
|
|
|
free(mutable);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
sep = mutable;
|
|
|
|
while ((mask = strsep(&sep, ","))) {
|
2016-11-05 21:35:33 +01:00
|
|
|
unsigned long cidr = ULONG_MAX;
|
2015-06-05 15:58:00 +02:00
|
|
|
char *end, *ip = strsep(&mask, "/");
|
2017-09-25 04:22:09 +02:00
|
|
|
new_allowedip = calloc(1, sizeof(struct wgallowedip));
|
|
|
|
if (!new_allowedip) {
|
|
|
|
perror("calloc");
|
2015-06-05 15:58:00 +02:00
|
|
|
free(mutable);
|
|
|
|
return false;
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
if (allowedip)
|
|
|
|
allowedip->next_allowedip = new_allowedip;
|
|
|
|
else
|
|
|
|
peer->first_allowedip = new_allowedip;
|
|
|
|
allowedip = new_allowedip;
|
2015-06-05 15:58:00 +02:00
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
if (!parse_ip(allowedip, ip)) {
|
2015-06-05 15:58:00 +02:00
|
|
|
free(mutable);
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-05 21:35:33 +01:00
|
|
|
if (mask && *mask) {
|
|
|
|
cidr = strtoul(mask, &end, 10);
|
|
|
|
if (*end)
|
|
|
|
cidr = ULONG_MAX;
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
if (allowedip->family == AF_INET)
|
2016-11-05 21:35:33 +01:00
|
|
|
cidr = cidr > 32 ? 32 : cidr;
|
2017-09-25 04:22:09 +02:00
|
|
|
else if (allowedip->family == AF_INET6)
|
2016-11-05 21:35:33 +01:00
|
|
|
cidr = cidr > 128 ? 128 : cidr;
|
|
|
|
else
|
2015-06-05 15:58:00 +02:00
|
|
|
continue;
|
2017-09-25 04:22:09 +02:00
|
|
|
allowedip->cidr = cidr;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
free(mutable);
|
2017-09-25 04:22:09 +02:00
|
|
|
*last_allowedip = allowedip;
|
2015-06-05 15:58:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool process_line(struct config_ctx *ctx, const char *line)
|
|
|
|
{
|
|
|
|
const char *value;
|
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
if (!strcasecmp(line, "[Interface]")) {
|
|
|
|
ctx->is_peer_section = false;
|
|
|
|
ctx->is_device_section = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(line, "[Peer]")) {
|
2017-09-25 04:22:09 +02:00
|
|
|
struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
|
|
|
|
if (!new_peer) {
|
|
|
|
perror("calloc");
|
2015-06-05 15:58:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
ctx->last_allowedip = NULL;
|
|
|
|
if (ctx->last_peer)
|
|
|
|
ctx->last_peer->next_peer = new_peer;
|
|
|
|
else
|
|
|
|
ctx->device->first_peer = new_peer;
|
|
|
|
ctx->last_peer = new_peer;
|
2015-06-05 15:58:00 +02:00
|
|
|
ctx->is_peer_section = true;
|
|
|
|
ctx->is_device_section = false;
|
2017-09-25 04:22:09 +02:00
|
|
|
ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
|
2015-06-05 15:58:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define key_match(key) (value = get_value(line, key "="))
|
|
|
|
|
|
|
|
if (ctx->is_device_section) {
|
|
|
|
if (key_match("ListenPort"))
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
|
2017-01-24 17:43:35 +01:00
|
|
|
else if (key_match("FwMark"))
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
|
2015-06-05 15:58:00 +02:00
|
|
|
else if (key_match("PrivateKey")) {
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_key(ctx->device->private_key, value);
|
2015-06-05 15:58:00 +02:00
|
|
|
if (!ret)
|
2017-09-25 04:22:09 +02:00
|
|
|
memset(ctx->device->private_key, 0, WG_KEY_LEN);
|
|
|
|
else
|
|
|
|
ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
|
2015-06-05 15:58:00 +02:00
|
|
|
} else
|
|
|
|
goto error;
|
|
|
|
} else if (ctx->is_peer_section) {
|
|
|
|
if (key_match("Endpoint"))
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
|
2015-06-05 15:58:00 +02:00
|
|
|
else if (key_match("PublicKey"))
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_key(ctx->last_peer->public_key, value);
|
2015-06-05 15:58:00 +02:00
|
|
|
else if (key_match("AllowedIPs"))
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
|
2016-07-08 02:30:03 +02:00
|
|
|
else if (key_match("PersistentKeepalive"))
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
|
2017-04-27 11:10:50 +02:00
|
|
|
else if (key_match("PresharedKey")) {
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = parse_key(ctx->last_peer->preshared_key, value);
|
2017-04-27 11:10:50 +02:00
|
|
|
if (!ret)
|
2017-09-25 04:22:09 +02:00
|
|
|
memset(ctx->last_peer->preshared_key, 0, WG_KEY_LEN);
|
|
|
|
else
|
|
|
|
ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
|
2017-04-27 11:10:50 +02:00
|
|
|
} else
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
|
|
|
} else
|
|
|
|
goto error;
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
#undef key_match
|
|
|
|
|
|
|
|
error:
|
|
|
|
fprintf(stderr, "Line unrecognized: `%s'\n", line);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool config_read_line(struct config_ctx *ctx, const char *input)
|
|
|
|
{
|
|
|
|
size_t len = strlen(input), cleaned_len = 0;
|
|
|
|
char *line = calloc(len + 1, sizeof(char));
|
|
|
|
bool ret = true;
|
|
|
|
if (!line) {
|
|
|
|
perror("calloc");
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = false;
|
|
|
|
goto out;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
if (!len)
|
|
|
|
goto out;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
|
|
if (!isspace(input[i]))
|
|
|
|
line[cleaned_len++] = input[i];
|
|
|
|
}
|
|
|
|
if (!cleaned_len)
|
|
|
|
goto out;
|
|
|
|
if (line[0] == COMMENT_CHAR)
|
|
|
|
goto out;
|
|
|
|
ret = process_line(ctx, line);
|
|
|
|
out:
|
|
|
|
free(line);
|
2017-09-25 04:22:09 +02:00
|
|
|
if (!ret)
|
|
|
|
free_wgdevice(ctx->device);
|
2015-06-05 15:58:00 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
bool config_read_init(struct config_ctx *ctx, bool append)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
|
|
|
memset(ctx, 0, sizeof(struct config_ctx));
|
2017-09-25 04:22:09 +02:00
|
|
|
ctx->device = calloc(1, sizeof(struct wgdevice));
|
|
|
|
if (!ctx->device) {
|
2015-06-05 15:58:00 +02:00
|
|
|
perror("calloc");
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-10 04:47:29 +01:00
|
|
|
if (!append)
|
2017-09-25 04:22:09 +02:00
|
|
|
ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
|
2015-06-05 15:58:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
struct wgdevice *config_read_finish(struct config_ctx *ctx)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
|
|
|
struct wgpeer *peer;
|
2017-09-25 04:22:09 +02:00
|
|
|
if (ctx->device->flags & WGDEVICE_REPLACE_PEERS && key_is_zero(ctx->device->private_key)) {
|
|
|
|
fprintf(stderr, "No private key is configured\n");
|
2015-06-05 15:58:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2017-01-24 04:11:56 +01:00
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
for_each_wgpeer (ctx->device, peer) {
|
2017-09-22 04:04:00 +02:00
|
|
|
if (key_is_zero(peer->public_key)) {
|
2015-06-05 15:58:00 +02:00
|
|
|
fprintf(stderr, "A peer is missing a public key\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
return ctx->device;
|
2015-06-05 15:58:00 +02:00
|
|
|
err:
|
2017-09-25 04:22:09 +02:00
|
|
|
free_wgdevice(ctx->device);
|
|
|
|
return NULL;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
static bool read_keyfile(char dst[WG_KEY_LEN_BASE64], const char *path)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
|
|
|
FILE *f;
|
2017-09-25 04:22:09 +02:00
|
|
|
int c;
|
|
|
|
bool ret = false;
|
2015-06-05 15:58:00 +02:00
|
|
|
|
|
|
|
f = fopen(path, "r");
|
|
|
|
if (!f) {
|
|
|
|
perror("fopen");
|
2017-09-25 04:22:09 +02:00
|
|
|
return false;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
2017-05-12 15:03:38 +02:00
|
|
|
|
|
|
|
if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
|
|
|
|
if (errno) {
|
|
|
|
perror("fread");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/* If we're at the end and we didn't read anything, we're /dev/null. */
|
|
|
|
if (!ferror(f) && feof(f) && !ftell(f)) {
|
2017-09-25 04:22:09 +02:00
|
|
|
static const uint8_t zeros[WG_KEY_LEN] = { 0 };
|
|
|
|
key_to_base64(dst, zeros);
|
|
|
|
ret = true;
|
2017-05-12 15:03:38 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Invalid length key in key file\n");
|
|
|
|
goto out;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
2017-05-12 15:03:38 +02:00
|
|
|
dst[WG_KEY_LEN_BASE64 - 1] = '\0';
|
|
|
|
|
|
|
|
while ((c = getc(f)) != EOF) {
|
|
|
|
if (!isspace(c)) {
|
|
|
|
fprintf(stderr, "Found trailing character in key file: `%c`\n", c);
|
|
|
|
goto out;
|
|
|
|
}
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
2017-05-12 15:03:38 +02:00
|
|
|
if (ferror(f) && errno) {
|
|
|
|
perror("getc");
|
|
|
|
goto out;
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
ret = true;
|
2017-05-12 15:03:38 +02:00
|
|
|
|
|
|
|
out:
|
|
|
|
fclose(f);
|
|
|
|
return ret;
|
2015-06-05 15:58:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *strip_spaces(const char *in)
|
|
|
|
{
|
|
|
|
char *out;
|
|
|
|
size_t t, l, i;
|
|
|
|
|
|
|
|
t = strlen(in);
|
|
|
|
out = calloc(t + 1, sizeof(char));
|
|
|
|
if (!out) {
|
|
|
|
perror("calloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (i = 0, l = 0; i < t; ++i) {
|
|
|
|
if (!isspace(in[i]))
|
|
|
|
out[l++] = in[i];
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2017-09-25 04:22:09 +02:00
|
|
|
struct wgdevice *config_read_cmd(char *argv[], int argc)
|
2015-06-05 15:58:00 +02:00
|
|
|
{
|
2017-09-25 04:22:09 +02:00
|
|
|
struct wgdevice *device = calloc(1, sizeof(struct wgdevice));
|
|
|
|
struct wgpeer *peer = NULL;
|
|
|
|
struct wgallowedip *allowedip = NULL;
|
|
|
|
if (!device) {
|
2015-06-05 15:58:00 +02:00
|
|
|
perror("calloc");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while (argc > 0) {
|
2017-09-25 04:22:09 +02:00
|
|
|
if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
|
|
|
|
if (!parse_port(&device->listen_port, &device->flags, argv[1]))
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
|
|
|
|
if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
|
2017-01-24 17:43:35 +01:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
|
2017-05-12 15:03:38 +02:00
|
|
|
char key_line[WG_KEY_LEN_BASE64];
|
2017-09-25 04:22:09 +02:00
|
|
|
if (read_keyfile(key_line, argv[1])) {
|
|
|
|
if (!parse_key(device->private_key, key_line))
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
2017-09-25 04:22:09 +02:00
|
|
|
device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
|
|
|
|
} else
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
|
|
|
} else if (!strcmp(argv[0], "peer") && argc >= 2) {
|
2017-09-25 04:22:09 +02:00
|
|
|
struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
|
|
|
|
allowedip = NULL;
|
|
|
|
if (!new_peer) {
|
|
|
|
perror("calloc");
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
if (peer)
|
|
|
|
peer->next_peer = new_peer;
|
|
|
|
else
|
|
|
|
device->first_peer = new_peer;
|
|
|
|
peer = new_peer;
|
|
|
|
if (!parse_key(peer->public_key, argv[1]))
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
|
|
|
|
peer->flags |= WGPEER_REMOVE_ME;
|
2015-06-05 15:58:00 +02:00
|
|
|
argv += 1;
|
|
|
|
argc -= 1;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
|
|
|
|
if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
|
2015-06-05 15:58:00 +02:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
|
2015-06-05 15:58:00 +02:00
|
|
|
char *line = strip_spaces(argv[1]);
|
|
|
|
if (!line)
|
|
|
|
goto error;
|
2017-09-25 04:22:09 +02:00
|
|
|
if (!parse_allowedips(peer, &allowedip, line)) {
|
2015-06-05 15:58:00 +02:00
|
|
|
free(line);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
|
|
|
|
if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
|
2016-07-08 02:30:03 +02:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2017-09-25 04:22:09 +02:00
|
|
|
} else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
|
2017-05-12 15:03:38 +02:00
|
|
|
char key_line[WG_KEY_LEN_BASE64];
|
2017-09-25 04:22:09 +02:00
|
|
|
if (read_keyfile(key_line, argv[1])) {
|
|
|
|
if (!parse_key(peer->preshared_key, key_line))
|
2017-04-27 11:10:50 +02:00
|
|
|
goto error;
|
2017-09-25 04:22:09 +02:00
|
|
|
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
|
|
|
|
} else
|
2017-04-27 11:10:50 +02:00
|
|
|
goto error;
|
|
|
|
argv += 2;
|
|
|
|
argc -= 2;
|
2015-06-05 15:58:00 +02:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2017-09-25 04:22:09 +02:00
|
|
|
return device;
|
2015-06-05 15:58:00 +02:00
|
|
|
error:
|
2017-09-25 04:22:09 +02:00
|
|
|
free_wgdevice(device);
|
2015-06-05 15:58:00 +02:00
|
|
|
return false;
|
|
|
|
}
|