Imported Upstream version 1.40
[platform/upstream/connman.git] / vpn / plugins / wireguard.c
1 /*
2  *  ConnMan VPN daemon
3  *
4  *  Copyright (C) 2019  Daniel Wagner. All rights reserved.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <net/if.h>
29 #include <arpa/inet.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netdb.h>
33
34 #include <glib.h>
35
36 #define CONNMAN_API_SUBJECT_TO_CHANGE
37 #include <connman/plugin.h>
38 #include <connman/log.h>
39 #include <connman/task.h>
40 #include <connman/ipconfig.h>
41 #include <connman/inet.h>
42 #include <connman/dbus.h>
43 #include <connman/setting.h>
44 #include <connman/vpn-dbus.h>
45
46 #include "../vpn-provider.h"
47 #include "../vpn.h"
48
49 #include "vpn.h"
50 #include "wireguard.h"
51
52 #define DNS_RERESOLVE_TIMEOUT 20
53
54 struct wireguard_info {
55         struct wg_device device;
56         struct wg_peer peer;
57         char *endpoint_fqdn;
58         char *port;
59         int reresolve_id;
60 };
61
62 struct sockaddr_u {
63         union {
64                 struct sockaddr sa;
65                 struct sockaddr_in sin;
66                 struct sockaddr_in6 sin6;
67         };
68 };
69
70 static int parse_key(const char *str, wg_key key)
71 {
72         unsigned char *buf;
73         size_t len;
74
75         buf = g_base64_decode(str, &len);
76
77         if (len != 32) {
78                 g_free(buf);
79                 return -EINVAL;
80         }
81
82         memcpy(key, buf, 32);
83
84         g_free(buf);
85         return 0;
86 }
87
88 static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
89 {
90         struct wg_allowedip *curaip, *allowedip;
91         char buf[INET6_ADDRSTRLEN];
92         char **tokens, **toks;
93         char *send;
94         int i;
95
96         curaip = NULL;
97         tokens = g_strsplit(allowed_ips, ", ", -1);
98         for (i = 0; tokens[i]; i++) {
99                 toks = g_strsplit(tokens[i], "/", -1);
100                 if (g_strv_length(toks) != 2) {
101                         DBG("Ignore AllowedIPs value %s", tokens[i]);
102                         g_strfreev(toks);
103                         continue;
104                 }
105
106                 allowedip = g_malloc0(sizeof(*allowedip));
107
108                 if (inet_pton(AF_INET, toks[0], buf) == 1) {
109                         allowedip->family = AF_INET;
110                         memcpy(&allowedip->ip4, buf, sizeof(allowedip->ip4));
111                 } else if (inet_pton(AF_INET6, toks[0], buf) == 1) {
112                         allowedip->family = AF_INET6;
113                         memcpy(&allowedip->ip6, buf, sizeof(allowedip->ip6));
114                 } else {
115                         DBG("Ignore AllowedIPs value %s", tokens[i]);
116                         g_free(allowedip);
117                         g_strfreev(toks);
118                         continue;
119                 }
120
121                 allowedip->cidr = g_ascii_strtoull(toks[1], &send, 10);
122
123                 if (!curaip)
124                         peer->first_allowedip = allowedip;
125                 else
126                         curaip->next_allowedip = allowedip;
127
128                 curaip = allowedip;
129         }
130
131         peer->last_allowedip = curaip;
132         g_strfreev(tokens);
133
134         return 0;
135 }
136
137 static int parse_endpoint(const char *host, const char *port, struct sockaddr_u *addr)
138 {
139         struct addrinfo hints;
140         struct addrinfo *result, *rp;
141         int sk;
142
143         memset(&hints, 0, sizeof(struct addrinfo));
144         hints.ai_family = AF_UNSPEC;
145         hints.ai_socktype = SOCK_DGRAM;
146         hints.ai_flags = 0;
147         hints.ai_protocol = 0;
148
149         if (getaddrinfo(host, port, &hints, &result) < 0) {
150                 DBG("Failed to resolve host address");
151                 return -EINVAL;
152         }
153
154         for (rp = result; rp; rp = rp->ai_next) {
155                 sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
156                 if (sk < 0)
157                         continue;
158                 if (connect(sk, rp->ai_addr, rp->ai_addrlen) != -1) {
159                         /* success */
160                         close(sk);
161                         break;
162                 }
163
164                 close(sk);
165         }
166
167         if (!rp) {
168                 freeaddrinfo(result);
169                 return -EINVAL;
170         }
171
172         memcpy(addr, rp->ai_addr, rp->ai_addrlen);
173         freeaddrinfo(result);
174
175         return 0;
176 }
177
178 static int parse_address(const char *address, const char *gateway,
179                 struct connman_ipaddress **ipaddress)
180 {
181         char buf[INET6_ADDRSTRLEN];
182         unsigned char prefixlen;
183         char **tokens;
184         char *end, *netmask;
185         int err;
186
187         tokens = g_strsplit(address, "/", -1);
188         if (g_strv_length(tokens) != 2) {
189                 g_strfreev(tokens);
190                 return -EINVAL;
191         }
192
193         prefixlen = g_ascii_strtoull(tokens[1], &end, 10);
194
195         if (inet_pton(AF_INET, tokens[0], buf) == 1) {
196                 netmask = g_strdup_printf("%d.%d.%d.%d",
197                                 ((0xffffffff << (32 - prefixlen)) >> 24) & 0xff,
198                                 ((0xffffffff << (32 - prefixlen)) >> 16) & 0xff,
199                                 ((0xffffffff << (32 - prefixlen)) >> 8) & 0xff,
200                                 ((0xffffffff << (32 - prefixlen)) >> 0) & 0xff);
201
202                 *ipaddress = connman_ipaddress_alloc(AF_INET);
203                 err = connman_ipaddress_set_ipv4(*ipaddress, tokens[0],
204                                                 netmask, gateway);
205                 g_free(netmask);
206         } else if (inet_pton(AF_INET6, tokens[0], buf) == 1) {
207                 *ipaddress = connman_ipaddress_alloc(AF_INET6);
208                 err = connman_ipaddress_set_ipv6(*ipaddress, tokens[0],
209                                                 prefixlen, gateway);
210         } else {
211                 DBG("Invalid Wireguard.Address value");
212                 err = -EINVAL;
213         }
214
215         connman_ipaddress_set_p2p(*ipaddress, true);
216
217         g_strfreev(tokens);
218         if (err)
219                 connman_ipaddress_free(*ipaddress);
220
221         return err;
222 }
223
224 struct ifname_data {
225         char *ifname;
226         bool found;
227 };
228
229 static void ifname_check_cb(int index, void *user_data)
230 {
231         struct ifname_data *data = (struct ifname_data *)user_data;
232         char *ifname;
233
234         ifname = connman_inet_ifname(index);
235
236         if (!g_strcmp0(ifname, data->ifname))
237                 data->found = true;
238 }
239
240 static char *get_ifname(void)
241 {
242         struct ifname_data data;
243         int i;
244
245         for (i = 0; i < 256; i++) {
246                 data.ifname = g_strdup_printf("wg%d", i);
247                 data.found = false;
248                 vpn_ipconfig_foreach(ifname_check_cb, &data);
249
250                 if (!data.found)
251                         return data.ifname;
252
253                 g_free(data.ifname);
254         }
255
256         return NULL;
257 }
258
259 static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b)
260 {
261         if (a->sa.sa_family != b->sa.sa_family)
262                 return false;
263
264         if (a->sa.sa_family == AF_INET)
265                 return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in));
266         else if (a->sa.sa_family == AF_INET6)
267                 return !memcmp(a->sin6.sin6_addr.s6_addr,
268                                 b->sin6.sin6_addr.s6_addr,
269                                 sizeof(a->sin6.sin6_addr.s6_addr));
270
271         return false;
272 }
273
274 static gboolean wg_dns_reresolve_cb(gpointer user_data)
275 {
276         struct wireguard_info *info = user_data;
277         struct sockaddr_u addr;
278         int err;
279
280         DBG("");
281
282         err = parse_endpoint(info->endpoint_fqdn,
283                         info->port, &addr);
284         if (err)
285                 return TRUE;
286
287         if (sockaddr_cmp_addr(&addr,
288                         (struct sockaddr_u *)&info->peer.endpoint.addr))
289                 return TRUE;
290
291         if (addr.sa.sa_family == AF_INET)
292                 memcpy(&info->peer.endpoint.addr, &addr.sin,
293                         sizeof(info->peer.endpoint.addr4));
294         else
295                 memcpy(&info->peer.endpoint.addr, &addr.sin6,
296                         sizeof(info->peer.endpoint.addr6));
297
298         DBG("Endpoint address has changed, udpate WireGuard device");
299         err = wg_set_device(&info->device);
300         if (err)
301                 DBG("Failed to update Endpoint address for WireGuard device %s",
302                         info->device.name);
303
304         return TRUE;
305 }
306
307 static int wg_connect(struct vpn_provider *provider,
308                         struct connman_task *task, const char *if_name,
309                         vpn_provider_connect_cb_t cb,
310                         const char *dbus_sender, void *user_data)
311 {
312         struct connman_ipaddress *ipaddress = NULL;
313         struct wireguard_info *info;
314         const char *option, *gateway;
315         char *ifname;
316         int err = -EINVAL;
317
318         info = g_malloc0(sizeof(struct wireguard_info));
319         info->peer.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS;
320         info->device.flags = WGDEVICE_HAS_PRIVATE_KEY;
321         info->device.first_peer = &info->peer;
322         info->device.last_peer = &info->peer;
323
324         vpn_provider_set_plugin_data(provider, info);
325
326         option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
327         if (option) {
328                 char *end;
329                 info->device.listen_port = g_ascii_strtoull(option, &end, 10);
330                 info->device.flags |= WGDEVICE_HAS_LISTEN_PORT;
331         }
332
333         option = vpn_provider_get_string(provider, "WireGuard.DNS");
334         if (option) {
335                 err = vpn_provider_set_nameservers(provider, option);
336                 if (err)
337                         goto done;
338         }
339
340         option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
341         if (!option) {
342                 DBG("WireGuard.PrivateKey is missing");
343                 goto done;
344         }
345         err = parse_key(option, info->device.private_key);
346         if (err)
347                 goto done;
348
349         option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
350         if (!option) {
351                 DBG("WireGuard.PublicKey is missing");
352                 goto done;
353         }
354         err = parse_key(option, info->peer.public_key);
355         if (err)
356                 goto done;
357
358         option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
359         if (option) {
360                 info->peer.flags |= WGPEER_HAS_PRESHARED_KEY;
361                 err = parse_key(option, info->peer.preshared_key);
362                 if (err)
363                         goto done;
364         }
365
366         option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
367         if (!option) {
368                 DBG("WireGuard.AllowedIPs is missing");
369                 goto done;
370         }
371         err = parse_allowed_ips(option, &info->peer);
372         if (err)
373                 goto done;
374
375         option = vpn_provider_get_string(provider,
376                                         "WireGuard.PersistentKeepalive");
377         if (option) {
378                 char *end;
379                 info->peer.persistent_keepalive_interval =
380                         g_ascii_strtoull(option, &end, 10);
381                 info->peer.flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
382         }
383
384         option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
385         if (!option)
386                 option = "51820";
387
388         gateway = vpn_provider_get_string(provider, "Host");
389         err = parse_endpoint(gateway, option,
390                         (struct sockaddr_u *)&info->peer.endpoint.addr);
391         if (err)
392                 goto done;
393
394         info->endpoint_fqdn = g_strdup(gateway);
395         info->port = g_strdup(option);
396
397         option = vpn_provider_get_string(provider, "WireGuard.Address");
398         if (!option) {
399                 DBG("Missing WireGuard.Address configuration");
400                 goto done;
401         }
402         err = parse_address(option, gateway, &ipaddress);
403         if (err)
404                 goto done;
405
406         ifname = get_ifname();
407         if (!ifname) {
408                 DBG("Failed to find an usable device name");
409                 err = -ENOENT;
410                 goto done;
411         }
412         stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1);
413         g_free(ifname);
414
415         err = wg_add_device(info->device.name);
416         if (err) {
417                 DBG("Failed to creating WireGuard device %s", info->device.name);
418                 goto done;
419         }
420
421         err = wg_set_device(&info->device);
422         if (err) {
423                 DBG("Failed to configure WireGuard device %s", info->device.name);
424                 wg_del_device(info->device.name);
425         }
426
427         vpn_set_ifname(provider, info->device.name);
428         if (ipaddress)
429                 vpn_provider_set_ipaddress(provider, ipaddress);
430
431 done:
432         if (cb)
433                 cb(provider, user_data, err);
434
435         connman_ipaddress_free(ipaddress);
436
437         if (!err)
438                 info->reresolve_id =
439                         g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
440                                                 wg_dns_reresolve_cb, info);
441
442         return err;
443 }
444
445 static void wg_disconnect(struct vpn_provider *provider)
446 {
447         struct wireguard_info *info;
448
449         info = vpn_provider_get_plugin_data(provider);
450         if (!info)
451                 return;
452
453         if (info->reresolve_id > 0)
454                 g_source_remove(info->reresolve_id);
455
456         vpn_provider_set_plugin_data(provider, NULL);
457
458         wg_del_device(info->device.name);
459
460         g_free(info->endpoint_fqdn);
461         g_free(info->port);
462         g_free(info);
463 }
464
465 static struct vpn_driver vpn_driver = {
466         .flags          = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON,
467         .connect        = wg_connect,
468         .disconnect     = wg_disconnect,
469 };
470
471 static int wg_init(void)
472 {
473         return vpn_register("wireguard", &vpn_driver, NULL);
474 }
475
476 static void wg_exit(void)
477 {
478         vpn_unregister("wireguard");
479 }
480
481 CONNMAN_PLUGIN_DEFINE(wireguard, "WireGuard VPN plugin", VERSION,
482         CONNMAN_PLUGIN_PRIORITY_DEFAULT, wg_init, wg_exit)