xref: /illumos-gate/usr/src/lib/libuuid/common/uuid.c (revision 9d12795f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2012 Milan Jurik. All rights reserved.
25  * Copyright 2015 Joyent, Inc. All rights reserved.
26  * Copyright 2014 Andrew Stormont.
27  */
28 
29 /*
30  * The copyright in this file is taken from the original Leach & Salz
31  * UUID specification, from which this implementation is derived.
32  */
33 
34 /*
35  * Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
36  * Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
37  * Digital Equipment Corporation, Maynard, Mass.  Copyright (c) 1998
38  * Microsoft.  To anyone who acknowledges that this file is provided
39  * "AS IS" without any express or implied warranty: permission to use,
40  * copy, modify, and distribute this file for any purpose is hereby
41  * granted without fee, provided that the above copyright notices and
42  * this notice appears in all source code copies, and that none of the
43  * names of Open Software Foundation, Inc., Hewlett-Packard Company,
44  * or Digital Equipment Corporation be used in advertising or
45  * publicity pertaining to distribution of the software without
46  * specific, written prior permission.  Neither Open Software
47  * Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
48  * Equipment Corporation makes any representations about the
49  * suitability of this software for any purpose.
50  */
51 
52 /*
53  * This module is the workhorse for generating abstract
54  * UUIDs.  It delegates system-specific tasks (such
55  * as obtaining the node identifier or system time)
56  * to the sysdep module.
57  */
58 
59 #include <ctype.h>
60 #include <sys/param.h>
61 #include <sys/stat.h>
62 #include <errno.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <strings.h>
66 #include <fcntl.h>
67 #include <unistd.h>
68 #include <synch.h>
69 #include <sys/mman.h>
70 #include "uuid_misc.h"
71 
72 shared_buffer_t		*data;
73 
74 static	uuid_node_t	node_id_cache;
75 static	int		node_init;
76 static	int		file_type;
77 static	int		fd;
78 
79 /*
80  * misc routines
81  */
82 uint16_t		get_random(void);
83 void			get_current_time(uuid_time_t *);
84 
85 void			struct_to_string(uuid_t, struct uuid *);
86 void			string_to_struct(struct uuid *, uuid_t);
87 int			get_ethernet_address(uuid_node_t *);
88 
89 /*
90  * local functions
91  */
92 static	int		map_state();
93 static	void 		format_uuid(struct uuid *, uint16_t, uuid_time_t,
94     uuid_node_t);
95 static	int		uuid_create(struct uuid *);
96 static	void		gen_ethernet_address(uuid_node_t *);
97 static	void		revalidate_data(uuid_node_t *);
98 
99 /*
100  * Generates a uuid based on version 1 format.
101  * Returns 0 on success and -1 on failure.
102  */
103 static int
uuid_create(struct uuid * uuid)104 uuid_create(struct uuid *uuid)
105 {
106 	uuid_time_t	timestamp;
107 	uuid_node_t	system_node;
108 	int		ret, non_unique = 0;
109 
110 	/*
111 	 * Get the system MAC address and/or cache it
112 	 */
113 	if (node_init) {
114 		bcopy(&node_id_cache, &system_node, sizeof (uuid_node_t));
115 	} else {
116 		gen_ethernet_address(&system_node);
117 		bcopy(&system_node, &node_id_cache, sizeof (uuid_node_t));
118 		node_init = 1;
119 	}
120 
121 	/*
122 	 * Access the state file, mmap it and initialize the shared lock.
123 	 * file_type tells us whether we had access to the state file or
124 	 * created a temporary one.
125 	 */
126 	if (map_state() == -1)
127 		return (-1);
128 
129 	/*
130 	 * Acquire the lock
131 	 */
132 	for (;;) {
133 		if ((ret = mutex_lock(&data->lock)) == 0)
134 			break;
135 		else
136 			switch (ret) {
137 				case EOWNERDEAD:
138 					revalidate_data(&system_node);
139 					(void) mutex_consistent(&data->lock);
140 					(void) mutex_unlock(&data->lock);
141 					break;
142 				case ENOTRECOVERABLE:
143 					return (ret);
144 			}
145 	}
146 
147 	/* State file is either new or is temporary, get a random clock seq */
148 	if (data->state.clock == 0) {
149 		data->state.clock = get_random();
150 		non_unique++;
151 	}
152 
153 	if (memcmp(&system_node, &data->state.node, sizeof (uuid_node_t)) != 0)
154 		data->state.clock++;
155 
156 	get_current_time(&timestamp);
157 
158 	/*
159 	 * If timestamp is not set or is not in the past, bump
160 	 * data->state.clock
161 	 */
162 	if ((data->state.ts == 0) || (data->state.ts >= timestamp)) {
163 		data->state.clock++;
164 		data->state.ts = timestamp;
165 	}
166 
167 	if (non_unique)
168 		system_node.nodeID[0] |= 0x80;
169 
170 	/* Stuff fields into the UUID struct */
171 	format_uuid(uuid, data->state.clock, timestamp, system_node);
172 
173 	(void) mutex_unlock(&data->lock);
174 
175 	return (0);
176 }
177 
178 /*
179  * Fills system_node with Ethernet address if available,
180  * else fills random numbers
181  */
182 static void
gen_ethernet_address(uuid_node_t * system_node)183 gen_ethernet_address(uuid_node_t *system_node)
184 {
185 	uchar_t		node[6];
186 
187 	if (get_ethernet_address(system_node) != 0) {
188 		arc4random_buf(node, 6);
189 		(void) memcpy(system_node->nodeID, node, 6);
190 		/*
191 		 * use 8:0:20 with the multicast bit set
192 		 * to avoid namespace collisions.
193 		 */
194 		system_node->nodeID[0] = 0x88;
195 		system_node->nodeID[1] = 0x00;
196 		system_node->nodeID[2] = 0x20;
197 	}
198 }
199 
200 /*
201  * Formats a UUID, given the clock_seq timestamp, and node address.
202  * Fills in passed-in pointer with the resulting uuid.
203  */
204 static void
format_uuid(struct uuid * uuid,uint16_t clock_seq,uuid_time_t timestamp,uuid_node_t node)205 format_uuid(struct uuid *uuid, uint16_t clock_seq,
206     uuid_time_t timestamp, uuid_node_t node)
207 {
208 
209 	/*
210 	 * First set up the first 60 bits from the timestamp
211 	 */
212 	uuid->time_low = (uint32_t)(timestamp & 0xFFFFFFFF);
213 	uuid->time_mid = (uint16_t)((timestamp >> 32) & 0xFFFF);
214 	uuid->time_hi_and_version = (uint16_t)((timestamp >> 48) & 0x0FFF);
215 
216 	/*
217 	 * This is version 1, so say so in the UUID version field (4 bits)
218 	 */
219 	uuid->time_hi_and_version |= (1 << 12);
220 
221 	/*
222 	 * Now do the clock sequence
223 	 */
224 	uuid->clock_seq_low = clock_seq & 0xFF;
225 
226 	/*
227 	 * We must save the most-significant 2 bits for the reserved field
228 	 */
229 	uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
230 
231 	/*
232 	 * The variant for this format is the 2 high bits set to 10,
233 	 * so here it is
234 	 */
235 	uuid->clock_seq_hi_and_reserved |= 0x80;
236 
237 	/*
238 	 * write result to passed-in pointer
239 	 */
240 	(void) memcpy(&uuid->node_addr, &node, sizeof (uuid->node_addr));
241 }
242 
243 /*
244  * Opens/creates the state file, falling back to a tmp
245  */
246 static int
map_state()247 map_state()
248 {
249 	FILE	*tmp;
250 
251 	/* If file's mapped, return */
252 	if (file_type != 0)
253 		return (1);
254 
255 	if ((fd = open(STATE_LOCATION, O_RDWR)) < 0) {
256 		file_type = TEMP_FILE;
257 
258 		if ((tmp = tmpfile()) == NULL)
259 			return (-1);
260 		else
261 			fd = fileno(tmp);
262 	} else {
263 		file_type = STATE_FILE;
264 	}
265 
266 	(void) ftruncate(fd, (off_t)sizeof (shared_buffer_t));
267 
268 	/* LINTED - alignment */
269 	data = (shared_buffer_t *)mmap(NULL, sizeof (shared_buffer_t),
270 	    PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
271 
272 	if (data == MAP_FAILED)
273 		return (-1);
274 
275 	(void) mutex_init(&data->lock, USYNC_PROCESS|LOCK_ROBUST, 0);
276 
277 	(void) close(fd);
278 
279 	return (1);
280 }
281 
282 static void
revalidate_data(uuid_node_t * node)283 revalidate_data(uuid_node_t *node)
284 {
285 	int i;
286 
287 	data->state.ts = 0;
288 
289 	for (i = 0; i < sizeof (data->state.node.nodeID); i++)
290 		data->state.node.nodeID[i] = 0;
291 
292 	data->state.clock = 0;
293 
294 	gen_ethernet_address(node);
295 	bcopy(node, &node_id_cache, sizeof (uuid_node_t));
296 	node_init = 1;
297 }
298 
299 /*
300  * Prints a nicely-formatted uuid to stdout.
301  */
302 void
uuid_print(struct uuid u)303 uuid_print(struct uuid u)
304 {
305 	int i;
306 
307 	(void) printf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", u.time_low, u.time_mid,
308 	    u.time_hi_and_version, u.clock_seq_hi_and_reserved,
309 	    u.clock_seq_low);
310 	for (i = 0; i < 6; i++)
311 		(void) printf("%2.2x", u.node_addr[i]);
312 	(void) printf("\n");
313 }
314 
315 /*
316  * Unpacks the structure members in "struct uuid" to a char string "uuid_t".
317  */
318 void
struct_to_string(uuid_t ptr,struct uuid * uu)319 struct_to_string(uuid_t ptr, struct uuid *uu)
320 {
321 	uint_t		tmp;
322 	uchar_t		*out = ptr;
323 
324 	tmp = uu->time_low;
325 	out[3] = (uchar_t)tmp;
326 	tmp >>= 8;
327 	out[2] = (uchar_t)tmp;
328 	tmp >>= 8;
329 	out[1] = (uchar_t)tmp;
330 	tmp >>= 8;
331 	out[0] = (uchar_t)tmp;
332 
333 	tmp = uu->time_mid;
334 	out[5] = (uchar_t)tmp;
335 	tmp >>= 8;
336 	out[4] = (uchar_t)tmp;
337 
338 	tmp = uu->time_hi_and_version;
339 	out[7] = (uchar_t)tmp;
340 	tmp >>= 8;
341 	out[6] = (uchar_t)tmp;
342 
343 	tmp = uu->clock_seq_hi_and_reserved;
344 	out[8] = (uchar_t)tmp;
345 	tmp = uu->clock_seq_low;
346 	out[9] = (uchar_t)tmp;
347 
348 	(void) memcpy(out+10, uu->node_addr, 6);
349 
350 }
351 
352 /*
353  * Packs the values in the "uuid_t" string into "struct uuid".
354  */
355 void
string_to_struct(struct uuid * uuid,uuid_t in)356 string_to_struct(struct uuid *uuid, uuid_t in)
357 {
358 	uchar_t	*ptr;
359 	uint_t	tmp;
360 
361 	ptr = in;
362 
363 	tmp = *ptr++;
364 	tmp = (tmp << 8) | *ptr++;
365 	tmp = (tmp << 8) | *ptr++;
366 	tmp = (tmp << 8) | *ptr++;
367 	uuid->time_low = tmp;
368 
369 	tmp = *ptr++;
370 	tmp = (tmp << 8) | *ptr++;
371 	uuid->time_mid = tmp;
372 
373 	tmp = *ptr++;
374 	tmp = (tmp << 8) | *ptr++;
375 	uuid->time_hi_and_version = tmp;
376 
377 	tmp = *ptr++;
378 	uuid->clock_seq_hi_and_reserved = tmp;
379 
380 	tmp = *ptr++;
381 	uuid->clock_seq_low = tmp;
382 
383 	(void) memcpy(uuid->node_addr, ptr, 6);
384 
385 }
386 
387 /*
388  * Generates UUID based on DCE Version 4
389  */
390 void
uuid_generate_random(uuid_t uu)391 uuid_generate_random(uuid_t uu)
392 {
393 	struct uuid	uuid;
394 
395 	if (uu == NULL)
396 		return;
397 
398 	(void) memset(uu, 0, sizeof (uuid_t));
399 	(void) memset(&uuid, 0, sizeof (struct uuid));
400 
401 	arc4random_buf(uu, sizeof (uuid_t));
402 	string_to_struct(&uuid, uu);
403 	/*
404 	 * This is version 4, so say so in the UUID version field (4 bits)
405 	 */
406 	uuid.time_hi_and_version |= (1 << 14);
407 	/*
408 	 * we don't want the bit 1 to be set also which is for version 1
409 	 */
410 	uuid.time_hi_and_version &= VER1_MASK;
411 
412 	/*
413 	 * The variant for this format is the 2 high bits set to 10,
414 	 * so here it is
415 	 */
416 	uuid.clock_seq_hi_and_reserved |= 0x80;
417 
418 	/*
419 	 * Set MSB of Ethernet address to 1 to indicate that it was generated
420 	 * randomly
421 	 */
422 	uuid.node_addr[0] |= 0x80;
423 	struct_to_string(uu, &uuid);
424 }
425 
426 /*
427  * Generates UUID based on DCE Version 1.
428  */
429 void
uuid_generate_time(uuid_t uu)430 uuid_generate_time(uuid_t uu)
431 {
432 	struct 	uuid uuid;
433 
434 	if (uu == NULL)
435 		return;
436 
437 	if (uuid_create(&uuid) < 0) {
438 		uuid_generate_random(uu);
439 		return;
440 	}
441 
442 	struct_to_string(uu, &uuid);
443 }
444 
445 /*
446  * Creates a new UUID. The uuid will be generated based on high-quality
447  * randomness from arc4random(3C).
448  */
449 void
uuid_generate(uuid_t uu)450 uuid_generate(uuid_t uu)
451 {
452 	uuid_generate_random(uu);
453 }
454 
455 /*
456  * Copies the UUID variable src to dst.
457  */
458 void
uuid_copy(uuid_t dst,uuid_t src)459 uuid_copy(uuid_t dst, uuid_t src)
460 {
461 	(void) memcpy(dst, src, UUID_LEN);
462 }
463 
464 /*
465  * Sets the value of the supplied uuid variable uu, to the NULL value.
466  */
467 void
uuid_clear(uuid_t uu)468 uuid_clear(uuid_t uu)
469 {
470 	(void) memset(uu, 0, UUID_LEN);
471 }
472 
473 /*
474  * This function converts the supplied UUID uu from the internal
475  * binary format into a 36-byte string (plus trailing null char)
476  * and stores this value in the character string pointed to by out.
477  */
478 static void
uuid_unparse_common(uuid_t uu,char * out,boolean_t upper)479 uuid_unparse_common(uuid_t uu, char *out, boolean_t upper)
480 {
481 	struct uuid 	uuid;
482 	uint16_t	clock_seq;
483 	char		etheraddr[13];
484 	int		index = 0, i;
485 
486 	/* basic sanity checking */
487 	if (uu == NULL) {
488 		return;
489 	}
490 
491 	string_to_struct(&uuid, uu);
492 	clock_seq = uuid.clock_seq_hi_and_reserved;
493 	clock_seq = (clock_seq  << 8) | uuid.clock_seq_low;
494 	for (i = 0; i < 6; i++) {
495 		(void) sprintf(&etheraddr[index++], upper ? "%.2X" : "%.2x",
496 		    uuid.node_addr[i]);
497 		index++;
498 	}
499 	etheraddr[index] = '\0';
500 
501 	(void) snprintf(out, 25,
502 	    upper ? "%08X-%04X-%04X-%04X-" : "%08x-%04x-%04x-%04x-",
503 	    uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, clock_seq);
504 	(void) strlcat(out, etheraddr, UUID_PRINTABLE_STRING_LENGTH);
505 }
506 
507 void
uuid_unparse_upper(uuid_t uu,char * out)508 uuid_unparse_upper(uuid_t uu, char *out)
509 {
510 	uuid_unparse_common(uu, out, B_TRUE);
511 }
512 
513 void
uuid_unparse_lower(uuid_t uu,char * out)514 uuid_unparse_lower(uuid_t uu, char *out)
515 {
516 	uuid_unparse_common(uu, out, B_FALSE);
517 }
518 
519 void
uuid_unparse(uuid_t uu,char * out)520 uuid_unparse(uuid_t uu, char *out)
521 {
522 	/*
523 	 * Historically uuid_unparse on Solaris returns lower case,
524 	 * for compatibility we preserve this behaviour.
525 	 */
526 	uuid_unparse_common(uu, out, B_FALSE);
527 }
528 
529 /*
530  * The uuid_is_null function compares the value of the supplied
531  * UUID variable uu to the NULL value. If the value is equal
532  * to the NULL UUID, 1 is returned, otherwise 0 is returned.
533  */
534 int
uuid_is_null(uuid_t uu)535 uuid_is_null(uuid_t uu)
536 {
537 	int		i;
538 	uuid_t		null_uu;
539 
540 	(void) memset(null_uu, 0, sizeof (uuid_t));
541 	i = memcmp(uu, null_uu, sizeof (uuid_t));
542 	if (i == 0) {
543 		/* uu is NULL uuid */
544 		return (1);
545 	} else {
546 		return (0);
547 	}
548 }
549 
550 /*
551  * uuid_parse converts the UUID string given by 'in' into the
552  * internal uuid_t format. The input UUID is a string of the form
553  * cefa7a9c-1dd2-11b2-8350-880020adbeef in printf(3C) format.
554  * Upon successfully parsing the input string, UUID is stored
555  * in the location pointed to by uu
556  */
557 int
uuid_parse(char * in,uuid_t uu)558 uuid_parse(char *in, uuid_t uu)
559 {
560 
561 	char		*ptr, buf[3];
562 	int		i;
563 	struct uuid	uuid;
564 	uint16_t	clock_seq;
565 
566 	/* do some sanity checking */
567 	if ((strlen(in) != 36) || (uu == NULL) || (in[36] != '\0')) {
568 		return (-1);
569 	}
570 
571 	ptr = in;
572 	for (i = 0; i < 36; i++, ptr++) {
573 		if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
574 			if (*ptr != '-') {
575 				return (-1);
576 			}
577 		} else {
578 			if (!isxdigit(*ptr)) {
579 				return (-1);
580 			}
581 		}
582 	}
583 
584 	uuid.time_low = strtoul(in, NULL, 16);
585 	uuid.time_mid = strtoul(in+9, NULL, 16);
586 	uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
587 	clock_seq = strtoul(in+19, NULL, 16);
588 	uuid.clock_seq_hi_and_reserved = (clock_seq & 0xFF00) >> 8;
589 	uuid.clock_seq_low = (clock_seq & 0xFF);
590 
591 	ptr = in+24;
592 	buf[2] = '\0';
593 	for (i = 0; i < 6; i++) {
594 		buf[0] = *ptr++;
595 		buf[1] = *ptr++;
596 		uuid.node_addr[i] = strtoul(buf, NULL, 16);
597 	}
598 	struct_to_string(uu, &uuid);
599 	return (0);
600 }
601 
602 /*
603  * uuid_time extracts the time at which the supplied UUID uu
604  * was created. This function can only extract the creation
605  * time for UUIDs created with the uuid_generate_time function.
606  * The time at which the UUID was created, in seconds and
607  * microseconds since the epoch is stored in the location
608  * pointed to by ret_tv.
609  */
610 time_t
uuid_time(uuid_t uu,struct timeval * ret_tv)611 uuid_time(uuid_t uu, struct timeval *ret_tv)
612 {
613 	struct uuid	uuid;
614 	uint_t		high;
615 	struct timeval	tv;
616 	u_longlong_t	clock_reg;
617 	uint_t		tmp;
618 	uint8_t		clk;
619 
620 	string_to_struct(&uuid, uu);
621 	tmp = (uuid.time_hi_and_version & 0xF000) >> 12;
622 	clk = uuid.clock_seq_hi_and_reserved;
623 
624 	/* check if uu is NULL, Version = 1 of DCE and Variant = 0b10x */
625 	if ((uu == NULL) || ((tmp & 0x01) != 0x01) || ((clk & 0x80) != 0x80)) {
626 		return (-1);
627 	}
628 	high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
629 	clock_reg = uuid.time_low | ((u_longlong_t)high << 32);
630 
631 	clock_reg -= (((u_longlong_t)0x01B21DD2) << 32) + 0x13814000;
632 	tv.tv_sec = clock_reg / 10000000;
633 	tv.tv_usec = (clock_reg % 10000000) / 10;
634 
635 	if (ret_tv) {
636 		*ret_tv = tv;
637 	}
638 
639 	return (tv.tv_sec);
640 }
641