diff options
Diffstat (limited to 'libnm/nm-vpn-service-plugin.c')
| -rw-r--r-- | libnm/nm-vpn-service-plugin.c | 1334 |
1 files changed, 0 insertions, 1334 deletions
diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c deleted file mode 100644 index bf5f292f..00000000 --- a/libnm/nm-vpn-service-plugin.c +++ /dev/null @@ -1,1334 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2007 - 2015 Red Hat, Inc. - */ - -#include "libnm/nm-default-libnm.h" - -#include "nm-vpn-service-plugin.h" - -#include <signal.h> -#include <stdlib.h> - -#include "nm-glib-aux/nm-secret-utils.h" -#include "nm-glib-aux/nm-dbus-aux.h" -#include "nm-enum-types.h" -#include "nm-utils.h" -#include "nm-connection.h" -#include "nm-dbus-helpers.h" -#include "nm-core-internal.h" -#include "nm-simple-connection.h" - -#include "introspection/org.freedesktop.NetworkManager.VPN.Plugin.h" - -#define NM_VPN_SERVICE_PLUGIN_QUIT_TIMER 180 - -static void nm_vpn_service_plugin_initable_iface_init(GInitableIface *iface); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE(NMVpnServicePlugin, - nm_vpn_service_plugin, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, - nm_vpn_service_plugin_initable_iface_init);) - -typedef struct { - NMVpnServiceState state; - - /* DBUS-y stuff */ - GDBusConnection *connection; - NMDBusVpnPlugin *dbus_vpn_service_plugin; - char * dbus_service_name; - gboolean dbus_watch_peer; - - /* Temporary stuff */ - guint connect_timer; - guint quit_timer; - guint fail_stop_id; - guint peer_watch_id; - gboolean interactive; - - gboolean got_config; - gboolean has_ip4, got_ip4; - gboolean has_ip6, got_ip6; - - /* Config stuff copied from config to ip4config */ - GVariant *banner, *tundev, *gateway, *mtu; -} NMVpnServicePluginPrivate; - -#define NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginPrivate)) - -enum { - STATE_CHANGED, - CONFIG, - IP4_CONFIG, - IP6_CONFIG, - LOGIN_BANNER, - FAILURE, - QUIT, - SECRETS_REQUIRED, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DBUS_SERVICE_NAME, PROP_DBUS_WATCH_PEER, PROP_STATE, ); - -static GSList *active_plugins = NULL; - -static void -nm_vpn_service_plugin_set_connection(NMVpnServicePlugin *plugin, GDBusConnection *connection) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - g_clear_object(&priv->connection); - - if (connection) - priv->connection = g_object_ref(connection); -} - -/** - * nm_vpn_service_plugin_get_connection: - * - * Returns: (transfer full): - * - * Since: 1.2 - */ -GDBusConnection * -nm_vpn_service_plugin_get_connection(NMVpnServicePlugin *plugin) -{ - GDBusConnection *connection; - - g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), NULL); - - connection = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->connection; - - if (connection) - g_object_ref(connection); - - return connection; -} - -static NMVpnServiceState -nm_vpn_service_plugin_get_state(NMVpnServicePlugin *plugin) -{ - g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), NM_VPN_SERVICE_STATE_UNKNOWN); - - return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->state; -} - -static void -nm_vpn_service_plugin_set_state(NMVpnServicePlugin *plugin, NMVpnServiceState state) -{ - NMVpnServicePluginPrivate *priv; - - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - - priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - if (priv->state != state) { - priv->state = state; - g_signal_emit(plugin, signals[STATE_CHANGED], 0, state); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_state_changed(priv->dbus_vpn_service_plugin, state); - } -} - -void -nm_vpn_service_plugin_set_login_banner(NMVpnServicePlugin *plugin, const char *banner) -{ - NMVpnServicePluginPrivate *priv; - - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - g_return_if_fail(banner != NULL); - - priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - g_signal_emit(plugin, signals[LOGIN_BANNER], 0, banner); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_login_banner(priv->dbus_vpn_service_plugin, banner); -} - -static void -_emit_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - g_signal_emit(plugin, signals[FAILURE], 0, reason); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_failure(priv->dbus_vpn_service_plugin, reason); -} - -void -nm_vpn_service_plugin_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure reason) -{ - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - - _emit_failure(plugin, reason); - nm_vpn_service_plugin_disconnect(plugin, NULL); -} - -gboolean -nm_vpn_service_plugin_disconnect(NMVpnServicePlugin *plugin, GError **err) -{ - gboolean ret = FALSE; - NMVpnServiceState state; - - g_return_val_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin), FALSE); - - state = nm_vpn_service_plugin_get_state(plugin); - switch (state) { - case NM_VPN_SERVICE_STATE_STOPPING: - g_set_error( - err, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, - "%s", - "Could not process the request because the VPN connection is already being stopped."); - break; - case NM_VPN_SERVICE_STATE_STOPPED: - g_set_error(err, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, - "%s", - "Could not process the request because no VPN connection was active."); - break; - case NM_VPN_SERVICE_STATE_STARTING: - _emit_failure(plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); - /* fall-through */ - case NM_VPN_SERVICE_STATE_STARTED: - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPING); - ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->disconnect(plugin, err); - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); - break; - case NM_VPN_SERVICE_STATE_INIT: - ret = TRUE; - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED); - break; - - default: - g_warning("Unhandled VPN service state %d", state); - g_assert_not_reached(); - break; - } - - return ret; -} - -static void -nm_vpn_service_plugin_emit_quit(NMVpnServicePlugin *plugin) -{ - g_signal_emit(plugin, signals[QUIT], 0); -} - -/** - * nm_vpn_service_plugin_shutdown: - * @plugin: the #NMVpnServicePlugin instance - * - * Shutdown the @plugin and disconnect from D-Bus. After this, - * the plugin instance is dead and should no longer be used. - * It ensures to get no more requests from D-Bus. In principle, - * you don't need to shutdown the plugin, disposing the instance - * has the same effect. However, this gives a way to deactivate - * the plugin before giving up the last reference. - * - * Since: 1.12 - */ -void -nm_vpn_service_plugin_shutdown(NMVpnServicePlugin *plugin) -{ - NMVpnServicePluginPrivate *priv; - NMVpnServiceState state; - GError * error = NULL; - - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - - priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - nm_clear_g_source(&priv->fail_stop_id); - nm_clear_g_source(&priv->quit_timer); - nm_clear_g_source(&priv->connect_timer); - - state = nm_vpn_service_plugin_get_state(plugin); - if (state == NM_VPN_SERVICE_STATE_STARTED || state == NM_VPN_SERVICE_STATE_STARTING) { - nm_vpn_service_plugin_disconnect(plugin, &error); - - if (error) { - g_warning("Error disconnecting VPN connection: %s", error->message); - g_error_free(error); - } - } - - if (priv->dbus_vpn_service_plugin) { - g_dbus_interface_skeleton_unexport( - G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_service_plugin)); - g_clear_object(&priv->dbus_vpn_service_plugin); - } -} - -static gboolean -connect_timer_expired(gpointer data) -{ - NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN(data); - GError * err = NULL; - - NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin)->connect_timer = 0; - g_message("Connect timer expired, disconnecting."); - nm_vpn_service_plugin_disconnect(plugin, &err); - if (err) { - g_warning("Disconnect failed: %s", err->message); - g_error_free(err); - } - - return G_SOURCE_REMOVE; -} - -static gboolean -quit_timer_expired(gpointer data) -{ - NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN(data); - - NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self)->quit_timer = 0; - nm_vpn_service_plugin_emit_quit(self); - return G_SOURCE_REMOVE; -} - -static void -schedule_quit_timer(NMVpnServicePlugin *self) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self); - - nm_clear_g_source(&priv->quit_timer); - priv->quit_timer = - g_timeout_add_seconds(NM_VPN_SERVICE_PLUGIN_QUIT_TIMER, quit_timer_expired, self); -} - -static gboolean -fail_stop(gpointer data) -{ - NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN(data); - - NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(self)->fail_stop_id = 0; - nm_vpn_service_plugin_set_state(self, NM_VPN_SERVICE_STATE_STOPPED); - return G_SOURCE_REMOVE; -} - -static void -schedule_fail_stop(NMVpnServicePlugin *plugin, guint timeout_secs) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - nm_clear_g_source(&priv->fail_stop_id); - if (timeout_secs) - priv->fail_stop_id = g_timeout_add_seconds(timeout_secs, fail_stop, plugin); - else - priv->fail_stop_id = g_idle_add(fail_stop, plugin); -} - -void -nm_vpn_service_plugin_set_config(NMVpnServicePlugin *plugin, GVariant *config) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - g_return_if_fail(config != NULL); - - priv->got_config = TRUE; - - (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4); - (void) g_variant_lookup(config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6); - - /* Record the items that need to also be inserted into the - * ip4config, for compatibility with older daemons. - */ - if (priv->banner) - g_variant_unref(priv->banner); - priv->banner = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_BANNER, G_VARIANT_TYPE("s")); - if (priv->tundev) - g_variant_unref(priv->tundev); - priv->tundev = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_TUNDEV, G_VARIANT_TYPE("s")); - if (priv->gateway) - g_variant_unref(priv->gateway); - priv->gateway = - g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, G_VARIANT_TYPE("u")); - if (priv->mtu) - g_variant_unref(priv->mtu); - priv->mtu = g_variant_lookup_value(config, NM_VPN_PLUGIN_CONFIG_MTU, G_VARIANT_TYPE("u")); - - g_signal_emit(plugin, signals[CONFIG], 0, config); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_config(priv->dbus_vpn_service_plugin, config); - - if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); -} - -void -nm_vpn_service_plugin_set_ip4_config(NMVpnServicePlugin *plugin, GVariant *ip4_config) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - GVariant * combined_config; - GVariantBuilder builder; - GVariantIter iter; - const char * key; - GVariant * value; - - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - g_return_if_fail(ip4_config != NULL); - - priv->got_ip4 = TRUE; - - /* Old plugins won't send the "config" signal and thus can't send - * NM_VPN_SERVICE_PLUGIN_CONFIG_HAS_IP4 either. But since they don't support IPv6, - * we can safely assume that, if we don't receive a "config" signal but do - * receive an "ip4-config" signal, the old plugin supports IPv4. - */ - if (!priv->got_config) - priv->has_ip4 = TRUE; - - /* Older NetworkManager daemons expect all config info to be in - * the ip4 config, so they won't even notice the "config" signal - * being emitted. So just copy all of that data into the ip4 - * config too. - */ - g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); - g_variant_iter_init(&iter, ip4_config); - while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { - g_variant_builder_add(&builder, "{sv}", key, value); - g_variant_unref(value); - } - - if (priv->banner) - g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, priv->banner); - if (priv->tundev) - g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, priv->tundev); - if (priv->gateway) - g_variant_builder_add(&builder, - "{sv}", - NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, - priv->gateway); - if (priv->mtu) - g_variant_builder_add(&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, priv->mtu); - - combined_config = g_variant_builder_end(&builder); - g_variant_ref_sink(combined_config); - g_signal_emit(plugin, signals[IP4_CONFIG], 0, combined_config); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_ip4_config(priv->dbus_vpn_service_plugin, combined_config); - g_variant_unref(combined_config); - - if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); -} - -void -nm_vpn_service_plugin_set_ip6_config(NMVpnServicePlugin *plugin, GVariant *ip6_config) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - g_return_if_fail(NM_IS_VPN_SERVICE_PLUGIN(plugin)); - g_return_if_fail(ip6_config != NULL); - - g_variant_ref_sink(ip6_config); - - priv->got_ip6 = TRUE; - g_signal_emit(plugin, signals[IP6_CONFIG], 0, ip6_config); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_ip6_config(priv->dbus_vpn_service_plugin, ip6_config); - - g_variant_unref(ip6_config); - - if (priv->has_ip4 == priv->got_ip4 && priv->has_ip6 == priv->got_ip6) - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTED); -} - -static void -connect_timer_start(NMVpnServicePlugin *plugin) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - nm_clear_g_source(&priv->connect_timer); - priv->connect_timer = g_timeout_add_seconds(60, connect_timer_expired, plugin); -} - -static void -peer_vanished(GDBusConnection *connection, - const char * sender_name, - const char * object_path, - const char * interface_name, - const char * signal_name, - GVariant * parameters, - gpointer user_data) -{ - nm_vpn_service_plugin_disconnect(NM_VPN_SERVICE_PLUGIN(user_data), NULL); -} - -static guint -watch_peer(NMVpnServicePlugin *plugin, GDBusMethodInvocation *context) -{ - GDBusConnection *connection = g_dbus_method_invocation_get_connection(context); - const char *peer = g_dbus_message_get_sender(g_dbus_method_invocation_get_message(context)); - - return nm_dbus_connection_signal_subscribe_name_owner_changed(connection, - peer, - peer_vanished, - plugin, - NULL); -} - -static void -_connect_generic(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * properties, - GVariant * details) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - NMVpnServicePluginClass * vpn_class = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin); - NMConnection * connection; - gboolean success = FALSE; - GError * error = NULL; - guint fail_stop_timeout = 0; - - if (priv->state != NM_VPN_SERVICE_STATE_STOPPED && priv->state != NM_VPN_SERVICE_STATE_INIT) { - g_dbus_method_invocation_return_error(context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_WRONG_STATE, - "Could not start connection: wrong plugin state %d", - priv->state); - return; - } - - connection = - _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); - if (!connection) { - g_dbus_method_invocation_return_error(context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "Invalid connection: %s", - error->message); - g_clear_error(&error); - return; - } - - priv->interactive = FALSE; - if (details && !vpn_class->connect_interactive) { - g_dbus_method_invocation_return_error(context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, - "Plugin does not implement ConnectInteractive()"); - return; - } - - nm_clear_g_source(&priv->fail_stop_id); - - if (priv->dbus_watch_peer) - priv->peer_watch_id = watch_peer(plugin, context); - - if (details) { - priv->interactive = TRUE; - success = vpn_class->connect_interactive(plugin, connection, details, &error); - if (g_error_matches(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) { - /* Give NetworkManager a bit of time to fall back to Connect() */ - fail_stop_timeout = 5; - } - } else - success = vpn_class->connect(plugin, connection, &error); - - if (success) { - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STARTING); - - g_dbus_method_invocation_return_value(context, NULL); - - /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ - connect_timer_start(plugin); - } else { - g_dbus_method_invocation_take_error(context, error); - - /* Stop the plugin from an idle handler so that the Connect - * method return gets sent before the STOP StateChanged signal. - */ - schedule_fail_stop(plugin, fail_stop_timeout); - } - - g_object_unref(connection); -} - -static void -impl_vpn_service_plugin_connect(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * connection, - gpointer user_data) -{ - _connect_generic(plugin, context, connection, NULL); -} - -static void -impl_vpn_service_plugin_connect_interactive(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * connection, - GVariant * details, - gpointer user_data) -{ - _connect_generic(plugin, context, connection, details); -} - -/*****************************************************************************/ - -static void -impl_vpn_service_plugin_need_secrets(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * properties, - gpointer user_data) -{ - NMConnection *connection; - const char * setting_name; - gboolean needed; - GError * error = NULL; - - connection = - _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); - if (!connection) { - g_dbus_method_invocation_return_error(context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION, - "The connection was invalid: %s", - error->message); - g_error_free(error); - return; - } - - if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->need_secrets) { - g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); - return; - } - - needed = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->need_secrets(plugin, - connection, - &setting_name, - &error); - if (error) { - g_dbus_method_invocation_take_error(context, error); - return; - } - - if (needed) { - /* Push back the quit timer so the VPN plugin doesn't quit in the - * middle of asking the user for secrets. - */ - schedule_quit_timer(plugin); - - g_assert(setting_name); - g_dbus_method_invocation_return_value(context, g_variant_new("(s)", setting_name)); - } else { - /* No secrets required */ - g_dbus_method_invocation_return_value(context, g_variant_new("(s)", "")); - } -} - -static void -impl_vpn_service_plugin_new_secrets(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * properties, - gpointer user_data) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - NMConnection * connection; - GError * error = NULL; - gboolean success; - - if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { - g_dbus_method_invocation_return_error(context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_WRONG_STATE, - "Could not accept new secrets: wrong plugin state %d", - priv->state); - return; - } - - connection = - _nm_simple_connection_new_from_dbus(properties, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error); - if (!connection) { - g_dbus_method_invocation_return_error(context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - "Invalid connection: %s", - error->message); - g_clear_error(&error); - return; - } - - if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets) { - g_dbus_method_invocation_return_error( - context, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED, - "Could not accept new secrets: plugin cannot process interactive secrets"); - g_object_unref(connection); - return; - } - - success = NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets(plugin, connection, &error); - if (success) { - g_dbus_method_invocation_return_value(context, NULL); - - /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ - connect_timer_start(plugin); - } else { - g_dbus_method_invocation_take_error(context, error); - - /* Stop the plugin from and idle handler so that the NewSecrets - * method return gets sent before the STOP StateChanged signal. - */ - schedule_fail_stop(plugin, 0); - } - - g_object_unref(connection); -} - -/** - * nm_vpn_service_plugin_secrets_required: - * @plugin: the #NMVpnServicePlugin - * @message: an information message about why secrets are required, if any - * @hints: VPN specific secret names for required new secrets - * - * Called by VPN plugin implementations to signal to NetworkManager that secrets - * are required during the connection process. This signal may be used to - * request new secrets when the secrets originally provided by NetworkManager - * are insufficient, or the VPN process indicates that it needs additional - * information to complete the request. - * - * Since: 1.2 - */ -void -nm_vpn_service_plugin_secrets_required(NMVpnServicePlugin *plugin, - const char * message, - const char ** hints) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - /* Plugin must be able to accept the new secrets if it calls this method */ - g_return_if_fail(NM_VPN_SERVICE_PLUGIN_GET_CLASS(plugin)->new_secrets); - - /* Plugin cannot call this method if NetworkManager didn't originally call - * ConnectInteractive(). - */ - g_return_if_fail(priv->interactive == TRUE); - - /* Cancel the connect timer since secrets might take a while. It'll - * get restarted when the secrets come back via NewSecrets(). - */ - nm_clear_g_source(&priv->connect_timer); - - g_signal_emit(plugin, signals[SECRETS_REQUIRED], 0, message, hints); - if (priv->dbus_vpn_service_plugin) - nmdbus_vpn_plugin_emit_secrets_required(priv->dbus_vpn_service_plugin, message, hints); -} - -/*****************************************************************************/ - -#define DATA_KEY_TAG "DATA_KEY=" -#define DATA_VAL_TAG "DATA_VAL=" -#define SECRET_KEY_TAG "SECRET_KEY=" -#define SECRET_VAL_TAG "SECRET_VAL=" - -/** - * nm_vpn_service_plugin_read_vpn_details: - * @fd: file descriptor to read from, usually stdin (0) - * @out_data: (out) (transfer full): on successful return, a hash table - * (mapping char*:char*) containing the key/value pairs of VPN data items - * @out_secrets: (out) (transfer full): on successful return, a hash table - * (mapping char*:char*) containing the key/value pairsof VPN secrets - * - * Parses key/value pairs from a file descriptor (normally stdin) passed by - * an applet when the applet calls the authentication dialog of the VPN plugin. - * - * Returns: %TRUE if reading values was successful, %FALSE if not - * - * Since: 1.2 - **/ -gboolean -nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable **out_secrets) -{ - gs_unref_hashtable GHashTable *data = NULL; - gs_unref_hashtable GHashTable *secrets = NULL; - gboolean success = FALSE; - GHashTable * hash = NULL; - nm_auto_free_gstring GString *key = NULL; - nm_auto_free_gstring GString *val = NULL; - nm_auto_free_gstring GString *line = NULL; - char c; - GString * str = NULL; - - if (out_data) - g_return_val_if_fail(*out_data == NULL, FALSE); - if (out_secrets) - g_return_val_if_fail(*out_secrets == NULL, FALSE); - - data = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); - secrets = - g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret); - - line = g_string_new(NULL); - - /* Read stdin for data and secret items until we get a DONE */ - while (1) { - ssize_t nr; - - nr = read(fd, &c, 1); - if (nr < 0) { - if (errno == EAGAIN) { - g_usleep(100); - continue; - } - break; - } - if (nr > 0 && c != '\n') { - g_string_append_c(line, c); - continue; - } - - if (str && *line->str == '=') { - /* continuation */ - g_string_append_c(str, '\n'); - g_string_append(str, line->str + 1); - } else if (key && val) { - /* done a line */ - g_return_val_if_fail(hash, FALSE); - g_hash_table_insert(hash, g_string_free(key, FALSE), g_string_free(val, FALSE)); - key = NULL; - val = NULL; - hash = NULL; - success = TRUE; /* Got at least one value */ - } - - if (strcmp(line->str, "DONE") == 0) { - /* finish marker */ - break; - } else if (strncmp(line->str, DATA_KEY_TAG, strlen(DATA_KEY_TAG)) == 0) { - if (nm_clear_g_string(&key)) - g_warning("a value expected"); - key = g_string_new(line->str + strlen(DATA_KEY_TAG)); - str = key; - hash = data; - } else if (strncmp(line->str, DATA_VAL_TAG, strlen(DATA_VAL_TAG)) == 0) { - if (val || !key || hash != data) { - g_warning("%s not preceded by %s", DATA_VAL_TAG, DATA_KEY_TAG); - break; - } - val = g_string_new(line->str + strlen(DATA_VAL_TAG)); - str = val; - } else if (strncmp(line->str, SECRET_KEY_TAG, strlen(SECRET_KEY_TAG)) == 0) { - if (nm_clear_g_string(&key)) - g_warning("a value expected"); - key = g_string_new(line->str + strlen(SECRET_KEY_TAG)); - str = key; - hash = secrets; - } else if (strncmp(line->str, SECRET_VAL_TAG, strlen(SECRET_VAL_TAG)) == 0) { - if (val || !key || hash != secrets) { - g_warning("%s not preceded by %s", SECRET_VAL_TAG, SECRET_KEY_TAG); - break; - } - val = g_string_new(line->str + strlen(SECRET_VAL_TAG)); - str = val; - } - - g_string_truncate(line, 0); - - if (nr == 0) - break; - } - - if (success) { - NM_SET_OUT(out_data, g_steal_pointer(&data)); - NM_SET_OUT(out_secrets, g_steal_pointer(&secrets)); - } - return success; -} - -/** - * nm_vpn_service_plugin_get_secret_flags: - * @data: hash table containing VPN key/value pair data items - * @secret_name: VPN secret key name for which to retrieve flags for - * @out_flags: (out): on success, the flags associated with @secret_name - * - * Given a VPN secret key name, attempts to find the corresponding flags data - * item in @data. If found, converts the flags data item to - * #NMSettingSecretFlags and returns it. - * - * Returns: %TRUE if the flag data item was found and successfully converted - * to flags, %FALSE if not - * - * Since: 1.2 - **/ -gboolean -nm_vpn_service_plugin_get_secret_flags(GHashTable * data, - const char * secret_name, - NMSettingSecretFlags *out_flags) -{ - gs_free char * flag_name_free = NULL; - const char * s; - gint64 t1; - NMSettingSecretFlags t0; - - g_return_val_if_fail(data, FALSE); - g_return_val_if_fail(out_flags && *out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE); - if (!secret_name || !*secret_name) - g_return_val_if_reached(FALSE); - - s = g_hash_table_lookup(data, nm_construct_name_a("%s-flags", secret_name, &flag_name_free)); - if (!s) - return FALSE; - t1 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXINT64, -1); - if (t1 == -1) - return FALSE; - t0 = (NMSettingSecretFlags) t1; - if ((gint64) t0 != t1) - return FALSE; - NM_SET_OUT(out_flags, t0); - return TRUE; -} - -/*****************************************************************************/ - -static void -impl_vpn_service_plugin_disconnect(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - gpointer user_data) -{ - GError *error = NULL; - - if (nm_vpn_service_plugin_disconnect(plugin, &error)) - g_dbus_method_invocation_return_value(context, NULL); - else - g_dbus_method_invocation_take_error(context, error); -} - -static void -impl_vpn_service_plugin_set_config(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * config, - gpointer user_data) -{ - nm_vpn_service_plugin_set_config(plugin, config); - g_dbus_method_invocation_return_value(context, NULL); -} - -static void -impl_vpn_service_plugin_set_ip4_config(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * config, - gpointer user_data) -{ - nm_vpn_service_plugin_set_ip4_config(plugin, config); - g_dbus_method_invocation_return_value(context, NULL); -} - -static void -impl_vpn_service_plugin_set_ip6_config(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - GVariant * config, - gpointer user_data) -{ - nm_vpn_service_plugin_set_ip6_config(plugin, config); - g_dbus_method_invocation_return_value(context, NULL); -} - -static void -impl_vpn_service_plugin_set_failure(NMVpnServicePlugin * plugin, - GDBusMethodInvocation *context, - char * reason, - gpointer user_data) -{ - nm_vpn_service_plugin_failure(plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG); - g_dbus_method_invocation_return_value(context, NULL); -} - -/*****************************************************************************/ - -static void -_emit_quit(gpointer data, gpointer user_data) -{ - NMVpnServicePlugin *plugin = data; - - nm_vpn_service_plugin_emit_quit(plugin); -} - -static void -sigterm_handler(int signum) -{ - g_slist_foreach(active_plugins, _emit_quit, NULL); -} - -static void -setup_unix_signal_handler(void) -{ - struct sigaction action; - sigset_t block_mask; - - action.sa_handler = sigterm_handler; - sigemptyset(&block_mask); - action.sa_mask = block_mask; - action.sa_flags = 0; - sigaction(SIGINT, &action, NULL); - sigaction(SIGTERM, &action, NULL); -} - -/*****************************************************************************/ - -static void -one_plugin_destroyed(gpointer data, GObject *object) -{ - active_plugins = g_slist_remove(active_plugins, object); -} - -static void -nm_vpn_service_plugin_init(NMVpnServicePlugin *plugin) -{ - active_plugins = g_slist_append(active_plugins, plugin); - g_object_weak_ref(G_OBJECT(plugin), one_plugin_destroyed, NULL); -} - -static gboolean -init_sync(GInitable *initable, GCancellable *cancellable, GError **error) -{ - NMVpnServicePlugin * plugin = NM_VPN_SERVICE_PLUGIN(initable); - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - gs_unref_object GDBusConnection *connection = NULL; - gs_unref_object GDBusProxy *proxy = NULL; - GVariant * ret; - - if (!priv->dbus_service_name) { - g_set_error_literal(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, - _("No service name specified")); - return FALSE; - } - - connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); - if (!connection) - return FALSE; - - proxy = g_dbus_proxy_new_sync(connection, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES - | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - cancellable, - error); - if (!proxy) - return FALSE; - - priv->dbus_vpn_service_plugin = nmdbus_vpn_plugin_skeleton_new(); - - _nm_dbus_bind_properties(plugin, priv->dbus_vpn_service_plugin); - _nm_dbus_bind_methods(plugin, - priv->dbus_vpn_service_plugin, - "Connect", - impl_vpn_service_plugin_connect, - "ConnectInteractive", - impl_vpn_service_plugin_connect_interactive, - "NeedSecrets", - impl_vpn_service_plugin_need_secrets, - "NewSecrets", - impl_vpn_service_plugin_new_secrets, - "Disconnect", - impl_vpn_service_plugin_disconnect, - "SetConfig", - impl_vpn_service_plugin_set_config, - "SetIp4Config", - impl_vpn_service_plugin_set_ip4_config, - "SetIp6Config", - impl_vpn_service_plugin_set_ip6_config, - "SetFailure", - impl_vpn_service_plugin_set_failure, - NULL); - - if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(priv->dbus_vpn_service_plugin), - connection, - NM_VPN_DBUS_PLUGIN_PATH, - error)) - return FALSE; - - nm_vpn_service_plugin_set_connection(plugin, connection); - nm_vpn_service_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_INIT); - - ret = g_dbus_proxy_call_sync(proxy, - "RequestName", - g_variant_new("(su)", priv->dbus_service_name, 0), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - error); - if (!ret) { - if (error && *error) - g_dbus_error_strip_remote_error(*error); - return FALSE; - } - g_variant_unref(ret); - - return TRUE; -} - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(object); - - switch (prop_id) { - case PROP_DBUS_SERVICE_NAME: - /* construct-only */ - priv->dbus_service_name = g_value_dup_string(value); - break; - case PROP_DBUS_WATCH_PEER: - /* construct-only */ - priv->dbus_watch_peer = g_value_get_boolean(value); - break; - case PROP_STATE: - nm_vpn_service_plugin_set_state(NM_VPN_SERVICE_PLUGIN(object), - (NMVpnServiceState) g_value_get_enum(value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(object); - - switch (prop_id) { - case PROP_DBUS_SERVICE_NAME: - g_value_set_string(value, priv->dbus_service_name); - break; - case PROP_DBUS_WATCH_PEER: - g_value_set_boolean(value, priv->dbus_watch_peer); - break; - case PROP_STATE: - g_value_set_enum(value, nm_vpn_service_plugin_get_state(NM_VPN_SERVICE_PLUGIN(object))); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -dispose(GObject *object) -{ - nm_vpn_service_plugin_shutdown(NM_VPN_SERVICE_PLUGIN(object)); - G_OBJECT_CLASS(nm_vpn_service_plugin_parent_class)->dispose(object); -} - -static void -finalize(GObject *object) -{ - NMVpnServicePlugin * plugin = NM_VPN_SERVICE_PLUGIN(object); - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - nm_vpn_service_plugin_set_connection(plugin, NULL); - g_free(priv->dbus_service_name); - - nm_clear_pointer(&priv->banner, g_variant_unref); - nm_clear_pointer(&priv->tundev, g_variant_unref); - nm_clear_pointer(&priv->gateway, g_variant_unref); - nm_clear_pointer(&priv->mtu, g_variant_unref); - - G_OBJECT_CLASS(nm_vpn_service_plugin_parent_class)->finalize(object); -} - -static void -state_changed(NMVpnServicePlugin *plugin, NMVpnServiceState state) -{ - NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(plugin); - - switch (state) { - case NM_VPN_SERVICE_STATE_STARTING: - nm_clear_g_source(&priv->quit_timer); - nm_clear_g_source(&priv->fail_stop_id); - break; - case NM_VPN_SERVICE_STATE_STOPPED: - if (priv->dbus_watch_peer) - nm_vpn_service_plugin_emit_quit(plugin); - else - schedule_quit_timer(plugin); - nm_clear_g_dbus_connection_signal(nm_vpn_service_plugin_get_connection(plugin), - &priv->peer_watch_id); - break; - default: - /* Clean up all timers we might have set up. */ - nm_clear_g_source(&priv->connect_timer); - nm_clear_g_source(&priv->quit_timer); - nm_clear_g_source(&priv->fail_stop_id); - break; - } -} - -static void -nm_vpn_service_plugin_class_init(NMVpnServicePluginClass *plugin_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS(plugin_class); - - g_type_class_add_private(object_class, sizeof(NMVpnServicePluginPrivate)); - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->dispose = dispose; - object_class->finalize = finalize; - - plugin_class->state_changed = state_changed; - - /** - * NMVpnServicePlugin:service-name: - * - * The D-Bus service name of this plugin. - * - * Since: 1.2 - */ - obj_properties[PROP_DBUS_SERVICE_NAME] = - g_param_spec_string(NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - /** - * NMVpnServicePlugin:watch-peer: - * - * Whether to watch for D-Bus peer's changes. - * - * Since: 1.2 - */ - obj_properties[PROP_DBUS_WATCH_PEER] = - g_param_spec_boolean(NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER, - "", - "", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - /** - * NMVpnServicePlugin:state: - * - * The state of the plugin. - * - * Since: 1.2 - */ - obj_properties[PROP_STATE] = g_param_spec_enum(NM_VPN_SERVICE_PLUGIN_STATE, - "", - "", - NM_TYPE_VPN_SERVICE_STATE, - NM_VPN_SERVICE_STATE_INIT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[STATE_CHANGED] = g_signal_new("state-changed", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, state_changed), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - signals[SECRETS_REQUIRED] = g_signal_new("secrets-required", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_STRING, - G_TYPE_STRV); - - signals[CONFIG] = g_signal_new("config", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, config), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_VARIANT); - - signals[IP4_CONFIG] = g_signal_new("ip4-config", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, ip4_config), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_VARIANT); - - signals[IP6_CONFIG] = g_signal_new("ip6-config", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, ip6_config), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_VARIANT); - - signals[LOGIN_BANNER] = g_signal_new("login-banner", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, login_banner), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - signals[FAILURE] = g_signal_new("failure", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, failure), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_UINT); - - signals[QUIT] = g_signal_new("quit", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(NMVpnServicePluginClass, quit), - NULL, - NULL, - NULL, - G_TYPE_NONE, - 0, - G_TYPE_NONE); - - setup_unix_signal_handler(); -} - -static void -nm_vpn_service_plugin_initable_iface_init(GInitableIface *iface) -{ - iface->init = init_sync; -} - -/*****************************************************************************/ - -/* this header is intended to be copied to users of nm_vpn_editor_plugin_call(), - * to simplify invocation of generic functions. Include it here, to compile - * the code. */ -#include "nm-utils/nm-vpn-editor-plugin-call.h" |