1// Copyright (C) 2019 The Qt Company Ltd.
2// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
3// Copyright (C) 2016 Richard J. Moore <rich@kde.org>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6/****************************************************************************
7**
8** In addition, as a special exception, the copyright holders listed above give
9** permission to link the code of its release of Qt with the OpenSSL project's
10** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
11** same license as the original version), and distribute the linked executables.
12**
13** You must comply with the GNU General Public License version 2 in all
14** respects for all of the code used other than the "OpenSSL" code. If you
15** modify this file, you may extend this exception to your version of the file,
16** but you are not obligated to do so. If you do not wish to do so, delete
17** this exception statement from your version of this file.
18**
19****************************************************************************/
20
21#include "openssl_symbols_p.h"
22#include <QtCore/qurl.h>
23#include <QtCore/qset.h>
24
25#ifdef Q_OS_WIN
26# include <private/qsystemlibrary_p.h>
27#elif QT_CONFIG(library)
28# include <QtCore/qlibrary.h>
29#endif
30#include <QtCore/qmutex.h>
31#include <QtCore/qdatetime.h>
32#include <QtCore/qtimezone.h>
33#if defined(Q_OS_UNIX)
34#include <QtCore/qdir.h>
35#endif
36#include <memory>
37#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
38#include <link.h>
39#endif
40#ifdef Q_OS_DARWIN
41#include "private/qcore_mac_p.h"
42#endif
43
44#include <algorithm>
45
46QT_BEGIN_NAMESPACE
47
48Q_LOGGING_CATEGORY(lcSsl, "qt.opcua.ssl");
49
50
51/*
52 Note to maintainer:
53 -------------------
54
55 We load OpenSSL symbols dynamically. Because symbols are known to
56 disappear, and signatures sometimes change, between releases, we need to
57 be careful about how this is done. To ensure we don't end up dereferencing
58 null function pointers, and continue running even if certain functions are
59 missing, we define helper functions for each of the symbols we load from
60 OpenSSL, all prefixed with "q_" (declared in
61 qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect
62 directly, we call q_SSL_connect, which is a function that checks if the
63 actual SSL_connect fptr is null, and returns a failure if it is, or calls
64 SSL_connect if it isn't.
65
66 This requires a somewhat tedious process of declaring each function we
67 want to call in OpenSSL thrice: once with the q_, in _p.h, once using the
68 DEFINEFUNC macros below, and once in the function that actually resolves
69 the symbols, below the DEFINEFUNC declarations below.
70
71 There's one DEFINEFUNC macro declared for every number of arguments
72 exposed by OpenSSL (feel free to extend when needed). The easiest thing to
73 do is to find an existing entry that matches the arg count of the function
74 you want to import, and do the same.
75
76 The first macro arg is the function return type. The second is the
77 verbatim name of the function/symbol. Then follows a list of N pairs of
78 argument types with a variable name, and just the variable name (char *a,
79 a, char *b, b, etc). Finally there's two arguments - a suitable return
80 statement for the error case (for an int function, return 0 or return -1
81 is usually right). Then either just "return" or DUMMYARG, the latter being
82 for void functions.
83
84 Note: Take into account that these macros and declarations are processed
85 at compile-time, and the result depends on the OpenSSL headers the
86 compiling host has installed, but the symbols are resolved at run-time,
87 possibly with a different version of OpenSSL.
88*/
89
90#ifndef QT_LINKED_OPENSSL
91
92namespace {
93void qsslSocketUnresolvedSymbolWarning(const char *functionName)
94{
95 qCWarning(lcSsl, "QSslSocket: cannot call unresolved function %s", functionName);
96}
97
98#if QT_CONFIG(library)
99void qsslSocketCannotResolveSymbolWarning(const char *functionName)
100{
101 qCWarning(lcSsl, "QSslSocket: cannot resolve %s", functionName);
102}
103#endif
104
105}
106
107#endif // QT_LINKED_OPENSSL
108
109DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
110DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return)
111DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
112DEFINEFUNC(int, EVP_PKEY_get_base_id, const EVP_PKEY *pkey, pkey, return -1, return)
113DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
114DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
115DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG)
116DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
117DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
118DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
119DEFINEFUNC(void, BIO_free_all, BIO *a, a, return, return)
120DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return nullptr, return)
121DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG)
122DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
123DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return)
124DEFINEFUNC(const EVP_MD *, EVP_sha256, DUMMYARG, DUMMYARG, return nullptr, return)
125DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
126DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
127DEFINEFUNC4(X509_EXTENSION* , X509V3_EXT_conf_nid, LHASH_OF(CONF_VALUE) *conf, conf, X509V3_CTX *ctx, ctx, int ext_nid, ext_nid, char *value, value, return NULL, return)
128DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
129DEFINEFUNC7(int, PEM_write_bio_PKCS8PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
130DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
131DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return)
132DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
133DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return)
134DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
135DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG)
136DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return)
137DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return)
138DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return nullptr, return)
139DEFINEFUNC(X509_REQ*, X509_REQ_new, void, DUMMYARG, return NULL, return);
140DEFINEFUNC(void, X509_REQ_free, X509_REQ *req, req, return, return);
141DEFINEFUNC(X509_NAME*, X509_REQ_get_subject_name, X509_REQ *req, req, return NULL, return);
142DEFINEFUNC2(EVP_PKEY_CTX*, EVP_PKEY_CTX_new_id, int id, id, ENGINE *e, e, return NULL, return);
143DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return);
144DEFINEFUNC(int, EVP_PKEY_keygen_init, EVP_PKEY_CTX *ctx, ctx, return 0, return);
145DEFINEFUNC2(int, EVP_PKEY_keygen, EVP_PKEY_CTX *ctx, ctx, EVP_PKEY **pkey, pkey, return 0, return);
146DEFINEFUNC5(int, RSA_pkey_ctx_ctrl, EVP_PKEY_CTX *ctx, ctx, int optype, optype, int cmd, cmd, int p1, p1, void *p2, p2, return 0, return)
147DEFINEFUNC7(int, X509_NAME_add_entry_by_OBJ, X509_NAME *name, name, const ASN1_OBJECT *obj, obj, int type, type, const unsigned char *bytes, bytes, int len, len, int loc, loc, int set, set, return 0, return);
148DEFINEFUNC2(ASN1_OBJECT *, OBJ_txt2obj, const char *s, s, int no_name, no_name, return NULL, return);
149DEFINEFUNC2(int, X509_REQ_set_pubkey, X509_REQ *x, x, EVP_PKEY *pkey, pkey, return 0, return);
150DEFINEFUNC3(int, X509_REQ_sign, X509_REQ *x, x, EVP_PKEY *pkey, pkey, const EVP_MD *md, md, return 0, return);
151DEFINEFUNC2(int, PEM_write_bio_X509_REQ, BIO *bp, bp, X509_REQ *x, x, return 0, return);
152DEFINEFUNC2(int, X509_REQ_set_version, X509_REQ *x, x, long version, version, return 0, return);
153DEFINEFUNC2(int, X509_REQ_add_extensions, X509_REQ *req, req, STACK_OF(X509_EXTENSION) *exts, exts, return 0, return)
154DEFINEFUNC(void, X509_EXTENSION_free, X509_EXTENSION *ext, ext, return, return)
155DEFINEFUNC2(int, X509_EXTENSION_set_critical, X509_EXTENSION *ex, ex, int crit, crit, return 0, return)
156DEFINEFUNC2(int, PEM_write_bio_X509, BIO *bp, bp, X509 *x, x, return 0, return)
157DEFINEFUNC(X509*, X509_new, void, DUMMYARG, return NULL, return)
158DEFINEFUNC2(int, X509_set_pubkey,X509 *x, x, EVP_PKEY *key, key, return 0, return)
159DEFINEFUNC3(int, X509_sign, X509 *x, x, EVP_PKEY *key, key, const EVP_MD *md, md, return 0, return)
160DEFINEFUNC3(int, X509_add_ext, X509 *x, x, X509_EXTENSION *ex, ex, int loc, loc, return 0, return)
161DEFINEFUNC2(int, X509_set_version, X509 *x, x, long version, version, return 0, return)
162DEFINEFUNC(ASN1_OCTET_STRING *, ASN1_OCTET_STRING_new, void, DUMMYARG, return NULL, return)
163DEFINEFUNC4(int, X509_pubkey_digest, const X509 *data, data, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return 0, return)
164DEFINEFUNC3(int, ASN1_OCTET_STRING_set, ASN1_OCTET_STRING *str, str, const unsigned char *data, data, int len, len, return 0, return)
165DEFINEFUNC5(int, X509_add1_ext_i2d, X509 *x, x, int nid, nid, void *value, value, int crit, crit, unsigned long flags, flags, return 0, return)
166DEFINEFUNC(void, ASN1_OCTET_STRING_free, ASN1_OCTET_STRING *a, a, return, return)
167DEFINEFUNC(ASN1_INTEGER *, ASN1_INTEGER_new, void, DUMMYARG, return NULL, return)
168DEFINEFUNC(GENERAL_NAMES *, GENERAL_NAMES_new, void, DUMMYARG, return NULL, return)
169DEFINEFUNC(GENERAL_NAME *, GENERAL_NAME_new, void, DUMMYARG, return NULL, return)
170DEFINEFUNC(X509_NAME *, X509_NAME_dup, X509_NAME *xn, xn, return NULL, return)
171DEFINEFUNC2(int, X509_set_serialNumber, X509 *x, x, ASN1_INTEGER *serial, serial, return 0, return)
172DEFINEFUNC(AUTHORITY_KEYID *, AUTHORITY_KEYID_new, void, DUMMYARG, return NULL, return)
173DEFINEFUNC(ASN1_INTEGER *, ASN1_INTEGER_dup, const ASN1_INTEGER *x, x, return NULL, return)
174DEFINEFUNC4(int, X509_NAME_digest, const X509_NAME *data, data, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return 0, return)
175DEFINEFUNC(void, ASN1_INTEGER_free, ASN1_INTEGER *a, a, return, return)
176DEFINEFUNC2(int, i2d_X509_REQ_bio, BIO *bp, bp, X509_REQ *req, req, return 0, return)
177DEFINEFUNC2(int, i2d_X509_bio, BIO *bp, bp, X509 *x509, x509, return 0, return)
178
179#define RESOLVEFUNC(func) \
180 if (!(_q_##func = _q_PTR_##func(libs.ssl->resolve(#func))) \
181 && !(_q_##func = _q_PTR_##func(libs.crypto->resolve(#func)))) \
182 qsslSocketCannotResolveSymbolWarning(#func);
183
184#if !defined QT_LINKED_OPENSSL
185
186#if !QT_CONFIG(library)
187bool q_resolveOpenSslSymbols()
188{
189 qCWarning(lcSsl, "QSslSocket: unable to resolve symbols. Qt is configured without the "
190 "'library' feature, which means runtime resolving of libraries won't work.");
191 qCWarning(lcSsl, "Either compile Qt statically or with support for runtime resolving "
192 "of libraries.");
193 return false;
194}
195#else
196
197# ifdef Q_OS_UNIX
198struct NumericallyLess
199{
200 typedef bool result_type;
201 result_type operator()(const QStringView &lhs, const QStringView &rhs) const
202 {
203 bool ok = false;
204 int b = 0;
205 int a = lhs.toInt(ok: &ok);
206 if (ok)
207 b = rhs.toInt(ok: &ok);
208 if (ok) {
209 // both toInt succeeded
210 return a < b;
211 } else {
212 // compare as strings;
213 return lhs < rhs;
214 }
215 }
216};
217
218struct LibGreaterThan
219{
220 typedef bool result_type;
221 result_type operator()(const QString &lhs, const QString &rhs) const
222 {
223 const QList<QStringView> lhsparts = QStringView{lhs}.split(sep: QLatin1Char('.'));
224 const QList<QStringView> rhsparts = QStringView{rhs}.split(sep: QLatin1Char('.'));
225 Q_ASSERT(lhsparts.size() > 1 && rhsparts.size() > 1);
226
227 // note: checking rhs < lhs, the same as lhs > rhs
228 return std::lexicographical_compare(first1: rhsparts.begin() + 1, last1: rhsparts.end(),
229 first2: lhsparts.begin() + 1, last2: lhsparts.end(),
230 comp: NumericallyLess());
231 }
232};
233
234#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
235static int dlIterateCallback(struct dl_phdr_info *info, size_t size, void *data)
236{
237 if (size < sizeof (info->dlpi_addr) + sizeof (info->dlpi_name))
238 return 1;
239 QSet<QString> *paths = (QSet<QString> *)data;
240 QString path = QString::fromLocal8Bit(ba: info->dlpi_name);
241 if (!path.isEmpty()) {
242 QFileInfo fi(path);
243 path = fi.absolutePath();
244 if (!path.isEmpty())
245 paths->insert(value: path);
246 }
247 return 0;
248}
249#endif
250
251static QStringList libraryPathList()
252{
253 QStringList paths;
254# ifdef Q_OS_DARWIN
255 paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH"))
256 .split(QLatin1Char(':'), Qt::SkipEmptyParts);
257
258 // search in .app/Contents/Frameworks
259 UInt32 packageType;
260 CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, nullptr);
261 if (packageType == FOUR_CHAR_CODE('APPL')) {
262 QUrl bundleUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyBundleURL(CFBundleGetMainBundle())));
263 QUrl frameworksUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyPrivateFrameworksURL(CFBundleGetMainBundle())));
264 paths << bundleUrl.resolved(frameworksUrl).path();
265 }
266# else
267 paths = QString::fromLatin1(ba: qgetenv(varName: "LD_LIBRARY_PATH"))
268 .split(sep: QLatin1Char(':'), behavior: Qt::SkipEmptyParts);
269# endif
270 paths << QLatin1String("/lib") << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib");
271 paths << QLatin1String("/lib64") << QLatin1String("/usr/lib64") << QLatin1String("/usr/local/lib64");
272 paths << QLatin1String("/lib32") << QLatin1String("/usr/lib32") << QLatin1String("/usr/local/lib32");
273
274#if defined(Q_OS_ANDROID)
275 paths << QLatin1String("/system/lib");
276#elif defined(Q_OS_LINUX)
277 // discover paths of already loaded libraries
278 QSet<QString> loadedPaths;
279 dl_iterate_phdr(callback: dlIterateCallback, data: &loadedPaths);
280 paths.append(other: loadedPaths.values());
281#endif
282
283 return paths;
284}
285
286Q_NEVER_INLINE
287static QStringList findAllLibs(QLatin1String filter)
288{
289 const QStringList paths = libraryPathList();
290 QStringList found;
291 const QStringList filters((QString(filter)));
292
293 for (const QString &path : paths) {
294 QDir dir(path);
295 QStringList entryList = dir.entryList(nameFilters: filters, filters: QDir::Files);
296
297 std::sort(first: entryList.begin(), last: entryList.end(), comp: LibGreaterThan());
298 for (const QString &entry : std::as_const(t&: entryList))
299 found << path + QLatin1Char('/') + entry;
300 }
301
302 return found;
303}
304
305static QStringList findAllLibSsl()
306{
307 return findAllLibs(filter: QLatin1String("libssl.*"));
308}
309
310static QStringList findAllLibCrypto()
311{
312 return findAllLibs(filter: QLatin1String("libcrypto.*"));
313}
314# endif
315
316#ifdef Q_OS_WIN
317
318struct LoadedOpenSsl {
319 std::unique_ptr<QSystemLibrary> ssl, crypto;
320};
321
322static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, LoadedOpenSsl &result)
323{
324 auto ssleay32 = std::make_unique<QSystemLibrary>(ssleay32LibName);
325 if (!ssleay32->load(false)) {
326 return FALSE;
327 }
328
329 auto libeay32 = std::make_unique<QSystemLibrary>(libeay32LibName);
330 if (!libeay32->load(false)) {
331 return FALSE;
332 }
333
334 result.ssl = std::move(ssleay32);
335 result.crypto = std::move(libeay32);
336 return TRUE;
337}
338
339static LoadedOpenSsl loadOpenSsl()
340{
341 LoadedOpenSsl result;
342
343#if defined(Q_PROCESSOR_X86_64)
344#define QT_SSL_SUFFIX "-x64"
345#elif defined(Q_PROCESSOR_ARM_64)
346#define QT_SSL_SUFFIX "-arm64"
347#elif defined(Q_PROCESSOR_ARM_32)
348#define QT_SSL_SUFFIX "-arm"
349#else
350#define QT_SSL_SUFFIX
351#endif
352
353tryToLoadOpenSslWin32Library(QLatin1String("libssl-3" QT_SSL_SUFFIX),
354 QLatin1String("libcrypto-3" QT_SSL_SUFFIX), result);
355
356#undef QT_SSL_SUFFIX
357
358 return result;
359}
360#else
361
362struct LoadedOpenSsl {
363 std::unique_ptr<QLibrary> ssl, crypto;
364};
365
366static LoadedOpenSsl loadOpenSsl()
367{
368 LoadedOpenSsl result = {.ssl: std::make_unique<QLibrary>(), .crypto: std::make_unique<QLibrary>()};
369
370# if defined(Q_OS_UNIX)
371 QLibrary * const libssl = result.ssl.get();
372 QLibrary * const libcrypto = result.crypto.get();
373
374 // Try to find the libssl library on the system.
375 //
376 // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that
377 // is, libssl.so on most Unix systems. However, the .so file isn't present in
378 // user installations because it's considered a development file.
379 //
380 // The right thing to do is to load the library at the major version we know how
381 // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h)
382 //
383 // However, OpenSSL is a well-known case of binary-compatibility breakage. To
384 // avoid such problems, many system integrators and Linux distributions change
385 // the soname of the binary, letting the full version number be the soname. So
386 // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that
387 // reason, we will search a few common paths (see findAllLibSsl() above) in hopes
388 // we find one that works.
389 //
390 // If that fails, for OpenSSL 1.0 we also try some fallbacks -- look up
391 // libssl.so with a hardcoded soname. The reason is QTBUG-68156: the binary
392 // builds of Qt happen (at the time of this writing) on RHEL machines,
393 // which change SHLIB_VERSION_NUMBER to a non-portable string. When running
394 // those binaries on the target systems, this code won't pick up
395 // libssl.so.MODIFIED_SHLIB_VERSION_NUMBER because it doesn't exist there.
396 // Given that the only 1.0 supported release (at the time of this writing)
397 // is 1.0.2, with soname "1.0.0", give that a try too. Note that we mandate
398 // OpenSSL >= 1.0.0 with a configure-time check, and OpenSSL has kept binary
399 // compatibility between 1.0.0 and 1.0.2.
400 //
401 // It is important, however, to try the canonical name and the unversioned name
402 // without going through the loop. By not specifying a path, we let the system
403 // dlopen(3) function determine it for us. This will include any DT_RUNPATH or
404 // DT_RPATH tags on our library header as well as other system-specific search
405 // paths. See the man page for dlopen(3) on your system for more information.
406
407#ifdef Q_OS_OPENBSD
408 libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint);
409#endif
410#if defined(SHLIB_VERSION_NUMBER) && !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so
411 // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER>
412 libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER));
413 libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER));
414 if (libcrypto->load() && libssl->load()) {
415 // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found
416 return result;
417 } else {
418 libssl->unload();
419 libcrypto->unload();
420 }
421
422#if QT_CONFIG(opensslv30)
423 // first-and-half attempts: for OpenSSL 1.0 try to load some hardcoded sonames:
424 // - "1.0.0" is the official upstream one
425 // - "1.0.2" is found on some distributions (e.g. Debian) that patch OpenSSL
426 static const QLatin1String fallbackSonames[] = {
427 QLatin1String("1.0.0"),
428 QLatin1String("1.0.2")
429 };
430
431 for (auto fallbackSoname : fallbackSonames) {
432 libssl->setFileNameAndVersion(QLatin1String("ssl"), fallbackSoname);
433 libcrypto->setFileNameAndVersion(QLatin1String("crypto"), fallbackSoname);
434 if (libcrypto->load() && libssl->load()) {
435 return result;
436 } else {
437 libssl->unload();
438 libcrypto->unload();
439 }
440 }
441#endif
442#endif
443
444#ifndef Q_OS_DARWIN
445 // second attempt: find the development files libssl.so and libcrypto.so
446 //
447 // disabled on macOS/iOS:
448 // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third
449 // attempt, _after_ <bundle>/Contents/Frameworks has been searched.
450 // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place.
451# if defined(Q_OS_ANDROID)
452 // OpenSSL 3.x.x must be suffixed in order to be able to find the right OpenSSL libraries
453 auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) {
454 auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX");
455 if (suffix.isEmpty())
456 return defaultSuffix;
457 return suffix;
458 };
459 static QString suffix = QString::fromLatin1(openSSLSuffix("_3"));
460 libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1);
461 libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1);
462# else
463 libssl->setFileNameAndVersion(fileName: QLatin1String("ssl"), verNum: -1);
464 libcrypto->setFileNameAndVersion(fileName: QLatin1String("crypto"), verNum: -1);
465# endif
466 if (libcrypto->load() && libssl->load()) {
467 // libssl.so.0 and libcrypto.so.0 found
468 return result;
469 } else {
470 libssl->unload();
471 libcrypto->unload();
472 }
473#endif
474
475 // third attempt: loop on the most common library paths and find libssl
476 const QStringList sslList = findAllLibSsl();
477 const QStringList cryptoList = findAllLibCrypto();
478
479 for (const QString &crypto : cryptoList) {
480 libcrypto->setFileNameAndVersion(fileName: crypto, verNum: -1);
481 if (libcrypto->load()) {
482 QFileInfo fi(crypto);
483 QString version = fi.completeSuffix();
484
485 for (const QString &ssl : sslList) {
486 if (!ssl.endsWith(s: version))
487 continue;
488
489 libssl->setFileNameAndVersion(fileName: ssl, verNum: -1);
490
491 if (libssl->load()) {
492 // libssl.so.x and libcrypto.so.x found
493 return result;
494 } else {
495 libssl->unload();
496 }
497 }
498 }
499 libcrypto->unload();
500 }
501
502 // failed to load anything
503 result = {};
504 return result;
505
506# else
507 // not implemented for this platform yet
508 return result;
509# endif
510}
511#endif
512
513static QBasicMutex symbolResolveMutex;
514static QBasicAtomicInt symbolsResolved = Q_BASIC_ATOMIC_INITIALIZER(false);
515static bool triedToResolveSymbols = false;
516
517bool q_resolveOpenSslSymbols()
518{
519 if (symbolsResolved.loadAcquire())
520 return true;
521 QMutexLocker locker(&symbolResolveMutex);
522 if (symbolsResolved.loadRelaxed())
523 return true;
524 if (triedToResolveSymbols)
525 return false;
526 triedToResolveSymbols = true;
527
528 LoadedOpenSsl libs = loadOpenSsl();
529 if (!libs.ssl || !libs.crypto)
530 // failed to load them
531 return false;
532
533 RESOLVEFUNC(X509_REQ_get_subject_name)
534 RESOLVEFUNC(OPENSSL_init_crypto)
535 RESOLVEFUNC(EVP_PKEY_get_base_id)
536 RESOLVEFUNC(OPENSSL_sk_new_null)
537 RESOLVEFUNC(OPENSSL_sk_push)
538 RESOLVEFUNC(OPENSSL_sk_pop_free)
539 RESOLVEFUNC(X509_getm_notBefore)
540 RESOLVEFUNC(X509_getm_notAfter)
541 RESOLVEFUNC(EVP_PKEY_CTX_new_id)
542 RESOLVEFUNC(EVP_PKEY_CTX_free)
543 RESOLVEFUNC(EVP_PKEY_keygen_init)
544 RESOLVEFUNC(EVP_PKEY_keygen);
545 RESOLVEFUNC(RSA_pkey_ctx_ctrl);
546 RESOLVEFUNC(BIO_free_all)
547 RESOLVEFUNC(X509_REQ_new)
548 RESOLVEFUNC(X509_REQ_set_version)
549 RESOLVEFUNC(OBJ_txt2obj)
550 RESOLVEFUNC(X509_NAME_add_entry_by_OBJ)
551 RESOLVEFUNC(X509_REQ_add_extensions)
552 RESOLVEFUNC(X509_EXTENSION_free)
553 RESOLVEFUNC(X509_REQ_set_pubkey)
554 RESOLVEFUNC(X509_REQ_sign)
555 RESOLVEFUNC(PEM_write_bio_X509_REQ)
556 RESOLVEFUNC(X509_REQ_free)
557 RESOLVEFUNC(BIO_ctrl)
558 RESOLVEFUNC(BIO_new)
559 RESOLVEFUNC(BIO_new_mem_buf)
560 RESOLVEFUNC(BIO_s_mem)
561 RESOLVEFUNC(X509V3_EXT_conf_nid)
562 RESOLVEFUNC(X509_EXTENSION_set_critical)
563 RESOLVEFUNC(ERR_error_string_n)
564 RESOLVEFUNC(ERR_get_error)
565 RESOLVEFUNC(EVP_sha1)
566 RESOLVEFUNC(EVP_sha256)
567 RESOLVEFUNC(EVP_aes_128_cbc)
568 RESOLVEFUNC(EVP_PKEY_free)
569 RESOLVEFUNC(PEM_read_bio_PrivateKey)
570 RESOLVEFUNC(PEM_write_bio_PKCS8PrivateKey)
571 RESOLVEFUNC(PEM_read_bio_PUBKEY)
572 RESOLVEFUNC(PEM_write_bio_PUBKEY)
573 RESOLVEFUNC(X509_free)
574 RESOLVEFUNC(X509_gmtime_adj)
575 RESOLVEFUNC(X509_get_ext_d2i)
576 RESOLVEFUNC(AUTHORITY_KEYID_free)
577 RESOLVEFUNC(X509_get_issuer_name)
578 RESOLVEFUNC(X509_get_subject_name)
579 RESOLVEFUNC(X509_get_serialNumber)
580 RESOLVEFUNC(PEM_write_bio_X509)
581 RESOLVEFUNC(X509_new)
582 RESOLVEFUNC(X509_set_pubkey)
583 RESOLVEFUNC(X509_sign)
584 RESOLVEFUNC(X509_add_ext)
585 RESOLVEFUNC(X509_set_version)
586 RESOLVEFUNC(ASN1_OCTET_STRING_new)
587 RESOLVEFUNC(X509_pubkey_digest)
588 RESOLVEFUNC(ASN1_OCTET_STRING_set)
589 RESOLVEFUNC(X509_add1_ext_i2d)
590 RESOLVEFUNC(ASN1_OCTET_STRING_free)
591 RESOLVEFUNC(ASN1_INTEGER_new)
592 RESOLVEFUNC(GENERAL_NAMES_new)
593 RESOLVEFUNC(GENERAL_NAME_new)
594 RESOLVEFUNC(X509_NAME_dup)
595 RESOLVEFUNC(X509_set_serialNumber)
596 RESOLVEFUNC(AUTHORITY_KEYID_new)
597 RESOLVEFUNC(ASN1_INTEGER_dup)
598 RESOLVEFUNC(X509_NAME_digest)
599 RESOLVEFUNC(ASN1_INTEGER_free)
600 RESOLVEFUNC(i2d_X509_REQ_bio)
601 RESOLVEFUNC(i2d_X509_bio)
602
603 symbolsResolved.storeRelease(newValue: true);
604 return true;
605}
606#endif // QT_CONFIG(library)
607#else // !defined QT_LINKED_OPENSSL
608
609bool q_resolveOpenSslSymbols()
610{
611#ifdef QT_NO_OPENSSL
612 return false;
613#endif
614 return true;
615}
616#endif // !defined QT_LINKED_OPENSSL
617
618//==============================================================================
619// contributed by Jay Case of Sarvega, Inc.; http://sarvega.com/
620// Based on X509_cmp_time() for initial buffer hacking.
621//==============================================================================
622QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
623{
624 size_t lTimeLength = aTime->length;
625 char *pString = (char *) aTime->data;
626
627 if (aTime->type == V_ASN1_UTCTIME) {
628
629 char lBuffer[24];
630 char *pBuffer = lBuffer;
631
632 if ((lTimeLength < 11) || (lTimeLength > 17))
633 return QDateTime();
634
635 memcpy(dest: pBuffer, src: pString, n: 10);
636 pBuffer += 10;
637 pString += 10;
638
639 if ((*pString == 'Z') || (*pString == '-') || (*pString == '+')) {
640 *pBuffer++ = '0';
641 *pBuffer++ = '0';
642 } else {
643 *pBuffer++ = *pString++;
644 *pBuffer++ = *pString++;
645 // Skip any fractional seconds...
646 if (*pString == '.') {
647 pString++;
648 while ((*pString >= '0') && (*pString <= '9'))
649 pString++;
650 }
651 }
652
653 *pBuffer++ = 'Z';
654 *pBuffer++ = '\0';
655
656 time_t lSecondsFromUCT;
657 if (*pString == 'Z') {
658 lSecondsFromUCT = 0;
659 } else {
660 if ((*pString != '+') && (*pString != '-'))
661 return QDateTime();
662
663 lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
664 lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0');
665 lSecondsFromUCT *= 60;
666 if (*pString == '-')
667 lSecondsFromUCT = -lSecondsFromUCT;
668 }
669
670 tm lTime;
671 lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
672 lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
673 lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
674 lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
675 lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
676 lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
677 if (lTime.tm_year < 50)
678 lTime.tm_year += 100; // RFC 2459
679
680 QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday);
681 QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
682
683 QDateTime result(resDate, resTime, QTimeZone::UTC);
684 result = result.addSecs(secs: lSecondsFromUCT);
685 return result;
686
687 } else if (aTime->type == V_ASN1_GENERALIZEDTIME) {
688
689 if (lTimeLength < 15)
690 return QDateTime(); // hopefully never triggered
691
692 // generalized time is always YYYYMMDDHHMMSSZ (RFC 2459, section 4.1.2.5.2)
693 tm lTime;
694 lTime.tm_sec = ((pString[12] - '0') * 10) + (pString[13] - '0');
695 lTime.tm_min = ((pString[10] - '0') * 10) + (pString[11] - '0');
696 lTime.tm_hour = ((pString[8] - '0') * 10) + (pString[9] - '0');
697 lTime.tm_mday = ((pString[6] - '0') * 10) + (pString[7] - '0');
698 lTime.tm_mon = (((pString[4] - '0') * 10) + (pString[5] - '0'));
699 lTime.tm_year = ((pString[0] - '0') * 1000) + ((pString[1] - '0') * 100) +
700 ((pString[2] - '0') * 10) + (pString[3] - '0');
701
702 QDate resDate(lTime.tm_year, lTime.tm_mon, lTime.tm_mday);
703 QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
704
705 QDateTime result(resDate, resTime, QTimeZone::UTC);
706 return result;
707
708 } else {
709 qCWarning(lcSsl, "unsupported date format detected");
710 return QDateTime();
711 }
712
713}
714
715QT_END_NAMESPACE
716

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtopcua/src/opcua/x509/openssl_symbols.cpp