dhcp: generate DUID-UUID from /etc/machine-id (bgo #691885)
DHCPv6 RFCs require the DUID to be constant over time and to be used as a permanent identifiers of the machine. This precludes using a network interface MAC address since the MAC address may change, and there may be more than one network interface installed. Storing the DUID causes problems when an OS image is cloned between virtual or physical machines because then the saved DUID is no longer unique. To fix all these issues, generate the DUID from the machine's hashed machine-id, which is already a unique identifier of the machine and will always be present because dbus requires it, and NM requires dbus. It is assumed administrators will change the machine-id when cloning an OS image; thus they only have to update one file, not two (machine-id and the stored DUID). Administrators may still override the machine-id-derived DUID by setting a DUID in the default dhclient config file or the interface-specific dhclient config files. dhclient no longer saves a generated DUID to the config files, because the default DUID will always be regenerated from the machine-id on startup and thus always stable.
This commit is contained in:
@@ -52,6 +52,7 @@ libdhcp_manager_la_SOURCES = \
|
||||
libdhcp_manager_la_CPPFLAGS = \
|
||||
$(DBUS_CFLAGS) \
|
||||
$(GLIB_CFLAGS) \
|
||||
-DSYSCONFDIR=\"$(sysconfdir)\" \
|
||||
-DLIBEXECDIR=\"$(libexecdir)\" \
|
||||
-DLOCALSTATEDIR=\"$(localstatedir)\" \
|
||||
-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
|
||||
|
@@ -25,10 +25,8 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include "nm-utils.h"
|
||||
#include "nm-logging.h"
|
||||
@@ -325,38 +323,58 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self,
|
||||
return priv->pid ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
struct duid_header {
|
||||
uint16_t duid_type;
|
||||
uint16_t hw_type;
|
||||
uint32_t time;
|
||||
/* link-layer address follows */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define DUID_TIME_EPOCH 946684800
|
||||
|
||||
/* Generate a DHCP Unique Identifier for DHCPv6 using the
|
||||
* DUID-LLT method (see RFC 3315 s9.2), following Debian's
|
||||
* netcfg DUID-LL generation method.
|
||||
*/
|
||||
static GByteArray *
|
||||
generate_duid (const GByteArray *hwaddr)
|
||||
generate_duid_from_machine_id (void)
|
||||
{
|
||||
GByteArray *duid;
|
||||
struct duid_header p;
|
||||
int arptype;
|
||||
char *contents = NULL;
|
||||
GError *error = NULL;
|
||||
GChecksum *sum;
|
||||
guint8 buffer[32]; /* SHA256 digest size */
|
||||
gsize sumlen = sizeof (buffer);
|
||||
const guint16 duid_type = g_htons (4);
|
||||
uuid_t uuid;
|
||||
int ret;
|
||||
|
||||
g_return_val_if_fail (hwaddr != NULL, NULL);
|
||||
/* Get the machine ID from /etc/machine-id; it's always in /etc no matter
|
||||
* where our configured SYSCONFDIR is.
|
||||
*/
|
||||
if (!g_file_get_contents ("/etc/machine-id", &contents, NULL, &error)) {
|
||||
nm_log_warn (LOGD_DHCP6, "Failed to read " SYSCONFDIR "/machine-id to generate DHCPv6 DUID: (%d) %s",
|
||||
error ? error->code : -1,
|
||||
error ? error->message : "(unknown)");
|
||||
g_clear_error (&error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset (&p, 0, sizeof (p));
|
||||
p.duid_type = g_htons(1);
|
||||
arptype = nm_utils_hwaddr_type (hwaddr->len);
|
||||
g_assert (arptype == ARPHRD_ETHER || arptype == ARPHRD_INFINIBAND);
|
||||
p.hw_type = g_htons (arptype);
|
||||
p.time = g_htonl (time (NULL) - DUID_TIME_EPOCH);
|
||||
contents = g_strstrip (contents);
|
||||
ret = uuid_parse (contents, uuid);
|
||||
g_free (contents);
|
||||
|
||||
duid = g_byte_array_sized_new (sizeof (p) + hwaddr->len);
|
||||
g_byte_array_append (duid, (const guint8 *) &p, sizeof (p));
|
||||
g_byte_array_append (duid, hwaddr->data, hwaddr->len);
|
||||
if (ret != 0) {
|
||||
nm_log_warn (LOGD_DHCP6, "Failed to parse " SYSCONFDIR "/machine-id to generate DHCPv6 DUID.");
|
||||
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, buffer, &sumlen);
|
||||
g_checksum_free (sum);
|
||||
|
||||
/* Generate a DHCP Unique Identifier for DHCPv6 using the
|
||||
* DUID-UUID method (see RFC 6355 section 4). Format is:
|
||||
*
|
||||
* u16: type (DUID-UUID = 4)
|
||||
* u8[16]: UUID bytes
|
||||
*/
|
||||
duid = g_byte_array_sized_new (18);
|
||||
g_byte_array_append (duid, (guint8 *) &duid_type, sizeof (duid_type));
|
||||
|
||||
/* Since SHA256 is 256 bits, but UUID is 128 bits, we just take the first
|
||||
* 128 bits of the SHA256 as the DUID-UUID.
|
||||
*/
|
||||
g_byte_array_append (duid, buffer, 16);
|
||||
|
||||
return duid;
|
||||
}
|
||||
@@ -364,8 +382,18 @@ generate_duid (const GByteArray *hwaddr)
|
||||
static GByteArray *
|
||||
get_duid (NMDHCPClient *self)
|
||||
{
|
||||
/* generate a default DUID */
|
||||
return generate_duid (NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr);
|
||||
static GByteArray *duid = NULL;
|
||||
GByteArray *copy = NULL;
|
||||
|
||||
if (G_UNLIKELY (duid == NULL))
|
||||
duid = generate_duid_from_machine_id ();
|
||||
|
||||
if (G_LIKELY (duid)) {
|
||||
copy = g_byte_array_sized_new (duid->len);
|
||||
g_byte_array_append (copy, duid->data, duid->len);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static char *
|
||||
|
@@ -53,8 +53,6 @@ typedef struct {
|
||||
const char *def_leasefile;
|
||||
char *lease_file;
|
||||
char *pid_file;
|
||||
gboolean lease_duid_found;
|
||||
gboolean default_duid_found;
|
||||
} NMDHCPDhclientPrivate;
|
||||
|
||||
const char *
|
||||
@@ -499,36 +497,6 @@ dhclient_start (NMDHCPClient *client,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ipv6 && duid) {
|
||||
char *escaped = NULL;
|
||||
|
||||
escaped = nm_dhcp_dhclient_escape_duid (duid);
|
||||
|
||||
/* Save the generated DUID into the default leasefile for persistent storage */
|
||||
if (!priv->default_duid_found) {
|
||||
nm_log_dbg (LOGD_DHCP6, "Saving DHCPv6 DUID '%s' to leasefile %s",
|
||||
escaped, priv->def_leasefile);
|
||||
if (!nm_dhcp_dhclient_save_duid (priv->def_leasefile, escaped, &error)) {
|
||||
g_assert (error);
|
||||
nm_log_warn (LOGD_DHCP6, "Could not save DUID: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the DUID to the network-specific leasefile so it'll actually get used */
|
||||
if (!priv->lease_duid_found) {
|
||||
nm_log_dbg (LOGD_DHCP6, "Saving DHCPv6 DUID '%s' to leasefile %s",
|
||||
escaped, priv->lease_file);
|
||||
if (!nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &error)) {
|
||||
g_assert (error);
|
||||
nm_log_warn (LOGD_DHCP6, "Could not save DUID: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (escaped);
|
||||
}
|
||||
|
||||
argv = g_ptr_array_new ();
|
||||
g_ptr_array_add (argv, (gpointer) priv->path);
|
||||
|
||||
@@ -675,7 +643,6 @@ get_duid (NMDHCPClient *client)
|
||||
TRUE);
|
||||
nm_log_dbg (LOGD_DHCP, "Looking for DHCPv6 DUID in '%s'.", leasefile);
|
||||
duid = nm_dhcp_dhclient_read_duid (leasefile, &error);
|
||||
priv->lease_duid_found = !!duid;
|
||||
g_free (leasefile);
|
||||
|
||||
if (error) {
|
||||
@@ -690,7 +657,6 @@ get_duid (NMDHCPClient *client)
|
||||
/* Otherwise read the default machine-wide DUID */
|
||||
nm_log_dbg (LOGD_DHCP, "Looking for default DHCPv6 DUID in '%s'.", priv->def_leasefile);
|
||||
duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error);
|
||||
priv->default_duid_found = !!duid;
|
||||
if (error) {
|
||||
nm_log_warn (LOGD_DHCP, "Failed to read leasefile '%s': (%d) %s",
|
||||
priv->def_leasefile,
|
||||
|
Reference in New Issue
Block a user