1/* SPDX-License-Identifier: BSD-2-Clause */ 2/* 3 * dhcpcd - DHCP client daemon 4 * Copyright (c) 2006-2023 Roy Marples <roy@marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#define UUID_LEN 36 30#define DUID_TIME_EPOCH 946684800 31 32#include <sys/param.h> 33#include <sys/socket.h> 34#include <sys/types.h> 35#ifdef BSD 36# include <sys/sysctl.h> 37#endif 38 39#include <arpa/inet.h> 40 41#include <net/if.h> 42#include <net/if_arp.h> 43 44#include <errno.h> 45#include <stdint.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <time.h> 50#include <unistd.h> 51 52#include "common.h" 53#include "dhcpcd.h" 54#include "duid.h" 55#include "logerr.h" 56 57/* 58 * Machine, system or product UUIDs are not guaranteed unique. 59 * Let's not use them by default. 60 */ 61#ifdef USE_MACHINE_UUID 62static size_t 63duid_machineuuid(char *uuid, size_t uuid_len) 64{ 65 int r; 66 size_t len = uuid_len; 67 68#if defined(HW_UUID) /* OpenBSD */ 69 int mib[] = { CTL_HW, HW_UUID }; 70 71 r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0); 72#elif defined(KERN_HOSTUUID) /* FreeBSD */ 73 int mib[] = { CTL_KERN, KERN_HOSTUUID }; 74 75 r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0); 76#elif defined(__NetBSD__) 77 r = sysctlbyname("machdep.dmi.system-uuid", uuid, &len, NULL, 0); 78#elif defined(__linux__) 79 FILE *fp; 80 81 fp = fopen("/sys/class/dmi/id/product_uuid", "r"); 82 if (fp == NULL) 83 return 0; 84 if (fgets(uuid, (int)uuid_len, fp) == NULL) { 85 fclose(fp); 86 return 0; 87 } 88 len = strlen(uuid) + 1; 89 fclose(fp); 90 r = len == 1 ? -1 : 0; 91#else 92 UNUSED(uuid); 93 r = -1; 94 errno = ENOSYS; 95#endif 96 97 if (r == -1) 98 return 0; 99 return len; 100} 101 102static size_t 103duid_make_uuid(uint8_t *d) 104{ 105 uint16_t type = htons(DUID_UUID); 106 char uuid[UUID_LEN + 1]; 107 size_t l; 108 109 if (duid_machineuuid(uuid, sizeof(uuid)) != sizeof(uuid)) 110 return 0; 111 112 /* All zeros UUID is not valid */ 113 if (strcmp("00000000-0000-0000-0000-000000000000", uuid) == 0) 114 return 0; 115 116 memcpy(d, &type, sizeof(type)); 117 l = sizeof(type); 118 d += sizeof(type); 119 l += hwaddr_aton(d, uuid); 120 return l; 121} 122#endif 123 124size_t 125duid_make(void *d, const struct interface *ifp, uint16_t type) 126{ 127 uint8_t *p; 128 uint16_t u16; 129 time_t t; 130 uint32_t u32; 131 132 if (ifp->hwlen == 0) 133 return 0; 134 135 p = d; 136 u16 = htons(type); 137 memcpy(p, &u16, sizeof(u16)); 138 p += sizeof(u16); 139 u16 = htons(ifp->hwtype); 140 memcpy(p, &u16, sizeof(u16)); 141 p += sizeof(u16); 142 if (type == DUID_LLT) { 143 /* time returns seconds from jan 1 1970, but DUID-LLT is 144 * seconds from jan 1 2000 modulo 2^32 */ 145 t = time(NULL) - DUID_TIME_EPOCH; 146 u32 = htonl((uint32_t)t & 0xffffffff); 147 memcpy(p, &u32, sizeof(u32)); 148 p += sizeof(u32); 149 } 150 /* Finally, add the MAC address of the interface */ 151 memcpy(p, ifp->hwaddr, ifp->hwlen); 152 p += ifp->hwlen; 153 return (size_t)(p - (uint8_t *)d); 154} 155 156#define DUID_STRLEN DUID_LEN * 3 157static size_t 158duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp) 159{ 160 uint8_t *data; 161 size_t len, slen; 162 char line[DUID_STRLEN]; 163 const struct interface *ifp2; 164 165 /* If we already have a DUID then use it as it's never supposed 166 * to change once we have one even if the interfaces do */ 167 if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) { 168 if (len <= DUID_LEN) { 169 ctx->duid = data; 170 return len; 171 } 172 logerrx("DUID too big (max %u): %s", DUID_LEN, DUID); 173 /* Keep the buffer, will assign below. */ 174 } else { 175 if (errno != ENOENT) 176 logerr("%s", DUID); 177 if ((data = malloc(DUID_LEN)) == NULL) { 178 logerr(__func__); 179 return 0; 180 } 181 } 182 183 /* No file? OK, lets make one based the machines UUID */ 184 if (ifp == NULL) { 185#ifdef USE_MACHINE_UUID 186 if (ctx->duid_type != DUID_DEFAULT && 187 ctx->duid_type != DUID_UUID) 188 len = 0; 189 else 190 len = duid_make_uuid(data); 191 if (len == 0) 192 free(data); 193 else 194 ctx->duid = data; 195 return len; 196#else 197 free(data); 198 return 0; 199#endif 200 } 201 202 /* Regardless of what happens we will create a DUID to use. */ 203 ctx->duid = data; 204 205 /* No UUID? OK, lets make one based on our interface */ 206 if (ifp->hwlen == 0) { 207 logwarnx("%s: does not have hardware address", ifp->name); 208 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 209 if (ifp2->hwlen != 0) 210 break; 211 } 212 if (ifp2) { 213 ifp = ifp2; 214 logwarnx("picked interface %s to generate a DUID", 215 ifp->name); 216 } else { 217 if (ctx->duid_type != DUID_LL) 218 logwarnx("no interfaces have a fixed hardware " 219 "address"); 220 return duid_make(data, ifp, DUID_LL); 221 } 222 } 223 224 len = duid_make(data, ifp, 225 ctx->duid_type == DUID_LL ? DUID_LL : DUID_LLT); 226 hwaddr_ntoa(data, len, line, sizeof(line)); 227 slen = strlen(line); 228 if (slen < sizeof(line) - 2) { 229 line[slen++] = '\n'; 230 line[slen] = '\0'; 231 } 232 if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) { 233 logerr("%s: cannot write duid", __func__); 234 if (ctx->duid_type != DUID_LL) 235 return duid_make(data, ifp, DUID_LL); 236 } 237 return len; 238} 239 240size_t 241duid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp) 242{ 243 244 if (ctx->duid == NULL) 245 ctx->duid_len = duid_get(ctx, ifp); 246 return ctx->duid_len; 247} 248