From 958e43a1abba257755129247dc4f82915accf9b1 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Tue, 24 Sep 2019 18:53:20 +0800 Subject: [PATCH 1/2] auth/Crypto: fallback to /dev/urandom if getentropy() fails we still support Linux 3.10, which does not support `getrandom(2)`. so even if the glibc installed on the building host offers `getentropy()` call, we still need to check for the syscall's existence at runtime in case that the Linux kernel version is lower than 3.17 which introduced the support of `getrandom(2)`. in this chance, we detect the existence of `getentropy()` by using it when constructing `CryptoRandom`, this system call could fail due to 3 reasons: 1. ENOSYS: the kernel does not support `getrandom(2)` 2. EPERM: the syscall is blocked by a security policy 3. EINTR: interrupted by a signal so we fall back to /dev/urandom in cases of ENOSYS and EPERM, retry on EINTR, otherwise, an exception is thrown. Fixes: https://tracker.ceph.com/issues/42018 Signed-off-by: Kefu Chai (cherry picked from commit bb6d9c32ad6a5c5bba2e688837149c5cff6697a3) --- src/auth/Crypto.cc | 41 +++++++++++++++++++++++++++++++++++++---- src/auth/Crypto.h | 7 ++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/auth/Crypto.cc b/src/auth/Crypto.cc index 8b355bf114230..49b32cab6f22e 100644 --- a/src/auth/Crypto.cc +++ b/src/auth/Crypto.cc @@ -41,12 +41,37 @@ #include -CryptoRandom::CryptoRandom() : fd(0) {} -CryptoRandom::~CryptoRandom() = default; +static bool getentropy_works() +{ + char buf; + auto ret = TEMP_FAILURE_RETRY(::getentropy(&buf, sizeof(buf))); + if (ret == 0) { + return true; + } else if (errno == ENOSYS || errno == EPERM) { + return false; + } else { + throw std::system_error(errno, std::system_category()); + } +} + +CryptoRandom::CryptoRandom() : fd(getentropy_works() ? -1 : open_urandom()) +{} + +CryptoRandom::~CryptoRandom() +{ + if (fd >= 0) { + VOID_TEMP_FAILURE_RETRY(::close(fd)); + } +} void CryptoRandom::get_bytes(char *buf, int len) { - auto ret = TEMP_FAILURE_RETRY(::getentropy(buf, len)); + ssize_t ret = 0; + if (unlikely(fd >= 0)) { + ret = safe_read_exact(fd, buf, len); + } else { + ret = TEMP_FAILURE_RETRY(::getentropy(buf, len)); + } if (ret < 0) { throw std::system_error(errno, std::system_category()); } @@ -56,7 +81,7 @@ void CryptoRandom::get_bytes(char *buf, int len) // open /dev/urandom once on construction and reuse the fd for all reads CryptoRandom::CryptoRandom() - : fd(TEMP_FAILURE_RETRY(::open("/dev/urandom", O_CLOEXEC|O_RDONLY))) + : fd{open_urandom()} { if (fd < 0) { throw std::system_error(errno, std::system_category()); @@ -78,6 +103,14 @@ void CryptoRandom::get_bytes(char *buf, int len) #endif +int CryptoRandom::open_urandom() +{ + int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_CLOEXEC|O_RDONLY)); + if (fd < 0) { + throw std::system_error(errno, std::system_category()); + } + return fd; +} // --------------------------------------------------- // fallback implementation of the bufferlist-free diff --git a/src/auth/Crypto.h b/src/auth/Crypto.h index c3ace5c8aff5a..5ccc20fd56edc 100644 --- a/src/auth/Crypto.h +++ b/src/auth/Crypto.h @@ -29,13 +29,14 @@ namespace ceph { class Formatter; } * Random byte stream generator suitable for cryptographic use */ class CryptoRandom { - const int fd; - public: +public: CryptoRandom(); // throws on failure ~CryptoRandom(); - /// copy up to 256 random bytes into the given buffer. throws on failure void get_bytes(char *buf, int len); +private: + static int open_urandom(); + const int fd; }; /* From b1d44987109e792521be0dce7f0af12445814d45 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Tue, 24 Sep 2019 19:02:38 +0800 Subject: [PATCH 2/2] auth/Crypto: assert(len <= 256) before calling getentropy() this assert() will be optimized out in Release mode. Signed-off-by: Kefu Chai (cherry picked from commit ccae47ea08a51f6a1d6c237cde75732dbb7b74df) --- src/auth/Crypto.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/auth/Crypto.cc b/src/auth/Crypto.cc index 49b32cab6f22e..67c6643bb315c 100644 --- a/src/auth/Crypto.cc +++ b/src/auth/Crypto.cc @@ -70,6 +70,8 @@ void CryptoRandom::get_bytes(char *buf, int len) if (unlikely(fd >= 0)) { ret = safe_read_exact(fd, buf, len); } else { + // getentropy() reads up to 256 bytes + assert(len <= 256); ret = TEMP_FAILURE_RETRY(::getentropy(buf, len)); } if (ret < 0) {