diff options
author | Thomas Haller <thaller@redhat.com> | 2018-06-12 15:39:59 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-06-12 15:39:59 +0200 |
commit | 97bb298f58faebd63942a2d0c12daa356af1306c (patch) | |
tree | a34b556fa095afb2d236b2de88ffcd521d039518 | |
parent | e8a197506cf61161397d8f67304d89187ebbd26d (diff) | |
parent | fd878d826129141043e7b30e6521b23544c8967e (diff) | |
download | NetworkManager-97bb298f58faebd63942a2d0c12daa356af1306c.tar.gz |
dhcp: merge branch 'th/dhcp-client-id'
https://github.com/NetworkManager/NetworkManager/pull/133
-rw-r--r-- | examples/nm-conf.d/30-anon.conf | 37 | ||||
-rw-r--r-- | libnm-core/nm-utils.c | 27 | ||||
-rw-r--r-- | src/devices/nm-device.c | 268 | ||||
-rw-r--r-- | src/nm-core-utils.c | 6 |
4 files changed, 210 insertions, 128 deletions
diff --git a/examples/nm-conf.d/30-anon.conf b/examples/nm-conf.d/30-anon.conf index 28a9ae701a..3e879fc2d4 100644 --- a/examples/nm-conf.d/30-anon.conf +++ b/examples/nm-conf.d/30-anon.conf @@ -1,39 +1,44 @@ # 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 +# Drop this file 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 +# This enables some 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\)' +# 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\|ipv4.dhcp-client-id\|ipv6.dhcp-duid\)' # connection.stable-id: -- # 802-3-ethernet.cloned-mac-address: -- # 802-11-wireless.cloned-mac-address: -- # 802-11-wireless.mac-address-randomization:default +# ipv4.dhcp-client-id: -- # ipv6.ip6-privacy: -1 (unknown) # ipv6.addr-gen-mode: stable-privacy +# ipv6.dhcp-duid: -- # # then the default values are inherited and thus both the MAC -# address and the IPv6 host identifier are randomized. +# address, IPv6 host identifier, and DHCP identifiers are randomized. # Also, ipv6 private addresses (RFC4941) are used in # addition. # # +# The connection's stable-id is really a token associated with the identity +# of the connection. It means, by setting it to different values, different +# addresses and DHCP options are generated. # For some profiles it can make sense to reuse the same stable-id -# (and thus MAC address and IPv6 host identifier) for the duration +# (and thus share 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: +# ... or keep it stable accross reboots, but still distinct per profile: # # $ nmcli connection modify "$CON_NAME" connection.stable-id '${CONNECTION}' # @@ -53,3 +58,23 @@ connection.stable-id=${RANDOM} ethernet.cloned-mac-address=stable wifi.cloned-mac-address=stable ipv6.ip6-privacy=2 + +# RFC 7844 "DHCP Anonymity Profiles" mandates in combination with +# MAC address randomization: +# connection.stable-id=${RANDOM} +# ethernet.cloned-mac-address=stable +# wifi.cloned-mac-address=stable +# ipv4.dhcp-client-id=mac +# ipv6.dhcp-duid=ll +# In case, the interface cannot use MAC address randomization, +# RFC 7844 recomments +# connection.stable-id=${RANDOM} +# ipv4.dhcp-client-id=stable +# ipv6.dhcp-duid=stable-llt +# See https://tools.ietf.org/html/rfc7844#section-3.5 +# https://tools.ietf.org/html/rfc7844#section-4.3 +# +# In this example however, the defaults are set to a stable identifier +# depending on the connection.stable-id. +ipv4.dhcp-client-id=stable +ipv6.dhcp-duid=stable-uuid diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 861b283e45..eb705cbe0a 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -4448,11 +4448,10 @@ _nm_utils_inet6_is_token (const struct in6_addr *in6addr) gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin) { + guint8 duid_arr[128 + 2]; gsize duid_len; - gs_unref_bytes GBytes *duid_bin = NULL; - if (out_duid_bin) - *out_duid_bin = NULL; + NM_SET_OUT (out_duid_bin, NULL); if (!duid) return FALSE; @@ -4466,20 +4465,16 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin) return TRUE; } - duid_bin = nm_utils_hexstr2bin (duid); - if (!duid_bin) - return FALSE; - - duid_len = g_bytes_get_size (duid_bin); - /* MAX DUID lenght is 128 octects + the type code (2 octects). */ - if ( duid_len <= 2 - || duid_len > (128 + 2)) - return FALSE; - - if (out_duid_bin) - *out_duid_bin = g_steal_pointer (&duid_bin); + if (_str2bin (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) { + /* MAX DUID length is 128 octects + the type code (2 octects). */ + if ( duid_len > 2 + && duid_len <= (128 + 2)) { + NM_SET_OUT (out_duid_bin, g_bytes_new (duid_arr, duid_len)); + return TRUE; + } + } - return TRUE; + return FALSE; } /** diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 66bd419447..060e59d700 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7018,13 +7018,32 @@ get_dhcp_timeout (NMDevice *self, int addr_family) } static GBytes * -dhcp4_get_client_id (NMDevice *self, NMConnection *connection) +dhcp4_get_client_id_mac (const guint8 *hwaddr /* ETH_ALEN bytes */) +{ + guint8 *client_id_buf; + guint8 hwaddr_type = ARPHRD_ETHER; + + client_id_buf = g_malloc (ETH_ALEN + 1); + client_id_buf[0] = hwaddr_type; + memcpy (&client_id_buf[1], hwaddr, ETH_ALEN); + return g_bytes_new_take (client_id_buf, ETH_ALEN + 1); +} + +static GBytes * +dhcp4_get_client_id (NMDevice *self, + NMConnection *connection, + GBytes *hwaddr) { NMSettingIPConfig *s_ip4; const char *client_id; gs_free char *client_id_default = NULL; guint8 *client_id_buf; - gboolean is_mac; + const char *fail_reason; + guint8 hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX]; + const guint8 *hwaddr_bin; + gsize hwaddr_len; + GBytes *result; + gs_free char *logstr1 = NULL; s_ip4 = nm_connection_get_setting_ip4_config (connection); client_id = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)); @@ -7032,42 +7051,54 @@ dhcp4_get_client_id (NMDevice *self, NMConnection *connection) if (!client_id) { client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, "ipv4.dhcp-client-id", self); - if (client_id_default && client_id_default[0]) + if (client_id_default && client_id_default[0]) { + /* a non-empty client-id is always valid, see nm_dhcp_utils_client_id_string_to_bytes(). */ client_id = client_id_default; + } } - if (!client_id) + if (!client_id) { + _LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, + "ipv4.dhcp-client-id: no explicity client-id configured"); return NULL; + } - if ( (is_mac = nm_streq (client_id, "mac")) - || nm_streq (client_id, "perm-mac")) { - const char *hwaddr; - char addr_buf[NM_UTILS_HWADDR_LEN_MAX]; - gsize addr_len; - guint8 addr_type; - - hwaddr = is_mac - ? nm_device_get_hw_address (self) - : nm_device_get_permanent_hw_address (self); - if (!hwaddr) - return NULL; + if (nm_streq (client_id, "mac")) { + if (!hwaddr) { + fail_reason = "failed to get current MAC address"; + goto out_fail; + } + + hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); + if (hwaddr_len != ETH_ALEN) { + fail_reason = "MAC address is not ethernet"; + goto out_fail; + } + + result = dhcp4_get_client_id_mac (hwaddr_bin); + goto out_good; + } + + if (nm_streq (client_id, "perm-mac")) { + const char *hwaddr_str; + + hwaddr_str = nm_device_get_permanent_hw_address (self); + if (!hwaddr_str) { + fail_reason = "failed to get permanent MAC address"; + goto out_fail; + } - if (!_nm_utils_hwaddr_aton (hwaddr, addr_buf, sizeof (addr_buf), &addr_len)) + if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_bin_buf, sizeof (hwaddr_bin_buf), &hwaddr_len)) g_return_val_if_reached (NULL); - switch (addr_len) { - case ETH_ALEN: - addr_type = ARPHRD_ETHER; - break; - default: + if (hwaddr_len != ETH_ALEN) { /* unsupported type. */ - return NULL; + fail_reason = "MAC address is not ethernet"; + goto out_fail; } - client_id_buf = g_malloc (addr_len + 1); - client_id_buf[0] = addr_type; - memcpy (&client_id_buf[1], addr_buf, addr_len); - return g_bytes_new_take (client_id_buf, addr_len + 1); + result = dhcp4_get_client_id_mac (hwaddr_bin_buf); + goto out_good; } if (nm_streq (client_id, "stable")) { @@ -7103,10 +7134,30 @@ dhcp4_get_client_id (NMDevice *self, NMConnection *connection) client_id_buf = g_malloc (1 + 15); client_id_buf[0] = 0; memcpy (&client_id_buf[1], buf, 15); - return g_bytes_new_take (client_id_buf, 1 + 15); + result = g_bytes_new_take (client_id_buf, 1 + 15); + goto out_good; } - return nm_dhcp_utils_client_id_string_to_bytes (client_id); + result = nm_dhcp_utils_client_id_string_to_bytes (client_id); + goto out_good; + +out_fail: + nm_assert (fail_reason); + _LOGW (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, + "ipv4.dhcp-client-id: failure to generate client id (%s). Use random client id", + fail_reason); + client_id_buf = g_malloc (1 + 15); + client_id_buf[0] = 0; + nm_utils_random_bytes (&client_id_buf[1], 15); + result = g_bytes_new_take (client_id_buf, 1 + 15); + +out_good: + nm_assert (result); + _LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, + "ipv4.dhcp-client-id: use \"%s\" client ID: %s", + client_id, + (logstr1 = nm_dhcp_utils_duid_to_string (result))); + return result; } static NMActStageReturn @@ -7130,7 +7181,7 @@ dhcp4_start (NMDevice *self) hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); - client_id = dhcp4_get_client_id (self, connection); + client_id = dhcp4_get_client_id (self, connection, hwaddr); g_warn_if_fail (priv->dhcp4.client == NULL); priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), @@ -7764,18 +7815,14 @@ generate_duid_from_machine_id (void) return g_bytes_ref (global_duid); 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, sha256_digest, &len); - g_checksum_free (sum); - } else { - nm_log_warn (LOGD_IP6, "global duid: failed to read " SYSCONFDIR "/machine-id " - "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate " - "DHCPv6 DUID; creating non-persistent random DUID."); - nm_utils_random_bytes (sha256_digest, len); - } + if (!nm_utils_machine_id_parse (machine_id_s, uuid)) + return NULL; + + /* 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, sha256_digest, &len); + g_checksum_free (sum); global_duid = generate_duid_uuid (sha256_digest, len); return g_bytes_ref (global_duid); @@ -7787,12 +7834,12 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcp NMSettingIPConfig *s_ip6; const char *duid; gs_free char *duid_default = NULL; - const char *duid_error = NULL; - GBytes *duid_out = NULL; + const char *duid_error; + GBytes *duid_out; guint8 sha256_digest[32]; gsize len = sizeof (sha256_digest); - NMDhcpDuidEnforce duid_enforce = NM_DHCP_DUID_ENFORCE_NEVER; - + NMDhcpDuidEnforce duid_enforce = NM_DHCP_DUID_ENFORCE_ALWAYS; + gs_free char *logstr1 = NULL; s_ip6 = nm_connection_get_setting_ip6_config (connection); duid = nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6)); @@ -7801,31 +7848,56 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcp duid_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, "ipv6.dhcp-duid", self); duid = duid_default; + if (!duid) + duid = "lease"; } - if (!duid || nm_streq (duid, "lease")) { + if (nm_streq (duid, "lease")) { + duid_enforce = NM_DHCP_DUID_ENFORCE_NEVER; duid_out = generate_duid_from_machine_id (); - goto end; + if (!duid_out) { + duid_error = "failure to read machine-id"; + goto out_fail; + } + goto out_good; } if (!_nm_utils_dhcp_duid_valid (duid, &duid_out)) { duid_error = "invalid duid"; - goto end; + goto out_fail; } if (duid_out) - goto end; + goto out_good; if (NM_IN_STRSET (duid, "ll", "llt")) { if (!hwaddr) { duid_error = "missing link-layer address"; - goto end; + goto out_fail; } if (g_bytes_get_size (hwaddr) != ETH_ALEN) { duid_error = "unsupported link-layer address"; - goto end; + goto out_fail; } - } else if (NM_IN_STRSET (duid, "stable-llt", "stable-ll", "stable-uuid")) { + + if (nm_streq (duid, "ll")) { + duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); + } else { + gint64 time; + + time = nm_utils_secret_key_get_timestamp (); + if (!time) { + duid_error = "cannot retrieve the secret key timestamp"; + goto out_fail; + } + + duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), time); + } + + goto out_good; + } + + if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) { NMUtilsStableType stable_type; const char *stable_id = NULL; guint32 salted_header; @@ -7834,11 +7906,8 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcp gsize secret_key_len; stable_id = _get_stable_id (self, connection, &stable_type); - if (!stable_id) { - nm_assert_not_reached (); - duid_error = "cannot retrieve the stable id"; - goto end; - } + if (!stable_id) + g_return_val_if_reached (NULL); salted_header = htonl (670531087 + stable_type); @@ -7852,68 +7921,61 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcp g_checksum_get_digest (sum, sha256_digest, &len); g_checksum_free (sum); - } - duid_enforce = NM_DHCP_DUID_ENFORCE_ALWAYS; + if (nm_streq (duid, "stable-ll")) { + duid_out = generate_duid_ll (sha256_digest); + } else if (nm_streq (duid, "stable-llt")) { + gint64 time; #define EPOCH_DATETIME_THREE_YEARS (356 * 24 * 3600 * 3) - if (nm_streq0 (duid, "ll")) { - duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); - - } else if (nm_streq0 (duid, "llt")) { - gint64 time; - - time = nm_utils_secret_key_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto end; - } - - duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), time); - } else if (nm_streq0 (duid, "stable-ll")) { - duid_out = generate_duid_ll (sha256_digest); - } else if (nm_streq0 (duid, "stable-llt")) { - gint64 time; + /* We want a variable time between the secret_key timestamp and three years + * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll + * subtract it from the secret_key timestamp. + */ + time = nm_utils_secret_key_get_timestamp (); + if (!time) { + duid_error = "cannot retrieve the secret key timestamp"; + goto out_fail; + } + /* don't use too old timestamps. They cannot be expressed in DUID-LLT and + * would all be truncated to zero. */ + time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS); + time -= (unaligned_read_be32 (&sha256_digest[ETH_ALEN]) % EPOCH_DATETIME_THREE_YEARS); - /* We want a variable time between the secret_key timestamp and three years - * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll - * subtract it from the secret_key timestamp. - */ - time = nm_utils_secret_key_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto end; + duid_out = generate_duid_llt (sha256_digest, time); + } else { + nm_assert (nm_streq (duid, "stable-uuid")); + duid_out = generate_duid_uuid (sha256_digest, len); } - /* don't use too old timestamps. They cannot be expressed in DUID-LLT and - * would all be truncated to zero. */ - time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS); - time -= (unaligned_read_be32 (&sha256_digest[ETH_ALEN]) % EPOCH_DATETIME_THREE_YEARS); - duid_out = generate_duid_llt (sha256_digest, time); - - } else if (nm_streq0 (duid, "stable-uuid")) { - duid_out = generate_duid_uuid (sha256_digest, len); + goto out_good; } - duid_error = "generation failed"; -end: - if (!duid_out) { + g_return_val_if_reached (NULL); + +out_fail: + nm_assert (!duid_out && duid_error); + { guint8 uuid[16]; - if (duid_error) - _LOGW (LOGD_IP6, "duid-gen (%s): %s. Fallback to random DUID-UUID.", duid, duid_error); + _LOGW (LOGD_IP6 | LOGD_DHCP6, + "ipv6.dhcp-duid: failure to generate %s DUID: %s. Fallback to random DUID-UUID.", + duid, duid_error); nm_utils_random_bytes (uuid, sizeof (uuid)); duid_out = generate_duid_uuid (uuid, sizeof (uuid)); } - _LOGD (LOGD_IP6, "DUID gen: '%s' (%s)", - nm_dhcp_utils_duid_to_string (duid_out), - (duid_enforce == NM_DHCP_DUID_ENFORCE_ALWAYS) ? "enforcing" : "fallback"); +out_good: + nm_assert (duid_out); + _LOGD (LOGD_IP6 | LOGD_DHCP6, + "ipv6.dhcp-duid: generate %s DUID '%s' (%s)", + duid, + (logstr1 = nm_dhcp_utils_duid_to_string (duid_out)), + (duid_enforce == NM_DHCP_DUID_ENFORCE_ALWAYS) ? "enforcing" : "fallback"); NM_SET_OUT (out_enforce, duid_enforce); - return duid_out; } @@ -7924,7 +7986,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) NMSettingIPConfig *s_ip6; gs_unref_bytes GBytes *hwaddr = NULL; gs_unref_bytes GBytes *duid = NULL; - NMDhcpDuidEnforce enforce_duid; + NMDhcpDuidEnforce enforce_duid = NM_DHCP_DUID_ENFORCE_NEVER; const NMPlatformIP6Address *ll_addr = NULL; diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 09b465a07e..54ccdb836d 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2521,20 +2521,20 @@ nm_utils_machine_id_read (void) */ 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; + return NULL; contents = g_strstrip (contents); for (i = 0; i < 32; i++) { if (!g_ascii_isxdigit (contents[i])) - return FALSE; + return NULL; if (contents[i] >= 'A' && contents[i] <= 'F') { /* canonicalize to lower-case */ contents[i] = 'a' + (contents[i] - 'A'); } } if (contents[i] != '\0') - return FALSE; + return NULL; return g_steal_pointer (&contents); } |