mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-30 14:20:17 +01:00
device: merge branch 'th/default-wired-connection-stable-uuid-bgo765464'
https://bugzilla.gnome.org/show_bug.cgi?id=765464
This commit is contained in:
commit
c17b4ba6c7
8 changed files with 162 additions and 103 deletions
|
|
@ -209,6 +209,7 @@ libNetworkManager_base_la_CPPFLAGS = \
|
|||
-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
|
||||
-DNO_SYSTEMD_JOURNAL \
|
||||
-DPREFIX=\"$(prefix)\" \
|
||||
-DLOCALSTATEDIR=\"$(localstatedir)\" \
|
||||
-DNMSTATEDIR=\"$(nmstatedir)\" \
|
||||
$(GLIB_CFLAGS)
|
||||
|
||||
|
|
|
|||
|
|
@ -1433,7 +1433,9 @@ new_default_connection (NMDevice *self)
|
|||
const GSList *connections;
|
||||
NMSetting *setting;
|
||||
const char *hw_address;
|
||||
char *defname, *uuid;
|
||||
gs_free char *defname = NULL;
|
||||
gs_free char *uuid = NULL;
|
||||
gs_free char *machine_id = NULL;
|
||||
|
||||
if (nm_config_get_no_auto_default_for_device (nm_config_get (), self))
|
||||
return NULL;
|
||||
|
|
@ -1448,7 +1450,19 @@ new_default_connection (NMDevice *self)
|
|||
|
||||
connections = nm_connection_provider_get_connections (nm_connection_provider_get ());
|
||||
defname = nm_device_ethernet_utils_get_default_wired_name (connections);
|
||||
uuid = nm_utils_uuid_generate ();
|
||||
if (!defname)
|
||||
return NULL;
|
||||
|
||||
machine_id = nm_utils_machine_id_read ();
|
||||
|
||||
/* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode,
|
||||
* thus when it changes we will also generate different IPv6 addresses. */
|
||||
uuid = _nm_utils_uuid_generate_from_strings ("default-wired",
|
||||
machine_id ?: "",
|
||||
defname,
|
||||
hw_address,
|
||||
NULL);
|
||||
|
||||
g_object_set (setting,
|
||||
NM_SETTING_CONNECTION_ID, defname,
|
||||
NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
|
||||
|
|
@ -1457,8 +1471,6 @@ new_default_connection (NMDevice *self)
|
|||
NM_SETTING_CONNECTION_UUID, uuid,
|
||||
NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
|
||||
NULL);
|
||||
g_free (uuid);
|
||||
g_free (defname);
|
||||
|
||||
/* Lock the connection to the device */
|
||||
setting = nm_setting_wired_new ();
|
||||
|
|
|
|||
|
|
@ -436,37 +436,10 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
|
|||
return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
|
||||
}
|
||||
|
||||
/* uuid_parse does not work for machine-id, so we use our own converter */
|
||||
static gboolean
|
||||
machine_id_parse (const char *in, uuid_t uu)
|
||||
{
|
||||
const char *cp;
|
||||
int i;
|
||||
char buf[3];
|
||||
|
||||
g_return_val_if_fail (in != NULL, FALSE);
|
||||
g_return_val_if_fail (strlen (in) == 32, FALSE);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!g_ascii_isxdigit (in[i]))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
buf[2] = 0;
|
||||
cp = in;
|
||||
for (i = 0; i < 16; i++) {
|
||||
buf[0] = *cp++;
|
||||
buf[1] = *cp++;
|
||||
uu[i] = ((unsigned char) strtoul (buf, NULL, 16)) & 0xFF;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
generate_duid_from_machine_id (void)
|
||||
{
|
||||
GByteArray *duid;
|
||||
char *contents = NULL;
|
||||
GChecksum *sum;
|
||||
guint8 buffer[32]; /* SHA256 digest size */
|
||||
gsize sumlen = sizeof (buffer);
|
||||
|
|
@ -474,27 +447,16 @@ generate_duid_from_machine_id (void)
|
|||
uuid_t uuid;
|
||||
GRand *generator;
|
||||
guint i;
|
||||
gboolean success = FALSE;
|
||||
gs_free char *machine_id_s = NULL;
|
||||
|
||||
/* Get the machine ID from /etc/machine-id; it's always in /etc no matter
|
||||
* where our configured SYSCONFDIR is. Alternatively, it might be in
|
||||
* LOCALSTATEDIR /lib/dbus/machine-id.
|
||||
*/
|
||||
if ( g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL)
|
||||
|| g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) {
|
||||
contents = g_strstrip (contents);
|
||||
success = machine_id_parse (contents, uuid);
|
||||
if (success) {
|
||||
/* Hash the machine ID so it's not leaked to the network */
|
||||
sum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid));
|
||||
g_checksum_get_digest (sum, buffer, &sumlen);
|
||||
g_checksum_free (sum);
|
||||
}
|
||||
g_free (contents);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
machine_id_s = nm_utils_machine_id_read ();
|
||||
if (nm_utils_machine_id_parse (machine_id_s, uuid)) {
|
||||
/* Hash the machine ID so it's not leaked to the network */
|
||||
sum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid));
|
||||
g_checksum_get_digest (sum, buffer, &sumlen);
|
||||
g_checksum_free (sum);
|
||||
} else {
|
||||
nm_log_warn (LOGD_DHCP6, "dhcp6: failed to read " SYSCONFDIR "/machine-id "
|
||||
"or " LOCALSTATEDIR "/lib/dbus/machine-id to generate "
|
||||
"DHCPv6 DUID; creating non-persistent random DUID.");
|
||||
|
|
|
|||
|
|
@ -2588,6 +2588,121 @@ nm_utils_is_specific_hostname (const char *name)
|
|||
|
||||
/******************************************************************/
|
||||
|
||||
gboolean
|
||||
nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid)
|
||||
{
|
||||
int i;
|
||||
guint8 v0, v1;
|
||||
|
||||
if (!id_str)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!g_ascii_isxdigit (id_str[i]))
|
||||
return FALSE;
|
||||
}
|
||||
if (id_str[i] != '\0')
|
||||
return FALSE;
|
||||
|
||||
if (out_uuid) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
v0 = g_ascii_xdigit_value (*(id_str++));
|
||||
v1 = g_ascii_xdigit_value (*(id_str++));
|
||||
out_uuid[i] = (v0 << 4) + v1;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *
|
||||
nm_utils_machine_id_read (void)
|
||||
{
|
||||
gs_free char *contents = NULL;
|
||||
int i;
|
||||
|
||||
/* Get the machine ID from /etc/machine-id; it's always in /etc no matter
|
||||
* where our configured SYSCONFDIR is. Alternatively, it might be in
|
||||
* LOCALSTATEDIR /lib/dbus/machine-id.
|
||||
*/
|
||||
if ( !g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL)
|
||||
&& !g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL))
|
||||
return FALSE;
|
||||
|
||||
contents = g_strstrip (contents);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!g_ascii_isxdigit (contents[i]))
|
||||
return FALSE;
|
||||
if (contents[i] >= 'A' && contents[i] <= 'F') {
|
||||
/* canonicalize to lower-case */
|
||||
contents[i] = 'a' + (contents[i] - 'A');
|
||||
}
|
||||
}
|
||||
if (contents[i] != '\0')
|
||||
return FALSE;
|
||||
|
||||
return nm_unauto (&contents);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
guint8 *
|
||||
nm_utils_secret_key_read (gsize *out_key_len, GError **error)
|
||||
{
|
||||
guint8 *secret_key = NULL;
|
||||
gsize key_len;
|
||||
|
||||
/* out_key_len is not optional, because without it you cannot safely
|
||||
* access the returned memory. */
|
||||
*out_key_len = 0;
|
||||
|
||||
/* Let's try to load a saved secret key first. */
|
||||
if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, NULL)) {
|
||||
if (key_len < 16) {
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"Key is too short to be usable");
|
||||
key_len = 0;
|
||||
}
|
||||
} else {
|
||||
int urandom = open ("/dev/urandom", O_RDONLY);
|
||||
mode_t key_mask;
|
||||
|
||||
if (urandom == -1) {
|
||||
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"Can't open /dev/urandom: %s", strerror (errno));
|
||||
key_len = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* RFC7217 mandates the key SHOULD be at least 128 bits.
|
||||
* Let's use twice as much. */
|
||||
key_len = 32;
|
||||
secret_key = g_malloc (key_len);
|
||||
|
||||
key_mask = umask (0077);
|
||||
if (read (urandom, secret_key, key_len) == key_len) {
|
||||
if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) {
|
||||
g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: ");
|
||||
key_len = 0;
|
||||
}
|
||||
} else {
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"Could not obtain a secret");
|
||||
key_len = 0;
|
||||
}
|
||||
umask (key_mask);
|
||||
close (urandom);
|
||||
}
|
||||
|
||||
out:
|
||||
if (key_len) {
|
||||
*out_key_len = key_len;
|
||||
return secret_key;
|
||||
}
|
||||
g_free (secret_key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns the "u" (universal/local) bit value for a Modified EUI-64 */
|
||||
static gboolean
|
||||
get_gre_eui64_u_bit (guint32 addr)
|
||||
|
|
@ -2715,7 +2830,7 @@ _set_stable_privacy (struct in6_addr *addr,
|
|||
const char *ifname,
|
||||
const char *uuid,
|
||||
guint dad_counter,
|
||||
gchar *secret_key,
|
||||
guint8 *secret_key,
|
||||
gsize key_len,
|
||||
GError **error)
|
||||
{
|
||||
|
|
@ -2773,9 +2888,8 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
|
|||
guint dad_counter,
|
||||
GError **error)
|
||||
{
|
||||
gchar *secret_key = NULL;
|
||||
gs_free guint8 *secret_key = NULL;
|
||||
gsize key_len = 0;
|
||||
gboolean success = FALSE;
|
||||
|
||||
if (dad_counter >= RFC7217_IDGEN_RETRIES) {
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
|
|
@ -2783,50 +2897,12 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* Let's try to load a saved secret key first. */
|
||||
if (g_file_get_contents (NMSTATEDIR "/secret_key", &secret_key, &key_len, NULL)) {
|
||||
if (key_len < 16) {
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"Key is too short to be usable");
|
||||
key_len = 0;
|
||||
}
|
||||
} else {
|
||||
int urandom = open ("/dev/urandom", O_RDONLY);
|
||||
mode_t key_mask;
|
||||
secret_key = nm_utils_secret_key_read (&key_len, error);
|
||||
if (!secret_key)
|
||||
return FALSE;
|
||||
|
||||
if (urandom == -1) {
|
||||
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"Can't open /dev/urandom: %s", strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* RFC7217 mandates the key SHOULD be at least 128 bits.
|
||||
* Let's use twice as much. */
|
||||
key_len = 32;
|
||||
secret_key = g_malloc (key_len);
|
||||
|
||||
key_mask = umask (0077);
|
||||
if (read (urandom, secret_key, key_len) == key_len) {
|
||||
if (!g_file_set_contents (NMSTATEDIR "/secret_key", secret_key, key_len, error)) {
|
||||
g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: ");
|
||||
key_len = 0;
|
||||
}
|
||||
} else {
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"Could not obtain a secret");
|
||||
key_len = 0;
|
||||
}
|
||||
umask (key_mask);
|
||||
close (urandom);
|
||||
}
|
||||
|
||||
if (key_len) {
|
||||
success = _set_stable_privacy (addr, ifname, uuid, dad_counter,
|
||||
secret_key, key_len, error);
|
||||
}
|
||||
|
||||
g_free (secret_key);
|
||||
return success;
|
||||
return _set_stable_privacy (addr, ifname, uuid, dad_counter,
|
||||
secret_key, key_len, error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -307,6 +307,11 @@ const char *nm_utils_ip4_property_path (const char *ifname, const char *property
|
|||
|
||||
gboolean nm_utils_is_specific_hostname (const char *name);
|
||||
|
||||
char *nm_utils_machine_id_read (void);
|
||||
gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid);
|
||||
|
||||
guint8 *nm_utils_secret_key_read (gsize *out_key_len, GError **error);
|
||||
|
||||
/* IPv6 Interface Identifer helpers */
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1982,9 +1982,11 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
|
|||
g_object_unref (connection);
|
||||
|
||||
if (!added) {
|
||||
_LOGW ("(%s) couldn't create default wired connection: %s",
|
||||
nm_device_get_iface (device),
|
||||
error->message);
|
||||
if (!g_error_matches (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS)) {
|
||||
_LOGW ("(%s) couldn't create default wired connection: %s",
|
||||
nm_device_get_iface (device),
|
||||
error->message);
|
||||
}
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ test_utils_DEPENDENCIES = \
|
|||
test_utils_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
-DPREFIX=\"/nonexistent\" \
|
||||
-DLOCALSTATEDIR=\"$(localstatedir)\" \
|
||||
-DNMSTATEDIR=\"/nonsense\"
|
||||
|
||||
test_utils_LDADD = \
|
||||
|
|
|
|||
|
|
@ -34,17 +34,17 @@ test_stable_privacy (void)
|
|||
struct in6_addr addr1;
|
||||
|
||||
inet_pton (AF_INET6, "1234::", &addr1);
|
||||
_set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, "key", 3, NULL);
|
||||
_set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL);
|
||||
nmtst_assert_ip6_address (&addr1, "1234::4ceb:14cd:3d54:793f");
|
||||
|
||||
/* We get an address without the UUID. */
|
||||
inet_pton (AF_INET6, "1::", &addr1);
|
||||
_set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL);
|
||||
_set_stable_privacy (&addr1, "eth666", NULL, 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);
|
||||
_set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL);
|
||||
_set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
|
||||
nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue