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 */