diff options
author | Thomas Haller <thaller@redhat.com> | 2016-04-26 12:56:01 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2016-04-26 12:56:01 +0200 |
commit | c17b4ba6c750c375e80ae37759d9a4c9caa5bf08 (patch) | |
tree | dca2d57a4fa4be4983637aaa85d09d27c90d6040 | |
parent | dd4d8b24da29abfc786ce0b3030c74559b93d034 (diff) | |
parent | 89cf9429a76d8230bde8d2ca169e42bfb8803634 (diff) | |
download | NetworkManager-c17b4ba6c750c375e80ae37759d9a4c9caa5bf08.tar.gz |
device: merge branch 'th/default-wired-connection-stable-uuid-bgo765464'
https://bugzilla.gnome.org/show_bug.cgi?id=765464
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/devices/nm-device-ethernet.c | 20 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-client.c | 58 | ||||
-rw-r--r-- | src/nm-core-utils.c | 168 | ||||
-rw-r--r-- | src/nm-core-utils.h | 5 | ||||
-rw-r--r-- | src/settings/nm-settings.c | 8 | ||||
-rw-r--r-- | src/tests/Makefile.am | 1 | ||||
-rw-r--r-- | src/tests/test-utils.c | 6 |
8 files changed, 163 insertions, 104 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e567fe0a7c..4ec7b40156 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 6225ae7bf6..acf822a2e5 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -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 (); diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 7e72233670..3490c0d235 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -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; - - /* 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) { + gs_free char *machine_id_s = NULL; + + 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."); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 188e4737af..6581d6b711 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -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; - - 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); - } + secret_key = nm_utils_secret_key_read (&key_len, error); + if (!secret_key) + return FALSE; - g_free (secret_key); - return success; + return _set_stable_privacy (addr, ifname, uuid, dad_counter, + secret_key, key_len, error); } /** diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 486942a22a..3033c33a5f 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -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 */ /** diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 281f299f1f..3b8ac5b5df 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -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; } diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index b93d4da3b9..5b4e6b729f 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -142,6 +142,7 @@ test_utils_DEPENDENCIES = \ test_utils_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DPREFIX=\"/nonexistent\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DNMSTATEDIR=\"/nonsense\" test_utils_LDADD = \ diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index c8c82aefe4..6fc3978b9c 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -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"); } |