diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml
index 7a02b73ef..02b750134 100644
--- a/introspection/nm-device.xml
+++ b/introspection/nm-device.xml
@@ -65,10 +65,11 @@
+
+
Disconnects a device and prevents the device from automatically activating further connections without user intervention.
-
diff --git a/introspection/nm-manager-client.xml b/introspection/nm-manager-client.xml
index 9ede13e3c..f30d1d671 100644
--- a/introspection/nm-manager-client.xml
+++ b/introspection/nm-manager-client.xml
@@ -31,16 +31,19 @@ object. dbus-glib generates the same bound function names for D-Bus the methods
+
+
+
diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml
index 88686566d..e1b466799 100644
--- a/introspection/nm-manager.xml
+++ b/introspection/nm-manager.xml
@@ -77,6 +77,7 @@
Deactivate an active connection.
+
The currently active connection to deactivate.
diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c
index bbc54a862..a2eb74622 100644
--- a/src/nm-device-interface.c
+++ b/src/nm-device-interface.c
@@ -19,6 +19,8 @@
* Copyright (C) 2007 - 2010 Red Hat, Inc.
*/
+#include
+
#include "nm-marshal.h"
#include "nm-setting-connection.h"
#include "nm-device-interface.h"
@@ -26,8 +28,8 @@
#include "nm-properties-changed-signal.h"
#include "nm-rfkill.h"
-static gboolean impl_device_disconnect (NMDeviceInterface *device,
- GError **error);
+static void impl_device_disconnect (NMDeviceInterface *device,
+ DBusGMethodInvocation *context);
#include "nm-device-interface-glue.h"
@@ -211,6 +213,13 @@ nm_device_interface_init (gpointer g_iface)
G_TYPE_NONE, 3,
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+ g_signal_new (NM_DEVICE_INTERFACE_DISCONNECT_REQUEST,
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
dbus_g_object_type_install_info (iface_type,
&dbus_glib_nm_device_interface_object_info);
@@ -334,11 +343,11 @@ nm_device_interface_disconnect (NMDeviceInterface *device,
return success;
}
-static gboolean
+static void
impl_device_disconnect (NMDeviceInterface *device,
- GError **error)
+ DBusGMethodInvocation *context)
{
- return nm_device_interface_disconnect (device, error);
+ g_signal_emit_by_name (device, NM_DEVICE_INTERFACE_DISCONNECT_REQUEST, context);
}
void
diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h
index ea152602e..1fee131bd 100644
--- a/src/nm-device-interface.h
+++ b/src/nm-device-interface.h
@@ -45,6 +45,8 @@ typedef enum
#define NM_DEVICE_INTERFACE_ERROR (nm_device_interface_error_quark ())
#define NM_TYPE_DEVICE_INTERFACE_ERROR (nm_device_interface_error_get_type ())
+#define NM_DEVICE_INTERFACE_DISCONNECT_REQUEST "disconnect-request"
+
#define NM_DEVICE_INTERFACE_UDI "udi"
#define NM_DEVICE_INTERFACE_IFACE "interface"
#define NM_DEVICE_INTERFACE_DRIVER "driver"
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 7560b81af..85b847b0d 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -67,9 +67,9 @@ static void impl_manager_activate_connection (NMManager *manager,
const char *specific_object_path,
DBusGMethodInvocation *context);
-static gboolean impl_manager_deactivate_connection (NMManager *manager,
- const char *connection_path,
- GError **error);
+static void impl_manager_deactivate_connection (NMManager *manager,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
static void impl_manager_sleep (NMManager *manager,
gboolean do_sleep,
@@ -714,6 +714,41 @@ out:
nm_auth_chain_unref (chain);
}
+static gboolean
+check_user_authorized (NMDBusManager *dbus_mgr,
+ DBusGProxy *user_proxy,
+ DBusGMethodInvocation *context,
+ NMConnectionScope scope,
+ gulong *out_sender_uid,
+ const char **out_error_desc)
+{
+ g_return_val_if_fail (dbus_mgr != NULL, FALSE);
+ g_return_val_if_fail (user_proxy != NULL, FALSE);
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (out_sender_uid != NULL, FALSE);
+ g_return_val_if_fail (out_error_desc != NULL, FALSE);
+
+ *out_sender_uid = G_MAXULONG;
+
+ /* Get the UID */
+ if (!nm_auth_get_caller_uid (context, dbus_mgr, out_sender_uid, out_error_desc))
+ return FALSE;
+
+ /* root gets to do anything */
+ if (0 == *out_sender_uid)
+ return TRUE;
+
+ /* Check whether the UID is authorized for user connections */
+ if ( scope == NM_CONNECTION_SCOPE_USER
+ && !nm_auth_uid_authorized (*out_sender_uid,
+ dbus_mgr,
+ user_proxy,
+ out_error_desc))
+ return FALSE;
+
+ return TRUE;
+}
+
static void
pending_activation_check_authorized (PendingActivation *pending,
NMDBusManager *dbus_mgr,
@@ -727,8 +762,12 @@ pending_activation_check_authorized (PendingActivation *pending,
g_return_if_fail (dbus_mgr != NULL);
g_return_if_fail (user_proxy != NULL);
- /* Get the UID */
- if (!nm_auth_get_caller_uid (pending->context, dbus_mgr, &sender_uid, &error_desc)) {
+ if (!check_user_authorized (dbus_mgr,
+ user_proxy,
+ pending->context,
+ pending->scope,
+ &sender_uid,
+ &error_desc)) {
error = g_error_new_literal (NM_MANAGER_ERROR,
NM_MANAGER_ERROR_PERMISSION_DENIED,
error_desc);
@@ -737,26 +776,12 @@ pending_activation_check_authorized (PendingActivation *pending,
return;
}
- /* root gets to do anything */
+ /* Yay for root */
if (0 == sender_uid) {
pending->callback (pending, NULL);
return;
}
- /* Check whether the UID is authorized for user connections */
- if ( pending->scope == NM_CONNECTION_SCOPE_USER
- && !nm_auth_uid_authorized (sender_uid,
- dbus_mgr,
- user_proxy,
- &error_desc)) {
- error = g_error_new_literal (NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- error_desc);
- pending->callback (pending, error);
- g_error_free (error);
- return;
- }
-
/* First check if the user is allowed to use networking at all, giving
* the user a chance to authenticate to gain the permission.
*/
@@ -765,6 +790,7 @@ pending_activation_check_authorized (PendingActivation *pending,
NULL,
pending_auth_net_done,
pending);
+ g_assert (pending->chain);
nm_auth_chain_add_call (pending->chain,
NM_AUTH_PERMISSION_NETWORK_CONTROL,
TRUE);
@@ -1805,6 +1831,175 @@ manager_modem_enabled_changed (NMModem *device, gpointer user_data)
nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN);
}
+static GError *
+deactivate_disconnect_check_error (GError *auth_error,
+ NMAuthCallResult result,
+ const char *detail)
+{
+ if (auth_error) {
+ nm_log_dbg (LOGD_CORE, "%s request failed: %s", detail, auth_error->message);
+ return g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "%s request failed: %s",
+ detail, auth_error->message);
+ } else if (result != NM_AUTH_CALL_RESULT_YES) {
+ return g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized to %s user connections",
+ detail);
+ }
+ return NULL;
+}
+
+static void
+disconnect_user_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+ NMDevice *device;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS));
+ ret_error = deactivate_disconnect_check_error (error, result, "Disconnect");
+g_message ("%s: here! ret error %p", __func__, ret_error);
+ if (!ret_error) {
+ /* Everything authorized, deactivate the connection */
+ device = nm_auth_chain_get_data (chain, "device");
+g_message ("%s: here! device %p", __func__, device);
+ if (nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error))
+ dbus_g_method_return (context);
+ }
+
+ if (ret_error)
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+disconnect_net_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+ NMConnectionScope scope;
+ NMDevice *device;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+ ret_error = deactivate_disconnect_check_error (error, result, "Disconnect");
+g_message ("%s: here! ret error %p", __func__, ret_error);
+ if (ret_error) {
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ goto done;
+ }
+
+ /* If it's a system connection, we're done */
+ device = nm_auth_chain_get_data (chain, "device");
+ scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope"));
+ if (scope == NM_CONNECTION_SCOPE_USER) {
+ NMAuthChain *user_chain;
+
+ /* It's a user connection, so we need to ensure the caller is
+ * authorized to manipulate user connections.
+ */
+ user_chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_user_auth_done_cb, self);
+ g_assert (user_chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, user_chain);
+
+ nm_auth_chain_set_data (user_chain, "device", g_object_ref (device), g_object_unref);
+ nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE);
+ } else {
+ if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error)) {
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+ } else
+ dbus_g_method_return (context);
+ }
+
+done:
+ nm_auth_chain_unref (chain);
+}
+
+static void
+manager_device_disconnect_request (NMDevice *device,
+ DBusGMethodInvocation *context,
+ NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMActRequest *req;
+ NMConnection *connection;
+ GError *error = NULL;
+ NMConnectionScope scope;
+ gulong sender_uid = G_MAXULONG;
+ const char *error_desc = NULL;
+
+ req = nm_device_get_act_request (device);
+ if (!req) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+ "This device is not active");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+
+ /* Need to check the caller's permissions and stuff before we can
+ * deactivate the connection.
+ */
+ scope = nm_connection_get_scope (connection);
+ if (!check_user_authorized (priv->dbus_mgr,
+ priv->user_proxy,
+ context,
+ scope,
+ &sender_uid,
+ &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Yay for root */
+ if (0 == sender_uid) {
+ if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ } else
+ dbus_g_method_return (context);
+ } else {
+ NMAuthChain *chain;
+
+ /* Otherwise validate the user request */
+ chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_net_auth_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref);
+ nm_auth_chain_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+ }
+}
+
static void
add_device (NMManager *self, NMDevice *device)
{
@@ -1832,6 +2027,10 @@ add_device (NMManager *self, NMDevice *device)
G_CALLBACK (manager_device_state_changed),
self);
+ g_signal_connect (device, NM_DEVICE_INTERFACE_DISCONNECT_REQUEST,
+ G_CALLBACK (manager_device_disconnect_request),
+ self);
+
if (NM_IS_DEVICE_WIFI (device)) {
/* Attach to the access-point-added signal so that the manager can fill
* non-SSID-broadcasting APs with an SSID.
@@ -2895,15 +3094,173 @@ done:
return success;
}
-static gboolean
-impl_manager_deactivate_connection (NMManager *manager,
- const char *connection_path,
- GError **error)
+static void
+deactivate_user_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- return nm_manager_deactivate_connection (manager,
- connection_path,
- NM_DEVICE_STATE_REASON_USER_REQUESTED,
- error);
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS));
+ ret_error = deactivate_disconnect_check_error (error, result, "Deactivate");
+ if (!ret_error) {
+ /* Everything authorized, deactivate the connection */
+ if (nm_manager_deactivate_connection (self,
+ nm_auth_chain_get_data (chain, "path"),
+ NM_DEVICE_STATE_REASON_USER_REQUESTED,
+ &ret_error))
+ dbus_g_method_return (context);
+ }
+
+ if (ret_error)
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+deactivate_net_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+ const char *active_path;
+ NMConnectionScope scope;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+ ret_error = deactivate_disconnect_check_error (error, result, "Deactivate");
+ if (ret_error) {
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ goto done;
+ }
+
+ /* If it's a system connection, we're done */
+ active_path = nm_auth_chain_get_data (chain, "path");
+ scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope"));
+ if (scope == NM_CONNECTION_SCOPE_USER) {
+ NMAuthChain *user_chain;
+
+ /* It's a user connection, so we need to ensure the caller is
+ * authorized to manipulate user connections.
+ */
+ user_chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_user_auth_done_cb, self);
+ g_assert (user_chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, user_chain);
+
+ nm_auth_chain_set_data (user_chain, "path", g_strdup (active_path), g_free);
+ nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE);
+ } else {
+ if (!nm_manager_deactivate_connection (self,
+ active_path,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED,
+ &ret_error)) {
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+ } else
+ dbus_g_method_return (context);
+ }
+
+done:
+ nm_auth_chain_unref (chain);
+}
+
+static void
+impl_manager_deactivate_connection (NMManager *self,
+ const char *active_path,
+ DBusGMethodInvocation *context)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMConnection *connection = NULL;
+ GError *error = NULL;
+ GSList *iter;
+ NMAuthChain *chain;
+ gulong sender_uid = G_MAXULONG;
+ NMConnectionScope scope;
+ const char *error_desc = NULL;
+
+ /* Check for device connections first */
+ for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+ NMActRequest *req;
+ const char *req_path = NULL;
+
+ req = nm_device_get_act_request (NM_DEVICE (iter->data));
+ if (req)
+ req_path = nm_act_request_get_active_connection_path (req);
+
+ if (req_path && !strcmp (active_path, req_path)) {
+ connection = nm_act_request_get_connection (req);
+ break;
+ }
+ }
+
+ /* Maybe it's a VPN */
+ if (!connection)
+ connection = nm_vpn_manager_get_connection_for_active (priv->vpn_manager, active_path);
+
+ if (!connection) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+ "The connection was not active.");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Need to check the caller's permissions and stuff before we can
+ * deactivate the connection.
+ */
+ scope = nm_connection_get_scope (connection);
+ if (!check_user_authorized (priv->dbus_mgr,
+ priv->user_proxy,
+ context,
+ scope,
+ &sender_uid,
+ &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Yay for root */
+ if (0 == sender_uid) {
+ if (!nm_manager_deactivate_connection (self,
+ active_path,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ } else
+ dbus_g_method_return (context);
+
+ return;
+ }
+
+ /* Otherwise validate the user request */
+ chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_net_auth_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_set_data (chain, "path", g_strdup (active_path), g_free);
+ nm_auth_chain_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
}
static void
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 221a8b548..309331aed 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -304,6 +304,33 @@ nm_vpn_manager_get_active_connections (NMVPNManager *manager)
return list;
}
+NMConnection *
+nm_vpn_manager_get_connection_for_active (NMVPNManager *manager,
+ const char *active_path)
+{
+ NMVPNManagerPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+
+ priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+ for (iter = priv->services; iter; iter = g_slist_next (iter)) {
+ GSList *active, *elt;
+
+ active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+ for (elt = active; elt; elt = g_slist_next (elt)) {
+ NMVPNConnection *candidate = NM_VPN_CONNECTION (elt->data);
+ const char *ac_path;
+
+ ac_path = nm_vpn_connection_get_active_connection_path (candidate);
+ if (ac_path && !strcmp (ac_path, active_path))
+ return nm_vpn_connection_get_connection (candidate);
+ }
+ }
+
+ return NULL;
+}
+
NMVPNManager *
nm_vpn_manager_get (void)
{
diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h
index d07aa2509..f14844a9d 100644
--- a/src/vpn-manager/nm-vpn-manager.h
+++ b/src/vpn-manager/nm-vpn-manager.h
@@ -83,4 +83,7 @@ void nm_vpn_manager_add_active_connections (NMVPNManager *manager,
GSList *nm_vpn_manager_get_active_connections (NMVPNManager *manager);
+NMConnection *nm_vpn_manager_get_connection_for_active (NMVPNManager *manager,
+ const char *active_path);
+
#endif /* NM_VPN_VPN_MANAGER_H */