diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 8f41df856..930b0ab60 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -8434,15 +8434,8 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole if (nm_streq (duid, "ll")) { duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); } else { - gint64 time; - - time = nm_utils_host_id_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); + duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), + nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND); } goto out_good; @@ -8492,11 +8485,8 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll * subtract it from the host_id timestamp. */ - time = nm_utils_host_id_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto out_fail; - } + time = nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND; + /* 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); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 817293345..91da1d521 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2525,6 +2525,49 @@ nm_utils_machine_id_is_fake (void) #define SECRET_KEY_V2_PREFIX "nm-v2:" #define SECRET_KEY_FILE NMSTATEDIR"/secret_key" +static gboolean +_host_id_read_timestamp (gboolean use_secret_key_file, + const guint8 *host_id, + gsize host_id_len, + gint64 *out_timestamp_ns) +{ + struct stat st; + gint64 now; + guint64 v; + + if ( use_secret_key_file + && stat (SECRET_KEY_FILE, &st) == 0) { + /* don't check for overflow or timestamps in the future. We get whatever + * (bogus) date is on the file. */ + *out_timestamp_ns = (st.st_mtim.tv_sec * NM_UTILS_NS_PER_SECOND) + st.st_mtim.tv_nsec; + return TRUE; + } + + /* generate a fake timestamp based on the host-id. + * + * This really should never happen under normal circumstances. We already + * are in a code path, where the system has a problem (unable to get good randomness + * and/or can't access the secret_key). In such a scenario, a fake timestamp is the + * least of our problems. + * + * At least, generate something sensible so we don't have to worry about the + * timestamp. It is wrong to worry about using a fake timestamp (which is tied to + * the secret_key) if we are unable to access the secret_key file in the first place. + * + * Pick a random timestamp from the past two years. Yes, this timestamp + * is not stable accross restarts, but apparently neither is the host-id + * nor the secret_key itself. */ + +#define EPOCH_TWO_YEARS (G_GINT64_CONSTANT (2 * 365 * 24 * 3600) * NM_UTILS_NS_PER_SECOND) + + v = nm_hash_siphash42 (1156657133u, host_id, host_id_len); + + now = time (NULL); + *out_timestamp_ns = NM_MAX ((gint64) 1, + (now * NM_UTILS_NS_PER_SECOND) - ((gint64) (v % ((guint64) (EPOCH_TWO_YEARS))))); + return FALSE; +} + static const guint8 * _host_id_hash_v2 (const guint8 *seed_arr, gsize seed_len, @@ -2672,7 +2715,9 @@ out: typedef struct { guint8 *host_id; gsize host_id_len; + gint64 timestamp_ns; bool is_good:1; + bool timestamp_is_good:1; } HostIdData; static const HostIdData * @@ -2692,6 +2737,15 @@ again: host_id_data.is_good = _host_id_read (&host_id_data.host_id, &host_id_data.host_id_len); + + host_id_data.timestamp_is_good = _host_id_read_timestamp (host_id_data.is_good, + host_id_data.host_id, + host_id_data.host_id_len, + &host_id_data.timestamp_ns); + if ( !host_id_data.timestamp_is_good + && host_id_data.is_good) + nm_log_warn (LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)"); + host_id = &host_id_data; g_atomic_pointer_set (&host_id_static, host_id); g_once_init_leave (&init_value, 1); @@ -2728,17 +2782,9 @@ nm_utils_host_id_get (const guint8 **out_host_id, } gint64 -nm_utils_host_id_get_timestamp (void) +nm_utils_host_id_get_timestamp_ns (void) { - struct stat stat_buf; - - if (!_host_id_get ()->is_good) - return 0; - - if (stat (SECRET_KEY_FILE, &stat_buf) != 0) - return 0; - - return stat_buf.st_mtim.tv_sec; + return _host_id_get ()->timestamp_ns; } /*****************************************************************************/ diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index dd09f3213..a01cac1c0 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -280,7 +280,7 @@ const struct _NMUuid *nm_utils_boot_id_bin (void); gboolean nm_utils_host_id_get (const guint8 **out_host_id, gsize *out_host_id_len); -gint64 nm_utils_host_id_get_timestamp (void); +gint64 nm_utils_host_id_get_timestamp_ns (void); /* IPv6 Interface Identifier helpers */