[network-manager-openswan/lr/multiple-vpn: 1/3] service: process the configuration in the service, not the helper



commit 65707ef9dcc7a86ec7f22b8fa5a5f67bfe963663
Author: Lubomir Rintel <lkundrak v3 sk>
Date:   Wed Oct 28 12:26:01 2015 +0100

    service: process the configuration in the service, not the helper
    
    For multiple instance support we need to pass arguments to the helper. We use
    the ifupdown option for that.
    
    As we call our ifupdown script we need to manage the routes ourselves. That's
    probably a good idea anyway. However, the helper never knows all the routes.
    Therefore we have to move the logic of creating the IP4Config in the service.
    
    The service adds a new bus object for the helper to talk to and the helper just
    pushes in all the PLUTO_* environment variables to the service.
    
    It's probably cleaner that way anyway + our DBus exposed VPN configuration now
    makes sense. Yay!

 .gitignore                         |    2 +
 configure.ac                       |    2 +-
 nm-openswan-service.conf           |    1 +
 src/Makefile.am                    |   25 ++-
 src/nm-openswan-helper-service.xml |    9 +
 src/nm-openswan-service-helper.c   |  395 ++------------------------------
 src/nm-openswan-service.c          |  440 +++++++++++++++++++++++++++++++++++-
 src/nm-openswan-service.h          |    7 +-
 8 files changed, 499 insertions(+), 382 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 31bb06f..a50672f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,5 +36,7 @@ nm-openswan.desktop
 src/nm-openswan-service
 src/nm-openswan-service-helper
 src/show-xfrm
+src/nm-openswan-helper-service-dbus.c
+src/nm-openswan-helper-service-dbus.h
 
 /NetworkManager-openswan*.tar*
diff --git a/configure.ac b/configure.ac
index c9319ea..2947f61 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,7 +62,7 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
 IT_PROG_INTLTOOL([0.35])
 AM_GLIB_GNU_GETTEXT
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.32)
+PKG_CHECK_MODULES(GLIB, gio-unix-2.0 >= 2.32)
 GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32"
 
 PKG_CHECK_MODULES(LIBNL, libnl-3.0 >= 3.2.8)
diff --git a/nm-openswan-service.conf b/nm-openswan-service.conf
index 1e2ba06..4fe4e45 100644
--- a/nm-openswan-service.conf
+++ b/nm-openswan-service.conf
@@ -5,6 +5,7 @@
        <policy user="root">
                <allow own="org.freedesktop.NetworkManager.openswan"/>
                <allow send_destination="org.freedesktop.NetworkManager.openswan"/>
+               <allow send_interface="org.freedesktop.NetworkManager.openswan.helper"/>
        </policy>
        <policy context="default">
                <deny own="org.freedesktop.NetworkManager.openswan"/>
diff --git a/src/Makefile.am b/src/Makefile.am
index bb41a43..46a7018 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,6 +13,25 @@ AM_CPPFLAGS = \
 
 libexec_PROGRAMS = nm-openswan-service nm-openswan-service-helper
 
+# D-Bus stuff
+noinst_LTLIBRARIES = libnm-openswan-helper-service-dbus.la
+
+nodist_libnm_openswan_helper_service_dbus_la_SOURCES =         \
+       nm-openswan-helper-service-dbus.c                       \
+       nm-openswan-helper-service-dbus.h
+
+libnm_openswan_helper_service_dbus_la_CPPFLAGS = $(filter-out -DGLIB_VERSION_MAX_ALLOWED%,$(AM_CPPFLAGS))
+
+nm-openswan-helper-service-dbus.h: $(top_srcdir)/src/nm-openswan-helper-service.xml
+       $(AM_V_GEN) gdbus-codegen \
+               --generate-c-code $(basename $@) \
+               --c-namespace NMDBus \
+               --interface-prefix org.freedesktop.NetworkManager \
+               $<
+
+nm-openswan-helper-service-dbus.c:
+       @true
+
 nm_openswan_service_SOURCES = \
        nm-openswan-service.c \
        nm-openswan-service.h
@@ -20,6 +39,8 @@ nm_openswan_service_SOURCES = \
 nm_openswan_service_LDADD = \
        $(GLIB_LIBS) \
        $(LIBNM_LIBS) \
+       $(LIBNL_LIBS) \
+       libnm-openswan-helper-service-dbus.la \
        -lutil
 
 nm_openswan_service_helper_SOURCES = \
@@ -27,7 +48,7 @@ nm_openswan_service_helper_SOURCES = \
 
 nm_openswan_service_helper_LDADD = \
        $(LIBNM_LIBS) \
-       $(LIBNL_LIBS)
+       libnm-openswan-helper-service-dbus.la
 
 install-exec-hook:
        $(LN_S) -f $(DESTDIR)$(libexecdir)/nm-openswan-service-helper \
@@ -42,4 +63,6 @@ show_xfrm_SOURCES = show-xfrm.c
 
 show_xfrm_LDADD = $(LIBNL_LIBS) $(GLIB_LIBS)
 
+BUILT_SOURCES = nm-openswan-helper-service-dbus.h nm-openswan-helper-service-dbus.c
+
 CLEANFILES = *~
diff --git a/src/nm-openswan-helper-service.xml b/src/nm-openswan-helper-service.xml
new file mode 100644
index 0000000..6fd9c10
--- /dev/null
+++ b/src/nm-openswan-helper-service.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+  <interface name="org.freedesktop.NetworkManager.openswan.helper">
+    <method name="Callback">
+      <arg name="environment" type="a{ss}" direction="in"/>
+    </method>
+  </interface>
+</node>
diff --git a/src/nm-openswan-service-helper.c b/src/nm-openswan-service-helper.c
index cc2d175..b4cbded 100644
--- a/src/nm-openswan-service-helper.c
+++ b/src/nm-openswan-service-helper.c
@@ -1,8 +1,7 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* nm-openswan-service-helper - openswan integration with NetworkManager
  *
- * Dan Williams <dcbw redhat com>
- * Avesh Agarwal <avagarwa redhat com>
+ * Lubomir Rintel <lkundrak v3 sk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,400 +17,56 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2005 - 2014 Red Hat, Inc.
+ * Copyright 2015 Red Hat, Inc.
  */
 
-#define _GNU_SOURCE 1
-
 #include <glib.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-
-#include <netlink/netlink.h>
-#include <netlink/msg.h>
-
-#define _LINUX_IN6_H 1
-#include <linux/xfrm.h>
-
-#include <NetworkManager.h>
 
-#include <nm-vpn-service-plugin.h>
+#include "nm-openswan-helper-service-dbus.h"
 #include "nm-openswan-service.h"
-#include "nm-utils.h"
-
-static void
-helper_failed (GDBusProxy *proxy, const char *reason)
-{
-       GError *err = NULL;
-
-       g_warning ("This helper did not receive a valid %s from the IPSec daemon", reason);
-
-       if (!g_dbus_proxy_call_sync (proxy, "SetFailure",
-                                    g_variant_new ("(s)", reason),
-                                    G_DBUS_CALL_FLAGS_NONE, -1,
-                                    NULL,
-                                    &err)) {
-               g_warning ("Could not send failure information: %s", err->message);
-               g_error_free (err);
-       }
-
-       exit (1);
-}
-
-static void
-send_ip4_config (GDBusProxy *proxy, GVariant *config)
-{
-       GError *err = NULL;
-
-       if (!g_dbus_proxy_call_sync (proxy, "SetIp4Config",
-                                    g_variant_new ("(*)", config),
-                                    G_DBUS_CALL_FLAGS_NONE, -1,
-                                    NULL,
-                                    &err)) {
-               g_warning ("Could not send IPv4 configuration: %s", err->message);
-               g_error_free (err);
-       }
-}
-
-/********************************************************************/
-
-/* The various SWANs don't tell helper scripts whether upstream sent
- * specific subnets to be routed over the VPN (eg, CISCO_SPLIT_INC).
- * This is what we need to automatically determine 'never-default' behavior.
- * Instead, we have to inspect the kernel's SAD (Security Assocation Database)
- * for IPSec-secured routes pointing to the VPN gateway.
- */
-
-typedef struct {
-       struct in_addr gw4;
-       gboolean have_routes4;
-} RoutesInfo;
-
-static int
-verify_source (struct nl_msg *msg, gpointer user_data)
-{
-       struct ucred *creds = nlmsg_get_creds (msg);
-
-       if (!creds || creds->pid || creds->uid || creds->gid) {
-               if (creds) {
-                       g_warning ("netlink: received non-kernel message (pid %d uid %d gid %d)",
-                                  creds->pid, creds->uid, creds->gid);
-               } else
-                       g_warning ("netlink: received message without credentials");
-               return NL_STOP;
-       }
 
-       return NL_OK;
-}
-
-static struct nl_sock *
-setup_socket (void)
-{
-       struct nl_sock *sk;
-       int err;
-
-       sk = nl_socket_alloc ();
-       g_return_val_if_fail (sk, NULL);
-
-       /* Only ever accept messages from kernel */
-       err = nl_socket_modify_cb (sk, NL_CB_MSG_IN, NL_CB_CUSTOM, verify_source, NULL);
-       g_assert (!err);
-
-       err = nl_connect (sk, NETLINK_XFRM);
-       g_assert (!err);
-       err = nl_socket_set_passcred (sk, 1);
-       g_assert (!err);
-
-       return sk;
-}
-
-static int
-parse_reply (struct nl_msg *msg, void *arg)
-{
-       RoutesInfo *info = arg;
-       struct nlmsghdr *n = nlmsg_hdr (msg);
-       struct nlattr *tb[XFRMA_MAX + 1];
-       struct xfrm_userpolicy_info *xpinfo = NULL;
-
-       if (info->have_routes4) {
-               /* already found some routes */
-               return NL_SKIP;
-       }
-
-       if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) {
-               g_warning ("msg type %d not NEWPOLICY", n->nlmsg_type);
-               return NL_SKIP;
-       }
-
-       /* Netlink message header is followed by 'struct xfrm_userpolicy_info' and
-        * then the attributes.
-        */
-
-       if (!nlmsg_valid_hdr (n, sizeof (struct xfrm_userpolicy_info))) {
-               g_warning ("msg too short");
-               return -NLE_MSG_TOOSHORT;
-       }
-
-       xpinfo = nlmsg_data (n);
-       if (nla_parse (tb, XFRMA_MAX,
-                      nlmsg_attrdata (n, sizeof (struct xfrm_userpolicy_info)),
-                      nlmsg_attrlen (n, sizeof (struct xfrm_userpolicy_info)),
-                      NULL) < 0) {
-               g_warning ("failed to parse attributes");
-               return NL_SKIP;
-       }
-
-       if (tb[XFRMA_TMPL]) {
-               int attrlen = nla_len (tb[XFRMA_TMPL]);
-               struct xfrm_user_tmpl *list = nla_data (tb[XFRMA_TMPL]);
-               int i;
-
-               /* We only look for subnet route associations, eg where
-                * (sel->prefixlen_d > 0), and for those associations, we match
-                * the xfrm_user_tmpl's destination address against the PLUTO_PEER.
-                */
-               if (xpinfo->sel.family == AF_INET && xpinfo->sel.prefixlen_d > 0) {
-                       for (i = 0; i < attrlen / sizeof (struct xfrm_user_tmpl); i++) {
-                               struct xfrm_user_tmpl *tmpl = &list[i];
-
-                               if (   tmpl->family == AF_INET
-                                   && memcmp (&tmpl->id.daddr, &info->gw4, sizeof (struct in_addr)) == 0) {
-                                       info->have_routes4 = TRUE;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       return NL_OK;
-}
-
-static gboolean
-have_sad_routes (const char *gw_addr4)
-{
-       RoutesInfo info = { { 0 }, FALSE };
-       struct nl_sock *sk;
-       int err;
-
-       if (inet_pton (AF_INET, gw_addr4, &info.gw4) != 1)
-               return FALSE;
-
-       sk = setup_socket ();
-       if (!sk)
-               return FALSE;
-
-       err = nl_send_simple (sk, XFRM_MSG_GETPOLICY, NLM_F_DUMP, NULL, 0);
-       if (err < 0) {
-               g_warning ("Error sending: %d %s", err, nl_geterror (err));
-               goto done;
-       }
-
-       nl_socket_modify_cb (sk, NL_CB_VALID, NL_CB_CUSTOM, parse_reply, &info);
-
-       err = nl_recvmsgs_default (sk);
-       if (err < 0) {
-               g_warning ("Error parsing: %d %s", err, nl_geterror (err));
-               goto done;
-       }
-
-done:
-       nl_socket_free (sk);
-       return info.have_routes4;
-}
-
-/********************************************************************/
-
-static GVariant *
-str_to_gvariant (const char *str, gboolean try_convert)
-{
-
-       /* Empty */
-       if (!str || strlen (str) < 1)
-               return NULL;
-
-       if (!g_utf8_validate (str, -1, NULL)) {
-               if (try_convert && !(str = g_convert (str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL)))
-                       str = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
-
-               if (!str)
-                       /* Invalid */
-                       return NULL;
-       }
-
-       return g_variant_new_string (str);
-}
-
-static GVariant *
-addr4_to_gvariant (const char *str)
-{
-       struct in_addr  temp_addr;
-
-       /* Empty */
-       if (!str || strlen (str) < 1)
-               return NULL;
-
-       if (inet_pton (AF_INET, str, &temp_addr) <= 0)
-               return NULL;
-
-       return g_variant_new_uint32 (temp_addr.s_addr);
-}
-
-static GVariant *
-addr4_list_to_gvariant (const char *str)
-{
-       GVariantBuilder builder;
-       char **split;
-       int i;
-
-       /* Empty */
-       if (!str || strlen (str) < 1)
-               return NULL;
-
-       split = g_strsplit (str, " ", -1);
-       if (g_strv_length (split) == 0)
-               return NULL;
-
-       g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
-
-       for (i = 0; split[i]; i++) {
-               struct in_addr addr;
-
-               if (inet_pton (AF_INET, split[i], &addr) > 0) {
-                       g_variant_builder_add_value (&builder, g_variant_new_uint32 (addr.s_addr));
-               } else {
-                       g_strfreev (split);
-                       g_variant_unref (g_variant_builder_end (&builder));
-                       return NULL;
-               }
-       }
-
-       g_strfreev (split);
-
-       return g_variant_builder_end (&builder);
-}
-
-/*
- * Environment variables passed to this helper:
- *
- * PLUTO_PEER                -- vpn gateway address
- * PLUTO_MY_SOURCEIP         -- address
- * PLUTO_CISCO_DNS_INFO/     -- list of dns servers
- *    PLUTO_PEER_DNS_INFO
- * PLUTO_CISCO_DOMAIN_INFO/  -- default domain name
- *    PLUTO_PEER_DOMAIN_INFO
- * PLUTO_PEER_BANNER         -- banner from server
- *
- * NOTE: this helper is currently called explicitly by the ipsec up/down
- * script /usr/libexec/ipsec/_updown.netkey when the configuration contains
- * "nm_configured=yes".  Eventually we want to somehow pass the helper
- * directly to pluto/whack with the --updown option.
- */
 int 
 main (int argc, char *argv[])
 {
-       GDBusProxy *proxy;
-       char *tmp=NULL;
-       GVariantBuilder config;
-       GVariant *val;
+       NMDBusOpenswanHelper *proxy;
+       GVariantBuilder environment;
        GError *err = NULL;
+       gchar **environ;
+       gchar **p;
 
 #if !GLIB_CHECK_VERSION (2, 35, 0)
        g_type_init ();
 #endif
 
-       /* The IPSec service gives us a "reason" code.  If we are given one,
-        * don't proceed unless its "connect".
-        */
-       tmp = getenv ("openswan_reason");
-       if (!tmp)
-               tmp = getenv ("libreswan_reason");
-       if (g_strcmp0 (tmp, "connect") != 0)
-               exit (0);
-
-       
-       proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-                                              G_DBUS_PROXY_FLAGS_NONE,
-                                              NULL,
-                                              NM_DBUS_SERVICE_OPENSWAN,
-                                              NM_VPN_DBUS_PLUGIN_PATH,
-                                              NM_VPN_DBUS_PLUGIN_INTERFACE,
-                                              NULL, &err);
+       proxy = nmdbus_openswan_helper_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                              G_DBUS_PROXY_FLAGS_NONE,
+                                                              NM_DBUS_SERVICE_OPENSWAN,
+                                                              NM_DBUS_PATH_OPENSWAN_HELPER,
+                                                              NULL, &err);
        if (!proxy) {
                g_warning ("Could not create a D-Bus proxy: %s", err->message);
                g_error_free (err);
                exit (1);
        }
 
-       g_variant_builder_init (&config, G_VARIANT_TYPE_VARDICT);
-
-
-       /* Right peer (or Gateway) */
-       val = addr4_to_gvariant (getenv ("PLUTO_PEER"));
-       if (val)
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY, val);
-       else
-               helper_failed (proxy, "IPsec/Pluto Right Peer (VPN Gateway)");
-
-
-       /*
-        * Tunnel device
-        * Indicate that this plugin doesn't use tun/tap device
-        */
-       val = g_variant_new_string (NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV_NONE);
-       g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
-
-       /* IP address */
-       val = addr4_to_gvariant (getenv ("PLUTO_MY_SOURCEIP"));
-       if (val)
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
-       else
-               helper_failed (proxy, "IP4 Address");
-
-       /* PTP address; PTP address == internal IP4 address */
-       val = addr4_to_gvariant (getenv ("PLUTO_MY_SOURCEIP"));
-       if (val)
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
-       else
-               helper_failed (proxy, "IP4 PTP Address");
-
-       /* Netmask */
-       val = g_variant_new_uint32 (32);
-       g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
-
-       /* DNS */
-       val = addr4_list_to_gvariant (getenv ("PLUTO_CISCO_DNS_INFO"));
-       if (!val) {
-               /* libreswan value */
-               val = addr4_list_to_gvariant (getenv ("PLUTO_PEER_DNS_INFO"));
+       g_variant_builder_init (&environment, G_VARIANT_TYPE ("a{ss}"));
+       environ = g_listenv ();
+       for (p = environ; *p; p++) {
+               if (strncmp ("PLUTO_", *p, 6))
+                       continue;
+               g_variant_builder_add (&environment, "{ss}", *p, g_getenv (*p));
        }
-       if (val)
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, val);
 
-
-       /* Default domain */
-       val = str_to_gvariant (getenv ("PLUTO_CISCO_DOMAIN_INFO"), TRUE);
-       if (!val) {
-               /* libreswan value */
-               val = str_to_gvariant (getenv ("PLUTO_PEER_DOMAIN_INFO"), TRUE);
+       if (!nmdbus_openswan_helper_call_callback_sync (proxy,
+                                                       g_variant_builder_end (&environment),
+                                                       NULL, &err)) {
+               g_warning ("Could not call the plugin: %s", err->message);
+               g_error_free (err);
+               g_object_unref (proxy);
+               exit (1);
        }
-       if (val)
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN, val);
-
-       /* Banner */
-       val = str_to_gvariant (getenv ("PLUTO_PEER_BANNER"), TRUE);
-       if (val)
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, val);
-
-       if (have_sad_routes (getenv ("PLUTO_PEER")))
-               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, 
g_variant_new_boolean (TRUE));
-
-       /* Send the config info to the VPN plugin */
-       send_ip4_config (proxy, g_variant_builder_end (&config));
 
        g_object_unref (proxy);
 
diff --git a/src/nm-openswan-service.c b/src/nm-openswan-service.c
index 9f459fe..cfd6f30 100644
--- a/src/nm-openswan-service.c
+++ b/src/nm-openswan-service.c
@@ -1,6 +1,10 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager-openswan -- Network Manager Openswan plugin
  *
+ * Dan Williams <dcbw redhat com>
+ * Avesh Agarwal <avagarwa redhat com>
+ * Lubomir Rintel <lkundrak v3 sk>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -15,9 +19,20 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2010 - 2011 Red Hat, Inc.
+ * Copyright (C) 2010 - 2015 Red Hat, Inc.
  */
 
+#define _GNU_SOURCE 1
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
+
+#define _LINUX_IN6_H 1
+#include <linux/xfrm.h>
+
 #include <config.h>
 #include <stdio.h>
 #include <string.h>
@@ -37,6 +52,8 @@
 
 #include <NetworkManager.h>
 #include <nm-vpn-service-plugin.h>
+
+#include "nm-openswan-helper-service-dbus.h"
 #include "nm-openswan-service.h"
 #include "nm-utils.h"
 
@@ -94,6 +111,8 @@ typedef struct {
        guint retries;
        ConnectStep connect_step;
        NMConnection *connection;
+       NMDBusOpenswanHelper *dbus_skeleton;
+       GSList *routes;
 
        GIOChannel *channel;
        guint io_id;
@@ -106,11 +125,6 @@ typedef struct {
 
 #define NM_OPENSWAN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OPENSWAN_PLUGIN, 
NMOpenSwanPluginPrivate))
 
-/* NOTE: the helper is currently called explicitly by the ipsec up/down
- * script /usr/libexec/ipsec/_updown.netkey when the configuration contains
- * "nm_configured=yes".  Eventually we want to somehow pass the helper
- * directly to pluto/whack with the --updown option.
- */
 #define NM_OPENSWAN_HELPER_PATH                LIBEXECDIR"/nm-openswan-service-helper"
 
 #define DEBUG(...) \
@@ -416,6 +430,9 @@ ipsec_stop (NMOpenSwanPlugin *self, GError **error)
        const char *argv[5];
        guint i = 0;
 
+       g_slist_free_full (priv->routes, (GDestroyNotify) g_variant_unref);
+       priv->routes = NULL;
+
        if (!priv->connection)
                return TRUE;
 
@@ -668,6 +685,7 @@ nm_openswan_config_write (gint fd,
        write_config_option (fd, " leftid= %s\n", nm_setting_vpn_get_data_item (s_vpn, NM_OPENSWAN_LEFTID));
        write_config_option (fd, " leftxauthclient=yes\n");
        write_config_option (fd, " leftmodecfgclient=yes\n");
+       write_config_option (fd, " leftupdown=\"" NM_OPENSWAN_HELPER_PATH "\"\n");
 
        default_username = nm_setting_vpn_get_user_name (s_vpn);
        props_username = nm_setting_vpn_get_data_item (s_vpn, NM_OPENSWAN_LEFTXAUTHUSER);
@@ -694,7 +712,6 @@ nm_openswan_config_write (gint fd,
        else
                write_config_option (fd, " esp=%s\n", phase2_alg_str);
 
-       write_config_option (fd, " nm_configured=yes\n");
        write_config_option (fd, " rekey=yes\n");
        write_config_option (fd, " salifetime=24h\n");
        write_config_option (fd, " ikelifetime=24h\n");
@@ -870,6 +887,375 @@ spawn_pty (int *out_stdout,
 
 /****************************************************************/
 
+/* The various SWANs don't tell helper scripts whether upstream sent
+ * specific subnets to be routed over the VPN (eg, CISCO_SPLIT_INC).
+ * This is what we need to automatically determine 'never-default' behavior.
+ * Instead, we have to inspect the kernel's SAD (Security Assocation Database)
+ * for IPSec-secured routes pointing to the VPN gateway.
+ */
+
+typedef struct {
+       struct in_addr gw4;
+       gboolean have_routes4;
+} RoutesInfo;
+
+static int
+verify_source (struct nl_msg *msg, gpointer user_data)
+{
+       struct ucred *creds = nlmsg_get_creds (msg);
+
+       if (!creds || creds->pid || creds->uid || creds->gid) {
+               if (creds) {
+                       g_warning ("netlink: received non-kernel message (pid %d uid %d gid %d)",
+                                  creds->pid, creds->uid, creds->gid);
+               } else
+                       g_warning ("netlink: received message without credentials");
+               return NL_STOP;
+       }
+
+       return NL_OK;
+}
+
+static struct nl_sock *
+setup_socket (void)
+{
+       struct nl_sock *sk;
+       int err;
+
+       sk = nl_socket_alloc ();
+       g_return_val_if_fail (sk, NULL);
+
+       /* Only ever accept messages from kernel */
+       err = nl_socket_modify_cb (sk, NL_CB_MSG_IN, NL_CB_CUSTOM, verify_source, NULL);
+       g_assert (!err);
+
+       err = nl_connect (sk, NETLINK_XFRM);
+       g_assert (!err);
+       err = nl_socket_set_passcred (sk, 1);
+       g_assert (!err);
+
+       return sk;
+}
+
+static int
+parse_reply (struct nl_msg *msg, void *arg)
+{
+       RoutesInfo *info = arg;
+       struct nlmsghdr *n = nlmsg_hdr (msg);
+       struct nlattr *tb[XFRMA_MAX + 1];
+       struct xfrm_userpolicy_info *xpinfo = NULL;
+
+       if (info->have_routes4) {
+               /* already found some routes */
+               return NL_SKIP;
+       }
+
+       if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) {
+               g_warning ("msg type %d not NEWPOLICY", n->nlmsg_type);
+               return NL_SKIP;
+       }
+
+       /* Netlink message header is followed by 'struct xfrm_userpolicy_info' and
+        * then the attributes.
+        */
+
+       if (!nlmsg_valid_hdr (n, sizeof (struct xfrm_userpolicy_info))) {
+               g_warning ("msg too short");
+               return -NLE_MSG_TOOSHORT;
+       }
+
+       xpinfo = nlmsg_data (n);
+       if (nla_parse (tb, XFRMA_MAX,
+                      nlmsg_attrdata (n, sizeof (struct xfrm_userpolicy_info)),
+                      nlmsg_attrlen (n, sizeof (struct xfrm_userpolicy_info)),
+                      NULL) < 0) {
+               g_warning ("failed to parse attributes");
+               return NL_SKIP;
+       }
+
+       if (tb[XFRMA_TMPL]) {
+               int attrlen = nla_len (tb[XFRMA_TMPL]);
+               struct xfrm_user_tmpl *list = nla_data (tb[XFRMA_TMPL]);
+               int i;
+
+               /* We only look for subnet route associations, eg where
+                * (sel->prefixlen_d > 0), and for those associations, we match
+                * the xfrm_user_tmpl's destination address against the PLUTO_PEER.
+                */
+               if (xpinfo->sel.family == AF_INET && xpinfo->sel.prefixlen_d > 0) {
+                       for (i = 0; i < attrlen / sizeof (struct xfrm_user_tmpl); i++) {
+                               struct xfrm_user_tmpl *tmpl = &list[i];
+
+                               if (   tmpl->family == AF_INET
+                                   && memcmp (&tmpl->id.daddr, &info->gw4, sizeof (struct in_addr)) == 0) {
+                                       info->have_routes4 = TRUE;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return NL_OK;
+}
+
+static gboolean
+have_sad_routes (const char *gw_addr4)
+{
+       RoutesInfo info = { { 0 }, FALSE };
+       struct nl_sock *sk;
+       int err;
+
+       if (inet_pton (AF_INET, gw_addr4, &info.gw4) != 1)
+               return FALSE;
+
+       sk = setup_socket ();
+       if (!sk)
+               return FALSE;
+
+       err = nl_send_simple (sk, XFRM_MSG_GETPOLICY, NLM_F_DUMP, NULL, 0);
+       if (err < 0) {
+               g_warning ("Error sending: %d %s", err, nl_geterror (err));
+               goto done;
+       }
+
+       nl_socket_modify_cb (sk, NL_CB_VALID, NL_CB_CUSTOM, parse_reply, &info);
+
+       err = nl_recvmsgs_default (sk);
+       if (err < 0) {
+               g_warning ("Error parsing: %d %s", err, nl_geterror (err));
+               goto done;
+       }
+
+done:
+       nl_socket_free (sk);
+       return info.have_routes4;
+}
+
+/****************************************************************/
+
+static GVariant *
+str_to_gvariant (const char *str, gboolean try_convert)
+{
+
+       /* Empty */
+       if (!str || strlen (str) < 1)
+               return NULL;
+
+       if (!g_utf8_validate (str, -1, NULL)) {
+               if (try_convert && !(str = g_convert (str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL)))
+                       str = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
+
+               if (!str)
+                       /* Invalid */
+                       return NULL;
+       }
+
+       return g_variant_new_string (str);
+}
+
+static GVariant *
+addr4_to_gvariant (const char *str)
+{
+       struct in_addr  temp_addr;
+
+       /* Empty */
+       if (!str || strlen (str) < 1)
+               return NULL;
+
+       if (inet_pton (AF_INET, str, &temp_addr) <= 0)
+               return NULL;
+
+       return g_variant_new_uint32 (temp_addr.s_addr);
+}
+
+static GVariant *
+netmask4_to_gvariant (const char *str)
+{
+       struct in_addr  temp_addr;
+
+       /* Empty */
+       if (!str || strlen (str) < 1)
+               return NULL;
+
+       if (inet_pton (AF_INET, str, &temp_addr) <= 0)
+               return NULL;
+
+       return g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
+}
+
+static GVariant *
+addr4_list_to_gvariant (const char *str)
+{
+       GVariantBuilder builder;
+       char **split;
+       int i;
+
+       /* Empty */
+       if (!str || strlen (str) < 1)
+               return NULL;
+
+       split = g_strsplit (str, " ", -1);
+       if (g_strv_length (split) == 0)
+               return NULL;
+
+       g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+
+       for (i = 0; split[i]; i++) {
+               struct in_addr addr;
+
+               if (inet_pton (AF_INET, split[i], &addr) > 0) {
+                       g_variant_builder_add_value (&builder, g_variant_new_uint32 (addr.s_addr));
+               } else {
+                       g_strfreev (split);
+                       g_variant_unref (g_variant_builder_end (&builder));
+                       return NULL;
+               }
+       }
+
+       g_strfreev (split);
+
+       return g_variant_builder_end (&builder);
+}
+
+static const gchar *
+lookup_string (GVariant *dict, const gchar *key)
+{
+       const gchar *value = NULL;
+
+       g_variant_lookup (dict, key, "s", &value);
+       return value;
+}
+
+static gboolean
+handle_callback (NMDBusOpenswanHelper *object,
+                 GDBusMethodInvocation *invocation,
+                 GVariant *env,
+                 gpointer user_data)
+{
+       NMOpenSwanPluginPrivate *priv = NM_OPENSWAN_PLUGIN_GET_PRIVATE (user_data);
+       GVariantBuilder config;
+       GVariantBuilder builder;
+       GSList *iter;
+       GVariant *val;
+       gboolean success = FALSE;
+
+       g_message ("Configuration from the helper received.");
+
+       if (g_strcmp0 (lookup_string (env, "PLUTO_VERB"), "up-client") != 0) {
+               nmdbus_openswan_helper_complete_callback (object, invocation);
+               return TRUE;
+       }
+
+       g_variant_builder_init (&config, G_VARIANT_TYPE_VARDICT);
+
+       /* Right peer (or Gateway) */
+       val = addr4_to_gvariant (lookup_string (env, "PLUTO_PEER"));
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY, val);
+       else {
+               g_warning ("IPsec/Pluto Right Peer (VPN Gateway)");
+               goto out;
+       }
+
+       /*
+        * Tunnel device
+        * Indicate that this plugin doesn't use tun/tap device
+        */
+       val = g_variant_new_string (NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV_NONE);
+       g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
+
+       /* IP address */
+       val = addr4_to_gvariant (lookup_string (env, "PLUTO_MY_SOURCEIP"));
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
+       else {
+               g_warning ("IP4 Address");
+               goto out;
+       }
+
+       /* PTP address; PTP address == internal IP4 address */
+       val = addr4_to_gvariant (lookup_string (env, "PLUTO_MY_SOURCEIP"));
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
+       else {
+               g_warning ("IP4 PTP Address");
+               goto out;
+       }
+
+       /* Netmask */
+       val = g_variant_new_uint32 (32);
+       g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
+
+       /* DNS */
+       val = addr4_list_to_gvariant (lookup_string (env, "PLUTO_CISCO_DNS_INFO"));
+       if (!val) {
+               /* libreswan value */
+               val = addr4_list_to_gvariant (lookup_string (env, "PLUTO_PEER_DNS_INFO"));
+       }
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, val);
+
+
+       /* Default domain */
+       val = str_to_gvariant (lookup_string (env, "PLUTO_CISCO_DOMAIN_INFO"), TRUE);
+       if (!val) {
+               /* libreswan value */
+               val = str_to_gvariant (lookup_string (env, "PLUTO_PEER_DOMAIN_INFO"), TRUE);
+       }
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN, val);
+
+       /* Banner */
+       val = str_to_gvariant (lookup_string (env, "PLUTO_PEER_BANNER"), TRUE);
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, val);
+
+
+       val = addr4_to_gvariant (lookup_string (env, "PLUTO_NEXT_HOP"));
+       if (val)
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
+
+       /* This route */
+       /* TODO: We just cumulate the routes on up-client. We probably should add and remove them
+        * on route-client and unroute-client verbs. */
+       if (lookup_string (env, "PLUTO_PEER_CLIENT")) {
+               g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+               g_variant_builder_add_value (&builder, addr4_to_gvariant (lookup_string (env, 
"PLUTO_PEER_CLIENT_NET")));
+               g_variant_builder_add_value (&builder, netmask4_to_gvariant (lookup_string (env, 
"PLUTO_PEER_CLIENT_MASK")));
+               g_variant_builder_add_value (&builder, addr4_to_gvariant (lookup_string (env, 
"PLUTO_NEXT_HOP")));
+               g_variant_builder_add_value (&builder, g_variant_new_uint32 (0));
+               g_variant_builder_add_value (&builder, addr4_to_gvariant (lookup_string (env, 
"PLUTO_MY_SOURCEIP")));
+               priv->routes = g_slist_append (priv->routes, g_variant_ref_sink (g_variant_builder_end 
(&builder)));
+       }
+
+       /* Routes */
+       g_variant_builder_init (&builder, G_VARIANT_TYPE ("aau"));
+       for (iter = priv->routes; iter; iter = g_slist_next (iter))
+               g_variant_builder_add_value (&builder, (GVariant *) iter->data);
+       g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ROUTES,
+                              g_variant_builder_end (&builder));
+
+       /* :( */
+       if (have_sad_routes (lookup_string (env, "PLUTO_PEER")))
+               g_variant_builder_add (&config, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, 
g_variant_new_boolean (TRUE));
+
+       success = TRUE;
+
+out:
+       if (success) {
+               nm_vpn_service_plugin_set_ip4_config (NM_VPN_SERVICE_PLUGIN (user_data),
+                                                     g_variant_builder_end (&config));
+       } else {
+               connect_failed (NM_OPENSWAN_PLUGIN (user_data), NULL,
+                               NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
+       }
+
+       nmdbus_openswan_helper_complete_callback (object, invocation);
+       return TRUE;
+}
+
+/****************************************************************/
+
 #define PASSPHRASE_REQUEST "Enter passphrase: "
 
 static gboolean
@@ -1329,6 +1715,24 @@ real_disconnect (NMVpnServicePlugin *plugin, GError **error)
 }
 
 static void
+dispose (GObject *object)
+{
+       NMOpenSwanPluginPrivate *priv = NM_OPENSWAN_PLUGIN_GET_PRIVATE (object);
+       GDBusInterfaceSkeleton *skeleton = NULL;
+
+       if (priv->dbus_skeleton)
+               skeleton = G_DBUS_INTERFACE_SKELETON (priv->dbus_skeleton);
+
+       if (skeleton) {
+               if (g_dbus_interface_skeleton_get_object_path (skeleton))
+                       g_dbus_interface_skeleton_unexport (skeleton);
+               g_signal_handlers_disconnect_by_func (skeleton, handle_callback, object);
+       }
+
+       G_OBJECT_CLASS (nm_openswan_plugin_parent_class)->dispose (object);
+}
+
+static void
 nm_openswan_plugin_init (NMOpenSwanPlugin *plugin)
 {
 }
@@ -1354,6 +1758,7 @@ nm_openswan_plugin_class_init (NMOpenSwanPluginClass *openswan_class)
        g_type_class_add_private (object_class, sizeof (NMOpenSwanPluginPrivate));
 
        /* virtual methods */
+       object_class->dispose = dispose;
        object_class->finalize = finalize;
        parent_class->connect = real_connect;
        parent_class->connect_interactive = real_connect_interactive;
@@ -1393,8 +1798,10 @@ int
 main (int argc, char *argv[])
 {
        NMOpenSwanPlugin *plugin;
+       NMOpenSwanPluginPrivate *priv;
        gboolean persist = FALSE;
        GOptionContext *opt_ctx = NULL;
+       GDBusConnection *connection;
        GError *error = NULL;
 
        GOptionEntry options[] = {
@@ -1442,6 +1849,25 @@ main (int argc, char *argv[])
                exit (1);
        }
 
+       connection = nm_vpn_service_plugin_get_connection (NM_VPN_SERVICE_PLUGIN (plugin)),
+       priv = NM_OPENSWAN_PLUGIN_GET_PRIVATE (plugin);
+       priv->dbus_skeleton = nmdbus_openswan_helper_skeleton_new ();
+       if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_skeleton),
+                                              connection,
+                                              NM_DBUS_PATH_OPENSWAN_HELPER,
+                                              &error)) {
+               g_warning ("Failed to export helper interface: %s", error->message);
+               g_error_free (error);
+               g_clear_object (&plugin);
+               exit (1);
+       }
+
+       g_dbus_connection_register_object (connection, NM_VPN_DBUS_PLUGIN_PATH,
+                                          nmdbus_openswan_helper_interface_info (),
+                                          NULL, NULL, NULL, NULL);
+
+       g_signal_connect (priv->dbus_skeleton, "handle-callback", G_CALLBACK (handle_callback), plugin);
+
        loop = g_main_loop_new (NULL, FALSE);
 
        if (!persist)
diff --git a/src/nm-openswan-service.h b/src/nm-openswan-service.h
index 156ce68..c926d0b 100644
--- a/src/nm-openswan-service.h
+++ b/src/nm-openswan-service.h
@@ -24,9 +24,10 @@
 #include <glib.h>
 #include <glib-object.h>
 
-#define NM_DBUS_SERVICE_OPENSWAN    "org.freedesktop.NetworkManager.openswan"
-#define NM_DBUS_INTERFACE_OPENSWAN  "org.freedesktop.NetworkManager.openswan"
-#define NM_DBUS_PATH_OPENSWAN       "/org/freedesktop/NetworkManager/openswan"
+#define NM_DBUS_SERVICE_OPENSWAN     "org.freedesktop.NetworkManager.openswan"
+#define NM_DBUS_INTERFACE_OPENSWAN   "org.freedesktop.NetworkManager.openswan"
+#define NM_DBUS_PATH_OPENSWAN        "/org/freedesktop/NetworkManager/openswan"
+#define NM_DBUS_PATH_OPENSWAN_HELPER "/org/freedesktop/NetworkManager/openswan/helper"
 
 #define NM_OPENSWAN_RIGHT  "right"
 #define NM_OPENSWAN_LEFTID "leftid"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]