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
5
* Tim Niemueller [www.niemueller.de]
6
* Based on work by Dan Williams <dcbw@redhat.com>
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.
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.
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.
22
* (C) Copyright 2005 Red Hat, Inc.
23
* (C) Copyright 2005 Tim Niemueller
25
* $Id: nm-openvpn-service-openvpn-helper.c 4170 2008-10-11 14:44:45Z dcbw $
36
#include <sys/socket.h>
37
#include <netinet/in.h>
38
#include <arpa/inet.h>
42
#include <dbus/dbus.h>
43
#include <dbus/dbus-glib-lowlevel.h>
44
#include <dbus/dbus-glib.h>
45
#include <NetworkManager.h>
47
#include "nm-openvpn-service.h"
50
extern char **environ;
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))
58
helper_failed (DBusGConnection *connection, const char *reason)
63
g_warning ("nm-openvpn-service-openvpn-helper did not receive a valid %s from openvpn", reason);
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);
70
dbus_g_proxy_call (proxy, "SetFailure", &err,
71
G_TYPE_STRING, reason,
76
g_warning ("Could not send failure information: %s", err->message);
80
g_object_unref (proxy);
86
send_ip4_config (DBusGConnection *connection, GHashTable *config)
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);
96
dbus_g_proxy_call (proxy, "SetIp4Config", &err,
97
dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
103
g_warning ("Could not send failure information: %s", err->message);
107
g_object_unref (proxy);
111
str_to_gvalue (const char *str, gboolean try_convert)
116
if (!str || strlen (str) < 1)
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);
128
val = g_slice_new0 (GValue);
129
g_value_init (val, G_TYPE_STRING);
130
g_value_set_string (val, str);
136
uint_to_gvalue (guint32 num)
143
val = g_slice_new0 (GValue);
144
g_value_init (val, G_TYPE_UINT);
145
g_value_set_uint (val, num);
151
addr_to_gvalue (const char *str)
153
struct in_addr temp_addr;
157
if (!str || strlen (str) < 1)
160
if (inet_pton (AF_INET, str, &temp_addr) <= 0)
163
val = g_slice_new0 (GValue);
164
g_value_init (val, G_TYPE_UINT);
165
g_value_set_uint (val, temp_addr.s_addr);
171
parse_addr_list (GValue *value_array, const char *str)
175
struct in_addr temp_addr;
179
if (!str || strlen (str) < 1)
183
array = (GArray *) g_value_get_boxed (value_array);
185
array = g_array_new (FALSE, FALSE, sizeof (guint));
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);
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);
204
static inline gboolean
205
is_domain_valid (const char *str)
207
return (str && (strlen(str) >= 1) && (strlen(str) <= 255));
213
GValue *value = NULL;
220
routes = g_ptr_array_new ();
222
for (i = 1; i < 256; i++) {
225
struct in_addr network;
226
struct in_addr netmask;
227
struct in_addr gateway = { 0, };
228
guint32 prefix, metric = 0;
230
snprintf (buf, BUFLEN, "route_network_%d", i);
232
if (!tmp || strlen (tmp) < 1)
235
if (inet_pton (AF_INET, tmp, &network) <= 0) {
236
g_warning ("Ignoring invalid static route address '%s'", tmp ? tmp : "NULL");
240
snprintf (buf, BUFLEN, "route_netmask_%d", i);
242
if (!tmp || inet_pton (AF_INET, tmp, &netmask) <= 0) {
243
g_warning ("Ignoring invalid static route netmask '%s'", tmp ? tmp : "NULL");
247
snprintf (buf, BUFLEN, "route_gateway_%d", i);
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");
255
snprintf (buf, BUFLEN, "route_metric_%d", i);
257
/* metric can be missing */
258
if (tmp && strlen (tmp)) {
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);
267
metric = (guint32) tmp_metric;
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);
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);
284
g_ptr_array_free (routes, TRUE);
290
trusted_remote_to_gvalue (void)
296
gboolean is_name = FALSE;
298
tmp = getenv ("trusted_ip");
300
tmp = getenv ("remote_1");
302
g_warning ("%s: did not receive remote gateway address", __func__);
306
/* Check if it seems to be a hostname hostname */
309
if (*p != '.' && !isdigit (*p)) {
316
/* Resolve a hostname if required */
318
struct addrinfo hints;
319
struct addrinfo *result = NULL, *rp;
322
memset (&hints, 0, sizeof (hints));
324
hints.ai_family = AF_INET;
325
hints.ai_flags = AI_ADDRCONFIG;
326
err = getaddrinfo (tmp, NULL, &hints, &result);
328
g_warning ("%s: failed to look up VPN gateway address '%s' (%d)",
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.
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;
342
memcpy (&addr, &(inptr->sin_addr), sizeof (struct in_addr));
347
freeaddrinfo (result);
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);
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);
362
g_warning ("%s: failed to convert or look up VPN gateway address '%s'",
370
main (int argc, char *argv[])
372
DBusGConnection *connection;
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;
385
#if !GLIB_CHECK_VERSION (2, 35, 0)
389
connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
391
g_warning ("Could not get the system bus: %s", err->message);
395
if (argc >= 2 && !g_strcmp0 (argv[1], "--helper-debug")) {
396
g_message ("openvpn script environment ---------------------------");
398
while (iter && *iter)
399
g_print ("%s\n", *iter++);
400
g_message ("------------------------------------------------------");
403
config = g_hash_table_new (g_str_hash, g_str_equal);
405
/* External world-visible VPN gateway */
406
val = trusted_remote_to_gvalue ();
408
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, val);
410
helper_failed (connection, "VPN Gateway");
412
/* Internal VPN subnet gateway */
413
val = addr_to_gvalue (getenv ("route_vpn_gateway"));
415
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
418
tmp = getenv ("dev");
419
val = str_to_gvalue (tmp, FALSE);
421
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
423
helper_failed (connection, "Tunnel Device");
425
if (strncmp (tmp, "tap", 3) == 0)
429
val = addr_to_gvalue (getenv ("ifconfig_local"));
431
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
433
helper_failed (connection, "IP4 Address");
435
/* PTP address; for vpnc PTP address == internal IP4 address */
436
val = addr_to_gvalue (getenv ("ifconfig_remote"));
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.
442
tmp = getenv ("ifconfig_remote");
443
if (tmp && !strncmp (tmp, "255.", 4)) {
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);
451
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
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.
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);
475
g_warning ("No IP4 netmask/prefix (missing or invalid 'ifconfig_netmask')");
479
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val);
481
/* DNS and WINS servers */
482
dns_domains = g_ptr_array_sized_new (3);
483
for (i = 1; i < 256; i++) {
486
env_name = g_strdup_printf ("foreign_option_%d", i);
487
tmp = getenv (env_name);
490
if (!tmp || strlen (tmp) < 1)
493
if (!g_str_has_prefix (tmp, "dhcp-option "))
496
tmp += 12; /* strlen ("dhcp-option ") */
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);
507
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_DNS, dns_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);
516
g_ptr_array_free (dns_domains, TRUE);
519
tmp = getenv ("tun_mtu");
520
if (tmp && strlen (tmp)) {
524
mtu = strtol (tmp, NULL, 10);
525
if (errno || mtu < 0 || mtu > 20000) {
526
g_warning ("Ignoring invalid tunnel MTU '%s'", tmp);
528
val = uint_to_gvalue ((guint32) mtu);
529
g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, val);
533
/* Send the config info to nm-openvpn-service */
534
send_ip4_config (connection, config);