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:
Dan Williams
2013-02-04 16:45:12 -06:00
parent 3b2556ae38
commit 140ebbbf89
3 changed files with 60 additions and 65 deletions

View File

@@ -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)\" \

View File

@@ -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 *

View File

@@ -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,