]>
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 | ||
30 | namespace TOPNSPC::crypto::ssl { | |
31 | ||
32 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
33 | static std::atomic_uint32_t crypto_refs; | |
34 | ||
35 | ||
36 | static auto ssl_mutexes = ceph::make_lock_container<ceph::shared_mutex>( | |
37 | static_cast<size_t>(std::max(CRYPTO_num_locks(), 0)), | |
38 | [](const size_t i) { | |
39 | return ceph::make_shared_mutex( | |
40 | std::string("ssl-mutex-") + std::to_string(i)); | |
41 | }); | |
42 | ||
43 | static struct { | |
44 | // we could use e.g. unordered_set instead at the price of providing | |
45 | // std::hash<...> specialization. However, we can live with duplicates | |
46 | // quite well while the benefit is not worth the effort. | |
47 | std::vector<CRYPTO_THREADID> tids; | |
48 | ceph::mutex lock = ceph::make_mutex("crypto::ssl::init_records::lock");; | |
49 | } init_records; | |
50 | ||
51 | static void | |
52 | ssl_locking_callback( | |
53 | const int mode, | |
54 | const int mutex_num, | |
55 | [[maybe_unused]] const char *file, | |
56 | [[maybe_unused]] const int line) | |
7c673cae | 57 | { |
9f95a23c TL |
58 | if (mutex_num < 0 || static_cast<size_t>(mutex_num) >= ssl_mutexes.size()) { |
59 | ceph_assert_always("openssl passed wrong mutex index" == nullptr); | |
60 | } | |
61 | ||
62 | if (mode & CRYPTO_READ) { | |
63 | if (mode & CRYPTO_LOCK) { | |
64 | ssl_mutexes[mutex_num].lock_shared(); | |
65 | } else if (mode & CRYPTO_UNLOCK) { | |
66 | ssl_mutexes[mutex_num].unlock_shared(); | |
67 | } | |
68 | } else if (mode & CRYPTO_WRITE) { | |
69 | if (mode & CRYPTO_LOCK) { | |
70 | ssl_mutexes[mutex_num].lock(); | |
71 | } else if (mode & CRYPTO_UNLOCK) { | |
72 | ssl_mutexes[mutex_num].unlock(); | |
7c673cae | 73 | } |
7c673cae | 74 | } |
9f95a23c TL |
75 | } |
76 | ||
77 | static unsigned long | |
78 | ssl_get_thread_id(void) | |
79 | { | |
80 | static_assert(sizeof(unsigned long) >= sizeof(pthread_t)); | |
81 | /* pthread_t may be any data type, so a simple cast to unsigned long | |
82 | * can rise a warning/error, depending on the platform. | |
83 | * Here memcpy is used as an anything-to-anything cast. */ | |
84 | unsigned long ret = 0; | |
85 | pthread_t t = pthread_self(); | |
86 | memcpy(&ret, &t, sizeof(pthread_t)); | |
87 | return ret; | |
88 | } | |
89 | #endif /* not OPENSSL_VERSION_NUMBER < 0x10100000L */ | |
7c673cae | 90 | |
9f95a23c TL |
91 | static void init() { |
92 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
7c673cae | 93 | if (++crypto_refs == 1) { |
9f95a23c TL |
94 | // according to |
95 | // https://wiki.openssl.org/index.php/Library_Initialization#libcrypto_Initialization | |
96 | OpenSSL_add_all_algorithms(); | |
97 | ERR_load_crypto_strings(); | |
7c673cae | 98 | |
9f95a23c TL |
99 | // initialize locking callbacks, needed for thread safety. |
100 | // http://www.openssl.org/support/faq.html#PROG1 | |
101 | CRYPTO_set_locking_callback(&ssl_locking_callback); | |
102 | CRYPTO_set_id_callback(&ssl_get_thread_id); | |
103 | ||
104 | OPENSSL_config(nullptr); | |
105 | } | |
106 | ||
107 | // we need to record IDs of all threads calling the initialization in | |
108 | // order to *manually* free per-thread memory OpenSSL *automagically* | |
109 | // allocated in ERR_get_state(). | |
110 | // XXX: this solution/nasty hack is IMPERFECT. A leak will appear when | |
111 | // a client init()ializes the crypto subsystem with one thread and then | |
112 | // uses it from another one in a way that results in ERR_get_state(). | |
113 | // XXX: for discussion about another approaches please refer to: | |
114 | // https://www.mail-archive.com/openssl-users@openssl.org/msg59070.html | |
115 | { | |
116 | std::lock_guard l(init_records.lock); | |
117 | CRYPTO_THREADID tmp; | |
118 | CRYPTO_THREADID_current(&tmp); | |
119 | init_records.tids.emplace_back(std::move(tmp)); | |
7c673cae | 120 | } |
9f95a23c | 121 | #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ |
7c673cae FG |
122 | } |
123 | ||
9f95a23c TL |
124 | static void shutdown() { |
125 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
126 | if (--crypto_refs != 0) { | |
127 | return; | |
128 | } | |
129 | ||
130 | // drop error queue for each thread that called the init() function to | |
131 | // satisfy valgrind. | |
132 | { | |
133 | std::lock_guard l(init_records.lock); | |
134 | ||
135 | // NOTE: in OpenSSL 1.0.2g the signature is: | |
136 | // void ERR_remove_thread_state(const CRYPTO_THREADID *tid); | |
137 | // but in 1.1.0j it has been changed to | |
138 | // void ERR_remove_thread_state(void *); | |
139 | // We're basing on the OPENSSL_VERSION_NUMBER check to preserve | |
140 | // const-correctness without failing builds on modern envs. | |
141 | for (const auto& tid : init_records.tids) { | |
142 | ERR_remove_thread_state(&tid); | |
7c673cae | 143 | } |
7c673cae | 144 | } |
9f95a23c TL |
145 | |
146 | // Shutdown according to | |
147 | // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup | |
148 | // http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl | |
149 | // | |
150 | // The call to CONF_modules_free() has been introduced after a valgring run. | |
151 | CRYPTO_set_locking_callback(nullptr); | |
152 | CRYPTO_set_id_callback(nullptr); | |
153 | ENGINE_cleanup(); | |
154 | CONF_modules_free(); | |
155 | CONF_modules_unload(1); | |
156 | ERR_free_strings(); | |
157 | EVP_cleanup(); | |
158 | CRYPTO_cleanup_all_ex_data(); | |
159 | ||
160 | // NOTE: don't clear ssl_mutexes as we should be ready for init-deinit-init | |
161 | // sequence. | |
162 | #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ | |
7c673cae FG |
163 | } |
164 | ||
9f95a23c TL |
165 | void zeroize_for_security(void* const s, const size_t n) { |
166 | OPENSSL_cleanse(s, n); | |
167 | } | |
168 | ||
169 | } // namespace TOPNSPC::crypto::openssl | |
170 | ||
171 | ||
172 | namespace TOPNSPC::crypto { | |
173 | void init() { | |
174 | ssl::init(); | |
7c673cae FG |
175 | } |
176 | ||
9f95a23c TL |
177 | void shutdown([[maybe_unused]] const bool shared) { |
178 | ssl::shutdown(); | |
179 | } | |
11fdf7f2 | 180 | |
9f95a23c TL |
181 | void zeroize_for_security(void* const s, const size_t n) { |
182 | ssl::zeroize_for_security(s, n); | |
183 | } | |
11fdf7f2 | 184 | |
9f95a23c | 185 | ssl::OpenSSLDigest::OpenSSLDigest(const EVP_MD * _type) |
11fdf7f2 TL |
186 | : mpContext(EVP_MD_CTX_create()) |
187 | , mpType(_type) { | |
188 | this->Restart(); | |
189 | } | |
190 | ||
9f95a23c | 191 | ssl::OpenSSLDigest::~OpenSSLDigest() { |
11fdf7f2 TL |
192 | EVP_MD_CTX_destroy(mpContext); |
193 | } | |
194 | ||
9f95a23c | 195 | void ssl::OpenSSLDigest::Restart() { |
11fdf7f2 TL |
196 | EVP_DigestInit_ex(mpContext, mpType, NULL); |
197 | } | |
198 | ||
20effc67 TL |
199 | void ssl::OpenSSLDigest::SetFlags(int flags) { |
200 | EVP_MD_CTX_set_flags(mpContext, flags); | |
201 | this->Restart(); | |
202 | } | |
203 | ||
9f95a23c | 204 | void ssl::OpenSSLDigest::Update(const unsigned char *input, size_t length) { |
11fdf7f2 TL |
205 | if (length) { |
206 | EVP_DigestUpdate(mpContext, const_cast<void *>(reinterpret_cast<const void *>(input)), length); | |
207 | } | |
208 | } | |
209 | ||
9f95a23c | 210 | void ssl::OpenSSLDigest::Final(unsigned char *digest) { |
11fdf7f2 TL |
211 | unsigned int s; |
212 | EVP_DigestFinal_ex(mpContext, digest, &s); | |
213 | } | |
9f95a23c | 214 | |
92f5a8d4 | 215 | } |