From: Kefu Chai Date: Tue, 24 Sep 2019 10:53:20 +0000 (+0800) Subject: auth/Crypto: fallback to /dev/urandom if getentropy() fails X-Git-Tag: v15.1.0~1386^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=bb6d9c32ad6a5c5bba2e688837149c5cff6697a3;p=ceph-ci.git 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 --- diff --git a/src/auth/Crypto.cc b/src/auth/Crypto.cc index def55596311..0eadacd887d 100644 --- a/src/auth/Crypto.cc +++ b/src/auth/Crypto.cc @@ -39,12 +39,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()); } @@ -54,7 +79,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()); @@ -76,6 +101,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 06cc58d4a90..dc03dfe4483 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; }; /*