summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-12-12 11:04:12 +0100
committerThomas Haller <thaller@redhat.com>2018-12-12 14:03:35 +0100
commita68d027ba4537a6e30272e39a9f7927bcdca0eee (patch)
tree30ebef90c5d0dad23b28bd8639328c0bc5eac105
parente9887d4816805d939e91215ad137b94ffa3c2c5d (diff)
downloadNetworkManager-a68d027ba4537a6e30272e39a9f7927bcdca0eee.tar.gz
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file. Under normal circumstances, reading the timestamp should never fail, and reading it multiple times should always yield the same result. If we unexpectedly fail to read the timestamp from the file we want: - log a warning, so that the user can find out what's wrong. But do so only once. - we don't want to handle errors or fail operation due to a missing timestamp. Remember, it's not supposed to ever fail, and if it does, just log a warning and proceed with a fake timestamp instead. In that case something is wrong, but using a non-stable, fake timestamp is the least of the problems here. We already have a stable identifier (the host-id) which we can use to generate a fake timestamp. Use it. In case the user would replace the secret_key file, we also don't want that accessing nm_utils_host_id_get_timestamp*() yields different results. It's not implemented (nor necessary) to support reloading a different timestamp. Hence, nm_utils_host_id_get_timestamp() should memoize the value and ensure that it never changes.
-rw-r--r--src/devices/nm-device.c18
-rw-r--r--src/nm-core-utils.c66
-rw-r--r--src/nm-core-utils.h2
3 files changed, 61 insertions, 25 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 8f41df8566..930b0ab603 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 8172933457..91da1d5219 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 dd09f3213b..a01cac1c0c 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 */