From 02eb8daf8c158700a94cec894434dce629962483 Mon Sep 17 00:00:00 2001
From: Lonnie Abelbeck <lonnie@abelbeck.com>
Date: Thu, 7 Dec 2017 09:07:34 -0600
Subject: [PATCH 1/1] wg: add support for peer names using a file in userspace

Disabled by default, build with WITH_PEERDATA=yes to enable peer name support.

Config [Peer] sections can optionally be [Peer-custom_name] with show and showconf

displaying the peer-custom_name label.  Spaces are ignored.

The data file location is PEERDATAFILE, which defaults to /var/run/wg.peerdata
---
 src/tools/Makefile   |   5 +++
 src/tools/config.c   |  33 +++++++++++++++-
 src/tools/peerdata.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tools/peerdata.h |  20 ++++++++++
 src/tools/show.c     |   7 ++++
 src/tools/showconf.c |   6 +++
 6 files changed, 173 insertions(+), 2 deletions(-)
 create mode 100644 src/tools/peerdata.c
 create mode 100644 src/tools/peerdata.h

diff --git a/src/tools/Makefile b/src/tools/Makefile
index e277b2f..761ee2a 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -12,9 +12,11 @@ MANDIR ?= $(PREFIX)/share/man
 BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions
 SYSTEMDUNITDIR ?= $(shell $(PKG_CONFIG) --variable=systemdsystemunitdir systemd 2>/dev/null || echo "$(PREFIX)/lib/systemd/system")
 RUNSTATEDIR ?= /var/run
+PEERDATAFILE ?= /var/run/wg.peerdata
 WITH_BASHCOMPLETION ?=
 WITH_WGQUICK ?=
 WITH_SYSTEMDUNITS ?=
+WITH_PEERDATA ?=
 
 ifeq ($(WITH_BASHCOMPLETION),)
 ifneq ($(strip $(wildcard $(BASHCOMPDIR))),)
@@ -40,6 +42,9 @@ CFLAGS += -std=gnu11 -D_GNU_SOURCE
 CFLAGS += -Wall -Wextra
 CFLAGS += -MMD -MP
 CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
+ifeq ($(WITH_PEERDATA),yes)
+CFLAGS += -DPEERDATA -DPEERDATAFILE="\"$(PEERDATAFILE)\""
+endif
 ifeq ($(DEBUG_TOOLS),y)
 CFLAGS += -g
 endif
diff --git a/src/tools/config.c b/src/tools/config.c
index 1fddb64..6077a6b 100644
--- a/src/tools/config.c
+++ b/src/tools/config.c
@@ -19,6 +19,7 @@
 #include "containers.h"
 #include "ipc.h"
 #include "encoding.h"
+#include "peerdata.h"
 
 #define COMMENT_CHAR '#'
 
@@ -358,7 +359,13 @@ static bool process_line(struct config_ctx *ctx, const char *line)
 		ctx->is_device_section = true;
 		return true;
 	}
+#ifdef PEERDATA
+	static char peer_name[WG_PEERDATA_MAXLEN];
+	bool is_peername = !strncasecmp(line, "[Peer-", 6) && line[strlen(line) - 1] == ']';
+	if (!strcasecmp(line, "[Peer]") || is_peername) {
+#else
 	if (!strcasecmp(line, "[Peer]")) {
+#endif
 		struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
 
 		if (!new_peer) {
@@ -374,6 +381,15 @@ static bool process_line(struct config_ctx *ctx, const char *line)
 		ctx->is_peer_section = true;
 		ctx->is_device_section = false;
 		ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
+#ifdef PEERDATA
+		if (is_peername) {
+			strncpy(peer_name, line + 6, WG_PEERDATA_MAXLEN);	/* jump over "[Peer-" */
+			peer_name[WG_PEERDATA_MAXLEN - 1] = '\0';
+			peer_name[strlen(peer_name) - 1] = '\0';			/* overwrite last character ']' */
+		} else {
+			peer_name[0] = '\0';
+		}
+#endif
 		return true;
 	}
 
@@ -395,8 +411,17 @@ static bool process_line(struct config_ctx *ctx, const char *line)
 			ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
 		else if (key_match("PublicKey")) {
 			ret = parse_key(ctx->last_peer->public_key, value);
-			if (ret)
+			if (ret) {
 				ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+#ifdef PEERDATA
+				if (peer_name[0]) {
+					static char base64[WG_KEY_LEN_BASE64];
+					key_to_base64(base64, ctx->last_peer->public_key);
+					file_put_peerdata(base64, "name", peer_name);
+					peer_name[0] = '\0';
+				}
+#endif
+			}
 		} else if (key_match("AllowedIPs"))
 			ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
 		else if (key_match("PersistentKeepalive"))
@@ -455,8 +480,12 @@ bool config_read_init(struct config_ctx *ctx, bool append)
 		perror("calloc");
 		return false;
 	}
-	if (!append)
+	if (!append) {
 		ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
+#ifdef PEERDATA
+		file_init_peerdata();
+#endif
+	}
 	return true;
 }
 
diff --git a/src/tools/peerdata.c b/src/tools/peerdata.c
new file mode 100644
index 0000000..9bd7871
--- /dev/null
+++ b/src/tools/peerdata.c
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifdef PEERDATA
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "peerdata.h"
+
+bool file_init_peerdata(void)
+{
+	FILE *f;
+	bool ret = true;
+
+	f = fopen(WG_PEERDATA_FILE, "r");
+	if (f) {
+		fclose(f);
+		if (remove(WG_PEERDATA_FILE)) {
+			perror("remove");
+			ret = false;
+		}
+	}
+	return ret;
+}
+
+bool file_put_peerdata(const char *pubkey, const char *type, const char *data)
+{
+	FILE *f;
+	char *buffer;
+	size_t buffer_len = strlen(pubkey) + strlen(type) + strlen(data) + 4;
+	bool ret = false;
+
+	f = fopen(WG_PEERDATA_FILE, "a");
+	if (!f) {
+		perror("fopen");
+		return false;
+	}
+
+	buffer = calloc(buffer_len, sizeof(char));
+	if (!buffer) {
+		perror("calloc");
+		fclose(f);
+		return false;
+	}
+
+	snprintf(buffer, buffer_len, "%s,%s,%s\n", pubkey, type, data);
+	if (fwrite(buffer, strlen(buffer), 1, f) != 1) {
+		if (errno) {
+			perror("fwrite");
+		}
+		goto out;
+	}
+	ret = true;
+
+out:
+	fclose(f);
+	free(buffer);
+	return ret;
+}
+
+char *file_get_peerdata(const char *pubkey, const char *type)
+{
+	static char data[WG_PEERDATA_MAXLEN];
+	char *data_rtn = NULL;
+	FILE *f;
+	char *buffer = NULL;
+	size_t buffer_len = 0;
+	char *line, *token;
+
+	f = fopen(WG_PEERDATA_FILE, "r");
+	if (!f) {
+		return NULL;
+	}
+
+	while (getline(&buffer, &buffer_len, f) >= 0) {
+		line = buffer;
+		if ((token = strsep(&line, ","))) {
+			if (!strcmp(token, pubkey)) {
+				if ((token = strsep(&line, ","))) {
+					if (!strcmp(token, type)) {
+						if ((token = strsep(&line, "\n"))) {
+							strncpy(data, token, WG_PEERDATA_MAXLEN);
+							data[WG_PEERDATA_MAXLEN - 1] = '\0';
+							data_rtn = data;
+							goto out;
+						}
+					}
+				}
+			}
+		}
+	}
+
+out:
+	fclose(f);
+	free(buffer);
+	return data_rtn;
+}
+
+#endif
diff --git a/src/tools/peerdata.h b/src/tools/peerdata.h
new file mode 100644
index 0000000..42380f0
--- /dev/null
+++ b/src/tools/peerdata.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef PEERDATA_H
+#define PEERDATA_H
+
+#ifdef PEERDATA
+#define WG_PEERDATA_FILE PEERDATAFILE
+#define WG_PEERDATA_MAXLEN 64
+
+#include <stdbool.h>
+
+bool file_init_peerdata(void);
+bool file_put_peerdata(const char *pubkey, const char *type, const char *data);
+char *file_get_peerdata(const char *pubkey, const char *type);
+#endif
+
+#endif
diff --git a/src/tools/show.c b/src/tools/show.c
index c5be788..bb37463 100644
--- a/src/tools/show.c
+++ b/src/tools/show.c
@@ -21,6 +21,7 @@
 #include "terminal.h"
 #include "encoding.h"
 #include "subcommands.h"
+#include "peerdata.h"
 
 static int peer_cmp(const void *first, const void *second)
 {
@@ -222,7 +223,13 @@ static void pretty_print(struct wgdevice *device)
 		terminal_printf("\n");
 	}
 	for_each_wgpeer(device, peer) {
+#ifdef PEERDATA
+		char *pubkey = key(peer->public_key);
+		char *peerdata = file_get_peerdata(pubkey, "name");
+		terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer%s%s" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", (peerdata ? "-" : ""), (peerdata ? peerdata : ""), pubkey);
+#else
 		terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
+#endif
 		if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
 			terminal_printf("  " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key));
 		if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
diff --git a/src/tools/showconf.c b/src/tools/showconf.c
index 2e3fbd4..443bccc 100644
--- a/src/tools/showconf.c
+++ b/src/tools/showconf.c
@@ -16,6 +16,7 @@
 #include "encoding.h"
 #include "ipc.h"
 #include "subcommands.h"
+#include "peerdata.h"
 
 int showconf_main(int argc, char *argv[])
 {
@@ -48,7 +49,12 @@ int showconf_main(int argc, char *argv[])
 	printf("\n");
 	for_each_wgpeer(device, peer) {
 		key_to_base64(base64, peer->public_key);
+#ifdef PEERDATA
+		char *peerdata = file_get_peerdata(base64, "name");
+		printf("[Peer%s%s]\nPublicKey = %s\n", (peerdata ? "-" : ""), (peerdata ? peerdata : ""), base64);
+#else
 		printf("[Peer]\nPublicKey = %s\n", base64);
+#endif
 		if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
 			key_to_base64(base64, peer->preshared_key);
 			printf("PresharedKey = %s\n", base64);
-- 
1.8.3.1

