xref: /dflybsd-src/contrib/dhcpcd/src/duid.c (revision c80c9bba1b2fa2824af94c686145cb7eb7db2cd5)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
4*80aa9461SRoy Marples  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57827cba2SAaron LI  * All rights reserved
67827cba2SAaron LI 
77827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI  * modification, are permitted provided that the following conditions
97827cba2SAaron LI  * are met:
107827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
157827cba2SAaron LI  *
167827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI  * SUCH DAMAGE.
277827cba2SAaron LI  */
287827cba2SAaron LI 
297827cba2SAaron LI #define	UUID_LEN	36
307827cba2SAaron LI #define	DUID_TIME_EPOCH 946684800
317827cba2SAaron LI 
327827cba2SAaron LI #include <sys/param.h>
337827cba2SAaron LI #include <sys/socket.h>
347827cba2SAaron LI #include <sys/types.h>
357827cba2SAaron LI #ifdef BSD
367827cba2SAaron LI #  include <sys/sysctl.h>
377827cba2SAaron LI #endif
387827cba2SAaron LI 
397827cba2SAaron LI #include <arpa/inet.h>
407827cba2SAaron LI 
417827cba2SAaron LI #include <net/if.h>
427827cba2SAaron LI #include <net/if_arp.h>
437827cba2SAaron LI 
447827cba2SAaron LI #include <errno.h>
457827cba2SAaron LI #include <stdint.h>
467827cba2SAaron LI #include <stdio.h>
477827cba2SAaron LI #include <stdlib.h>
487827cba2SAaron LI #include <string.h>
497827cba2SAaron LI #include <time.h>
507827cba2SAaron LI #include <unistd.h>
517827cba2SAaron LI 
527827cba2SAaron LI #include "common.h"
537827cba2SAaron LI #include "dhcpcd.h"
547827cba2SAaron LI #include "duid.h"
557827cba2SAaron LI #include "logerr.h"
567827cba2SAaron LI 
57*80aa9461SRoy Marples /*
58*80aa9461SRoy Marples  * Machine, system or product UUIDs are not guaranteed unique.
59*80aa9461SRoy Marples  * Let's not use them by default.
60*80aa9461SRoy Marples  */
61*80aa9461SRoy Marples #ifdef USE_MACHINE_UUID
627827cba2SAaron LI static size_t
duid_machineuuid(char * uuid,size_t uuid_len)637827cba2SAaron LI duid_machineuuid(char *uuid, size_t uuid_len)
647827cba2SAaron LI {
657827cba2SAaron LI 	int r;
667827cba2SAaron LI 	size_t len = uuid_len;
677827cba2SAaron LI 
687827cba2SAaron LI #if defined(HW_UUID) /* OpenBSD */
697827cba2SAaron LI 	int mib[] = { CTL_HW, HW_UUID };
707827cba2SAaron LI 
717827cba2SAaron LI 	r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0);
727827cba2SAaron LI #elif defined(KERN_HOSTUUID) /* FreeBSD */
737827cba2SAaron LI 	int mib[] = { CTL_KERN, KERN_HOSTUUID };
747827cba2SAaron LI 
757827cba2SAaron LI 	r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0);
767827cba2SAaron LI #elif defined(__NetBSD__)
777827cba2SAaron LI 	r = sysctlbyname("machdep.dmi.system-uuid", uuid, &len, NULL, 0);
787827cba2SAaron LI #elif defined(__linux__)
797827cba2SAaron LI 	FILE *fp;
807827cba2SAaron LI 
817827cba2SAaron LI 	fp = fopen("/sys/class/dmi/id/product_uuid", "r");
827827cba2SAaron LI 	if (fp == NULL)
837827cba2SAaron LI 		return 0;
847827cba2SAaron LI 	if (fgets(uuid, (int)uuid_len, fp) == NULL) {
857827cba2SAaron LI 		fclose(fp);
867827cba2SAaron LI 		return 0;
877827cba2SAaron LI 	}
887827cba2SAaron LI 	len = strlen(uuid) + 1;
897827cba2SAaron LI 	fclose(fp);
908d36e1dfSRoy Marples 	r = len == 1 ? -1 : 0;
917827cba2SAaron LI #else
928d36e1dfSRoy Marples 	UNUSED(uuid);
937827cba2SAaron LI 	r = -1;
947827cba2SAaron LI 	errno = ENOSYS;
957827cba2SAaron LI #endif
967827cba2SAaron LI 
977827cba2SAaron LI 	if (r == -1)
987827cba2SAaron LI 		return 0;
997827cba2SAaron LI 	return len;
1007827cba2SAaron LI }
1017827cba2SAaron LI 
1027827cba2SAaron LI static size_t
duid_make_uuid(uint8_t * d)1037827cba2SAaron LI duid_make_uuid(uint8_t *d)
1047827cba2SAaron LI {
1057827cba2SAaron LI 	uint16_t type = htons(DUID_UUID);
1067827cba2SAaron LI 	char uuid[UUID_LEN + 1];
1077827cba2SAaron LI 	size_t l;
1087827cba2SAaron LI 
1097827cba2SAaron LI 	if (duid_machineuuid(uuid, sizeof(uuid)) != sizeof(uuid))
1107827cba2SAaron LI 		return 0;
1117827cba2SAaron LI 
1127827cba2SAaron LI 	/* All zeros UUID is not valid */
1137827cba2SAaron LI 	if (strcmp("00000000-0000-0000-0000-000000000000", uuid) == 0)
1147827cba2SAaron LI 		return 0;
1157827cba2SAaron LI 
1167827cba2SAaron LI 	memcpy(d, &type, sizeof(type));
1177827cba2SAaron LI 	l = sizeof(type);
1187827cba2SAaron LI 	d += sizeof(type);
1197827cba2SAaron LI 	l += hwaddr_aton(d, uuid);
1207827cba2SAaron LI 	return l;
1217827cba2SAaron LI }
122*80aa9461SRoy Marples #endif
1237827cba2SAaron LI 
1246e63cc1fSRoy Marples size_t
duid_make(void * d,const struct interface * ifp,uint16_t type)1256e63cc1fSRoy Marples duid_make(void *d, const struct interface *ifp, uint16_t type)
1267827cba2SAaron LI {
1277827cba2SAaron LI 	uint8_t *p;
1287827cba2SAaron LI 	uint16_t u16;
1297827cba2SAaron LI 	time_t t;
1307827cba2SAaron LI 	uint32_t u32;
1317827cba2SAaron LI 
1326e63cc1fSRoy Marples 	if (ifp->hwlen == 0)
1336e63cc1fSRoy Marples 		return 0;
1346e63cc1fSRoy Marples 
1357827cba2SAaron LI 	p = d;
1367827cba2SAaron LI 	u16 = htons(type);
1376e63cc1fSRoy Marples 	memcpy(p, &u16, sizeof(u16));
1386e63cc1fSRoy Marples 	p += sizeof(u16);
139d4fb1e02SRoy Marples 	u16 = htons(ifp->hwtype);
1406e63cc1fSRoy Marples 	memcpy(p, &u16, sizeof(u16));
1416e63cc1fSRoy Marples 	p += sizeof(u16);
1427827cba2SAaron LI 	if (type == DUID_LLT) {
1437827cba2SAaron LI 		/* time returns seconds from jan 1 1970, but DUID-LLT is
1447827cba2SAaron LI 		 * seconds from jan 1 2000 modulo 2^32 */
1457827cba2SAaron LI 		t = time(NULL) - DUID_TIME_EPOCH;
1467827cba2SAaron LI 		u32 = htonl((uint32_t)t & 0xffffffff);
1476e63cc1fSRoy Marples 		memcpy(p, &u32, sizeof(u32));
1486e63cc1fSRoy Marples 		p += sizeof(u32);
1497827cba2SAaron LI 	}
1507827cba2SAaron LI 	/* Finally, add the MAC address of the interface */
1517827cba2SAaron LI 	memcpy(p, ifp->hwaddr, ifp->hwlen);
1527827cba2SAaron LI 	p += ifp->hwlen;
1536e63cc1fSRoy Marples 	return (size_t)(p - (uint8_t *)d);
1547827cba2SAaron LI }
1557827cba2SAaron LI 
1567827cba2SAaron LI #define DUID_STRLEN DUID_LEN * 3
1577827cba2SAaron LI static size_t
duid_get(struct dhcpcd_ctx * ctx,const struct interface * ifp)158d4fb1e02SRoy Marples duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
1597827cba2SAaron LI {
1607827cba2SAaron LI 	uint8_t *data;
161d4fb1e02SRoy Marples 	size_t len, slen;
1627827cba2SAaron LI 	char line[DUID_STRLEN];
1637827cba2SAaron LI 	const struct interface *ifp2;
1647827cba2SAaron LI 
1657827cba2SAaron LI 	/* If we already have a DUID then use it as it's never supposed
1667827cba2SAaron LI 	 * to change once we have one even if the interfaces do */
167d4fb1e02SRoy Marples 	if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) {
1687827cba2SAaron LI 		if (len <= DUID_LEN) {
169d4fb1e02SRoy Marples 			ctx->duid = data;
1707827cba2SAaron LI 			return len;
1717827cba2SAaron LI 		}
1727827cba2SAaron LI 		logerrx("DUID too big (max %u): %s", DUID_LEN, DUID);
1737827cba2SAaron LI 		/* Keep the buffer, will assign below. */
1747827cba2SAaron LI 	} else {
1757827cba2SAaron LI 		if (errno != ENOENT)
1767827cba2SAaron LI 			logerr("%s", DUID);
1777827cba2SAaron LI 		if ((data = malloc(DUID_LEN)) == NULL) {
1787827cba2SAaron LI 			logerr(__func__);
1797827cba2SAaron LI 			return 0;
1807827cba2SAaron LI 		}
1817827cba2SAaron LI 	}
1827827cba2SAaron LI 
1837827cba2SAaron LI 	/* No file? OK, lets make one based the machines UUID */
184d4fb1e02SRoy Marples 	if (ifp == NULL) {
185*80aa9461SRoy Marples #ifdef USE_MACHINE_UUID
186a0d9933aSRoy Marples 		if (ctx->duid_type != DUID_DEFAULT &&
187a0d9933aSRoy Marples 		    ctx->duid_type != DUID_UUID)
18893ddca5eSRoy Marples 			len = 0;
18993ddca5eSRoy Marples 		else
1907827cba2SAaron LI 			len = duid_make_uuid(data);
191d4fb1e02SRoy Marples 		if (len == 0)
192d4fb1e02SRoy Marples 			free(data);
193d4fb1e02SRoy Marples 		else
194d4fb1e02SRoy Marples 			ctx->duid = data;
1957827cba2SAaron LI 		return len;
196*80aa9461SRoy Marples #else
197*80aa9461SRoy Marples 		free(data);
198*80aa9461SRoy Marples 		return 0;
199*80aa9461SRoy Marples #endif
200d4fb1e02SRoy Marples 	}
201d4fb1e02SRoy Marples 
202d4fb1e02SRoy Marples 	/* Regardless of what happens we will create a DUID to use. */
203d4fb1e02SRoy Marples 	ctx->duid = data;
2047827cba2SAaron LI 
2057827cba2SAaron LI 	/* No UUID? OK, lets make one based on our interface */
2068d36e1dfSRoy Marples 	if (ifp->hwlen == 0) {
2078d36e1dfSRoy Marples 		logwarnx("%s: does not have hardware address", ifp->name);
2087827cba2SAaron LI 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
2098d36e1dfSRoy Marples 			if (ifp2->hwlen != 0)
2107827cba2SAaron LI 				break;
2117827cba2SAaron LI 		}
2127827cba2SAaron LI 		if (ifp2) {
2137827cba2SAaron LI 			ifp = ifp2;
2147827cba2SAaron LI 			logwarnx("picked interface %s to generate a DUID",
2157827cba2SAaron LI 			    ifp->name);
2167827cba2SAaron LI 		} else {
217a0d9933aSRoy Marples 			if (ctx->duid_type != DUID_LL)
2187827cba2SAaron LI 				logwarnx("no interfaces have a fixed hardware "
2197827cba2SAaron LI 				    "address");
2207827cba2SAaron LI 			return duid_make(data, ifp, DUID_LL);
2217827cba2SAaron LI 		}
2227827cba2SAaron LI 	}
2237827cba2SAaron LI 
224a0d9933aSRoy Marples 	len = duid_make(data, ifp,
225a0d9933aSRoy Marples 	    ctx->duid_type == DUID_LL ? DUID_LL : DUID_LLT);
226d4fb1e02SRoy Marples 	hwaddr_ntoa(data, len, line, sizeof(line));
227d4fb1e02SRoy Marples 	slen = strlen(line);
228d4fb1e02SRoy Marples 	if (slen < sizeof(line) - 2) {
229d4fb1e02SRoy Marples 		line[slen++] = '\n';
230d4fb1e02SRoy Marples 		line[slen] = '\0';
231d4fb1e02SRoy Marples 	}
232d4fb1e02SRoy Marples 	if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) {
233d4fb1e02SRoy Marples 		logerr("%s: cannot write duid", __func__);
234a0d9933aSRoy Marples 		if (ctx->duid_type != DUID_LL)
2357827cba2SAaron LI 			return duid_make(data, ifp, DUID_LL);
2367827cba2SAaron LI 	}
2377827cba2SAaron LI 	return len;
2387827cba2SAaron LI }
2397827cba2SAaron LI 
240d4fb1e02SRoy Marples size_t
duid_init(struct dhcpcd_ctx * ctx,const struct interface * ifp)241d4fb1e02SRoy Marples duid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp)
2427827cba2SAaron LI {
2437827cba2SAaron LI 
244d4fb1e02SRoy Marples 	if (ctx->duid == NULL)
245d4fb1e02SRoy Marples 		ctx->duid_len = duid_get(ctx, ifp);
246d4fb1e02SRoy Marples 	return ctx->duid_len;
2477827cba2SAaron LI }
248