| // 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 <linux/random.h> | |
| #include <stdio.h> | |
| #include <sys/syscall.h> | |
| #include <unistd.h> | |
| #include "../internal.h" | |
| using namespace bssl; | |
| #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; | |
| } | |
| // 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() { | |
| 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; | |
| } | |
| // 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 bssl::CRYPTO_init_sysrand() { CRYPTO_once(&rand_once, init_once); } | |
| // CRYPTO_sysrand writes |len| bytes of entropy into |out|. | |
| void bssl::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) { | |
| r = boringssl_getrandom(out, len, 0); | |
| } 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 |