1// Copyright (c) 2006, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#include "common/linux/eintr_wrapper.h"
35#include "common/linux/guid_creator.h"
36
37#include <assert.h>
38#include <fcntl.h>
39#include <pthread.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <sys/stat.h>
44#include <time.h>
45#include <unistd.h>
46
47#if defined(HAVE_SYS_RANDOM_H)
48#include <sys/random.h>
49#endif
50
51//
52// GUIDGenerator
53//
54// This class is used to generate random GUID.
55// Currently use random number to generate a GUID since Linux has
56// no native GUID generator. This should be OK since we don't expect
57// crash to happen very offen.
58//
59class GUIDGenerator {
60 public:
61 static uint32_t BytesToUInt32(const uint8_t bytes[]) {
62 return ((uint32_t) bytes[0]
63 | ((uint32_t) bytes[1] << 8)
64 | ((uint32_t) bytes[2] << 16)
65 | ((uint32_t) bytes[3] << 24));
66 }
67
68 static void UInt32ToBytes(uint8_t bytes[], uint32_t n) {
69 bytes[0] = n & 0xff;
70 bytes[1] = (n >> 8) & 0xff;
71 bytes[2] = (n >> 16) & 0xff;
72 bytes[3] = (n >> 24) & 0xff;
73 }
74
75 static bool CreateGUID(GUID *guid) {
76#if defined(HAVE_ARC4RANDOM) // Android, BSD, ...
77 CreateGuidFromArc4Random(guid);
78#else // Linux
79 bool success = false;
80
81#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
82 success = CreateGUIDFromGetrandom(guid);
83#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM
84 if (!success) {
85 success = CreateGUIDFromDevUrandom(guid);
86 }
87
88 if (!success) {
89 CreateGUIDFromRand(guid);
90 success = true;
91 }
92#endif
93
94 // Put in the version according to RFC 4122.
95 guid->data3 &= 0x0fff;
96 guid->data3 |= 0x4000;
97
98 // Put in the variant according to RFC 4122.
99 guid->data4[0] &= 0x3f;
100 guid->data4[0] |= 0x80;
101
102 return true;
103 }
104
105 private:
106#ifdef HAVE_ARC4RANDOM
107 static void CreateGuidFromArc4Random(GUID *guid) {
108 char *buf = reinterpret_cast<char*>(guid);
109
110 for (size_t i = 0; i < sizeof(GUID); i += sizeof(uint32_t)) {
111 uint32_t random_data = arc4random();
112
113 memcpy(buf + i, &random_data, sizeof(uint32_t));
114 }
115 }
116#else
117 static void InitOnce() {
118 pthread_once(&once_control, &InitOnceImpl);
119 }
120
121 static void InitOnceImpl() {
122 // time(NULL) is a very poor seed, so lacking anything better mix an
123 // address into it. We drop the four rightmost bits as they're likely to
124 // be 0 on almost all architectures.
125 srand(time(NULL) | ((uintptr_t)&once_control >> 4));
126 }
127
128 static pthread_once_t once_control;
129
130#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
131 static bool CreateGUIDFromGetrandom(GUID *guid) {
132 char *buf = reinterpret_cast<char*>(guid);
133 int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK);
134
135 return (read_bytes == static_cast<int>(sizeof(GUID)));
136 }
137#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM
138
139 // Populate the GUID using random bytes read from /dev/urandom, returns false
140 // if the GUID wasn't fully populated with random data.
141 static bool CreateGUIDFromDevUrandom(GUID *guid) {
142 char *buf = reinterpret_cast<char*>(guid);
143 int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
144
145 if (fd == -1) {
146 return false;
147 }
148
149 ssize_t read_bytes = HANDLE_EINTR(read(fd, buf, sizeof(GUID)));
150 close(fd);
151
152 return (read_bytes == static_cast<ssize_t>(sizeof(GUID)));
153 }
154
155 // Populate the GUID using a stream of random bytes obtained from rand().
156 static void CreateGUIDFromRand(GUID *guid) {
157 char *buf = reinterpret_cast<char*>(guid);
158
159 InitOnce();
160
161 for (size_t i = 0; i < sizeof(GUID); i++) {
162 buf[i] = rand();
163 }
164 }
165#endif
166};
167
168#ifndef HAVE_ARC4RANDOM
169pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
170#endif
171
172bool CreateGUID(GUID *guid) {
173 return GUIDGenerator::CreateGUID(guid);
174}
175
176// Parse guid to string.
177bool GUIDToString(const GUID *guid, char *buf, int buf_len) {
178 // Should allow more space the the max length of GUID.
179 assert(buf_len > kGUIDStringLength);
180 int num = snprintf(buf, buf_len, kGUIDFormatString,
181 guid->data1, guid->data2, guid->data3,
182 GUIDGenerator::BytesToUInt32(&(guid->data4[0])),
183 GUIDGenerator::BytesToUInt32(&(guid->data4[4])));
184 if (num != kGUIDStringLength)
185 return false;
186
187 buf[num] = '\0';
188 return true;
189}
190