From b4089e5e6e7b7fcd241ad334cf84c51470110aeb Mon Sep 17 00:00:00 2001
From: Li Hang <lihang48@huawei.com>
Date: Wed, 10 Jun 2020 11:45:24 +0800
Subject: [PATCH 1/1] common, rgw: add openssl engine support

this patch enables the use of openssl engine for
specific openssl algorithm(like DIGESTS/CIPHERS/...)

we can pass configuration to ceph by opt "openssl_engine_opts"
like "engine_id=engine1,dynamic_path=/some/path/engine1.so,
default_algorithms=DIGESTS:engine_id=engine2,dynamic_path=
/some/path/engine2.so,default_algorithms=CIPHERS,
other_ctrl=other_value"
---
 src/common/CMakeLists.txt          |   1 +
 src/common/legacy_config_opts.h    |   2 +-
 src/common/openssl_opts_handler.cc | 138 +++++++++++++++++++++++++++++
 src/common/openssl_opts_handler.h  |  24 +++++
 src/common/options.cc              |   6 ++
 src/rgw/rgw_main.cc                |   3 +
 6 files changed, 173 insertions(+), 1 deletion(-)
 create mode 100644 src/common/openssl_opts_handler.cc
 create mode 100644 src/common/openssl_opts_handler.h

diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index bd9276520d..2a653a772b 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -76,6 +76,7 @@ set(common_srcs
   mime.c
   mutex_debug.cc
   numa.cc
+  openssl_opts_handler.cc
   options.cc
   page.cc
   perf_counters.cc
diff --git a/src/common/legacy_config_opts.h b/src/common/legacy_config_opts.h
index 7aae31e8ea..af4e544c48 100644
--- a/src/common/legacy_config_opts.h
+++ b/src/common/legacy_config_opts.h
@@ -103,7 +103,7 @@ OPTION(plugin_crypto_accelerator, OPT_STR)
 
 OPTION(mempool_debug, OPT_BOOL)
 
-
+OPTION(openssl_engine_opts, OPT_STR)
 
 OPTION(key, OPT_STR)
 OPTION(keyfile, OPT_STR)
diff --git a/src/common/openssl_opts_handler.cc b/src/common/openssl_opts_handler.cc
new file mode 100644
index 0000000000..c0164dc7b4
--- /dev/null
+++ b/src/common/openssl_opts_handler.cc
@@ -0,0 +1,138 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include "openssl_opts_handler.h"
+
+#include <openssl/bio.h>
+#include <openssl/conf.h>
+#include <openssl/engine.h>
+#include <mutex>
+#include <vector>
+#include <algorithm>
+
+#include "common/debug.h"
+#include "global/global_context.h"
+#include "include/str_list.h"
+#include "include/scope_guard.h"
+
+using std::string;
+using std::vector;
+
+// -----------------------------------------------------------------------------
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_common
+#undef dout_prefix
+#define dout_prefix _prefix(_dout)
+
+static ostream &_prefix(std::ostream *_dout)
+{
+  return *_dout << "OpenSSLOptsHandler: ";
+}
+// -----------------------------------------------------------------------------
+
+string construct_engine_conf(const string &opts)
+{
+  const string conf_header = "openssl_conf=openssl_def\n[openssl_def]\n";
+  const string engine_header = "engines=engine_section\n[engine_section]\n";
+
+  string engine_id, engine_statement, engine_detail;
+  const string id_prefix = "engine";
+  const string suffix = "_section";
+  const char delimiter = '\n';
+
+  int index = 1;
+  vector<string> confs = get_str_vec(opts, ":");
+  for (auto conf : confs) {
+    // Construct engine section statement like "engine1=engine1_section"
+    engine_id = id_prefix + to_string(index++);
+    engine_statement += engine_id + "=" + engine_id + suffix + delimiter;
+
+    // Adapt to OpenSSL parser
+    // Replace ',' with '\n' and add section in front
+    std::replace(conf.begin(), conf.end(), ',', delimiter);
+    engine_detail += "[" + engine_id + suffix + "]" + delimiter;
+    engine_detail += conf + delimiter;
+  }
+
+  return conf_header + engine_header + engine_statement + engine_detail;
+}
+
+string get_openssl_error()
+{
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (bio == nullptr) {
+    return "failed to create BIO for more error printing";
+  }
+  ERR_print_errors(bio);
+  char* buf;
+  size_t len = BIO_get_mem_data(bio, &buf);
+  string ret(buf, len);
+  BIO_free(bio);
+  return ret;
+}
+
+void log_error(const string &err)
+{
+  derr << "Intended OpenSSL engine acceleration failed.\n"
+       << "set by openssl_engine_opts = "
+       << g_ceph_context->_conf->openssl_engine_opts
+       << "\ndetail error information:\n" << err << dendl;
+}
+
+void load_module(const string &engine_conf)
+{
+  BIO *mem = BIO_new_mem_buf(engine_conf.c_str(), engine_conf.size());
+  if (mem == nullptr) {
+    log_error("failed to new BIO memory");
+    return;
+  }
+  auto sg_mem = make_scope_guard([&mem] { BIO_free(mem); });
+
+  CONF *conf = NCONF_new(nullptr);
+  if (conf == nullptr) {
+    log_error("failed to new OpenSSL CONF");
+    return;
+  }
+  auto sg_conf = make_scope_guard([&conf] { NCONF_free(conf); });
+
+  if (NCONF_load_bio(conf, mem, nullptr) <= 0) {
+    log_error("failed to load CONF from BIO:\n" + get_openssl_error());
+    return;
+  }
+
+  OPENSSL_load_builtin_modules();
+  ENGINE_load_builtin_engines();
+
+  if (CONF_modules_load(
+          conf, nullptr,
+          CONF_MFLAGS_DEFAULT_SECTION | CONF_MFLAGS_IGNORE_MISSING_FILE) <= 0) {
+    log_error("failed to load modules from CONF:\n" + get_openssl_error());
+  }
+}
+
+void init_engine()
+{
+  string opts = g_ceph_context->_conf->openssl_engine_opts;
+  if (opts.empty()) {
+    return;
+  }
+  string engine_conf = construct_engine_conf(opts);
+  load_module(engine_conf);
+}
+
+void ceph::crypto::init_openssl_engine_once()
+{
+  static std::once_flag flag;
+  std::call_once(flag, init_engine);
+}
diff --git a/src/common/openssl_opts_handler.h b/src/common/openssl_opts_handler.h
new file mode 100644
index 0000000000..cad9060c9f
--- /dev/null
+++ b/src/common/openssl_opts_handler.h
@@ -0,0 +1,24 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef CEPH_OPENSSL_OPTS_HANDLER_H
+#define CEPH_OPENSSL_OPTS_HANDLER_H
+
+namespace ceph {
+  namespace crypto {
+    void init_openssl_engine_once();
+  }
+}
+
+#endif
diff --git a/src/common/options.cc b/src/common/options.cc
index 8135ea8f1e..4032f8cebc 100644
--- a/src/common/options.cc
+++ b/src/common/options.cc
@@ -824,6 +824,12 @@ std::vector<Option> get_global_options() {
     .set_default("crypto_isal")
     .set_description("Crypto accelerator library to use"),
 
+    Option("openssl_engine_opts", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("")
+    .set_flag(Option::FLAG_STARTUP)
+    .set_description("Use engine for specific openssl algorithm")
+    .set_long_description("Pass opts in this way: engine_id=engine1,dynamic_path=/some/path/engine1.so,default_algorithms=DIGESTS:engine_id=engine2,dynamic_path=/some/path/engine2.so,default_algorithms=CIPHERS,other_ctrl=other_value"),
+
     Option("mempool_debug", Option::TYPE_BOOL, Option::LEVEL_DEV)
     .set_default(false)
     .set_flag(Option::FLAG_NO_MON_UPDATE)
diff --git a/src/rgw/rgw_main.cc b/src/rgw/rgw_main.cc
index a27c4beba9..3569ace07c 100644
--- a/src/rgw/rgw_main.cc
+++ b/src/rgw/rgw_main.cc
@@ -9,6 +9,7 @@
 #include "common/Timer.h"
 #include "common/safe_io.h"
 #include "common/TracepointProvider.h"
+#include "common/openssl_opts_handler.h"
 #include "include/compat.h"
 #include "include/str_list.h"
 #include "include/stringify.h"
@@ -283,6 +284,8 @@ int main(int argc, const char **argv)
   init_timer.add_event_after(g_conf()->rgw_init_timeout, new C_InitTimeout);
   mutex.Unlock();
 
+  ceph::crypto::init_openssl_engine_once();
+
   common_init_finish(g_ceph_context);
 
   init_async_signal_handler();
-- 
2.17.1

