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
pubkey_cmp(const void * first,const void * second)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
sync_conf(struct wgdevice * file)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
setconf_main(int argc,const char * argv[])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