summaryrefslogtreecommitdiffstats
path: root/src/common/openssl_opts_handler.cc
blob: c0164dc7b4137ccb3324c4b0d5cee4f4d4e60c54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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);
}