~ubuntu-branches/ubuntu/utopic/network-manager-openvpn/utopic

« back to all changes in this revision

Viewing changes to .pc/enable_ipv6.patch/src/nm-openvpn-service-openvpn-helper.c

  • Committer: Package Import Robot
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2014-03-06 11:58:16 UTC
  • mfrom: (15.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140306115816-moi5xjroohn8r31x
Tags: 0.9.8.2-1ubuntu3
debian/patches/enable_ipv6.patch: allow openvpn to tunnel IPv6.
(LP: #777161)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
2
/* nm-openvpn-service-openvpn-helper - helper called after OpenVPN established
 
3
 * a connection, uses DBUS to send information back to nm-openvpn-service
 
4
 *
 
5
 * Tim Niemueller [www.niemueller.de]
 
6
 * Based on work by Dan Williams <dcbw@redhat.com>
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License along
 
19
 * with this program; if not, write to the Free Software Foundation, Inc.,
 
20
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
21
 *
 
22
 * (C) Copyright 2005 Red Hat, Inc.
 
23
 * (C) Copyright 2005 Tim Niemueller
 
24
 *
 
25
 * $Id: nm-openvpn-service-openvpn-helper.c 4170 2008-10-11 14:44:45Z dcbw $
 
26
 * 
 
27
 */
 
28
 
 
29
#include <glib.h>
 
30
#include <stdlib.h>
 
31
#include <unistd.h>
 
32
#include <stdio.h>
 
33
#include <string.h>
 
34
#include <errno.h>
 
35
#include <regex.h>
 
36
#include <sys/socket.h>
 
37
#include <netinet/in.h>
 
38
#include <arpa/inet.h>
 
39
#include <ctype.h>
 
40
#include <netdb.h>
 
41
 
 
42
#include <dbus/dbus.h>
 
43
#include <dbus/dbus-glib-lowlevel.h>
 
44
#include <dbus/dbus-glib.h>
 
45
#include <NetworkManager.h>
 
46
 
 
47
#include "nm-openvpn-service.h"
 
48
#include "nm-utils.h"
 
49
 
 
50
extern char **environ;
 
51
 
 
52
/* These are here because nm-dbus-glib-types.h isn't exported */
 
53
#define DBUS_TYPE_G_ARRAY_OF_UINT          (dbus_g_type_get_collection ("GArray", G_TYPE_UINT))
 
54
#define DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_ARRAY_OF_UINT))
 
55
#define DBUS_TYPE_G_PTR_ARRAY_OF_STRING    (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING))
 
56
 
 
57
static void
 
58
helper_failed (DBusGConnection *connection, const char *reason)
 
59
{
 
60
        DBusGProxy *proxy;
 
61
        GError *err = NULL;
 
62
 
 
63
        g_warning ("nm-openvpn-service-openvpn-helper did not receive a valid %s from openvpn", reason);
 
64
 
 
65
        proxy = dbus_g_proxy_new_for_name (connection,
 
66
                                                                NM_DBUS_SERVICE_OPENVPN,
 
67
                                                                NM_VPN_DBUS_PLUGIN_PATH,
 
68
                                                                NM_VPN_DBUS_PLUGIN_INTERFACE);
 
69
 
 
70
        dbus_g_proxy_call (proxy, "SetFailure", &err,
 
71
                                    G_TYPE_STRING, reason,
 
72
                                    G_TYPE_INVALID,
 
73
                                    G_TYPE_INVALID);
 
74
 
 
75
        if (err) {
 
76
                g_warning ("Could not send failure information: %s", err->message);
 
77
                g_error_free (err);
 
78
        }
 
79
 
 
80
        g_object_unref (proxy);
 
81
 
 
82
        exit (1);
 
83
}
 
84
 
 
85
static void
 
86
send_ip4_config (DBusGConnection *connection, GHashTable *config)
 
87
{
 
88
        DBusGProxy *proxy;
 
89
        GError *err = NULL;
 
90
 
 
91
        proxy = dbus_g_proxy_new_for_name (connection,
 
92
                                                                NM_DBUS_SERVICE_OPENVPN,
 
93
                                                                NM_VPN_DBUS_PLUGIN_PATH,
 
94
                                                                NM_VPN_DBUS_PLUGIN_INTERFACE);
 
95
 
 
96
        dbus_g_proxy_call (proxy, "SetIp4Config", &err,
 
97
                                    dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
 
98
                                    config,
 
99
                                    G_TYPE_INVALID,
 
100
                                    G_TYPE_INVALID);
 
101
 
 
102
        if (err) {
 
103
                g_warning ("Could not send failure information: %s", err->message);
 
104
                g_error_free (err);
 
105
        }
 
106
 
 
107
        g_object_unref (proxy);
 
108
}
 
109
 
 
110
static GValue *
 
111
str_to_gvalue (const char *str, gboolean try_convert)
 
112
{
 
113
        GValue *val;
 
114
 
 
115
        /* Empty */
 
116
        if (!str || strlen (str) < 1)
 
117
                return NULL;
 
118
 
 
119
        if (!g_utf8_validate (str, -1, NULL)) {
 
120
                if (try_convert && !(str = g_convert (str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL)))
 
121
                        str = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
 
122
 
 
123
                if (!str)
 
124
                        /* Invalid */
 
125
                        return NULL;
 
126
        }
 
127
 
 
128
        val = g_slice_new0 (GValue);
 
129
        g_value_init (val, G_TYPE_STRING);
 
130
        g_value_set_string (val, str);
 
131
 
 
132
        return val;
 
133
}
 
134
 
 
135
static GValue *
 
136
uint_to_gvalue (guint32 num)
 
137
{
 
138
        GValue *val;
 
139
 
 
140
        if (num == 0)
 
141
                return NULL;
 
142
 
 
143
        val = g_slice_new0 (GValue);
 
144
        g_value_init (val, G_TYPE_UINT);
 
145
        g_value_set_uint (val, num);
 
146
 
 
147
        return val;
 
148
}
 
149
 
 
150
static GValue *
 
151
addr_to_gvalue (const char *str)
 
152
{
 
153
        struct in_addr  temp_addr;
 
154
        GValue *val;
 
155
 
 
156
        /* Empty */
 
157
        if (!str || strlen (str) < 1)
 
158
                return NULL;
 
159
 
 
160
        if (inet_pton (AF_INET, str, &temp_addr) <= 0)
 
161
                return NULL;
 
162
 
 
163
        val = g_slice_new0 (GValue);
 
164
        g_value_init (val, G_TYPE_UINT);
 
165
        g_value_set_uint (val, temp_addr.s_addr);
 
166
 
 
167
        return val;
 
168
}
 
169
 
 
170
static GValue *
 
171
parse_addr_list (GValue *value_array, const char *str)
 
172
{
 
173
        char **split;
 
174
        int i;
 
175
        struct in_addr  temp_addr;
 
176
        GArray *array;
 
177
 
 
178
        /* Empty */
 
179
        if (!str || strlen (str) < 1)
 
180
                return value_array;
 
181
 
 
182
        if (value_array)
 
183
                array = (GArray *) g_value_get_boxed (value_array);
 
184
        else
 
185
                array = g_array_new (FALSE, FALSE, sizeof (guint));
 
186
 
 
187
        split = g_strsplit (str, " ", -1);
 
188
        for (i = 0; split[i]; i++) {
 
189
                if (inet_pton (AF_INET, split[i], &temp_addr) > 0)
 
190
                        g_array_append_val (array, temp_addr.s_addr);
 
191
        }
 
192
 
 
193
        g_strfreev (split);
 
194
 
 
195
        if (!value_array && array->len > 0) {
 
196
                value_array = g_slice_new0 (GValue);
 
197
                g_value_init (value_array, DBUS_TYPE_G_UINT_ARRAY);
 
198
                g_value_set_boxed (value_array, array);
 
199
        }
 
200
 
 
201
        return value_array;
 
202
}
 
203
 
 
204
static inline gboolean
 
205
is_domain_valid (const char *str)
 
206
{
 
207
        return (str && (strlen(str) >= 1) && (strlen(str) <= 255));
 
208
}
 
209
 
 
210
static GValue *
 
211
get_routes (void)
 
212
{
 
213
        GValue *value = NULL;
 
214
        GPtrArray *routes;
 
215
        char *tmp;
 
216
        int i;
 
217
 
 
218
#define BUFLEN 256
 
219
 
 
220
        routes = g_ptr_array_new ();
 
221
 
 
222
        for (i = 1; i < 256; i++) {
 
223
                GArray *array;
 
224
                char buf[BUFLEN];
 
225
                struct in_addr network;
 
226
                struct in_addr netmask;
 
227
                struct in_addr gateway = { 0, };
 
228
                guint32 prefix, metric = 0;
 
229
 
 
230
                snprintf (buf, BUFLEN, "route_network_%d", i);
 
231
                tmp = getenv (buf);
 
232
                if (!tmp || strlen (tmp) < 1)
 
233
                        break;
 
234
 
 
235
                if (inet_pton (AF_INET, tmp, &network) <= 0) {
 
236
                        g_warning ("Ignoring invalid static route address '%s'", tmp ? tmp : "NULL");
 
237
                        continue;
 
238
                }
 
239
 
 
240
                snprintf (buf, BUFLEN, "route_netmask_%d", i);
 
241
                tmp = getenv (buf);
 
242
                if (!tmp || inet_pton (AF_INET, tmp, &netmask) <= 0) {
 
243
                        g_warning ("Ignoring invalid static route netmask '%s'", tmp ? tmp : "NULL");
 
244
                        continue;
 
245
                }
 
246
 
 
247
                snprintf (buf, BUFLEN, "route_gateway_%d", i);
 
248
                tmp = getenv (buf);
 
249
                /* gateway can be missing */
 
250
                if (tmp && (inet_pton (AF_INET, tmp, &gateway) <= 0)) {
 
251
                        g_warning ("Ignoring invalid static route gateway '%s'", tmp ? tmp : "NULL");
 
252
                        continue;
 
253
                }
 
254
 
 
255
                snprintf (buf, BUFLEN, "route_metric_%d", i);
 
256
                tmp = getenv (buf);
 
257
                /* metric can be missing */
 
258
                if (tmp && strlen (tmp)) {
 
259
                        long int tmp_metric;
 
260
 
 
261
                        errno = 0;
 
262
                        tmp_metric = strtol (tmp, NULL, 10);
 
263
                        if (errno || tmp_metric < 0 || tmp_metric > G_MAXUINT32) {
 
264
                                g_warning ("Ignoring invalid static route metric '%s'", tmp);
 
265
                                continue;
 
266
                        }
 
267
                        metric = (guint32) tmp_metric;
 
268
                }
 
269
 
 
270
                array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
 
271
                g_array_append_val (array, network.s_addr);
 
272
                prefix = nm_utils_ip4_netmask_to_prefix (netmask.s_addr);
 
273
                g_array_append_val (array, prefix);
 
274
                g_array_append_val (array, gateway.s_addr);
 
275
                g_array_append_val (array, metric);
 
276
                g_ptr_array_add (routes, array);
 
277
        }
 
278
 
 
279
        if (routes->len > 0) {
 
280
                value = g_new0 (GValue, 1);
 
281
                g_value_init (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT);
 
282
                g_value_take_boxed (value, routes);
 
283
        } else
 
284
                g_ptr_array_free (routes, TRUE);
 
285
 
 
286
        return value;
 
287
}
 
288
 
 
289
static GValue *
 
290
trusted_remote_to_gvalue (void)
 
291
{
 
292
        char *tmp;
 
293
        GValue *val = NULL;
 
294
        struct in_addr addr;
 
295
        const char *p;
 
296
        gboolean is_name = FALSE;
 
297
 
 
298
        tmp = getenv ("trusted_ip");
 
299
        if (!tmp)
 
300
                tmp = getenv ("remote_1");
 
301
        if (!tmp) {
 
302
                g_warning ("%s: did not receive remote gateway address", __func__);
 
303
                return NULL;
 
304
        }
 
305
 
 
306
        /* Check if it seems to be a hostname hostname */
 
307
        p = tmp;
 
308
        while (*p) {
 
309
                if (*p != '.' && !isdigit (*p)) {
 
310
                        is_name = TRUE;
 
311
                        break;
 
312
                }
 
313
                p++;
 
314
        }
 
315
 
 
316
        /* Resolve a hostname if required */
 
317
        if (is_name) {
 
318
                struct addrinfo hints;
 
319
                struct addrinfo *result = NULL, *rp;
 
320
                int err;
 
321
 
 
322
                memset (&hints, 0, sizeof (hints));
 
323
 
 
324
                hints.ai_family = AF_INET;
 
325
                hints.ai_flags = AI_ADDRCONFIG;
 
326
                err = getaddrinfo (tmp, NULL, &hints, &result);
 
327
                if (err != 0) {
 
328
                        g_warning ("%s: failed to look up VPN gateway address '%s' (%d)",
 
329
                                   __func__, tmp, err);
 
330
                        return NULL;
 
331
                }
 
332
 
 
333
                /* FIXME: so what if the name resolves to multiple IP addresses?  We
 
334
                 * don't know which one pptp decided to use so we could end up using a
 
335
                 * different one here, and the VPN just won't work.
 
336
                 */
 
337
                for (rp = result; rp; rp = rp->ai_next) {
 
338
                        if (   (rp->ai_family == AF_INET)
 
339
                            && (rp->ai_addrlen == sizeof (struct sockaddr_in))) {
 
340
                                struct sockaddr_in *inptr = (struct sockaddr_in *) rp->ai_addr;
 
341
 
 
342
                                memcpy (&addr, &(inptr->sin_addr), sizeof (struct in_addr));
 
343
                                break;
 
344
                        }
 
345
                }
 
346
 
 
347
                freeaddrinfo (result);
 
348
        } else {
 
349
                errno = 0;
 
350
                if (inet_pton (AF_INET, tmp, &addr) <= 0) {
 
351
                        g_warning ("%s: failed to convert VPN gateway address '%s' (%d)",
 
352
                                   __func__, tmp, errno);
 
353
                        return NULL;
 
354
                }
 
355
        }
 
356
 
 
357
        if (addr.s_addr != 0) {
 
358
                val = g_slice_new0 (GValue);
 
359
                g_value_init (val, G_TYPE_UINT);
 
360
                g_value_set_uint (val, addr.s_addr);
 
361
        } else {
 
362
                g_warning ("%s: failed to convert or look up VPN gateway address '%s'",
 
363
                           __func__, tmp);
 
364
        }
 
365
 
 
366
        return val;
 
367
}
 
368
 
 
369
int
 
370
main (int argc, char *argv[])
 
371
{
 
372
        DBusGConnection *connection;
 
373
        GHashTable *config;
 
374
        char *tmp;
 
375
        GValue *val;
 
376
        int i;
 
377
        GError *err = NULL;
 
378
        GValue *dns_list = NULL;
 
379
        GValue *nbns_list = NULL;
 
380
        GPtrArray *dns_domains = NULL;
 
381
        struct in_addr temp_addr;
 
382
        gboolean tapdev = FALSE;
 
383
        char **iter;
 
384
 
 
385
#if !GLIB_CHECK_VERSION (2, 35, 0)
 
386
        g_type_init ();
 
387
#endif
 
388
 
 
389
        connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
 
390
        if (!connection) {
 
391
                g_warning ("Could not get the system bus: %s", err->message);
 
392
                exit (1);
 
393
        }
 
394
 
 
395
        if (argc >= 2 && !g_strcmp0 (argv[1], "--helper-debug")) {
 
396
                g_message ("openvpn script environment ---------------------------");
 
397
                iter = environ;
 
398
                while (iter && *iter)
 
399
                        g_print ("%s\n", *iter++);
 
400
                g_message ("------------------------------------------------------");
 
401
        }
 
402
 
 
403
        config = g_hash_table_new (g_str_hash, g_str_equal);
 
404
 
 
405
        /* External world-visible VPN gateway */
 
406
        val = trusted_remote_to_gvalue ();
 
407
        if (val)
 
408
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, val);
 
409
        else
 
410
                helper_failed (connection, "VPN Gateway");
 
411
 
 
412
        /* Internal VPN subnet gateway */
 
413
        val = addr_to_gvalue (getenv ("route_vpn_gateway"));
 
414
        if (val)
 
415
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
 
416
 
 
417
        /* VPN device */
 
418
        tmp = getenv ("dev");
 
419
        val = str_to_gvalue (tmp, FALSE);
 
420
        if (val)
 
421
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
 
422
        else
 
423
                helper_failed (connection, "Tunnel Device");
 
424
 
 
425
        if (strncmp (tmp, "tap", 3) == 0)
 
426
                tapdev = TRUE;
 
427
 
 
428
        /* IP address */
 
429
        val = addr_to_gvalue (getenv ("ifconfig_local"));
 
430
        if (val)
 
431
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
 
432
        else
 
433
                helper_failed (connection, "IP4 Address");
 
434
 
 
435
        /* PTP address; for vpnc PTP address == internal IP4 address */
 
436
        val = addr_to_gvalue (getenv ("ifconfig_remote"));
 
437
        if (val) {
 
438
                /* Sigh.  Openvpn added 'topology' stuff in 2.1 that changes the meaning
 
439
                 * of the ifconfig bits without actually telling you what they are
 
440
                 * supposed to mean; basically relying on specific 'ifconfig' behavior.
 
441
                 */
 
442
                tmp = getenv ("ifconfig_remote");
 
443
                if (tmp && !strncmp (tmp, "255.", 4)) {
 
444
                        guint32 addr;
 
445
 
 
446
                        /* probably a netmask, not a PTP address; topology == subnet */
 
447
                        addr = g_value_get_uint (val);
 
448
                        g_value_set_uint (val, nm_utils_ip4_netmask_to_prefix (addr));
 
449
                        g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
 
450
                } else
 
451
                        g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
 
452
        }
 
453
 
 
454
        /* Netmask
 
455
         *
 
456
         * Either TAP or TUN modes can have an arbitrary netmask in newer versions
 
457
         * of openvpn, while in older versions only TAP mode would.  So accept a
 
458
         * netmask if passed, otherwise default to /32 for TUN devices since they
 
459
         * are usually point-to-point.
 
460
         */
 
461
        tmp = getenv ("ifconfig_netmask");
 
462
        if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) {
 
463
                val = g_slice_new0 (GValue);
 
464
                g_value_init (val, G_TYPE_UINT);
 
465
                g_value_set_uint (val, nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
 
466
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
 
467
        } else if (!tapdev) {
 
468
                if (!g_hash_table_lookup (config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX)) {
 
469
                        val = g_slice_new0 (GValue);
 
470
                        g_value_init (val, G_TYPE_UINT);
 
471
                        g_value_set_uint (val, 32);
 
472
                        g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
 
473
                }
 
474
        } else
 
475
                g_warning ("No IP4 netmask/prefix (missing or invalid 'ifconfig_netmask')");
 
476
 
 
477
        val = get_routes ();
 
478
        if (val)
 
479
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val);
 
480
 
 
481
        /* DNS and WINS servers */
 
482
        dns_domains = g_ptr_array_sized_new (3);
 
483
        for (i = 1; i < 256; i++) {
 
484
                char *env_name;
 
485
 
 
486
                env_name = g_strdup_printf ("foreign_option_%d", i);
 
487
                tmp = getenv (env_name);
 
488
                g_free (env_name);
 
489
 
 
490
                if (!tmp || strlen (tmp) < 1)
 
491
                        break;
 
492
 
 
493
                if (!g_str_has_prefix (tmp, "dhcp-option "))
 
494
                        continue;
 
495
 
 
496
                tmp += 12; /* strlen ("dhcp-option ") */
 
497
 
 
498
                if (g_str_has_prefix (tmp, "DNS "))
 
499
                        dns_list = parse_addr_list (dns_list, tmp + 4);
 
500
                else if (g_str_has_prefix (tmp, "WINS "))
 
501
                        nbns_list = parse_addr_list (nbns_list, tmp + 5);
 
502
                else if (g_str_has_prefix (tmp, "DOMAIN ") && is_domain_valid (tmp + 7))
 
503
                        g_ptr_array_add (dns_domains, tmp + 7);
 
504
        }
 
505
 
 
506
        if (dns_list)
 
507
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_DNS, dns_list);
 
508
        if (nbns_list)
 
509
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, nbns_list);
 
510
        if (dns_domains->len) {
 
511
                val = g_slice_new0 (GValue);
 
512
                g_value_init (val, DBUS_TYPE_G_PTR_ARRAY_OF_STRING);
 
513
                g_value_take_boxed (val, dns_domains);
 
514
                g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, val);
 
515
        } else
 
516
                g_ptr_array_free (dns_domains, TRUE);
 
517
 
 
518
        /* Tunnel MTU */
 
519
        tmp = getenv ("tun_mtu");
 
520
        if (tmp && strlen (tmp)) {
 
521
                long int mtu;
 
522
 
 
523
                errno = 0;
 
524
                mtu = strtol (tmp, NULL, 10);
 
525
                if (errno || mtu < 0 || mtu > 20000) {
 
526
                        g_warning ("Ignoring invalid tunnel MTU '%s'", tmp);
 
527
                } else {
 
528
                        val = uint_to_gvalue ((guint32) mtu);
 
529
                        g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, val);
 
530
                }
 
531
        }
 
532
 
 
533
        /* Send the config info to nm-openvpn-service */
 
534
        send_ip4_config (connection, config);
 
535
 
 
536
        return 0;
 
537
}