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