]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
9f95a23c TL |
15 | #include <vector> |
16 | ||
11fdf7f2 | 17 | #include "common/ceph_context.h" |
9f95a23c | 18 | #include "common/ceph_mutex.h" |
7c673cae | 19 | #include "common/config.h" |
7c673cae | 20 | #include "ceph_crypto.h" |
7c673cae | 21 | |
11fdf7f2 | 22 | #include <openssl/evp.h> |
11fdf7f2 | 23 | |
9f95a23c TL |
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 | ||
2a845540 TL |
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 | ||
9f95a23c TL |
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) | |
7c673cae | 63 | { |
9f95a23c TL |
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(); | |
7c673cae | 79 | } |
7c673cae | 80 | } |
9f95a23c TL |
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 */ | |
7c673cae | 96 | |
9f95a23c TL |
97 | static void init() { |
98 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
7c673cae | 99 | if (++crypto_refs == 1) { |
9f95a23c TL |
100 | // according to |
101 | // https://wiki.openssl.org/index.php/Library_Initialization#libcrypto_Initialization | |
102 | OpenSSL_add_all_algorithms(); | |
103 | ERR_load_crypto_strings(); | |
7c673cae | 104 | |
9f95a23c TL |
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)); | |
7c673cae | 126 | } |
9f95a23c | 127 | #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ |
7c673cae FG |
128 | } |
129 | ||
9f95a23c TL |
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); | |
7c673cae | 149 | } |
7c673cae | 150 | } |
9f95a23c TL |
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 */ | |
7c673cae FG |
169 | } |
170 | ||
9f95a23c TL |
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(); | |
7c673cae FG |
181 | } |
182 | ||
9f95a23c TL |
183 | void shutdown([[maybe_unused]] const bool shared) { |
184 | ssl::shutdown(); | |
185 | } | |
11fdf7f2 | 186 | |
9f95a23c TL |
187 | void zeroize_for_security(void* const s, const size_t n) { |
188 | ssl::zeroize_for_security(s, n); | |
189 | } | |
11fdf7f2 | 190 | |
9f95a23c | 191 | ssl::OpenSSLDigest::OpenSSLDigest(const EVP_MD * _type) |
11fdf7f2 TL |
192 | : mpContext(EVP_MD_CTX_create()) |
193 | , mpType(_type) { | |
194 | this->Restart(); | |
195 | } | |
196 | ||
9f95a23c | 197 | ssl::OpenSSLDigest::~OpenSSLDigest() { |
11fdf7f2 | 198 | EVP_MD_CTX_destroy(mpContext); |
1e59de90 TL |
199 | if (mpType_FIPS) { |
200 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | |
201 | EVP_MD_free(mpType_FIPS); | |
202 | #endif // OPENSSL_VERSION_NUMBER >= 0x30000000L | |
203 | } | |
11fdf7f2 TL |
204 | } |
205 | ||
9f95a23c | 206 | void ssl::OpenSSLDigest::Restart() { |
1e59de90 TL |
207 | if (mpType_FIPS) { |
208 | EVP_DigestInit_ex(mpContext, mpType_FIPS, NULL); | |
209 | } else { | |
210 | EVP_DigestInit_ex(mpContext, mpType, NULL); | |
211 | } | |
11fdf7f2 TL |
212 | } |
213 | ||
20effc67 | 214 | void ssl::OpenSSLDigest::SetFlags(int flags) { |
1e59de90 TL |
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 | } | |
20effc67 TL |
222 | this->Restart(); |
223 | } | |
224 | ||
9f95a23c | 225 | void ssl::OpenSSLDigest::Update(const unsigned char *input, size_t length) { |
11fdf7f2 TL |
226 | if (length) { |
227 | EVP_DigestUpdate(mpContext, const_cast<void *>(reinterpret_cast<const void *>(input)), length); | |
228 | } | |
229 | } | |
230 | ||
9f95a23c | 231 | void ssl::OpenSSLDigest::Final(unsigned char *digest) { |
11fdf7f2 TL |
232 | unsigned int s; |
233 | EVP_DigestFinal_ex(mpContext, digest, &s); | |
234 | } | |
9f95a23c | 235 | |
92f5a8d4 | 236 | } |
2a845540 TL |
237 | |
238 | #pragma clang diagnostic pop | |
239 | #pragma GCC diagnostic pop |