📄 qsslsocket_openssl.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtNetwork module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************//******************************************************************************** In addition, as a special exception, Trolltech gives permission to link** the code of its release of Qt with the OpenSSL project's "OpenSSL" library** (or modified versions of the "OpenSSL" library that use the same license** as the original version), and distribute the linked executables.**** You must comply with the GNU General Public License version 2 in all** respects for all of the code used other than the "OpenSSL" code. If you** modify this file, you may extend this exception to your version of the file,** but you are not obligated to do so. If you do not wish to do so, delete** this exception statement from your version of this file.******************************************************************************/#include "qsslsocket_openssl_p.h"#include "qsslsocket_openssl_symbols_p.h"#include "qsslsocket.h"#include "qsslcertificate_p.h"#include "qsslcipher_p.h"#include <QtCore/qdatetime.h>#include <QtCore/qdebug.h>#include <QtCore/qdir.h>#include <QtCore/qdiriterator.h>#include <QtCore/qfile.h>#include <QtCore/qfileinfo.h>#include <QtCore/qmutex.h>#include <QtCore/qthread.h>#include <QtCore/qurl.h>#include <QtCore/qvarlengtharray.h>// Useful defines#define SSL_ERRORSTR() QString::fromLocal8Bit(q_ERR_error_string(q_ERR_get_error(), NULL))/* \internal From OpenSSL's thread(3) manual page: OpenSSL can safely be used in multi-threaded applications provided that at least two callback functions are set. locking_function(int mode, int n, const char *file, int line) is needed to perform locking on shared data structures. (Note that OpenSSL uses a number of global data structures that will be implicitly shared when-whenever ever multiple threads use OpenSSL.) Multi-threaded applications will crash at random if it is not set. ... ... id_function(void) is a function that returns a thread ID. It is not needed on Windows nor on platforms where getpid() returns a different ID for each thread (most notably Linux)*/class OpenSslLocks{public: inline OpenSslLocks() : initLocker(QMutex::Recursive), locksLocker(QMutex::Recursive) { QMutexLocker locker(&locksLocker); int numLocks = q_CRYPTO_num_locks(); locks = new QMutex *[numLocks]; memset(locks, 0, numLocks * sizeof(QMutex *)); } inline ~OpenSslLocks() { QMutexLocker locker(&locksLocker); for (int i = 0; i < q_CRYPTO_num_locks(); ++i) delete locks[i]; delete [] locks; } inline QMutex *lock(int num) { QMutexLocker locker(&locksLocker); QMutex *tmp = locks[num]; if (!tmp) tmp = locks[num] = new QMutex(QMutex::Recursive); return tmp; } QMutex *globalLock() { return &locksLocker; } QMutex *initLock() { return &initLocker; }private: QMutex initLocker; QMutex locksLocker; QMutex **locks;};Q_GLOBAL_STATIC(OpenSslLocks, openssl_locks)extern "C" {static void locking_function(int mode, int lockNumber, const char *, int){ QMutex *mutex = openssl_locks()->lock(lockNumber); // Lock or unlock it if (mode & CRYPTO_LOCK) mutex->lock(); else mutex->unlock();}static unsigned long id_function(){ return (unsigned long)QThread::currentThreadId();}} // extern "C"QSslSocketBackendPrivate::QSslSocketBackendPrivate() : ssl(0), ctx(0), readBio(0), writeBio(0), session(0){ // Calls SSL_library_init(). ensureInitialized();}QSslSocketBackendPrivate::~QSslSocketBackendPrivate(){}QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher){ QSslCipher ciph; char buf [256]; QString descriptionOneLine = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf))); QStringList descriptionList = descriptionOneLine.split(QLatin1String(" "), QString::SkipEmptyParts); if (descriptionList.size() > 5) { // ### crude code. ciph.d->isNull = false; ciph.d->name = descriptionList.at(0); QString protoString = descriptionList.at(1); ciph.d->protocolString = protoString; ciph.d->protocol = QSsl::UnknownProtocol; if (protoString == QLatin1String("SSLv3")) ciph.d->protocol = QSsl::SslV3; else if (protoString == QLatin1String("SSLv2")) ciph.d->protocol = QSsl::SslV2; else if (protoString == QLatin1String("TLSv1")) ciph.d->protocol = QSsl::TlsV1; if (descriptionList.at(2).startsWith(QLatin1String("Kx="))) ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3); if (descriptionList.at(3).startsWith(QLatin1String("Au="))) ciph.d->authenticationMethod = descriptionList.at(3).mid(3); if (descriptionList.at(4).startsWith(QLatin1String("Enc="))) ciph.d->encryptionMethod = descriptionList.at(4).mid(4); ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export")); ciph.d->bits = cipher->strength_bits; ciph.d->supportedBits = cipher->alg_bits; } return ciph;}// ### This list is shared between all threads, and protected by a// mutex. Investigate using thread local storage instead.struct QSslErrorList{ QMutex mutex; QList<int> errors;};Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList)static int q_X509Callback(int ok, X509_STORE_CTX *ctx){ if (!ok) _q_sslErrorList()->errors << ctx->error; return ctx->error;}bool QSslSocketBackendPrivate::initSslContext(){ Q_Q(QSslSocket); // Create and initialize SSL context. Accept SSLv2, SSLv3 and TLSv1. bool client = (mode == QSslSocket::SslClientMode); switch (protocol) { case QSsl::SslV2: ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method()); break; case QSsl::SslV3: ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method()); break; case QSsl::AnyProtocol: default: ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); break; case QSsl::TlsV1: ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method()); break; } if (!ctx) { // ### Bad error code q->setErrorString(QSslSocket::tr("Error creating SSL context (%1)").arg(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Enable all bug workarounds. q_SSL_CTX_set_options(ctx, SSL_OP_ALL); // Initialize ciphers QByteArray cipherString; int first = true; foreach (const QSslCipher &cipher, ciphers.isEmpty() ? defaultCiphers() : ciphers) { if (first) first = false; else cipherString.append(":"); cipherString.append(cipher.name().toLatin1()); } if (!q_SSL_CTX_set_cipher_list(ctx, cipherString.data())) { // ### Bad error code q->setErrorString(QSslSocket::tr("Invalid or empty cipher list (%1)").arg(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Add all our CAs to this store. foreach (const QSslCertificate &caCertificate, q->caCertificates()) q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); // Register a custom callback to get all verification errors. X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback); if (!localCertificate.isNull()) { // Require a private key as well. if (privateKey.isNull()) { q->setErrorString(QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(SSL_ERRORSTR())); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Load certificate if (!q_SSL_CTX_use_certificate(ctx, (X509 *)localCertificate.handle())) { q->setErrorString(QSslSocket::tr("Error loading local certificate, %1").arg(SSL_ERRORSTR())); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Load private key EVP_PKEY *pkey = q_EVP_PKEY_new(); if (privateKey.algorithm() == QSsl::Rsa) q_EVP_PKEY_assign_RSA(pkey, (RSA *)privateKey.handle()); else q_EVP_PKEY_assign_DSA(pkey, (DSA *)privateKey.handle()); if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) { q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(SSL_ERRORSTR())); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Check if the certificate matches the private key. if (!q_SSL_CTX_check_private_key(ctx)) { q->setErrorString(QSslSocket::tr("Private key does not certificate public key, %1").arg(SSL_ERRORSTR())); emit q->error(QAbstractSocket::UnknownSocketError); return false; } } // Create and initialize SSL session if (!(ssl = q_SSL_new(ctx))) { // ### Bad error code q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Clear the session. q_SSL_clear(ssl); errorList.clear(); // Initialize memory BIOs for encryption and decryption. readBio = q_BIO_new(q_BIO_s_mem()); writeBio = q_BIO_new(q_BIO_s_mem()); if (!readBio || !writeBio) { // ### Bad error code q->setErrorString(QSslSocket::tr("Error creating SSL session: %1").arg(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError); return false; } // Assign the bios. q_SSL_set_bio(ssl, readBio, writeBio); if (mode == QSslSocket::SslClientMode) q_SSL_set_connect_state(ssl); else q_SSL_set_accept_state(ssl); return true;}/*! \internal Declared static in QSslSocketPrivate, makes sure the SSL libraries have been initialized.*/bool QSslSocketPrivate::ensureInitialized(){ if (!q_resolveOpenSslSymbols()) return false; // Check if the library itself needs to be initialized. QMutexLocker locker(openssl_locks()->initLock()); static int q_initialized = false; if (!q_initialized) { q_initialized = true; // Initialize OpenSSL. q_CRYPTO_set_id_callback(id_function); q_CRYPTO_set_locking_callback(locking_function); if (q_SSL_library_init() != 1) return false; q_SSL_load_error_strings(); // Initialize OpenSSL's random seed. if (!q_RAND_status()) { struct { int msec; int sec; void *stack; } randomish; // This is probably not secure enough. randomish.stack = (void *)&randomish; randomish.msec = QTime::currentTime().msec(); randomish.sec = QTime::currentTime().second(); q_RAND_seed((const char *)&randomish, sizeof(randomish)); } resetDefaultCiphers(); setDefaultCaCertificates(systemCaCertificates());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -