1//
2// OpenSSLInitializer.cpp
3//
4// Library: Crypto
5// Package: CryptoCore
6// Module: OpenSSLInitializer
7//
8// Copyright (c) 2006-2009, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Crypto/OpenSSLInitializer.h"
16#include "Poco/RandomStream.h"
17#include "Poco/Thread.h"
18#include <openssl/ssl.h>
19#include <openssl/rand.h>
20#include <openssl/crypto.h>
21#include <openssl/err.h>
22#if OPENSSL_VERSION_NUMBER >= 0x0907000L
23#include <openssl/conf.h>
24#endif
25#if defined(POCO_OS_FAMILY_WINDOWS)
26 #define POCO_STR_HELPER(x) #x
27 #define POCO_STR(x) POCO_STR_HELPER(x)
28 #if defined POCO_INTERNAL_OPENSSL_MSVC_VER
29 #define POCO_INTERNAL_OPENSSL_BUILD \
30 " (POCO internal build, MSVC version " \
31 POCO_STR(POCO_INTERNAL_OPENSSL_MSVC_VER) ")"
32 #else
33 #define POCO_INTERNAL_OPENSSL_BUILD ""
34 #endif
35 #pragma message (OPENSSL_VERSION_TEXT POCO_INTERNAL_OPENSSL_BUILD)
36#endif
37
38
39using Poco::RandomInputStream;
40using Poco::Thread;
41
42
43#if defined(_MSC_VER) && !defined(_DLL) && defined(POCO_INTERNAL_OPENSSL_MSVC_VER)
44
45 #if (POCO_MSVS_VERSION >= 2015)
46 FILE _iob[] = { *stdin, *stdout, *stderr };
47 extern "C" FILE * __cdecl __iob_func(void) { return _iob; }
48 #endif // (POCO_MSVS_VERSION >= 2015)
49
50 #if (POCO_MSVS_VERSION < 2012)
51 extern "C" __declspec(noreturn) void __cdecl __report_rangecheckfailure(void) { ::ExitProcess(1); }
52 #endif // (POCO_MSVS_VERSION < 2012)
53
54#endif // defined(_MSC_VER) && !defined(_DLL) && defined(POCO_INTERNAL_OPENSSL_MSVC_VER)
55
56
57namespace Poco {
58namespace Crypto {
59
60Poco::FastMutex OpenSSLInitializer::_mutex;
61Poco::FastMutex* OpenSSLInitializer::_mutexes(0);
62int OpenSSLInitializer::_rc(0);
63bool OpenSSLInitializer::_disableSSLInitialization = false;
64
65OpenSSLInitializer::OpenSSLInitializer()
66{
67 initialize();
68}
69
70
71OpenSSLInitializer::~OpenSSLInitializer()
72{
73 try
74 {
75 uninitialize();
76 }
77 catch (...)
78 {
79 poco_unexpected();
80 }
81}
82
83
84void OpenSSLInitializer::initialize()
85{
86 FastMutex::ScopedLock lock(_mutex);
87 if (++_rc == 1)
88 {
89#if OPENSSL_VERSION_NUMBER >= 0x0907000L
90 OPENSSL_config(NULL);
91#endif
92 if(! _disableSSLInitialization)
93 {
94 SSL_library_init();
95 SSL_load_error_strings();
96 OpenSSL_add_all_algorithms();
97 }
98
99 char seed[SEEDSIZE];
100 RandomInputStream rnd;
101 rnd.read(seed, sizeof(seed));
102 RAND_seed(seed, SEEDSIZE);
103
104 if(CRYPTO_get_locking_callback() == NULL)
105 {
106 int nMutexes = CRYPTO_num_locks();
107 _mutexes = new Poco::FastMutex[nMutexes];
108 CRYPTO_set_locking_callback(&OpenSSLInitializer::lock);
109#ifndef POCO_OS_FAMILY_WINDOWS
110// Not needed on Windows (see SF #110: random unhandled exceptions when linking with ssl).
111// https://sourceforge.net/p/poco/bugs/110/
112//
113// From http://www.openssl.org/docs/crypto/threads.html :
114// "If the application does not register such a callback using CRYPTO_THREADID_set_callback(),
115// then a default implementation is used - on Windows and BeOS this uses the system's
116// default thread identifying APIs"
117#ifndef OPENSSL_NO_DEPRECATED
118 CRYPTO_set_id_callback(&OpenSSLInitializer::id);
119#else
120 CRYPTO_THREADID tid;
121 CRYPTO_THREADID_set_numeric(&tid, OpenSSLInitializer::id());
122#endif /* OPENSSL_NO_DEPRECATED */
123#endif
124 CRYPTO_set_dynlock_create_callback(&OpenSSLInitializer::dynlockCreate);
125 CRYPTO_set_dynlock_lock_callback(&OpenSSLInitializer::dynlock);
126 CRYPTO_set_dynlock_destroy_callback(&OpenSSLInitializer::dynlockDestroy);
127 }
128 }
129}
130
131
132void OpenSSLInitializer::uninitialize()
133{
134 FastMutex::ScopedLock lock(_mutex);
135 if (--_rc == 0)
136 {
137 if(_mutexes != NULL)
138 {
139 CRYPTO_set_dynlock_create_callback(0);
140 CRYPTO_set_dynlock_lock_callback(0);
141 CRYPTO_set_dynlock_destroy_callback(0);
142 CRYPTO_set_locking_callback(0);
143#ifndef POCO_OS_FAMILY_WINDOWS
144#ifndef OPENSSL_NO_DEPRECATED
145 CRYPTO_set_id_callback(0);
146#else
147 CRYPTO_THREADID tid;
148 CRYPTO_THREADID_set_numeric(&tid, 0);
149#endif /* OPENSSL_NO_DEPRECATED */
150#endif
151 delete [] _mutexes;
152 }
153
154 if(! _disableSSLInitialization)
155 {
156 EVP_cleanup();
157 ERR_free_strings();
158 CONF_modules_free();
159 }
160 }
161}
162
163
164void OpenSSLInitializer::lock(int mode, int n, const char* file, int line)
165{
166 if (mode & CRYPTO_LOCK)
167 _mutexes[n].lock();
168 else
169 _mutexes[n].unlock();
170}
171
172
173#ifndef POCO_OS_FAMILY_WINDOWS
174
175unsigned long OpenSSLInitializer::id()
176{
177 // Note: we use an old-style C cast here because
178 // neither static_cast<> nor reinterpret_cast<>
179 // work uniformly across all platforms.
180 return (unsigned long) Poco::Thread::currentTid();
181}
182
183#endif
184
185
186struct CRYPTO_dynlock_value* OpenSSLInitializer::dynlockCreate(const char* file, int line)
187{
188 return new CRYPTO_dynlock_value;
189}
190
191
192void OpenSSLInitializer::dynlock(int mode, struct CRYPTO_dynlock_value* lock, const char* file, int line)
193{
194 poco_check_ptr (lock);
195
196 if (mode & CRYPTO_LOCK)
197 lock->_mutex.lock();
198 else
199 lock->_mutex.unlock();
200}
201
202
203void OpenSSLInitializer::dynlockDestroy(struct CRYPTO_dynlock_value* lock, const char* file, int line)
204{
205 delete lock;
206}
207
208
209void initializeCrypto()
210{
211 OpenSSLInitializer::initialize();
212}
213
214
215void uninitializeCrypto()
216{
217 OpenSSLInitializer::uninitialize();
218}
219
220
221} } // namespace Poco::Crypto
222