diff options
Diffstat (limited to 'src/network/ssl')
-rw-r--r-- | src/network/ssl/qssl.cpp | 7 | ||||
-rw-r--r-- | src/network/ssl/qssl.h | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate.cpp | 46 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration.cpp | 52 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration.h | 2 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration_p.h | 2 | ||||
-rw-r--r-- | src/network/ssl/qsslerror.cpp | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslerror.h | 1 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 89 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.h | 3 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 89 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_p.h | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols.cpp | 33 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols_p.h | 11 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_p.h | 10 |
15 files changed, 252 insertions, 105 deletions
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp index 341ec8f..586c894 100644 --- a/src/network/ssl/qssl.cpp +++ b/src/network/ssl/qssl.cpp @@ -101,12 +101,17 @@ QT_BEGIN_NAMESPACE Describes the protocol of the cipher. - \value SslV3 SSLv3 - the default protocol. + \value SslV3 SSLv3 \value SslV2 SSLv2 \value TlsV1 TLSv1 \value UnknownProtocol The cipher's protocol cannot be determined. \value AnyProtocol The socket understands SSLv2, SSLv3, and TLSv1. This value is used by QSslSocket only. + \value TlsV1SslV3 On the client side, this will send + a TLS 1.0 Client Hello, enabling TLSv1 and SSLv3 connections. + On the server side, this will enable both SSLv3 and TLSv1 connections. + \value SecureProtocols The default option, using protocols known to be secure; + currently behaves like TlsV1SslV3. Note: most servers using SSL understand both versions (2 and 3), but it is recommended to use the latest version only for security diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h index b77053b..2ecd1c3 100644 --- a/src/network/ssl/qssl.h +++ b/src/network/ssl/qssl.h @@ -75,8 +75,10 @@ namespace QSsl { enum SslProtocol { SslV3, SslV2, - TlsV1, + TlsV1, // ### Qt 5: rename to TlsV1_0 or so AnyProtocol, + TlsV1SslV3, + SecureProtocols, UnknownProtocol = -1 }; } diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 328c5c2..76b7d41 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -127,7 +127,7 @@ QT_BEGIN_NAMESPACE // forward declaration -static QMap<QString, QString> _q_mapFromOnelineName(char *name); +static QMap<QString, QString> _q_mapFromX509Name(X509_NAME *name); /*! Constructs a QSslCertificate by reading \a format encoded data @@ -324,7 +324,7 @@ QString QSslCertificate::issuerInfo(SubjectInfo info) const // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = - _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0)); + _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); return d->issuerInfo.value(_q_SubjectInfoToString(info)); } @@ -341,7 +341,7 @@ QString QSslCertificate::issuerInfo(const QByteArray &tag) const // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = - _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0)); + _q_mapFromX509Name(q_X509_get_issuer_name(d->x509)); return d->issuerInfo.value(QString::fromLatin1(tag)); } @@ -360,7 +360,7 @@ QString QSslCertificate::subjectInfo(SubjectInfo info) const // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = - _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0)); + _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); return d->subjectInfo.value(_q_SubjectInfoToString(info)); } @@ -376,7 +376,7 @@ QString QSslCertificate::subjectInfo(const QByteArray &tag) const // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = - _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0)); + _q_mapFromX509Name(q_X509_get_subject_name(d->x509)); return d->subjectInfo.value(QString::fromLatin1(tag)); } @@ -666,37 +666,17 @@ QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::Encodi return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; } -static QMap<QString, QString> _q_mapFromOnelineName(char *name) +static QMap<QString, QString> _q_mapFromX509Name(X509_NAME *name) { QMap<QString, QString> info; - QString infoStr = QString::fromLocal8Bit(name); - q_CRYPTO_free(name); - - // ### The right-hand encoding seems to allow hex (Regulierungsbeh\xC8orde) - //entry.replace(QLatin1String("\\x"), QLatin1String("%")); - //entry = QUrl::fromPercentEncoding(entry.toLatin1()); - // ### See RFC-4630 for more details! - - QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)")); - - int pos = 0; - while ((pos = rx.indexIn(infoStr, pos)) != -1) { - const QString name = rx.cap(1); - - QString value = rx.cap(2); - const int valuePos = rx.pos(2); - - const int next = rx.indexIn(value); - if (next == -1) { - info.insert(name, value); - break; - } - - value = value.left(next); - info.insert(name, value); - pos = valuePos + value.length(); + for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) { + X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i); + const char *obj = q_OBJ_nid2sn(q_OBJ_obj2nid(q_X509_NAME_ENTRY_get_object(e))); + unsigned char *data = 0; + int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e)); + info[QString::fromUtf8(obj)] = QString::fromUtf8((char*)data, size); + q_CRYPTO_free(data); } - return info; } diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index a1f6918..69d3b66 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -47,18 +47,6 @@ QT_BEGIN_NAMESPACE -template<> void QSharedDataPointer<QSslConfigurationPrivate>::detach() -{ - if (d && d->ref == 1) - return; - QSslConfigurationPrivate *x = (d ? new QSslConfigurationPrivate(*d) - : new QSslConfigurationPrivate); - x->ref.ref(); - if (d && !d->ref.deref()) - delete d; - d = x; -} - /*! \class QSslConfiguration \brief The QSslConfiguration class holds the configuration and state of an SSL connection @@ -126,7 +114,7 @@ template<> void QSharedDataPointer<QSslConfigurationPrivate>::detach() Once any setter methods are called, isNull() will return false. */ QSslConfiguration::QSslConfiguration() - : d(0) + : d(new QSslConfigurationPrivate) { } @@ -176,7 +164,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->privateKey == other.d->privateKey && d->sessionCipher == other.d->sessionCipher && d->ciphers == other.d->ciphers && - d->caCertificates == d->caCertificates && + d->caCertificates == other.d->caCertificates && d->protocol == other.d->protocol && d->peerVerifyMode == other.d->peerVerifyMode && d->peerVerifyDepth == other.d->peerVerifyDepth; @@ -203,7 +191,15 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const */ bool QSslConfiguration::isNull() const { - return d == 0; + return (d->protocol == QSsl::SecureProtocols && + d->peerVerifyMode == QSslSocket::AutoVerifyPeer && + d->peerVerifyDepth == 0 && + d->caCertificates.count() == 0 && + d->ciphers.count() == 0 && + d->localCertificate.isNull() && + d->privateKey.isNull() && + d->peerCertificate.isNull() && + d->peerCertificateChain.count() == 0); } /*! @@ -213,7 +209,7 @@ bool QSslConfiguration::isNull() const */ QSsl::SslProtocol QSslConfiguration::protocol() const { - return d ? d->protocol : QSsl::SslV3; + return d->protocol; } /*! @@ -237,13 +233,13 @@ void QSslConfiguration::setProtocol(QSsl::SslProtocol protocol) client), and whether it should require that this certificate is valid. The default mode is AutoVerifyPeer, which tells QSslSocket to use - VerifyPeer for clients, QueryPeer for clients. + VerifyPeer for clients, QueryPeer for servers. \sa setPeerVerifyMode() */ QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const { - return d ? d->peerVerifyMode : QSslSocket::AutoVerifyPeer; + return d->peerVerifyMode; } /*! @@ -253,7 +249,7 @@ QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const client), and whether it should require that this certificate is valid. The default mode is AutoVerifyPeer, which tells QSslSocket to use - VerifyPeer for clients, QueryPeer for clients. + VerifyPeer for clients, QueryPeer for servers. \sa peerVerifyMode() */ @@ -276,7 +272,7 @@ void QSslConfiguration::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode) */ int QSslConfiguration::peerVerifyDepth() const { - return d ? d->peerVerifyDepth : 0; + return d->peerVerifyDepth; } /*! @@ -307,7 +303,7 @@ void QSslConfiguration::setPeerVerifyDepth(int depth) */ QSslCertificate QSslConfiguration::localCertificate() const { - return d ? d->localCertificate : QSslCertificate(); + return d->localCertificate; } /*! @@ -361,7 +357,7 @@ void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate) */ QSslCertificate QSslConfiguration::peerCertificate() const { - return d ? d->peerCertificate : QSslCertificate(); + return d->peerCertificate; } /*! @@ -393,7 +389,7 @@ QSslCertificate QSslConfiguration::peerCertificate() const */ QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const { - return d ? d->peerCertificateChain : QList<QSslCertificate>(); + return d->peerCertificateChain; } /*! @@ -411,7 +407,7 @@ QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const */ QSslCipher QSslConfiguration::sessionCipher() const { - return d ? d->sessionCipher : QSslCipher(); + return d->sessionCipher; } /*! @@ -422,7 +418,7 @@ QSslCipher QSslConfiguration::sessionCipher() const */ QSslKey QSslConfiguration::privateKey() const { - return d ? d->privateKey : QSslKey(); + return d->privateKey; } /*! @@ -464,7 +460,7 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key) */ QList<QSslCipher> QSslConfiguration::ciphers() const { - return d ? d->ciphers : QList<QSslCipher>(); + return d->ciphers; } /*! @@ -494,7 +490,7 @@ void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers) */ QList<QSslCertificate> QSslConfiguration::caCertificates() const { - return d ? d->caCertificates : QList<QSslCertificate>(); + return d->caCertificates; } /*! @@ -518,7 +514,7 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific \list \o no local certificate and no private key - \o protocol SSLv3 + \o protocol SecureProtocols (meaning either TLS 1.0 or SSL 3 will be used) \o the system's default CA certificate list \o the cipher list equal to the list of the SSL libraries' supported SSL ciphers diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 6f8ddbd..258b454 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -86,7 +86,7 @@ public: inline bool operator!=(const QSslConfiguration &other) const { return !(*this == other); } - bool isNull() const; + bool isNull() const; // ### Qt 5: remove; who would need this? QSsl::SslProtocol protocol() const; void setProtocol(QSsl::SslProtocol protocol); diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index fcb00b2..af80e4c 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -80,7 +80,7 @@ class QSslConfigurationPrivate: public QSharedData { public: QSslConfigurationPrivate() - : protocol(QSsl::SslV3), + : protocol(QSsl::SecureProtocols), peerVerifyMode(QSslSocket::AutoVerifyPeer), peerVerifyDepth(0) { } diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp index f9fa90b..5091ed2 100644 --- a/src/network/ssl/qsslerror.cpp +++ b/src/network/ssl/qsslerror.cpp @@ -86,6 +86,7 @@ \value HostNameMismatch \value UnspecifiedError \value NoSslSupport + \value CertificateBlacklisted \sa QSslError::errorString() */ @@ -281,6 +282,9 @@ QString QSslError::errorString() const break; case NoSslSupport: break; + case CertificateBlacklisted: + errStr = QSslSocket::tr("The peer certificate is blacklisted"); + break; default: errStr = QSslSocket::tr("Unknown error"); break; diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h index 54cc4c1..451c95f 100644 --- a/src/network/ssl/qsslerror.h +++ b/src/network/ssl/qsslerror.h @@ -83,6 +83,7 @@ public: NoPeerCertificate, HostNameMismatch, NoSslSupport, + CertificateBlacklisted, UnspecifiedError = -1 }; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 80ce5bc..df61fb6 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -56,7 +56,7 @@ QSslSocket establishes a secure, encrypted TCP connection you can use for transmitting encrypted data. It can operate in both client and server mode, and it supports modern SSL protocols, including - SSLv3 and TLSv1. By default, QSslSocket uses SSLv3, but you can + SSLv3 and TLSv1. By default, QSslSocket uses TLSv1, but you can change the SSL protocol by calling setProtocol() as long as you do it before the handshake has started. @@ -143,6 +143,15 @@ setDefaultCaCertificates(). \endlist + \note If available, root certificates on Unix (excluding Mac OS X) will be + loaded on demand from the standard certificate directories. If + you do not want to load root certificates on demand, you need to call either + the static function setDefaultCaCertificates() before the first SSL handshake + is made in your application, (e.g. via + "QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());"), + or call setCaCertificates() on your QSslSocket instance prior to the SSL + handshake. + For more information about ciphers and certificates, refer to QSslCipher and QSslCertificate. @@ -543,7 +552,7 @@ bool QSslSocket::isEncrypted() const } /*! - Returns the socket's SSL protocol. By default, \l QSsl::SslV3 is used. + Returns the socket's SSL protocol. By default, \l QSsl::SecureProtocols is used. \sa setProtocol() */ @@ -650,6 +659,34 @@ void QSslSocket::setPeerVerifyDepth(int depth) } /*! + \since 4.8 + + Returns the different hostname for the certificate validation, as set by + setPeerVerifyName or by connectToHostEncrypted. + + \sa setPeerVerifyName(), connectToHostEncrypted() +*/ +QString QSslSocket::peerVerifyName() const +{ + Q_D(const QSslSocket); + return d->verificationPeerName; +} + +/*! + \since 4.8 + + Sets a different host name, given by \a hostName, for the certificate + validation instead of the one used for the TCP connection. + + \sa connectToHostEncrypted() +*/ +void QSslSocket::setPeerVerifyName(const QString &hostName) +{ + Q_D(QSslSocket); + d->verificationPeerName = hostName; +} + +/*! \reimp Returns the number of decrypted bytes that are immediately available for @@ -791,14 +828,8 @@ void QSslSocket::setReadBufferSize(qint64 size) Q_D(QSslSocket); d->readBufferMaxSize = size; - // set the plain socket's buffer size to 1k if we have a limit - // see also the same logic in QSslSocketPrivate::createPlainSocket - if (d->plainSocket) { - if (d->mode == UnencryptedMode) - d->plainSocket->setReadBufferSize(size); - else - d->plainSocket->setReadBufferSize(size ? 1024 : 0); - } + if (d->plainSocket) + d->plainSocket->setReadBufferSize(size); } /*! @@ -865,6 +896,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); d->configuration.peerVerifyMode = configuration.peerVerifyMode(); d->configuration.protocol = configuration.protocol(); + d->allowRootCertOnDemandLoading = false; } /*! @@ -1249,6 +1281,7 @@ void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates) { Q_D(QSslSocket); d->configuration.caCertificates = certificates; + d->allowRootCertOnDemandLoading = false; } /*! @@ -1258,6 +1291,9 @@ void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates) handshake with addCaCertificate(), addCaCertificates(), and setCaCertificates(). + \note On Unix, this method may return an empty list if the root + certificates are loaded on demand. + \sa addCaCertificate(), addCaCertificates(), setCaCertificates() */ QList<QSslCertificate> QSslSocket::caCertificates() const @@ -1311,10 +1347,9 @@ void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certific /*! Sets the default CA certificate database to \a certificates. The default CA certificate database is originally set to your system's - default CA certificate database. If no system default database is - found, Qt will provide its own default database. You can override - the default CA certificate database with your own CA certificate - database using this function. + default CA certificate database. You can override the default CA + certificate database with your own CA certificate database using + this function. Each SSL socket's CA certificate database is initialized to the default CA certificate database. @@ -1336,6 +1371,9 @@ void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certific Each SSL socket's CA certificate database is initialized to the default CA certificate database. + \note On Unix, this method may return an empty list if the root + certificates are loaded on demand. + \sa caCertificates() */ QList<QSslCertificate> QSslSocket::defaultCaCertificates() @@ -1805,6 +1843,7 @@ QSslSocketPrivate::QSslSocketPrivate() , connectionEncrypted(false) , ignoreAllSslErrors(false) , readyReadEmittedPointer(0) + , allowRootCertOnDemandLoading(true) , plainSocket(0) { QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); @@ -1881,6 +1920,7 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph */ QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates() { + // ### Qt5: rename everything containing "caCertificates" to "rootCertificates" or similar QSslSocketPrivate::ensureInitialized(); QMutexLocker locker(&globalData()->mutex); return globalData()->config->caCertificates; @@ -1895,6 +1935,9 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &c QMutexLocker locker(&globalData()->mutex); globalData()->config.detach(); globalData()->config->caCertificates = certs; + // when the certificates are set explicitly, we do not want to + // load the system certificates on demand + s_loadRootCertsOnDemand = false; } /*! @@ -2002,6 +2045,10 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode) q->setPeerName(QString()); plainSocket = new QTcpSocket(q); +#ifndef QT_NO_BEARERMANAGEMENT + //copy network session down to the plain socket (if it has been set) + plainSocket->setProperty("_q_networksession", q->property("_q_networksession")); +#endif q->connect(plainSocket, SIGNAL(connected()), q, SLOT(_q_connectedSlot()), Qt::DirectConnection); @@ -2194,6 +2241,20 @@ void QSslSocketPrivate::_q_flushReadBuffer() transmit(); } +/*! + \internal +*/ +QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories() +{ + return QList<QByteArray>() << "/etc/ssl/certs/" // (K)ubuntu, OpenSUSE, Mandriva, MeeGo ... + << "/usr/lib/ssl/certs/" // Gentoo, Mandrake + << "/usr/share/ssl/" // Centos, Redhat, SuSE + << "/usr/local/ssl/" // Normal OpenSSL Tarball + << "/var/ssl/certs/" // AIX + << "/usr/local/ssl/certs/" // Solaris + << "/opt/openssl/certs/"; // HP-UX +} + QT_END_NAMESPACE // For private slots diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index 3f3e59e..1e7c67c 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -106,6 +106,9 @@ public: int peerVerifyDepth() const; void setPeerVerifyDepth(int depth); + QString peerVerifyName() const; + void setPeerVerifyName(const QString &hostName); + // From QIODevice qint64 bytesAvailable() const; qint64 bytesToWrite() const; diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 141d80a..9a137a6 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -60,9 +60,15 @@ #include <QtCore/qvarlengtharray.h> #include <QLibrary> // for loading the security lib for the CA store +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +// Symbian does not seem to have the symbol for SNI defined +#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME +#define SSL_CTRL_SET_TLSEXT_HOSTNAME 55 +#endif +#endif QT_BEGIN_NAMESPACE -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) #define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file PtrSecCertificateGetData QSslSocketPrivate::ptrSecCertificateGetData = 0; PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0; @@ -80,6 +86,7 @@ QT_BEGIN_NAMESPACE bool QSslSocketPrivate::s_libraryLoaded = false; bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; +bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; /* \internal @@ -252,6 +259,8 @@ init_context: case QSsl::SslV3: ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method()); break; + case QSsl::SecureProtocols: // SslV2 will be disabled below + case QSsl::TlsV1SslV3: // SslV2 will be disabled below case QSsl::AnyProtocol: default: ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); @@ -277,7 +286,11 @@ init_context: } // Enable all bug workarounds. - q_SSL_CTX_set_options(ctx, SSL_OP_ALL); + if (configuration.protocol == QSsl::TlsV1SslV3 || configuration.protocol == QSsl::SecureProtocols) { + q_SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); + } else { + q_SSL_CTX_set_options(ctx, SSL_OP_ALL); + } // Initialize ciphers QByteArray cipherString; @@ -326,6 +339,13 @@ init_context: } } + if (s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) { + // tell OpenSSL the directories where to look up the root certs on demand + QList<QByteArray> unixDirs = unixRootCertDirectories(); + for (int a = 0; a < unixDirs.count(); ++a) + q_SSL_CTX_load_verify_locations(ctx, 0, unixDirs.at(a).constData()); + } + // Register a custom callback to get all verification errors. X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback); @@ -387,6 +407,25 @@ init_context: return false; } +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) + if ((configuration.protocol == QSsl::TlsV1SslV3 || + configuration.protocol == QSsl::TlsV1 || + configuration.protocol == QSsl::SecureProtocols || + configuration.protocol == QSsl::AnyProtocol) && + client && q_SSLeay() >= 0x00090806fL) { + // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format. + QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName; + if (tlsHostName.isEmpty()) + tlsHostName = hostName; + QByteArray ace = QUrl::toAce(tlsHostName); + // only send the SNI header if the URL is valid and not an IP + if (!ace.isEmpty() && !QHostAddress().setAddress(tlsHostName)) { + if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.constData())) + qWarning("could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled"); + } + } +#endif + // Clear the session. q_SSL_clear(ssl); errorList.clear(); @@ -493,7 +532,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() resetDefaultCiphers(); //load symbols needed to receive certificates from system store -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security"); if (securityLib.load()) { ptrSecCertificateGetData = (PtrSecCertificateGetData) securityLib.resolve("SecCertificateGetData"); @@ -526,8 +565,22 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() } else { qWarning("could not load crypt32 library"); // should never happen } +#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_MAC) + // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) + QList<QByteArray> dirs = unixRootCertDirectories(); + QStringList symLinkFilter; + symLinkFilter << QLatin1String("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"); + for (int a = 0; a < dirs.count(); ++a) { + QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files); + if (iterator.hasNext()) { + s_loadRootCertsOnDemand = true; + break; + } + } #endif - setDefaultCaCertificates(systemCaCertificates()); + // if on-demand loading was not enabled, load the certs now + if (!s_loadRootCertsOnDemand) + setDefaultCaCertificates(systemCaCertificates()); } /*! @@ -760,7 +813,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() timer.start(); #endif QList<QSslCertificate> systemCerts; -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) CFArrayRef cfCerts; OSStatus status = 1; @@ -823,15 +876,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() } #elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) QSet<QString> certFiles; - QList<QByteArray> directories; - directories << "/etc/ssl/certs/"; // (K)ubuntu, OpenSUSE, Mandriva, MeeGo ... - directories << "/usr/lib/ssl/certs/"; // Gentoo, Mandrake - directories << "/usr/share/ssl/"; // Centos, Redhat, SuSE - directories << "/usr/local/ssl/"; // Normal OpenSSL Tarball - directories << "/var/ssl/certs/"; // AIX - directories << "/usr/local/ssl/certs/"; // Solaris - directories << "/opt/openssl/certs/"; // HP-UX - + QList<QByteArray> directories = unixRootCertDirectories(); QDir currentDir; QStringList nameFilters; nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt"); @@ -1193,16 +1238,18 @@ bool QSslSocketBackendPrivate::startHandshake() X509 *x509 = q_SSL_get_peer_certificate(ssl); configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); q_X509_free(x509); - if (QSslCertificatePrivate::isBlacklisted(configuration.peerCertificate)) { - q->setErrorString(QSslSocket::tr("The peer certificate is blacklisted")); - q->setSocketError(QAbstractSocket::SslHandshakeFailedError); - emit q->error(QAbstractSocket::SslHandshakeFailedError); - plainSocket->disconnectFromHost(); - return false; - } // Start translating errors. QList<QSslError> errors; + + if (QSslCertificatePrivate::isBlacklisted(configuration.peerCertificate)) { + QSslError error(QSslError::CertificateBlacklisted, configuration.peerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer && mode == QSslSocket::SslClientMode); diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index ae630bf..7e489a4 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -79,6 +79,10 @@ #include <openssl/x509_vfy.h> #include <openssl/dsa.h> #include <openssl/rsa.h> +#include <openssl/crypto.h> +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +#include <openssl/tls1.h> +#endif #if OPENSSL_VERSION_NUMBER >= 0x10000000L typedef _STACK STACK; diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index d370f12..a4cc3c4 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -101,6 +101,7 @@ DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return) DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return 0, return) DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return) +DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return); DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return) DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return) DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return) @@ -208,6 +209,9 @@ DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return) DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return) DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, const void *parg, parg, return -1, return) +#endif DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return) DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG) DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG) @@ -245,7 +249,10 @@ DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return 0, return) DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return 0, return) DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return) -DEFINEFUNC3(char *, X509_NAME_oneline, X509_NAME *a, a, char *b, b, int c, c, return 0, return) +DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return) +DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return 0, return) +DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return 0, return) +DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return 0, return) DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return 0, return) DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG) DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return 0, return) @@ -262,6 +269,8 @@ DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, #endif DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return) +DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return) #ifdef Q_OS_SYMBIAN #define RESOLVEFUNC(func, ordinal, lib) \ @@ -513,6 +522,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(ASN1_INTEGER_get, 48, libs.second ) RESOLVEFUNC(ASN1_STRING_data, 71, libs.second ) RESOLVEFUNC(ASN1_STRING_length, 76, libs.second ) + RESOLVEFUNC(ASN1_STRING_to_UTF8, 86, libs.second ) RESOLVEFUNC(BIO_ctrl, 184, libs.second ) RESOLVEFUNC(BIO_free, 209, libs.second ) RESOLVEFUNC(BIO_new, 222, libs.second ) @@ -585,6 +595,9 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_library_init, 137, libs.first ) RESOLVEFUNC(SSL_load_error_strings, 139, libs.first ) RESOLVEFUNC(SSL_new, 140, libs.first ) +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) + RESOLVEFUNC(SSL_ctrl, 95, libs.first ) +#endif RESOLVEFUNC(SSL_read, 143, libs.first ) RESOLVEFUNC(SSL_set_accept_state, 148, libs.first ) RESOLVEFUNC(SSL_set_bio, 149, libs.first ) @@ -599,7 +612,11 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSLv3_server_method, 197, libs.first ) RESOLVEFUNC(SSLv23_server_method, 191, libs.first ) RESOLVEFUNC(TLSv1_server_method, 200, libs.first ) - RESOLVEFUNC(X509_NAME_oneline, 1830, libs.second ) + RESOLVEFUNC(SSL_CTX_load_verify_locations, 34, libs.first ) + RESOLVEFUNC(X509_NAME_entry_count, 1821, libs.second ) + RESOLVEFUNC(X509_NAME_get_entry, 1823, libs.second ) + RESOLVEFUNC(X509_NAME_ENTRY_get_data, 1808, libs.second ) + RESOLVEFUNC(X509_NAME_ENTRY_get_object, 1809, libs.second ) RESOLVEFUNC(X509_PUBKEY_get, 1844, libs.second ) RESOLVEFUNC(X509_STORE_free, 1939, libs.second ) RESOLVEFUNC(X509_STORE_new, 1942, libs.second ) @@ -630,6 +647,7 @@ bool q_resolveOpenSslSymbols() #endif RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf, 1153, libs.second ) RESOLVEFUNC(OPENSSL_add_all_algorithms_conf, 1152, libs.second ) + RESOLVEFUNC(SSLeay, 1504, libs.second ) #else // Q_OS_SYMBIAN #ifdef SSLEAY_MACROS RESOLVEFUNC(ASN1_dup) @@ -637,6 +655,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(ASN1_INTEGER_get) RESOLVEFUNC(ASN1_STRING_data) RESOLVEFUNC(ASN1_STRING_length) + RESOLVEFUNC(ASN1_STRING_to_UTF8) RESOLVEFUNC(BIO_ctrl) RESOLVEFUNC(BIO_free) RESOLVEFUNC(BIO_new) @@ -709,6 +728,9 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_library_init) RESOLVEFUNC(SSL_load_error_strings) RESOLVEFUNC(SSL_new) +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) + RESOLVEFUNC(SSL_ctrl) +#endif RESOLVEFUNC(SSL_read) RESOLVEFUNC(SSL_set_accept_state) RESOLVEFUNC(SSL_set_bio) @@ -723,7 +745,10 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSLv3_server_method) RESOLVEFUNC(SSLv23_server_method) RESOLVEFUNC(TLSv1_server_method) - RESOLVEFUNC(X509_NAME_oneline) + RESOLVEFUNC(X509_NAME_entry_count) + RESOLVEFUNC(X509_NAME_get_entry) + RESOLVEFUNC(X509_NAME_ENTRY_get_data) + RESOLVEFUNC(X509_NAME_ENTRY_get_object) RESOLVEFUNC(X509_PUBKEY_get) RESOLVEFUNC(X509_STORE_free) RESOLVEFUNC(X509_STORE_new) @@ -754,6 +779,8 @@ bool q_resolveOpenSslSymbols() #endif RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf) RESOLVEFUNC(OPENSSL_add_all_algorithms_conf) + RESOLVEFUNC(SSL_CTX_load_verify_locations) + RESOLVEFUNC(SSLeay) #endif // Q_OS_SYMBIAN symbolsResolved = true; delete libs.first; diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 671f6fb..c0a3b4d 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -204,6 +204,7 @@ bool q_resolveOpenSslSymbols(); long q_ASN1_INTEGER_get(ASN1_INTEGER *a); unsigned char * q_ASN1_STRING_data(ASN1_STRING *a); int q_ASN1_STRING_length(ASN1_STRING *a); +int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b); long q_BIO_ctrl(BIO *a, int b, long c, void *d); int q_BIO_free(BIO *a); BIO *q_BIO_new(BIO_METHOD *a); @@ -316,6 +317,9 @@ long q_SSL_get_verify_result(SSL *a); int q_SSL_library_init(); void q_SSL_load_error_strings(); SSL *q_SSL_new(SSL_CTX *a); +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +long q_SSL_ctrl(SSL *ssl,int cmd, long larg, const void *parg); +#endif int q_SSL_read(SSL *a, void *b, int c); void q_SSL_set_bio(SSL *a, BIO *b, BIO *c); void q_SSL_set_accept_state(SSL *a); @@ -357,7 +361,10 @@ void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d); X509_NAME *q_X509_get_issuer_name(X509 *a); X509_NAME *q_X509_get_subject_name(X509 *a); int q_X509_verify_cert(X509_STORE_CTX *ctx); -char *q_X509_NAME_oneline(X509_NAME *a, char *b, int c); +int q_X509_NAME_entry_count(X509_NAME *a); +X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b); +ASN1_STRING *q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a); +ASN1_OBJECT *q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a); EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a); void q_X509_STORE_free(X509_STORE *store); X509_STORE *q_X509_STORE_new(); @@ -412,6 +419,8 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); #endif void q_OPENSSL_add_all_algorithms_noconf(); void q_OPENSSL_add_all_algorithms_conf(); +int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); +long q_SSLeay(); // Helper function class QDateTime; diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 0f1ded8..ee6361f 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -69,10 +69,13 @@ QT_BEGIN_NAMESPACE #if defined(Q_OS_MAC) #include <Security/SecCertificate.h> #include <CoreFoundation/CFArray.h> +#ifndef QT_NO_CORESERVICES typedef OSStatus (*PtrSecCertificateGetData)(SecCertificateRef, CSSM_DATA_PTR); typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*); typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*); +#endif #elif defined(Q_OS_WIN) +#include <windows.h> #include <wincrypt.h> #ifndef HCRYPTPROV_LEGACY #define HCRYPTPROV_LEGACY HCRYPTPROV @@ -112,6 +115,8 @@ public: // that was used for connecting to. QString verificationPeerName; + bool allowRootCertOnDemandLoading; + static bool supportsSsl(); static void ensureInitialized(); static void deinitialize(); @@ -129,7 +134,7 @@ public: static void addDefaultCaCertificate(const QSslCertificate &cert); static void addDefaultCaCertificates(const QList<QSslCertificate> &certs); -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) static PtrSecCertificateGetData ptrSecCertificateGetData; static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates; static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates; @@ -168,6 +173,9 @@ private: static bool s_libraryLoaded; static bool s_loadedCiphersAndCerts; +protected: + static bool s_loadRootCertsOnDemand; + static QList<QByteArray> unixRootCertDirectories(); }; QT_END_NAMESPACE |