summaryrefslogtreecommitdiff
path: root/src/NetworkManagerUtils.c
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2015-10-03 19:44:27 +0200
committerLubomir Rintel <lkundrak@v3.sk>2015-11-02 20:27:36 +0100
commite603c86926ce48a7dd53b892fe85d506e6322378 (patch)
tree85e0e069ba9443b83ee850835021a525e71a6faf /src/NetworkManagerUtils.c
parentf85728ecff824b1fece43aba51d8171db2766ea2 (diff)
core: add support for RFC7217 stable privacy addressing
RFC7217 introduces an alternative mechanism for creating addresses during stateless IPv6 address configuration. It's supposed to create addresses whose host part stays stable in a particular network but changes when the hosts enters another network to mitigate possibility of tracking the host movement. It can be used alongside RFC 4941 privacy extensions (temporary addresses) and replaces the use of RFC 4862 interface identifiers. The address creation mode is controlld by ip6.addr_gen_mode property (ADDR_GEN_MODE in ifcfg-rh), with values of "stable-privacy" and "eui-64", defaulting to "eui-64" if unspecified. The host part of an address is computed by hashing a system-specific secret salted with various stable values that identify the connection with a secure hash algorithm: RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) For NetworkManager we use these parameters: * F() SHA256 hash function. * Prefix This is a network part of the /64 address * Net_Iface We use the interface name (e.g. "eth0"). This ensures the address won't change with the change of interface hardware. * Network_ID We use the connection UUID here. This ensures the salt is different for wireless networks with a different SSID as suggested by RFC7217. * DAD_Counter A per-address counter that increases with each DAD failure. * secret_key We store the secret key in /var/lib/NetworkManager/secret_key. If it's shorter than 128 bits then it's rejected. If the file is not present we initialize it by fetching 256 pseudo-random bits from /dev/urandom on first use. Duplicate address detection uses IDGEN_RETRIES = 3 and does not utilize the IDGEN_DELAY delay (despite it SHOULD). This is for ease of implementation and may change in future. Neither parameter is currently configurable.
Diffstat (limited to 'src/NetworkManagerUtils.c')
-rw-r--r--src/NetworkManagerUtils.c121
1 files changed, 120 insertions, 1 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index deab2a5869..cd8d717e96 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -29,6 +29,7 @@
#include <resolv.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include <linux/if.h>
#include <linux/if_infiniband.h>
@@ -3213,7 +3214,6 @@ nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
}
return FALSE;
}
-
void
nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
const NMUtilsIPv6IfaceId iid)
@@ -3228,6 +3228,125 @@ nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
memcpy (iid, addr->s6_addr + 8, 8);
}
+static gboolean
+_set_stable_privacy (struct in6_addr *addr,
+ const char *ifname,
+ const char *uuid,
+ guint dad_counter,
+ gchar *secret_key,
+ gsize key_len,
+ GError **error)
+{
+ GChecksum *sum;
+ guint8 digest[32];
+ guint32 tmp[2];
+ gsize len = sizeof (digest);
+
+ g_return_val_if_fail (key_len, FALSE);
+
+ /* Documentation suggests that this can fail.
+ * Maybe in case of a missing algorithm in crypto library? */
+ sum = g_checksum_new (G_CHECKSUM_SHA256);
+ if (!sum) {
+ g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+ "Can't create a SHA256 hash");
+ return FALSE;
+ }
+
+ key_len = CLAMP (key_len, 0, G_MAXUINT32);
+
+ g_checksum_update (sum, addr->s6_addr, 8);
+ g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1);
+ if (!uuid)
+ uuid = "";
+ g_checksum_update (sum, (const guchar *) uuid, strlen (uuid) + 1);
+ tmp[0] = htonl (dad_counter);
+ tmp[1] = htonl (key_len);
+ g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp));
+ g_checksum_update (sum, (const guchar *) secret_key, key_len);
+
+ g_checksum_get_digest (sum, digest, &len);
+ g_checksum_free (sum);
+
+ g_return_val_if_fail (len == 32, FALSE);
+
+ memcpy (addr->s6_addr + 8, &digest[0], 8);
+
+ return TRUE;
+}
+
+#define RFC7217_IDGEN_RETRIES 3
+/**
+ * nm_utils_ipv6_addr_set_stable_privacy:
+ *
+ * Extend the address prefix with an interface identifier using the
+ * RFC 7217 Stable Privacy mechanism.
+ *
+ * Returns: %TRUE on success, %FALSE if the address could not be generated.
+ */
+gboolean
+nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
+ const char *ifname,
+ const char *uuid,
+ guint dad_counter,
+ GError **error)
+{
+ gchar *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,
+ "Too many DAD collisions");
+ 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) {
+ 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);
+ }
+
+ g_free (secret_key);
+ return success;
+}
+
/**
* nm_utils_setpgid:
* @unused: unused