| // Copyright 2014 The BoringSSL Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #if !defined(_GNU_SOURCE) |
| #define _GNU_SOURCE // needed for syscall() on Linux. |
| #endif |
| |
| #include <openssl/rand.h> |
| |
| #include "../bcm_support.h" |
| #include "internal.h" |
| |
| #if defined(OPENSSL_RAND_URANDOM) |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <sys/syscall.h> |
| #include <unistd.h> |
| |
| #include "../internal.h" |
| #include "getrandom_fillin.h" |
| |
| |
| #if defined(USE_NR_getrandom) |
| |
| #if defined(OPENSSL_MSAN) |
| extern "C" { |
| void __msan_unpoison(void *, size_t); |
| } |
| #endif |
| |
| static ssize_t boringssl_getrandom(void *buf, size_t buf_len, unsigned flags) { |
| ssize_t ret; |
| do { |
| ret = syscall(__NR_getrandom, buf, buf_len, flags); |
| } while (ret == -1 && errno == EINTR); |
| |
| #if defined(OPENSSL_MSAN) |
| if (ret > 0) { |
| // MSAN doesn't recognise |syscall| and thus doesn't notice that we have |
| // initialised the output buffer. |
| __msan_unpoison(buf, ret); |
| } |
| #endif // OPENSSL_MSAN |
| |
| return ret; |
| } |
| |
| #endif // USE_NR_getrandom |
| |
| // kHaveGetrandom in |urandom_fd| signals that |getrandom| or |getentropy| is |
| // available and should be used instead. |
| static const int kHaveGetrandom = -3; |
| |
| // urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|. |
| static int urandom_fd; |
| |
| static CRYPTO_once_t rand_once = CRYPTO_ONCE_INIT; |
| |
| // init_once initializes the state of this module to values previously |
| // requested. This is the only function that modifies |urandom_fd|, which may be |
| // read safely after calling the once. |
| static void init_once(void) { |
| #if defined(USE_NR_getrandom) |
| int have_getrandom; |
| uint8_t dummy; |
| ssize_t getrandom_ret = |
| boringssl_getrandom(&dummy, sizeof(dummy), GRND_NONBLOCK); |
| if (getrandom_ret == 1) { |
| have_getrandom = 1; |
| } else if (getrandom_ret == -1 && errno == EAGAIN) { |
| // We have getrandom, but the entropy pool has not been initialized yet. |
| have_getrandom = 1; |
| } else if (getrandom_ret == -1 && errno == ENOSYS) { |
| // Fallthrough to using /dev/urandom, below. |
| have_getrandom = 0; |
| } else { |
| // Other errors are fatal. |
| perror("getrandom"); |
| abort(); |
| } |
| |
| if (have_getrandom) { |
| urandom_fd = kHaveGetrandom; |
| return; |
| } |
| #endif // USE_NR_getrandom |
| |
| // FIPS builds must support getrandom. |
| #if defined(BORINGSSL_FIPS) |
| perror("getrandom not found"); |
| abort(); |
| #endif |
| |
| int fd; |
| do { |
| fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); |
| } while (fd == -1 && errno == EINTR); |
| |
| if (fd < 0) { |
| perror("failed to open /dev/urandom"); |
| abort(); |
| } |
| |
| urandom_fd = fd; |
| } |
| |
| void CRYPTO_init_sysrand(void) { CRYPTO_once(&rand_once, init_once); } |
| |
| // CRYPTO_sysrand writes |len| bytes of entropy into |out|. |
| void CRYPTO_sysrand(uint8_t *out, size_t len) { |
| if (len == 0) { |
| return; |
| } |
| |
| CRYPTO_init_sysrand(); |
| |
| // Clear |errno| so it has defined value if |read| or |getrandom| |
| // "successfully" returns zero. |
| errno = 0; |
| while (len > 0) { |
| ssize_t r; |
| |
| if (urandom_fd == kHaveGetrandom) { |
| #if defined(USE_NR_getrandom) |
| r = boringssl_getrandom(out, len, 0); |
| #else // USE_NR_getrandom |
| fprintf(stderr, "urandom fd corrupt.\n"); |
| abort(); |
| #endif |
| } else { |
| do { |
| r = read(urandom_fd, out, len); |
| } while (r == -1 && errno == EINTR); |
| } |
| |
| if (r <= 0) { |
| perror("entropy fill failed"); |
| abort(); |
| } |
| out += r; |
| len -= r; |
| } |
| } |
| |
| #endif // OPENSSL_RAND_URANDOM |