core: cleanup generating DUID in nm-device.c

- use NMUuid type where appropriate.

- no error handling for generate_duid_from_machine_id().
  It cannot fail anymore.

- add thread-safety to generate_duid_from_machine_id() with
  double-checked locking.

- use unions for converting the sha256 digest to the target
  type.

(cherry picked from commit 50121ee028)
This commit is contained in:
Thomas Haller 2018-10-31 11:18:46 +01:00
parent 10e280686e
commit b04c033007

View file

@ -7533,11 +7533,10 @@ dhcp4_get_client_id (NMDevice *self,
}
if (nm_streq (client_id, "stable")) {
nm_auto_free_checksum GChecksum *sum = NULL;
guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA1];
NMUtilsStableType stable_type;
const char *stable_id;
GChecksum *sum;
guint8 buf[20];
gsize buf_size;
guint32 salted_header;
const guint8 *secret_key;
gsize secret_key_len;
@ -7551,20 +7550,14 @@ dhcp4_get_client_id (NMDevice *self,
nm_utils_secret_key_get (&secret_key, &secret_key_len);
sum = g_checksum_new (G_CHECKSUM_SHA1);
g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header));
g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1);
g_checksum_update (sum, (const guchar *) secret_key, secret_key_len);
buf_size = sizeof (buf);
g_checksum_get_digest (sum, buf, &buf_size);
nm_assert (buf_size == sizeof (buf));
g_checksum_free (sum);
nm_utils_checksum_get_digest (sum, digest);
client_id_buf = g_malloc (1 + 15);
client_id_buf[0] = 0;
memcpy (&client_id_buf[1], buf, 15);
memcpy (&client_id_buf[1], digest, 15);
result = g_bytes_new_take (client_id_buf, 1 + 15);
goto out_good;
}
@ -8186,6 +8179,8 @@ dhcp6_prefix_delegated (NMDhcpClient *client,
g_signal_emit (self, signals[IP6_PREFIX_DELEGATED], 0, prefix);
}
/*****************************************************************************/
/* RFC 3315 defines the epoch for the DUID-LLT time field on Jan 1st 2000. */
#define EPOCH_DATETIME_200001010000 946684800
@ -8225,14 +8220,12 @@ generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */)
}
static GBytes *
generate_duid_uuid (guint8 *data, gsize data_len)
generate_duid_uuid (const NMUuid *uuid)
{
const guint16 duid_type = g_htons (4);
const int DUID_SIZE = 18;
const guint16 duid_type = htons (4);
guint8 *duid_buffer;
nm_assert (data);
nm_assert (data_len >= 16);
nm_assert (uuid);
/* Generate a DHCP Unique Identifier for DHCPv6 using the
* DUID-UUID method (see RFC 6355 section 4). Format is:
@ -8240,41 +8233,47 @@ generate_duid_uuid (guint8 *data, gsize data_len)
* u16: type (DUID-UUID = 4)
* u8[16]: UUID bytes
*/
duid_buffer = g_malloc (DUID_SIZE);
G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2);
G_STATIC_ASSERT_EXPR (sizeof (*uuid) == 16);
duid_buffer = g_malloc (18);
memcpy (&duid_buffer[0], &duid_type, 2);
/* UUID is 128 bits, we just take the first 128 bits
* (regardless of data size) as the DUID-UUID.
*/
memcpy (&duid_buffer[2], data, 16);
return g_bytes_new_take (duid_buffer, DUID_SIZE);
memcpy (&duid_buffer[2], uuid, 16);
return g_bytes_new_take (duid_buffer, 18);
}
static GBytes *
generate_duid_from_machine_id (void)
{
const NMUuid *uuid;
GChecksum *sum;
guint8 sha256_digest[32];
gsize len = sizeof (sha256_digest);
static GBytes *global_duid = NULL;
static GBytes *volatile global_duid = NULL;
GBytes *p;
if (global_duid)
return g_bytes_ref (global_duid);
again:
p = g_atomic_pointer_get (&global_duid);
if (G_UNLIKELY (!p)) {
nm_auto_free_checksum GChecksum *sum = NULL;
const NMUuid *machine_id;
union {
guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256];
NMUuid uuid;
} digest;
uuid = nm_utils_machine_id_bin ();
machine_id = nm_utils_machine_id_bin ();
/* 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);
/* 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 *) machine_id, sizeof (*machine_id));
nm_utils_checksum_get_digest (sum, digest.sha256);
global_duid = generate_duid_uuid (sha256_digest, len);
return g_bytes_ref (global_duid);
G_STATIC_ASSERT_EXPR (sizeof (digest.sha256) > sizeof (digest.uuid));
p = generate_duid_uuid (&digest.uuid);
if (!g_atomic_pointer_compare_and_exchange (&global_duid, NULL, p)) {
g_bytes_unref (p);
goto again;
}
}
return g_bytes_ref (p);
}
static GBytes *
@ -8285,8 +8284,6 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
gs_free char *duid_default = NULL;
const char *duid_error;
GBytes *duid_out;
guint8 sha256_digest[32];
gsize len = sizeof (sha256_digest);
gboolean duid_enforce = TRUE;
gs_free char *logstr1 = NULL;
@ -8304,10 +8301,6 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
if (nm_streq (duid, "lease")) {
duid_enforce = FALSE;
duid_out = generate_duid_from_machine_id ();
if (!duid_out) {
duid_error = "failure to read machine-id";
goto out_fail;
}
goto out_good;
}
@ -8347,12 +8340,21 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
}
if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) {
nm_auto_free_checksum GChecksum *sum = NULL;
NMUtilsStableType stable_type;
const char *stable_id = NULL;
guint32 salted_header;
GChecksum *sum;
const guint8 *secret_key;
gsize secret_key_len;
union {
guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256];
guint8 hwaddr[ETH_ALEN];
NMUuid uuid;
struct _nm_packed {
guint8 hwaddr[ETH_ALEN];
guint32 timestamp;
} llt;
} digest;
stable_id = _get_stable_id (self, connection, &stable_type);
if (!stable_id)
@ -8363,16 +8365,15 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
nm_utils_secret_key_get (&secret_key, &secret_key_len);
sum = g_checksum_new (G_CHECKSUM_SHA256);
g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header));
g_checksum_update (sum, (const guchar *) stable_id, -1);
g_checksum_update (sum, (const guchar *) secret_key, secret_key_len);
nm_utils_checksum_get_digest (sum, digest.sha256);
g_checksum_get_digest (sum, sha256_digest, &len);
g_checksum_free (sum);
G_STATIC_ASSERT_EXPR (sizeof (digest) == sizeof (digest.sha256));
if (nm_streq (duid, "stable-ll")) {
duid_out = generate_duid_ll (sha256_digest);
duid_out = generate_duid_ll (digest.hwaddr);
} else if (nm_streq (duid, "stable-llt")) {
gint64 time;
@ -8390,12 +8391,12 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
/* 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);
time -= unaligned_read_be32 (&digest.llt.timestamp) % EPOCH_DATETIME_THREE_YEARS;
duid_out = generate_duid_llt (sha256_digest, time);
duid_out = generate_duid_llt (digest.llt.hwaddr, time);
} else {
nm_assert (nm_streq (duid, "stable-uuid"));
duid_out = generate_duid_uuid (sha256_digest, len);
duid_out = generate_duid_uuid (&digest.uuid);
}
goto out_good;
@ -8406,14 +8407,14 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole
out_fail:
nm_assert (!duid_out && duid_error);
{
guint8 uuid[16];
NMUuid uuid;
_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));
nm_utils_random_bytes (&uuid, sizeof (uuid));
duid_out = generate_duid_uuid (&uuid);
}
out_good:
@ -8428,6 +8429,8 @@ out_good:
return duid_out;
}
/*****************************************************************************/
static gboolean
dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
{