// SPDX-License-Identifier: GPL-2.0 OR MIT /* * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. */ #include #include #include #include #include "containers.h" #include "config.h" #include "ipc.h" #include "subcommands.h" struct peer_origin { struct wgpeer *peer; bool from_file; }; static int peer_cmp(const void *first, const void *second) { const struct peer_origin *a = first, *b = second; int ret = memcmp(a->peer->public_key, b->peer->public_key, WG_KEY_LEN); if (ret) return ret; return a->from_file - b->from_file; } static bool sync_conf(struct wgdevice *file) { struct wgdevice *runtime; struct wgpeer *peer; struct peer_origin *peers; size_t peer_count = 0, i = 0; if (!file->first_peer) return true; for_each_wgpeer(file, peer) ++peer_count; if (ipc_get_device(&runtime, file->name) != 0) { perror("Unable to retrieve current interface configuration"); return false; } if (!runtime->first_peer) { free_wgdevice(runtime); return true; } file->flags &= ~WGDEVICE_REPLACE_PEERS; for_each_wgpeer(runtime, peer) ++peer_count; peers = calloc(peer_count, sizeof(*peers)); if (!peers) { free_wgdevice(runtime); perror("Peer list allocation"); return false; } for_each_wgpeer(file, peer) { peers[i].peer = peer; peers[i].from_file = true; ++i; } for_each_wgpeer(runtime, peer) { peers[i].peer = peer; peers[i].from_file = false; ++i; } qsort(peers, peer_count, sizeof(*peers), peer_cmp); for (i = 0; i < peer_count; ++i) { if (peers[i].from_file) continue; if (i == peer_count - 1 || !peers[i + 1].from_file || memcmp(peers[i].peer->public_key, peers[i + 1].peer->public_key, WG_KEY_LEN)) { peer = calloc(1, sizeof(struct wgpeer)); if (!peer) { free_wgdevice(runtime); free(peers); perror("Peer allocation"); return false; } peer->flags = WGPEER_REMOVE_ME; memcpy(peer->public_key, peers[i].peer->public_key, WG_KEY_LEN); peer->next_peer = file->first_peer; file->first_peer = peer; if (!file->last_peer) file->last_peer = peer; } else if (i < peer_count - 1 && peers[i + 1].from_file && (peers[i].peer->flags & WGPEER_HAS_PRESHARED_KEY) && !(peers[i + 1].peer->flags & WGPEER_HAS_PRESHARED_KEY) && !memcmp(peers[i].peer->public_key, peers[i + 1].peer->public_key, WG_KEY_LEN)) { memset(peers[i + 1].peer->preshared_key, 0, WG_KEY_LEN); peers[i + 1].peer->flags |= WGPEER_HAS_PRESHARED_KEY; } } free_wgdevice(runtime); free(peers); return true; } int setconf_main(int argc, const char *argv[]) { struct wgdevice *device = NULL; struct config_ctx ctx; FILE *config_input = NULL; char *config_buffer = NULL; size_t config_buffer_len = 0; int ret = 1; if (argc != 3) { fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); return 1; } config_input = fopen(argv[2], "r"); if (!config_input) { perror("fopen"); return 1; } if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) { fclose(config_input); return 1; } while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) { if (!config_read_line(&ctx, config_buffer)) { fprintf(stderr, "Configuration parsing error\n"); goto cleanup; } } device = config_read_finish(&ctx); if (!device) { fprintf(stderr, "Invalid configuration\n"); goto cleanup; } strncpy(device->name, argv[1], IFNAMSIZ - 1); device->name[IFNAMSIZ - 1] = '\0'; if (!strcmp(argv[0], "syncconf")) { if (!sync_conf(device)) goto cleanup; } if (ipc_set_device(device) != 0) { perror("Unable to modify interface"); goto cleanup; } ret = 0; cleanup: if (config_input) fclose(config_input); free(config_buffer); free_wgdevice(device); return ret; }