xref: /openbsd/usr.sbin/ldapd/uuid.c (revision e5dd7070)
1 /*	$OpenBSD: uuid.c,v 1.6 2018/04/26 12:42:51 guenther Exp $ */
2 /*
3  * Copyright (c) 2002, Stockholms Universitet
4  * (Stockholm University, Stockholm Sweden)
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of the university nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /*
36  * NCS/DCE/AFS/GUID generator
37  *
38  *  for more information about DCE UUID, see
39  *  <http://www.opengroup.org/onlinepubs/9629399/apdxa.htm>
40  *
41  *  Note, the Microsoft GUID is a DCE UUID, but it seems like they
42  *  folded in the seq num with the node part. That would explain how
43  *  the reserved field have a bit pattern 110 when reserved is a 2 bit
44  *  field.
45  *
46  *  XXX should hash the node address for privacy issues
47  */
48 
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <netinet/in.h>
53 #include <net/if.h>
54 #include <net/if_types.h>
55 #include <net/if_dl.h>
56 
57 #include <fcntl.h>
58 #include <ifaddrs.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "uuid.h"
65 
66 static uint32_t seq_num;
67 static struct timeval last_time;
68 static int32_t counter;
69 static char nodeaddr[6];
70 
71 enum { UUID_NODE_MULTICAST = 0x80 };
72 
73 static int
74 time_cmp(struct timeval *tv1, struct timeval *tv2)
75 {
76     if (tv1->tv_sec > tv2->tv_sec)
77 	return -1;
78     if (tv1->tv_sec < tv2->tv_sec)
79 	return 1;
80     if (tv1->tv_usec > tv2->tv_usec)
81 	return -1;
82     if (tv1->tv_usec < tv2->tv_usec)
83 	return 1;
84     return 0;
85 }
86 
87 static void
88 get_node_addr(char *addr)
89 {
90     struct ifaddrs *ifa, *ifa0;
91     int found_mac = 0;
92 
93     if (getifaddrs(&ifa0) != 0)
94 	ifa0 = NULL;
95 
96     for (ifa = ifa0; ifa != NULL && !found_mac; ifa = ifa->ifa_next) {
97 	if (ifa->ifa_addr == NULL)
98 	    continue;
99 
100 #if IFF_LOOPBACK
101 	if (ifa->ifa_flags & IFF_LOOPBACK)
102 	    continue;
103 #endif
104 
105 	switch (ifa->ifa_addr->sa_family) {
106 #ifdef AF_LINK
107 	case AF_LINK: {
108 	    struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr;
109 
110 	    switch (dl->sdl_type) {
111 	    case IFT_ETHER:
112 	    case IFT_FDDI:
113 		if (dl->sdl_alen == 6) {
114 		    memcpy(addr, LLADDR(dl), 6);
115 		    found_mac = 1;
116 		}
117 	    }
118 
119 	}
120 #endif
121 	default:
122 	    break;
123 	}
124     }
125 
126     if (ifa0 != NULL)
127 	freeifaddrs(ifa0);
128 
129     if (!found_mac) {
130 	/*
131 	 * Set the multicast bit to make sure we won't collide with an
132 	 * allocated (mac) address.
133 	 */
134 	arc4random_buf(addr, 6);
135 	addr[0] |= UUID_NODE_MULTICAST;
136     }
137     return;
138 }
139 
140 /*
141  *    Creates a new UUID.
142  */
143 
144 void
145 uuid_create(afsUUID *uuid)
146 {
147     static int uuid_inited = 0;
148     struct timeval tv;
149     int ret, got_time;
150     uint64_t dce_time;
151 
152     if (uuid_inited == 0) {
153 	gettimeofday(&last_time, NULL);
154 	seq_num = arc4random();
155 	get_node_addr(nodeaddr);
156 	uuid_inited = 1;
157     }
158 
159     gettimeofday(&tv, NULL);
160 
161     got_time = 0;
162 
163     do {
164 	ret = time_cmp(&tv, &last_time);
165 	if (ret < 0) {
166 	    /* Time went backward, just inc seq_num and be done.
167 	     * seq_num is 6 + 8 bit field it the uuid, so let it wrap
168 	     * around. don't let it be zero.
169 	     */
170 	    seq_num = (seq_num + 1) & 0x3fff ;
171 	    if (seq_num == 0)
172 		seq_num++;
173 	    got_time = 1;
174 	    counter = 0;
175 	    last_time = tv;
176 	} else if (ret > 0) {
177 	    /* time went forward, reset counter and be happy */
178 	    last_time = tv;
179 	    counter = 0;
180 	    got_time = 1;
181 	} else {
182 #define UUID_MAX_HZ (1) /* make this bigger fix you have larger tickrate */
183 #define MULTIPLIER_100_NANO_SEC 10
184 	    if (++counter < UUID_MAX_HZ * MULTIPLIER_100_NANO_SEC)
185 		got_time = 1;
186 	}
187     } while(!got_time);
188 
189     /*
190      * now shift time to dce_time, epoch 00:00:00:00, 15 October 1582
191      * dce time ends year ~3400, so start to worry now
192      */
193 
194     dce_time = tv.tv_usec * MULTIPLIER_100_NANO_SEC + counter;
195     dce_time += ((uint64_t)tv.tv_sec) * 10000000;
196     dce_time += (((uint64_t)0x01b21dd2) << 32) + 0x13814000;
197 
198     uuid->time_low = dce_time & 0xffffffff;
199     uuid->time_mid = 0xffff & (dce_time >> 32);
200     uuid->time_hi_and_version = 0x0fff & (dce_time >> 48);
201 
202     uuid->time_hi_and_version |= (1 << 12);
203 
204     uuid->clock_seq_low = seq_num & 0xff;
205     uuid->clock_seq_hi_and_reserved = (seq_num >> 8) & 0x3f;
206     uuid->clock_seq_hi_and_reserved |= 0x80; /* dce variant */
207 
208     memcpy(uuid->node, nodeaddr, 6);
209 }
210 
211 /*
212  *    Converts a UUID from binary representation to a string representation.
213  */
214 
215 void
216 uuid_to_string(const afsUUID *uuid, char *str, size_t strsz)
217 {
218     snprintf(str, strsz,
219 	     "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
220 	     uuid->time_low,
221 	     uuid->time_mid,
222 	     uuid->time_hi_and_version,
223 	     (unsigned char)uuid->clock_seq_hi_and_reserved,
224 	     (unsigned char)uuid->clock_seq_low,
225 	     (unsigned char)uuid->node[0],
226 	     (unsigned char)uuid->node[1],
227 	     (unsigned char)uuid->node[2],
228 	     (unsigned char)uuid->node[3],
229 	     (unsigned char)uuid->node[4],
230 	     (unsigned char)uuid->node[5]);
231 }
232 
233 
234 #ifdef TEST
235 int
236 main(int argc, char **argv)
237 {
238     char str[1000];
239     afsUUID u1, u2;
240 
241     uuid_create(&u1);
242 
243     uuid_to_string(&u1, str, sizeof(str));
244 
245     printf("u: %s\n", str);
246 
247     if (uuid_from_string(str, &u2)) {
248 	printf("failed to parse\n");
249 	return 0;
250     }
251 
252     if (bcmp(&u1, &u2, sizeof(u1)) != 0)
253 	printf("u1 != u2\n");
254 
255     return 0;
256 }
257 #endif
258