xref: /openbsd-src/usr.sbin/ldapd/uuid.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: uuid.c,v 1.5 2016/08/27 01:42:37 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 #include <sys/file.h>
57 
58 #include <fcntl.h>
59 #include <ifaddrs.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 #include "uuid.h"
66 
67 static uint32_t seq_num;
68 static struct timeval last_time;
69 static int32_t counter;
70 static char nodeaddr[6];
71 
72 enum { UUID_NODE_MULTICAST = 0x80 };
73 
74 static int
75 time_cmp(struct timeval *tv1, struct timeval *tv2)
76 {
77     if (tv1->tv_sec > tv2->tv_sec)
78 	return -1;
79     if (tv1->tv_sec < tv2->tv_sec)
80 	return 1;
81     if (tv1->tv_usec > tv2->tv_usec)
82 	return -1;
83     if (tv1->tv_usec < tv2->tv_usec)
84 	return 1;
85     return 0;
86 }
87 
88 static void
89 get_node_addr(char *addr)
90 {
91     struct ifaddrs *ifa, *ifa0;
92     int found_mac = 0;
93 
94     if (getifaddrs(&ifa0) != 0)
95 	ifa0 = NULL;
96 
97     for (ifa = ifa0; ifa != NULL && !found_mac; ifa = ifa->ifa_next) {
98 	if (ifa->ifa_addr == NULL)
99 	    continue;
100 
101 #if IFF_LOOPBACK
102 	if (ifa->ifa_flags & IFF_LOOPBACK)
103 	    continue;
104 #endif
105 
106 	switch (ifa->ifa_addr->sa_family) {
107 #ifdef AF_LINK
108 	case AF_LINK: {
109 	    struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr;
110 
111 	    switch (dl->sdl_type) {
112 	    case IFT_ETHER:
113 	    case IFT_FDDI:
114 		if (dl->sdl_alen == 6) {
115 		    memcpy(addr, LLADDR(dl), 6);
116 		    found_mac = 1;
117 		}
118 	    }
119 
120 	}
121 #endif
122 	default:
123 	    break;
124 	}
125     }
126 
127     if (ifa0 != NULL)
128 	freeifaddrs(ifa0);
129 
130     if (!found_mac) {
131 	/*
132 	 * Set the multicast bit to make sure we won't collide with an
133 	 * allocated (mac) address.
134 	 */
135 	arc4random_buf(addr, 6);
136 	addr[0] |= UUID_NODE_MULTICAST;
137     }
138     return;
139 }
140 
141 /*
142  *    Creates a new UUID.
143  */
144 
145 void
146 uuid_create(afsUUID *uuid)
147 {
148     static int uuid_inited = 0;
149     struct timeval tv;
150     int ret, got_time;
151     uint64_t dce_time;
152 
153     if (uuid_inited == 0) {
154 	gettimeofday(&last_time, NULL);
155 	seq_num = arc4random();
156 	get_node_addr(nodeaddr);
157 	uuid_inited = 1;
158     }
159 
160     gettimeofday(&tv, NULL);
161 
162     got_time = 0;
163 
164     do {
165 	ret = time_cmp(&tv, &last_time);
166 	if (ret < 0) {
167 	    /* Time went backward, just inc seq_num and be done.
168 	     * seq_num is 6 + 8 bit field it the uuid, so let it wrap
169 	     * around. don't let it be zero.
170 	     */
171 	    seq_num = (seq_num + 1) & 0x3fff ;
172 	    if (seq_num == 0)
173 		seq_num++;
174 	    got_time = 1;
175 	    counter = 0;
176 	    last_time = tv;
177 	} else if (ret > 0) {
178 	    /* time went forward, reset counter and be happy */
179 	    last_time = tv;
180 	    counter = 0;
181 	    got_time = 1;
182 	} else {
183 #define UUID_MAX_HZ (1) /* make this bigger fix you have larger tickrate */
184 #define MULTIPLIER_100_NANO_SEC 10
185 	    if (++counter < UUID_MAX_HZ * MULTIPLIER_100_NANO_SEC)
186 		got_time = 1;
187 	}
188     } while(!got_time);
189 
190     /*
191      * now shift time to dce_time, epoch 00:00:00:00, 15 October 1582
192      * dce time ends year ~3400, so start to worry now
193      */
194 
195     dce_time = tv.tv_usec * MULTIPLIER_100_NANO_SEC + counter;
196     dce_time += ((uint64_t)tv.tv_sec) * 10000000;
197     dce_time += (((uint64_t)0x01b21dd2) << 32) + 0x13814000;
198 
199     uuid->time_low = dce_time & 0xffffffff;
200     uuid->time_mid = 0xffff & (dce_time >> 32);
201     uuid->time_hi_and_version = 0x0fff & (dce_time >> 48);
202 
203     uuid->time_hi_and_version |= (1 << 12);
204 
205     uuid->clock_seq_low = seq_num & 0xff;
206     uuid->clock_seq_hi_and_reserved = (seq_num >> 8) & 0x3f;
207     uuid->clock_seq_hi_and_reserved |= 0x80; /* dce variant */
208 
209     memcpy(uuid->node, nodeaddr, 6);
210 }
211 
212 /*
213  *    Converts a UUID from binary representation to a string representation.
214  */
215 
216 void
217 uuid_to_string(const afsUUID *uuid, char *str, size_t strsz)
218 {
219     snprintf(str, strsz,
220 	     "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
221 	     uuid->time_low,
222 	     uuid->time_mid,
223 	     uuid->time_hi_and_version,
224 	     (unsigned char)uuid->clock_seq_hi_and_reserved,
225 	     (unsigned char)uuid->clock_seq_low,
226 	     (unsigned char)uuid->node[0],
227 	     (unsigned char)uuid->node[1],
228 	     (unsigned char)uuid->node[2],
229 	     (unsigned char)uuid->node[3],
230 	     (unsigned char)uuid->node[4],
231 	     (unsigned char)uuid->node[5]);
232 }
233 
234 
235 #ifdef TEST
236 int
237 main(int argc, char **argv)
238 {
239     char str[1000];
240     afsUUID u1, u2;
241 
242     uuid_create(&u1);
243 
244     uuid_to_string(&u1, str, sizeof(str));
245 
246     printf("u: %s\n", str);
247 
248     if (uuid_from_string(str, &u2)) {
249 	printf("failed to parse\n");
250 	return 0;
251     }
252 
253     if (bcmp(&u1, &u2, sizeof(u1)) != 0)
254 	printf("u1 != u2\n");
255 
256     return 0;
257 }
258 #endif
259