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