xref: /freebsd/contrib/wireguard-tools/setconf.c (revision 1d386b48)
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /*
3  * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4  */
5 
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "containers.h"
12 #include "config.h"
13 #include "ipc.h"
14 #include "subcommands.h"
15 
16 struct pubkey_origin {
17 	uint8_t *pubkey;
18 	bool from_file;
19 };
20 
21 static int pubkey_cmp(const void *first, const void *second)
22 {
23 	const struct pubkey_origin *a = first, *b = second;
24 	int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN);
25 	if (ret)
26 		return ret;
27 	return a->from_file - b->from_file;
28 }
29 
30 static bool sync_conf(struct wgdevice *file)
31 {
32 	struct wgdevice *runtime;
33 	struct wgpeer *peer;
34 	struct pubkey_origin *pubkeys;
35 	size_t peer_count = 0, i = 0;
36 
37 	if (!file->first_peer)
38 		return true;
39 
40 	for_each_wgpeer(file, peer)
41 		++peer_count;
42 
43 	if (ipc_get_device(&runtime, file->name) != 0) {
44 		perror("Unable to retrieve current interface configuration");
45 		return false;
46 	}
47 
48 	if (!runtime->first_peer) {
49 		free_wgdevice(runtime);
50 		return true;
51 	}
52 
53 	file->flags &= ~WGDEVICE_REPLACE_PEERS;
54 
55 	for_each_wgpeer(runtime, peer)
56 		++peer_count;
57 
58 	pubkeys = calloc(peer_count, sizeof(*pubkeys));
59 	if (!pubkeys) {
60 		free_wgdevice(runtime);
61 		perror("Public key allocation");
62 		return false;
63 	}
64 
65 	for_each_wgpeer(file, peer) {
66 		pubkeys[i].pubkey = peer->public_key;
67 		pubkeys[i].from_file = true;
68 		++i;
69 	}
70 	for_each_wgpeer(runtime, peer) {
71 		pubkeys[i].pubkey = peer->public_key;
72 		pubkeys[i].from_file = false;
73 		++i;
74 	}
75 	qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp);
76 
77 	for (i = 0; i < peer_count; ++i) {
78 		if (pubkeys[i].from_file)
79 			continue;
80 		if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) {
81 			peer = calloc(1, sizeof(struct wgpeer));
82 			if (!peer) {
83 				free_wgdevice(runtime);
84 				free(pubkeys);
85 				perror("Peer allocation");
86 				return false;
87 			}
88 			peer->flags = WGPEER_REMOVE_ME;
89 			memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN);
90 			peer->next_peer = file->first_peer;
91 			file->first_peer = peer;
92 			if (!file->last_peer)
93 				file->last_peer = peer;
94 		}
95 	}
96 	free_wgdevice(runtime);
97 	free(pubkeys);
98 	return true;
99 }
100 
101 int setconf_main(int argc, const char *argv[])
102 {
103 	struct wgdevice *device = NULL;
104 	struct config_ctx ctx;
105 	FILE *config_input = NULL;
106 	char *config_buffer = NULL;
107 	size_t config_buffer_len = 0;
108 	int ret = 1;
109 
110 	if (argc != 3) {
111 		fprintf(stderr, "Usage: %s %s <interface> <configuration filename>\n", PROG_NAME, argv[0]);
112 		return 1;
113 	}
114 
115 	config_input = fopen(argv[2], "r");
116 	if (!config_input) {
117 		perror("fopen");
118 		return 1;
119 	}
120 	if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) {
121 		fclose(config_input);
122 		return 1;
123 	}
124 	while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) {
125 		if (!config_read_line(&ctx, config_buffer)) {
126 			fprintf(stderr, "Configuration parsing error\n");
127 			goto cleanup;
128 		}
129 	}
130 	device = config_read_finish(&ctx);
131 	if (!device) {
132 		fprintf(stderr, "Invalid configuration\n");
133 		goto cleanup;
134 	}
135 	strncpy(device->name, argv[1], IFNAMSIZ - 1);
136 	device->name[IFNAMSIZ - 1] = '\0';
137 
138 	if (!strcmp(argv[0], "syncconf")) {
139 		if (!sync_conf(device))
140 			goto cleanup;
141 	}
142 
143 	if (ipc_set_device(device) != 0) {
144 		perror("Unable to modify interface");
145 		goto cleanup;
146 	}
147 
148 	ret = 0;
149 
150 cleanup:
151 	if (config_input)
152 		fclose(config_input);
153 	free(config_buffer);
154 	free_wgdevice(device);
155 	return ret;
156 }
157