]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.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)
committerNathan Cutler <ncutler@suse.com>
Thu, 31 Oct 2019 18:13:00 +0000 (19:13 +0100)
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>
(cherry picked from commit bb6d9c32ad6a5c5bba2e688837149c5cff6697a3)

src/auth/Crypto.cc
src/auth/Crypto.h

index 8b355bf1142301c112509334876102f8f79cab66..49b32cab6f22ee9f07779f0d08d16a67b487b17b 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());
   }
@@ -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
index c3ace5c8aff5a67074a08b9f26217653d32ef623..5ccc20fd56edcd29bb132e65b7bae38a7745eaec 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;
 };
 
 /*