Commit b635b4d4 authored by Thomas Haller's avatar Thomas Haller

core: improve and fix keeping connection active based on "connection.permissions"

By setting "connection.permissions", a profile is restricted to a
particular user.
That means for example, that another user cannot see, modify, delete,
activate or deactivate the profile. It also means, that the profile
will only autoconnect when the user is logged in (has a session).

Note that root is always able to activate the profile. Likewise, the
user is also allowed to manually activate the own profile, even if no
session currently exists (which can easily happen with `sudo`).

When the user logs out (the session goes away), we want do disconnect
the profile, however there are conflicting goals here:

1) if the profile was activate by root user, then logging out the user
   should not disconnect the profile. The patch fixes that by not
   binding the activation to the connection, if the activation is done
   by the root user.

2) if the profile was activated by the owner when it had no session,
   then it should stay alive until the user logs in (once) and logs
   out again. This is already handled by the previous commit.

   Yes, this point is odd. If you first do

      $ sudo -u $OTHER_USER nmcli connection up $PROFILE

   the profile activates despite not having a session. If you then

      $ ssh guest@localhost nmcli device

   you'll still see the profile active. However, the moment the SSH session
   ends, a session closes and the profile disconnects. It's unclear, how to
   solve that any better. I think, a user who cares about this, should not
   activate the profile without having a session in the first place.

There are quite some special cases, in particular with internal
activations. In those cases we need to decide whether to bind the
activation to the profile's visibility.

Also, expose the "bind" setting in the D-Bus API. Note, that in the future
this flag may be modified via D-Bus API. Like we may also add related API
that allows to tweak the lifetime of the activation.

Also, I think we broke handling of connection visiblity with 37e8c53e
"core: Introduce helper class to track connection keep alive". This
should be fixed now too, with improved behavior.

Fixes: 37e8c53e

https://bugzilla.redhat.com/show_bug.cgi?id=1530977
parent b4d33348
......@@ -960,20 +960,25 @@ typedef enum { /*< flags >*/
* @NM_ACTIVATION_STATE_FLAG_IP6_READY: IPv6 setting is completed.
* @NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES: The master has any slave devices attached.
* This only makes sense if the device is a master.
* @NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY: the lifetime
* of the activation is bound to the visilibity of the connection profile,
* which in turn depends on "connection.permissions" and whether a session
* for the user exists. Since: 1.16
*
* Flags describing the current activation state.
*
* Since: 1.10
**/
typedef enum { /*< flags >*/
NM_ACTIVATION_STATE_FLAG_NONE = 0,
NM_ACTIVATION_STATE_FLAG_NONE = 0,
NM_ACTIVATION_STATE_FLAG_IS_MASTER = (1LL << 0),
NM_ACTIVATION_STATE_FLAG_IS_SLAVE = (1LL << 1),
NM_ACTIVATION_STATE_FLAG_LAYER2_READY = (1LL << 2),
NM_ACTIVATION_STATE_FLAG_IP4_READY = (1LL << 3),
NM_ACTIVATION_STATE_FLAG_IP6_READY = (1LL << 4),
NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = (1LL << 5),
NM_ACTIVATION_STATE_FLAG_IS_MASTER = (1LL << 0),
NM_ACTIVATION_STATE_FLAG_IS_SLAVE = (1LL << 1),
NM_ACTIVATION_STATE_FLAG_LAYER2_READY = (1LL << 2),
NM_ACTIVATION_STATE_FLAG_IP4_READY = (1LL << 3),
NM_ACTIVATION_STATE_FLAG_IP6_READY = (1LL << 4),
NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = (1LL << 5),
NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY = (1LL << 6),
} NMActivationStateFlags;
/**
......
......@@ -2384,10 +2384,27 @@ nm_device_get_act_request (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->act_request.obj;
}
NMActivationStateFlags
nm_device_get_activation_state_flags (NMDevice *self)
{
NMActRequest *ac;
g_return_val_if_fail (NM_IS_DEVICE (self), NM_ACTIVATION_STATE_FLAG_NONE);
ac = NM_DEVICE_GET_PRIVATE (self)->act_request.obj;
if (!ac)
return NM_ACTIVATION_STATE_FLAG_NONE;
return nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (ac));
}
NMSettingsConnection *
nm_device_get_settings_connection (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
priv = NM_DEVICE_GET_PRIVATE (self);
return priv->act_request.obj ? nm_act_request_get_settings_connection (priv->act_request.obj) : NULL;
}
......
......@@ -543,6 +543,7 @@ NMConnection * nm_device_get_settings_connection_get_connection (NMDevice *self
NMConnection * nm_device_get_applied_connection (NMDevice *dev);
gboolean nm_device_has_unmodified_applied_connection (NMDevice *self,
NMSettingCompareFlags compare_flags);
NMActivationStateFlags nm_device_get_activation_state_flags (NMDevice *self);
gpointer /* (NMSetting *) */ nm_device_get_applied_setting (NMDevice *dev,
GType setting_type);
......
......@@ -539,6 +539,7 @@ nm_act_request_init (NMActRequest *req)
* @subject: the #NMAuthSubject representing the requestor of the activation
* @activation_type: the #NMActivationType
* @activation_reason: the reason for activation
* @initial_state_flags: the initial state flags.
* @device: the device/interface to configure according to @connection
*
* Creates a new device-based activation request. If an applied connection is
......@@ -553,6 +554,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection,
NMAuthSubject *subject,
NMActivationType activation_type,
NMActivationReason activation_reason,
NMActivationStateFlags initial_state_flags,
NMDevice *device)
{
g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
......@@ -567,6 +569,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection,
NM_ACTIVE_CONNECTION_INT_SUBJECT, subject,
NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE, (int) activation_type,
NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, (int) activation_reason,
NM_ACTIVE_CONNECTION_STATE_FLAGS, (guint) initial_state_flags,
NULL);
}
......
......@@ -42,6 +42,7 @@ NMActRequest *nm_act_request_new (NMSettingsConnection *settings_connec
NMAuthSubject *subject,
NMActivationType activation_type,
NMActivationReason activation_reason,
NMActivationStateFlags initial_state_flags,
NMDevice *device);
NMSettingsConnection *nm_act_request_get_settings_connection (NMActRequest *req);
......
......@@ -163,14 +163,18 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, NMActiveConnectionState,
);
#define state_to_string(state) NM_UTILS_LOOKUP_STR (_state_to_string, state)
/* the maximum required buffer size for _state_flags_to_string(). */
#define _NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE (255)
NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags,
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"),
NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, "lifetime-bound-to-profile-visibility"),
);
/*****************************************************************************/
......@@ -361,14 +365,20 @@ nm_active_connection_set_state_flags_full (NMActiveConnection *self,
f = (priv->state_flags & ~mask) | (state_flags & mask);
if (f != priv->state_flags) {
char buf1[G_N_ELEMENTS (_nm_utils_to_string_buffer)];
char buf2[G_N_ELEMENTS (_nm_utils_to_string_buffer)];
char buf1[_NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE];
char buf2[_NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE];
_LOGD ("set state-flags %s (was %s)",
_state_flags_to_string (f, buf1, sizeof (buf1)),
_state_flags_to_string (priv->state_flags, buf2, sizeof (buf2)));
priv->state_flags = f;
_notify (self, PROP_STATE_FLAGS);
nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive,
NM_FLAGS_HAS (priv->state_flags,
NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY)
? priv->settings_connection.obj
: NULL);
}
}
......@@ -1384,6 +1394,12 @@ set_property (GObject *object, guint prop_id,
g_return_if_reached ();
_set_activation_type (self, (NMActivationType) i);
break;
case PROP_STATE_FLAGS:
/* construct-only */
priv->state_flags = g_value_get_uint (value);
nm_assert ((guint) priv->state_flags == g_value_get_uint (value));
nm_assert (!NM_FLAGS_ANY (priv->state_flags, ~NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY));
break;
case PROP_INT_ACTIVATION_REASON:
/* construct-only */
i = g_value_get_int (value);
......@@ -1467,13 +1483,12 @@ constructed (GObject *object)
g_steal_pointer (&priv->applied_connection));
}
if (NM_FLAGS_HAS (priv->state_flags,
NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY))
nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, priv->settings_connection.obj);
g_return_if_fail (priv->subject);
g_return_if_fail (priv->activation_reason != NM_ACTIVATION_REASON_UNSET);
if (NM_IN_SET ((NMActivationReason) priv->activation_reason,
NM_ACTIVATION_REASON_AUTOCONNECT,
NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES))
nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, priv->settings_connection.obj);
}
static void
......@@ -1625,7 +1640,7 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
obj_properties[PROP_STATE_FLAGS] =
g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE_FLAGS, "", "",
0, G_MAXUINT32, NM_ACTIVATION_STATE_FLAG_NONE,
G_PARAM_READABLE |
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_DEFAULT] =
......
......@@ -163,6 +163,13 @@ nm_active_connection_set_state_flags (NMActiveConnection *self,
nm_active_connection_set_state_flags_full (self, state_flags, state_flags);
}
static inline void
nm_active_connection_set_state_flags_clear (NMActiveConnection *self,
NMActivationStateFlags state_flags)
{
nm_active_connection_set_state_flags_full (self, NM_ACTIVATION_STATE_FLAG_NONE, state_flags);
}
NMDevice * nm_active_connection_get_device (NMActiveConnection *self);
gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device);
......
......@@ -46,6 +46,7 @@ typedef struct {
guint64 ac_version_id;
NMDeviceState state;
bool realized:1;
bool activation_lifetime_bound_to_profile_visiblity:1;
NMUnmanFlagOp unmanaged_explicit;
NMActivationReason activation_reason;
} DeviceCheckpoint;
......@@ -335,6 +336,9 @@ activate:
subject,
NM_ACTIVATION_TYPE_MANAGED,
dev_checkpoint->activation_reason,
dev_checkpoint->activation_lifetime_bound_to_profile_visiblity
? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY
: NM_ACTIVATION_STATE_FLAG_NONE,
&local_error)) {
_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
nm_settings_connection_get_id (connection),
......@@ -439,6 +443,8 @@ device_checkpoint_create (NMDevice *device)
dev_checkpoint->settings_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (settings_connection));
dev_checkpoint->ac_version_id = nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
dev_checkpoint->activation_reason = nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (act_request));
dev_checkpoint->activation_lifetime_bound_to_profile_visiblity = NM_FLAGS_HAS (nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (act_request)),
NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY);
}
return dev_checkpoint;
......
This diff is collapsed.
......@@ -175,6 +175,7 @@ NMActiveConnection *nm_manager_activate_connection (NMManager *manager,
NMAuthSubject *subject,
NMActivationType activation_type,
NMActivationReason activation_reason,
NMActivationStateFlags initial_state_flags,
GError **error);
gboolean nm_manager_deactivate_connection (NMManager *manager,
......
......@@ -1284,6 +1284,7 @@ auto_activate_device (NMPolicy *self,
subject,
NM_ACTIVATION_TYPE_MANAGED,
NM_ACTIVATION_REASON_AUTOCONNECT,
NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY,
&error);
if (!ac) {
_LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: %s",
......@@ -1674,9 +1675,14 @@ activate_secondary_connections (NMPolicy *self,
GError *error = NULL;
guint32 i;
gboolean success = TRUE;
NMActivationStateFlags initial_state_flags;
s_con = nm_connection_get_setting_connection (connection);
nm_assert (s_con);
nm_assert (NM_IS_SETTING_CONNECTION (s_con));
/* we propagate the activation's state flags. */
initial_state_flags = nm_device_get_activation_state_flags (device)
& NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY;
for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) {
NMSettingsConnection *sett_conn;
......@@ -1700,7 +1706,6 @@ activate_secondary_connections (NMPolicy *self,
}
req = nm_device_get_act_request (device);
g_assert (req);
_LOGD (LOGD_DEVICE, "activating secondary connection '%s (%s)' for base connection '%s (%s)'",
nm_settings_connection_get_id (sett_conn), sec_uuid,
......@@ -1713,6 +1718,7 @@ activate_secondary_connections (NMPolicy *self,
nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)),
NM_ACTIVATION_TYPE_MANAGED,
nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (req)),
initial_state_flags,
&error);
if (ac)
secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac));
......@@ -2162,6 +2168,8 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self)
nm_active_connection_get_subject (ac),
NM_ACTIVATION_TYPE_MANAGED,
nm_active_connection_get_activation_reason (ac),
( nm_active_connection_get_state_flags (ac)
& NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY),
&error)) {
_LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s",
nm_settings_connection_get_id (connection),
......
......@@ -851,6 +851,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection,
NMDevice *parent_device,
const char *specific_object,
NMActivationReason activation_reason,
NMActivationStateFlags initial_state_flags,
NMAuthSubject *subject)
{
g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
......@@ -864,6 +865,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection,
NM_ACTIVE_CONNECTION_INT_SUBJECT, subject,
NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, activation_reason,
NM_ACTIVE_CONNECTION_VPN, TRUE,
NM_ACTIVE_CONNECTION_STATE_FLAGS, (guint) initial_state_flags,
NULL);
}
......
......@@ -53,6 +53,7 @@ NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connecti
NMDevice *parent_device,
const char *specific_object,
NMActivationReason activation_reason,
NMActivationStateFlags initial_state_flags,
NMAuthSubject *subject);
void nm_vpn_connection_activate (NMVpnConnection *self,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment