1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301 USA.
18 * Copyright 2007 - 2008 Novell, Inc.
19 * Copyright 2007 - 2015 Red Hat, Inc.
22 #include "nm-default.h"
24 #include "nm-vpn-service-plugin.h"
30 #include "nm-enum-types.h"
32 #include "nm-connection.h"
33 #include "nm-dbus-helpers.h"
34 #include "nm-core-internal.h"
35 #include "nm-simple-connection.h"
37 #include "nmdbus-vpn-plugin.h"
39 #define NM_VPN_SERVICE_PLUGIN_QUIT_TIMER 180
41 static void nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface);
43 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMVpnServicePlugin, nm_vpn_service_plugin, G_TYPE_OBJECT,
44 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_vpn_service_plugin_initable_iface_init);
48 NMVpnServiceState state;
51 GDBusConnection *connection;
52 NMDBusVpnPlugin *dbus_vpn_service_plugin;
53 char *dbus_service_name;
54 gboolean dbus_watch_peer;
64 gboolean has_ip4, got_ip4;
65 gboolean has_ip6, got_ip6;
67 /* Config stuff copied from config to ip4config */
68 GVariant *banner, *tundev, *gateway, *mtu;
69 } NMVpnServicePluginPrivate;
71 #define NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginPrivate))
86 static guint signals[LAST_SIGNAL] = { 0 };
90 PROP_DBUS_SERVICE_NAME,
97 static GSList *active_plugins = NULL;
101 nm_vpn_service_plugin_set_connection (NMVpnServicePlugin *plugin,
102 GDBusConnection *connection)
104 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
106 g_clear_object (&priv->connection);
109 priv->connection = g_object_ref (connection);
113 * nm_vpn_service_plugin_get_connection:
115 * Returns: (transfer full):
120 nm_vpn_service_plugin_get_connection (NMVpnServicePlugin *plugin)
122 GDBusConnection *connection;
124 g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NULL);
126 connection = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->connection;
129 g_object_ref (connection);
134 static NMVpnServiceState
135 nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin)
137 g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN);
139 return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->state;
143 nm_vpn_service_plugin_set_state (NMVpnServicePlugin *plugin,
144 NMVpnServiceState state)
146 NMVpnServicePluginPrivate *priv;
148 g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
150 priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
151 if (priv->state != state) {
153 g_signal_emit (plugin, signals[STATE_CHANGED], 0, state);
154 nmdbus_vpn_plugin_emit_state_changed (priv->dbus_vpn_service_plugin, state);
159 nm_vpn_service_plugin_set_login_banner (NMVpnServicePlugin *plugin,
162 NMVpnServicePluginPrivate *priv;
164 g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
165 g_return_if_fail (banner != NULL);
167 priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
168 g_signal_emit (plugin, signals[LOGIN_BANNER], 0, banner);
169 nmdbus_vpn_plugin_emit_login_banner (priv->dbus_vpn_service_plugin, banner);
173 _emit_failure (NMVpnServicePlugin *plugin,
174 NMVpnPluginFailure reason)
176 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
178 g_signal_emit (plugin, signals[FAILURE], 0, reason);
179 nmdbus_vpn_plugin_emit_failure (priv->dbus_vpn_service_plugin, reason);
183 nm_vpn_service_plugin_failure (NMVpnServicePlugin *plugin,
184 NMVpnPluginFailure reason)
186 g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
188 _emit_failure (plugin, reason);
189 nm_vpn_service_plugin_disconnect (plugin, NULL);
193 nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err)
195 NMVpnServicePluginPrivate *priv;
196 gboolean ret = FALSE;
197 NMVpnServiceState state;
199 g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), FALSE);
201 priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
202 state = nm_vpn_service_plugin_get_state (plugin);
204 case NM_VPN_SERVICE_STATE_STOPPING:
207 NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS,
209 "Could not process the request because the VPN connection is already being stopped.");
211 case NM_VPN_SERVICE_STATE_STOPPED:
214 NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED,
216 "Could not process the request because no VPN connection was active.");
218 case NM_VPN_SERVICE_STATE_STARTING:
219 _emit_failure (plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
221 case NM_VPN_SERVICE_STATE_STARTED:
222 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING);
223 ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err);
224 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
226 case NM_VPN_SERVICE_STATE_INIT:
231 g_warning ("Unhandled VPN service state %d", state);
232 g_assert_not_reached ();
240 nm_vpn_service_plugin_emit_quit (NMVpnServicePlugin *plugin)
242 g_signal_emit (plugin, signals[QUIT], 0);
246 connect_timer_expired (gpointer data)
248 NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (data);
251 NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->connect_timer = 0;
252 g_message ("Connect timer expired, disconnecting.");
253 nm_vpn_service_plugin_disconnect (plugin, &err);
255 g_warning ("Disconnect failed: %s", err->message);
259 return G_SOURCE_REMOVE;
263 quit_timer_expired (gpointer data)
265 NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN (data);
267 NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self)->quit_timer = 0;
268 nm_vpn_service_plugin_emit_quit (self);
269 return G_SOURCE_REMOVE;
273 schedule_quit_timer (NMVpnServicePlugin *self)
275 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self);
277 nm_clear_g_source (&priv->quit_timer);
278 priv->quit_timer = g_timeout_add_seconds (NM_VPN_SERVICE_PLUGIN_QUIT_TIMER,
284 fail_stop (gpointer data)
286 NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN (data);
288 NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self)->fail_stop_id = 0;
289 nm_vpn_service_plugin_set_state (self, NM_VPN_SERVICE_STATE_STOPPED);
290 return G_SOURCE_REMOVE;
294 schedule_fail_stop (NMVpnServicePlugin *plugin, guint timeout_secs)
296 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
298 nm_clear_g_source (&priv->fail_stop_id);
300 priv->fail_stop_id = g_timeout_add_seconds (timeout_secs, fail_stop, plugin);
302 priv->fail_stop_id = g_idle_add (fail_stop, plugin);
306 nm_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
309 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
311 g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
312 g_return_if_fail (config != NULL);
314 priv->got_config = TRUE;
316 (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4);
317 (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6);
319 /* Record the items that need to also be inserted into the
320 * ip4config, for compatibility with older daemons.
323 g_variant_unref (priv->banner);
324 priv->banner = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_BANNER,
325 G_VARIANT_TYPE ("s"));
327 g_variant_unref (priv->tundev);
328 priv->tundev = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_TUNDEV,
329 G_VARIANT_TYPE ("s"));
331 g_variant_unref (priv->gateway);
332 priv->gateway = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY,
333 G_VARIANT_TYPE ("u"));
335 g_variant_unref (priv->mtu);
336 priv->mtu = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_MTU,
337 G_VARIANT_TYPE ("u"));
339 g_signal_emit (plugin, signals[CONFIG], 0, config);
340 nmdbus_vpn_plugin_emit_config (priv->dbus_vpn_service_plugin, config);
344 nm_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
345 GVariant *ip4_config)
347 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
348 GVariant *combined_config;
349 GVariantBuilder builder;
354 g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
355 g_return_if_fail (ip4_config != NULL);
357 priv->got_ip4 = TRUE;
359 /* Old plugins won't send the "config" signal and thus can't send
360 * NM_VPN_SERVICE_PLUGIN_CONFIG_HAS_IP4 either. But since they don't support IPv6,
361 * we can safely assume that, if we don't receive a "config" signal but do
362 * receive an "ip4-config" signal, the old plugin supports IPv4.
364 if (!priv->got_config)
365 priv->has_ip4 = TRUE;
367 /* Older NetworkManager daemons expect all config info to be in
368 * the ip4 config, so they won't even notice the "config" signal
369 * being emitted. So just copy all of that data into the ip4
372 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
373 g_variant_iter_init (&iter, ip4_config);
374 while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) {
375 g_variant_builder_add (&builder, "{sv}", key, value);
376 g_variant_unref (value);
380 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, priv->banner);
382 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, priv->tundev);
384 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, priv->gateway);
386 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, priv->mtu);
388 combined_config = g_variant_builder_end (&builder);
389 g_variant_ref_sink (combined_config);
390 g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config);
391 nmdbus_vpn_plugin_emit_ip4_config (priv->dbus_vpn_service_plugin, combined_config);
392 g_variant_unref (combined_config);
394 if ( priv->has_ip4 == priv->got_ip4
395 && priv->has_ip6 == priv->got_ip6)
396 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
400 nm_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
401 GVariant *ip6_config)
403 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
405 g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
406 g_return_if_fail (ip6_config != NULL);
408 g_variant_ref_sink (ip6_config);
410 priv->got_ip6 = TRUE;
411 g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config);
412 nmdbus_vpn_plugin_emit_ip6_config (priv->dbus_vpn_service_plugin, ip6_config);
414 g_variant_unref (ip6_config);
416 if ( priv->has_ip4 == priv->got_ip4
417 && priv->has_ip6 == priv->got_ip6)
418 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
422 connect_timer_start (NMVpnServicePlugin *plugin)
424 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
426 nm_clear_g_source (&priv->connect_timer);
427 priv->connect_timer = g_timeout_add_seconds (60, connect_timer_expired, plugin);
431 peer_vanished (GDBusConnection *connection,
432 const gchar *sender_name,
433 const gchar *object_path,
434 const gchar *interface_name,
435 const gchar *signal_name,
436 GVariant *parameters,
439 nm_vpn_service_plugin_disconnect (NM_VPN_SERVICE_PLUGIN (user_data), NULL);
443 watch_peer (NMVpnServicePlugin *plugin,
444 GDBusMethodInvocation *context)
446 GDBusConnection *connection = g_dbus_method_invocation_get_connection (context);
447 const gchar *peer = g_dbus_message_get_sender (g_dbus_method_invocation_get_message (context));
449 return g_dbus_connection_signal_subscribe (connection,
450 "org.freedesktop.DBus",
451 "org.freedesktop.DBus",
453 "/org/freedesktop/DBus",
455 G_DBUS_SIGNAL_FLAGS_NONE,
462 _connect_generic (NMVpnServicePlugin *plugin,
463 GDBusMethodInvocation *context,
464 GVariant *properties,
467 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
468 NMVpnServicePluginClass *vpn_class = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin);
469 NMConnection *connection;
470 gboolean success = FALSE;
471 GError *error = NULL;
472 guint fail_stop_timeout = 0;
474 if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
475 priv->state != NM_VPN_SERVICE_STATE_INIT) {
476 g_dbus_method_invocation_return_error (context,
478 NM_VPN_PLUGIN_ERROR_WRONG_STATE,
479 "Could not start connection: wrong plugin state %d",
484 connection = nm_simple_connection_new_from_dbus (properties, &error);
486 g_dbus_method_invocation_return_error (context,
488 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
489 "Invalid connection: %s",
491 g_clear_error (&error);
495 priv->interactive = FALSE;
496 if (details && !vpn_class->connect_interactive) {
497 g_dbus_method_invocation_return_error (context,
499 NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
500 "Plugin does not implement ConnectInteractive()");
504 nm_clear_g_source (&priv->fail_stop_id);
506 if (priv->dbus_watch_peer)
507 priv->peer_watch_id = watch_peer (plugin, context);
510 priv->interactive = TRUE;
511 success = vpn_class->connect_interactive (plugin, connection, details, &error);
512 if (g_error_matches (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) {
513 /* Give NetworkManager a bit of time to fall back to Connect() */
514 fail_stop_timeout = 5;
517 success = vpn_class->connect (plugin, connection, &error);
520 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
522 g_dbus_method_invocation_return_value (context, NULL);
524 /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
525 connect_timer_start (plugin);
527 g_dbus_method_invocation_take_error (context, error);
529 /* Stop the plugin from an idle handler so that the Connect
530 * method return gets sent before the STOP StateChanged signal.
532 schedule_fail_stop (plugin, fail_stop_timeout);
535 g_object_unref (connection);
539 impl_vpn_service_plugin_connect (NMVpnServicePlugin *plugin,
540 GDBusMethodInvocation *context,
541 GVariant *connection,
544 _connect_generic (plugin, context, connection, NULL);
548 impl_vpn_service_plugin_connect_interactive (NMVpnServicePlugin *plugin,
549 GDBusMethodInvocation *context,
550 GVariant *connection,
554 _connect_generic (plugin, context, connection, details);
557 /***************************************************************/
560 impl_vpn_service_plugin_need_secrets (NMVpnServicePlugin *plugin,
561 GDBusMethodInvocation *context,
562 GVariant *properties,
565 NMConnection *connection;
566 const char *setting_name;
568 GError *error = NULL;
570 connection = nm_simple_connection_new_from_dbus (properties, &error);
572 g_dbus_method_invocation_return_error (context,
574 NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION,
575 "The connection was invalid: %s",
577 g_error_free (error);
581 if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->need_secrets) {
582 g_dbus_method_invocation_return_value (context,
583 g_variant_new ("(s)", ""));
587 needed = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->need_secrets (plugin, connection, &setting_name, &error);
589 g_dbus_method_invocation_take_error (context, error);
594 /* Push back the quit timer so the VPN plugin doesn't quit in the
595 * middle of asking the user for secrets.
597 schedule_quit_timer (plugin);
599 g_assert (setting_name);
600 g_dbus_method_invocation_return_value (context,
601 g_variant_new ("(s)", setting_name));
603 /* No secrets required */
604 g_dbus_method_invocation_return_value (context,
605 g_variant_new ("(s)", ""));
610 impl_vpn_service_plugin_new_secrets (NMVpnServicePlugin *plugin,
611 GDBusMethodInvocation *context,
612 GVariant *properties,
615 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
616 NMConnection *connection;
617 GError *error = NULL;
620 if (priv->state != NM_VPN_SERVICE_STATE_STARTING) {
621 g_dbus_method_invocation_return_error (context,
623 NM_VPN_PLUGIN_ERROR_WRONG_STATE,
624 "Could not accept new secrets: wrong plugin state %d",
629 connection = nm_simple_connection_new_from_dbus (properties, &error);
631 g_dbus_method_invocation_return_error (context,
633 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
634 "Invalid connection: %s",
636 g_clear_error (&error);
640 if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets) {
641 g_dbus_method_invocation_return_error (context,
643 NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
644 "Could not accept new secrets: plugin cannot process interactive secrets");
645 g_object_unref (connection);
649 success = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, &error);
651 g_dbus_method_invocation_return_value (context, NULL);
653 /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
654 connect_timer_start (plugin);
656 g_dbus_method_invocation_take_error (context, error);
658 /* Stop the plugin from and idle handler so that the NewSecrets
659 * method return gets sent before the STOP StateChanged signal.
661 schedule_fail_stop (plugin, 0);
664 g_object_unref (connection);
668 * nm_vpn_service_plugin_secrets_required:
669 * @plugin: the #NMVpnServicePlugin
670 * @message: an information message about why secrets are required, if any
671 * @hints: VPN specific secret names for required new secrets
673 * Called by VPN plugin implementations to signal to NetworkManager that secrets
674 * are required during the connection process. This signal may be used to
675 * request new secrets when the secrets originally provided by NetworkManager
676 * are insufficient, or the VPN process indicates that it needs additional
677 * information to complete the request.
682 nm_vpn_service_plugin_secrets_required (NMVpnServicePlugin *plugin,
686 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
688 /* Plugin must be able to accept the new secrets if it calls this method */
689 g_return_if_fail (NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets);
691 /* Plugin cannot call this method if NetworkManager didn't originally call
692 * ConnectInteractive().
694 g_return_if_fail (priv->interactive == TRUE);
696 /* Cancel the connect timer since secrets might take a while. It'll
697 * get restarted when the secrets come back via NewSecrets().
699 nm_clear_g_source (&priv->connect_timer);
701 g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints);
702 nmdbus_vpn_plugin_emit_secrets_required (priv->dbus_vpn_service_plugin, message, hints);
705 /***************************************************************/
707 #define DATA_KEY_TAG "DATA_KEY="
708 #define DATA_VAL_TAG "DATA_VAL="
709 #define SECRET_KEY_TAG "SECRET_KEY="
710 #define SECRET_VAL_TAG "SECRET_VAL="
713 free_secret (gpointer data)
717 memset (secret, 0, strlen (secret));
722 * nm_vpn_service_plugin_read_vpn_details:
723 * @fd: file descriptor to read from, usually stdin (0)
724 * @out_data: (out) (transfer full): on successful return, a hash table
725 * (mapping char*:char*) containing the key/value pairs of VPN data items
726 * @out_secrets: (out) (transfer full): on successful return, a hash table
727 * (mapping char*:char*) containing the key/value pairsof VPN secrets
729 * Parses key/value pairs from a file descriptor (normally stdin) passed by
730 * an applet when the applet calls the authentication dialog of the VPN plugin.
732 * Returns: %TRUE if reading values was successful, %FALSE if not
737 nm_vpn_service_plugin_read_vpn_details (int fd,
738 GHashTable **out_data,
739 GHashTable **out_secrets)
741 GHashTable *data, *secrets;
742 gboolean success = FALSE;
743 char *key = NULL, *val = NULL;
748 g_return_val_if_fail (*out_data == NULL, FALSE);
750 g_return_val_if_fail (*out_secrets == NULL, FALSE);
752 data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
753 secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_secret);
755 line = g_string_new (NULL);
757 /* Read stdin for data and secret items until we get a DONE */
760 GHashTable *hash = NULL;
763 nr = read (fd, &c, 1);
765 if (errno == EAGAIN) {
773 g_string_append_c (line, c);
777 /* Check for the finish marker */
778 if (strcmp (line->str, "DONE") == 0)
781 /* Otherwise it's a data/secret item */
782 if (strncmp (line->str, DATA_KEY_TAG, strlen (DATA_KEY_TAG)) == 0) {
784 key = g_strdup (line->str + strlen (DATA_KEY_TAG));
785 } else if (strncmp (line->str, DATA_VAL_TAG, strlen (DATA_VAL_TAG)) == 0) {
787 val = g_strdup (line->str + strlen (DATA_VAL_TAG));
788 } else if (strncmp (line->str, SECRET_KEY_TAG, strlen (SECRET_KEY_TAG)) == 0) {
790 key = g_strdup (line->str + strlen (SECRET_KEY_TAG));
791 } else if (strncmp (line->str, SECRET_VAL_TAG, strlen (SECRET_VAL_TAG)) == 0) {
793 val = g_strdup (line->str + strlen (SECRET_VAL_TAG));
795 g_string_truncate (line, 0);
797 if (key && val && hash) {
798 g_hash_table_insert (hash, key, val);
801 success = TRUE; /* Got at least one value */
809 g_hash_table_destroy (data);
812 *out_secrets = secrets;
814 g_hash_table_destroy (secrets);
816 g_hash_table_destroy (data);
817 g_hash_table_destroy (secrets);
820 g_string_free (line, TRUE);
825 * nm_vpn_service_plugin_get_secret_flags:
826 * @data: hash table containing VPN key/value pair data items
827 * @secret_name: VPN secret key name for which to retrieve flags for
828 * @out_flags: (out): on success, the flags associated with @secret_name
830 * Given a VPN secret key name, attempts to find the corresponding flags data
831 * item in @data. If found, converts the flags data item to
832 * #NMSettingSecretFlags and returns it.
834 * Returns: %TRUE if the flag data item was found and successfully converted
835 * to flags, %FALSE if not
840 nm_vpn_service_plugin_get_secret_flags (GHashTable *data,
841 const char *secret_name,
842 NMSettingSecretFlags *out_flags)
847 gboolean success = FALSE;
849 g_return_val_if_fail (data != NULL, FALSE);
850 g_return_val_if_fail (secret_name != NULL, FALSE);
851 g_return_val_if_fail (out_flags != NULL, FALSE);
852 g_return_val_if_fail (*out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE);
854 flag_name = g_strdup_printf ("%s-flags", secret_name);
856 /* Try new flags value first */
857 val = g_hash_table_lookup (data, flag_name);
860 tmp = strtoul (val, NULL, 10);
861 if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) {
862 *out_flags = (NMSettingSecretFlags) tmp;
871 /***************************************************************/
874 impl_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin,
875 GDBusMethodInvocation *context,
878 GError *error = NULL;
880 if (nm_vpn_service_plugin_disconnect (plugin, &error))
881 g_dbus_method_invocation_return_value (context, NULL);
883 g_dbus_method_invocation_take_error (context, error);
887 impl_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
888 GDBusMethodInvocation *context,
892 nm_vpn_service_plugin_set_config (plugin, config);
893 g_dbus_method_invocation_return_value (context, NULL);
897 impl_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
898 GDBusMethodInvocation *context,
902 nm_vpn_service_plugin_set_ip4_config (plugin, config);
903 g_dbus_method_invocation_return_value (context, NULL);
907 impl_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
908 GDBusMethodInvocation *context,
912 nm_vpn_service_plugin_set_ip6_config (plugin, config);
913 g_dbus_method_invocation_return_value (context, NULL);
917 impl_vpn_service_plugin_set_failure (NMVpnServicePlugin *plugin,
918 GDBusMethodInvocation *context,
922 nm_vpn_service_plugin_failure (plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG);
923 g_dbus_method_invocation_return_value (context, NULL);
926 /*********************************************************************/
929 sigterm_handler (int signum)
931 g_slist_foreach (active_plugins, (GFunc) nm_vpn_service_plugin_emit_quit, NULL);
935 setup_unix_signal_handler (void)
937 struct sigaction action;
940 action.sa_handler = sigterm_handler;
941 sigemptyset (&block_mask);
942 action.sa_mask = block_mask;
944 sigaction (SIGINT, &action, NULL);
945 sigaction (SIGTERM, &action, NULL);
948 /*********************************************************************/
951 one_plugin_destroyed (gpointer data,
954 active_plugins = g_slist_remove (active_plugins, object);
958 nm_vpn_service_plugin_init (NMVpnServicePlugin *plugin)
960 active_plugins = g_slist_append (active_plugins, plugin);
961 g_object_weak_ref (G_OBJECT (plugin),
962 one_plugin_destroyed,
967 init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
969 NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (initable);
970 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
971 GDBusConnection *connection = NULL;
974 gboolean success = FALSE;
976 if (!priv->dbus_service_name) {
977 g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
978 _("No service name specified"));
982 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
986 proxy = g_dbus_proxy_new_sync (connection,
987 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
988 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
997 ret = g_dbus_proxy_call_sync (proxy,
999 g_variant_new ("(su)", priv->dbus_service_name, 0),
1000 G_DBUS_CALL_FLAGS_NONE, -1,
1001 cancellable, error);
1002 g_object_unref (proxy);
1004 if (error && *error)
1005 g_dbus_error_strip_remote_error (*error);
1008 g_variant_unref (ret);
1010 priv->dbus_vpn_service_plugin = nmdbus_vpn_plugin_skeleton_new ();
1011 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_vpn_service_plugin),
1013 NM_VPN_DBUS_PLUGIN_PATH,
1017 _nm_dbus_bind_properties (plugin, priv->dbus_vpn_service_plugin);
1018 _nm_dbus_bind_methods (plugin, priv->dbus_vpn_service_plugin,
1019 "Connect", impl_vpn_service_plugin_connect,
1020 "ConnectInteractive", impl_vpn_service_plugin_connect_interactive,
1021 "NeedSecrets", impl_vpn_service_plugin_need_secrets,
1022 "NewSecrets", impl_vpn_service_plugin_new_secrets,
1023 "Disconnect", impl_vpn_service_plugin_disconnect,
1024 "SetConfig", impl_vpn_service_plugin_set_config,
1025 "SetIp4Config", impl_vpn_service_plugin_set_ip4_config,
1026 "SetIp6Config", impl_vpn_service_plugin_set_ip6_config,
1027 "SetFailure", impl_vpn_service_plugin_set_failure,
1030 nm_vpn_service_plugin_set_connection (plugin, connection);
1031 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_INIT);
1036 g_clear_object (&connection);
1042 set_property (GObject *object, guint prop_id,
1043 const GValue *value, GParamSpec *pspec)
1045 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (object);
1048 case PROP_DBUS_SERVICE_NAME:
1049 /* Construct-only */
1050 priv->dbus_service_name = g_value_dup_string (value);
1052 case PROP_DBUS_WATCH_PEER:
1053 /* Construct-only */
1054 priv->dbus_watch_peer = g_value_get_boolean (value);
1057 nm_vpn_service_plugin_set_state (NM_VPN_SERVICE_PLUGIN (object),
1058 (NMVpnServiceState) g_value_get_enum (value));
1061 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1067 get_property (GObject *object, guint prop_id,
1068 GValue *value, GParamSpec *pspec)
1070 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (object);
1073 case PROP_DBUS_SERVICE_NAME:
1074 g_value_set_string (value, priv->dbus_service_name);
1076 case PROP_DBUS_WATCH_PEER:
1077 g_value_set_boolean (value, priv->dbus_watch_peer);
1080 g_value_set_enum (value, nm_vpn_service_plugin_get_state (NM_VPN_SERVICE_PLUGIN (object)));
1083 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1089 dispose (GObject *object)
1091 NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (object);
1092 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
1093 NMVpnServiceState state;
1096 nm_clear_g_source (&priv->fail_stop_id);
1097 nm_clear_g_source (&priv->quit_timer);
1098 nm_clear_g_source (&priv->connect_timer);
1100 state = nm_vpn_service_plugin_get_state (plugin);
1102 if (state == NM_VPN_SERVICE_STATE_STARTED ||
1103 state == NM_VPN_SERVICE_STATE_STARTING)
1104 nm_vpn_service_plugin_disconnect (plugin, &err);
1107 g_warning ("Error disconnecting VPN connection: %s", err->message);
1111 G_OBJECT_CLASS (nm_vpn_service_plugin_parent_class)->dispose (object);
1115 finalize (GObject *object)
1117 NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (object);
1118 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
1120 nm_vpn_service_plugin_set_connection (plugin, NULL);
1121 g_free (priv->dbus_service_name);
1123 g_clear_pointer (&priv->banner, g_variant_unref);
1124 g_clear_pointer (&priv->tundev, g_variant_unref);
1125 g_clear_pointer (&priv->gateway, g_variant_unref);
1126 g_clear_pointer (&priv->mtu, g_variant_unref);
1128 G_OBJECT_CLASS (nm_vpn_service_plugin_parent_class)->finalize (object);
1132 state_changed (NMVpnServicePlugin *plugin, NMVpnServiceState state)
1134 NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
1137 case NM_VPN_SERVICE_STATE_STARTING:
1138 nm_clear_g_source (&priv->quit_timer);
1139 nm_clear_g_source (&priv->fail_stop_id);
1141 case NM_VPN_SERVICE_STATE_STOPPED:
1142 if (priv->dbus_watch_peer)
1143 nm_vpn_service_plugin_emit_quit (plugin);
1145 schedule_quit_timer (plugin);
1146 if (priv->peer_watch_id) {
1147 g_dbus_connection_signal_unsubscribe (nm_vpn_service_plugin_get_connection (plugin),
1148 priv->peer_watch_id);
1149 priv->peer_watch_id = 0;
1153 /* Clean up all timers we might have set up. */
1154 nm_clear_g_source (&priv->connect_timer);
1155 nm_clear_g_source (&priv->quit_timer);
1156 nm_clear_g_source (&priv->fail_stop_id);
1162 nm_vpn_service_plugin_class_init (NMVpnServicePluginClass *plugin_class)
1164 GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
1166 g_type_class_add_private (object_class, sizeof (NMVpnServicePluginPrivate));
1168 /* virtual methods */
1169 object_class->set_property = set_property;
1170 object_class->get_property = get_property;
1171 object_class->dispose = dispose;
1172 object_class->finalize = finalize;
1174 plugin_class->state_changed = state_changed;
1179 * NMVpnServicePlugin:service-name:
1181 * The D-Bus service name of this plugin.
1185 g_object_class_install_property
1186 (object_class, PROP_DBUS_SERVICE_NAME,
1187 g_param_spec_string (NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, "", "",
1190 G_PARAM_CONSTRUCT_ONLY |
1191 G_PARAM_STATIC_STRINGS));
1194 * NMVpnServicePlugin:watch-peer:
1196 * Whether to watch for D-Bus peer's changes.
1200 g_object_class_install_property
1201 (object_class, PROP_DBUS_WATCH_PEER,
1202 g_param_spec_boolean (NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER, "", "",
1205 G_PARAM_CONSTRUCT_ONLY));
1208 * NMVpnServicePlugin:state:
1210 * The state of the plugin.
1214 g_object_class_install_property
1215 (object_class, PROP_STATE,
1216 g_param_spec_enum (NM_VPN_SERVICE_PLUGIN_STATE, "", "",
1217 NM_TYPE_VPN_SERVICE_STATE,
1218 NM_VPN_SERVICE_STATE_INIT,
1220 G_PARAM_STATIC_STRINGS));
1223 signals[STATE_CHANGED] =
1224 g_signal_new ("state-changed",
1225 G_OBJECT_CLASS_TYPE (object_class),
1227 G_STRUCT_OFFSET (NMVpnServicePluginClass, state_changed),
1233 signals[SECRETS_REQUIRED] =
1234 g_signal_new ("secrets-required",
1235 G_OBJECT_CLASS_TYPE (object_class),
1239 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRV);
1242 g_signal_new ("config",
1243 G_OBJECT_CLASS_TYPE (object_class),
1245 G_STRUCT_OFFSET (NMVpnServicePluginClass, config),
1251 signals[IP4_CONFIG] =
1252 g_signal_new ("ip4-config",
1253 G_OBJECT_CLASS_TYPE (object_class),
1255 G_STRUCT_OFFSET (NMVpnServicePluginClass, ip4_config),
1261 signals[IP6_CONFIG] =
1262 g_signal_new ("ip6-config",
1263 G_OBJECT_CLASS_TYPE (object_class),
1265 G_STRUCT_OFFSET (NMVpnServicePluginClass, ip6_config),
1271 signals[LOGIN_BANNER] =
1272 g_signal_new ("login-banner",
1273 G_OBJECT_CLASS_TYPE (object_class),
1275 G_STRUCT_OFFSET (NMVpnServicePluginClass, login_banner),
1282 g_signal_new ("failure",
1283 G_OBJECT_CLASS_TYPE (object_class),
1285 G_STRUCT_OFFSET (NMVpnServicePluginClass, failure),
1292 g_signal_new ("quit",
1293 G_OBJECT_CLASS_TYPE (object_class),
1295 G_STRUCT_OFFSET (NMVpnServicePluginClass, quit),
1301 setup_unix_signal_handler ();
1305 nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface)
1307 iface->init = init_sync;