mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-09 15:30:27 +01:00
core: merge branch 'th/stable-id-bgo776904'
https://bugzilla.gnome.org/show_bug.cgi?id=776904
This commit is contained in:
commit
4b9176eb7d
18 changed files with 525 additions and 53 deletions
|
|
@ -137,6 +137,8 @@ EXTRA_DIST += \
|
|||
examples/lua/lgi/change-vpn-username.lua \
|
||||
examples/lua/lgi/deactivate-all.lua \
|
||||
\
|
||||
examples/nm-conf.d/30-anon.conf \
|
||||
\
|
||||
examples/python/dbus/nm-state.py \
|
||||
examples/python/dbus/add-connection.py \
|
||||
examples/python/dbus/add-connection-compat.py \
|
||||
|
|
|
|||
55
examples/nm-conf.d/30-anon.conf
Normal file
55
examples/nm-conf.d/30-anon.conf
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Example configuration snippet for NetworkManager to
|
||||
# overwrite some default value for more privacy.
|
||||
# Put it for example to /etc/NetworkManager/conf.d/30-anon.conf
|
||||
#
|
||||
# See man NetworkManager.conf(5) for how default values
|
||||
# work. See man nm-settings(5) for the connection properties.
|
||||
#
|
||||
#
|
||||
# This enables privacy setting by default. The defaults
|
||||
# apply only to settings that do not explicitly configure
|
||||
# a per-connection override.
|
||||
# That means, if the connection profile has
|
||||
#
|
||||
# $ nmcli connection show "$CON_NAME" |
|
||||
# grep '^\(connection.stable-id\|ipv6.addr-gen-mode\|ipv6.ip6-privacy\|802-11-wireless.cloned-mac-address\|802-11-wireless.mac-address-randomization\|802-3-ethernet.cloned-mac-address\)'
|
||||
# connection.stable-id: --
|
||||
# 802-3-ethernet.cloned-mac-address: --
|
||||
# 802-11-wireless.cloned-mac-address: --
|
||||
# 802-11-wireless.mac-address-randomization:default
|
||||
# ipv6.ip6-privacy: -1 (unknown)
|
||||
# ipv6.addr-gen-mode: stable-privacy
|
||||
#
|
||||
# then the default values are inherited and thus both the MAC
|
||||
# address and the IPv6 host identifier are randomized.
|
||||
# Also, ipv6 private addresses (RFC4941) are used in
|
||||
# addition.
|
||||
#
|
||||
#
|
||||
# For some profiles it can make sense to reuse the same stable-id
|
||||
# (and thus MAC address and IPv6 host identifier) for the duration
|
||||
# of the current boot, but still exclusive to the connection profile.
|
||||
# Thus, explicitly set the stable-id like:
|
||||
#
|
||||
# $ nmcli connection modify "$CON_NAME" connection.stable-id '${CONNECTION}/${BOOT}'
|
||||
#
|
||||
# ... or keep it stable accross reboots, still distinct per profile:
|
||||
#
|
||||
# $ nmcli connection modify "$CON_NAME" connection.stable-id '${CONNECTION}'
|
||||
#
|
||||
# ... or use the same stable IDs for a bunch of profiles
|
||||
#
|
||||
# $ nmcli connection modify "$CON_NAME" connection.stable-id 'my-home-wifi yada yada'
|
||||
#
|
||||
# ... or use the same IDs for a bunch of profiles, but only for the current boot
|
||||
#
|
||||
# $ nmcli connection modify "$CON_NAME" connection.stable-id 'my-home-wifi yada yada/${BOOT}'
|
||||
|
||||
[device-anon]
|
||||
wifi.scan-rand-mac-address=yes
|
||||
|
||||
[connection-anon]
|
||||
connection.stable-id=${RANDOM}
|
||||
ethernet.cloned-mac-address=stable
|
||||
wifi.cloned-mac-address=stable
|
||||
ipv6.ip6-privacy=2
|
||||
|
|
@ -1376,11 +1376,6 @@ static void
|
|||
set_default_for_missing_key (NMSetting *setting, const char *property)
|
||||
{
|
||||
/* Set a value different from the default value of the property's spec */
|
||||
|
||||
if (NM_IS_SETTING_WIRELESS (setting)) {
|
||||
if (!strcmp (property, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION))
|
||||
g_object_set (setting, property, (NMSettingMacRandomization) NM_SETTING_MAC_RANDOMIZATION_NEVER, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -638,9 +638,6 @@ can_omit_default_value (NMSetting *setting, const char *property)
|
|||
} else if (NM_IS_SETTING_IP6_CONFIG (setting)) {
|
||||
if (!strcmp (property, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE))
|
||||
return FALSE;
|
||||
} else if (NM_IS_SETTING_WIRELESS (setting)) {
|
||||
if (!strcmp (property, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
|
|
|||
|
|
@ -1432,13 +1432,32 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
|
|||
/**
|
||||
* NMSettingConnection:stable-id:
|
||||
*
|
||||
* This token to generate stable IDs for the connection. If unset,
|
||||
* the UUID will be used instead.
|
||||
* Token to generate stable IDs for the connection.
|
||||
*
|
||||
* The stable-id is used instead of the connection UUID for generating
|
||||
* IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy.
|
||||
* It is also used to seed the generated cloned MAC address for
|
||||
* ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable.
|
||||
* The stable-id is used for generating IPv6 stable private addresses
|
||||
* with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the
|
||||
* generated cloned MAC address for ethernet.cloned-mac-address=stable
|
||||
* and wifi.cloned-mac-address=stable. Note that also the interface name
|
||||
* of the activating connection and a per-host secret key is included
|
||||
* into the address generation so that the same stable-id on different
|
||||
* hosts/devices yields different addresses.
|
||||
*
|
||||
* If the value is unset, an ID unique for the connection is used.
|
||||
* Specifing a stable-id allows multiple connections to generate the
|
||||
* same addresses. Another use is to generate IDs at runtime via
|
||||
* dynamic substitutions.
|
||||
*
|
||||
* The '$' character is treated special to perform dynamic substitutions
|
||||
* at runtime. Currently supported are "${CONNECTION}", "${BOOT}", "${RANDOM}".
|
||||
* These effectively create unique IDs per-connection, per-boot, or every time.
|
||||
* Any unrecognized patterns following '$' are treated verbatim, however
|
||||
* are reserved for future use. You are thus advised to avoid '$' or
|
||||
* escape it as "$$".
|
||||
* For example, set it to "${CONNECTION}/${BOOT}" to create a unique id for
|
||||
* this connection that changes with every reboot.
|
||||
*
|
||||
* Note that two connections only use the same effective id if
|
||||
* their stable-id is also identical before performing dynamic substitutions.
|
||||
*
|
||||
* Since: 1.4
|
||||
**/
|
||||
|
|
|
|||
|
|
@ -717,8 +717,8 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
|
|||
* when the interface hardware is replaced.
|
||||
*
|
||||
* The value of "stable-privacy" enables use of cryptographically
|
||||
* secure hash of a secret host-specific key along with the connection
|
||||
* identification and the network address as specified by RFC7217.
|
||||
* secure hash of a secret host-specific key along with the connection's
|
||||
* stable-id and the network address as specified by RFC7217.
|
||||
* This makes it impossible to use the address track host's presence,
|
||||
* and makes the address stable when the network interface hardware is
|
||||
* replaced.
|
||||
|
|
|
|||
|
|
@ -1150,8 +1150,8 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
|
|||
* "preserve" means not to touch the MAC address on activation.
|
||||
* "permanent" means to use the permanent hardware address of the device.
|
||||
* "random" creates a random MAC address on each connect.
|
||||
* "stable" creates a hashed MAC address based on connection.stable-id (or
|
||||
* the connection's UUID) and a machine dependent key.
|
||||
* "stable" creates a hashed MAC address based on connection.stable-id and a
|
||||
* machine dependent key.
|
||||
*
|
||||
* If unspecified, the value can be overwritten via global defaults, see manual
|
||||
* of NetworkManager.conf. If still unspecified, it defaults to "preserve"
|
||||
|
|
|
|||
|
|
@ -1348,8 +1348,8 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
|
|||
* "preserve" means not to touch the MAC address on activation.
|
||||
* "permanent" means to use the permanent hardware address of the device.
|
||||
* "random" creates a random MAC address on each connect.
|
||||
* "stable" creates a hashed MAC address based on connection.stable-id (or
|
||||
* the connection's UUID) and a machine dependent key.
|
||||
* "stable" creates a hashed MAC address based on connection.stable-id and a
|
||||
* machine dependent key.
|
||||
*
|
||||
* If unspecified, the value can be overwritten via global defaults, see manual
|
||||
* of NetworkManager.conf. If still unspecified, it defaults to "preserve"
|
||||
|
|
|
|||
|
|
@ -600,6 +600,9 @@ ipv6.ip6-privacy=0
|
|||
<varlistentry>
|
||||
<term><varname>connection.lldp</varname></term>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>connection.stable-id</varname></term>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>ethernet.cloned-mac-address</varname></term>
|
||||
<listitem><para>If left unspecified, it defaults to "preserve".</para></listitem>
|
||||
|
|
|
|||
|
|
@ -356,6 +356,25 @@ nm_strdup_not_empty (const char *str)
|
|||
return str && str[0] ? g_strdup (str) : NULL;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
nm_str_realloc (char *str)
|
||||
{
|
||||
gs_free char *s = str;
|
||||
|
||||
/* Returns a new clone of @str and frees @str. The point is that @str
|
||||
* possibly points to a larger chunck of memory. We want to freshly allocate
|
||||
* a buffer.
|
||||
*
|
||||
* We could use realloc(), but that might not do anything or leave
|
||||
* @str in its memory pool for chunks of a different size (bad for
|
||||
* fragmentation).
|
||||
*
|
||||
* This is only useful when we want to keep the buffer around for a long
|
||||
* time and want to re-allocate a more optimal buffer. */
|
||||
|
||||
return g_strdup (s);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \
|
||||
|
|
|
|||
|
|
@ -261,6 +261,9 @@ typedef struct _NMDevicePrivate {
|
|||
bool firmware_missing:1;
|
||||
bool nm_plugin_missing:1;
|
||||
bool hw_addr_perm_fake:1; /* whether the permanent HW address could not be read and is a fake */
|
||||
|
||||
NMUtilsStableType current_stable_id_type:3;
|
||||
|
||||
GHashTable * available_connections;
|
||||
char * hw_addr;
|
||||
char * hw_addr_perm;
|
||||
|
|
@ -310,6 +313,8 @@ typedef struct _NMDevicePrivate {
|
|||
guint32 dhcp_timeout;
|
||||
char * dhcp_anycast_address;
|
||||
|
||||
char * current_stable_id;
|
||||
|
||||
/* Proxy Configuration */
|
||||
NMProxyConfig *proxy_config;
|
||||
NMPacrunnerManager *pacrunner_manager;
|
||||
|
|
@ -663,25 +668,76 @@ _add_capabilities (NMDevice *self, NMDeviceCapabilities capabilities)
|
|||
/*****************************************************************************/
|
||||
|
||||
static const char *
|
||||
_get_stable_id (NMConnection *connection, NMUtilsStableType *out_stable_type)
|
||||
_get_stable_id (NMDevice *self,
|
||||
NMConnection *connection,
|
||||
NMUtilsStableType *out_stable_type)
|
||||
{
|
||||
NMSettingConnection *s_con;
|
||||
const char *stable_id;
|
||||
NMDevicePrivate *priv;
|
||||
|
||||
nm_assert (NM_IS_DEVICE (self));
|
||||
nm_assert (NM_IS_CONNECTION (connection));
|
||||
nm_assert (out_stable_type);
|
||||
|
||||
s_con = nm_connection_get_setting_connection (connection);
|
||||
g_return_val_if_fail (s_con, NULL);
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
stable_id = nm_setting_connection_get_stable_id (s_con);
|
||||
if (!stable_id) {
|
||||
*out_stable_type = NM_UTILS_STABLE_TYPE_UUID;
|
||||
return nm_connection_get_uuid (connection);
|
||||
/* we cache the generated stable ID for the time of an activation.
|
||||
*
|
||||
* The reason is, that we don't want the stable-id to change as long
|
||||
* as the device is active.
|
||||
*
|
||||
* Especially with ${RANDOM} stable-id we want to generate *one* configuration
|
||||
* for each activation. */
|
||||
if (G_UNLIKELY (!priv->current_stable_id)) {
|
||||
gs_free char *default_id = NULL;
|
||||
gs_free char *generated = NULL;
|
||||
NMUtilsStableType stable_type;
|
||||
NMSettingConnection *s_con;
|
||||
const char *stable_id;
|
||||
const char *uuid;
|
||||
|
||||
s_con = nm_connection_get_setting_connection (connection);
|
||||
|
||||
stable_id = nm_setting_connection_get_stable_id (s_con);
|
||||
|
||||
if (!stable_id) {
|
||||
default_id = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
|
||||
"connection.stable-id",
|
||||
self);
|
||||
stable_id = default_id;
|
||||
}
|
||||
|
||||
uuid = nm_connection_get_uuid (connection);
|
||||
|
||||
stable_type = nm_utils_stable_id_parse (stable_id,
|
||||
uuid,
|
||||
NULL,
|
||||
&generated);
|
||||
|
||||
/* current_stable_id_type is a bitfield! */
|
||||
nm_assert (stable_type <= (NMUtilsStableType) 0x2);
|
||||
nm_assert (stable_type + (NMUtilsStableType) 1 > (NMUtilsStableType) 0);
|
||||
priv->current_stable_id_type = stable_type;
|
||||
|
||||
if (stable_type == NM_UTILS_STABLE_TYPE_UUID)
|
||||
priv->current_stable_id = g_strdup (uuid);
|
||||
else if (stable_type == NM_UTILS_STABLE_TYPE_STABLE_ID)
|
||||
priv->current_stable_id = g_strdup (stable_id);
|
||||
else if (stable_type == NM_UTILS_STABLE_TYPE_GENERATED)
|
||||
priv->current_stable_id = nm_str_realloc (nm_utils_stable_id_generated_complete (generated));
|
||||
else {
|
||||
nm_assert (stable_type == NM_UTILS_STABLE_TYPE_RANDOM);
|
||||
priv->current_stable_id = nm_str_realloc (nm_utils_stable_id_random ());
|
||||
}
|
||||
_LOGT (LOGD_DEVICE,
|
||||
"stable-id: type=%d, \"%s\""
|
||||
"%s%s%s",
|
||||
(int) priv->current_stable_id_type,
|
||||
priv->current_stable_id,
|
||||
NM_PRINT_FMT_QUOTED (stable_type == NM_UTILS_STABLE_TYPE_GENERATED, " from \"", generated, "\"", ""));
|
||||
}
|
||||
|
||||
*out_stable_type = NM_UTILS_STABLE_TYPE_STABLE_ID;
|
||||
return stable_id;
|
||||
*out_stable_type = priv->current_stable_id_type;
|
||||
return priv->current_stable_id;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -6438,7 +6494,7 @@ check_and_add_ipv6ll_addr (NMDevice *self)
|
|||
NMUtilsStableType stable_type;
|
||||
const char *stable_id;
|
||||
|
||||
stable_id = _get_stable_id (connection, &stable_type);
|
||||
stable_id = _get_stable_id (self, connection, &stable_type);
|
||||
if ( !stable_id
|
||||
|| !nm_utils_ipv6_addr_set_stable_privacy (stable_type,
|
||||
&lladdr,
|
||||
|
|
@ -6843,7 +6899,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
|
|||
s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
|
||||
g_assert (s_ip6);
|
||||
|
||||
stable_id = _get_stable_id (connection, &stable_type);
|
||||
stable_id = _get_stable_id (self, connection, &stable_type);
|
||||
if (stable_id) {
|
||||
priv->ndisc = nm_lndp_ndisc_new (NM_PLATFORM_GET,
|
||||
nm_device_get_ip_ifindex (self),
|
||||
|
|
@ -11375,7 +11431,7 @@ nm_device_spawn_iface_helper (NMDevice *self)
|
|||
g_ptr_array_add (argv, g_strdup ("--uuid"));
|
||||
g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
|
||||
|
||||
stable_id = _get_stable_id (connection, &stable_type);
|
||||
stable_id = _get_stable_id (self, connection, &stable_type);
|
||||
if (stable_id && stable_type != NM_UTILS_STABLE_TYPE_UUID) {
|
||||
g_ptr_array_add (argv, g_strdup ("--stable-id"));
|
||||
g_ptr_array_add (argv, g_strdup_printf ("%d %s", (int) stable_type, stable_id));
|
||||
|
|
@ -11670,6 +11726,11 @@ _set_state_full (NMDevice *self,
|
|||
if (state >= NM_DEVICE_STATE_DISCONNECTED && old_state < NM_DEVICE_STATE_DISCONNECTED)
|
||||
nm_device_recheck_available_connections (self);
|
||||
|
||||
if (state <= NM_DEVICE_STATE_DISCONNECTED || state > NM_DEVICE_STATE_DEACTIVATING) {
|
||||
if (nm_clear_g_free (&priv->current_stable_id))
|
||||
_LOGT (LOGD_DEVICE, "stable-id: clear");
|
||||
}
|
||||
|
||||
/* Handle the new state here; but anything that could trigger
|
||||
* another state change should be done below.
|
||||
*/
|
||||
|
|
@ -12544,7 +12605,7 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
stable_id = _get_stable_id (connection, &stable_type);
|
||||
stable_id = _get_stable_id (self, connection, &stable_type);
|
||||
if (stable_id) {
|
||||
hw_addr_generated = nm_utils_hw_addr_gen_stable_eth (stable_type, stable_id,
|
||||
nm_device_get_ip_iface (self),
|
||||
|
|
@ -12933,6 +12994,7 @@ finalize (GObject *object)
|
|||
g_free (priv->type_desc);
|
||||
g_free (priv->type_description);
|
||||
g_free (priv->dhcp_anycast_address);
|
||||
g_free (priv->current_stable_id);
|
||||
|
||||
g_hash_table_unref (priv->ip6_saved_properties);
|
||||
g_hash_table_unref (priv->available_connections);
|
||||
|
|
|
|||
|
|
@ -374,6 +374,8 @@ nm_fake_ndisc_new (int ifindex, const char *ifname)
|
|||
NM_NDISC_IFINDEX, ifindex,
|
||||
NM_NDISC_IFNAME, ifname,
|
||||
NM_NDISC_NODE_TYPE, (int) NM_NDISC_NODE_TYPE_HOST,
|
||||
NM_NDISC_STABLE_TYPE, (int) NM_UTILS_STABLE_TYPE_UUID,
|
||||
NM_NDISC_NETWORK_ID, "fake",
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -550,6 +550,7 @@ nm_lndp_ndisc_new (NMPlatform *platform,
|
|||
|
||||
g_return_val_if_fail (NM_IS_PLATFORM (platform), NULL);
|
||||
g_return_val_if_fail (!error || !*error, NULL);
|
||||
g_return_val_if_fail (network_id, NULL);
|
||||
|
||||
if (!nm_platform_netns_push (platform, &netns))
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -1054,6 +1054,7 @@ set_property (GObject *object, guint prop_id,
|
|||
case PROP_NETWORK_ID:
|
||||
/* construct-only */
|
||||
priv->network_id = g_value_dup_string (value);
|
||||
g_return_if_fail (priv->network_id);
|
||||
break;
|
||||
case PROP_ADDR_GEN_MODE:
|
||||
/* construct-only */
|
||||
|
|
@ -1175,7 +1176,7 @@ nm_ndisc_class_init (NMNDiscClass *klass)
|
|||
G_PARAM_STATIC_STRINGS);
|
||||
obj_properties[PROP_STABLE_TYPE] =
|
||||
g_param_spec_int (NM_NDISC_STABLE_TYPE, "", "",
|
||||
NM_UTILS_STABLE_TYPE_UUID, NM_UTILS_STABLE_TYPE_STABLE_ID, NM_UTILS_STABLE_TYPE_UUID,
|
||||
NM_UTILS_STABLE_TYPE_UUID, NM_UTILS_STABLE_TYPE_RANDOM, NM_UTILS_STABLE_TYPE_UUID,
|
||||
G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
|
|
|
|||
|
|
@ -3040,6 +3040,39 @@ out:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
const char *
|
||||
nm_utils_get_boot_id (void)
|
||||
{
|
||||
static const char *boot_id;
|
||||
|
||||
if (G_UNLIKELY (!boot_id)) {
|
||||
gs_free char *contents = NULL;
|
||||
|
||||
nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0,
|
||||
&contents, NULL, NULL);
|
||||
if (contents) {
|
||||
g_strstrip (contents);
|
||||
if (contents[0]) {
|
||||
/* clone @contents because we keep @boot_id until the program
|
||||
* ends.
|
||||
* nm_utils_file_get_contents() likely allocated a larger
|
||||
* buffer chunk initially and (although using realloc to shrink
|
||||
* the buffer) it might not be best to keep this memory
|
||||
* around. */
|
||||
boot_id = g_strdup (contents);
|
||||
}
|
||||
}
|
||||
if (!boot_id)
|
||||
boot_id = nm_utils_uuid_generate ();
|
||||
}
|
||||
|
||||
return boot_id;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Returns the "u" (universal/local) bit value for a Modified EUI-64 */
|
||||
static gboolean
|
||||
get_gre_eui64_u_bit (guint32 addr)
|
||||
|
|
@ -3234,8 +3267,188 @@ nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid, char *buf)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
char *
|
||||
nm_utils_stable_id_random (void)
|
||||
{
|
||||
char buf[15];
|
||||
|
||||
if (nm_utils_read_urandom (buf, sizeof (buf)) < 0)
|
||||
g_return_val_if_reached (nm_utils_uuid_generate ());
|
||||
return g_base64_encode ((guchar *) buf, sizeof (buf));
|
||||
}
|
||||
|
||||
char *
|
||||
nm_utils_stable_id_generated_complete (const char *stable_id_generated)
|
||||
{
|
||||
guint8 buf[20];
|
||||
GChecksum *sum;
|
||||
gsize buf_size;
|
||||
char *base64;
|
||||
|
||||
/* for NM_UTILS_STABLE_TYPE_GENERATED we genererate a possibly long string
|
||||
* by doing text-substitutions in nm_utils_stable_id_parse().
|
||||
*
|
||||
* Let's shorten the (possibly) long stable_id to something more compact. */
|
||||
|
||||
g_return_val_if_fail (stable_id_generated, NULL);
|
||||
|
||||
sum = g_checksum_new (G_CHECKSUM_SHA1);
|
||||
nm_assert (sum);
|
||||
|
||||
g_checksum_update (sum, (guchar *) stable_id_generated, strlen (stable_id_generated));
|
||||
|
||||
buf_size = sizeof (buf);
|
||||
g_checksum_get_digest (sum, buf, &buf_size);
|
||||
nm_assert (buf_size == sizeof (buf));
|
||||
|
||||
g_checksum_free (sum);
|
||||
|
||||
/* we don't care to use the sha1 sum in common hex representation.
|
||||
* Use instead base64, it's 27 chars (stripping the padding) vs.
|
||||
* 40. */
|
||||
|
||||
base64 = g_base64_encode ((guchar *) buf, sizeof (buf));
|
||||
nm_assert (strlen (base64) == 28);
|
||||
nm_assert (base64[27] == '=');
|
||||
|
||||
base64[27] = '\0';
|
||||
return base64;
|
||||
}
|
||||
|
||||
static void
|
||||
_stable_id_append (GString *str,
|
||||
const char *substitution)
|
||||
{
|
||||
if (!substitution)
|
||||
substitution = "";
|
||||
g_string_append_printf (str, "=%zu{%s}", strlen (substitution), substitution);
|
||||
}
|
||||
|
||||
NMUtilsStableType
|
||||
nm_utils_stable_id_parse (const char *stable_id,
|
||||
const char *uuid,
|
||||
const char *bootid,
|
||||
char **out_generated)
|
||||
{
|
||||
gsize i, idx_start;
|
||||
GString *str = NULL;
|
||||
|
||||
g_return_val_if_fail (out_generated, NM_UTILS_STABLE_TYPE_RANDOM);
|
||||
|
||||
if (!stable_id) {
|
||||
out_generated = NULL;
|
||||
return NM_UTILS_STABLE_TYPE_UUID;
|
||||
}
|
||||
|
||||
/* the stable-id allows for some dynamic by performing text-substitutions
|
||||
* of ${...} patterns.
|
||||
*
|
||||
* At first, it looks a bit like bash parameter substitution.
|
||||
* In contrast however, the process is unambigious so that the resulting
|
||||
* effective id differs if:
|
||||
* - the original, untranslated stable-id differs
|
||||
* - or any of the subsitutions differs.
|
||||
*
|
||||
* The reason for that is, for example if you specify "${CONNECTION}" in the
|
||||
* stable-id, then the resulting ID should be always(!) unique for this connection.
|
||||
* There should be no way another connection could specify any stable-id that results
|
||||
* in the same addresses to be generated (aside hash collisions).
|
||||
*
|
||||
*
|
||||
* For example: say you have a connection with UUID
|
||||
* "123e4567-e89b-12d3-a456-426655440000" which happens also to be
|
||||
* the current boot-id.
|
||||
* Then:
|
||||
* (1) connection.stable-id = <NULL>
|
||||
* (2) connection.stable-id = "123e4567-e89b-12d3-a456-426655440000"
|
||||
* (3) connection.stable-id = "${CONNECTION}"
|
||||
* (3) connection.stable-id = "${BOOT}"
|
||||
* will all generate different addresses, although in one way or the
|
||||
* other, they all mangle the uuid "123e4567-e89b-12d3-a456-426655440000".
|
||||
*
|
||||
* For example, with stable-id="${FOO}${BAR}" the substitutions
|
||||
* - FOO="ab", BAR="c"
|
||||
* - FOO="a", BAR="bc"
|
||||
* should give a different effective id.
|
||||
*
|
||||
* For example, with FOO="x" and BAR="x", the stable-ids
|
||||
* - "${FOO}${BAR}"
|
||||
* - "${BAR}${FOO}"
|
||||
* should give a different effective id.
|
||||
*/
|
||||
|
||||
idx_start = 0;
|
||||
for (i = 0; stable_id[i]; ) {
|
||||
if (stable_id[i] != '$') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
#define CHECK_PREFIX(prefix) \
|
||||
({ \
|
||||
gboolean _match = FALSE; \
|
||||
\
|
||||
if (g_str_has_prefix (&stable_id[i], ""prefix"")) { \
|
||||
_match = TRUE; \
|
||||
if (!str) \
|
||||
str = g_string_sized_new (256); \
|
||||
i += NM_STRLEN (prefix); \
|
||||
g_string_append_len (str, &(stable_id)[idx_start], i - idx_start); \
|
||||
idx_start = i; \
|
||||
} \
|
||||
_match; \
|
||||
})
|
||||
if (CHECK_PREFIX ("${CONNECTION}"))
|
||||
_stable_id_append (str, uuid);
|
||||
else if (CHECK_PREFIX ("${BOOT}"))
|
||||
_stable_id_append (str, bootid ?: nm_utils_get_boot_id ());
|
||||
else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) {
|
||||
/* RANDOM makes not so much sense for cloned-mac-address
|
||||
* as the result is simmilar to specifing "cloned-mac-address=random".
|
||||
* It makes however sense for RFC 7217 Stable Privacy IPv6 addresses
|
||||
* where this is effectively the only way to generate a different
|
||||
* (random) host identifier for each connect.
|
||||
*
|
||||
* With RANDOM, the user can switch the lifetime of the
|
||||
* generated cloned-mac-address and IPv6 host identifier
|
||||
* by toggeling only the stable-id property of the connection.
|
||||
* With RANDOM being the most short-lived, ~non-stable~ variant.
|
||||
*/
|
||||
if (str)
|
||||
g_string_free (str, TRUE);
|
||||
*out_generated = NULL;
|
||||
return NM_UTILS_STABLE_TYPE_RANDOM;
|
||||
} else {
|
||||
/* The text following the '$' is not recognized as valid
|
||||
* substitution pattern. Treat it verbatim. */
|
||||
i++;
|
||||
|
||||
/* Note that using unrecognized substitution patterns might
|
||||
* yield different results with future versions. Avoid that,
|
||||
* by not using '$' (except for actual substitutions) or escape
|
||||
* it as "$$" (which is guaranteed to be treated verbatim
|
||||
* in future). */
|
||||
if (stable_id[i] == '$')
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#undef CHECK_PREFIX
|
||||
|
||||
if (!str) {
|
||||
*out_generated = NULL;
|
||||
return NM_UTILS_STABLE_TYPE_STABLE_ID;
|
||||
}
|
||||
|
||||
if (idx_start < i)
|
||||
g_string_append_len (str, &stable_id[idx_start], i - idx_start);
|
||||
*out_generated = g_string_free (str, FALSE);
|
||||
return NM_UTILS_STABLE_TYPE_GENERATED;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_set_stable_privacy (guint8 stable_type,
|
||||
_set_stable_privacy (NMUtilsStableType stable_type,
|
||||
struct in6_addr *addr,
|
||||
const char *ifname,
|
||||
const char *network_id,
|
||||
|
|
@ -3249,7 +3462,8 @@ _set_stable_privacy (guint8 stable_type,
|
|||
guint32 tmp[2];
|
||||
gsize len = sizeof (digest);
|
||||
|
||||
g_return_val_if_fail (key_len, FALSE);
|
||||
nm_assert (key_len);
|
||||
nm_assert (network_id);
|
||||
|
||||
/* Documentation suggests that this can fail.
|
||||
* Maybe in case of a missing algorithm in crypto library? */
|
||||
|
|
@ -3263,6 +3477,11 @@ _set_stable_privacy (guint8 stable_type,
|
|||
key_len = MIN (key_len, G_MAXUINT32);
|
||||
|
||||
if (stable_type != NM_UTILS_STABLE_TYPE_UUID) {
|
||||
guint8 stable_type_uint8;
|
||||
|
||||
nm_assert (stable_type < (NMUtilsStableType) 255);
|
||||
stable_type_uint8 = (guint8) stable_type;
|
||||
|
||||
/* Preferably, we would always like to include the stable-type,
|
||||
* but for backward compatibility reasons, we cannot for UUID.
|
||||
*
|
||||
|
|
@ -3272,13 +3491,11 @@ _set_stable_privacy (guint8 stable_type,
|
|||
* and the terminating '\0' of @network_id, it is unambigiously
|
||||
* possible to revert the process and deduce the @stable_type.
|
||||
*/
|
||||
g_checksum_update (sum, &stable_type, sizeof (stable_type));
|
||||
g_checksum_update (sum, &stable_type_uint8, sizeof (stable_type_uint8));
|
||||
}
|
||||
|
||||
g_checksum_update (sum, addr->s6_addr, 8);
|
||||
g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1);
|
||||
if (!network_id)
|
||||
network_id = "";
|
||||
g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1);
|
||||
tmp[0] = htonl (dad_counter);
|
||||
tmp[1] = htonl (key_len);
|
||||
|
|
@ -3296,7 +3513,7 @@ _set_stable_privacy (guint8 stable_type,
|
|||
}
|
||||
|
||||
gboolean
|
||||
nm_utils_ipv6_addr_set_stable_privacy_impl (guint8 stable_type,
|
||||
nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type,
|
||||
struct in6_addr *addr,
|
||||
const char *ifname,
|
||||
const char *network_id,
|
||||
|
|
@ -3328,9 +3545,7 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type,
|
|||
gs_free guint8 *secret_key = NULL;
|
||||
gsize key_len = 0;
|
||||
|
||||
nm_assert (NM_IN_SET (stable_type,
|
||||
NM_UTILS_STABLE_TYPE_UUID,
|
||||
NM_UTILS_STABLE_TYPE_STABLE_ID));
|
||||
g_return_val_if_fail (network_id, FALSE);
|
||||
|
||||
if (dad_counter >= RFC7217_IDGEN_RETRIES) {
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
|
|
@ -3430,9 +3645,6 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
|
|||
guint8 stable_type_uint8;
|
||||
|
||||
nm_assert (stable_id);
|
||||
nm_assert (NM_IN_SET (stable_type,
|
||||
NM_UTILS_STABLE_TYPE_UUID,
|
||||
NM_UTILS_STABLE_TYPE_STABLE_ID));
|
||||
nm_assert (secret_key);
|
||||
|
||||
sum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
|
|
@ -3441,6 +3653,7 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
|
|||
|
||||
key_len = MIN (key_len, G_MAXUINT32);
|
||||
|
||||
nm_assert (stable_type < (NMUtilsStableType) 255);
|
||||
stable_type_uint8 = stable_type;
|
||||
g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8));
|
||||
|
||||
|
|
|
|||
|
|
@ -322,6 +322,8 @@ gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_u
|
|||
|
||||
guint8 *nm_utils_secret_key_read (gsize *out_key_len, GError **error);
|
||||
|
||||
const char *nm_utils_get_boot_id (void);
|
||||
|
||||
/* IPv6 Interface Identifer helpers */
|
||||
|
||||
/**
|
||||
|
|
@ -360,13 +362,28 @@ gboolean nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
|
|||
guint dev_id,
|
||||
NMUtilsIPv6IfaceId *out_iid);
|
||||
|
||||
typedef enum { /*< skip >*/
|
||||
NM_UTILS_STABLE_TYPE_UUID = 0,
|
||||
typedef enum {
|
||||
/* The stable type. Note that this value is encoded in the
|
||||
* generated addresses, thus the numbers MUST not change.
|
||||
*
|
||||
* Also note, if we ever allocate ID 255, we must take care
|
||||
* that nm_utils_ipv6_addr_set_stable_privacy() extends the
|
||||
* uint8 encoding of this value. */
|
||||
NM_UTILS_STABLE_TYPE_UUID = 0,
|
||||
NM_UTILS_STABLE_TYPE_STABLE_ID = 1,
|
||||
NM_UTILS_STABLE_TYPE_GENERATED = 2,
|
||||
NM_UTILS_STABLE_TYPE_RANDOM = 3,
|
||||
} NMUtilsStableType;
|
||||
|
||||
NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id,
|
||||
const char *uuid,
|
||||
const char *bootid,
|
||||
char **out_generated);
|
||||
|
||||
gboolean nm_utils_ipv6_addr_set_stable_privacy_impl (guint8 stable_type,
|
||||
char *nm_utils_stable_id_random (void);
|
||||
char *nm_utils_stable_id_generated_complete (const char *msg);
|
||||
|
||||
gboolean nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type,
|
||||
struct in6_addr *addr,
|
||||
const char *ifname,
|
||||
const char *network_id,
|
||||
|
|
|
|||
|
|
@ -1481,6 +1481,89 @@ test_reverse_dns_ip6 (void)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
do_test_stable_id_parse (const char *stable_id,
|
||||
NMUtilsStableType expected_stable_type,
|
||||
const char *expected_generated)
|
||||
{
|
||||
gs_free char *generated = NULL;
|
||||
NMUtilsStableType stable_type;
|
||||
|
||||
if (expected_stable_type == NM_UTILS_STABLE_TYPE_GENERATED)
|
||||
g_assert (expected_generated);
|
||||
else
|
||||
g_assert (!expected_generated);
|
||||
|
||||
if (expected_stable_type == NM_UTILS_STABLE_TYPE_UUID)
|
||||
g_assert (!stable_id);
|
||||
else
|
||||
g_assert (stable_id);
|
||||
|
||||
stable_type = nm_utils_stable_id_parse (stable_id, "_CONNECTION", "_BOOT", &generated);
|
||||
|
||||
g_assert_cmpint (expected_stable_type, ==, stable_type);
|
||||
|
||||
if (stable_type == NM_UTILS_STABLE_TYPE_GENERATED) {
|
||||
g_assert_cmpstr (expected_generated, ==, generated);
|
||||
g_assert (generated);
|
||||
} else
|
||||
g_assert (!generated);
|
||||
}
|
||||
|
||||
static void
|
||||
test_stable_id_parse (void)
|
||||
{
|
||||
#define _parse_stable_id(stable_id) do_test_stable_id_parse (""stable_id"", NM_UTILS_STABLE_TYPE_STABLE_ID, NULL)
|
||||
#define _parse_generated(stable_id, expected_generated) do_test_stable_id_parse (""stable_id"", NM_UTILS_STABLE_TYPE_GENERATED, ""expected_generated"")
|
||||
#define _parse_random(stable_id) do_test_stable_id_parse (""stable_id"", NM_UTILS_STABLE_TYPE_RANDOM, NULL)
|
||||
do_test_stable_id_parse (NULL, NM_UTILS_STABLE_TYPE_UUID, NULL);
|
||||
_parse_stable_id ("");
|
||||
_parse_stable_id ("a");
|
||||
_parse_stable_id ("a$");
|
||||
_parse_stable_id ("a$x");
|
||||
_parse_stable_id (" ${a$x");
|
||||
_parse_stable_id ("${");
|
||||
_parse_stable_id ("${=");
|
||||
_parse_stable_id ("${a");
|
||||
_parse_stable_id ("${a$x");
|
||||
_parse_stable_id ("a$$");
|
||||
_parse_stable_id ("a$$x");
|
||||
_parse_stable_id ("a$${CONNECTION}");
|
||||
_parse_stable_id ("a$${CONNECTION}x");
|
||||
_parse_generated ("${CONNECTION}", "${CONNECTION}=11{_CONNECTION}");
|
||||
_parse_generated ("${${CONNECTION}", "${${CONNECTION}=11{_CONNECTION}");
|
||||
_parse_generated ("${CONNECTION}x", "${CONNECTION}=11{_CONNECTION}x");
|
||||
_parse_generated ("x${CONNECTION}", "x${CONNECTION}=11{_CONNECTION}");
|
||||
_parse_generated ("${BOOT}x", "${BOOT}=5{_BOOT}x");
|
||||
_parse_generated ("x${BOOT}", "x${BOOT}=5{_BOOT}");
|
||||
_parse_generated ("x${BOOT}${CONNECTION}", "x${BOOT}=5{_BOOT}${CONNECTION}=11{_CONNECTION}");
|
||||
_parse_generated ("xX${BOOT}yY${CONNECTION}zZ", "xX${BOOT}=5{_BOOT}yY${CONNECTION}=11{_CONNECTION}zZ");
|
||||
_parse_random ("${RANDOM}");
|
||||
_parse_random (" ${RANDOM}");
|
||||
_parse_random ("${BOOT}${RANDOM}");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
test_stable_id_generated_complete (void)
|
||||
{
|
||||
#define ASSERT(str, expected) \
|
||||
G_STMT_START { \
|
||||
gs_free char *_s = NULL; \
|
||||
\
|
||||
_s = nm_utils_stable_id_generated_complete ((str)); \
|
||||
g_assert_cmpstr ((expected), ==, _s); \
|
||||
} G_STMT_END
|
||||
|
||||
ASSERT ("", "2jmj7l5rSw0yVb/vlWAYkK/YBwk");
|
||||
ASSERT ("a", "hvfkN/qlp/zhXR3cuerq6jd2Z7g");
|
||||
ASSERT ("password", "W6ph5Mm5Pz8GgiULbPgzG37mj9g");
|
||||
#undef ASSERT
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMTST_DEFINE ();
|
||||
|
||||
int
|
||||
|
|
@ -1518,6 +1601,9 @@ main (int argc, char **argv)
|
|||
g_test_add_func ("/general/reverse_dns/ip4", test_reverse_dns_ip4);
|
||||
g_test_add_func ("/general/reverse_dns/ip6", test_reverse_dns_ip6);
|
||||
|
||||
g_test_add_func ("/general/stable-id/parse", test_stable_id_parse);
|
||||
g_test_add_func ("/general/stable-id/generated-complete", test_stable_id_generated_complete);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ test_stable_privacy (void)
|
|||
|
||||
/* We get an address without the UUID. */
|
||||
inet_pton (AF_INET6, "1::", &addr1);
|
||||
nm_utils_ipv6_addr_set_stable_privacy_impl (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
|
||||
nm_utils_ipv6_addr_set_stable_privacy_impl (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", "", 384, (guint8 *) "key", 3, NULL);
|
||||
nmtst_assert_ip6_address (&addr1, "1::11aa:2530:9144:dafa");
|
||||
|
||||
/* We get a different address in a different network. */
|
||||
inet_pton (AF_INET6, "2::", &addr1);
|
||||
nm_utils_ipv6_addr_set_stable_privacy_impl (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
|
||||
nm_utils_ipv6_addr_set_stable_privacy_impl (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", "", 384, (guint8 *) "key", 3, NULL);
|
||||
nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726");
|
||||
|
||||
inet_pton (AF_INET6, "1234::", &addr1);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue