]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/ceph_crypto.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / common / ceph_crypto.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2010-2011 Dreamhost
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include <vector>
16
17 #include "common/ceph_context.h"
18 #include "common/ceph_mutex.h"
19 #include "common/config.h"
20 #include "ceph_crypto.h"
21
22 #include <openssl/evp.h>
23
24 #if OPENSSL_VERSION_NUMBER < 0x10100000L
25 # include <openssl/conf.h>
26 # include <openssl/engine.h>
27 # include <openssl/err.h>
28 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
29
30 #pragma GCC diagnostic push
31 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
32
33 #pragma clang diagnostic push
34 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
35
36 namespace TOPNSPC::crypto::ssl {
37
38 #if OPENSSL_VERSION_NUMBER < 0x10100000L
39 static std::atomic_uint32_t crypto_refs;
40
41
42 static auto ssl_mutexes = ceph::make_lock_container<ceph::shared_mutex>(
43 static_cast<size_t>(std::max(CRYPTO_num_locks(), 0)),
44 [](const size_t i) {
45 return ceph::make_shared_mutex(
46 std::string("ssl-mutex-") + std::to_string(i));
47 });
48
49 static struct {
50 // we could use e.g. unordered_set instead at the price of providing
51 // std::hash<...> specialization. However, we can live with duplicates
52 // quite well while the benefit is not worth the effort.
53 std::vector<CRYPTO_THREADID> tids;
54 ceph::mutex lock = ceph::make_mutex("crypto::ssl::init_records::lock");;
55 } init_records;
56
57 static void
58 ssl_locking_callback(
59 const int mode,
60 const int mutex_num,
61 [[maybe_unused]] const char *file,
62 [[maybe_unused]] const int line)
63 {
64 if (mutex_num < 0 || static_cast<size_t>(mutex_num) >= ssl_mutexes.size()) {
65 ceph_assert_always("openssl passed wrong mutex index" == nullptr);
66 }
67
68 if (mode & CRYPTO_READ) {
69 if (mode & CRYPTO_LOCK) {
70 ssl_mutexes[mutex_num].lock_shared();
71 } else if (mode & CRYPTO_UNLOCK) {
72 ssl_mutexes[mutex_num].unlock_shared();
73 }
74 } else if (mode & CRYPTO_WRITE) {
75 if (mode & CRYPTO_LOCK) {
76 ssl_mutexes[mutex_num].lock();
77 } else if (mode & CRYPTO_UNLOCK) {
78 ssl_mutexes[mutex_num].unlock();
79 }
80 }
81 }
82
83 static unsigned long
84 ssl_get_thread_id(void)
85 {
86 static_assert(sizeof(unsigned long) >= sizeof(pthread_t));
87 /* pthread_t may be any data type, so a simple cast to unsigned long
88 * can rise a warning/error, depending on the platform.
89 * Here memcpy is used as an anything-to-anything cast. */
90 unsigned long ret = 0;
91 pthread_t t = pthread_self();
92 memcpy(&ret, &t, sizeof(pthread_t));
93 return ret;
94 }
95 #endif /* not OPENSSL_VERSION_NUMBER < 0x10100000L */
96
97 static void init() {
98 #if OPENSSL_VERSION_NUMBER < 0x10100000L
99 if (++crypto_refs == 1) {
100 // according to
101 // https://wiki.openssl.org/index.php/Library_Initialization#libcrypto_Initialization
102 OpenSSL_add_all_algorithms();
103 ERR_load_crypto_strings();
104
105 // initialize locking callbacks, needed for thread safety.
106 // http://www.openssl.org/support/faq.html#PROG1
107 CRYPTO_set_locking_callback(&ssl_locking_callback);
108 CRYPTO_set_id_callback(&ssl_get_thread_id);
109
110 OPENSSL_config(nullptr);
111 }
112
113 // we need to record IDs of all threads calling the initialization in
114 // order to *manually* free per-thread memory OpenSSL *automagically*
115 // allocated in ERR_get_state().
116 // XXX: this solution/nasty hack is IMPERFECT. A leak will appear when
117 // a client init()ializes the crypto subsystem with one thread and then
118 // uses it from another one in a way that results in ERR_get_state().
119 // XXX: for discussion about another approaches please refer to:
120 // https://www.mail-archive.com/openssl-users@openssl.org/msg59070.html
121 {
122 std::lock_guard l(init_records.lock);
123 CRYPTO_THREADID tmp;
124 CRYPTO_THREADID_current(&tmp);
125 init_records.tids.emplace_back(std::move(tmp));
126 }
127 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
128 }
129
130 static void shutdown() {
131 #if OPENSSL_VERSION_NUMBER < 0x10100000L
132 if (--crypto_refs != 0) {
133 return;
134 }
135
136 // drop error queue for each thread that called the init() function to
137 // satisfy valgrind.
138 {
139 std::lock_guard l(init_records.lock);
140
141 // NOTE: in OpenSSL 1.0.2g the signature is:
142 // void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
143 // but in 1.1.0j it has been changed to
144 // void ERR_remove_thread_state(void *);
145 // We're basing on the OPENSSL_VERSION_NUMBER check to preserve
146 // const-correctness without failing builds on modern envs.
147 for (const auto& tid : init_records.tids) {
148 ERR_remove_thread_state(&tid);
149 }
150 }
151
152 // Shutdown according to
153 // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
154 // http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
155 //
156 // The call to CONF_modules_free() has been introduced after a valgring run.
157 CRYPTO_set_locking_callback(nullptr);
158 CRYPTO_set_id_callback(nullptr);
159 ENGINE_cleanup();
160 CONF_modules_free();
161 CONF_modules_unload(1);
162 ERR_free_strings();
163 EVP_cleanup();
164 CRYPTO_cleanup_all_ex_data();
165
166 // NOTE: don't clear ssl_mutexes as we should be ready for init-deinit-init
167 // sequence.
168 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
169 }
170
171 void zeroize_for_security(void* const s, const size_t n) {
172 OPENSSL_cleanse(s, n);
173 }
174
175 } // namespace TOPNSPC::crypto::openssl
176
177
178 namespace TOPNSPC::crypto {
179 void init() {
180 ssl::init();
181 }
182
183 void shutdown([[maybe_unused]] const bool shared) {
184 ssl::shutdown();
185 }
186
187 void zeroize_for_security(void* const s, const size_t n) {
188 ssl::zeroize_for_security(s, n);
189 }
190
191 ssl::OpenSSLDigest::OpenSSLDigest(const EVP_MD * _type)
192 : mpContext(EVP_MD_CTX_create())
193 , mpType(_type) {
194 this->Restart();
195 }
196
197 ssl::OpenSSLDigest::~OpenSSLDigest() {
198 EVP_MD_CTX_destroy(mpContext);
199 if (mpType_FIPS) {
200 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
201 EVP_MD_free(mpType_FIPS);
202 #endif // OPENSSL_VERSION_NUMBER >= 0x30000000L
203 }
204 }
205
206 void ssl::OpenSSLDigest::Restart() {
207 if (mpType_FIPS) {
208 EVP_DigestInit_ex(mpContext, mpType_FIPS, NULL);
209 } else {
210 EVP_DigestInit_ex(mpContext, mpType, NULL);
211 }
212 }
213
214 void ssl::OpenSSLDigest::SetFlags(int flags) {
215 if (flags == EVP_MD_CTX_FLAG_NON_FIPS_ALLOW && OpenSSL_version_num() >= 0x30000000L && mpType == EVP_md5() && !mpType_FIPS) {
216 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
217 mpType_FIPS = EVP_MD_fetch(NULL, "MD5", "fips=no");
218 #endif // OPENSSL_VERSION_NUMBER >= 0x30000000L
219 } else {
220 EVP_MD_CTX_set_flags(mpContext, flags);
221 }
222 this->Restart();
223 }
224
225 void ssl::OpenSSLDigest::Update(const unsigned char *input, size_t length) {
226 if (length) {
227 EVP_DigestUpdate(mpContext, const_cast<void *>(reinterpret_cast<const void *>(input)), length);
228 }
229 }
230
231 void ssl::OpenSSLDigest::Final(unsigned char *digest) {
232 unsigned int s;
233 EVP_DigestFinal_ex(mpContext, digest, &s);
234 }
235
236 }
237
238 #pragma clang diagnostic pop
239 #pragma GCC diagnostic pop