diff options
author | Dan Williams <dcbw@redhat.com> | 2013-10-31 15:06:23 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2013-10-31 15:25:38 -0500 |
commit | 8a173e1d7a1e3174027920b8bfb4be1bd37ffb34 (patch) | |
tree | 19023e1581afa86e2ca6275df9baaf398c0781ce | |
parent | df406d06b6d7e140a5f56c2eb2dff41517f77f22 (diff) | |
parent | b3c0756f793f42eed95cc4fc183b189461c188c5 (diff) | |
download | NetworkManager-8a173e1d7a1e3174027920b8bfb4be1bd37ffb34.tar.gz |
core: removal of PendingActivation object from nm-manager.c (bgo #707335)
This branch decouples NMActiveConnection creation from device activation
so that the NMActiveConnection object tracks the entire activation request
(either internally-requested by the Policy or externally via D-Bus) from
start to finish, instead of the previous situation where the PendingActivation
handled D-Bus requests separately. This also will allow implementation of
the DEACTIVATING state in the future. The NMActiveConnection object tracking
the activation is not actually exported to D-Bus until the device or VPN
activation is completely authorized and actually begins.
It also encapsulates all the details needed to authorize a request into
a new NMAuthSubject class, replacing various "dbus_sender" and "user_requested"
arguments throughout the code.
31 files changed, 2097 insertions, 1344 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3a64a8f7d9..bcac9ac300 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -248,6 +248,8 @@ nm_sources = \ nm-ip6-config.h \ nm-manager-auth.c \ nm-manager-auth.h \ + nm-auth-subject.c \ + nm-auth-subject.h \ nm-manager.c \ nm-manager.h \ nm-netlink-monitor.c \ diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 4f71c7d7c3..b502fe70e2 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -147,6 +147,7 @@ get_generic_capabilities (NMDevice *dev) static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { + NMActStageReturn ret; NMActRequest *req; NMConnection *connection; NMSettingInfiniband *s_infiniband; @@ -156,6 +157,10 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + ret = NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (dev, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + req = nm_device_get_act_request (dev); g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -186,7 +191,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_FAILURE; } - return NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (dev, reason); + return NM_ACT_STAGE_RETURN_SUCCESS; } static void diff --git a/src/devices/nm-device-modem.c b/src/devices/nm-device-modem.c index 6ca3c2b84c..0bed60cd80 100644 --- a/src/devices/nm-device-modem.c +++ b/src/devices/nm-device-modem.c @@ -252,8 +252,13 @@ deactivate (NMDevice *device) static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { + NMActStageReturn ret; NMActRequest *req; + ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage1_prepare (device, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + req = nm_device_get_act_request (device); g_assert (req); diff --git a/src/devices/nm-device-olpc-mesh.c b/src/devices/nm-device-olpc-mesh.c index 8a032e45f0..c419daa5e7 100644 --- a/src/devices/nm-device-olpc-mesh.c +++ b/src/devices/nm-device-olpc-mesh.c @@ -252,8 +252,13 @@ static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev); + NMActStageReturn ret; gboolean scanning; + ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (dev, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + /* disconnect companion device, if it is connected */ if (nm_device_get_act_request (NM_DEVICE (priv->companion))) { nm_log_info (LOGD_OLPC_MESH, "(%s): disconnecting companion device %s", diff --git a/src/devices/nm-device-wifi.c b/src/devices/nm-device-wifi.c index 716ce9e3b3..e4c16613d9 100644 --- a/src/devices/nm-device-wifi.c +++ b/src/devices/nm-device-wifi.c @@ -2790,6 +2790,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMDeviceWifi *self = NM_DEVICE_WIFI (dev); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMActStageReturn ret; NMAccessPoint *ap = NULL; NMActRequest *req; NMConnection *connection; @@ -2798,6 +2799,10 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) GSList *iter; const char *mode; + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (dev, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + req = nm_device_get_act_request (NM_DEVICE (self)); g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 294aa9918f..660e26a59d 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -288,6 +288,7 @@ typedef struct { /* master interface for bridge/bond/team slave */ NMDevice * master; gboolean enslaved; + guint master_ready_id; /* slave management */ gboolean is_master; @@ -1935,10 +1936,61 @@ nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6) return FALSE; } +static void +master_ready_cb (NMActiveConnection *active, + GParamSpec *pspec, + NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActiveConnection *master; + + g_assert (priv->state == NM_DEVICE_STATE_PREPARE); + + /* Notify a master device that it has a new slave */ + g_assert (nm_active_connection_get_master_ready (active)); + master = nm_active_connection_get_master (active); + + priv->master = g_object_ref (nm_active_connection_get_device (master)); + nm_device_master_add_slave (priv->master, self); + + nm_log_dbg (LOGD_DEVICE, "(%s): master connection ready; master device %s", + nm_device_get_iface (self), + nm_device_get_iface (priv->master)); + + if (priv->master_ready_id) { + g_signal_handler_disconnect (active, priv->master_ready_id); + priv->master_ready_id = 0; + } + + nm_device_activate_schedule_stage2_device_config (self); +} + static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) { - return NM_ACT_STAGE_RETURN_SUCCESS; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); + + if (nm_active_connection_get_master (active)) { + /* If the master connection is ready for slaves, attach ourselves */ + if (nm_active_connection_get_master_ready (active)) + master_ready_cb (active, NULL, self); + else { + nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready", + nm_device_get_iface (self)); + + /* Attach a signal handler and wait for the master connection to begin activating */ + g_assert (priv->master_ready_id == 0); + priv->master_ready_id = g_signal_connect (active, + "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY, + (GCallback) master_ready_cb, + self); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } + } + + return ret; } /* @@ -3445,7 +3497,8 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *iface; int ifindex; - NMDevice *master; + NMActiveConnection *master; + NMDevice *master_device; /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); @@ -3471,11 +3524,12 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) */ master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)); if (master) { - if (priv->enslaved == FALSE) { + master_device = nm_active_connection_get_device (master); + if (master_device && priv->enslaved == FALSE) { nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on master '%s'", nm_device_get_iface (self), nm_connection_get_id (nm_device_get_connection (self)), - nm_device_get_iface (master)); + nm_device_get_iface (master_device)); } goto out; } @@ -4022,6 +4076,11 @@ clear_act_request (NMDevice *self) nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), FALSE); + if (priv->master_ready_id) { + g_signal_handler_disconnect (priv->act_request, priv->master_ready_id); + priv->master_ready_id = 0; + } + g_object_unref (priv->act_request); priv->act_request = NULL; } @@ -4425,8 +4484,6 @@ nm_device_activate (NMDevice *self, NMActRequest *req) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); nm_device_activate_schedule_stage3_ip_config_start (self); } else { - NMDevice *master; - /* HACK: update the state a bit early to avoid a race between the * scheduled stage1 handler and nm_policy_device_change_check() thinking * that the activation request isn't deferred because the deferred bit @@ -4434,17 +4491,6 @@ nm_device_activate (NMDevice *self, NMActRequest *req) */ nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - /* Handle any dependencies this connection might have */ - master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (req)); - if (master) { - /* Master should at least already be activating */ - g_assert (nm_device_get_state (master) > NM_DEVICE_STATE_DISCONNECTED); - - g_assert (priv->master == NULL); - priv->master = g_object_ref (master); - nm_device_master_add_slave (master, self); - } - nm_device_activate_schedule_stage1_device_prepare (self); } } @@ -5027,6 +5073,7 @@ dispose (GObject *object) dnsmasq_cleanup (self); g_warn_if_fail (priv->slaves == NULL); + g_assert (priv->master_ready_id == 0); /* Take the device itself down and clear its IPv4 configuration */ if (nm_device_get_managed (self) && deconfigure) { diff --git a/src/devices/wimax/Makefile.am b/src/devices/wimax/Makefile.am index 80a93b5be7..f6bd1e9e71 100644 --- a/src/devices/wimax/Makefile.am +++ b/src/devices/wimax/Makefile.am @@ -9,6 +9,7 @@ AM_CPPFLAGS = \ -I${top_builddir}/libnm-util \ -I${top_srcdir}/libnm-util \ $(DBUS_CFLAGS) \ + $(POLKIT_CFLAGS) \ $(IWMX_SDK_CFLAGS) \ $(LIBNL_CFLAGS) \ $(GUDEV_CFLAGS) diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index 508b10db5d..18968f7633 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -51,10 +51,6 @@ typedef struct { } ShareRule; typedef struct { - NMConnection *connection; - NMDevice *device; - guint device_state_id; - char *dbus_sender; GSList *secrets_calls; gboolean shared; GSList *share_rules; @@ -70,14 +66,6 @@ nm_act_request_get_connection (NMActRequest *req) return nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (req)); } -const char * -nm_act_request_get_dbus_sender (NMActRequest *req) -{ - g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL); - - return NM_ACT_REQUEST_GET_PRIVATE (req)->dbus_sender; -} - /*******************************************************************/ typedef struct { @@ -157,6 +145,7 @@ void nm_act_request_cancel_secrets (NMActRequest *self, guint32 call_id) { NMActRequestPrivate *priv; + NMConnection *connection; GSList *iter; g_return_if_fail (self); @@ -165,6 +154,7 @@ nm_act_request_cancel_secrets (NMActRequest *self, guint32 call_id) priv = NM_ACT_REQUEST_GET_PRIVATE (self); + connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (self)); for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) { GetSecretsInfo *info = iter->data; @@ -173,7 +163,7 @@ nm_act_request_cancel_secrets (NMActRequest *self, guint32 call_id) priv->secrets_calls = g_slist_remove_link (priv->secrets_calls, iter); g_slist_free (iter); - nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), call_id); + nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (connection), call_id); g_free (info); break; } @@ -295,13 +285,15 @@ nm_act_request_add_share_rule (NMActRequest *req, /********************************************************************/ static void -device_state_changed (NMDevice *device, GParamSpec *pspec, NMActRequest *self) +device_state_changed (NMActiveConnection *active, + NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state) { - NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self); NMActiveConnectionState ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; /* Set NMActiveConnection state based on the device's state */ - switch (nm_device_get_state (device)) { + switch (new_state) { case NM_DEVICE_STATE_PREPARE: case NM_DEVICE_STATE_CONFIG: case NM_DEVICE_STATE_NEED_AUTH: @@ -321,13 +313,6 @@ device_state_changed (NMDevice *device, GParamSpec *pspec, NMActRequest *self) case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: ac_state = NM_ACTIVE_CONNECTION_STATE_DEACTIVATED; - - /* No longer need to pay attention to device state */ - if (priv->device && priv->device_state_id) { - g_signal_handler_disconnect (priv->device, priv->device_state_id); - priv->device_state_id = 0; - } - g_clear_object (&priv->device); break; default: break; @@ -335,11 +320,33 @@ device_state_changed (NMDevice *device, GParamSpec *pspec, NMActRequest *self) if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED || ac_state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { - nm_active_connection_set_default (NM_ACTIVE_CONNECTION (self), FALSE); - nm_active_connection_set_default6 (NM_ACTIVE_CONNECTION (self), FALSE); + nm_active_connection_set_default (active, FALSE); + nm_active_connection_set_default6 (active, FALSE); } - nm_active_connection_set_state (NM_ACTIVE_CONNECTION (self), ac_state); + nm_active_connection_set_state (active, ac_state); +} + +static void +master_failed (NMActiveConnection *self) +{ + NMDevice *device; + NMDeviceState device_state; + + /* If the connection has an active device, fail it */ + device = nm_active_connection_get_device (self); + if (device) { + device_state = nm_device_get_state (device); + if (nm_device_is_activating (device) || (device_state == NM_DEVICE_STATE_ACTIVATED)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED); + return; + } + } + + /* If no device, or the device wasn't active, just move to deactivated state */ + nm_active_connection_set_state (self, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED); } /********************************************************************/ @@ -350,48 +357,31 @@ device_state_changed (NMDevice *device, GParamSpec *pspec, NMActRequest *self) * @connection: the connection to activate @device with * @specific_object: the object path of the specific object (ie, WiFi access point, * etc) that will be used to activate @connection and @device - * @user_requested: pass %TRUE if the activation was requested via D-Bus, - * otherwise %FALSE if requested internally by NM (ie, autoconnect) - * @user_uid: if @user_requested is %TRUE, the Unix UID of the user that requested - * @dbus_sender: if @user_requested is %TRUE, the D-BUS sender that requested - * the activation - * @device: the device/interface to configure according to @connection - * @master: if the activation depends on another device (ie, bond or bridge - * or team master to which this device will be enslaved) pass the #NMDevice - * that this activation request be enslaved to + * @subject: the #NMAuthSubject representing the requestor of the activation + * @device: the device/interface to configure according to @connection; or %NULL + * if the connection describes a software device which will be created during + * connection activation * - * Begins activation of @device using the given @connection and other details. + * Creates a new device-based activation request. * * Returns: the new activation request on success, %NULL on error. */ NMActRequest * nm_act_request_new (NMConnection *connection, const char *specific_object, - gboolean user_requested, - gulong user_uid, - const char *dbus_sender, - NMDevice *device, - NMDevice *master) + NMAuthSubject *subject, + NMDevice *device) { - GObject *object; - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (NM_DEVICE (device), NULL); - - object = g_object_new (NM_TYPE_ACT_REQUEST, - NM_ACTIVE_CONNECTION_INT_CONNECTION, connection, - NM_ACTIVE_CONNECTION_INT_DEVICE, device, - NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object, - NM_ACTIVE_CONNECTION_INT_USER_REQUESTED, user_requested, - NM_ACTIVE_CONNECTION_INT_USER_UID, user_uid, - NM_ACTIVE_CONNECTION_INT_MASTER, master, - NULL); - if (object) { - nm_active_connection_export (NM_ACTIVE_CONNECTION (object)); - NM_ACT_REQUEST_GET_PRIVATE (object)->dbus_sender = g_strdup (dbus_sender); - } - - return (NMActRequest *) object; + g_return_val_if_fail (!device || NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + + return (NMActRequest *) g_object_new (NM_TYPE_ACT_REQUEST, + NM_ACTIVE_CONNECTION_INT_CONNECTION, connection, + NM_ACTIVE_CONNECTION_INT_DEVICE, device, + NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object, + NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, + NULL); } static void @@ -400,38 +390,12 @@ nm_act_request_init (NMActRequest *req) } static void -constructed (GObject *object) -{ - NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object); - NMConnection *connection; - NMDevice *device; - - G_OBJECT_CLASS (nm_act_request_parent_class)->constructed (object); - - connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (object)); - priv->connection = g_object_ref (connection); - - device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object)); - if (device) { - priv->device = g_object_ref (device); - priv->device_state_id = g_signal_connect (priv->device, - "notify::" NM_DEVICE_STATE, - G_CALLBACK (device_state_changed), - NM_ACT_REQUEST (object)); - } -} - -static void dispose (GObject *object) { NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object); + NMConnection *connection; GSList *iter; - if (priv->device && priv->device_state_id) { - g_signal_handler_disconnect (priv->device, priv->device_state_id); - priv->device_state_id = 0; - } - /* Clear any share rules */ if (priv->share_rules) { nm_act_request_set_shared (NM_ACT_REQUEST (object), FALSE); @@ -439,22 +403,16 @@ dispose (GObject *object) } /* Kill any in-progress secrets requests */ - for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) { + connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (object)); + for (iter = priv->secrets_calls; connection && iter; iter = g_slist_next (iter)) { GetSecretsInfo *info = iter->data; - g_assert (priv->connection); - nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), info->call_id); + nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (connection), info->call_id); g_free (info); } g_slist_free (priv->secrets_calls); priv->secrets_calls = NULL; - g_free (priv->dbus_sender); - priv->dbus_sender = NULL; - - g_clear_object (&priv->device); - g_clear_object (&priv->connection); - G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object); } @@ -462,11 +420,13 @@ static void nm_act_request_class_init (NMActRequestClass *req_class) { GObjectClass *object_class = G_OBJECT_CLASS (req_class); + NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS (req_class); g_type_class_add_private (req_class, sizeof (NMActRequestPrivate)); /* virtual methods */ - object_class->constructed = constructed; object_class->dispose = dispose; + active_class->master_failed = master_failed; + active_class->device_state_changed = device_state_changed; } diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h index cd645ce9c2..87ea41f916 100644 --- a/src/nm-activation-request.h +++ b/src/nm-activation-request.h @@ -48,18 +48,11 @@ GType nm_act_request_get_type (void); NMActRequest *nm_act_request_new (NMConnection *connection, const char *specific_object, - gboolean user_requested, - gulong user_uid, - const char *dbus_sender, - NMDevice *device, - NMDevice *master); + NMAuthSubject *subject, + NMDevice *device); NMConnection *nm_act_request_get_connection (NMActRequest *req); -gulong nm_act_request_get_user_uid (NMActRequest *req); - -const char *nm_act_request_get_dbus_sender (NMActRequest *req); - gboolean nm_act_request_get_shared (NMActRequest *req); void nm_act_request_set_shared (NMActRequest *req, gboolean shared); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 5adf248708..c2442d46ab 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -27,6 +27,8 @@ #include "nm-dbus-manager.h" #include "nm-device.h" #include "nm-settings-connection.h" +#include "nm-manager-auth.h" +#include "NetworkManagerUtils.h" #include "nm-active-connection-glue.h" @@ -42,15 +44,22 @@ typedef struct { char *path; char *specific_object; NMDevice *device; + guint32 device_state_id; gboolean is_default; gboolean is_default6; NMActiveConnectionState state; gboolean vpn; - gboolean user_requested; - gulong user_uid; - NMDevice *master; + NMAuthSubject *subject; + NMActiveConnection *master; + gboolean master_ready; + + NMAuthChain *chain; + const char *wifi_shared_permission; + NMActiveConnectionAuthResultFunc result_func; + gpointer user_data1; + gpointer user_data2; } NMActiveConnectionPrivate; enum { @@ -67,15 +76,35 @@ enum { PROP_INT_CONNECTION, PROP_INT_DEVICE, - PROP_INT_USER_REQUESTED, - PROP_INT_USER_UID, + PROP_INT_SUBJECT, PROP_INT_MASTER, + PROP_INT_MASTER_READY, LAST_PROP }; +static void check_master_ready (NMActiveConnection *self); + /****************************************************************/ +static const char * +state_to_string (NMActiveConnectionState state) +{ + switch (state) { + case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: + return "unknown"; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: + return "activating"; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: + return "activated"; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: + return "deactivating"; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: + return "deactivated"; + } + return "(none)"; +} + NMActiveConnectionState nm_active_connection_get_state (NMActiveConnection *self) { @@ -100,6 +129,8 @@ nm_active_connection_set_state (NMActiveConnection *self, priv->state = new_state; g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_STATE); + check_master_ready (self); + if ( new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (priv->connection), @@ -107,8 +138,10 @@ nm_active_connection_set_state (NMActiveConnection *self, } if (priv->state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { - /* Device is no longer relevant when deactivated */ - g_clear_object (&priv->device); + /* Device is no longer relevant when deactivated; emit property change + * notification so clients re-read the value, which will be NULL due to + * conditions in get_property(). + */ g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEVICES); } } @@ -127,6 +160,21 @@ nm_active_connection_get_connection (NMActiveConnection *self) return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->connection; } +void +nm_active_connection_set_connection (NMActiveConnection *self, + NMConnection *connection) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + /* Can't change connection after the ActiveConnection is exported over D-Bus */ + g_return_if_fail (priv->path == NULL); + g_return_if_fail (priv->connection == NULL || !NM_IS_SETTINGS_CONNECTION (priv->connection)); + + if (priv->connection) + g_object_unref (priv->connection); + priv->connection = g_object_ref (connection); +} + const char * nm_active_connection_get_path (NMActiveConnection *self) { @@ -145,6 +193,11 @@ nm_active_connection_set_specific_object (NMActiveConnection *self, { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + /* Nothing that calls this function should be using paths from D-Bus, + * where NM uses "/" to mean NULL. + */ + g_assert (g_strcmp0 (specific_object, "/") != 0); + if (g_strcmp0 (priv->specific_object, specific_object) == 0) return; @@ -205,24 +258,37 @@ nm_active_connection_export (NMActiveConnection *self) NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); static guint32 counter = 0; + g_assert (priv->device || priv->vpn); + priv->path = g_strdup_printf (NM_DBUS_PATH "/ActiveConnection/%d", counter++); nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self); } +NMAuthSubject * +nm_active_connection_get_subject (NMActiveConnection *self) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->subject; +} + gboolean nm_active_connection_get_user_requested (NMActiveConnection *self) { g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE); - return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->user_requested; + return !nm_auth_subject_get_internal (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->subject); } gulong nm_active_connection_get_user_uid (NMActiveConnection *self) { + NMActiveConnectionPrivate *priv; + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), G_MAXULONG); + priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); - return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->user_uid; + return nm_auth_subject_get_uid (priv->subject); } NMDevice * @@ -233,7 +299,73 @@ nm_active_connection_get_device (NMActiveConnection *self) return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->device; } -NMDevice * +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data); + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + if (old_state < NM_DEVICE_STATE_DISCONNECTED) + return; + + if (old_state > NM_DEVICE_STATE_DISCONNECTED) { + /* Ignore disconnects if this ActiveConnection has not yet started + * activating. This is caused by activating a device when it's + * already activated, which causes a deactivating of the device before + * activating the new connection. + */ + if (new_state == NM_DEVICE_STATE_DISCONNECTED && + old_state > NM_DEVICE_STATE_DISCONNECTED && + priv->state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { + return; + } + + /* If the device used to be active, but now is disconnected/failed, we + * no longer care about its state. + */ + if (new_state <= NM_DEVICE_STATE_DISCONNECTED || new_state == NM_DEVICE_STATE_FAILED) { + g_signal_handler_disconnect (device, priv->device_state_id); + priv->device_state_id = 0; + } + } + + /* Let subclasses handle the state change */ + if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->device_state_changed) + NM_ACTIVE_CONNECTION_GET_CLASS (self)->device_state_changed (self, device, new_state, old_state); +} + +gboolean +nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) +{ + NMActiveConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE); + g_return_val_if_fail (!device || NM_IS_DEVICE (device), FALSE); + + priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + if (device) { + g_return_val_if_fail (priv->device == NULL, FALSE); + + /* Device obviously can't be its own master */ + g_return_val_if_fail (!priv->master || device != nm_active_connection_get_device (priv->master), FALSE); + + priv->device = g_object_ref (device); + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_DEVICE); + + priv->device_state_id = g_signal_connect (device, + "state-changed", + G_CALLBACK (device_state_changed), + self); + } + return TRUE; +} + +NMActiveConnection * nm_active_connection_get_master (NMActiveConnection *self) { g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL); @@ -241,6 +373,234 @@ nm_active_connection_get_master (NMActiveConnection *self) return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->master; } +/** + * nm_active_connection_get_master_ready: + * @self: the #NMActiveConnection + * + * Returns: %TRUE if the connection has a master connection, and that + * master connection is ready to accept slaves. Otherwise %FALSE. + */ +gboolean +nm_active_connection_get_master_ready (NMActiveConnection *self) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->master_ready; +} + +static void +check_master_ready (NMActiveConnection *self) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + NMActiveConnectionState master_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; + + if (priv->state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) { + nm_log_dbg (LOGD_DEVICE, "(%p): not signalling master-ready (not activating)", self); + return; + } + if (!priv->master) { + nm_log_dbg (LOGD_DEVICE, "(%p): not signalling master-ready (no master)", self); + return; + } + if (priv->master_ready) { + nm_log_dbg (LOGD_DEVICE, "(%p): not signalling master-ready (already signaled)", self); + return; + } + + /* ActiveConnetions don't enter the ACTIVATING state until they have a + * NMDevice in PREPARE or higher states, so the master active connection's + * device will be ready to accept slaves when the master is in ACTIVATING + * or higher states. + */ + master_state = nm_active_connection_get_state (priv->master); + nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection [%p] state now '%s' (%d)", + self, priv->master, state_to_string (master_state), master_state); + + if ( master_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING + || master_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + nm_log_dbg (LOGD_DEVICE, "(%p): signalling master-ready", self); + + priv->master_ready = TRUE; + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_MASTER_READY); + + /* Also notify clients to recheck the exported 'master' property to + * ensure that if the master connection was created without a device + * that we notify clients when the master device is known. + */ + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_MASTER); + } +} + +static void +master_state_cb (NMActiveConnection *master, + GParamSpec *pspec, + gpointer user_data) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data); + NMActiveConnectionState self_state = nm_active_connection_get_state (self); + NMActiveConnectionState master_state = nm_active_connection_get_state (master); + + check_master_ready (self); + + nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection [%p] state now '%s' (%d)", + self, master, state_to_string (master_state), master_state); + + /* Master is deactivating, so this active connection must also deactivate */ + if (self_state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATING && + master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection [%p] '%s' failed", + self, master, nm_active_connection_get_name (master)); + + g_signal_handlers_disconnect_by_func (master, + (GCallback) master_state_cb, + self); + if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed) + NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed (self); + } +} + +/** + * nm_active_connection_set_master: + * @self: the #NMActiveConnection + * @master: if the activation depends on another device (ie, bond or bridge + * master to which this device will be enslaved) pass the #NMActiveConnection + * that this activation request is a child of + * + * Sets the master active connection of @self. + */ +void +nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *master) +{ + NMActiveConnectionPrivate *priv; + + g_return_if_fail (NM_IS_ACTIVE_CONNECTION (self)); + g_return_if_fail (NM_IS_ACTIVE_CONNECTION (master)); + + priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + /* Master is write-once, and must be set before exporting the object */ + g_return_if_fail (priv->master == NULL); + g_return_if_fail (priv->path == NULL); + if (priv->device) { + /* Note, the master ActiveConnection may not yet have a device */ + g_return_if_fail (priv->device != nm_active_connection_get_device (master)); + } + + nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection is [%p] %s", + self, master, nm_active_connection_get_name (master)); + + priv->master = g_object_ref (master); + g_signal_connect (priv->master, + "notify::" NM_ACTIVE_CONNECTION_STATE, + (GCallback) master_state_cb, + self); + + check_master_ready (self); +} + +/****************************************************************/ + +static void +auth_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *unused, + gpointer user_data) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data); + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + NMAuthCallResult result; + + g_assert (priv->chain == chain); + g_assert (priv->result_func != NULL); + + /* Must stay alive over the callback */ + g_object_ref (self); + + if (error) { + priv->result_func (self, FALSE, error->message, priv->user_data1, priv->user_data2); + goto done; + } + + /* Caller has had a chance to obtain authorization, so we only need to + * check for 'yes' here. + */ + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); + if (result != NM_AUTH_CALL_RESULT_YES) { + priv->result_func (self, + FALSE, + "Not authorized to control networking.", + priv->user_data1, + priv->user_data2); + goto done; + } + + if (priv->wifi_shared_permission) { + result = nm_auth_chain_get_result (chain, priv->wifi_shared_permission); + if (result != NM_AUTH_CALL_RESULT_YES) { + priv->result_func (self, + FALSE, + "Not authorized to share connections via wifi.", + priv->user_data1, + priv->user_data2); + goto done; + } + } + + /* Otherwise authorized and available to activate */ + priv->result_func (self, TRUE, NULL, priv->user_data1, priv->user_data2); + +done: + nm_auth_chain_unref (chain); + priv->chain = NULL; + priv->result_func = NULL; + priv->user_data1 = NULL; + priv->user_data2 = NULL; + + g_object_unref (self); +} + +/** + * nm_active_connection_authorize: + * @self: the #NMActiveConnection + * @result_func: function to be called on success or error + * @user_data1: pointer passed to @result_func + * @user_data2: additional pointer passed to @result_func + * + * Checks whether the subject that initiated the active connection (read from + * the #NMActiveConnection::subject property) is authorized to complete this + * activation request. + */ +void +nm_active_connection_authorize (NMActiveConnection *self, + NMActiveConnectionAuthResultFunc result_func, + gpointer user_data1, + gpointer user_data2) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + const char *wifi_permission = NULL; + + g_return_if_fail (result_func != NULL); + g_return_if_fail (priv->chain == NULL); + + priv->chain = nm_auth_chain_new_subject (priv->subject, NULL, auth_done, self); + g_assert (priv->chain); + + /* Check that the subject is allowed to use networking at all */ + nm_auth_chain_add_call (priv->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + + /* Shared wifi connections require special permissions too */ + wifi_permission = nm_utils_get_shared_wifi_permission (priv->connection); + if (wifi_permission) { + priv->wifi_shared_permission = wifi_permission; + nm_auth_chain_add_call (priv->chain, wifi_permission, TRUE); + } + + /* Wait for authorization */ + priv->result_func = result_func; + priv->user_data1 = user_data1; + priv->user_data2 = user_data2; +} + /****************************************************************/ static void @@ -249,10 +609,18 @@ nm_active_connection_init (NMActiveConnection *self) } static void +constructed (GObject *object) +{ + G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object); + g_assert (NM_ACTIVE_CONNECTION_GET_PRIVATE (object)->subject); +} + +static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object); + const char *tmp; switch (prop_id) { case PROP_INT_CONNECTION: @@ -260,25 +628,19 @@ set_property (GObject *object, guint prop_id, priv->connection = g_value_dup_object (value); break; case PROP_INT_DEVICE: - g_warn_if_fail (priv->device == NULL); - priv->device = g_value_dup_object (value); - if (priv->device) - g_warn_if_fail (priv->device != priv->master); + nm_active_connection_set_device (NM_ACTIVE_CONNECTION (object), g_value_get_object (value)); break; - case PROP_INT_USER_REQUESTED: - priv->user_requested = g_value_get_boolean (value); - break; - case PROP_INT_USER_UID: - priv->user_uid = g_value_get_ulong (value); + case PROP_INT_SUBJECT: + priv->subject = g_value_dup_object (value); break; case PROP_INT_MASTER: - g_warn_if_fail (priv->master == NULL); - priv->master = g_value_dup_object (value); - if (priv->master) - g_warn_if_fail (priv->master != priv->device); + nm_active_connection_set_master (NM_ACTIVE_CONNECTION (object), g_value_get_object (value)); break; case PROP_SPECIFIC_OBJECT: - priv->specific_object = g_value_dup_boxed (value); + tmp = g_value_get_boxed (value); + /* NM uses "/" to mean NULL */ + if (g_strcmp0 (tmp, "/") != 0) + priv->specific_object = g_value_dup_boxed (value); break; case PROP_DEFAULT: priv->is_default = g_value_get_boolean (value); @@ -303,6 +665,7 @@ get_property (GObject *object, guint prop_id, { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object); GPtrArray *devices; + NMDevice *master_device = NULL; switch (prop_id) { case PROP_CONNECTION: @@ -316,7 +679,7 @@ get_property (GObject *object, guint prop_id, break; case PROP_DEVICES: devices = g_ptr_array_sized_new (1); - if (priv->device) + if (priv->device && priv->state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) g_ptr_array_add (devices, g_strdup (nm_device_get_path (priv->device))); g_value_take_boxed (value, devices); break; @@ -333,7 +696,15 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->vpn); break; case PROP_MASTER: - g_value_set_boxed (value, priv->master ? nm_device_get_path (priv->master) : "/"); + if (priv->master) + master_device = nm_active_connection_get_device (priv->master); + g_value_set_boxed (value, master_device ? nm_device_get_path (master_device) : "/"); + break; + case PROP_INT_SUBJECT: + g_value_set_object (value, priv->subject); + break; + case PROP_INT_MASTER_READY: + g_value_set_boolean (value, priv->master_ready); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -346,14 +717,32 @@ dispose (GObject *object) { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object); + if (priv->chain) { + nm_auth_chain_unref (priv->chain); + priv->chain = NULL; + } + g_free (priv->path); priv->path = NULL; g_free (priv->specific_object); priv->specific_object = NULL; g_clear_object (&priv->connection); + + if (priv->device_state_id) { + g_assert (priv->device); + g_signal_handler_disconnect (priv->device, priv->device_state_id); + priv->device_state_id = 0; + } g_clear_object (&priv->device); + + if (priv->master) { + g_signal_handlers_disconnect_by_func (priv->master, + (GCallback) master_state_cb, + NM_ACTIVE_CONNECTION (object)); + } g_clear_object (&priv->master); + g_clear_object (&priv->subject); G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object); } @@ -368,6 +757,7 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) /* virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; + object_class->constructed = constructed; object_class->dispose = dispose; /* D-Bus exported properties */ @@ -449,28 +839,27 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) "Internal device", "Internal device", NM_TYPE_DEVICE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE)); - g_object_class_install_property (object_class, PROP_INT_USER_REQUESTED, - g_param_spec_boolean (NM_ACTIVE_CONNECTION_INT_USER_REQUESTED, - "User requested", - "User requested", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, PROP_INT_USER_UID, - g_param_spec_ulong (NM_ACTIVE_CONNECTION_INT_USER_UID, - "User UID", - "User UID (if user requested)", - 0, G_MAXULONG, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_INT_SUBJECT, + g_param_spec_object (NM_ACTIVE_CONNECTION_INT_SUBJECT, + "Subject", + "Subject", + NM_TYPE_AUTH_SUBJECT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_INT_MASTER, g_param_spec_object (NM_ACTIVE_CONNECTION_INT_MASTER, - "Internal master device", - "Internal device", - NM_TYPE_DEVICE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + "Internal master active connection", + "Internal active connection", + NM_TYPE_ACTIVE_CONNECTION, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_INT_MASTER_READY, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_INT_MASTER_READY, + "Internal master active connection ready for slaves", + "Internal active connection ready", + FALSE, G_PARAM_READABLE)); nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (ac_class), diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 96bbfedd75..fa9e5fde4c 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -24,6 +24,7 @@ #include <glib-object.h> #include "nm-types.h" #include "nm-connection.h" +#include "nm-auth-subject.h" #define NM_TYPE_ACTIVE_CONNECTION (nm_active_connection_get_type ()) #define NM_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection)) @@ -43,13 +44,12 @@ #define NM_ACTIVE_CONNECTION_VPN "vpn" #define NM_ACTIVE_CONNECTION_MASTER "master" -/* Internal non-exported construct-time properties */ +/* Internal non-exported properties */ #define NM_ACTIVE_CONNECTION_INT_CONNECTION "int-connection" #define NM_ACTIVE_CONNECTION_INT_DEVICE "int-device" -#define NM_ACTIVE_CONNECTION_INT_USER_REQUESTED "int-user-requested" -#define NM_ACTIVE_CONNECTION_INT_USER_UID "int-user-uid" +#define NM_ACTIVE_CONNECTION_INT_SUBJECT "int-subject" #define NM_ACTIVE_CONNECTION_INT_MASTER "int-master" - +#define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" typedef struct { GObject parent; @@ -58,13 +58,36 @@ typedef struct { typedef struct { GObjectClass parent; + /* re-emits device state changes as a convenience for subclasses for + * device states >= DISCONNECTED. + */ + void (*device_state_changed) (NMActiveConnection *connection, + NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state); + void (*master_failed) (NMActiveConnection *connection); } NMActiveConnectionClass; GType nm_active_connection_get_type (void); +typedef void (*NMActiveConnectionAuthResultFunc) (NMActiveConnection *self, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2); + +void nm_active_connection_authorize (NMActiveConnection *self, + NMActiveConnectionAuthResultFunc result_func, + gpointer user_data1, + gpointer user_data2); + void nm_active_connection_export (NMActiveConnection *self); NMConnection *nm_active_connection_get_connection (NMActiveConnection *self); + +void nm_active_connection_set_connection (NMActiveConnection *self, + NMConnection *connection); + const char * nm_active_connection_get_name (NMActiveConnection *self); const char * nm_active_connection_get_path (NMActiveConnection *self); @@ -91,10 +114,19 @@ void nm_active_connection_set_state (NMActiveConnection *self, NMDevice * nm_active_connection_get_device (NMActiveConnection *self); +gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device); + +NMAuthSubject *nm_active_connection_get_subject (NMActiveConnection *self); + gboolean nm_active_connection_get_user_requested (NMActiveConnection *self); gulong nm_active_connection_get_user_uid (NMActiveConnection *self); -NMDevice * nm_active_connection_get_master (NMActiveConnection *self); +NMActiveConnection *nm_active_connection_get_master (NMActiveConnection *self); + +gboolean nm_active_connection_get_master_ready (NMActiveConnection *self); + +void nm_active_connection_set_master (NMActiveConnection *self, + NMActiveConnection *master); #endif /* NM_ACTIVE_CONNECTION_H */ diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c new file mode 100644 index 0000000000..66c0d4e63e --- /dev/null +++ b/src/nm-auth-subject.c @@ -0,0 +1,210 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nm-auth-subject + * @short_description: Encapsulates authentication information about a requestor + * + * #NMAuthSubject encpasulates identifying information about an entity that + * makes requests, like process identifier and user UID. + */ + +#include <config.h> +#include <glib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#if WITH_POLKIT +#include <polkit/polkit.h> +#endif + +#include "nm-auth-subject.h" +#include "nm-dbus-manager.h" + +G_DEFINE_TYPE (NMAuthSubject, nm_auth_subject, G_TYPE_OBJECT) + +#define NM_AUTH_SUBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectPrivate)) + +typedef struct { + gulong pid; + gulong uid; + char *dbus_sender; + +#if WITH_POLKIT + PolkitSubject *pk_subject; +#endif +} NMAuthSubjectPrivate; + +static NMAuthSubject * +_new_common (DBusGMethodInvocation *context, + DBusConnection *connection, + DBusMessage *message, + gboolean internal) +{ + NMAuthSubject *subject; + NMAuthSubjectPrivate *priv; + NMDBusManager *dbus_mgr; + gboolean success = FALSE; + + g_return_val_if_fail (context || (connection && message) || internal, NULL); + if (internal) + g_return_val_if_fail (context == NULL && connection == NULL && message == NULL, NULL); + + subject = NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, NULL)); + priv = NM_AUTH_SUBJECT_GET_PRIVATE (subject); + + dbus_mgr = nm_dbus_manager_get (); + + if (internal) { + priv->uid = 0; + priv->pid = 0; + return subject; + } + + if (context) { + success = nm_dbus_manager_get_caller_info (dbus_mgr, + context, + &priv->dbus_sender, + &priv->uid, + &priv->pid); + } else if (message) { + success = nm_dbus_manager_get_caller_info_from_message (dbus_mgr, + connection, + message, + &priv->dbus_sender, + &priv->uid, + &priv->pid); + } else + g_assert_not_reached (); + + if (!success) { + g_object_unref (subject); + return NULL; + } + + g_assert (priv->dbus_sender); + g_assert_cmpuint (priv->pid, !=, 0); + +#if WITH_POLKIT + /* FIXME: should we use polkit_unix_session_new() to store the session ID + * of a short-lived process, so that the process can exit but we can still + * ask that user for authorization? + */ + priv->pk_subject = polkit_unix_process_new_for_owner (priv->pid, 0, priv->uid); + if (!priv->pk_subject) + return NULL; +#endif + + return subject; +} + + +NMAuthSubject * +nm_auth_subject_new_from_context (DBusGMethodInvocation *context) +{ + return _new_common (context, NULL, NULL, FALSE); +} + +NMAuthSubject * +nm_auth_subject_new_from_message (DBusConnection *connection, + DBusMessage *message) +{ + return _new_common (NULL, connection, message, FALSE); +} + +/** + * nm_auth_subject_new_internal(): + * + * Creates a new auth subject representing the NetworkManager process itself. + * + * Returns: the new #NMAuthSubject + */ +NMAuthSubject * +nm_auth_subject_new_internal (void) +{ + return _new_common (NULL, NULL, NULL, TRUE); +} + +/**************************************************************/ + +gulong +nm_auth_subject_get_uid (NMAuthSubject *subject) +{ + return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->uid; +} + +const char * +nm_auth_subject_get_dbus_sender (NMAuthSubject *subject) +{ + return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->dbus_sender; +} + +gboolean +nm_auth_subject_get_internal (NMAuthSubject *subject) +{ + /* internal requests will have no dbus sender */ + return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->dbus_sender ? FALSE : TRUE; +} + +#if WITH_POLKIT +PolkitSubject * +nm_auth_subject_get_polkit_subject (NMAuthSubject *subject) +{ + return NM_AUTH_SUBJECT_GET_PRIVATE (subject)->pk_subject; +} +#endif + +/******************************************************************/ + +static void +nm_auth_subject_init (NMAuthSubject *self) +{ + NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (self); + + priv->pid = G_MAXULONG; + priv->uid = G_MAXULONG; +} + +static void +finalize (GObject *object) +{ + NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object); + + g_free (priv->dbus_sender); + +#if WITH_POLKIT + if (priv->pk_subject) + g_object_unref (priv->pk_subject); +#endif + + G_OBJECT_CLASS (nm_auth_subject_parent_class)->finalize (object); +} + +static void +nm_auth_subject_class_init (NMAuthSubjectClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMAuthSubjectPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; +} diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h new file mode 100644 index 0000000000..8f049a8405 --- /dev/null +++ b/src/nm-auth-subject.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NM_AUTH_SUBJECT_H +#define NM_AUTH_SUBJECT_H + +#include <config.h> +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> + +#if WITH_POLKIT +#include <polkit/polkit.h> +#endif + +#define NM_TYPE_AUTH_SUBJECT (nm_auth_subject_get_type ()) +#define NM_AUTH_SUBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubject)) +#define NM_AUTH_SUBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectClass)) +#define NM_IS_AUTH_SUBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AUTH_SUBJECT)) +#define NM_IS_AUTH_SUBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AUTH_SUBJECT)) +#define NM_AUTH_SUBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectClass)) + +typedef struct { + GObject parent; +} NMAuthSubject; + +typedef struct { + GObjectClass parent; + +} NMAuthSubjectClass; + +GType nm_auth_subject_get_type (void); + +NMAuthSubject *nm_auth_subject_new_from_context (DBusGMethodInvocation *context); + +NMAuthSubject *nm_auth_subject_new_from_message (DBusConnection *connection, DBusMessage *message); + +NMAuthSubject *nm_auth_subject_new_internal (void); + +gulong nm_auth_subject_get_uid (NMAuthSubject *subject); + +const char *nm_auth_subject_get_dbus_sender (NMAuthSubject *subject); + +gboolean nm_auth_subject_get_internal (NMAuthSubject *subject); + +#if WITH_POLKIT +PolkitSubject *nm_auth_subject_get_polkit_subject (NMAuthSubject *subject); +#endif + +#endif /* NM_AUTH_SUBJECT_H */ diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index 891a765f46..e9d5fce5c1 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -267,6 +267,27 @@ private_server_get_connection_owner (PrivateServer *s, DBusGConnection *connecti /**************************************************************/ +static gboolean +_bus_get_unix_pid (NMDBusManager *self, + const char *sender, + gulong *out_pid, + GError **error) +{ + guint32 unix_pid = G_MAXUINT32; + + if (!dbus_g_proxy_call_with_timeout (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, + "GetConnectionUnixProcessID", 2000, error, + G_TYPE_STRING, sender, + G_TYPE_INVALID, + G_TYPE_UINT, &unix_pid, + G_TYPE_INVALID)) { + return FALSE; + } + + *out_pid = (gulong) unix_pid; + return TRUE; +} + /** * _get_caller_info_from_context(): * @@ -279,7 +300,8 @@ _get_caller_info (NMDBusManager *self, DBusConnection *connection, DBusMessage *message, char **out_sender, - gulong *out_uid) + gulong *out_uid, + gulong *out_pid) { NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); DBusGConnection *gconn; @@ -312,6 +334,10 @@ _get_caller_info (NMDBusManager *self, *out_uid = 0; if (out_sender) *out_sender = g_strdup (priv_sender); + if (out_pid) { + if (!dbus_connection_get_unix_process_id (connection, out_pid)) + *out_pid = G_MAXULONG; + } return TRUE; } } @@ -331,6 +357,14 @@ _get_caller_info (NMDBusManager *self, } } + if (out_pid) { + if (!_bus_get_unix_pid (self, sender, out_pid, NULL)) { + *out_pid = G_MAXULONG; + g_free (sender); + return FALSE; + } + } + if (out_sender) *out_sender = g_strdup (sender); @@ -342,9 +376,10 @@ gboolean nm_dbus_manager_get_caller_info (NMDBusManager *self, DBusGMethodInvocation *context, char **out_sender, - gulong *out_uid) + gulong *out_uid, + gulong *out_pid) { - return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid); + return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid, out_pid); } gboolean @@ -352,9 +387,10 @@ nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self, DBusConnection *connection, DBusMessage *message, char **out_sender, - gulong *out_uid) + gulong *out_uid, + gulong *out_pid) { - return _get_caller_info (self, NULL, connection, message, out_sender, out_uid); + return _get_caller_info (self, NULL, connection, message, out_sender, out_uid, out_pid); } gboolean diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h index 319252e87f..7895a20375 100644 --- a/src/nm-dbus-manager.h +++ b/src/nm-dbus-manager.h @@ -87,7 +87,8 @@ DBusGConnection * nm_dbus_manager_get_connection (NMDBusManager *self); gboolean nm_dbus_manager_get_caller_info (NMDBusManager *self, DBusGMethodInvocation *context, char **out_sender, - gulong *out_uid); + gulong *out_uid, + gulong *out_pid); gboolean nm_dbus_manager_get_unix_user (NMDBusManager *self, const char *sender, @@ -97,7 +98,8 @@ gboolean nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self, DBusConnection *connection, DBusMessage *message, char **out_sender, - gulong *out_uid); + gulong *out_uid, + gulong *out_pid); void nm_dbus_manager_register_exported_type (NMDBusManager *self, GType object_type, diff --git a/src/nm-manager-auth.c b/src/nm-manager-auth.c index eea74f2152..57f5c72569 100644 --- a/src/nm-manager-auth.c +++ b/src/nm-manager-auth.c @@ -31,6 +31,7 @@ #include "nm-manager-auth.h" #include "nm-logging.h" #include "nm-dbus-manager.h" +#include "nm-auth-subject.h" struct NMAuthChain { guint32 refcount; @@ -43,6 +44,7 @@ struct NMAuthChain { DBusGMethodInvocation *context; char *owner; gulong user_uid; + NMAuthSubject *subject; GError *error; guint idle_id; @@ -105,16 +107,16 @@ pk_authority_get (GError **error) #endif static NMAuthChain * -_auth_chain_new (DBusGMethodInvocation *context, - DBusMessage *message, +_auth_chain_new (NMAuthSubject *subject, const char *dbus_sender, gulong user_uid, + DBusGMethodInvocation *context, NMAuthChainResultFunc done_func, gpointer user_data) { NMAuthChain *self; - g_return_val_if_fail (message || dbus_sender, NULL); + g_return_val_if_fail (subject || user_uid == 0 || dbus_sender, NULL); self = g_malloc0 (sizeof (NMAuthChain)); self->refcount = 1; @@ -125,65 +127,73 @@ _auth_chain_new (DBusGMethodInvocation *context, self->done_func = done_func; self->user_data = user_data; self->context = context; - self->user_uid = user_uid; - if (message) - self->owner = g_strdup (dbus_message_get_sender (message)); - else if (dbus_sender) + if (subject) { + self->user_uid = nm_auth_subject_get_uid (subject); + self->subject = g_object_ref (subject); + } else { + self->user_uid = user_uid; self->owner = g_strdup (dbus_sender); - - if (user_uid > 0 && !self->owner) { - /* Need an owner */ - g_warn_if_fail (self->owner); - nm_auth_chain_unref (self); - self = NULL; + if (user_uid > 0 && !self->owner) { + /* Need an owner */ + g_warn_if_fail (self->owner); + nm_auth_chain_unref (self); + self = NULL; + } } return self; } NMAuthChain * -nm_auth_chain_new (DBusGMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data, - const char **out_error_desc) +nm_auth_chain_new_dbus_sender (const char *dbus_sender, + gulong user_uid, + NMAuthChainResultFunc done_func, + gpointer user_data) +{ + return _auth_chain_new (NULL, dbus_sender, user_uid, NULL, done_func, user_data); +} + +/* Creates the NMAuthSubject automatically */ +NMAuthChain * +nm_auth_chain_new_context (DBusGMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data) { - gulong sender_uid = G_MAXULONG; - char *sender = NULL; - NMAuthChain *chain = NULL; + NMAuthSubject *subject; + NMAuthChain *chain; g_return_val_if_fail (context != NULL, NULL); - if (nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), - context, - &sender, - &sender_uid)) { - chain = _auth_chain_new (context, NULL, sender, sender_uid, done_func, user_data); - } - - if (!chain && out_error_desc) - *out_error_desc = "Unable to determine request UID and sender."; + subject = nm_auth_subject_new_from_context (context); + if (!subject) + return NULL; - g_free (sender); + chain = nm_auth_chain_new_subject (subject, + context, + done_func, + user_data); + g_object_unref (subject); return chain; } +/* Requires an NMAuthSubject */ NMAuthChain * -nm_auth_chain_new_raw_message (DBusMessage *message, - gulong user_uid, - NMAuthChainResultFunc done_func, - gpointer user_data) +nm_auth_chain_new_subject (NMAuthSubject *subject, + DBusGMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data) { - return _auth_chain_new (NULL, message, NULL, user_uid, done_func, user_data); -} + NMAuthChain *chain; -NMAuthChain * -nm_auth_chain_new_dbus_sender (const char *dbus_sender, - gulong user_uid, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - return _auth_chain_new (NULL, NULL, dbus_sender, user_uid, done_func, user_data); + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + chain = _auth_chain_new (subject, NULL, G_MAXULONG, context, done_func, user_data); + + /* Chains creation from a valid NMAuthSubject cannot fail since the + * subject already has all the necessary auth info. + */ + g_assert (chain); + return chain; } gpointer @@ -422,7 +432,7 @@ _add_call_polkit (NMAuthChain *self, AuthCall *call; g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (self->owner != NULL, FALSE); + g_return_val_if_fail (self->owner || self->subject, FALSE); g_return_val_if_fail (permission != NULL, FALSE); call = auth_call_new (self, permission); @@ -433,10 +443,16 @@ _add_call_polkit (NMAuthChain *self, return FALSE; } - subject = polkit_system_bus_name_new (self->owner); - if (!subject) { - auth_call_schedule_complete_with_error (call, "Failed to create polkit subject"); - return FALSE; + if (self->subject) { + subject = g_object_ref (nm_auth_subject_get_polkit_subject (self->subject)); + g_assert (subject); + } else { + g_assert (self->owner); + subject = polkit_system_bus_name_new (self->owner); + if (!subject) { + auth_call_schedule_complete_with_error (call, "Failed to create polkit subject"); + return FALSE; + } } if (allow_interaction) @@ -496,6 +512,7 @@ nm_auth_chain_unref (NMAuthChain *self) g_object_unref (self->authority); #endif g_free (self->owner); + g_object_unref (self->subject); for (iter = self->calls; iter; iter = g_slist_next (iter)) auth_call_cancel ((AuthCall *) iter->data); diff --git a/src/nm-manager-auth.h b/src/nm-manager-auth.h index ebfb707254..43dd639873 100644 --- a/src/nm-manager-auth.h +++ b/src/nm-manager-auth.h @@ -27,6 +27,7 @@ #include <nm-connection.h> #include "nm-dbus-manager.h" #include "nm-session-monitor.h" +#include "nm-auth-subject.h" #define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" #define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake" @@ -55,21 +56,20 @@ typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain, DBusGMethodInvocation *context, gpointer user_data); -NMAuthChain *nm_auth_chain_new (DBusGMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data, - const char **out_error_desc); - -NMAuthChain *nm_auth_chain_new_raw_message (DBusMessage *message, - gulong user_uid, - NMAuthChainResultFunc done_func, - gpointer user_data); - NMAuthChain *nm_auth_chain_new_dbus_sender (const char *dbus_sender, gulong user_uid, NMAuthChainResultFunc done_func, gpointer user_data); +NMAuthChain *nm_auth_chain_new_context (DBusGMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data); + +NMAuthChain *nm_auth_chain_new_subject (NMAuthSubject *subject, + DBusGMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data); + gpointer nm_auth_chain_get_data (NMAuthChain *chain, const char *tag); gpointer nm_auth_chain_steal_data (NMAuthChain *chain, const char *tag); diff --git a/src/nm-manager.c b/src/nm-manager.c index d6e6f57cef..af663c95ab 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -156,16 +156,14 @@ static void remove_device (NMManager *self, NMDevice *device, gboolean quitting) static void hostname_provider_init (NMHostnameProvider *provider_class); -static NMActiveConnection *internal_activate_device (NMManager *manager, - NMDevice *device, - NMConnection *connection, - const char *specific_object, - gboolean user_requested, - gulong sender_uid, - const char *dbus_sender, - gboolean assumed, - NMActiveConnection *master, - GError **error); +static NMActiveConnection *_new_active_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMDevice *device, + NMAuthSubject *subject, + GError **error); + +static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data); static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface); @@ -181,24 +179,6 @@ platform_link_added_cb (NMPlatform *platform, #define SSD_POKE_INTERVAL 120 #define ORIGDEV_TAG "originating-device" -typedef struct PendingActivation PendingActivation; -typedef void (*PendingActivationFunc) (PendingActivation *pending, - GError *error); - -struct PendingActivation { - NMManager *manager; - - DBusGMethodInvocation *context; - PendingActivationFunc callback; - NMAuthChain *chain; - const char *wifi_shared_permission; - - char *connection_path; - NMConnection *connection; - char *specific_object_path; - char *device_path; -}; - typedef struct { gboolean user_enabled; gboolean daemon_enabled; @@ -334,12 +314,19 @@ static void active_connection_state_changed (NMActiveConnection *active, GParamSpec *pspec, NMManager *self); -static void -active_connection_removed (NMManager *self, NMActiveConnection *active) +/* Returns: whether to notify D-Bus of the removal or not */ +static gboolean +active_connection_remove (NMManager *self, NMActiveConnection *active) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean notify = !!nm_active_connection_get_path (active); + + priv->active_connections = g_slist_remove (priv->active_connections, active); g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active); g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self); g_object_unref (active); + + return notify; } static gboolean @@ -348,24 +335,21 @@ _active_connection_cleanup (gpointer user_data) NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter; - gboolean changed = FALSE; priv->ac_cleanup_id = 0; + g_object_freeze_notify (G_OBJECT (self)); iter = priv->active_connections; while (iter) { NMActiveConnection *ac = iter->data; iter = iter->next; if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { - priv->active_connections = g_slist_remove (priv->active_connections, ac); - active_connection_removed (self, ac); - changed = TRUE; + if (active_connection_remove (self, ac)) + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); } } - - if (changed) - g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + g_object_thaw_notify (G_OBJECT (self)); return FALSE; } @@ -403,7 +387,10 @@ active_connection_add (NMManager *self, NMActiveConnection *active) self); g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active); - g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + + /* Only notify D-Bus if the active connection is actually exported */ + if (nm_active_connection_get_path (active)) + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); } const GSList * @@ -424,7 +411,7 @@ active_connection_get_by_path (NMManager *manager, const char *path) for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { NMActiveConnection *candidate = iter->data; - if (strcmp (path, nm_active_connection_get_path (candidate)) == 0) + if (g_strcmp0 (path, nm_active_connection_get_path (candidate)) == 0) return candidate; } return NULL; @@ -846,246 +833,6 @@ nm_manager_get_state (NMManager *manager) return NM_MANAGER_GET_PRIVATE (manager)->state; } -static gboolean -might_be_vpn (NMConnection *connection) -{ - NMSettingConnection *s_con; - const char *ctype = NULL; - - if (nm_connection_get_setting_vpn (connection)) - return TRUE; - - /* Make sure it's not a VPN, which we can't autocomplete yet */ - s_con = nm_connection_get_setting_connection (connection); - if (s_con) - ctype = nm_setting_connection_get_connection_type (s_con); - - return (g_strcmp0 (ctype, NM_SETTING_VPN_SETTING_NAME) == 0); -} - -static gboolean -try_complete_vpn (NMConnection *connection, GSList *existing, GError **error) -{ - g_assert (might_be_vpn (connection) == TRUE); - - if (!nm_connection_get_setting_vpn (connection)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE, - "VPN connections require a 'vpn' setting"); - return FALSE; - } - - nm_utils_complete_generic (connection, - NM_SETTING_VPN_SETTING_NAME, - existing, - _("VPN connection %d"), - NULL, - FALSE); /* No IPv6 by default for now */ - - return TRUE; -} - -static PendingActivation * -pending_activation_new (NMManager *manager, - DBusGMethodInvocation *context, - const char *device_path, - const char *connection_path, - GHashTable *settings, - const char *specific_object_path, - PendingActivationFunc callback, - GError **error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - PendingActivation *pending; - NMDevice *device = NULL; - NMConnection *connection = NULL; - GSList *all_connections = NULL; - gboolean success; - - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (context != NULL, NULL); - g_return_val_if_fail (device_path != NULL, NULL); - - /* A object path of "/" means NULL */ - if (g_strcmp0 (specific_object_path, "/") == 0) - specific_object_path = NULL; - if (g_strcmp0 (device_path, "/") == 0) - device_path = NULL; - - /* Create the partial connection from the given settings */ - if (settings) { - if (device_path) - device = nm_manager_get_device_by_path (manager, device_path); - if (!device) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device not found"); - return NULL; - } - - connection = nm_connection_new (); - nm_connection_replace_settings (connection, settings, NULL); - - all_connections = nm_settings_get_connections (priv->settings); - - if (might_be_vpn (connection)) { - /* Try to fill the VPN's connection setting and name at least */ - success = try_complete_vpn (connection, all_connections, error); - } else { - /* Let each device subclass complete the connection */ - success = nm_device_complete_connection (device, - connection, - specific_object_path, - all_connections, - error); - } - g_slist_free (all_connections); - - if (success == FALSE) { - g_object_unref (connection); - return NULL; - } - } - - pending = g_slice_new0 (PendingActivation); - pending->manager = manager; - pending->context = context; - pending->callback = callback; - - pending->connection_path = g_strdup (connection_path); - pending->connection = connection; - - /* "/" is special-cased to NULL to get through D-Bus */ - if (specific_object_path && strcmp (specific_object_path, "/")) - pending->specific_object_path = g_strdup (specific_object_path); - if (device_path && strcmp (device_path, "/")) - pending->device_path = g_strdup (device_path); - - return pending; -} - -static void -pending_auth_done (NMAuthChain *chain, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) -{ - PendingActivation *pending = user_data; - NMAuthCallResult result; - GError *tmp_error = NULL; - - /* Caller has had a chance to obtain authorization, so we only need to - * check for 'yes' here. - */ - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); - if (error) - tmp_error = g_error_copy (error); - else if (result != NM_AUTH_CALL_RESULT_YES) { - tmp_error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to control networking."); - } else if (pending->wifi_shared_permission) { - result = nm_auth_chain_get_result (chain, pending->wifi_shared_permission); - if (result != NM_AUTH_CALL_RESULT_YES) { - tmp_error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to share connections via wifi."); - } - } - - pending->callback (pending, tmp_error); - g_clear_error (&tmp_error); -} - -static void -pending_activation_check_authorized (PendingActivation *pending) -{ - GError *error; - const char *wifi_permission = NULL; - NMConnection *connection; - NMSettings *settings; - const char *error_desc = NULL; - - g_return_if_fail (pending != NULL); - - /* By this point we have an auto-completed connection (for AddAndActivate) - * or an existing connection (for Activate). - */ - connection = pending->connection; - if (!connection) { - settings = NM_MANAGER_GET_PRIVATE (pending->manager)->settings; - connection = (NMConnection *) nm_settings_get_connection_by_path (settings, pending->connection_path); - } - - if (!connection) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection could not be found."); - 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. - */ - pending->chain = nm_auth_chain_new (pending->context, - pending_auth_done, - pending, - &error_desc); - if (pending->chain) { - nm_auth_chain_add_call (pending->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); - - /* Shared wifi connections require special permissions too */ - wifi_permission = nm_utils_get_shared_wifi_permission (connection); - if (wifi_permission) { - pending->wifi_shared_permission = wifi_permission; - nm_auth_chain_add_call (pending->chain, wifi_permission, TRUE); - } - } else { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - pending->callback (pending, error); - g_error_free (error); - } -} - -static void -pending_activation_destroy (PendingActivation *pending, - GError *error, - NMActiveConnection *ac) -{ - g_return_if_fail (pending != NULL); - - if (error) - dbus_g_method_return_error (pending->context, error); - else if (ac) { - if (pending->connection) { - dbus_g_method_return (pending->context, - pending->connection_path, - nm_active_connection_get_path (ac)); - } else { - dbus_g_method_return (pending->context, - nm_active_connection_get_path (ac)); - } - } - - g_free (pending->connection_path); - g_free (pending->specific_object_path); - g_free (pending->device_path); - if (pending->connection) - g_object_unref (pending->connection); - - if (pending->chain) - nm_auth_chain_unref (pending->chain); - - memset (pending, 0, sizeof (PendingActivation)); - g_slice_free (PendingActivation, pending); -} - /*******************************************************************/ /* Settings stuff via NMSettings */ /*******************************************************************/ @@ -1886,6 +1633,8 @@ device_auth_done_cb (NMAuthChain *chain, const char *permission; NMDeviceAuthRequestFunc callback; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); permission = nm_auth_chain_get_data (chain, "requested-permission"); @@ -1935,25 +1684,24 @@ device_auth_request_cb (NMDevice *device, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; NMAuthChain *chain; - const char *error_desc = NULL; /* Validate the request */ - chain = nm_auth_chain_new (context, device_auth_done_cb, self, &error_desc); - if (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, "requested-permission", g_strdup (permission), g_free); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "user-data", user_data, NULL); - nm_auth_chain_add_call (chain, permission, allow_interaction); - } else { + chain = nm_auth_chain_new_context (context, device_auth_done_cb, self); + if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); + "Unable to authenticate request."); callback (device, context, error, user_data); - g_error_free (error); + g_clear_error (&error); + return; } + + 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, "requested-permission", g_strdup (permission), g_free); + nm_auth_chain_set_data (chain, "callback", callback, NULL); + nm_auth_chain_set_data (chain, "user-data", user_data, NULL); + nm_auth_chain_add_call (chain, permission, allow_interaction); } /* This should really be moved to gsystem. */ @@ -2167,22 +1915,35 @@ add_device (NMManager *self, NMDevice *device) /* If the device has a connection it can assume, do that now */ if (connection && nm_device_can_activate (device, connection)) { - NMActiveConnection *ac; + NMActiveConnection *active; + NMAuthSubject *subject; GError *error = NULL; nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection", nm_device_get_iface (device)); - ac = internal_activate_device (self, device, connection, NULL, FALSE, 0, NULL, TRUE, NULL, &error); - if (ac) - active_connection_add (self, ac); - else { + /* Tear down any existing connection */ + if (nm_device_get_act_request (device)) { + nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.", + nm_device_get_iface (device)); + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + } + + subject = nm_auth_subject_new_internal (); + active = _new_active_connection (self, connection, NULL, device, subject, &error); + if (active) { + active_connection_add (self, active); + nm_device_activate (device, NM_ACT_REQUEST (active)); + } else { nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s", nm_connection_get_path (connection), error ? error->code : -1, error && error->message ? error->message : "(unknown)"); g_error_free (error); } + g_object_unref (subject); } } @@ -2640,54 +2401,6 @@ impl_manager_get_device_by_ip_iface (NMManager *self, return path ? TRUE : FALSE; } -static NMActiveConnection * -internal_activate_device (NMManager *manager, - NMDevice *device, - NMConnection *connection, - const char *specific_object, - gboolean user_requested, - gulong sender_uid, - const char *dbus_sender, - gboolean assumed, - NMActiveConnection *master, - GError **error) -{ - NMActRequest *req; - NMDevice *master_device = NULL; - - g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - - /* Ensure the requested connection is compatible with the device */ - if (!nm_device_check_connection_compatible (device, connection, error)) - return NULL; - - /* Tear down any existing connection */ - if (nm_device_get_act_request (device)) { - nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.", - nm_device_get_iface (device)); - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } - - if (master) - master_device = nm_active_connection_get_device (master); - - req = nm_act_request_new (connection, - specific_object, - user_requested, - sender_uid, - dbus_sender, - device, - master_device); - g_assert (req); - nm_device_activate (device, req); - - return NM_ACTIVE_CONNECTION (req); -} - /** * find_master: * @self: #NMManager object @@ -2800,9 +2513,7 @@ is_compatible_with_slave (NMConnection *master, NMConnection *slave) * ensure_master_active_connection: * * @self: the #NMManager - * @dbus_sender: if the request was initiated by a user via D-Bus, the - * dbus sender name of the client that requested the activation; for auto - * activated connections use %NULL + * @subject: the #NMAuthSubject representing the requestor of this activation * @connection: the connection that should depend on @master_connection * @device: the #NMDevice, if any, which will activate @connection * @master_connection: the master connection @@ -2817,7 +2528,7 @@ is_compatible_with_slave (NMConnection *master, NMConnection *slave) */ static NMActiveConnection * ensure_master_active_connection (NMManager *self, - const char *dbus_sender, + NMAuthSubject *subject, NMConnection *connection, NMDevice *device, NMConnection *master_connection, @@ -2872,8 +2583,8 @@ ensure_master_active_connection (NMManager *self, master_ac = nm_manager_activate_connection (self, candidate, NULL, - nm_device_get_path (master_device), - dbus_sender, + master_device, + subject, error); if (!master_ac) g_prefix_error (error, "%s", "Master device activation failed: "); @@ -2920,8 +2631,8 @@ ensure_master_active_connection (NMManager *self, master_ac = nm_manager_activate_connection (self, master_connection, NULL, - nm_device_get_path (candidate), - dbus_sender, + candidate, + subject, error); if (!master_ac) g_prefix_error (error, "%s", "Master device activation failed: "); @@ -2936,7 +2647,7 @@ ensure_master_active_connection (NMManager *self, master_connection, NULL, NULL, - dbus_sender, + subject, error); if (!master_ac) g_prefix_error (error, "%s", "Master device activation failed: "); @@ -2954,219 +2665,119 @@ ensure_master_active_connection (NMManager *self, return NULL; } -static NMActiveConnection * -activate_vpn_connection (NMManager *self, - NMConnection *connection, - const char *specific_object, - gulong sender_uid, - GError **error) +static gboolean +_internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMActiveConnection *parent = NULL; - NMDevice *device = NULL; - GSList *iter; - - if (specific_object) { - /* Find the specifc connection the client requested we use */ - parent = active_connection_get_by_path (self, specific_object); - if (!parent) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "Base connection for VPN connection not active."); - return NULL; - } - } else { - for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { - NMActiveConnection *candidate = iter->data; + g_assert (NM_IS_VPN_CONNECTION (active)); - if (nm_active_connection_get_default (candidate)) { - parent = candidate; - break; - } - } - } - - if (!parent) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Could not find source connection."); - return NULL; - } - - device = nm_active_connection_get_device (parent); - if (!device) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Source connection had no active device."); - return NULL; - } - - return nm_vpn_manager_activate_connection (priv->vpn_manager, - connection, - device, - nm_active_connection_get_path (parent), - TRUE, - sender_uid, + return nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager, + NM_VPN_CONNECTION (active), error); } -NMActiveConnection * -nm_manager_activate_connection (NMManager *manager, - NMConnection *connection, - const char *specific_object, - const char *device_path, - const char *dbus_sender, - GError **error) +static gboolean +_internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error) { - NMManagerPrivate *priv; - NMDevice *device = NULL; - gulong sender_uid = G_MAXULONG; - char *iface; - NMDevice *master_device = NULL; + NMDevice *device, *master_device = NULL; + NMConnection *connection; NMConnection *master_connection = NULL; - NMActiveConnection *master_ac = NULL, *ac = NULL; - gboolean matched; - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (connection != NULL, NULL); - g_return_val_if_fail (error != NULL, NULL); - g_return_val_if_fail (*error == NULL, NULL); + g_assert (NM_IS_VPN_CONNECTION (active) == FALSE); - priv = NM_MANAGER_GET_PRIVATE (manager); + connection = nm_active_connection_get_connection (active); + g_assert (connection); - /* Get the UID of the user that originated the request, if any */ - if (dbus_sender) { - if (!nm_dbus_manager_get_unix_user (priv->dbus_mgr, dbus_sender, &sender_uid)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - "Failed to get unix user for dbus sender"); - return NULL; - } - } else { - /* No sender means an internal/automatic activation request */ - sender_uid = 0; - } + device = nm_active_connection_get_device (active); + if (!device) { + char *iface; - /* VPN ? */ - if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) { - ac = activate_vpn_connection (manager, connection, specific_object, sender_uid, error); - goto activated; - } + g_assert (connection_needs_virtual_device (connection)); - /* Device-based connection */ - if (device_path) { - device = nm_manager_get_device_by_path (manager, device_path); - if (!device) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device not found"); - return NULL; - } + iface = get_virtual_iface_name (self, connection, NULL); + g_assert (iface); - /* If it's a virtual interface make sure the device given by the - * path matches the connection's interface details. + /* Create the software device. Only exception is when: + * - this is an auto-activation *and* the device denies auto-activation + * at this time (the device was manually disconnected/deleted before) */ - if (connection_needs_virtual_device (connection)) { - iface = get_virtual_iface_name (manager, connection, NULL); - if (!iface) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to determine connection's virtual interface name"); - return NULL; - } - - matched = g_str_equal (iface, nm_device_get_ip_iface (device)); - g_free (iface); - if (!matched) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device given by path did not match connection's virtual interface name"); - return NULL; + if (!nm_manager_can_device_auto_connect (self, iface)) { + if (nm_active_connection_get_user_requested (active)) { + /* Manual activation - allow device auto-activation again */ + nm_manager_prevent_device_auto_connect (self, iface, FALSE); + } else { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED, + "Automatic activation of '%s' not allowed for connection '%s'", + iface, nm_connection_get_id (connection)); + g_free (iface); + return FALSE; } } - } else { - /* Virtual connections (VLAN, bond, team, etc) may not specify - * a device path because the device may not be created yet, - * or it be given by the connection's properties instead. - * Find the device the connection refers to, or create it - * if needed. - */ - if (!connection_needs_virtual_device (connection)) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "This connection requires an existing device."); - return NULL; - } - - iface = get_virtual_iface_name (manager, connection, NULL); - if (!iface) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to determine connection's virtual interface name"); - return NULL; - } + g_free (iface); - device = find_device_by_ip_iface (manager, iface); + device = system_create_virtual_device (self, connection); if (!device) { - /* Create the software device. Only exception is when: - * - this is an auto-activation *and* the device denies auto-activation - * at this time (the device was manually disconnected/deleted before) - */ - if (!nm_manager_can_device_auto_connect (manager, iface)) { - if (dbus_sender) { - /* Manual activation - allow device auto-activation again */ - nm_manager_prevent_device_auto_connect (manager, iface, FALSE); - } else { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED, - "'%s' does not allow automatic connections at this time => software device '%s' not created for '%s'", - iface, iface, nm_connection_get_id (connection)); - g_free (iface); - return NULL; - } - } + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to create virtual interface"); + return FALSE; + } - device = system_create_virtual_device (manager, connection); - if (!device) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to create virtual interface '%s'", iface); - g_free (iface); - return NULL; - } + if (!nm_active_connection_set_device (active, device)) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "The device could not be activated with this connection"); + return FALSE; + } - /* A newly created device, if allowed to be managed by NM, will be - * in the UNAVAILABLE state here. To ensure it can be activated - * immediately, we transition it to DISCONNECTED so it passes the - * nm_device_can_activate() check below. - */ - if ( nm_device_is_available (device) - && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } + /* A newly created device, if allowed to be managed by NM, will be + * in the UNAVAILABLE state here. To ensure it can be activated + * immediately, we transition it to DISCONNECTED so it passes the + * nm_device_can_activate() check below. + */ + if ( nm_device_is_available (device) + && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); } - g_free (iface); } + /* Final connection must be compatible with the device */ + if (!nm_device_check_connection_compatible (device, connection, error)) + return FALSE; + if (!nm_device_can_activate (device, connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, "Device not managed by NetworkManager or unavailable"); - return NULL; + return FALSE; } /* If this is an autoconnect request, but the device isn't allowing autoconnect * right now, we reject it. */ - if (!dbus_sender && !nm_device_autoconnect_allowed (device)) { + if (!nm_active_connection_get_user_requested (active) && + !nm_device_autoconnect_allowed (device)) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED, "%s does not allow automatic connections at this time", nm_device_get_iface (device)); - return NULL; + return FALSE; } /* Try to find the master connection/device if the connection has a dependency */ - if (!find_master (manager, connection, device, &master_connection, &master_device)) { + if (!find_master (self, connection, device, &master_connection, &master_device)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, "Master connection not found or invalid"); - return NULL; + return FALSE; } /* Ensure there's a master active connection the new connection we're * activating can depend on. */ if (master_connection || master_device) { + NMActiveConnection *master_ac = NULL; + if (master_connection) { nm_log_dbg (LOGD_CORE, "Activation of '%s' requires master connection '%s'", nm_connection_get_id (connection), @@ -3178,17 +2789,15 @@ nm_manager_activate_connection (NMManager *manager, nm_device_get_ip_iface (master_device)); } - /* Ensure eg bond/team slave and the candidate master is - * a bond/team master - */ + /* Ensure eg bond slave and the candidate master is a bond master */ if (master_connection && !is_compatible_with_slave (master_connection, connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, "The master connection was not compatible"); - return NULL; + return FALSE; } - master_ac = ensure_master_active_connection (manager, - dbus_sender, + master_ac = ensure_master_active_connection (self, + nm_active_connection_get_subject (active), connection, device, master_connection, @@ -3197,95 +2806,293 @@ nm_manager_activate_connection (NMManager *manager, if (!master_ac) { if (error) g_assert (*error); - return NULL; + return FALSE; } + nm_active_connection_set_master (active, master_ac); nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %s", nm_connection_get_id (connection), nm_active_connection_get_path (master_ac)); } - ac = internal_activate_device (manager, - device, - connection, - specific_object, - dbus_sender ? TRUE : FALSE, - dbus_sender ? sender_uid : 0, - dbus_sender, - FALSE, - master_ac, - error); + /* Tear down any existing connection */ + if (nm_device_get_act_request (device)) { + nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.", + nm_device_get_iface (device)); + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + } + + /* Start the new activation */ + nm_device_activate (device, NM_ACT_REQUEST (active)); + return TRUE; +} -activated: - if (ac) - active_connection_add (manager, ac); +static gboolean +_internal_activate_generic (NMManager *self, NMActiveConnection *active, GError **error) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean success = FALSE; - return ac; + if (NM_IS_VPN_CONNECTION (active)) + success = _internal_activate_vpn (self, active, error); + else + success = _internal_activate_device (self, active, error); + + if (success) { + nm_active_connection_export (active); + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + + /* Force an update of the Manager's activating-connection property. + * The device changes state before the AC gets exported, which causes + * the manager's 'activating-connection' property to be NULL since the + * AC only gets a D-Bus path when it's exported. So now that the AC + * is exported, make sure the manager's activating-connection property + * is up-to-date. + */ + policy_activating_device_changed (G_OBJECT (priv->policy), NULL, self); + } + + return success; } -/* - * TODO this function was created and named in the era of user settings, where - * we could get activation requests for a connection before we got the settings - * data of that connection. Now that user settings are gone, flatten or rename - * it. - */ -static void -pending_activate (NMManager *self, PendingActivation *pending) +static NMActiveConnection * +_new_vpn_active_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMAuthSubject *subject, + GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMSettingsConnection *connection; - NMActiveConnection *ac = NULL; - GError *error = NULL; - char *sender = NULL; + NMActiveConnection *parent = NULL; + NMDevice *device = NULL; - /* Ok, we're authorized */ + if (specific_object) { + /* Find the specifc connection the client requested we use */ + parent = active_connection_get_by_path (self, specific_object); + if (!parent) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + "Base connection for VPN connection not active."); + return NULL; + } + } else + parent = priv->primary_connection; - connection = nm_settings_get_connection_by_path (priv->settings, pending->connection_path); - if (!connection) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection could not be found."); - goto out; + if (!parent) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Could not find source connection."); + return NULL; } - if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, - pending->context, - &sender, - NULL)) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "D-Bus sendder could not be determined."); - goto out; + device = nm_active_connection_get_device (parent); + if (!device) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Source connection had no active device."); + return NULL; } - g_assert (sender); - ac = nm_manager_activate_connection (self, - NM_CONNECTION (connection), - pending->specific_object_path, - pending->device_path, - sender, - &error); - g_free (sender); + return (NMActiveConnection *) nm_vpn_connection_new (connection, + device, + nm_active_connection_get_path (parent), + subject); +} - if (!ac) { - nm_log_warn (LOGD_CORE, "connection %s failed to activate: (%d) %s", - pending->connection_path, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); +static NMActiveConnection * +_new_active_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMDevice *device, + NMAuthSubject *subject, + GError **error) +{ + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + + /* Normalize the specific object */ + if (specific_object && g_strcmp0 (specific_object, "/") == 0) + specific_object = NULL; + + if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) { + return _new_vpn_active_connection (self, + connection, + specific_object, + subject, + error); } -out: - pending_activation_destroy (pending, error, ac); - g_clear_error (&error); + return (NMActiveConnection *) nm_act_request_new (connection, + specific_object, + subject, + device); } static void -activation_auth_done (PendingActivation *pending, GError *error) +_internal_activation_auth_done (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2) { - if (error) - pending_activation_destroy (pending, error, NULL); - else - pending_activate (pending->manager, pending); + NMManager *self = user_data1; + GError *error = NULL; + + if (success) { + if (_internal_activate_generic (self, active, &error)) + return; + g_assert (error); + } + + active_connection_remove (self, active); + nm_log_warn (LOGD_CORE, "Failed to activate '%s': %s", + nm_connection_get_id (nm_active_connection_get_connection (active)), + error_desc ? error_desc : error->message); + g_clear_error (&error); +} + +NMActiveConnection * +nm_manager_activate_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMDevice *device, + NMAuthSubject *subject, + GError **error) +{ + NMManagerPrivate *priv; + NMActiveConnection *active; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + priv = NM_MANAGER_GET_PRIVATE (self); + + active = _new_active_connection (self, + connection, + specific_object, + device, + subject, + error); + if (active) { + nm_active_connection_authorize (active, _internal_activation_auth_done, self, NULL); + active_connection_add (self, active); + } + return active; +} + +static NMAuthSubject * +validate_activation_request (NMManager *self, + DBusGMethodInvocation *context, + NMConnection *connection, + const char *device_path, + NMDevice **out_device, + gboolean *out_vpn, + GError **error) +{ + NMDevice *device = NULL; + gboolean vpn = FALSE; + NMAuthSubject *subject = NULL; + + g_assert (connection); + g_assert (out_device); + g_assert (out_vpn); + + /* Validate the caller */ + subject = nm_auth_subject_new_from_context (context); + if (!subject) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Failed to get request UID."); + return NULL; + } + + /* Check whether it's a VPN or not */ + if ( nm_connection_get_setting_vpn (connection) + || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) + vpn = TRUE; + + /* Normalize device path */ + if (device_path && g_strcmp0 (device_path, "/") == 0) + device_path = NULL; + + /* And validate it */ + if (device_path) { + device = nm_manager_get_device_by_path (self, device_path); + if (!device) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Device not found"); + goto error; + } + } else { + gboolean is_software = connection_needs_virtual_device (connection); + + /* VPN and software-device connections don't need a device yet */ + if (!vpn && !is_software) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "This connection requires an existing device."); + goto error; + } + + if (is_software) { + /* Look for an existing device with the connection's interface name */ + char *iface; + + iface = get_virtual_iface_name (self, connection, NULL); + if (!iface) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to determine connection's virtual interface name"); + goto error; + } + + device = find_device_by_ip_iface (self, iface); + g_free (iface); + } + } + + *out_device = device; + *out_vpn = vpn; + return subject; + +error: + g_object_unref (subject); + return NULL; +} + +/***********************************************************************/ + +static void +activation_auth_done (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2) +{ + NMManager *self = user_data1; + DBusGMethodInvocation *context = user_data2; + GError *error = NULL; + + if (success) { + if (_internal_activate_generic (self, active, &error)) { + dbus_g_method_return (context, nm_active_connection_get_path (active)); + return; + } + } else { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + } + + active_connection_remove (self, active); + dbus_g_method_return_error (context, error); + g_error_free (error); } static void @@ -3295,64 +3102,129 @@ impl_manager_activate_connection (NMManager *self, const char *specific_object_path, DBusGMethodInvocation *context) { - PendingActivation *pending; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMActiveConnection *active = NULL; + NMAuthSubject *subject = NULL; + NMConnection *connection; + NMDevice *device = NULL; + gboolean is_vpn = FALSE; GError *error = NULL; - /* Need to check the caller's permissions and stuff before we can - * activate the connection. - */ - pending = pending_activation_new (self, - context, - device_path, - connection_path, - NULL, - specific_object_path, - activation_auth_done, - &error); - if (pending) - pending_activation_check_authorized (pending); - else { - g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); - } + connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, connection_path); + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection could not be found."); + goto error; + } + + subject = validate_activation_request (self, + context, + connection, + device_path, + &device, + &is_vpn, + &error); + if (!subject) + goto error; + + active = _new_active_connection (self, + connection, + specific_object_path, + device, + subject, + &error); + if (!active) + goto error; + + nm_active_connection_authorize (active, activation_auth_done, self, context); + active_connection_add (self, active); + g_clear_object (&subject); + return; + +error: + g_clear_object (&active); + g_clear_object (&subject); + + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); } +/***********************************************************************/ + +typedef struct { + NMManager *manager; + NMActiveConnection *active; +} AddAndActivateInfo; + static void activation_add_done (NMSettings *self, - NMSettingsConnection *connection, + NMSettingsConnection *new_connection, GError *error, DBusGMethodInvocation *context, gpointer user_data) { - PendingActivation *pending = user_data; + AddAndActivateInfo *info = user_data; + GError *local = NULL; - if (error) - pending_activation_destroy (pending, error, NULL); - else { - /* Save the new connection's D-Bus path */ - pending->connection_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection))); + if (!error) { + nm_active_connection_set_connection (info->active, NM_CONNECTION (new_connection)); - /* And activate it */ - pending_activate (pending->manager, pending); + if (_internal_activate_generic (info->manager, info->active, &local)) { + dbus_g_method_return (context, + nm_connection_get_path (NM_CONNECTION (new_connection)), + nm_active_connection_get_path (info->active)); + goto done; + } + error = local; } + + active_connection_remove (info->manager, info->active); + + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&local); + +done: + g_object_unref (info->active); + g_free (info); } static void -add_and_activate_auth_done (PendingActivation *pending, GError *error) +add_and_activate_auth_done (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2) { - if (error) - pending_activation_destroy (pending, error, NULL); - else { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pending->manager); + NMManager *self = user_data1; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGMethodInvocation *context = user_data2; + AddAndActivateInfo *info; + GError *error = NULL; + + if (success) { + info = g_malloc0 (sizeof (*info)); + info->manager = self; + info->active = g_object_ref (active); /* Basic sender auth checks performed; try to add the connection */ nm_settings_add_connection_dbus (priv->settings, - pending->connection, + nm_active_connection_get_connection (active), TRUE, - pending->context, + context, activation_add_done, - pending); + info); + } else { + active_connection_remove (self, active); + + g_assert (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); } } @@ -3363,29 +3235,99 @@ impl_manager_add_and_activate_connection (NMManager *self, const char *specific_object_path, DBusGMethodInvocation *context) { - PendingActivation *pending; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMConnection *connection = NULL; + GSList *all_connections = NULL; + NMActiveConnection *active; + NMAuthSubject *subject = NULL; GError *error = NULL; + NMDevice *device = NULL; + gboolean vpn = FALSE; - /* Need to check the caller's permissions and stuff before we can - * activate the connection. - */ - pending = pending_activation_new (self, - context, - device_path, - NULL, - settings, - specific_object_path, - add_and_activate_auth_done, - &error); - if (pending) - pending_activation_check_authorized (pending); - else { - g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); + if (!settings || !g_hash_table_size (settings)) { + error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Settings required to create a connection."); + goto error; } + + /* Try to create a new connection with the given settings */ + connection = nm_connection_new (); + nm_connection_replace_settings (connection, settings, NULL); + + subject = validate_activation_request (self, + context, + connection, + device_path, + &device, + &vpn, + &error); + if (!subject) + goto error; + + /* AddAndActivate() requires a device to complete the connection with */ + if (!device) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "This connection requires an existing device."); + goto error; + } + + all_connections = nm_settings_get_connections (priv->settings); + if (vpn) { + /* Try to fill the VPN's connection setting and name at least */ + if (!nm_connection_get_setting_vpn (connection)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE, + "VPN connections require a 'vpn' setting"); + goto error; + } + + nm_utils_complete_generic (connection, + NM_SETTING_VPN_SETTING_NAME, + all_connections, + _("VPN connection %d"), + NULL, + FALSE); /* No IPv6 by default for now */ + } else { + /* Let each device subclass complete the connection */ + if (!nm_device_complete_connection (device, + connection, + specific_object_path, + all_connections, + &error)) + goto error; + } + g_slist_free (all_connections); + all_connections = NULL; + + active = _new_active_connection (self, + connection, + specific_object_path, + device, + subject, + &error); + if (!active) + goto error; + + nm_active_connection_authorize (active, add_and_activate_auth_done, self, context); + active_connection_add (self, active); + g_object_unref (connection); + g_object_unref (subject); + return; + +error: + g_clear_object (&connection); + g_slist_free (all_connections); + g_clear_object (&subject); + g_clear_object (&active); + + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); } +/***********************************************************************/ + gboolean nm_manager_deactivate_connection (NMManager *manager, const char *connection_path, @@ -3439,6 +3381,8 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, GError *error = NULL; NMAuthCallResult result; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); @@ -3481,7 +3425,6 @@ impl_manager_deactivate_connection (NMManager *self, GError *error = NULL; GSList *iter; NMAuthChain *chain; - const char *error_desc = NULL; /* Find the connection by its object path */ for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { @@ -3497,25 +3440,26 @@ impl_manager_deactivate_connection (NMManager *self, 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; + goto done; } /* Validate the user request */ - chain = nm_auth_chain_new (context, deactivate_net_auth_done_cb, self, &error_desc); - if (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_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); - } else { + chain = nm_auth_chain_new_context (context, deactivate_net_auth_done_cb, self); + if (!chain) { 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); + "Unable to authenticate request."); + goto done; } + + 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_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + +done: + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&error); } /* @@ -3775,6 +3719,8 @@ enable_net_done_cb (NMAuthChain *chain, NMAuthCallResult result; gboolean enable; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); @@ -3811,7 +3757,6 @@ impl_manager_enable (NMManager *self, NMManagerPrivate *priv; NMAuthChain *chain; GError *error = NULL; - const char *error_desc; g_return_if_fail (NM_IS_MANAGER (self)); @@ -3821,24 +3766,25 @@ impl_manager_enable (NMManager *self, error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, "Already %s", enable ? "enabled" : "disabled"); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + goto done; } - chain = nm_auth_chain_new (context, enable_net_done_cb, self, &error_desc); - if (chain) { - priv->auth_chains = g_slist_append (priv->auth_chains, chain); - - nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE); - } else { + chain = nm_auth_chain_new_context (context, enable_net_done_cb, self); + if (!chain) { 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); + "Unable to authenticate request."); + goto done; } + + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE); + +done: + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&error); } /* Permissions */ @@ -3871,6 +3817,8 @@ get_permissions_done_cb (NMAuthChain *chain, GError *ret_error; GHashTable *results; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); if (error) { nm_log_dbg (LOGD_CORE, "Permissions request failed: %s", error->message); @@ -3908,31 +3856,30 @@ impl_manager_get_permissions (NMManager *self, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; - const char *error_desc = NULL; - GError *error; - - chain = nm_auth_chain_new (context, get_permissions_done_cb, self, &error_desc); - if (chain) { - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + GError *error = NULL; - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, FALSE); - } else { + chain = nm_auth_chain_new_context (context, get_permissions_done_cb, self); + if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); + "Unable to authenticate request."); dbus_g_method_return_error (context, error); - g_error_free (error); + g_clear_error (&error); + return; } + + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, FALSE); } static gboolean @@ -4033,22 +3980,21 @@ impl_manager_check_connectivity (NMManager *manager, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); NMAuthChain *chain; - const char *error_desc = NULL; - GError *error; - - /* Validate the user request */ - chain = nm_auth_chain_new (context, check_connectivity_auth_done_cb, manager, &error_desc); - if (chain) { - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + GError *error = NULL; - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); - } else { + /* Validate the request */ + chain = nm_auth_chain_new_context (context, check_connectivity_auth_done_cb, manager); + if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); + "Unable to authenticate request."); dbus_g_method_return_error (context, error); - g_error_free (error); + g_clear_error (&error); + return; } + + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } void @@ -4304,9 +4250,9 @@ prop_filter (DBusConnection *connection, const char *propiface = NULL; const char *propname = NULL; const char *glib_propname = NULL, *permission = NULL; - gulong caller_uid = G_MAXULONG; DBusMessage *reply = NULL; gboolean set_enabled = FALSE; + NMAuthSubject *subject = NULL; NMAuthChain *chain; GObject *obj; @@ -4366,19 +4312,21 @@ prop_filter (DBusConnection *connection, goto out; } - if (!nm_dbus_manager_get_caller_info_from_message (priv->dbus_mgr, - connection, - message, - NULL, - &caller_uid)) { + subject = nm_auth_subject_new_from_message (connection, message);\ + if (!subject) { reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR, "Could not determine request UID."); goto out; } /* Validate the user request */ - chain = nm_auth_chain_new_raw_message (message, caller_uid, prop_set_auth_done_cb, self); - g_assert (chain); + chain = nm_auth_chain_new_subject (subject, NULL, prop_set_auth_done_cb, self); + if (!chain) { + reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR, + "Could not authenticate request."); + goto out; + } + priv->auth_chains = g_slist_append (priv->auth_chains, chain); nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free); nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free); @@ -4393,6 +4341,8 @@ out: dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } + g_clear_object (&subject); + return DBUS_HANDLER_RESULT_HANDLED; } @@ -4537,7 +4487,6 @@ dispose (GObject *object) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); DBusGConnection *bus; DBusConnection *dbus_connection; - GSList *iter; if (priv->disposed) { G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); @@ -4561,8 +4510,8 @@ dispose (GObject *object) priv->ac_cleanup_id = 0; } - for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) - active_connection_removed (manager, NM_ACTIVE_CONNECTION (iter->data)); + while (priv->active_connections) + active_connection_remove (manager, NM_ACTIVE_CONNECTION (priv->active_connections->data)); g_slist_free (priv->active_connections); priv->active_connections = NULL; g_clear_object (&priv->primary_connection); @@ -4812,7 +4761,8 @@ get_property (GObject *object, guint prop_id, active = g_ptr_array_sized_new (3); for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (iter->data)); - g_ptr_array_add (active, g_strdup (path)); + if (path) + g_ptr_array_add (active, g_strdup (path)); } g_value_take_boxed (value, active); break; @@ -4820,12 +4770,12 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); break; case PROP_PRIMARY_CONNECTION: - path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : "/"; - g_value_set_boxed (value, path); + path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : NULL; + g_value_set_boxed (value, path ? path : "/"); break; case PROP_ACTIVATING_CONNECTION: - path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : "/"; - g_value_set_boxed (value, path); + path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : NULL; + g_value_set_boxed (value, path ? path : "/"); break; case PROP_HOSTNAME: g_value_set_string (value, priv->hostname); diff --git a/src/nm-manager.h b/src/nm-manager.h index fd6fa0aeee..5f848688d9 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -27,6 +27,7 @@ #include <dbus/dbus-glib.h> #include "nm-device.h" #include "nm-settings.h" +#include "nm-auth-subject.h" #define NM_TYPE_MANAGER (nm_manager_get_type ()) #define NM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager)) @@ -116,8 +117,8 @@ NMDevice *nm_manager_get_device_by_ifindex (NMManager *manager, NMActiveConnection *nm_manager_activate_connection (NMManager *manager, NMConnection *connection, const char *specific_object, - const char *device_path, - const char *dbus_sender, /* NULL if automatic */ + NMDevice *device, + NMAuthSubject *subject, GError **error); gboolean nm_manager_deactivate_connection (NMManager *manager, diff --git a/src/nm-policy.c b/src/nm-policy.c index 49c005c47e..98818047c0 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -669,7 +669,7 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update) gw_addr = nm_ip4_config_get_gateway (ip4_config); if (vpn) { - NMDevice *parent = nm_vpn_connection_get_parent_device (vpn); + NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); int parent_ifindex = nm_device_get_ip_ifindex (parent); NMIP4Config *parent_ip4 = nm_device_get_ip4_config (parent); guint32 parent_mss = parent_ip4 ? nm_ip4_config_get_mss (parent_ip4) : 0; @@ -683,7 +683,7 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update) } } - default_device = nm_vpn_connection_get_parent_device (vpn); + default_device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); } else { int mss = nm_ip4_config_get_mss (ip4_config); @@ -856,7 +856,7 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update) gw_addr = &in6addr_any; if (vpn) { - NMDevice *parent = nm_vpn_connection_get_parent_device (vpn); + NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); int parent_ifindex = nm_device_get_ip_ifindex (parent); NMIP6Config *parent_ip6 = nm_device_get_ip6_config (parent); guint32 parent_mss = parent_ip6 ? nm_ip6_config_get_mss (parent_ip6) : 0; @@ -873,7 +873,7 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update) } } - default_device6 = nm_vpn_connection_get_parent_device (vpn); + default_device6 = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); } else { int mss = nm_ip6_config_get_mss (ip6_config); @@ -1034,14 +1034,16 @@ auto_activate_device (gpointer user_data) best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object); if (best_connection) { GError *error = NULL; + NMAuthSubject *subject; nm_log_info (LOGD_DEVICE, "Auto-activating connection '%s'.", nm_connection_get_id (best_connection)); + subject = nm_auth_subject_new_internal (); if (!nm_manager_activate_connection (priv->manager, best_connection, specific_object, - nm_device_get_path (data->device), - NULL, + data->device, + subject, &error)) { nm_log_info (LOGD_DEVICE, "Connection '%s' auto-activation failed: (%d) %s", nm_connection_get_id (best_connection), @@ -1049,6 +1051,7 @@ auto_activate_device (gpointer user_data) error ? error->message : "(none)"); g_error_free (error); } + g_object_unref (subject); } g_slist_free (connections); @@ -1110,7 +1113,7 @@ static void pending_secondary_data_free (PendingSecondaryData *data) { g_object_unref (data->device); - g_slist_free_full (data->secondaries, g_free); + g_slist_free_full (data->secondaries, g_object_unref); memset (data, 0, sizeof (*data)); g_free (data); } @@ -1121,51 +1124,47 @@ process_secondaries (NMPolicy *policy, gboolean connected) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy); - NMDevice *device = NULL; - const char *ac_path; GSList *iter, *iter2; - nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' %s; active path '%s'", - nm_active_connection_get_name (active), - connected ? "SUCCEEDED" : "FAILED", - nm_active_connection_get_path (active)); - - ac_path = nm_active_connection_get_path (active); - - if (NM_IS_VPN_CONNECTION (active)) - device = nm_vpn_connection_get_parent_device (NM_VPN_CONNECTION (active)); - + /* Loop through devices waiting for secondary connections to activate */ for (iter = priv->pending_secondaries; iter; iter = g_slist_next (iter)) { PendingSecondaryData *secondary_data = (PendingSecondaryData *) iter->data; NMDevice *item_device = secondary_data->device; - if (!device || item_device == device) { - for (iter2 = secondary_data->secondaries; iter2; iter2 = g_slist_next (iter2)) { - char *list_ac_path = (char *) iter2->data; - - if (g_strcmp0 (ac_path, list_ac_path) == 0) { - if (connected) { - /* Secondary connection activated */ - secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, list_ac_path); - g_free (list_ac_path); - if (!secondary_data->secondaries) { - /* None secondary UUID remained -> remove the secondary data item */ - priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data); - pending_secondary_data_free (secondary_data); - nm_device_state_changed (item_device, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE); - return; - } - } else { - /* Secondary connection failed -> do not watch other connections */ - priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data); - pending_secondary_data_free (secondary_data); - nm_device_state_changed (item_device, NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED); - return; - } + /* Look for 'active' in each device's secondary connections list */ + for (iter2 = secondary_data->secondaries; iter2; iter2 = g_slist_next (iter2)) { + NMActiveConnection *secondary_active = NM_ACTIVE_CONNECTION (iter2->data); + + if (active != secondary_active) + continue; + + if (connected) { + nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' SUCCEEDED; active path '%s'", + nm_active_connection_get_name (active), + nm_active_connection_get_path (active)); + + /* Secondary connection activated */ + secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, secondary_active); + g_object_unref (secondary_active); + if (!secondary_data->secondaries) { + /* No secondary UUID remained -> remove the secondary data item */ + priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data); + pending_secondary_data_free (secondary_data); + nm_device_state_changed (item_device, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE); + break; } + } else { + nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' FAILED; active path '%s'", + nm_active_connection_get_name (active), + nm_active_connection_get_path (active)); + + /* Secondary connection failed -> do not watch other connections */ + priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data); + pending_secondary_data_free (secondary_data); + nm_device_state_changed (item_device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED); + break; } - return; } } } @@ -1233,6 +1232,7 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy); ActivateData *data; + const GSList *active_connections, *iter; if (nm_manager_get_state (priv->manager) == NM_STATE_ASLEEP) return; @@ -1246,6 +1246,15 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds if (!nm_device_autoconnect_allowed (device)) return; + /* If the device already has an activation in-progress or waiting for + * authentication, don't start an auto-activation for it. + */ + active_connections = nm_manager_get_active_connections (priv->manager); + for (iter = active_connections; iter; iter = iter->next) { + if (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (iter->data)) == device) + return; + } + /* Schedule an auto-activation if there isn't one already for this device */ if (find_pending_activation (priv->pending_activation_checks, device) == NULL) { data = activate_data_new (policy, device, delay_seconds); @@ -1357,13 +1366,12 @@ activate_secondary_connections (NMPolicy *policy, ac = nm_manager_activate_connection (priv->manager, NM_CONNECTION (settings_con), nm_active_connection_get_path (NM_ACTIVE_CONNECTION (req)), - nm_device_get_path (device), - nm_act_request_get_dbus_sender (req), + device, + nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)), &error); - if (ac) { - secondary_ac_list = g_slist_append (secondary_ac_list, - g_strdup (nm_active_connection_get_path (ac))); - } else { + if (ac) + secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac)); + else { nm_log_warn (LOGD_DEVICE, "Secondary connection '%s' auto-activation failed: (%d) %s", sec_uuid, error ? error->code : 0, @@ -1384,7 +1392,7 @@ activate_secondary_connections (NMPolicy *policy, secondary_data = pending_secondary_data_new (device, secondary_ac_list); priv->pending_secondaries = g_slist_append (priv->pending_secondaries, secondary_data); } else - g_slist_free_full (secondary_ac_list, g_free); + g_slist_free_full (secondary_ac_list, g_object_unref); return success; } @@ -1720,8 +1728,6 @@ vpn_connection_activated (NMPolicy *policy, NMVPNConnection *vpn) update_routing_and_dns (policy, TRUE); nm_dns_manager_end_updates (mgr, __func__); - - process_secondaries (policy, NM_ACTIVE_CONNECTION (vpn), TRUE); } static void @@ -1737,7 +1743,7 @@ vpn_connection_deactivated (NMPolicy *policy, NMVPNConnection *vpn) nm_dns_manager_begin_updates (mgr, __func__); ip_iface = nm_vpn_connection_get_ip_iface (vpn); - parent = nm_vpn_connection_get_parent_device (vpn); + parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); ip4_config = nm_vpn_connection_get_ip4_config (vpn); if (ip4_config) { @@ -1754,8 +1760,23 @@ vpn_connection_deactivated (NMPolicy *policy, NMVPNConnection *vpn) update_routing_and_dns (policy, TRUE); nm_dns_manager_end_updates (mgr, __func__); +} - process_secondaries (policy, NM_ACTIVE_CONNECTION (vpn), FALSE); +static void +vpn_connection_state_changed (NMVPNConnection *vpn, + NMVPNConnectionState new_state, + NMVPNConnectionState old_state, + NMVPNConnectionStateReason reason, + NMPolicy *policy) +{ + if (new_state == NM_VPN_CONNECTION_STATE_ACTIVATED) + vpn_connection_activated (policy, vpn); + else if (new_state >= NM_VPN_CONNECTION_STATE_FAILED) { + /* Only clean up IP/DNS if the connection ever got past IP_CONFIG */ + if (old_state >= NM_VPN_CONNECTION_STATE_IP_CONFIG_GET && + old_state <= NM_VPN_CONNECTION_STATE_ACTIVATED) + vpn_connection_deactivated (policy, vpn); + } } static void @@ -1763,18 +1784,12 @@ active_connection_state_changed (NMActiveConnection *active, GParamSpec *pspec, NMPolicy *policy) { - switch (nm_active_connection_get_state (active)) { - case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: - if (NM_IS_VPN_CONNECTION (active)) - vpn_connection_activated (policy, NM_VPN_CONNECTION (active)); - break; - case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: - if (NM_IS_VPN_CONNECTION (active)) - vpn_connection_deactivated (policy, NM_VPN_CONNECTION (active)); - break; - default: - break; - } + NMActiveConnectionState state = nm_active_connection_get_state (active); + + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) + process_secondaries (policy, active, TRUE); + else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) + process_secondaries (policy, active, FALSE); } static void @@ -1782,7 +1797,13 @@ active_connection_added (NMManager *manager, NMActiveConnection *active, gpointer user_data) { - NMPolicy *policy = (NMPolicy *) user_data; + NMPolicy *policy = NM_POLICY (user_data); + + if (NM_IS_VPN_CONNECTION (active)) { + g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, + G_CALLBACK (vpn_connection_state_changed), + policy); + } g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE, G_CALLBACK (active_connection_state_changed), @@ -1794,9 +1815,14 @@ active_connection_removed (NMManager *manager, NMActiveConnection *active, gpointer user_data) { + NMPolicy *policy = NM_POLICY (user_data); + + g_signal_handlers_disconnect_by_func (active, + vpn_connection_state_changed, + policy); g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, - (NMPolicy *) user_data); + policy); } /**************************************************************************/ diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 5d1c519efa..33fbbe7ff3 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -206,6 +206,8 @@ agent_register_permissions_done (NMAuthChain *chain, GHashTableIter iter; Request *req; + g_assert (context); + priv->chains = g_slist_remove (priv->chains, chain); if (error) { @@ -270,22 +272,20 @@ impl_agent_manager_register_with_capabilities (NMAgentManager *self, DBusGMethodInvocation *context) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - char *sender = NULL; + NMAuthSubject *subject; gulong sender_uid = G_MAXULONG; GError *error = NULL, *local = NULL; NMSecretAgent *agent; NMAuthChain *chain; - const char *error_desc = NULL; - if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, - context, - &sender, - &sender_uid)) { + subject = nm_auth_subject_new_from_context (context); + if (!subject) { error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, "Unable to determine request sender and UID."); goto done; } + sender_uid = nm_auth_subject_get_uid (subject); if ( 0 != sender_uid && !nm_session_monitor_uid_has_session (priv->session_monitor, @@ -311,7 +311,7 @@ impl_agent_manager_register_with_capabilities (NMAgentManager *self, } /* Success, add the new agent */ - agent = nm_secret_agent_new (context, sender, identifier, sender_uid, capabilities); + agent = nm_secret_agent_new (context, subject, identifier, capabilities); if (!agent) { error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_INTERNAL_ERROR, @@ -323,7 +323,7 @@ impl_agent_manager_register_with_capabilities (NMAgentManager *self, nm_secret_agent_get_description (agent)); /* Kick off permissions requests for this agent */ - chain = nm_auth_chain_new (context, agent_register_permissions_done, self, &error_desc); + chain = nm_auth_chain_new_subject (subject, context, agent_register_permissions_done, self); if (chain) { nm_auth_chain_set_data (chain, "agent", agent, g_object_unref); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); @@ -333,7 +333,7 @@ impl_agent_manager_register_with_capabilities (NMAgentManager *self, } else { error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, - error_desc); + "Unable to start agent authentication."); } done: @@ -341,7 +341,7 @@ done: dbus_g_method_return_error (context, error); g_clear_error (&error); g_clear_error (&local); - g_free (sender); + g_clear_object (&subject); } static void @@ -363,6 +363,7 @@ impl_agent_manager_unregister (NMAgentManager *self, if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, &sender, + NULL, NULL)) { error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, @@ -1016,10 +1017,10 @@ get_next_cb (Request *parent) nm_log_dbg (LOGD_AGENTS, "(%p/%s/%s) request has system secrets; checking agent %s for MODIFY", req, parent->detail, req->setting_name, agent_dbus_owner); - req->chain = nm_auth_chain_new_dbus_sender (agent_dbus_owner, - nm_secret_agent_get_owner_uid (parent->current), - get_agent_modify_auth_cb, - req); + req->chain = nm_auth_chain_new_subject (nm_secret_agent_get_subject (parent->current), + NULL, + get_agent_modify_auth_cb, + req); g_assert (req->chain); /* If the caller is the only user in the connection's permissions, then @@ -1502,10 +1503,10 @@ authority_changed_cb (gpointer user_data) NMAuthChain *chain; /* Kick off permissions requests for this agent */ - chain = nm_auth_chain_new_dbus_sender (nm_secret_agent_get_dbus_owner (agent), - nm_secret_agent_get_owner_uid (agent), - agent_permissions_changed_done, - self); + chain = nm_auth_chain_new_subject (nm_secret_agent_get_subject (agent), + NULL, + agent_permissions_changed_done, + self); g_assert (chain); priv->chains = g_slist_append (priv->chains, chain); diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index 7f0e879a3a..66ddc36a2a 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -31,6 +31,7 @@ #include "nm-secret-agent.h" #include "nm-dbus-manager.h" #include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" #include "nm-logging.h" G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) @@ -40,12 +41,9 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) NMSecretAgentPrivate)) typedef struct { - gboolean disposed; - char *description; - char *owner; + NMAuthSubject *subject; char *identifier; - uid_t owner_uid; char *owner_username; NMSecretAgentCapabilities capabilities; guint32 hash; @@ -106,10 +104,10 @@ nm_secret_agent_get_description (NMSecretAgent *agent) priv = NM_SECRET_AGENT_GET_PRIVATE (agent); if (!priv->description) { - priv->description = g_strdup_printf ("%s/%s/%u", - priv->owner, + priv->description = g_strdup_printf ("%s/%s/%lu", + nm_auth_subject_get_dbus_sender (priv->subject), priv->identifier, - priv->owner_uid); + nm_auth_subject_get_uid (priv->subject)); } return priv->description; @@ -120,7 +118,7 @@ nm_secret_agent_get_dbus_owner (NMSecretAgent *agent) { g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), NULL); - return NM_SECRET_AGENT_GET_PRIVATE (agent)->owner; + return nm_auth_subject_get_dbus_sender (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); } const char * @@ -131,16 +129,16 @@ nm_secret_agent_get_identifier (NMSecretAgent *agent) return NM_SECRET_AGENT_GET_PRIVATE (agent)->identifier; } -uid_t +gulong nm_secret_agent_get_owner_uid (NMSecretAgent *agent) { - g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), G_MAXUINT); + g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), G_MAXULONG); - return NM_SECRET_AGENT_GET_PRIVATE (agent)->owner_uid; + return nm_auth_subject_get_uid (NM_SECRET_AGENT_GET_PRIVATE (agent)->subject); } const char * -nm_secret_agent_get_owner_username(NMSecretAgent *agent) +nm_secret_agent_get_owner_username (NMSecretAgent *agent) { g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), NULL); @@ -156,13 +154,21 @@ nm_secret_agent_get_capabilities (NMSecretAgent *agent) } guint32 -nm_secret_agent_get_hash (NMSecretAgent *agent) +nm_secret_agent_get_hash (NMSecretAgent *agent) { g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), 0); return NM_SECRET_AGENT_GET_PRIVATE (agent)->hash; } +NMAuthSubject * +nm_secret_agent_get_subject (NMSecretAgent *agent) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), NULL); + + return NM_SECRET_AGENT_GET_PRIVATE (agent)->subject; +} + /** * nm_secret_agent_add_permission: * @agent: A #NMSecretAgent. @@ -443,9 +449,8 @@ proxy_cleanup (NMSecretAgent *self) NMSecretAgent * nm_secret_agent_new (DBusGMethodInvocation *context, - const char *owner, + NMAuthSubject *subject, const char *identifier, - uid_t owner_uid, NMSecretAgentCapabilities capabilities) { NMSecretAgent *self; @@ -453,10 +458,11 @@ nm_secret_agent_new (DBusGMethodInvocation *context, char *hash_str, *username; struct passwd *pw; - g_return_val_if_fail (owner != NULL, NULL); + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); g_return_val_if_fail (identifier != NULL, NULL); - pw = getpwuid (owner_uid); + pw = getpwuid (nm_auth_subject_get_uid (subject)); g_return_val_if_fail (pw != NULL, NULL); g_return_val_if_fail (pw->pw_name[0] != '\0', NULL); username = g_strdup (pw->pw_name); @@ -464,19 +470,18 @@ nm_secret_agent_new (DBusGMethodInvocation *context, self = (NMSecretAgent *) g_object_new (NM_TYPE_SECRET_AGENT, NULL); priv = NM_SECRET_AGENT_GET_PRIVATE (self); - priv->owner = g_strdup (owner); priv->identifier = g_strdup (identifier); - priv->owner_uid = owner_uid; priv->owner_username = g_strdup (username); priv->capabilities = capabilities; + priv->subject = g_object_ref (subject); - hash_str = g_strdup_printf ("%08u%s", owner_uid, identifier); + hash_str = g_strdup_printf ("%16lu%s", nm_auth_subject_get_uid (subject), identifier); priv->hash = g_str_hash (hash_str); g_free (hash_str); priv->proxy = nm_dbus_manager_new_proxy (nm_dbus_manager_get (), context, - owner, + nm_auth_subject_get_dbus_sender (subject), NM_DBUS_PATH_SECRET_AGENT, NM_DBUS_INTERFACE_SECRET_AGENT); g_assert (priv->proxy); @@ -501,21 +506,21 @@ dispose (GObject *object) { NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object); - if (!priv->disposed) { - priv->disposed = TRUE; - - g_free (priv->description); - g_free (priv->owner); - g_free (priv->identifier); - g_free (priv->owner_username); + g_clear_pointer (&priv->description, g_free); + g_clear_pointer (&priv->identifier, g_free); + g_clear_pointer (&priv->owner_username, g_free); - g_slist_free_full (priv->permissions, g_free); + g_slist_free_full (priv->permissions, g_free); + priv->permissions = NULL; + if (priv->requests) { g_hash_table_destroy (priv->requests); - - proxy_cleanup (NM_SECRET_AGENT (object)); + priv->requests = NULL; } + proxy_cleanup (NM_SECRET_AGENT (object)); + g_clear_object (&priv->subject); + G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object); } diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h index a59182e533..b75f7177db 100644 --- a/src/settings/nm-secret-agent.h +++ b/src/settings/nm-secret-agent.h @@ -29,6 +29,7 @@ #include <nm-connection.h> #include "nm-dbus-manager.h" #include "nm-settings-flags.h" +#include "nm-auth-subject.h" /* NOTE: ensure these capabilities match those in introspection/nm-secret-agent.xml and * libnm-glib/nm-secret-agent.h. @@ -56,9 +57,8 @@ typedef struct { GType nm_secret_agent_get_type (void); NMSecretAgent *nm_secret_agent_new (DBusGMethodInvocation *context, - const char *owner, + NMAuthSubject *subject, const char *identifier, - uid_t owner_uid, NMSecretAgentCapabilities capabilities); const char *nm_secret_agent_get_description (NMSecretAgent *agent); @@ -67,7 +67,7 @@ const char *nm_secret_agent_get_dbus_owner (NMSecretAgent *agent); const char *nm_secret_agent_get_identifier (NMSecretAgent *agent); -uid_t nm_secret_agent_get_owner_uid (NMSecretAgent *agent); +gulong nm_secret_agent_get_owner_uid (NMSecretAgent *agent); const char *nm_secret_agent_get_owner_username (NMSecretAgent *agent); @@ -75,6 +75,8 @@ NMSecretAgentCapabilities nm_secret_agent_get_capabilities (NMSecretAgent *agent guint32 nm_secret_agent_get_hash (NMSecretAgent *agent); +NMAuthSubject *nm_secret_agent_get_subject (NMSecretAgent *agent); + void nm_secret_agent_add_permission (NMSecretAgent *agent, const char *permission, gboolean allowed); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 0d7fe3a8e1..59b29ad199 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -994,47 +994,35 @@ pk_auth_cb (NMAuthChain *chain, nm_auth_chain_unref (chain); } -static gboolean -check_user_in_acl (NMConnection *connection, - DBusGMethodInvocation *context, - NMSessionMonitor *session_monitor, - gulong *out_sender_uid, - GError **error) +/** + * _new_auth_subject: + * @context: the D-Bus method invocation context + * @error: on failure, a #GError + * + * Creates an NMAuthSubject for the caller. + * + * Returns: the #NMAuthSubject on success, or %NULL on failure and sets @error + */ +static NMAuthSubject * +_new_auth_subject (DBusGMethodInvocation *context, GError **error) { - gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; + NMAuthSubject *subject; - g_return_val_if_fail (connection != NULL, FALSE); - g_return_val_if_fail (context != NULL, FALSE); - g_return_val_if_fail (session_monitor != NULL, FALSE); - - /* Get the caller's UID */ - if (!nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), context, NULL, &sender_uid)) { + subject = nm_auth_subject_new_from_context (context); + if (!subject) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, "Unable to determine UID of request."); - return FALSE; } - /* Make sure the UID can view this connection */ - if (!nm_auth_uid_in_acl (connection, session_monitor, sender_uid, &error_desc)) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - return FALSE; - } - - if (out_sender_uid) - *out_sender_uid = sender_uid; - return TRUE; + return subject; } static void auth_start (NMSettingsConnection *self, DBusGMethodInvocation *context, + NMAuthSubject *subject, const char *check_permission, AuthCallback callback, gpointer callback_data) @@ -1043,41 +1031,49 @@ auth_start (NMSettingsConnection *self, NMAuthChain *chain; gulong sender_uid = G_MAXULONG; GError *error = NULL; - const char *error_desc = NULL; + char *error_desc = NULL; + + g_return_if_fail (context != NULL); + g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); + + /* Ensure the caller can view this connection */ + if (!nm_auth_uid_in_acl (NM_CONNECTION (self), + priv->session_monitor, + nm_auth_subject_get_uid (subject), + &error_desc)) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); - if (!check_user_in_acl (NM_CONNECTION (self), - context, - priv->session_monitor, - &sender_uid, - &error)) { callback (self, context, G_MAXULONG, error, callback_data); g_clear_error (&error); return; } - if (check_permission) { - chain = nm_auth_chain_new (context, pk_auth_cb, self, &error_desc); - if (chain) { - priv->pending_auths = g_slist_append (priv->pending_auths, chain); - - nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL); - nm_auth_chain_set_data_ulong (chain, "sender-uid", sender_uid); - - nm_auth_chain_add_call (chain, check_permission, TRUE); - } else { - g_set_error_literal (&error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - callback (self, context, G_MAXULONG, error, callback_data); - g_clear_error (&error); - } - } else { + if (!check_permission) { /* Don't need polkit auth, automatic success */ - callback (self, context, sender_uid, NULL, callback_data); + callback (self, context, nm_auth_subject_get_uid (subject), NULL, callback_data); + return; + } + + chain = nm_auth_chain_new_subject (subject, context, pk_auth_cb, self); + if (!chain) { + g_set_error_literal (&error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to authenticate the request."); + callback (self, context, G_MAXULONG, error, callback_data); + g_clear_error (&error); + return; } + + priv->pending_auths = g_slist_append (priv->pending_auths, chain); + nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL); + nm_auth_chain_set_data (chain, "callback", callback, NULL); + nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL); + nm_auth_chain_set_data_ulong (chain, "sender-uid", sender_uid); + nm_auth_chain_add_call (chain, check_permission, TRUE); } /**** DBus method handlers ************************************/ @@ -1184,7 +1180,17 @@ static void impl_settings_connection_get_settings (NMSettingsConnection *self, DBusGMethodInvocation *context) { - auth_start (self, context, NULL, get_settings_auth_cb, NULL); + NMAuthSubject *subject; + GError *error = NULL; + + subject = _new_auth_subject (context, &error); + if (subject) { + auth_start (self, context, subject, NULL, get_settings_auth_cb, NULL); + g_object_unref (subject); + } else { + dbus_g_method_return_error (context, error); + g_error_free (error); + } } typedef struct { @@ -1307,10 +1313,12 @@ impl_settings_connection_update_helper (NMSettingsConnection *self, gboolean save_to_disk) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + NMAuthSubject *subject = NULL; NMConnection *tmp = NULL; GError *error = NULL; UpdateInfo *info; const char *permission; + char *error_desc = NULL; g_assert (new_settings != NULL || save_to_disk == TRUE); @@ -1318,36 +1326,35 @@ impl_settings_connection_update_helper (NMSettingsConnection *self, * the problem (ex a system settings plugin that can't write connections out) * instead of over D-Bus. */ - if (!check_writable (NM_CONNECTION (self), &error)) { - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } + if (!check_writable (NM_CONNECTION (self), &error)) + goto error; /* Check if the settings are valid first */ if (new_settings) { tmp = nm_connection_new_from_hash (new_settings, &error); if (!tmp) { g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + goto error; } } + subject = _new_auth_subject (context, &error); + if (!subject) + goto error; + /* And that the new connection settings will be visible to the user * that's sending the update request. You can't make a connection * invisible to yourself. */ - if (!check_user_in_acl (tmp ? tmp : NM_CONNECTION (self), - context, - priv->session_monitor, - NULL, - &error)) { - dbus_g_method_return_error (context, error); - g_clear_error (&error); - g_object_unref (tmp); - return; + if (!nm_auth_uid_in_acl (tmp ? tmp : NM_CONNECTION (self), + priv->session_monitor, + nm_auth_subject_get_uid (subject), + &error_desc)) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); + goto error; } info = g_malloc0 (sizeof (*info)); @@ -1359,7 +1366,16 @@ impl_settings_connection_update_helper (NMSettingsConnection *self, permission = get_update_modify_permission (NM_CONNECTION (self), tmp ? tmp : NM_CONNECTION (self)); - auth_start (self, context, permission, update_auth_cb, info); + auth_start (self, context, subject, permission, update_auth_cb, info); + g_object_unref (subject); + return; + +error: + g_clear_object (&tmp); + g_clear_object (&subject); + + dbus_g_method_return_error (context, error); + g_clear_error (&error); } static void @@ -1440,6 +1456,7 @@ static void impl_settings_connection_delete (NMSettingsConnection *self, DBusGMethodInvocation *context) { + NMAuthSubject *subject; GError *error = NULL; if (!check_writable (NM_CONNECTION (self), &error)) { @@ -1448,7 +1465,14 @@ impl_settings_connection_delete (NMSettingsConnection *self, return; } - auth_start (self, context, get_modify_permission_basic (self), delete_auth_cb, NULL); + subject = _new_auth_subject (context, &error); + if (subject) { + auth_start (self, context, subject, get_modify_permission_basic (self), delete_auth_cb, NULL); + g_object_unref (subject); + } else { + dbus_g_method_return_error (context, error); + g_error_free (error); + } } /**************************************************************/ @@ -1524,11 +1548,22 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self, const gchar *setting_name, DBusGMethodInvocation *context) { - auth_start (self, - context, - get_modify_permission_basic (self), - dbus_secrets_auth_cb, - g_strdup (setting_name)); + NMAuthSubject *subject; + GError *error = NULL; + + subject = _new_auth_subject (context, &error); + if (subject) { + auth_start (self, + context, + subject, + get_modify_permission_basic (self), + dbus_secrets_auth_cb, + g_strdup (setting_name)); + g_object_unref (subject); + } else { + dbus_g_method_return_error (context, error); + g_error_free (error); + } } /**************************************************************/ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 2898888add..b5afbd71ab 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1013,6 +1013,8 @@ pk_add_cb (NMAuthChain *chain, const char *perm; gboolean save_to_disk; + g_assert (context); + priv->auths = g_slist_remove (priv->auths, chain); perm = nm_auth_chain_get_data (chain, "perm"); @@ -1092,13 +1094,15 @@ nm_settings_add_connection_dbus (NMSettings *self, { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMSettingConnection *s_con; + NMAuthSubject *subject = NULL; NMAuthChain *chain; GError *error = NULL, *tmp_error = NULL; - gulong caller_uid = G_MAXULONG; char *error_desc = NULL; - const char *auth_error_desc = NULL; const char *perm; + g_return_if_fail (connection != NULL); + g_return_if_fail (context != NULL); + /* Connection must be valid, of course */ if (!nm_connection_verify (connection, &tmp_error)) { error = g_error_new (NM_SETTINGS_ERROR, @@ -1106,9 +1110,7 @@ nm_settings_add_connection_dbus (NMSettings *self, "The connection was invalid: %s", tmp_error ? tmp_error->message : "(unknown)"); g_error_free (tmp_error); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } /* The kernel doesn't support Ad-Hoc WPA connections well at this time, @@ -1119,9 +1121,7 @@ nm_settings_add_connection_dbus (NMSettings *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "WPA Ad-Hoc disabled due to kernel bugs"); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } /* Do any of the plugins support adding? */ @@ -1129,32 +1129,29 @@ nm_settings_add_connection_dbus (NMSettings *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_NOT_SUPPORTED, "None of the registered plugins support add."); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } - /* Get the caller's UID */ - if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid)) { + subject = nm_auth_subject_new_from_context (context); + if (!subject) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Unable to determine request UID."); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + "Unable to determine UID of request."); + goto done; } /* Ensure the caller's username exists in the connection's permissions, * or that the permissions is empty (ie, visible by everyone). */ - if (!nm_auth_uid_in_acl (connection, priv->session_monitor, caller_uid, &error_desc)) { + if (!nm_auth_uid_in_acl (connection, + priv->session_monitor, + nm_auth_subject_get_uid (subject), + &error_desc)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, error_desc); g_free (error_desc); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } /* If the caller is the only user in the connection's permissions, then @@ -1169,23 +1166,29 @@ nm_settings_add_connection_dbus (NMSettings *self, perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM; /* Validate the user request */ - chain = nm_auth_chain_new (context, pk_add_cb, self, &auth_error_desc); - if (chain) { - priv->auths = g_slist_append (priv->auths, chain); - nm_auth_chain_add_call (chain, perm, TRUE); - nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL); - nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); - nm_auth_chain_set_data_ulong (chain, "caller-uid", caller_uid); - nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL); - } else { + chain = nm_auth_chain_new_subject (subject, context, pk_add_cb, self); + if (!chain) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, - auth_error_desc); - callback (self, NULL, error, context, user_data); - g_error_free (error); + "Unable to authenticate the request."); + goto done; } + + priv->auths = g_slist_append (priv->auths, chain); + nm_auth_chain_add_call (chain, perm, TRUE); + nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL); + nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); + nm_auth_chain_set_data (chain, "callback", callback, NULL); + nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); + nm_auth_chain_set_data_ulong (chain, "caller-uid", nm_auth_subject_get_uid (subject)); + nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL); + +done: + if (error) + callback (self, NULL, error, context, user_data); + + g_clear_error (&error); + g_clear_object (&subject); } static void @@ -1251,7 +1254,7 @@ impl_settings_reload_connections (NMSettings *self, gulong caller_uid; GError *error = NULL; - if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid)) { + if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid, NULL)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, "Unable to determine request UID."); @@ -1294,6 +1297,8 @@ pk_hostname_cb (NMAuthChain *chain, GSList *iter; const char *hostname; + g_assert (context); + priv->auths = g_slist_remove (priv->auths, chain); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); @@ -1344,30 +1349,31 @@ impl_settings_save_hostname (NMSettings *self, NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthChain *chain; GError *error = NULL; - const char *error_desc = NULL; /* Do any of the plugins support setting the hostname? */ if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, "None of the registered plugins support setting the hostname."); - } else { - chain = nm_auth_chain_new (context, pk_hostname_cb, self, &error_desc); - if (chain) { - priv->auths = g_slist_append (priv->auths, chain); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); - nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free); - } else { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - } + goto done; } - if (error) { - dbus_g_method_return_error (context, error); - g_error_free (error); + chain = nm_auth_chain_new_context (context, pk_hostname_cb, self); + if (!chain) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to authenticate the request."); + goto done; } + + priv->auths = g_slist_append (priv->auths, chain); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); + nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free); + +done: + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&error); } static gboolean diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 9f52a2a1fa..6962e301d0 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -71,9 +71,6 @@ typedef struct { SecretsReq secrets_idx; char *username; - NMDevice *parent_dev; - gulong device_monitor; - NMVPNConnectionState vpn_state; NMVPNConnectionStateReason failure_reason; DBusGProxy *proxy; @@ -163,7 +160,7 @@ call_plugin_disconnect (NMVPNConnection *self) } static void -vpn_cleanup (NMVPNConnection *connection) +vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); @@ -173,8 +170,8 @@ vpn_cleanup (NMVPNConnection *connection) nm_platform_address_flush (priv->ip_ifindex); } - nm_device_set_vpn4_config (priv->parent_dev, NULL); - nm_device_set_vpn6_config (priv->parent_dev, NULL); + nm_device_set_vpn4_config (parent_dev, NULL); + nm_device_set_vpn6_config (parent_dev, NULL); g_free (priv->banner); priv->banner = NULL; @@ -197,6 +194,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, { NMVPNConnectionPrivate *priv; NMVPNConnectionState old_vpn_state; + NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection)); g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); @@ -242,7 +240,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, /* Let dispatcher scripts know we're up and running */ nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_UP, priv->connection, - priv->parent_dev, + parent_dev, priv->ip_iface, priv->ip4_config, priv->ip6_config, @@ -255,7 +253,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, /* Let dispatcher scripts know we're about to go down */ nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN, priv->connection, - priv->parent_dev, + parent_dev, priv->ip_iface, NULL, NULL, @@ -265,7 +263,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, /* Tear down and clean up the connection */ call_plugin_disconnect (connection); - vpn_cleanup (connection); + vpn_cleanup (connection, parent_dev); /* Fall through */ default: priv->secrets_idx = SECRETS_REQ_SYSTEM; @@ -276,26 +274,34 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, } static void -device_state_changed (NMDevice *device, +device_state_changed (NMActiveConnection *active, + NMDevice *device, NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) + NMDeviceState old_state) { - NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); - if (new_state <= NM_DEVICE_STATE_DISCONNECTED) { - nm_vpn_connection_set_vpn_state (connection, + nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active), NM_VPN_CONNECTION_STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } else if (new_state == NM_DEVICE_STATE_FAILED) { - nm_vpn_connection_set_vpn_state (connection, + nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active), NM_VPN_CONNECTION_STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } } static void +master_failed (NMActiveConnection *self) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (self); + + /* Master failure fails the VPN */ + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); +} + +static void add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw) { NMIP4Config *parent_config; @@ -400,26 +406,18 @@ NMVPNConnection * nm_vpn_connection_new (NMConnection *connection, NMDevice *parent_device, const char *specific_object, - gboolean user_requested, - gulong user_uid) + NMAuthSubject *subject) { - NMVPNConnection *self; - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); - self = (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, + return (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, NM_ACTIVE_CONNECTION_INT_CONNECTION, connection, NM_ACTIVE_CONNECTION_INT_DEVICE, parent_device, NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object, - NM_ACTIVE_CONNECTION_INT_USER_REQUESTED, user_requested, - NM_ACTIVE_CONNECTION_INT_USER_UID, user_uid, + NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, NM_ACTIVE_CONNECTION_VPN, TRUE, NULL); - if (self) - nm_active_connection_export (NM_ACTIVE_CONNECTION (self)); - - return self; } static const char * @@ -652,6 +650,7 @@ static gboolean nm_vpn_connection_apply_config (NMVPNConnection *connection) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection)); nm_platform_link_set_up (priv->ip_ifindex); @@ -668,9 +667,9 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection) /* Add any explicit route to the VPN gateway through the parent device */ if (priv->ip4_external_gw) - add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_external_gw); + add_ip4_vpn_gateway_route (parent_dev, priv->ip4_external_gw); if (priv->ip6_external_gw) - add_ip6_vpn_gateway_route (priv->parent_dev, priv->ip6_external_gw); + add_ip6_vpn_gateway_route (parent_dev, priv->ip6_external_gw); nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", nm_connection_get_id (priv->connection)); @@ -1397,14 +1396,6 @@ nm_vpn_connection_get_ip_ifindex (NMVPNConnection *connection) return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip_ifindex; } -NMDevice * -nm_vpn_connection_get_parent_device (NMVPNConnection *connection) -{ - g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); - - return NM_VPN_CONNECTION_GET_PRIVATE (connection)->parent_dev; -} - guint32 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection) { @@ -1674,23 +1665,12 @@ nm_vpn_connection_init (NMVPNConnection *self) static void constructed (GObject *object) { - NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); NMConnection *connection; - NMDevice *device; G_OBJECT_CLASS (nm_vpn_connection_parent_class)->constructed (object); connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (object)); - priv->connection = g_object_ref (connection); - - device = (NMDevice *) nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object)); - g_assert (device); - - priv->parent_dev = g_object_ref (device); - - priv->device_monitor = g_signal_connect (device, "state-changed", - G_CALLBACK (device_state_changed), - object); + NM_VPN_CONNECTION_GET_PRIVATE (object)->connection = g_object_ref (connection); } static void @@ -1712,11 +1692,6 @@ dispose (GObject *object) if (priv->ip6_external_gw) g_free (priv->ip6_external_gw); - if (priv->device_monitor) - g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor); - - g_clear_object (&priv->parent_dev); - if (priv->ip4_config) g_object_unref (priv->ip4_config); if (priv->ip6_config) @@ -1755,6 +1730,7 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); + NMDevice *parent_dev; switch (prop_id) { case PROP_VPN_STATE: @@ -1764,7 +1740,8 @@ get_property (GObject *object, guint prop_id, g_value_set_string (value, priv->banner ? priv->banner : ""); break; case PROP_MASTER: - g_value_set_boxed (value, nm_device_get_path (priv->parent_dev)); + parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object)); + g_value_set_boxed (value, parent_dev ? nm_device_get_path (parent_dev) : "/"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1776,6 +1753,7 @@ static void nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) { GObjectClass *object_class = G_OBJECT_CLASS (connection_class); + NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS (connection_class); g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate)); @@ -1784,6 +1762,8 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) object_class->constructed = constructed; object_class->dispose = dispose; object_class->finalize = finalize; + active_class->master_failed = master_failed; + active_class->device_state_changed = device_state_changed; g_object_class_override_property (object_class, PROP_MASTER, NM_ACTIVE_CONNECTION_MASTER); diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index bf5433456d..b98dcbe629 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -26,6 +26,7 @@ #include <glib-object.h> #include "NetworkManagerVPN.h" #include "nm-device.h" +#include "nm-auth-subject.h" #define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type ()) #define NM_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnection)) @@ -67,8 +68,7 @@ GType nm_vpn_connection_get_type (void); NMVPNConnection * nm_vpn_connection_new (NMConnection *connection, NMDevice *parent_device, const char *specific_object, - gboolean user_requested, - gulong user_uid); + NMAuthSubject *subject); void nm_vpn_connection_activate (NMVPNConnection *connection); NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection); @@ -82,7 +82,6 @@ NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connect NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection); const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection); int nm_vpn_connection_get_ip_ifindex (NMVPNConnection *connection); -NMDevice * nm_vpn_connection_get_parent_device (NMVPNConnection *connection); guint32 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection); struct in6_addr * nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection); diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 9cd140e54e..6c63edc1e7 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -78,7 +78,7 @@ get_service_by_namefile (NMVPNManager *self, const char *namefile) } static NMVPNConnection * -find_active_vpn_connection_by_connection (NMVPNManager *self, NMConnection *connection) +find_active_vpn_connection (NMVPNManager *self, NMConnection *connection) { NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); GHashTableIter iter; @@ -104,66 +104,53 @@ find_active_vpn_connection_by_connection (NMVPNManager *self, NMConnection *conn return found; } -NMActiveConnection * +gboolean nm_vpn_manager_activate_connection (NMVPNManager *manager, - NMConnection *connection, - NMDevice *device, - const char *specific_object, - gboolean user_requested, - gulong user_uid, + NMVPNConnection *vpn, GError **error) { - NMSettingVPN *vpn_setting; + NMVPNConnection *existing = NULL; + NMConnection *connection; + NMSettingVPN *s_vpn; NMVPNService *service; - NMVPNConnection *vpn = NULL; const char *service_name; + NMDevice *device; - g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - g_return_val_if_fail (error != NULL, NULL); - g_return_val_if_fail (*error == NULL, NULL); + g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE); + g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE); + g_return_val_if_fail (error != NULL, FALSE); + g_return_val_if_fail (*error == NULL, FALSE); + device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn)); + g_assert (device); if ( nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED && nm_device_get_state (device) != NM_DEVICE_STATE_SECONDARIES) { - g_set_error (error, - NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, - "%s", "The base device for the VPN connection was not active."); - return NULL; + g_set_error_literal (error, NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, + "The base device for the VPN connection was not active."); + return FALSE; } - vpn_setting = nm_connection_get_setting_vpn (connection); - if (!vpn_setting) { - g_set_error (error, - NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, - "%s", "The connection was not a VPN connection."); - return NULL; - } + connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn)); + g_assert (connection); + s_vpn = nm_connection_get_setting_vpn (connection); + g_assert (s_vpn); - vpn = find_active_vpn_connection_by_connection (manager, connection); - if (vpn) { - nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); - vpn = NULL; - } - - service_name = nm_setting_vpn_get_service_type (vpn_setting); + service_name = nm_setting_vpn_get_service_type (s_vpn); g_assert (service_name); service = g_hash_table_lookup (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name); if (!service) { - g_set_error (error, - NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID, + g_set_error (error, NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID, "The VPN service '%s' was not installed.", service_name); - return NULL; + return FALSE; } - return (NMActiveConnection *) nm_vpn_service_activate (service, - connection, - device, - specific_object, - user_requested, - user_uid, - error); + existing = find_active_vpn_connection (manager, + nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn))); + if (existing) + nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + + return nm_vpn_service_activate (service, vpn, error); } gboolean diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h index 1d398ea2cb..57f947b0e4 100644 --- a/src/vpn-manager/nm-vpn-manager.h +++ b/src/vpn-manager/nm-vpn-manager.h @@ -59,13 +59,9 @@ GType nm_vpn_manager_get_type (void); NMVPNManager *nm_vpn_manager_get (void); -NMActiveConnection *nm_vpn_manager_activate_connection (NMVPNManager *manager, - NMConnection *connection, - NMDevice *device, - const char *specific_object, - gboolean user_requested, - gulong user_uid, - GError **error); +gboolean nm_vpn_manager_activate_connection (NMVPNManager *manager, + NMVPNConnection *vpn, + GError **error); gboolean nm_vpn_manager_deactivate_connection (NMVPNManager *manager, NMVPNConnection *connection, diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index a34c782e73..8ecd9a1176 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -323,45 +323,37 @@ connection_vpn_state_changed (NMVPNConnection *connection, } } -NMVPNConnection * +gboolean nm_vpn_service_activate (NMVPNService *service, - NMConnection *connection, - NMDevice *device, - const char *specific_object, - gboolean user_requested, - gulong user_uid, + NMVPNConnection *vpn, GError **error) { - NMVPNConnection *vpn; NMVPNServicePrivate *priv; - g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - g_return_val_if_fail (error != NULL, NULL); - g_return_val_if_fail (*error == NULL, NULL); + g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); + g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE); + g_return_val_if_fail (error != NULL, FALSE); + g_return_val_if_fail (*error == NULL, FALSE); priv = NM_VPN_SERVICE_GET_PRIVATE (service); clear_quit_timeout (service); - vpn = nm_vpn_connection_new (connection, device, specific_object, user_requested, user_uid); g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, G_CALLBACK (connection_vpn_state_changed), service); priv->connections = g_slist_prepend (priv->connections, g_object_ref (vpn)); - if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) { - // FIXME: fill in error when errors happen + if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) nm_vpn_connection_activate (vpn); - } else if (priv->start_timeout == 0) { + else if (priv->start_timeout == 0) { nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name); if (!nm_vpn_service_daemon_exec (service, error)) - vpn = NULL; + return FALSE; } - return vpn; + return TRUE; } const GSList * diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h index e883d55f8b..2ad79a0d6b 100644 --- a/src/vpn-manager/nm-vpn-service.h +++ b/src/vpn-manager/nm-vpn-service.h @@ -54,13 +54,9 @@ const char *nm_vpn_service_get_dbus_service (NMVPNService *service); /* Returns the path of the VPN service's .name file */ const char *nm_vpn_service_get_name_file (NMVPNService *service); -NMVPNConnection * nm_vpn_service_activate (NMVPNService *service, - NMConnection *connection, - NMDevice *device, - const char *specific_object, - gboolean user_requested, - gulong user_uid, - GError **error); +gboolean nm_vpn_service_activate (NMVPNService *service, + NMVPNConnection *vpn, + GError **error); const GSList *nm_vpn_service_get_active_connections (NMVPNService *service); |