1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/rand_util.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <sys/syscall.h>
12 #include <sys/utsname.h>
13 #include <unistd.h>
14
15 #include "base/check.h"
16 #include "base/compiler_specific.h"
17 #include "base/feature_list.h"
18 #include "base/files/file_util.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/no_destructor.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/time/time.h"
23 #include "build/build_config.h"
24
25 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
26 #include "third_party/lss/linux_syscall_support.h"
27 #elif BUILDFLAG(IS_MAC)
28 // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK.
29 // (See below.)
30 #include <sys/random.h>
31 #endif
32
33 #if !BUILDFLAG(IS_NACL)
34 #include "third_party/boringssl/src/include/openssl/crypto.h"
35 #include "third_party/boringssl/src/include/openssl/rand.h"
36 #endif
37
38 namespace base {
39
40 namespace {
41
42 #if BUILDFLAG(IS_AIX)
43 // AIX has no 64-bit support for O_CLOEXEC.
44 static constexpr int kOpenFlags = O_RDONLY;
45 #else
46 static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
47 #endif
48
49 // We keep the file descriptor for /dev/urandom around so we don't need to
50 // reopen it (which is expensive), and since we may not even be able to reopen
51 // it if we are later put in a sandbox. This class wraps the file descriptor so
52 // we can use a static-local variable to handle opening it on the first access.
53 class URandomFd {
54 public:
URandomFd()55 URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
56 CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
57 }
58
~URandomFd()59 ~URandomFd() { close(fd_); }
60
fd() const61 int fd() const { return fd_; }
62
63 private:
64 const int fd_;
65 };
66
67 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
68 BUILDFLAG(IS_ANDROID)) && \
69 !BUILDFLAG(IS_NACL)
70 // TODO(pasko): Unify reading kernel version numbers in:
71 // mojo/core/channel_linux.cc
72 // chrome/browser/android/seccomp_support_detector.cc
KernelVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)73 void KernelVersionNumbers(int32_t* major_version,
74 int32_t* minor_version,
75 int32_t* bugfix_version) {
76 struct utsname info;
77 if (uname(&info) < 0) {
78 NOTREACHED();
79 *major_version = 0;
80 *minor_version = 0;
81 *bugfix_version = 0;
82 return;
83 }
84 int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
85 bugfix_version);
86 if (num_read < 1)
87 *major_version = 0;
88 if (num_read < 2)
89 *minor_version = 0;
90 if (num_read < 3)
91 *bugfix_version = 0;
92 }
93
KernelSupportsGetRandom()94 bool KernelSupportsGetRandom() {
95 int32_t major = 0;
96 int32_t minor = 0;
97 int32_t bugfix = 0;
98 KernelVersionNumbers(&major, &minor, &bugfix);
99 if (major > 3 || (major == 3 && minor >= 17))
100 return true;
101 return false;
102 }
103
GetRandomSyscall(void * output,size_t output_length)104 bool GetRandomSyscall(void* output, size_t output_length) {
105 // We have to call `getrandom` via Linux Syscall Support, rather than through
106 // the libc wrapper, because we might not have an up-to-date libc (e.g. on
107 // some bots).
108 const ssize_t r =
109 HANDLE_EINTR(syscall(__NR_getrandom, output, output_length, 0));
110
111 // Return success only on total success. In case errno == ENOSYS (or any other
112 // error), we'll fall through to reading from urandom below.
113 if (output_length == static_cast<size_t>(r)) {
114 MSAN_UNPOISON(output, output_length);
115 return true;
116 }
117 return false;
118 }
119 #endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
120 // BUILDFLAG(IS_ANDROID)) && !BUILDFLAG(IS_NACL)
121
122 #if BUILDFLAG(IS_ANDROID)
123 std::atomic<bool> g_use_getrandom;
124
125 // Note: the BoringSSL feature takes precedence over the getrandom() trial if
126 // both are enabled.
127 BASE_FEATURE(kUseGetrandomForRandBytes,
128 "UseGetrandomForRandBytes",
129 FEATURE_ENABLED_BY_DEFAULT);
130
UseGetrandom()131 bool UseGetrandom() {
132 return g_use_getrandom.load(std::memory_order_relaxed);
133 }
134 #elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
UseGetrandom()135 bool UseGetrandom() {
136 return true;
137 }
138 #endif
139
140 } // namespace
141
142 namespace internal {
143
144 #if BUILDFLAG(IS_ANDROID)
ConfigureRandBytesFieldTrial()145 void ConfigureRandBytesFieldTrial() {
146 g_use_getrandom.store(FeatureList::IsEnabled(kUseGetrandomForRandBytes),
147 std::memory_order_relaxed);
148 }
149 #endif
150
151 namespace {
152
153 #if !BUILDFLAG(IS_NACL)
154 // The BoringSSl helpers are duplicated in rand_util_fuchsia.cc and
155 // rand_util_win.cc.
156 std::atomic<bool> g_use_boringssl;
157
158 BASE_FEATURE(kUseBoringSSLForRandBytes,
159 "UseBoringSSLForRandBytes",
160 FEATURE_DISABLED_BY_DEFAULT);
161
162 } // namespace
163
ConfigureBoringSSLBackedRandBytesFieldTrial()164 void ConfigureBoringSSLBackedRandBytesFieldTrial() {
165 g_use_boringssl.store(FeatureList::IsEnabled(kUseBoringSSLForRandBytes),
166 std::memory_order_relaxed);
167 }
168
UseBoringSSLForRandBytes()169 bool UseBoringSSLForRandBytes() {
170 return g_use_boringssl.load(std::memory_order_relaxed);
171 }
172 #endif
173
174 } // namespace internal
175
176 namespace {
177
RandBytes(void * output,size_t output_length,bool avoid_allocation)178 void RandBytes(void* output, size_t output_length, bool avoid_allocation) {
179 #if !BUILDFLAG(IS_NACL)
180 // The BoringSSL experiment takes priority over everything else.
181 if (!avoid_allocation && internal::UseBoringSSLForRandBytes()) {
182 // Ensure BoringSSL is initialized so it can use things like RDRAND.
183 CRYPTO_library_init();
184 // BoringSSL's RAND_bytes always returns 1. Any error aborts the program.
185 (void)RAND_bytes(static_cast<uint8_t*>(output), output_length);
186 return;
187 }
188 #endif
189 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
190 BUILDFLAG(IS_ANDROID)) && \
191 !BUILDFLAG(IS_NACL)
192 if (avoid_allocation || UseGetrandom()) {
193 // On Android it is mandatory to check that the kernel _version_ has the
194 // support for a syscall before calling. The same check is made on Linux and
195 // ChromeOS to avoid making a syscall that predictably returns ENOSYS.
196 static const bool kernel_has_support = KernelSupportsGetRandom();
197 if (kernel_has_support && GetRandomSyscall(output, output_length))
198 return;
199 }
200 #elif BUILDFLAG(IS_MAC)
201 // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives
202 // in its SDK.
203 if (getentropy(output, output_length) == 0) {
204 return;
205 }
206 #endif
207
208 // If the OS-specific mechanisms didn't work, fall through to reading from
209 // urandom.
210 //
211 // TODO(crbug.com/995996): When we no longer need to support old Linux
212 // kernels, we can get rid of this /dev/urandom branch altogether.
213 const int urandom_fd = GetUrandomFD();
214 const bool success =
215 ReadFromFD(urandom_fd, static_cast<char*>(output), output_length);
216 CHECK(success);
217 }
218
219 } // namespace
220
221 namespace internal {
222
RandDoubleAvoidAllocation()223 double RandDoubleAvoidAllocation() {
224 uint64_t number;
225 RandBytes(&number, sizeof(number), /*avoid_allocation=*/true);
226 // This transformation is explained in rand_util.cc.
227 return (number >> 11) * 0x1.0p-53;
228 }
229
230 } // namespace internal
231
RandBytes(void * output,size_t output_length)232 void RandBytes(void* output, size_t output_length) {
233 RandBytes(output, output_length, /*avoid_allocation=*/false);
234 }
235
GetUrandomFD()236 int GetUrandomFD() {
237 static NoDestructor<URandomFd> urandom_fd;
238 return urandom_fd->fd();
239 }
240
241 } // namespace base
242