]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/ceph_crypto.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / common / ceph_crypto.cc
CommitLineData
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
30namespace TOPNSPC::crypto::ssl {
31
32#if OPENSSL_VERSION_NUMBER < 0x10100000L
33static std::atomic_uint32_t crypto_refs;
34
35
36static 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
43static 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
51static void
52ssl_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
77static unsigned long
78ssl_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
91static 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
124static 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
165void zeroize_for_security(void* const s, const size_t n) {
166 OPENSSL_cleanse(s, n);
167}
168
169} // namespace TOPNSPC::crypto::openssl
170
171
172namespace TOPNSPC::crypto {
173void init() {
174 ssl::init();
7c673cae
FG
175}
176
9f95a23c
TL
177void shutdown([[maybe_unused]] const bool shared) {
178 ssl::shutdown();
179}
11fdf7f2 180
9f95a23c
TL
181void zeroize_for_security(void* const s, const size_t n) {
182 ssl::zeroize_for_security(s, n);
183}
11fdf7f2 184
9f95a23c 185ssl::OpenSSLDigest::OpenSSLDigest(const EVP_MD * _type)
11fdf7f2
TL
186 : mpContext(EVP_MD_CTX_create())
187 , mpType(_type) {
188 this->Restart();
189}
190
9f95a23c 191ssl::OpenSSLDigest::~OpenSSLDigest() {
11fdf7f2
TL
192 EVP_MD_CTX_destroy(mpContext);
193}
194
9f95a23c 195void ssl::OpenSSLDigest::Restart() {
11fdf7f2
TL
196 EVP_DigestInit_ex(mpContext, mpType, NULL);
197}
198
20effc67
TL
199void ssl::OpenSSLDigest::SetFlags(int flags) {
200 EVP_MD_CTX_set_flags(mpContext, flags);
201 this->Restart();
202}
203
9f95a23c 204void 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 210void ssl::OpenSSLDigest::Final(unsigned char *digest) {
11fdf7f2
TL
211 unsigned int s;
212 EVP_DigestFinal_ex(mpContext, digest, &s);
213}
9f95a23c 214
92f5a8d4 215}