]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
auth/Crypto: fallback to /dev/urandom if getentropy() fails
authorKefu Chai <kchai@redhat.com>
Tue, 24 Sep 2019 10:53:20 +0000 (18:53 +0800)
committerKefu Chai <kchai@redhat.com>
Tue, 24 Sep 2019 13:30:54 +0000 (21:30 +0800)
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 <kchai@redhat.com>
src/auth/Crypto.cc
src/auth/Crypto.h

index def55596311690dd7c810462df52f326be3d9793..0eadacd887dd7ee588f900aa79fb646021a64e4d 100644 (file)
 
 #include <unistd.h>
 
-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
index 06cc58d4a900ad070adc0110015d29ca4e699f67..dc03dfe448387966f11d31c7ab237970bd569d6d 100644 (file)
@@ -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;
 };
 
 /*