diff options
Diffstat (limited to 'src/network/ssl')
24 files changed, 9304 insertions, 0 deletions
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp new file mode 100644 index 0000000000..55942969e4 --- /dev/null +++ b/src/network/ssl/qssl.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslkey.h" + +QT_BEGIN_NAMESPACE + +/*! \namespace QSsl + + \brief The QSsl namespace declares enums common to all SSL classes in QtNetwork. + \since 4.3 + + \ingroup network + \ingroup ssl + \inmodule QtNetwork +*/ + +/*! + \enum QSsl::KeyType + + Describes the two types of keys QSslKey supports. + + \value PrivateKey A private key. + \value PublicKey A public key. +*/ + +/*! + \enum QSsl::KeyAlgorithm + + Describes the different key algorithms supported by QSslKey. + + \value Rsa The RSA algorithm. + \value Dsa The DSA algorithm. +*/ + +/*! + \enum QSsl::EncodingFormat + + Describes supported encoding formats for certificates and keys. + + \value Pem The PEM format. + \value Der The DER format. +*/ + +/*! + \enum QSsl::AlternateNameEntryType + + Describes the key types for alternate name entries in QSslCertificate. + + \value EmailEntry An email entry; the entry contains an email address that + the certificate is valid for. + + \value DnsEntry A DNS host name entry; the entry contains a host name + entry that the certificate is valid for. The entry may contain wildcards. + + \sa QSslCertificate::alternateSubjectNames() + +*/ + +/*! + \enum QSsl::SslProtocol + + Describes the protocol of the cipher. + + \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 + reasons. However, SSL and TLS are not compatible with each other: + if you get unexpected handshake failures, verify that you chose + the correct setting for your protocol. +*/ + +QT_END_NAMESPACE diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h new file mode 100644 index 0000000000..24dbb09747 --- /dev/null +++ b/src/network/ssl/qssl.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSL_H +#define QSSL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +namespace QSsl { + enum KeyType { + PrivateKey, + PublicKey + }; + + enum EncodingFormat { + Pem, + Der + }; + + enum KeyAlgorithm { + Rsa, + Dsa + }; + + enum AlternateNameEntryType { + EmailEntry, + DnsEntry + }; + + enum SslProtocol { + SslV3, + SslV2, + TlsV1, // ### Qt 5: rename to TlsV1_0 or so + AnyProtocol, + TlsV1SslV3, + SecureProtocols, + UnknownProtocol = -1 + }; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSSL_H diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp new file mode 100644 index 0000000000..a5cdf011aa --- /dev/null +++ b/src/network/ssl/qsslcertificate.cpp @@ -0,0 +1,858 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslCertificate + \brief The QSslCertificate class provides a convenient API for an X509 certificate. + \since 4.3 + + \reentrant + \ingroup network + \ingroup ssl + \inmodule QtNetwork + + QSslCertificate stores an X509 certificate, and is commonly used + to verify the identity and store information about the local host, + a remotely connected peer, or a trusted third party Certificate + Authority. + + There are many ways to construct a QSslCertificate. The most + common way is to call QSslSocket::peerCertificate(), which returns + a QSslCertificate object, or QSslSocket::peerCertificateChain(), + which returns a list of them. You can also load certificates from + a DER (binary) or PEM (Base64) encoded bundle, typically stored as + one or more local files, or in a Qt Resource. + + You can call isNull() to check if your certificate is null. By + default, QSslCertificate constructs a null certificate. To check + if the certificate is valid, call isValid(). A null certificate is + invalid, but an invalid certificate is not necessarily null. If + you want to reset all contents in a certificate, call clear(). + + After loading a certificate, you can find information about the + certificate, its subject, and its issuer, by calling one of the + many accessor functions, including version(), serialNumber(), + issuerInfo() and subjectInfo(). You can call effectiveDate() and + expiryDate() to check when the certificate starts being + effective and when it expires. + The publicKey() function returns the certificate + subject's public key as a QSslKey. You can call issuerInfo() or + subjectInfo() to get detailed information about the certificate + issuer and its subject. + + Internally, QSslCertificate is stored as an X509 structure. You + can access this handle by calling handle(), but the results are + likely to not be portable. + + \sa QSslSocket, QSslKey, QSslCipher, QSslError +*/ + +/*! + \enum QSslCertificate::SubjectInfo + + Describes keys that you can pass to QSslCertificate::issuerInfo() or + QSslCertificate::subjectInfo() to get information about the certificate + issuer or subject. + + \value Organization "O" The name of the organization. + + \value CommonName "CN" The common name; most often this is used to store + the host name. + + \value LocalityName "L" The locality. + + \value OrganizationalUnitName "OU" The organizational unit name. + + \value CountryName "C" The country. + + \value StateOrProvinceName "ST" The state or province. +*/ + +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslcertificate.h" +#include "qsslcertificate_p.h" +#include "qsslkey.h" +#include "qsslkey_p.h" + +#include <QtCore/qatomic.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/qmap.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +// forward declaration +static QMap<QString, QString> _q_mapFromOnelineName(char *name); + +/*! + Constructs a QSslCertificate by reading \a format encoded data + from \a device and using the first certificate found. You can + later call isNull() to see if \a device contained a certificate, + and if this certificate was loaded successfully. +*/ +QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format) + : d(new QSslCertificatePrivate) +{ + QSslSocketPrivate::ensureInitialized(); + if (device) + d->init(device->readAll(), format); +} + +/*! + Constructs a QSslCertificate by parsing the \a format encoded + \a data and using the first available certificate found. You can + later call isNull() to see if \a data contained a certificate, + and if this certificate was loaded successfully. +*/ +QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format) + : d(new QSslCertificatePrivate) +{ + QSslSocketPrivate::ensureInitialized(); + d->init(data, format); +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslCertificate::QSslCertificate(const QSslCertificate &other) : d(other.d) +{ +} + +/*! + Destroys the QSslCertificate. +*/ +QSslCertificate::~QSslCertificate() +{ +} + +/*! + Copies the contents of \a other into this certificate, making the two + certificates identical. +*/ +QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this certificate is the same as \a other; otherwise + returns false. +*/ +bool QSslCertificate::operator==(const QSslCertificate &other) const +{ + if (d == other.d) + return true; + if (d->null && other.d->null) + return true; + if (d->x509 && other.d->x509) + return q_X509_cmp(d->x509, other.d->x509) == 0; + return false; +} + +/*! + \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const + + Returns true if this certificate is not the same as \a other; otherwise + returns false. +*/ + +/*! + Returns true if this is a null certificate (i.e., a certificate + with no contents); otherwise returns false. + + By default, QSslCertificate constructs a null certificate. + + \sa isValid(), clear() +*/ +bool QSslCertificate::isNull() const +{ + return d->null; +} + +/*! + Returns true if this certificate is valid; otherwise returns + false. + + Note: Currently, this function checks that the current + data-time is within the date-time range during which the + certificate is considered valid, and checks that the + certificate is not in a blacklist of fraudulent certificates. + + \sa isNull() +*/ +bool QSslCertificate::isValid() const +{ + const QDateTime currentTime = QDateTime::currentDateTime(); + return currentTime >= d->notValidBefore && + currentTime <= d->notValidAfter && + ! QSslCertificatePrivate::isBlacklisted(*this); +} + +/*! + Clears the contents of this certificate, making it a null + certificate. + + \sa isNull() +*/ +void QSslCertificate::clear() +{ + if (isNull()) + return; + d = new QSslCertificatePrivate; +} + +/*! + Returns the certificate's version string. +*/ +QByteArray QSslCertificate::version() const +{ + if (d->versionString.isEmpty() && d->x509) + d->versionString = + QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1); + + return d->versionString; +} + +/*! + Returns the certificate's serial number string in decimal format. + In case the serial number cannot be converted to decimal format + (i.e. if it is bigger than 4294967295, which means it does not fit into 4 bytes), + its hexadecimal version is returned. +*/ +QByteArray QSslCertificate::serialNumber() const +{ + if (d->serialNumberString.isEmpty() && d->x509) { + ASN1_INTEGER *serialNumber = d->x509->cert_info->serialNumber; + // if we cannot convert to a long, just output the hexadecimal number + if (serialNumber->length > 4) { + QByteArray hexString; + hexString.reserve(serialNumber->length * 3); + for (int a = 0; a < serialNumber->length; ++a) { + hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0'); + hexString += ':'; + } + hexString.chop(1); + d->serialNumberString = hexString; + } else { + d->serialNumberString = QByteArray::number(qlonglong(q_ASN1_INTEGER_get(serialNumber))); + } + } + return d->serialNumberString; +} + +/*! + Returns a cryptographic digest of this certificate. By default, + an MD5 digest will be generated, but you can also specify a + custom \a algorithm. +*/ +QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const +{ + return QCryptographicHash::hash(toDer(), algorithm); +} + +static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info) +{ + QString str; + switch (info) { + case QSslCertificate::Organization: str = QLatin1String("O"); break; + case QSslCertificate::CommonName: str = QLatin1String("CN"); break; + case QSslCertificate::LocalityName: str = QLatin1String("L"); break; + case QSslCertificate::OrganizationalUnitName: str = QLatin1String("OU"); break; + case QSslCertificate::CountryName: str = QLatin1String("C"); break; + case QSslCertificate::StateOrProvinceName: str = QLatin1String("ST"); break; + } + return str; +} + +/*! + \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const + + Returns the issuer information for the \a subject from the + certificate, or an empty string if there is no information for + \a subject in the certificate. + + \sa subjectInfo() +*/ +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)); + + return d->issuerInfo.value(_q_SubjectInfoToString(info)); +} + +/*! + Returns the issuer information for \a tag from the certificate, + or an empty string if there is no information for \a tag in the + certificate. + + \sa subjectInfo() +*/ +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)); + + return d->issuerInfo.value(QString::fromLatin1(tag)); +} + +/*! + + \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const + + Returns the information for the \a subject, or an empty string if + there is no information for \a subject in the certificate. + + \sa issuerInfo() +*/ +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)); + + return d->subjectInfo.value(_q_SubjectInfoToString(info)); +} + +/*! + Returns the subject information for \a tag, or an empty string if + there is no information for \a tag in the certificate. + + \sa issuerInfo() +*/ +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)); + + return d->subjectInfo.value(QString::fromLatin1(tag)); +} + +/*! + Returns the list of alternative subject names for this + certificate. The alternate subject names typically contain host + names, optionally with wildcards, that are valid for this + certificate. + + These names are tested against the connected peer's host name, if + either the subject information for \l CommonName doesn't define a + valid host name, or the subject info name doesn't match the peer's + host name. + + \sa subjectInfo() +*/ +QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const +{ + QMultiMap<QSsl::AlternateNameEntryType, QString> result; + + if (!d->x509) + return result; + + STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0); + + if (altNames) { + for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) { + const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i); + if (genName->type != GEN_DNS && genName->type != GEN_EMAIL) + continue; + + int len = q_ASN1_STRING_length(genName->d.ia5); + if (len < 0 || len >= 8192) { + // broken name + continue; + } + + const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_data(genName->d.ia5)); + const QString altName = QString::fromLatin1(altNameStr, len); + if (genName->type == GEN_DNS) + result.insert(QSsl::DnsEntry, altName); + else if (genName->type == GEN_EMAIL) + result.insert(QSsl::EmailEntry, altName); + } + q_sk_pop_free((STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_sk_free)); + } + + return result; +} + +/*! + Returns the date-time that the certificate becomes valid, or an + empty QDateTime if this is a null certificate. + + \sa expiryDate() +*/ +QDateTime QSslCertificate::effectiveDate() const +{ + return d->notValidBefore; +} + +/*! + Returns the date-time that the certificate expires, or an empty + QDateTime if this is a null certificate. + + \sa effectiveDate() +*/ +QDateTime QSslCertificate::expiryDate() const +{ + return d->notValidAfter; +} + +/*! + Returns a pointer to the native certificate handle, if there is + one, or a null pointer otherwise. + + You can use this handle, together with the native API, to access + extended information about the certificate. + + \warning Use of this function has a high probability of being + non-portable, and its return value may vary from platform to + platform or change from minor release to minor release. +*/ +Qt::HANDLE QSslCertificate::handle() const +{ + return Qt::HANDLE(d->x509); +} + +/*! + Returns the certificate subject's public key. +*/ +QSslKey QSslCertificate::publicKey() const +{ + if (!d->x509) + return QSslKey(); + + QSslKey key; + + key.d->type = QSsl::PublicKey; + X509_PUBKEY *xkey = d->x509->cert_info->key; + EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey); + Q_ASSERT(pkey); + + if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) { + key.d->rsa = q_EVP_PKEY_get1_RSA(pkey); + key.d->algorithm = QSsl::Rsa; + key.d->isNull = false; + } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) { + key.d->dsa = q_EVP_PKEY_get1_DSA(pkey); + key.d->algorithm = QSsl::Dsa; + key.d->isNull = false; + } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) { + // DH unsupported + } else { + // error? + } + + q_EVP_PKEY_free(pkey); + return key; +} + +/*! + Returns this certificate converted to a PEM (Base64) encoded + representation. +*/ +QByteArray QSslCertificate::toPem() const +{ + if (!d->x509) + return QByteArray(); + return d->QByteArray_from_X509(d->x509, QSsl::Pem); +} + +/*! + Returns this certificate converted to a DER (binary) encoded + representation. +*/ +QByteArray QSslCertificate::toDer() const +{ + if (!d->x509) + return QByteArray(); + return d->QByteArray_from_X509(d->x509, QSsl::Der); +} + +/*! + Searches all files in the \a path for certificates encoded in the + specified \a format and returns them in a list. \e must be a file or a + pattern matching one or more files, as specified by \a syntax. + + Example: + + \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0 + + \sa fromData() +*/ +QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, + QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax) +{ + // $, (,), *, +, ., ?, [, ,], ^, {, | and }. + int pos = -1; + if (syntax == QRegExp::Wildcard) + pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\*\\?\\[\\]]"))); + else if (syntax != QRegExp::FixedString) + pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"))); + QString pathPrefix = path.left(pos); // == path if pos < 0 + if (pos != -1) + pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/'))); + + // Special case - if the prefix ends up being nothing, use "." instead and + // chop off the first two characters from the glob'ed paths. + int startIndex = 0; + if (pathPrefix.trimmed().isEmpty()) { + if(path.startsWith(QLatin1Char('/'))) { + pathPrefix = path.left(path.indexOf(QRegExp(QLatin1String("[\\*\\?\\[]")))); + pathPrefix = path.left(path.lastIndexOf(QLatin1Char('/'))); + } else { + startIndex = 2; + pathPrefix = QLatin1String("."); + } + } + + // The path is a file. + if (pos == -1 && QFileInfo(pathPrefix).isFile()) { + QFile file(pathPrefix); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + return QSslCertificate::fromData(file.readAll(),format); + return QList<QSslCertificate>(); + } + + // The path can be a file or directory. + QList<QSslCertificate> certs; + QRegExp pattern(path, Qt::CaseSensitive, syntax); + QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + while (it.hasNext()) { + QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex); + if (!pattern.exactMatch(filePath)) + continue; + + QFile file(filePath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + certs += QSslCertificate::fromData(file.readAll(),format); + } + return certs; +} + +/*! + Searches for and parses all certificates in \a device that are + encoded in the specified \a format and returns them in a list of + certificates. + + \sa fromData() +*/ +QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format) +{ + if (!device) { + qWarning("QSslCertificate::fromDevice: cannot read from a null device"); + return QList<QSslCertificate>(); + } + return fromData(device->readAll(), format); +} + +/*! + Searches for and parses all certificates in \a data that are + encoded in the specified \a format and returns them in a list of + certificates. + + \sa fromDevice() +*/ +QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format) +{ + return (format == QSsl::Pem) + ? QSslCertificatePrivate::certificatesFromPem(data) + : QSslCertificatePrivate::certificatesFromDer(data); +} + +void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) +{ + if (!data.isEmpty()) { + QList<QSslCertificate> certs = (format == QSsl::Pem) + ? certificatesFromPem(data, 1) + : certificatesFromDer(data, 1); + if (!certs.isEmpty()) { + *this = *certs.first().d; + if (x509) + x509 = q_X509_dup(x509); + } + } +} + +#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" +#define ENDCERTSTRING "-----END CERTIFICATE-----" + +// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations) +QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format) +{ + if (!x509) { + qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509"); + return QByteArray(); + } + + // Use i2d_X509 to convert the X509 to an array. + int length = q_i2d_X509(x509, 0); + QByteArray array; + array.resize(length); + char *data = array.data(); + char **dataP = &data; + unsigned char **dataPu = (unsigned char **)dataP; + if (q_i2d_X509(x509, dataPu) < 0) + return QByteArray(); + + if (format == QSsl::Der) + return array; + + // Convert to Base64 - wrap at 64 characters. + array = array.toBase64(); + QByteArray tmp; + for (int i = 0; i <= array.size() - 64; i += 64) { + tmp += QByteArray::fromRawData(array.data() + i, 64); + tmp += '\n'; + } + if (int remainder = array.size() % 64) { + tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); + tmp += '\n'; + } + + return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; +} + +static QMap<QString, QString> _q_mapFromOnelineName(char *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(); + } + + return info; +} + +QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509) +{ + QSslCertificate certificate; + if (!x509 || !QSslSocket::supportsSsl()) + return certificate; + + ASN1_TIME *nbef = q_X509_get_notBefore(x509); + ASN1_TIME *naft = q_X509_get_notAfter(x509); + certificate.d->notValidBefore = q_getTimeFromASN1(nbef); + certificate.d->notValidAfter = q_getTimeFromASN1(naft); + certificate.d->null = false; + certificate.d->x509 = q_X509_dup(x509); + + return certificate; +} + +static bool matchLineFeed(const QByteArray &pem, int *offset) +{ + char ch = 0; + + // ignore extra whitespace at the end of the line + while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ') + ++*offset; + + if (ch == '\n') { + *offset += 1; + return true; + } + if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') { + *offset += 2; + return true; + } + return false; +} + +QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) +{ + QList<QSslCertificate> certificates; + QSslSocketPrivate::ensureInitialized(); + + int offset = 0; + while (count == -1 || certificates.size() < count) { + int startPos = pem.indexOf(BEGINCERTSTRING, offset); + if (startPos == -1) + break; + startPos += sizeof(BEGINCERTSTRING) - 1; + if (!matchLineFeed(pem, &startPos)) + break; + + int endPos = pem.indexOf(ENDCERTSTRING, startPos); + if (endPos == -1) + break; + + offset = endPos + sizeof(ENDCERTSTRING) - 1; + if (offset < pem.size() && !matchLineFeed(pem, &offset)) + break; + + QByteArray decoded = QByteArray::fromBase64( + QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + const unsigned char *data = (const unsigned char *)decoded.data(); +#else + unsigned char *data = (unsigned char *)decoded.data(); +#endif + + if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) { + certificates << QSslCertificate_from_X509(x509); + q_X509_free(x509); + } + } + + return certificates; +} + +QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) +{ + QList<QSslCertificate> certificates; + QSslSocketPrivate::ensureInitialized(); + + +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + const unsigned char *data = (const unsigned char *)der.data(); +#else + unsigned char *data = (unsigned char *)der.data(); +#endif + int size = der.size(); + + while (count == -1 || certificates.size() < count) { + if (X509 *x509 = q_d2i_X509(0, &data, size)) { + certificates << QSslCertificate_from_X509(x509); + q_X509_free(x509); + } else { + break; + } + size -= ((char *)data - der.data()); + } + + return certificates; +} + +// These certificates are known to be fraudulent and were created during the comodo +// compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html +static const char *certificate_blacklist[] = { + "04:7e:cb:e9:fc:a5:5f:7b:d0:9e:ae:36:e1:0c:ae:1e", + "f5:c8:6a:f3:61:62:f1:3a:64:f5:4f:6d:c9:58:7c:06", + "d7:55:8f:da:f5:f1:10:5b:b2:13:28:2b:70:77:29:a3", + "39:2a:43:4f:0e:07:df:1f:8a:a3:05:de:34:e0:c2:29", + "3e:75:ce:d4:6b:69:30:21:21:88:30:ae:86:a8:2a:71", + "e9:02:8b:95:78:e4:15:dc:1a:71:0a:2b:88:15:44:47", + "92:39:d5:34:8f:40:d1:69:5a:74:54:70:e1:f2:3f:43", + "b0:b7:13:3e:d0:96:f9:b5:6f:ae:91:c8:74:bd:3a:c0", + "d8:f3:5f:4e:b7:87:2b:2d:ab:06:92:e3:15:38:2f:b0", + 0 +}; + +bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate) +{ + for (int a = 0; certificate_blacklist[a] != 0; a++) { + if (certificate.serialNumber() == certificate_blacklist[a]) + return true; + } + return false; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QSslCertificate &certificate) +{ + debug << "QSslCertificate(" + << certificate.version() + << ',' << certificate.serialNumber() + << ',' << certificate.digest().toBase64() + << ',' << certificate.issuerInfo(QSslCertificate::Organization) + << ',' << certificate.subjectInfo(QSslCertificate::Organization) + << ',' << certificate.alternateSubjectNames() +#ifndef QT_NO_TEXTSTREAM + << ',' << certificate.effectiveDate() + << ',' << certificate.expiryDate() +#endif + << ')'; + return debug; +} +QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info) +{ + switch (info) { + case QSslCertificate::Organization: debug << "Organization"; break; + case QSslCertificate::CommonName: debug << "CommonName"; break; + case QSslCertificate::CountryName: debug << "CountryName"; break; + case QSslCertificate::LocalityName: debug << "LocalityName"; break; + case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break; + case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break; + } + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h new file mode 100644 index 0000000000..e972ee7239 --- /dev/null +++ b/src/network/ssl/qsslcertificate.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCERTIFICATE_H +#define QSSLCERTIFICATE_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qcryptographichash.h> +#include <QtCore/qregexp.h> +#include <QtCore/qsharedpointer.h> +#include <QtNetwork/qssl.h> + +typedef struct x509_st X509; // ### check if this works + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QDateTime; +class QIODevice; +class QSslKey; +class QStringList; +template <typename T, typename U> class QMultiMap; + +class QSslCertificatePrivate; +class Q_NETWORK_EXPORT QSslCertificate +{ +public: + enum SubjectInfo { + Organization, + CommonName, + LocalityName, + OrganizationalUnitName, + CountryName, + StateOrProvinceName + }; + + QSslCertificate(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); + QSslCertificate( // ### s/encoded/data (to be consistent with signature in .cpp file) ? + const QByteArray &encoded = QByteArray(), QSsl::EncodingFormat format = QSsl::Pem); + QSslCertificate(const QSslCertificate &other); + ~QSslCertificate(); + QSslCertificate &operator=(const QSslCertificate &other); + bool operator==(const QSslCertificate &other) const; + inline bool operator!=(const QSslCertificate &other) const { return !operator==(other); } + + bool isNull() const; + bool isValid() const; + void clear(); + + // Certificate info + QByteArray version() const; + QByteArray serialNumber() const; + QByteArray digest(QCryptographicHash::Algorithm algorithm = QCryptographicHash::Md5) const; + QString issuerInfo(SubjectInfo info) const; + QString issuerInfo(const QByteArray &tag) const; + QString subjectInfo(SubjectInfo info) const; + QString subjectInfo(const QByteArray &tag) const; + QMultiMap<QSsl::AlternateNameEntryType, QString> alternateSubjectNames() const; + QDateTime effectiveDate() const; + QDateTime expiryDate() const; + QSslKey publicKey() const; + + QByteArray toPem() const; + QByteArray toDer() const; + + static QList<QSslCertificate> fromPath( + const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QRegExp::PatternSyntax syntax = QRegExp::FixedString); + static QList<QSslCertificate> fromDevice( + QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); + static QList<QSslCertificate> fromData( + const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); + + Qt::HANDLE handle() const; + +private: + QExplicitlySharedDataPointer<QSslCertificatePrivate> d; + friend class QSslCertificatePrivate; + friend class QSslSocketBackendPrivate; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCertificate &certificate); +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h new file mode 100644 index 0000000000..1ce33d3bfd --- /dev/null +++ b/src/network/ssl/qsslcertificate_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCERTIFICATE_P_H +#define QSSLCERTIFICATE_P_H + +#include "qsslcertificate.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslsocket_p.h" +#include <QtCore/qdatetime.h> +#include <QtCore/qmap.h> + +#include <openssl/x509.h> + +QT_BEGIN_NAMESPACE + +class QSslCertificatePrivate +{ +public: + QSslCertificatePrivate() + : null(true), x509(0) + { + QSslSocketPrivate::ensureInitialized(); + } + + ~QSslCertificatePrivate() + { + if (x509) + q_X509_free(x509); + } + + bool null; + QByteArray versionString; + QByteArray serialNumberString; + + QMap<QString, QString> issuerInfo; + QMap<QString, QString> subjectInfo; + QDateTime notValidAfter; + QDateTime notValidBefore; + + X509 *x509; + + void init(const QByteArray &data, QSsl::EncodingFormat format); + + static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format); + static QSslCertificate QSslCertificate_from_X509(X509 *x509); + static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count = -1); + static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count = -1); + static bool isBlacklisted(const QSslCertificate &certificate); + + friend class QSslSocketBackendPrivate; + + QAtomicInt ref; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp new file mode 100644 index 0000000000..33d4b66a50 --- /dev/null +++ b/src/network/ssl/qsslcipher.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslCipher + \brief The QSslCipher class represents an SSL cryptographic cipher. + \since 4.3 + + \reentrant + \ingroup network + \ingroup ssl + \inmodule QtNetwork + + QSslCipher stores information about one cryptographic cipher. It + is most commonly used with QSslSocket, either for configuring + which ciphers the socket can use, or for displaying the socket's + ciphers to the user. + + \sa QSslSocket, QSslKey +*/ + +#include "qsslcipher.h" +#include "qsslcipher_p.h" +#include "qsslsocket.h" + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + Constructs an empty QSslCipher object. +*/ +QSslCipher::QSslCipher() + : d(new QSslCipherPrivate) +{ +} + +/*! + Constructs a QSslCipher object for the cipher determined by \a + name and \a protocol. The constructor accepts only supported + ciphers (i.e., the \a name and \a protocol must identify a cipher + in the list of ciphers returned by + QSslSocket::supportedCiphers()). + + You can call isNull() after construction to check if \a name and + \a protocol correctly identified a supported cipher. +*/ +QSslCipher::QSslCipher(const QString &name, QSsl::SslProtocol protocol) + : d(new QSslCipherPrivate) +{ + foreach (const QSslCipher &cipher, QSslSocket::supportedCiphers()) { + if (cipher.name() == name && cipher.protocol() == protocol) { + *this = cipher; + return; + } + } +} + +/*! + Constructs an identical copy of the \a other cipher. +*/ +QSslCipher::QSslCipher(const QSslCipher &other) + : d(new QSslCipherPrivate) +{ + *d.data() = *other.d.data(); +} + +/*! + Destroys the QSslCipher object. +*/ +QSslCipher::~QSslCipher() +{ +} + +/*! + Copies the contents of \a other into this cipher, making the two + ciphers identical. +*/ +QSslCipher &QSslCipher::operator=(const QSslCipher &other) +{ + *d.data() = *other.d.data(); + return *this; +} + +/*! + Returns true if this cipher is the same as \a other; otherwise, + false is returned. +*/ +bool QSslCipher::operator==(const QSslCipher &other) const +{ + return d->name == other.d->name && d->protocol == other.d->protocol; +} + +/*! + \fn bool QSslCipher::operator!=(const QSslCipher &other) const + + Returns true if this cipher is not the same as \a other; + otherwise, false is returned. +*/ + +/*! + Returns true if this is a null cipher; otherwise returns false. +*/ +bool QSslCipher::isNull() const +{ + return d->isNull; +} + +/*! + Returns the name of the cipher, or an empty QString if this is a null + cipher. + + \sa isNull() +*/ +QString QSslCipher::name() const +{ + return d->name; +} + +/*! + Returns the number of bits supported by the cipher. + + \sa usedBits() +*/ +int QSslCipher::supportedBits()const +{ + return d->supportedBits; +} + +/*! + Returns the number of bits used by the cipher. + + \sa supportedBits() +*/ +int QSslCipher::usedBits() const +{ + return d->bits; +} + +/*! + Returns the cipher's key exchange method as a QString. +*/ +QString QSslCipher::keyExchangeMethod() const +{ + return d->keyExchangeMethod; +} + +/*! + Returns the cipher's authentication method as a QString. +*/ +QString QSslCipher::authenticationMethod() const +{ + return d->authenticationMethod; +} + +/*! + Returns the cipher's encryption method as a QString. +*/ +QString QSslCipher::encryptionMethod() const +{ + return d->encryptionMethod; +} + +/*! + Returns the cipher's protocol as a QString. + + \sa protocol() +*/ +QString QSslCipher::protocolString() const +{ + return d->protocolString; +} + +/*! + Returns the cipher's protocol type, or \l QSsl::UnknownProtocol if + QSslCipher is unable to determine the protocol (protocolString() may + contain more information). + + \sa protocolString() +*/ +QSsl::SslProtocol QSslCipher::protocol() const +{ + return d->protocol; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QSslCipher &cipher) +{ + debug << "QSslCipher(name=" << qPrintable(cipher.name()) + << ", bits=" << cipher.usedBits() + << ", proto=" << qPrintable(cipher.protocolString()) + << ')'; + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h new file mode 100644 index 0000000000..edaed2c2e8 --- /dev/null +++ b/src/network/ssl/qsslcipher.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCIPHER_H +#define QSSLCIPHER_H + +#include <QtCore/qstring.h> +#include <QtCore/qscopedpointer.h> +#include <QtNetwork/qssl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QSslCipherPrivate; +class Q_NETWORK_EXPORT QSslCipher +{ +public: + QSslCipher(); + QSslCipher(const QString &name, QSsl::SslProtocol protocol); + QSslCipher(const QSslCipher &other); + ~QSslCipher(); + QSslCipher &operator=(const QSslCipher &other); + bool operator==(const QSslCipher &other) const; + inline bool operator!=(const QSslCipher &other) const { return !operator==(other); } + + bool isNull() const; + QString name() const; + int supportedBits() const; + int usedBits() const; + + QString keyExchangeMethod() const; + QString authenticationMethod() const; + QString encryptionMethod() const; + QString protocolString() const; + QSsl::SslProtocol protocol() const; + +private: + QScopedPointer<QSslCipherPrivate> d; + friend class QSslSocketBackendPrivate; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCipher &cipher); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/network/ssl/qsslcipher_p.h b/src/network/ssl/qsslcipher_p.h new file mode 100644 index 0000000000..79fe911280 --- /dev/null +++ b/src/network/ssl/qsslcipher_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslcipher.h" + +QT_BEGIN_NAMESPACE + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +class QSslCipherPrivate +{ +public: + QSslCipherPrivate() + : isNull(true), supportedBits(0), bits(0), + exportable(false), protocol(QSsl::UnknownProtocol) + { + } + + bool isNull; + QString name; + int supportedBits; + int bits; + QString keyExchangeMethod; + QString authenticationMethod; + QString encryptionMethod; + bool exportable; + QString protocolString; + QSsl::SslProtocol protocol; +}; + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp new file mode 100644 index 0000000000..70d7dd8df1 --- /dev/null +++ b/src/network/ssl/qsslconfiguration.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslconfiguration.h" +#include "qsslconfiguration_p.h" +#include "qsslsocket.h" +#include "qmutex.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSslConfiguration + \brief The QSslConfiguration class holds the configuration and state of an SSL connection + \since 4.4 + + \reentrant + \inmodule QtNetwork + \ingroup network + \ingroup ssl + + QSslConfiguration is used by Qt networking classes to relay + information about an open SSL connection and to allow the + application to control certain features of that connection. + + The settings that QSslConfiguration currently supports are: + + \list + \o The SSL/TLS protocol to be used + \o The certificate to be presented to the peer during connection + and its associated private key + \o The ciphers allowed to be used for encrypting the connection + \o The list of Certificate Authorities certificates that are + used to validate the peer's certificate + \endlist + + These settings are applied only during the connection + handshake. Setting them after the connection has been established + has no effect. + + The state that QSslConfiguration supports are: + \list + \o The certificate the peer presented during handshake, along + with the chain leading to a CA certificate + \o The cipher used to encrypt this session + \endlist + + The state can only be obtained once the SSL connection starts, but + not necessarily before it's done. Some settings may change during + the course of the SSL connection without need to restart it (for + instance, the cipher can be changed over time). + + State in QSslConfiguration objects cannot be changed. + + QSslConfiguration can be used with QSslSocket and the Network + Access API. + + Note that changing settings in QSslConfiguration is not enough to + change the settings in the related SSL connection. You must call + setSslConfiguration on a modified QSslConfiguration object to + achieve that. The following example illustrates how to change the + protocol to TLSv1 in a QSslSocket object: + + \snippet doc/src/snippets/code/src_network_ssl_qsslconfiguration.cpp 0 + + \sa QSsl::SslProtocol, QSslCertificate, QSslCipher, QSslKey + QSslSocket, QNetworkAccessManager, + QSslSocket::sslConfiguration(), QSslSocket::setSslConfiguration() +*/ + +/*! + Constructs an empty SSL configuration. This configuration contains + no valid settings and the state will be empty. isNull() will + return true after this constructor is called. + + Once any setter methods are called, isNull() will return false. +*/ +QSslConfiguration::QSslConfiguration() + : d(new QSslConfigurationPrivate) +{ +} + +/*! + Copies the configuration and state of \a other. If \a other is + null, this object will be null too. +*/ +QSslConfiguration::QSslConfiguration(const QSslConfiguration &other) + : d(other.d) +{ +} + +/*! + Releases any resources held by QSslConfiguration. +*/ +QSslConfiguration::~QSslConfiguration() +{ + // QSharedDataPointer deletes d for us if necessary +} + +/*! + Copies the configuration and state of \a other. If \a other is + null, this object will be null too. +*/ +QSslConfiguration &QSslConfiguration::operator=(const QSslConfiguration &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QSslConfiguration object is equal to \a + other. + + Two QSslConfiguration objects are considered equal if they have + the exact same settings and state. + + \sa operator!=() +*/ +bool QSslConfiguration::operator==(const QSslConfiguration &other) const +{ + if (d == other.d) + return true; + return d->peerCertificate == other.d->peerCertificate && + d->peerCertificateChain == other.d->peerCertificateChain && + d->localCertificate == other.d->localCertificate && + d->privateKey == other.d->privateKey && + d->sessionCipher == other.d->sessionCipher && + d->ciphers == other.d->ciphers && + d->caCertificates == other.d->caCertificates && + d->protocol == other.d->protocol && + d->peerVerifyMode == other.d->peerVerifyMode && + d->peerVerifyDepth == other.d->peerVerifyDepth; +} + +/*! + \fn QSslConfiguration::operator!=(const QSslConfiguration &other) const + + Returns true if this QSslConfiguration differs from \a other. Two + QSslConfiguration objects are considered different if any state or + setting is different. + + \sa operator==() +*/ + +/*! + Returns true if this is a null QSslConfiguration object. + + A QSslConfiguration object is null if it has been + default-constructed and no setter methods have been called. + + \sa setProtocol(), setLocalCertificate(), setPrivateKey(), + setCiphers(), setCaCertificates() +*/ +bool QSslConfiguration::isNull() const +{ + 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); +} + +/*! + Returns the protocol setting for this SSL configuration. + + \sa setProtocol() +*/ +QSsl::SslProtocol QSslConfiguration::protocol() const +{ + return d->protocol; +} + +/*! + Sets the protocol setting for this configuration to be \a + protocol. + + Setting the protocol once the connection has already been + established has no effect. + + \sa protocol() +*/ +void QSslConfiguration::setProtocol(QSsl::SslProtocol protocol) +{ + d->protocol = protocol; +} + +/*! + Returns the verify mode. This mode decides whether QSslSocket should + request a certificate from the peer (i.e., the client requests a + certificate from the server, or a server requesting a certificate from the + 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. + + \sa setPeerVerifyMode() +*/ +QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const +{ + return d->peerVerifyMode; +} + +/*! + Sets the verify mode to \a mode. This mode decides whether QSslSocket + should request a certificate from the peer (i.e., the client requests a + certificate from the server, or a server requesting a certificate from the + 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. + + \sa peerVerifyMode() +*/ +void QSslConfiguration::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode) +{ + d->peerVerifyMode = mode; +} + + +/*! + Returns the maximum number of certificates in the peer's certificate chain + to be checked during the SSL handshake phase, or 0 (the default) if no + maximum depth has been set, indicating that the whole certificate chain + should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa setPeerVerifyDepth(), peerVerifyMode() +*/ +int QSslConfiguration::peerVerifyDepth() const +{ + return d->peerVerifyDepth; +} + +/*! + Sets the maximum number of certificates in the peer's certificate chain to + be checked during the SSL handshake phase, to \a depth. Setting a depth of + 0 means that no maximum depth is set, indicating that the whole + certificate chain should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa peerVerifyDepth(), setPeerVerifyMode() +*/ +void QSslConfiguration::setPeerVerifyDepth(int depth) +{ + if (depth < 0) { + qWarning("QSslConfiguration::setPeerVerifyDepth: cannot set negative depth of %d", depth); + return; + } + d->peerVerifyDepth = depth; +} + +/*! + Returns the certificate to be presented to the peer during the SSL + handshake process. + + \sa setLocalCertificate() +*/ +QSslCertificate QSslConfiguration::localCertificate() const +{ + return d->localCertificate; +} + +/*! + Sets the certificate to be presented to the peer during SSL + handshake to be \a certificate. + + Setting the certificate once the connection has been established + has no effect. + + A certificate is the means of identification used in the SSL + process. The local certificate is used by the remote end to verify + the local user's identity against its list of Certification + Authorities. In most cases, such as in HTTP web browsing, only + servers identify to the clients, so the client does not send a + certificate. + + \sa localCertificate() +*/ +void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate) +{ + d->localCertificate = certificate; +} + +/*! + Returns the peer's digital certificate (i.e., the immediate + certificate of the host you are connected to), or a null + certificate, if the peer has not assigned a certificate. + + The peer certificate is checked automatically during the + handshake phase, so this function is normally used to fetch + the certificate for display or for connection diagnostic + purposes. It contains information about the peer, including + its host name, the certificate issuer, and the peer's public + key. + + Because the peer certificate is set during the handshake phase, it + is safe to access the peer certificate from a slot connected to + the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors() + signal, or the QSslSocket::encrypted() signal. + + If a null certificate is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to check the peer's complete chain of certificates, + use peerCertificateChain() to get them all at once. + + \sa peerCertificateChain(), + QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(), + QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors() +*/ +QSslCertificate QSslConfiguration::peerCertificate() const +{ + return d->peerCertificate; +} + +/*! + Returns the peer's chain of digital certificates, starting with + the peer's immediate certificate and ending with the CA's + certificate. + + Peer certificates are checked automatically during the handshake + phase. This function is normally used to fetch certificates for + display, or for performing connection diagnostics. Certificates + contain information about the peer and the certificate issuers, + including host name, issuer names, and issuer public keys. + + Because the peer certificate is set during the handshake phase, it + is safe to access the peer certificate from a slot connected to + the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors() + signal, or the QSslSocket::encrypted() signal. + + If an empty list is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to get only the peer's immediate certificate, use + peerCertificate(). + + \sa peerCertificate(), + QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(), + QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors() +*/ +QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const +{ + return d->peerCertificateChain; +} + +/*! + Returns the socket's cryptographic \l {QSslCipher} {cipher}, or a + null cipher if the connection isn't encrypted. The socket's cipher + for the session is set during the handshake phase. The cipher is + used to encrypt and decrypt data transmitted through the socket. + + The SSL infrastructure also provides functions for setting the + ordered list of ciphers from which the handshake phase will + eventually select the session cipher. This ordered list must be in + place before the handshake phase begins. + + \sa ciphers(), setCiphers(), QSslSocket::supportedCiphers() +*/ +QSslCipher QSslConfiguration::sessionCipher() const +{ + return d->sessionCipher; +} + +/*! + Returns the \l {QSslKey} {SSL key} assigned to this connection or + a null key if none has been assigned yet. + + \sa setPrivateKey(), localCertificate() +*/ +QSslKey QSslConfiguration::privateKey() const +{ + return d->privateKey; +} + +/*! + Sets the connection's private \l {QSslKey} {key} to \a key. The + private key and the local \l {QSslCertificate} {certificate} are + used by clients and servers that must prove their identity to + SSL peers. + + Both the key and the local certificate are required if you are + creating an SSL server socket. If you are creating an SSL client + socket, the key and local certificate are required if your client + must identify itself to an SSL server. + + \sa privateKey(), setLocalCertificate() +*/ +void QSslConfiguration::setPrivateKey(const QSslKey &key) +{ + d->privateKey = key; +} + +/*! + Returns this connection's current cryptographic cipher suite. This + list is used during the handshake phase for choosing a + session cipher. The returned list of ciphers is ordered by + descending preference. (i.e., the first cipher in the list is the + most preferred cipher). The session cipher will be the first one + in the list that is also supported by the peer. + + By default, the handshake phase can choose any of the ciphers + supported by this system's SSL libraries, which may vary from + system to system. The list of ciphers supported by this system's + SSL libraries is returned by QSslSocket::supportedCiphers(). You can restrict + the list of ciphers used for choosing the session cipher for this + socket by calling setCiphers() with a subset of the supported + ciphers. You can revert to using the entire set by calling + setCiphers() with the list returned by QSslSocket::supportedCiphers(). + + \sa setCiphers(), QSslSocket::supportedCiphers() +*/ +QList<QSslCipher> QSslConfiguration::ciphers() const +{ + return d->ciphers; +} + +/*! + Sets the cryptographic cipher suite for this socket to \a ciphers, + which must contain a subset of the ciphers in the list returned by + supportedCiphers(). + + Restricting the cipher suite must be done before the handshake + phase, where the session cipher is chosen. + + \sa ciphers(), QSslSocket::supportedCiphers() +*/ +void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers) +{ + d->ciphers = ciphers; +} + +/*! + Returns this connection's CA certificate database. The CA certificate + database is used by the socket during the handshake phase to + validate the peer's certificate. It can be modified prior to the + handshake with setCaCertificates(), or with \l{QSslSocket}'s + \l{QSslSocket::}{addCaCertificate()} and + \l{QSslSocket::}{addCaCertificates()}. + + \sa setCaCertificates() +*/ +QList<QSslCertificate> QSslConfiguration::caCertificates() const +{ + return d->caCertificates; +} + +/*! + Sets this socket's CA certificate database to be \a certificates. + The certificate database must be set prior to the SSL handshake. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + \sa caCertificates() +*/ +void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certificates) +{ + d->caCertificates = certificates; +} + +/*! + Returns the default SSL configuration to be used in new SSL + connections. + + The default SSL configuration consists of: + + \list + \o no local certificate and no private key + \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 + \endlist + + \sa QSslSocket::supportedCiphers(), setDefaultConfiguration() +*/ +QSslConfiguration QSslConfiguration::defaultConfiguration() +{ + return QSslConfigurationPrivate::defaultConfiguration(); +} + +/*! + Sets the default SSL configuration to be used in new SSL + connections to be \a configuration. Existing connections are not + affected by this call. + + \sa QSslSocket::supportedCiphers(), defaultConfiguration() +*/ +void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configuration) +{ + QSslConfigurationPrivate::setDefaultConfiguration(configuration); +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h new file mode 100644 index 0000000000..143566bef1 --- /dev/null +++ b/src/network/ssl/qsslconfiguration.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, Nokia 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. +** +****************************************************************************/ + +#ifndef QSSLCONFIGURATION_H +#define QSSLCONFIGURATION_H + +#include <QtCore/qshareddata.h> +#include <QtNetwork/qsslsocket.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +template<typename T> class QList; +class QSslCertificate; +class QSslCipher; +class QSslKey; + +class QSslConfigurationPrivate; +class Q_NETWORK_EXPORT QSslConfiguration +{ +public: + QSslConfiguration(); + QSslConfiguration(const QSslConfiguration &other); + ~QSslConfiguration(); + QSslConfiguration &operator=(const QSslConfiguration &other); + + bool operator==(const QSslConfiguration &other) const; + inline bool operator!=(const QSslConfiguration &other) const + { return !(*this == other); } + + bool isNull() const; // ### Qt 5: remove; who would need this? + + QSsl::SslProtocol protocol() const; + void setProtocol(QSsl::SslProtocol protocol); + + // Verification + QSslSocket::PeerVerifyMode peerVerifyMode() const; + void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode); + + int peerVerifyDepth() const; + void setPeerVerifyDepth(int depth); + + // Certificate & cipher configuration + QSslCertificate localCertificate() const; + void setLocalCertificate(const QSslCertificate &certificate); + + QSslCertificate peerCertificate() const; + QList<QSslCertificate> peerCertificateChain() const; + QSslCipher sessionCipher() const; + + // Private keys, for server sockets + QSslKey privateKey() const; + void setPrivateKey(const QSslKey &key); + + // Cipher settings + QList<QSslCipher> ciphers() const; + void setCiphers(const QList<QSslCipher> &ciphers); + + // Certificate Authority (CA) settings + QList<QSslCertificate> caCertificates() const; + void setCaCertificates(const QList<QSslCertificate> &certificates); + + static QSslConfiguration defaultConfiguration(); + static void setDefaultConfiguration(const QSslConfiguration &configuration); + +private: + friend class QSslSocket; + friend class QSslConfigurationPrivate; + QSslConfiguration(QSslConfigurationPrivate *dd); + QSharedDataPointer<QSslConfigurationPrivate> d; +}; + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h new file mode 100644 index 0000000000..a5af51a8db --- /dev/null +++ b/src/network/ssl/qsslconfiguration_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, Nokia 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. +** +****************************************************************************/ + +#ifndef QSSLCONFIGURATION_P_H +#define QSSLCONFIGURATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QSslSocket API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslconfiguration.h" +#include "qlist.h" +#include "qsslcertificate.h" +#include "qsslcipher.h" +#include "qsslkey.h" + +QT_BEGIN_NAMESPACE + +class QSslConfigurationPrivate: public QSharedData +{ +public: + QSslConfigurationPrivate() + : protocol(QSsl::SecureProtocols), + peerVerifyMode(QSslSocket::AutoVerifyPeer), + peerVerifyDepth(0) + { } + + QSslCertificate peerCertificate; + QList<QSslCertificate> peerCertificateChain; + QSslCertificate localCertificate; + + QSslKey privateKey; + QSslCipher sessionCipher; + QList<QSslCipher> ciphers; + QList<QSslCertificate> caCertificates; + + QSsl::SslProtocol protocol; + QSslSocket::PeerVerifyMode peerVerifyMode; + int peerVerifyDepth; + + // in qsslsocket.cpp: + static QSslConfiguration defaultConfiguration(); + static void setDefaultConfiguration(const QSslConfiguration &configuration); + static void deepCopyDefaultConfiguration(QSslConfigurationPrivate *config); +}; + +// implemented here for inlining purposes +inline QSslConfiguration::QSslConfiguration(QSslConfigurationPrivate *dd) + : d(dd) +{ +} + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp new file mode 100644 index 0000000000..ae18b47170 --- /dev/null +++ b/src/network/ssl/qsslerror.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslError + \brief The QSslError class provides an SSL error. + \since 4.3 + + \reentrant + \ingroup network + \ingroup ssl + \inmodule QtNetwork + + QSslError provides a simple API for managing errors during QSslSocket's + SSL handshake. + + \sa QSslSocket, QSslCertificate, QSslCipher +*/ + +/*! + \enum QSslError::SslError + + Describes all recognized errors that can occur during an SSL handshake. + + \value NoError + \value UnableToGetIssuerCertificate + \value UnableToDecryptCertificateSignature + \value UnableToDecodeIssuerPublicKey + \value CertificateSignatureFailed + \value CertificateNotYetValid + \value CertificateExpired + \value InvalidNotBeforeField + \value InvalidNotAfterField + \value SelfSignedCertificate + \value SelfSignedCertificateInChain + \value UnableToGetLocalIssuerCertificate + \value UnableToVerifyFirstCertificate + \value CertificateRevoked + \value InvalidCaCertificate + \value PathLengthExceeded + \value InvalidPurpose + \value CertificateUntrusted + \value CertificateRejected + \value SubjectIssuerMismatch + \value AuthorityIssuerSerialNumberMismatch + \value NoPeerCertificate + \value HostNameMismatch + \value UnspecifiedError + \value NoSslSupport + \value CertificateBlacklisted + + \sa QSslError::errorString() +*/ + +#include "qsslerror.h" +#include "qsslsocket.h" +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +#endif + +class QSslErrorPrivate +{ +public: + QSslError::SslError error; + QSslCertificate certificate; +}; + +/*! + Constructs a QSslError object with no error and default certificate. + +*/ + +// RVCT compiler in debug build does not like about default values in const- +// So as an workaround we define all constructor overloads here explicitly +QSslError::QSslError() + : d(new QSslErrorPrivate) +{ + d->error = QSslError::NoError; + d->certificate = QSslCertificate(); +} + +/*! + Constructs a QSslError object. The argument specifies the \a + error that occurred. + +*/ +QSslError::QSslError(SslError error) + : d(new QSslErrorPrivate) +{ + d->error = error; + d->certificate = QSslCertificate(); +} + +/*! + Constructs a QSslError object. The two arguments specify the \a + error that occurred, and which \a certificate the error relates to. + + \sa QSslCertificate +*/ +QSslError::QSslError(SslError error, const QSslCertificate &certificate) + : d(new QSslErrorPrivate) +{ + d->error = error; + d->certificate = certificate; +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslError::QSslError(const QSslError &other) + : d(new QSslErrorPrivate) +{ + *d.data() = *other.d.data(); +} + +/*! + Destroys the QSslError object. +*/ +QSslError::~QSslError() +{ +} + +/*! + \since 4.4 + + Assigns the contents of \a other to this error. +*/ +QSslError &QSslError::operator=(const QSslError &other) +{ + *d.data() = *other.d.data(); + return *this; +} + +/*! + \since 4.4 + + Returns true if this error is equal to \a other; otherwise returns false. +*/ +bool QSslError::operator==(const QSslError &other) const +{ + return d->error == other.d->error + && d->certificate == other.d->certificate; +} + +/*! + \fn bool QSslError::operator!=(const QSslError &other) const + \since 4.4 + + Returns true if this error is not equal to \a other; otherwise returns + false. +*/ + +/*! + Returns the type of the error. + + \sa errorString(), certificate() +*/ +QSslError::SslError QSslError::error() const +{ + return d->error; +} + +/*! + Returns a short localized human-readable description of the error. + + \sa error(), certificate() +*/ +QString QSslError::errorString() const +{ + QString errStr; + switch (d->error) { + case NoError: + errStr = QSslSocket::tr("No error"); + break; + case UnableToGetIssuerCertificate: + errStr = QSslSocket::tr("The issuer certificate could not be found"); + break; + case UnableToDecryptCertificateSignature: + errStr = QSslSocket::tr("The certificate signature could not be decrypted"); + break; + case UnableToDecodeIssuerPublicKey: + errStr = QSslSocket::tr("The public key in the certificate could not be read"); + break; + case CertificateSignatureFailed: + errStr = QSslSocket::tr("The signature of the certificate is invalid"); + break; + case CertificateNotYetValid: + errStr = QSslSocket::tr("The certificate is not yet valid"); + break; + case CertificateExpired: + errStr = QSslSocket::tr("The certificate has expired"); + break; + case InvalidNotBeforeField: + errStr = QSslSocket::tr("The certificate's notBefore field contains an invalid time"); + break; + case InvalidNotAfterField: + errStr = QSslSocket::tr("The certificate's notAfter field contains an invalid time"); + break; + case SelfSignedCertificate: + errStr = QSslSocket::tr("The certificate is self-signed, and untrusted"); + break; + case SelfSignedCertificateInChain: + errStr = QSslSocket::tr("The root certificate of the certificate chain is self-signed, and untrusted"); + break; + case UnableToGetLocalIssuerCertificate: + errStr = QSslSocket::tr("The issuer certificate of a locally looked up certificate could not be found"); + break; + case UnableToVerifyFirstCertificate: + errStr = QSslSocket::tr("No certificates could be verified"); + break; + case InvalidCaCertificate: + errStr = QSslSocket::tr("One of the CA certificates is invalid"); + break; + case PathLengthExceeded: + errStr = QSslSocket::tr("The basicConstraints path length parameter has been exceeded"); + break; + case InvalidPurpose: + errStr = QSslSocket::tr("The supplied certificate is unsuitable for this purpose"); + break; + case CertificateUntrusted: + errStr = QSslSocket::tr("The root CA certificate is not trusted for this purpose"); + break; + case CertificateRejected: + errStr = QSslSocket::tr("The root CA certificate is marked to reject the specified purpose"); + break; + case SubjectIssuerMismatch: // hostname mismatch + errStr = QSslSocket::tr("The current candidate issuer certificate was rejected because its" + " subject name did not match the issuer name of the current certificate"); + break; + case AuthorityIssuerSerialNumberMismatch: + errStr = QSslSocket::tr("The current candidate issuer certificate was rejected because" + " its issuer name and serial number was present and did not match the" + " authority key identifier of the current certificate"); + break; + case NoPeerCertificate: + errStr = QSslSocket::tr("The peer did not present any certificate"); + break; + case HostNameMismatch: + errStr = QSslSocket::tr("The host name did not match any of the valid hosts" + " for this certificate"); + break; + case NoSslSupport: + break; + case CertificateBlacklisted: + errStr = QSslSocket::tr("The peer certificate is blacklisted"); + break; + default: + errStr = QSslSocket::tr("Unknown error"); + break; + } + + return errStr; +} + +/*! + Returns the certificate associated with this error, or a null certificate + if the error does not relate to any certificate. + + \sa error(), errorString() +*/ +QSslCertificate QSslError::certificate() const +{ + return d->certificate; +} + +#ifndef QT_NO_DEBUG_STREAM +//class QDebug; +QDebug operator<<(QDebug debug, const QSslError &error) +{ + debug << error.errorString(); + return debug; +} +QDebug operator<<(QDebug debug, const QSslError::SslError &error) +{ + debug << QSslError(error).errorString(); + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h new file mode 100644 index 0000000000..c30c02a8af --- /dev/null +++ b/src/network/ssl/qsslerror.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLERROR_H +#define QSSLERROR_H + +#include <QtCore/qvariant.h> +#include <QtNetwork/qsslcertificate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QSslErrorPrivate; +class Q_NETWORK_EXPORT QSslError +{ +public: + enum SslError { + NoError, + UnableToGetIssuerCertificate, + UnableToDecryptCertificateSignature, + UnableToDecodeIssuerPublicKey, + CertificateSignatureFailed, + CertificateNotYetValid, + CertificateExpired, + InvalidNotBeforeField, + InvalidNotAfterField, + SelfSignedCertificate, + SelfSignedCertificateInChain, + UnableToGetLocalIssuerCertificate, + UnableToVerifyFirstCertificate, + CertificateRevoked, + InvalidCaCertificate, + PathLengthExceeded, + InvalidPurpose, + CertificateUntrusted, + CertificateRejected, + SubjectIssuerMismatch, // hostname mismatch? + AuthorityIssuerSerialNumberMismatch, + NoPeerCertificate, + HostNameMismatch, + NoSslSupport, + CertificateBlacklisted, + UnspecifiedError = -1 + }; + + // RVCT compiler in debug build does not like about default values in const- + // So as an workaround we define all constructor overloads here explicitly + QSslError(); + QSslError(SslError error); + QSslError(SslError error, const QSslCertificate &certificate); + + QSslError(const QSslError &other); + + ~QSslError(); + QSslError &operator=(const QSslError &other); + bool operator==(const QSslError &other) const; + inline bool operator!=(const QSslError &other) const + { return !(*this == other); } + + SslError error() const; + QString errorString() const; + QSslCertificate certificate() const; + +private: + QScopedPointer<QSslErrorPrivate> d; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError &error); +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError::SslError &error); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslkey.cpp b/src/network/ssl/qsslkey.cpp new file mode 100644 index 0000000000..8b32f65405 --- /dev/null +++ b/src/network/ssl/qsslkey.cpp @@ -0,0 +1,460 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslKey + \brief The QSslKey class provides an interface for private and public keys. + \since 4.3 + + \reentrant + \ingroup network + \ingroup ssl + \inmodule QtNetwork + + QSslKey provides a simple API for managing keys. + + \sa QSslSocket, QSslCertificate, QSslCipher +*/ + +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslkey.h" +#include "qsslkey_p.h" +#include "qsslsocket.h" +#include "qsslsocket_p.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qiodevice.h> +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +#endif + + +/*! + \internal + */ +void QSslKeyPrivate::clear(bool deep) +{ + isNull = true; + if (!QSslSocket::supportsSsl()) + return; + if (rsa) { + if (deep) + q_RSA_free(rsa); + rsa = 0; + } + if (dsa) { + if (deep) + q_DSA_free(dsa); + dsa = 0; + } +} + +/*! + \internal + + Allocates a new rsa or dsa struct and decodes \a pem into it + according to the current algorithm and type. + + If \a deepClear is true, the rsa/dsa struct is freed if it is was + already allocated, otherwise we "leak" memory (which is exactly + what we want for copy construction). + + If \a passPhrase is non-empty, it will be used for decrypting + \a pem. +*/ +void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, + bool deepClear) +{ + if (pem.isEmpty()) + return; + + clear(deepClear); + + if (!QSslSocket::supportsSsl()) + return; + + BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size()); + if (!bio) + return; + + void *phrase = (void *)passPhrase.constData(); + + if (algorithm == QSsl::Rsa) { + RSA *result = (type == QSsl::PublicKey) + ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase) + : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase); + if (rsa && rsa == result) + isNull = false; + } else { + DSA *result = (type == QSsl::PublicKey) + ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase) + : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase); + if (dsa && dsa == result) + isNull = false; + } + + q_BIO_free(bio); +} + +/*! + Constructs a null key. + + \sa isNull() +*/ +QSslKey::QSslKey() + : d(new QSslKeyPrivate) +{ +} + +/*! + \internal +*/ +QByteArray QSslKeyPrivate::pemHeader() const +{ + // ### use QByteArray::fromRawData() instead + if (type == QSsl::PublicKey) + return QByteArray("-----BEGIN PUBLIC KEY-----\n"); + else if (algorithm == QSsl::Rsa) + return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n"); + return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n"); +} + +/*! + \internal +*/ +QByteArray QSslKeyPrivate::pemFooter() const +{ + // ### use QByteArray::fromRawData() instead + if (type == QSsl::PublicKey) + return QByteArray("-----END PUBLIC KEY-----\n"); + else if (algorithm == QSsl::Rsa) + return QByteArray("-----END RSA PRIVATE KEY-----\n"); + return QByteArray("-----END DSA PRIVATE KEY-----\n"); +} + +/*! + \internal + + Returns a DER key formatted as PEM. +*/ +QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const +{ + QByteArray pem(der.toBase64()); + + const int lineWidth = 64; // RFC 1421 + const int newLines = pem.size() / lineWidth; + const bool rem = pem.size() % lineWidth; + + // ### optimize + for (int i = 0; i < newLines; ++i) + pem.insert((i + 1) * lineWidth + i, '\n'); + if (rem) + pem.append('\n'); // ### + + pem.prepend(pemHeader()); + pem.append(pemFooter()); + + return pem; +} + +/*! + \internal + + Returns a PEM key formatted as DER. +*/ +QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const +{ + const QByteArray header = pemHeader(); + const QByteArray footer = pemFooter(); + + QByteArray der(pem); + + const int headerIndex = der.indexOf(header); + const int footerIndex = der.indexOf(footer); + if (headerIndex == -1 || footerIndex == -1) + return QByteArray(); + + der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size())); + + return QByteArray::fromBase64(der); // ignores newlines +} + +/*! + Constructs a QSslKey by decoding the string in the byte array + \a encoded using a specified \a algorithm and \a encoding format. + If the encoded key is encrypted, \a passPhrase is used to decrypt + it. \a type specifies whether the key is public or private. + + After construction, use isNull() to check if \a encoded contained + a valid key. +*/ +QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase) + : d(new QSslKeyPrivate) +{ + d->type = type; + d->algorithm = algorithm; + d->decodePem((encoding == QSsl::Der) + ? d->pemFromDer(encoded) : encoded, + passPhrase); +} + +/*! + Constructs a QSslKey by reading and decoding data from a + \a device using a specified \a algorithm and \a encoding format. + If the encoded key is encrypted, \a passPhrase is used to decrypt + it. \a type specifies whether the key is public or private. + + After construction, use isNull() to check if \a device provided + a valid key. +*/ +QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding, + QSsl::KeyType type, const QByteArray &passPhrase) + : d(new QSslKeyPrivate) +{ + QByteArray encoded; + if (device) + encoded = device->readAll(); + d->type = type; + d->algorithm = algorithm; + d->decodePem((encoding == QSsl::Der) ? + d->pemFromDer(encoded) : encoded, + passPhrase); +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslKey::QSslKey(const QSslKey &other) : d(other.d) +{ +} + +/*! + Destroys the QSslKey object. +*/ +QSslKey::~QSslKey() +{ +} + +/*! + Copies the contents of \a other into this key, making the two keys + identical. + + Returns a reference to this QSslKey. +*/ +QSslKey &QSslKey::operator=(const QSslKey &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this is a null key; otherwise false. + + \sa clear() +*/ +bool QSslKey::isNull() const +{ + return d->isNull; +} + +/*! + Clears the contents of this key, making it a null key. + + \sa isNull() +*/ +void QSslKey::clear() +{ + d = new QSslKeyPrivate; +} + +/*! + Returns the length of the key in bits, or -1 if the key is null. +*/ +int QSslKey::length() const +{ + if (d->isNull) + return -1; + return (d->algorithm == QSsl::Rsa) + ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p); +} + +/*! + Returns the type of the key (i.e., PublicKey or PrivateKey). +*/ +QSsl::KeyType QSslKey::type() const +{ + return d->type; +} + +/*! + Returns the key algorithm. +*/ +QSsl::KeyAlgorithm QSslKey::algorithm() const +{ + return d->algorithm; +} + +/*! + Returns the key in DER encoding. The result is encrypted with + \a passPhrase if the key is a private key and \a passPhrase is + non-empty. +*/ +// ### autotest failure for non-empty passPhrase and private key +QByteArray QSslKey::toDer(const QByteArray &passPhrase) const +{ + if (d->isNull) + return QByteArray(); + return d->derFromPem(toPem(passPhrase)); +} + +/*! + Returns the key in PEM encoding. The result is encrypted with + \a passPhrase if the key is a private key and \a passPhrase is + non-empty. +*/ +QByteArray QSslKey::toPem(const QByteArray &passPhrase) const +{ + if (!QSslSocket::supportsSsl() || d->isNull) + return QByteArray(); + + BIO *bio = q_BIO_new(q_BIO_s_mem()); + if (!bio) + return QByteArray(); + + bool fail = false; + + if (d->algorithm == QSsl::Rsa) { + if (d->type == QSsl::PublicKey) { + if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa)) + fail = true; + } else { + if (!q_PEM_write_bio_RSAPrivateKey( + bio, d->rsa, + // ### the cipher should be selectable in the API: + passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(), + (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) { + fail = true; + } + } + } else { + if (d->type == QSsl::PublicKey) { + if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa)) + fail = true; + } else { + if (!q_PEM_write_bio_DSAPrivateKey( + bio, d->dsa, + // ### the cipher should be selectable in the API: + passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(), + (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) { + fail = true; + } + } + } + + QByteArray pem; + if (!fail) { + char *data; + long size = q_BIO_get_mem_data(bio, &data); + pem = QByteArray(data, size); + } + q_BIO_free(bio); + return pem; +} + +/*! + Returns a pointer to the native key handle, if it is available; + otherwise a null pointer is returned. + + You can use this handle together with the native API to access + extended information about the key. + + \warning Use of this function has a high probability of being + non-portable, and its return value may vary across platforms, and + between minor Qt releases. +*/ +Qt::HANDLE QSslKey::handle() const +{ + return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa); +} + +/*! + Returns true if this key is equal to \a other; otherwise returns false. +*/ +bool QSslKey::operator==(const QSslKey &other) const +{ + if (isNull()) + return other.isNull(); + if (other.isNull()) + return isNull(); + if (algorithm() != other.algorithm()) + return false; + if (type() != other.type()) + return false; + if (length() != other.length()) + return false; + return toDer() == other.toDer(); +} + +/*! \fn bool QSslKey::operator!=(const QSslKey &other) const + + Returns true if this key is not equal to key \a other; otherwise + returns false. +*/ + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +QDebug operator<<(QDebug debug, const QSslKey &key) +{ + debug << "QSslKey(" + << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey") + << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA") + << ", " << key.length() + << ')'; + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h new file mode 100644 index 0000000000..89973042f9 --- /dev/null +++ b/src/network/ssl/qsslkey.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLKEY_H +#define QSSLKEY_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qsharedpointer.h> +#include <QtNetwork/qssl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +template <typename A, typename B> struct QPair; + +class QIODevice; + +class QSslKeyPrivate; +class Q_NETWORK_EXPORT QSslKey +{ +public: + QSslKey(); + QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat format = QSsl::Pem, + QSsl::KeyType type = QSsl::PrivateKey, + const QByteArray &passPhrase = QByteArray()); + QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat format = QSsl::Pem, + QSsl::KeyType type = QSsl::PrivateKey, + const QByteArray &passPhrase = QByteArray()); + QSslKey(const QSslKey &other); + ~QSslKey(); + QSslKey &operator=(const QSslKey &other); + + bool isNull() const; + void clear(); + + int length() const; + QSsl::KeyType type() const; + QSsl::KeyAlgorithm algorithm() const; + + QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const; + QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const; + + Qt::HANDLE handle() const; + + bool operator==(const QSslKey &key) const; + inline bool operator!=(const QSslKey &key) const { return !operator==(key); } + +private: + QExplicitlySharedDataPointer<QSslKeyPrivate> d; + friend class QSslCertificate; +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslKey &key); +#endif + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h new file mode 100644 index 0000000000..e476ecea8c --- /dev/null +++ b/src/network/ssl/qsslkey_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLKEY_P_H +#define QSSLKEY_P_H + +#include "qsslkey.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qsslcertificate.cpp. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <openssl/rsa.h> +#include <openssl/dsa.h> + +QT_BEGIN_NAMESPACE + +class QSslKeyPrivate +{ +public: + inline QSslKeyPrivate() + : rsa(0) + , dsa(0) + { + clear(); + } + + inline ~QSslKeyPrivate() + { clear(); } + + void clear(bool deep = true); + + void decodePem(const QByteArray &pem, const QByteArray &passPhrase, + bool deepClear = true); + QByteArray pemHeader() const; + QByteArray pemFooter() const; + QByteArray pemFromDer(const QByteArray &der) const; + QByteArray derFromPem(const QByteArray &pem) const; + + bool isNull; + QSsl::KeyType type; + QSsl::KeyAlgorithm algorithm; + RSA *rsa; + DSA *dsa; + + QAtomicInt ref; + +private: + Q_DISABLE_COPY(QSslKeyPrivate) +}; + +QT_END_NAMESPACE + +#endif // QSSLKEY_P_H diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp new file mode 100644 index 0000000000..0dbf4b5196 --- /dev/null +++ b/src/network/ssl/qsslsocket.cpp @@ -0,0 +1,2260 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +//#define QSSLSOCKET_DEBUG + +/*! + \class QSslSocket + \brief The QSslSocket class provides an SSL encrypted socket for both + clients and servers. + \since 4.3 + + \reentrant + \ingroup network + \ingroup ssl + \inmodule QtNetwork + + 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 TLSv1, but you can + change the SSL protocol by calling setProtocol() as long as you do + it before the handshake has started. + + SSL encryption operates on top of the existing TCP stream after + the socket enters the ConnectedState. There are two simple ways to + establish a secure connection using QSslSocket: With an immediate + SSL handshake, or with a delayed SSL handshake occurring after the + connection has been established in unencrypted mode. + + The most common way to use QSslSocket is to construct an object + and start a secure connection by calling connectToHostEncrypted(). + This method starts an immediate SSL handshake once the connection + has been established. + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 0 + + As with a plain QTcpSocket, QSslSocket enters the HostLookupState, + ConnectingState, and finally the ConnectedState, if the connection + is successful. The handshake then starts automatically, and if it + succeeds, the encrypted() signal is emitted to indicate the socket + has entered the encrypted state and is ready for use. + + Note that data can be written to the socket immediately after the + return from connectToHostEncrypted() (i.e., before the encrypted() + signal is emitted). The data is queued in QSslSocket until after + the encrypted() signal is emitted. + + An example of using the delayed SSL handshake to secure an + existing connection is the case where an SSL server secures an + incoming connection. Suppose you create an SSL server class as a + subclass of QTcpServer. You would override + QTcpServer::incomingConnection() with something like the example + below, which first constructs an instance of QSslSocket and then + calls setSocketDescriptor() to set the new socket's descriptor to + the existing one passed in. It then initiates the SSL handshake + by calling startServerEncryption(). + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 1 + + If an error occurs, QSslSocket emits the sslErrors() signal. In this + case, if no action is taken to ignore the error(s), the connection + is dropped. To continue, despite the occurrence of an error, you + can call ignoreSslErrors(), either from within this slot after the + error occurs, or any time after construction of the QSslSocket and + before the connection is attempted. This will allow QSslSocket to + ignore the errors it encounters when establishing the identity of + the peer. Ignoring errors during an SSL handshake should be used + with caution, since a fundamental characteristic of secure + connections is that they should be established with a successful + handshake. + + Once encrypted, you use QSslSocket as a regular QTcpSocket. When + readyRead() is emitted, you can call read(), canReadLine() and + readLine(), or getChar() to read decrypted data from QSslSocket's + internal buffer, and you can call write() or putChar() to write + data back to the peer. QSslSocket will automatically encrypt the + written data for you, and emit encryptedBytesWritten() once + the data has been written to the peer. + + As a convenience, QSslSocket supports QTcpSocket's blocking + functions waitForConnected(), waitForReadyRead(), + waitForBytesWritten(), and waitForDisconnected(). It also provides + waitForEncrypted(), which will block the calling thread until an + encrypted connection has been established. + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 2 + + QSslSocket provides an extensive, easy-to-use API for handling + cryptographic ciphers, private keys, and local, peer, and + Certification Authority (CA) certificates. It also provides an API + for handling errors that occur during the handshake phase. + + The following features can also be customized: + + \list + \o The socket's cryptographic cipher suite can be customized before + the handshake phase with setCiphers() and setDefaultCiphers(). + \o The socket's local certificate and private key can be customized + before the handshake phase with setLocalCertificate() and + setPrivateKey(). + \o The CA certificate database can be extended and customized with + addCaCertificate(), addCaCertificates(), setCaCertificates(), + addDefaultCaCertificate(), addDefaultCaCertificates(), and + 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. + + This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (\l{http://www.openssl.org/}). + + \note Be aware of the difference between the bytesWritten() signal and + the encryptedBytesWritten() signal. For a QTcpSocket, bytesWritten() + will get emitted as soon as data has been written to the TCP socket. + For a QSslSocket, bytesWritten() will get emitted when the data + is being encrypted and encryptedBytesWritten() + will get emitted as soon as data has been written to the TCP socket. + + \section1 Symbian Platform Security Requirements + + On Symbian, processes which use this class must have the + \c NetworkServices platform security capability. If the client + process lacks this capability, operations will fail. + + Platform security capabilities are added via the + \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY} + qmake variable. + + \sa QSslCertificate, QSslCipher, QSslError +*/ + +/*! + \enum QSslSocket::SslMode + + Describes the connection modes available for QSslSocket. + + \value UnencryptedMode The socket is unencrypted. Its + behavior is identical to QTcpSocket. + + \value SslClientMode The socket is a client-side SSL socket. + It is either alreayd encrypted, or it is in the SSL handshake + phase (see QSslSocket::isEncrypted()). + + \value SslServerMode The socket is a server-side SSL socket. + It is either already encrypted, or it is in the SSL handshake + phase (see QSslSocket::isEncrypted()). +*/ + +/*! + \enum QSslSocket::PeerVerifyMode + \since 4.4 + + Describes the peer verification modes for QSslSocket. The default mode is + AutoVerifyPeer, which selects an appropriate mode depending on the + socket's QSocket::SslMode. + + \value VerifyNone QSslSocket will not request a certificate from the + peer. You can set this mode if you are not interested in the identity of + the other side of the connection. The connection will still be encrypted, + and your socket will still send its local certificate to the peer if it's + requested. + + \value QueryPeer QSslSocket will request a certificate from the peer, but + does not require this certificate to be valid. This is useful when you + want to display peer certificate details to the user without affecting the + actual SSL handshake. This mode is the default for servers. + + \value VerifyPeer QSslSocket will request a certificate from the peer + during the SSL handshake phase, and requires that this certificate is + valid. On failure, QSslSocket will emit the QSslSocket::sslErrors() + signal. This mode is the default for clients. + + \value AutoVerifyPeer QSslSocket will automatically use QueryPeer for + server sockets and VerifyPeer for client sockets. + + \sa QSslSocket::peerVerifyMode() +*/ + +/*! + \fn QSslSocket::encrypted() + + This signal is emitted when QSslSocket enters encrypted mode. After this + signal has been emitted, QSslSocket::isEncrypted() will return true, and + all further transmissions on the socket will be encrypted. + + \sa QSslSocket::connectToHostEncrypted(), QSslSocket::isEncrypted() +*/ + +/*! + \fn QSslSocket::modeChanged(QSslSocket::SslMode mode) + + This signal is emitted when QSslSocket changes from \l + QSslSocket::UnencryptedMode to either \l QSslSocket::SslClientMode or \l + QSslSocket::SslServerMode. \a mode is the new mode. + + \sa QSslSocket::mode() +*/ + +/*! + \fn QSslSocket::encryptedBytesWritten(qint64 written) + \since 4.4 + + This signal is emitted when QSslSocket writes its encrypted data to the + network. The \a written parameter contains the number of bytes that were + successfully written. + + \sa QIODevice::bytesWritten() +*/ + +/*! + \fn void QSslSocket::peerVerifyError(const QSslError &error) + \since 4.4 + + QSslSocket can emit this signal several times during the SSL handshake, + before encryption has been established, to indicate that an error has + occurred while establishing the identity of the peer. The \a error is + usually an indication that QSslSocket is unable to securely identify the + peer. + + This signal provides you with an early indication when something's wrong. + By connecting to this signal, you can manually choose to tear down the + connection from inside the connected slot before the handshake has + completed. If no action is taken, QSslSocket will proceed to emitting + QSslSocket::sslErrors(). + + \sa sslErrors() +*/ + +/*! + \fn void QSslSocket::sslErrors(const QList<QSslError> &errors); + + QSslSocket emits this signal after the SSL handshake to indicate that one + or more errors have occurred while establishing the identity of the + peer. The errors are usually an indication that QSslSocket is unable to + securely identify the peer. Unless any action is taken, the connection + will be dropped after this signal has been emitted. + + If you want to continue connecting despite the errors that have occurred, + you must call QSslSocket::ignoreSslErrors() from inside a slot connected to + this signal. If you need to access the error list at a later point, you + can call sslErrors() (without arguments). + + \a errors contains one or more errors that prevent QSslSocket from + verifying the identity of the peer. + + Note: You cannot use Qt::QueuedConnection when connecting to this signal, + or calling QSslSocket::ignoreSslErrors() will have no effect. + + \sa peerVerifyError() +*/ + +#include "qsslcipher.h" +#include "qsslsocket.h" +#include "qsslsocket_openssl_p.h" +#include "qsslconfiguration_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qdir.h> +#include <QtCore/qmutex.h> +#include <QtCore/qelapsedtimer.h> +#include <QtNetwork/qhostaddress.h> +#include <QtNetwork/qhostinfo.h> + +QT_BEGIN_NAMESPACE + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int qt_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) + return -1; + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +class QSslSocketGlobalData +{ +public: + QSslSocketGlobalData() : config(new QSslConfigurationPrivate) {} + + QMutex mutex; + QList<QSslCipher> supportedCiphers; + QExplicitlySharedDataPointer<QSslConfigurationPrivate> config; +}; +Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData) + +/*! + Constructs a QSslSocket object. \a parent is passed to QObject's + constructor. The new socket's \l {QSslCipher} {cipher} suite is + set to the one returned by the static method defaultCiphers(). +*/ +QSslSocket::QSslSocket(QObject *parent) + : QTcpSocket(*new QSslSocketBackendPrivate, parent) +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::QSslSocket(" << parent << "), this =" << (void *)this; +#endif + d->q_ptr = this; + d->init(); +} + +/*! + Destroys the QSslSocket. +*/ +QSslSocket::~QSslSocket() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::~QSslSocket(), this =" << (void *)this; +#endif + delete d->plainSocket; + d->plainSocket = 0; +} + +/*! + Starts an encrypted connection to the device \a hostName on \a + port, using \a mode as the \l OpenMode. This is equivalent to + calling connectToHost() to establish the connection, followed by a + call to startClientEncryption(). + + QSslSocket first enters the HostLookupState. Then, after entering + either the event loop or one of the waitFor...() functions, it + enters the ConnectingState, emits connected(), and then initiates + the SSL client handshake. At each state change, QSslSocket emits + signal stateChanged(). + + After initiating the SSL client handshake, if the identity of the + peer can't be established, signal sslErrors() is emitted. If you + want to ignore the errors and continue connecting, you must call + ignoreSslErrors(), either from inside a slot function connected to + the sslErrors() signal, or prior to entering encrypted mode. If + ignoreSslErrors() is not called, the connection is dropped, signal + disconnected() is emitted, and QSslSocket returns to the + UnconnectedState. + + If the SSL handshake is successful, QSslSocket emits encrypted(). + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 3 + + \bold{Note:} The example above shows that text can be written to + the socket immediately after requesting the encrypted connection, + before the encrypted() signal has been emitted. In such cases, the + text is queued in the object and written to the socket \e after + the connection is established and the encrypted() signal has been + emitted. + + The default for \a mode is \l ReadWrite. + + If you want to create a QSslSocket on the server side of a connection, you + should instead call startServerEncryption() upon receiving the incoming + connection through QTcpServer. + + \sa connectToHost(), startClientEncryption(), waitForConnected(), waitForEncrypted() +*/ +void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode) +{ + Q_D(QSslSocket); + if (d->state == ConnectedState || d->state == ConnectingState) { + qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected"); + return; + } + + d->init(); + d->autoStartHandshake = true; + d->initialized = true; + + // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs) + // establish the connection immediately (i.e., first attempt). + connectToHost(hostName, port, mode); +} + +/*! + \since 4.6 + \overload + + In addition to the original behaviour of connectToHostEncrypted, + this overloaded method enables the usage of a different hostname + (\a sslPeerName) for the certificate validation instead of + the one used for the TCP connection (\a hostName). + + \sa connectToHostEncrypted() +*/ +void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, + const QString &sslPeerName, OpenMode mode) +{ + Q_D(QSslSocket); + if (d->state == ConnectedState || d->state == ConnectingState) { + qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected"); + return; + } + + d->init(); + d->autoStartHandshake = true; + d->initialized = true; + d->verificationPeerName = sslPeerName; + + // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs) + // establish the connection immediately (i.e., first attempt). + connectToHost(hostName, port, mode); +} + +/*! + Initializes QSslSocket with the native socket descriptor \a + socketDescriptor. Returns true if \a socketDescriptor is accepted + as a valid socket descriptor; otherwise returns false. + The socket is opened in the mode specified by \a openMode, and + enters the socket state specified by \a state. + + \bold{Note:} It is not possible to initialize two sockets with the same + native socket descriptor. + + \sa socketDescriptor() +*/ +bool QSslSocket::setSocketDescriptor(int socketDescriptor, SocketState state, OpenMode openMode) +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::setSocketDescriptor(" << socketDescriptor << ',' + << state << ',' << openMode << ')'; +#endif + if (!d->plainSocket) + d->createPlainSocket(openMode); + bool retVal = d->plainSocket->setSocketDescriptor(socketDescriptor, state, openMode); + d->cachedSocketDescriptor = d->plainSocket->socketDescriptor(); + setSocketError(d->plainSocket->error()); + setSocketState(state); + setOpenMode(openMode); + setLocalPort(d->plainSocket->localPort()); + setLocalAddress(d->plainSocket->localAddress()); + setPeerPort(d->plainSocket->peerPort()); + setPeerAddress(d->plainSocket->peerAddress()); + setPeerName(d->plainSocket->peerName()); + return retVal; +} + +/*! + \since 4.6 + Sets the given \a option to the value described by \a value. + + \sa socketOption() +*/ +void QSslSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) +{ + Q_D(QSslSocket); + if (d->plainSocket) + d->plainSocket->setSocketOption(option, value); +} + +/*! + \since 4.6 + Returns the value of the \a option option. + + \sa setSocketOption() +*/ +QVariant QSslSocket::socketOption(QAbstractSocket::SocketOption option) +{ + Q_D(QSslSocket); + if (d->plainSocket) + return d->plainSocket->socketOption(option); + else + return QVariant(); +} + +/*! + Returns the current mode for the socket; either UnencryptedMode, where + QSslSocket behaves identially to QTcpSocket, or one of SslClientMode or + SslServerMode, where the client is either negotiating or in encrypted + mode. + + When the mode changes, QSslSocket emits modeChanged() + + \sa SslMode +*/ +QSslSocket::SslMode QSslSocket::mode() const +{ + Q_D(const QSslSocket); + return d->mode; +} + +/*! + Returns true if the socket is encrypted; otherwise, false is returned. + + An encrypted socket encrypts all data that is written by calling write() + or putChar() before the data is written to the network, and decrypts all + incoming data as the data is received from the network, before you call + read(), readLine() or getChar(). + + QSslSocket emits encrypted() when it enters encrypted mode. + + You can call sessionCipher() to find which cryptographic cipher is used to + encrypt and decrypt your data. + + \sa mode() +*/ +bool QSslSocket::isEncrypted() const +{ + Q_D(const QSslSocket); + return d->connectionEncrypted; +} + +/*! + Returns the socket's SSL protocol. By default, \l QSsl::SecureProtocols is used. + + \sa setProtocol() +*/ +QSsl::SslProtocol QSslSocket::protocol() const +{ + Q_D(const QSslSocket); + return d->configuration.protocol; +} + +/*! + Sets the socket's SSL protocol to \a protocol. This will affect the next + initiated handshake; calling this function on an already-encrypted socket + will not affect the socket's protocol. +*/ +void QSslSocket::setProtocol(QSsl::SslProtocol protocol) +{ + Q_D(QSslSocket); + d->configuration.protocol = protocol; +} + +/*! + \since 4.4 + + Returns the socket's verify mode. This mode mode decides whether + QSslSocket should request a certificate from the peer (i.e., the client + requests a certificate from the server, or a server requesting a + certificate from the client), and whether it should require that this + certificate is valid. + + The default mode is AutoVerifyPeer, which tells QSslSocket to use + VerifyPeer for clients and QueryPeer for servers. + + \sa setPeerVerifyMode(), peerVerifyDepth(), mode() +*/ +QSslSocket::PeerVerifyMode QSslSocket::peerVerifyMode() const +{ + Q_D(const QSslSocket); + return d->configuration.peerVerifyMode; +} + +/*! + \since 4.4 + + Sets the socket's verify mode to \a mode. This mode decides whether + QSslSocket should request a certificate from the peer (i.e., the client + requests a certificate from the server, or a server requesting a + certificate from the client), and whether it should require that this + certificate is valid. + + The default mode is AutoVerifyPeer, which tells QSslSocket to use + VerifyPeer for clients and QueryPeer for servers. + + Setting this mode after encryption has started has no effect on the + current connection. + + \sa peerVerifyMode(), setPeerVerifyDepth(), mode() +*/ +void QSslSocket::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode) +{ + Q_D(QSslSocket); + d->configuration.peerVerifyMode = mode; +} + +/*! + \since 4.4 + + Returns the maximum number of certificates in the peer's certificate chain + to be checked during the SSL handshake phase, or 0 (the default) if no + maximum depth has been set, indicating that the whole certificate chain + should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa setPeerVerifyDepth(), peerVerifyMode() +*/ +int QSslSocket::peerVerifyDepth() const +{ + Q_D(const QSslSocket); + return d->configuration.peerVerifyDepth; +} + +/*! + \since 4.4 + + Sets the maximum number of certificates in the peer's certificate chain to + be checked during the SSL handshake phase, to \a depth. Setting a depth of + 0 means that no maximum depth is set, indicating that the whole + certificate chain should be checked. + + The certificates are checked in issuing order, starting with the peer's + own certificate, then its issuer's certificate, and so on. + + \sa peerVerifyDepth(), setPeerVerifyMode() +*/ +void QSslSocket::setPeerVerifyDepth(int depth) +{ + Q_D(QSslSocket); + if (depth < 0) { + qWarning("QSslSocket::setPeerVerifyDepth: cannot set negative depth of %d", depth); + return; + } + d->configuration.peerVerifyDepth = 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 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 + reading. +*/ +qint64 QSslSocket::bytesAvailable() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return QIODevice::bytesAvailable() + (d->plainSocket ? d->plainSocket->bytesAvailable() : 0); + return QIODevice::bytesAvailable() + d->readBuffer.size(); +} + +/*! + \reimp + + Returns the number of unencrypted bytes that are waiting to be encrypted + and written to the network. +*/ +qint64 QSslSocket::bytesToWrite() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return d->plainSocket ? d->plainSocket->bytesToWrite() : 0; + return d->writeBuffer.size(); +} + +/*! + \since 4.4 + + Returns the number of encrypted bytes that are awaiting decryption. + Normally, this function will return 0 because QSslSocket decrypts its + incoming data as soon as it can. +*/ +qint64 QSslSocket::encryptedBytesAvailable() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return 0; + return d->plainSocket->bytesAvailable(); +} + +/*! + \since 4.4 + + Returns the number of encrypted bytes that are waiting to be written to + the network. +*/ +qint64 QSslSocket::encryptedBytesToWrite() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return 0; + return d->plainSocket->bytesToWrite(); +} + +/*! + \reimp + + Returns true if you can read one while line (terminated by a single ASCII + '\n' character) of decrypted characters; otherwise, false is returned. +*/ +bool QSslSocket::canReadLine() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return QIODevice::canReadLine() || (d->plainSocket && d->plainSocket->canReadLine()); + return QIODevice::canReadLine() || (!d->readBuffer.isEmpty() && d->readBuffer.canReadLine()); +} + +/*! + \reimp +*/ +void QSslSocket::close() +{ +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::close()"; +#endif + Q_D(QSslSocket); + if (d->plainSocket) + d->plainSocket->close(); + QTcpSocket::close(); + + // must be cleared, reading/writing not possible on closed socket: + d->readBuffer.clear(); + d->writeBuffer.clear(); + // for QTcpSocket this is already done because it uses the readBuffer/writeBuffer + // if the QIODevice it is based on + // ### FIXME QSslSocket should probably do similar instead of having + // its own readBuffer/writeBuffer +} + +/*! + \reimp +*/ +bool QSslSocket::atEnd() const +{ + Q_D(const QSslSocket); + if (d->mode == UnencryptedMode) + return QIODevice::atEnd() && (!d->plainSocket || d->plainSocket->atEnd()); + return QIODevice::atEnd() && d->readBuffer.isEmpty(); +} + +/*! + This function writes as much as possible from the internal write buffer to + the underlying network socket, without blocking. If any data was written, + this function returns true; otherwise false is returned. + + Call this function if you need QSslSocket to start sending buffered data + immediately. The number of bytes successfully written depends on the + operating system. In most cases, you do not need to call this function, + because QAbstractSocket will start sending data automatically once control + goes back to the event loop. In the absence of an event loop, call + waitForBytesWritten() instead. + + \sa write(), waitForBytesWritten() +*/ +// Note! docs copied from QAbstractSocket::flush() +bool QSslSocket::flush() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::flush()"; +#endif + if (d->mode != UnencryptedMode) + // encrypt any unencrypted bytes in our buffer + d->transmit(); + + return d->plainSocket ? d->plainSocket->flush() : false; +} + +/*! + \since 4.4 + + Sets the size of QSslSocket's internal read buffer to be \a size bytes. +*/ +void QSslSocket::setReadBufferSize(qint64 size) +{ + Q_D(QSslSocket); + d->readBufferMaxSize = size; + + if (d->plainSocket) + d->plainSocket->setReadBufferSize(size); +} + +/*! + Aborts the current connection and resets the socket. Unlike + disconnectFromHost(), this function immediately closes the socket, + clearing any pending data in the write buffer. + + \sa disconnectFromHost(), close() +*/ +void QSslSocket::abort() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::abort()"; +#endif + if (d->plainSocket) + d->plainSocket->abort(); + close(); +} + +/*! + \since 4.4 + + Returns the socket's SSL configuration state. The default SSL + configuration of a socket is to use the default ciphers, + default CA certificates, no local private key or certificate. + + The SSL configuration also contains fields that can change with + time without notice. + + \sa localCertificate(), peerCertificate(), peerCertificateChain(), + sessionCipher(), privateKey(), ciphers(), caCertificates() +*/ +QSslConfiguration QSslSocket::sslConfiguration() const +{ + Q_D(const QSslSocket); + + // create a deep copy of our configuration + QSslConfigurationPrivate *copy = new QSslConfigurationPrivate(d->configuration); + copy->ref = 0; // the QSslConfiguration constructor refs up + copy->sessionCipher = d->sessionCipher(); + + return QSslConfiguration(copy); +} + +/*! + \since 4.4 + + Sets the socket's SSL configuration to be the contents of \a configuration. + This function sets the local certificate, the ciphers, the private key and the CA + certificates to those stored in \a configuration. + + It is not possible to set the SSL-state related fields. + + \sa setLocalCertificate(), setPrivateKey(), setCaCertificates(), setCiphers() +*/ +void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) +{ + Q_D(QSslSocket); + d->configuration.localCertificate = configuration.localCertificate(); + d->configuration.privateKey = configuration.privateKey(); + d->configuration.ciphers = configuration.ciphers(); + d->configuration.caCertificates = configuration.caCertificates(); + d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); + d->configuration.peerVerifyMode = configuration.peerVerifyMode(); + d->configuration.protocol = configuration.protocol(); + d->allowRootCertOnDemandLoading = false; +} + +/*! + Sets the socket's local certificate to \a certificate. The local + certificate is necessary if you need to confirm your identity to the + peer. It is used together with the private key; if you set the local + certificate, you must also set the private key. + + The local certificate and private key are always necessary for server + sockets, but are also rarely used by client sockets if the server requires + the client to authenticate. + + \sa localCertificate(), setPrivateKey() +*/ +void QSslSocket::setLocalCertificate(const QSslCertificate &certificate) +{ + Q_D(QSslSocket); + d->configuration.localCertificate = certificate; +} + +/*! + \overload + + Sets the socket's local \l {QSslCertificate} {certificate} to the + first one found in file \a path, which is parsed according to the + specified \a format. +*/ +void QSslSocket::setLocalCertificate(const QString &path, + QSsl::EncodingFormat format) +{ + Q_D(QSslSocket); + QFile file(path); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + d->configuration.localCertificate = QSslCertificate(file.readAll(), format); +} + +/*! + Returns the socket's local \l {QSslCertificate} {certificate}, or + an empty certificate if no local certificate has been assigned. + + \sa setLocalCertificate(), privateKey() +*/ +QSslCertificate QSslSocket::localCertificate() const +{ + Q_D(const QSslSocket); + return d->configuration.localCertificate; +} + +/*! + Returns the peer's digital certificate (i.e., the immediate + certificate of the host you are connected to), or a null + certificate, if the peer has not assigned a certificate. + + The peer certificate is checked automatically during the + handshake phase, so this function is normally used to fetch + the certificate for display or for connection diagnostic + purposes. It contains information about the peer, including + its host name, the certificate issuer, and the peer's public + key. + + Because the peer certificate is set during the handshake phase, it + is safe to access the peer certificate from a slot connected to + the sslErrors() signal or the encrypted() signal. + + If a null certificate is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to check the peer's complete chain of certificates, + use peerCertificateChain() to get them all at once. + + \sa peerCertificateChain() +*/ +QSslCertificate QSslSocket::peerCertificate() const +{ + Q_D(const QSslSocket); + return d->configuration.peerCertificate; +} + +/*! + Returns the peer's chain of digital certificates, or an empty list + of certificates. + + Peer certificates are checked automatically during the handshake + phase. This function is normally used to fetch certificates for + display, or for performing connection diagnostics. Certificates + contain information about the peer and the certificate issuers, + including host name, issuer names, and issuer public keys. + + The peer certificates are set in QSslSocket during the handshake + phase, so it is safe to call this function from a slot connected + to the sslErrors() signal or the encrypted() signal. + + If an empty list is returned, it can mean the SSL handshake + failed, or it can mean the host you are connected to doesn't have + a certificate, or it can mean there is no connection. + + If you want to get only the peer's immediate certificate, use + peerCertificate(). + + \sa peerCertificate() +*/ +QList<QSslCertificate> QSslSocket::peerCertificateChain() const +{ + Q_D(const QSslSocket); + return d->configuration.peerCertificateChain; +} + +/*! + Returns the socket's cryptographic \l {QSslCipher} {cipher}, or a + null cipher if the connection isn't encrypted. The socket's cipher + for the session is set during the handshake phase. The cipher is + used to encrypt and decrypt data transmitted through the socket. + + QSslSocket also provides functions for setting the ordered list of + ciphers from which the handshake phase will eventually select the + session cipher. This ordered list must be in place before the + handshake phase begins. + + \sa ciphers(), setCiphers(), setDefaultCiphers(), defaultCiphers(), + supportedCiphers() +*/ +QSslCipher QSslSocket::sessionCipher() const +{ + Q_D(const QSslSocket); + return d->sessionCipher(); +} + +/*! + Sets the socket's private \l {QSslKey} {key} to \a key. The + private key and the local \l {QSslCertificate} {certificate} are + used by clients and servers that must prove their identity to + SSL peers. + + Both the key and the local certificate are required if you are + creating an SSL server socket. If you are creating an SSL client + socket, the key and local certificate are required if your client + must identify itself to an SSL server. + + \sa privateKey(), setLocalCertificate() +*/ +void QSslSocket::setPrivateKey(const QSslKey &key) +{ + Q_D(QSslSocket); + d->configuration.privateKey = key; +} + +/*! + \overload + + Reads the string in file \a fileName and decodes it using + a specified \a algorithm and encoding \a format to construct + an \l {QSslKey} {SSL key}. If the encoded key is encrypted, + \a passPhrase is used to decrypt it. + + The socket's private key is set to the constructed key. The + private key and the local \l {QSslCertificate} {certificate} are + used by clients and servers that must prove their identity to SSL + peers. + + Both the key and the local certificate are required if you are + creating an SSL server socket. If you are creating an SSL client + socket, the key and local certificate are required if your client + must identify itself to an SSL server. + + \sa privateKey(), setLocalCertificate() +*/ +void QSslSocket::setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm, + QSsl::EncodingFormat format, const QByteArray &passPhrase) +{ + Q_D(QSslSocket); + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + d->configuration.privateKey = QSslKey(file.readAll(), algorithm, + format, QSsl::PrivateKey, passPhrase); + } +} + +/*! + Returns this socket's private key. + + \sa setPrivateKey(), localCertificate() +*/ +QSslKey QSslSocket::privateKey() const +{ + Q_D(const QSslSocket); + return d->configuration.privateKey; +} + +/*! + Returns this socket's current cryptographic cipher suite. This + list is used during the socket's handshake phase for choosing a + session cipher. The returned list of ciphers is ordered by + descending preference. (i.e., the first cipher in the list is the + most preferred cipher). The session cipher will be the first one + in the list that is also supported by the peer. + + By default, the handshake phase can choose any of the ciphers + supported by this system's SSL libraries, which may vary from + system to system. The list of ciphers supported by this system's + SSL libraries is returned by supportedCiphers(). You can restrict + the list of ciphers used for choosing the session cipher for this + socket by calling setCiphers() with a subset of the supported + ciphers. You can revert to using the entire set by calling + setCiphers() with the list returned by supportedCiphers(). + + You can restrict the list of ciphers used for choosing the session + cipher for \e all sockets by calling setDefaultCiphers() with a + subset of the supported ciphers. You can revert to using the + entire set by calling setCiphers() with the list returned by + supportedCiphers(). + + \sa setCiphers(), defaultCiphers(), setDefaultCiphers(), supportedCiphers() +*/ +QList<QSslCipher> QSslSocket::ciphers() const +{ + Q_D(const QSslSocket); + return d->configuration.ciphers; +} + +/*! + Sets the cryptographic cipher suite for this socket to \a ciphers, + which must contain a subset of the ciphers in the list returned by + supportedCiphers(). + + Restricting the cipher suite must be done before the handshake + phase, where the session cipher is chosen. + + \sa ciphers(), setDefaultCiphers(), supportedCiphers() +*/ +void QSslSocket::setCiphers(const QList<QSslCipher> &ciphers) +{ + Q_D(QSslSocket); + d->configuration.ciphers = ciphers; +} + +/*! + Sets the cryptographic cipher suite for this socket to \a ciphers, which + is a colon-separated list of cipher suite names. The ciphers are listed in + order of preference, starting with the most preferred cipher. For example: + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 4 + + Each cipher name in \a ciphers must be the name of a cipher in the + list returned by supportedCiphers(). Restricting the cipher suite + must be done before the handshake phase, where the session cipher + is chosen. + + \sa ciphers(), setDefaultCiphers(), supportedCiphers() +*/ +void QSslSocket::setCiphers(const QString &ciphers) +{ + Q_D(QSslSocket); + d->configuration.ciphers.clear(); + foreach (const QString &cipherName, ciphers.split(QLatin1String(":"),QString::SkipEmptyParts)) { + for (int i = 0; i < 3; ++i) { + // ### Crude + QSslCipher cipher(cipherName, QSsl::SslProtocol(i)); + if (!cipher.isNull()) + d->configuration.ciphers << cipher; + } + } +} + +/*! + Sets the default cryptographic cipher suite for all sockets in + this application to \a ciphers, which must contain a subset of the + ciphers in the list returned by supportedCiphers(). + + Restricting the default cipher suite only affects SSL sockets + that perform their handshake phase after the default cipher + suite has been changed. + + \sa setCiphers(), defaultCiphers(), supportedCiphers() +*/ +void QSslSocket::setDefaultCiphers(const QList<QSslCipher> &ciphers) +{ + QSslSocketPrivate::setDefaultCiphers(ciphers); +} + +/*! + Returns the default cryptographic cipher suite for all sockets in + this application. This list is used during the socket's handshake + phase when negotiating with the peer to choose a session cipher. + The list is ordered by preference (i.e., the first cipher in the + list is the most preferred cipher). + + By default, the handshake phase can choose any of the ciphers + supported by this system's SSL libraries, which may vary from + system to system. The list of ciphers supported by this system's + SSL libraries is returned by supportedCiphers(). + + \sa supportedCiphers() +*/ +QList<QSslCipher> QSslSocket::defaultCiphers() +{ + return QSslSocketPrivate::defaultCiphers(); +} + +/*! + Returns the list of cryptographic ciphers supported by this + system. This list is set by the system's SSL libraries and may + vary from system to system. + + \sa defaultCiphers(), ciphers(), setCiphers() +*/ +QList<QSslCipher> QSslSocket::supportedCiphers() +{ + return QSslSocketPrivate::supportedCiphers(); +} + +/*! + Searches all files in the \a path for certificates encoded in the + specified \a format and adds them to this socket's CA certificate + database. \a path can be explicit, or it can contain wildcards in + the format specified by \a syntax. Returns true if one or more + certificates are added to the socket's CA certificate database; + otherwise returns false. + + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + For more precise control, use addCaCertificate(). + + \sa addCaCertificate(), QSslCertificate::fromPath() +*/ +bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax) +{ + Q_D(QSslSocket); + QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax); + if (certs.isEmpty()) + return false; + + d->configuration.caCertificates += certs; + return true; +} + +/*! + Adds the \a certificate to this socket's CA certificate database. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + To add multiple certificates, use addCaCertificates(). + + \sa caCertificates(), setCaCertificates() +*/ +void QSslSocket::addCaCertificate(const QSslCertificate &certificate) +{ + Q_D(QSslSocket); + d->configuration.caCertificates += certificate; +} + +/*! + Adds the \a certificates to this socket's CA certificate database. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + For more precise control, use addCaCertificate(). + + \sa caCertificates(), addDefaultCaCertificate() +*/ +void QSslSocket::addCaCertificates(const QList<QSslCertificate> &certificates) +{ + Q_D(QSslSocket); + d->configuration.caCertificates += certificates; +} + +/*! + Sets this socket's CA certificate database to be \a certificates. + The certificate database must be set prior to the SSL handshake. + The CA certificate database is used by the socket during the + handshake phase to validate the peer's certificate. + + The CA certificate database can be reset to the current default CA + certificate database by calling this function with the list of CA + certificates returned by defaultCaCertificates(). + + \sa defaultCaCertificates() +*/ +void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates) +{ + Q_D(QSslSocket); + d->configuration.caCertificates = certificates; + d->allowRootCertOnDemandLoading = false; +} + +/*! + Returns this socket's CA certificate database. The CA certificate + database is used by the socket during the handshake phase to + validate the peer's certificate. It can be moodified prior to the + 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 +{ + Q_D(const QSslSocket); + return d->configuration.caCertificates; +} + +/*! + Searches all files in the \a path for certificates with the + specified \a encoding and adds them to the default CA certificate + database. \a path can be an explicit file, or it can contain + wildcards in the format specified by \a syntax. Returns true if + any CA certificates are added to the default database. + + Each SSL socket's CA certificate database is initialized to the + default CA certificate database. + + \sa defaultCaCertificates(), addCaCertificates(), addDefaultCaCertificate() +*/ +bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding, + QRegExp::PatternSyntax syntax) +{ + return QSslSocketPrivate::addDefaultCaCertificates(path, encoding, syntax); +} + +/*! + Adds \a certificate to the default CA certificate database. Each + SSL socket's CA certificate database is initialized to the default + CA certificate database. + + \sa defaultCaCertificates(), addCaCertificates() +*/ +void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate) +{ + QSslSocketPrivate::addDefaultCaCertificate(certificate); +} + +/*! + Adds \a certificates to the default CA certificate database. Each + SSL socket's CA certificate database is initialized to the default + CA certificate database. + + \sa defaultCaCertificates(), addCaCertificates() +*/ +void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certificates) +{ + QSslSocketPrivate::addDefaultCaCertificates(certificates); +} + +/*! + 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. 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. + + \sa addDefaultCaCertificate() +*/ +void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certificates) +{ + QSslSocketPrivate::setDefaultCaCertificates(certificates); +} + +/*! + Returns the current default CA certificate database. This database + is originally set to your system's default CA certificate database. + If no system default database is found, an empty database will be + returned. You can override the default CA certificate database + with your own CA certificate database using setDefaultCaCertificates(). + + 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() +{ + return QSslSocketPrivate::defaultCaCertificates(); +} + +/*! + This function provides the CA certificate database + provided by the operating system. The CA certificate database + returned by this function is used to initialize the database + returned by defaultCaCertificates(). You can replace that database + with your own with setDefaultCaCertificates(). + + \sa caCertificates(), defaultCaCertificates(), setDefaultCaCertificates() +*/ +QList<QSslCertificate> QSslSocket::systemCaCertificates() +{ + // we are calling ensureInitialized() in the method below + return QSslSocketPrivate::systemCaCertificates(); +} + +/*! + Waits until the socket is connected, or \a msecs milliseconds, + whichever happens first. If the connection has been established, + this function returns true; otherwise it returns false. + + \sa QAbstractSocket::waitForConnected() +*/ +bool QSslSocket::waitForConnected(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket) + return false; + bool retVal = d->plainSocket->waitForConnected(msecs); + if (!retVal) { + setSocketState(d->plainSocket->state()); + setSocketError(d->plainSocket->error()); + setErrorString(d->plainSocket->errorString()); + } + return retVal; +} + +/*! + Waits until the socket has completed the SSL handshake and has + emitted encrypted(), or \a msecs milliseconds, whichever comes + first. If encrypted() has been emitted, this function returns + true; otherwise (e.g., the socket is disconnected, or the SSL + handshake fails), false is returned. + + The following example waits up to one second for the socket to be + encrypted: + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 5 + + If msecs is -1, this function will not time out. + + \sa startClientEncryption(), startServerEncryption(), encrypted(), isEncrypted() +*/ +bool QSslSocket::waitForEncrypted(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket || d->connectionEncrypted) + return false; + if (d->mode == UnencryptedMode && !d->autoStartHandshake) + return false; + + QElapsedTimer stopWatch; + stopWatch.start(); + + if (d->plainSocket->state() != QAbstractSocket::ConnectedState) { + // Wait until we've entered connected state. + if (!d->plainSocket->waitForConnected(msecs)) + return false; + } + + while (!d->connectionEncrypted) { + // Start the handshake, if this hasn't been started yet. + if (d->mode == UnencryptedMode) + startClientEncryption(); + // Loop, waiting until the connection has been encrypted or an error + // occurs. + if (!d->plainSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) + return false; + } + return d->connectionEncrypted; +} + +/*! + \reimp +*/ +bool QSslSocket::waitForReadyRead(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket) + return false; + if (d->mode == UnencryptedMode && !d->autoStartHandshake) + return d->plainSocket->waitForReadyRead(msecs); + + // This function must return true if and only if readyRead() *was* emitted. + // So we initialize "readyReadEmitted" to false and check if it was set to true. + // waitForReadyRead() could be called recursively, so we can't use the same variable + // (the inner waitForReadyRead() may fail, but the outer one still succeeded) + bool readyReadEmitted = false; + bool *previousReadyReadEmittedPointer = d->readyReadEmittedPointer; + d->readyReadEmittedPointer = &readyReadEmitted; + + QElapsedTimer stopWatch; + stopWatch.start(); + + if (!d->connectionEncrypted) { + // Wait until we've entered encrypted mode, or until a failure occurs. + if (!waitForEncrypted(msecs)) { + d->readyReadEmittedPointer = previousReadyReadEmittedPointer; + return false; + } + } + + if (!d->writeBuffer.isEmpty()) { + // empty our cleartext write buffer first + d->transmit(); + } + + // test readyReadEmitted first because either operation above + // (waitForEncrypted or transmit) may have set it + while (!readyReadEmitted && + d->plainSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) { + } + + d->readyReadEmittedPointer = previousReadyReadEmittedPointer; + return readyReadEmitted; +} + +/*! + \reimp +*/ +bool QSslSocket::waitForBytesWritten(int msecs) +{ + Q_D(QSslSocket); + if (!d->plainSocket) + return false; + if (d->mode == UnencryptedMode) + return d->plainSocket->waitForBytesWritten(msecs); + + QElapsedTimer stopWatch; + stopWatch.start(); + + if (!d->connectionEncrypted) { + // Wait until we've entered encrypted mode, or until a failure occurs. + if (!waitForEncrypted(msecs)) + return false; + } + if (!d->writeBuffer.isEmpty()) { + // empty our cleartext write buffer first + d->transmit(); + } + + return d->plainSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed())); +} + +/*! + Waits until the socket has disconnected or \a msecs milliseconds, + whichever comes first. If the connection has been disconnected, + this function returns true; otherwise it returns false. + + \sa QAbstractSocket::waitForDisconnected() +*/ +bool QSslSocket::waitForDisconnected(int msecs) +{ + Q_D(QSslSocket); + + // require calling connectToHost() before waitForDisconnected() + if (state() == UnconnectedState) { + qWarning("QSslSocket::waitForDisconnected() is not allowed in UnconnectedState"); + return false; + } + + if (!d->plainSocket) + return false; + if (d->mode == UnencryptedMode) + return d->plainSocket->waitForDisconnected(msecs); + + QElapsedTimer stopWatch; + stopWatch.start(); + + if (!d->connectionEncrypted) { + // Wait until we've entered encrypted mode, or until a failure occurs. + if (!waitForEncrypted(msecs)) + return false; + } + bool retVal = d->plainSocket->waitForDisconnected(qt_timeout_value(msecs, stopWatch.elapsed())); + if (!retVal) { + setSocketState(d->plainSocket->state()); + setSocketError(d->plainSocket->error()); + setErrorString(d->plainSocket->errorString()); + } + return retVal; +} + +/*! + Returns a list of the last SSL errors that occurred. This is the + same list as QSslSocket passes via the sslErrors() signal. If the + connection has been encrypted with no errors, this function will + return an empty list. + + \sa connectToHostEncrypted() +*/ +QList<QSslError> QSslSocket::sslErrors() const +{ + Q_D(const QSslSocket); + return d->sslErrors; +} + +/*! + Returns true if this platform supports SSL; otherwise, returns + false. If the platform doesn't support SSL, the socket will fail + in the connection phase. +*/ +bool QSslSocket::supportsSsl() +{ + return QSslSocketPrivate::supportsSsl(); +} + +/*! + Starts a delayed SSL handshake for a client connection. This + function can be called when the socket is in the \l ConnectedState + but still in the \l UnencryptedMode. If it is not yet connected, + or if it is already encrypted, this function has no effect. + + Clients that implement STARTTLS functionality often make use of + delayed SSL handshakes. Most other clients can avoid calling this + function directly by using connectToHostEncrypted() instead, which + automatically performs the handshake. + + \sa connectToHostEncrypted(), startServerEncryption() +*/ +void QSslSocket::startClientEncryption() +{ + Q_D(QSslSocket); + if (d->mode != UnencryptedMode) { + qWarning("QSslSocket::startClientEncryption: cannot start handshake on non-plain connection"); + return; + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::startClientEncryption()"; +#endif + d->mode = SslClientMode; + emit modeChanged(d->mode); + d->startClientEncryption(); +} + +/*! + Starts a delayed SSL handshake for a server connection. This + function can be called when the socket is in the \l ConnectedState + but still in \l UnencryptedMode. If it is not connected or it is + already encrypted, the function has no effect. + + For server sockets, calling this function is the only way to + initiate the SSL handshake. Most servers will call this function + immediately upon receiving a connection, or as a result of having + received a protocol-specific command to enter SSL mode (e.g, the + server may respond to receiving the string "STARTTLS\r\n" by + calling this function). + + The most common way to implement an SSL server is to create a + subclass of QTcpServer and reimplement + QTcpServer::incomingConnection(). The returned socket descriptor + is then passed to QSslSocket::setSocketDescriptor(). + + \sa connectToHostEncrypted(), startClientEncryption() +*/ +void QSslSocket::startServerEncryption() +{ + Q_D(QSslSocket); + if (d->mode != UnencryptedMode) { + qWarning("QSslSocket::startServerEncryption: cannot start handshake on non-plain connection"); + return; + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::startServerEncryption()"; +#endif + d->mode = SslServerMode; + emit modeChanged(d->mode); + d->startServerEncryption(); +} + +/*! + This slot tells QSslSocket to ignore errors during QSslSocket's + handshake phase and continue connecting. If you want to continue + with the connection even if errors occur during the handshake + phase, then you must call this slot, either from a slot connected + to sslErrors(), or before the handshake phase. If you don't call + this slot, either in response to errors or before the handshake, + the connection will be dropped after the sslErrors() signal has + been emitted. + + If there are no errors during the SSL handshake phase (i.e., the + identity of the peer is established with no problems), QSslSocket + will not emit the sslErrors() signal, and it is unnecessary to + call this function. + + Ignoring errors that occur during an SSL handshake should be done + with caution. A fundamental characteristic of secure connections + is that they should be established with an error free handshake. + + \sa sslErrors() +*/ +void QSslSocket::ignoreSslErrors() +{ + Q_D(QSslSocket); + d->ignoreAllSslErrors = true; +} + +/*! + \overload + \since 4.6 + + This method tells QSslSocket to ignore only the errors given in \a + errors. + + Note that you can set the expected certificate in the SSL error: + If, for instance, you want to connect to a server that uses + a self-signed certificate, consider the following snippet: + + \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 6 + + Multiple calls to this function will replace the list of errors that + were passed in previous calls. + You can clear the list of errors you want to ignore by calling this + function with an empty list. + + \sa sslErrors() +*/ +void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors) +{ + Q_D(QSslSocket); + d->ignoreErrorsList = errors; +} + +/*! + \internal +*/ +void QSslSocket::connectToHostImplementation(const QString &hostName, quint16 port, + OpenMode openMode) +{ + Q_D(QSslSocket); + if (!d->initialized) + d->init(); + d->initialized = false; + +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::connectToHostImplementation(" + << hostName << ',' << port << ',' << openMode << ')'; +#endif + if (!d->plainSocket) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "\tcreating internal plain socket"; +#endif + d->createPlainSocket(openMode); + } +#ifndef QT_NO_NETWORKPROXY + d->plainSocket->setProxy(proxy()); +#endif + QIODevice::open(openMode); + d->plainSocket->connectToHost(hostName, port, openMode); + d->cachedSocketDescriptor = d->plainSocket->socketDescriptor(); +} + +/*! + \internal +*/ +void QSslSocket::disconnectFromHostImplementation() +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::disconnectFromHostImplementation()"; +#endif + if (!d->plainSocket) + return; + if (d->state == UnconnectedState) + return; + if (d->mode == UnencryptedMode && !d->autoStartHandshake) { + d->plainSocket->disconnectFromHost(); + return; + } + if (d->state <= ConnectingState) { + d->pendingClose = true; + return; + } + + // Perhaps emit closing() + if (d->state != ClosingState) { + d->state = ClosingState; + emit stateChanged(d->state); + } + + if (!d->writeBuffer.isEmpty()) + return; + + if (d->mode == UnencryptedMode) { + d->plainSocket->disconnectFromHost(); + } else { + d->disconnectFromHost(); + } +} + +/*! + \reimp +*/ +qint64 QSslSocket::readData(char *data, qint64 maxlen) +{ + Q_D(QSslSocket); + qint64 readBytes = 0; + + if (d->mode == UnencryptedMode && !d->autoStartHandshake) { + readBytes = d->plainSocket->read(data, maxlen); + } else { + do { + const char *readPtr = d->readBuffer.readPointer(); + int bytesToRead = qMin<int>(maxlen - readBytes, d->readBuffer.nextDataBlockSize()); + ::memcpy(data + readBytes, readPtr, bytesToRead); + readBytes += bytesToRead; + d->readBuffer.free(bytesToRead); + } while (!d->readBuffer.isEmpty() && readBytes < maxlen); + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::readData(" << (void *)data << ',' << maxlen << ") ==" << readBytes; +#endif + + // possibly trigger another transmit() to decrypt more data from the socket + if (d->readBuffer.isEmpty() && d->plainSocket->bytesAvailable()) + QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection); + + return readBytes; +} + +/*! + \reimp +*/ +qint64 QSslSocket::writeData(const char *data, qint64 len) +{ + Q_D(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::writeData(" << (void *)data << ',' << len << ')'; +#endif + if (d->mode == UnencryptedMode && !d->autoStartHandshake) + return d->plainSocket->write(data, len); + + char *writePtr = d->writeBuffer.reserve(len); + ::memcpy(writePtr, data, len); + + // make sure we flush to the plain socket's buffer + QMetaObject::invokeMethod(this, "_q_flushWriteBuffer", Qt::QueuedConnection); + + return len; +} + +/*! + \internal +*/ +QSslSocketPrivate::QSslSocketPrivate() + : initialized(false) + , mode(QSslSocket::UnencryptedMode) + , autoStartHandshake(false) + , connectionEncrypted(false) + , ignoreAllSslErrors(false) + , readyReadEmittedPointer(0) + , allowRootCertOnDemandLoading(true) + , plainSocket(0) +{ + QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); +} + +/*! + \internal +*/ +QSslSocketPrivate::~QSslSocketPrivate() +{ +} + +/*! + \internal +*/ +void QSslSocketPrivate::init() +{ + mode = QSslSocket::UnencryptedMode; + autoStartHandshake = false; + connectionEncrypted = false; + ignoreAllSslErrors = false; + + // we don't want to clear the ignoreErrorsList, so + // that it is possible setting it before connecting +// ignoreErrorsList.clear(); + + readBuffer.clear(); + writeBuffer.clear(); + configuration.peerCertificate.clear(); + configuration.peerCertificateChain.clear(); +} + +/*! + \internal +*/ +QList<QSslCipher> QSslSocketPrivate::defaultCiphers() +{ + QMutexLocker locker(&globalData()->mutex); + return globalData()->config->ciphers; +} + +/*! + \internal +*/ +QList<QSslCipher> QSslSocketPrivate::supportedCiphers() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return globalData()->supportedCiphers; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultCiphers(const QList<QSslCipher> &ciphers) +{ + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->ciphers = ciphers; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers) +{ + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->supportedCiphers = ciphers; +} + +/*! + \internal +*/ +QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates() +{ + // ### Qt5: rename everything containing "caCertificates" to "rootCertificates" or similar + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return globalData()->config->caCertificates; +} + +/*! + \internal +*/ +void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &certs) +{ + QSslSocketPrivate::ensureInitialized(); + 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; +} + +/*! + \internal +*/ +bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax) +{ + QSslSocketPrivate::ensureInitialized(); + QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax); + if (certs.isEmpty()) + return false; + + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates += certs; + return true; +} + +/*! + \internal +*/ +void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates += cert; +} + +/*! + \internal +*/ +void QSslSocketPrivate::addDefaultCaCertificates(const QList<QSslCertificate> &certs) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + globalData()->config.detach(); + globalData()->config->caCertificates += certs; +} + +/*! + \internal +*/ +QSslConfiguration QSslConfigurationPrivate::defaultConfiguration() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return QSslConfiguration(globalData()->config.data()); +} + +/*! + \internal +*/ +void QSslConfigurationPrivate::setDefaultConfiguration(const QSslConfiguration &configuration) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + if (globalData()->config == configuration.d) + return; // nothing to do + + globalData()->config = const_cast<QSslConfigurationPrivate*>(configuration.d.constData()); +} + +/*! + \internal +*/ +void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPrivate *ptr) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + const QSslConfigurationPrivate *global = globalData()->config.constData(); + + if (!global) { + ptr = 0; + return; + } + + ptr->ref = 1; + ptr->peerCertificate = global->peerCertificate; + ptr->peerCertificateChain = global->peerCertificateChain; + ptr->localCertificate = global->localCertificate; + ptr->privateKey = global->privateKey; + ptr->sessionCipher = global->sessionCipher; + ptr->ciphers = global->ciphers; + ptr->caCertificates = global->caCertificates; + ptr->protocol = global->protocol; + ptr->peerVerifyMode = global->peerVerifyMode; + ptr->peerVerifyDepth = global->peerVerifyDepth; +} + +/*! + \internal +*/ +void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode) +{ + Q_Q(QSslSocket); + q->setOpenMode(openMode); // <- from QIODevice + q->setSocketState(QAbstractSocket::UnconnectedState); + q->setSocketError(QAbstractSocket::UnknownSocketError); + q->setLocalPort(0); + q->setLocalAddress(QHostAddress()); + q->setPeerPort(0); + q->setPeerAddress(QHostAddress()); + 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); + q->connect(plainSocket, SIGNAL(hostFound()), + q, SLOT(_q_hostFoundSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(disconnected()), + q, SLOT(_q_disconnectedSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + q, SLOT(_q_stateChangedSlot(QAbstractSocket::SocketState)), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(error(QAbstractSocket::SocketError)), + q, SLOT(_q_errorSlot(QAbstractSocket::SocketError)), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(readyRead()), + q, SLOT(_q_readyReadSlot()), + Qt::DirectConnection); + q->connect(plainSocket, SIGNAL(bytesWritten(qint64)), + q, SLOT(_q_bytesWrittenSlot(qint64)), + Qt::DirectConnection); +#ifndef QT_NO_NETWORKPROXY + q->connect(plainSocket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); +#endif + + readBuffer.clear(); + writeBuffer.clear(); + connectionEncrypted = false; + configuration.peerCertificate.clear(); + configuration.peerCertificateChain.clear(); + mode = QSslSocket::UnencryptedMode; + q->setReadBufferSize(readBufferMaxSize); +} + +void QSslSocketPrivate::pauseSocketNotifiers(QSslSocket *socket) +{ + if (!socket->d_func()->plainSocket) + return; + QAbstractSocketPrivate::pauseSocketNotifiers(socket->d_func()->plainSocket); +} + +void QSslSocketPrivate::resumeSocketNotifiers(QSslSocket *socket) +{ + if (!socket->d_func()->plainSocket) + return; + QAbstractSocketPrivate::resumeSocketNotifiers(socket->d_func()->plainSocket); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_connectedSlot() +{ + Q_Q(QSslSocket); + q->setLocalPort(plainSocket->localPort()); + q->setLocalAddress(plainSocket->localAddress()); + q->setPeerPort(plainSocket->peerPort()); + q->setPeerAddress(plainSocket->peerAddress()); + q->setPeerName(plainSocket->peerName()); + cachedSocketDescriptor = plainSocket->socketDescriptor(); + +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_connectedSlot()"; + qDebug() << "\tstate =" << q->state(); + qDebug() << "\tpeer =" << q->peerName() << q->peerAddress() << q->peerPort(); + qDebug() << "\tlocal =" << QHostInfo::fromName(q->localAddress().toString()).hostName() + << q->localAddress() << q->localPort(); +#endif + emit q->connected(); + + if (autoStartHandshake) { + q->startClientEncryption(); + } else if (pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_hostFoundSlot() +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_hostFoundSlot()"; + qDebug() << "\tstate =" << q->state(); +#endif + emit q->hostFound(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_disconnectedSlot() +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_disconnectedSlot()"; + qDebug() << "\tstate =" << q->state(); +#endif + disconnected(); + emit q->disconnected(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_stateChangedSlot(QAbstractSocket::SocketState state) +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_stateChangedSlot(" << state << ')'; +#endif + q->setSocketState(state); + emit q->stateChanged(state); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error) +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_errorSlot(" << error << ')'; + qDebug() << "\tstate =" << q->state(); + qDebug() << "\terrorString =" << q->errorString(); +#endif + q->setSocketError(plainSocket->error()); + q->setErrorString(plainSocket->errorString()); + emit q->error(error); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_readyReadSlot() +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_readyReadSlot() -" << plainSocket->bytesAvailable() << "bytes available"; +#endif + if (mode == QSslSocket::UnencryptedMode) { + if (readyReadEmittedPointer) + *readyReadEmittedPointer = true; + emit q->readyRead(); + return; + } + + transmit(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_bytesWrittenSlot(qint64 written) +{ + Q_Q(QSslSocket); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocket::_q_bytesWrittenSlot(" << written << ')'; +#endif + + if (mode == QSslSocket::UnencryptedMode) + emit q->bytesWritten(written); + else + emit q->encryptedBytesWritten(written); + if (state == QAbstractSocket::ClosingState && writeBuffer.isEmpty()) + q->disconnectFromHost(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_flushWriteBuffer() +{ + Q_Q(QSslSocket); + if (!writeBuffer.isEmpty()) + q->flush(); +} + +/*! + \internal +*/ +void QSslSocketPrivate::_q_flushReadBuffer() +{ + // trigger a read from the plainSocket into SSL + if (mode != QSslSocket::UnencryptedMode) + 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 +#define d d_ptr +#include "moc_qsslsocket.cpp" diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h new file mode 100644 index 0000000000..648fd8c1d0 --- /dev/null +++ b/src/network/ssl/qsslsocket.h @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_H +#define QSSLSOCKET_H + +#include <QtCore/qlist.h> +#include <QtCore/qregexp.h> +#ifndef QT_NO_OPENSSL +# include <QtNetwork/qtcpsocket.h> +# include <QtNetwork/qsslerror.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_OPENSSL + +class QDir; +class QSslCipher; +class QSslCertificate; +class QSslConfiguration; + +class QSslSocketPrivate; +class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket +{ + Q_OBJECT +public: + enum SslMode { + UnencryptedMode, + SslClientMode, + SslServerMode + }; + + enum PeerVerifyMode { + VerifyNone, + QueryPeer, + VerifyPeer, + AutoVerifyPeer + }; + + QSslSocket(QObject *parent = 0); + ~QSslSocket(); + + // Autostarting the SSL client handshake. + void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite); + void connectToHostEncrypted(const QString &hostName, quint16 port, const QString &sslPeerName, OpenMode mode = ReadWrite); + bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState, + OpenMode openMode = ReadWrite); + + // ### Qt 5: Make virtual + void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value); + QVariant socketOption(QAbstractSocket::SocketOption option); + + SslMode mode() const; + bool isEncrypted() const; + + QSsl::SslProtocol protocol() const; + void setProtocol(QSsl::SslProtocol protocol); + + QSslSocket::PeerVerifyMode peerVerifyMode() const; + void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode); + + int peerVerifyDepth() const; + void setPeerVerifyDepth(int depth); + + QString peerVerifyName() const; + void setPeerVerifyName(const QString &hostName); + + // From QIODevice + qint64 bytesAvailable() const; + qint64 bytesToWrite() const; + bool canReadLine() const; + void close(); + bool atEnd() const; + bool flush(); + void abort(); + + // From QAbstractSocket: + void setReadBufferSize(qint64 size); + + // Similar to QIODevice's: + qint64 encryptedBytesAvailable() const; + qint64 encryptedBytesToWrite() const; + + // SSL configuration + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration &config); + + // Certificate & cipher accessors. + void setLocalCertificate(const QSslCertificate &certificate); + void setLocalCertificate(const QString &fileName, QSsl::EncodingFormat format = QSsl::Pem); + QSslCertificate localCertificate() const; + QSslCertificate peerCertificate() const; + QList<QSslCertificate> peerCertificateChain() const; + QSslCipher sessionCipher() const; + + // Private keys, for server sockets. + void setPrivateKey(const QSslKey &key); + void setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm = QSsl::Rsa, + QSsl::EncodingFormat format = QSsl::Pem, + const QByteArray &passPhrase = QByteArray()); + QSslKey privateKey() const; + + // Cipher settings. + QList<QSslCipher> ciphers() const; + void setCiphers(const QList<QSslCipher> &ciphers); + void setCiphers(const QString &ciphers); + static void setDefaultCiphers(const QList<QSslCipher> &ciphers); + static QList<QSslCipher> defaultCiphers(); + static QList<QSslCipher> supportedCiphers(); + + // CA settings. + bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QRegExp::PatternSyntax syntax = QRegExp::FixedString); + void addCaCertificate(const QSslCertificate &certificate); + void addCaCertificates(const QList<QSslCertificate> &certificates); + void setCaCertificates(const QList<QSslCertificate> &certificates); + QList<QSslCertificate> caCertificates() const; + static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem, + QRegExp::PatternSyntax syntax = QRegExp::FixedString); + static void addDefaultCaCertificate(const QSslCertificate &certificate); + static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates); + static void setDefaultCaCertificates(const QList<QSslCertificate> &certificates); + static QList<QSslCertificate> defaultCaCertificates(); + static QList<QSslCertificate> systemCaCertificates(); + + bool waitForConnected(int msecs = 30000); + bool waitForEncrypted(int msecs = 30000); + bool waitForReadyRead(int msecs = 30000); + bool waitForBytesWritten(int msecs = 30000); + bool waitForDisconnected(int msecs = 30000); + + QList<QSslError> sslErrors() const; + + static bool supportsSsl(); + void ignoreSslErrors(const QList<QSslError> &errors); + +public Q_SLOTS: + void startClientEncryption(); + void startServerEncryption(); + void ignoreSslErrors(); + +Q_SIGNALS: + void encrypted(); + void peerVerifyError(const QSslError &error); + void sslErrors(const QList<QSslError> &errors); + void modeChanged(QSslSocket::SslMode newMode); + void encryptedBytesWritten(qint64 totalBytes); + +protected Q_SLOTS: + void connectToHostImplementation(const QString &hostName, quint16 port, + OpenMode openMode); + void disconnectFromHostImplementation(); + +protected: + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + +private: + Q_DECLARE_PRIVATE(QSslSocket) + Q_DISABLE_COPY(QSslSocket) + Q_PRIVATE_SLOT(d_func(), void _q_connectedSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_hostFoundSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_disconnectedSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_stateChangedSlot(QAbstractSocket::SocketState)) + Q_PRIVATE_SLOT(d_func(), void _q_errorSlot(QAbstractSocket::SocketError)) + Q_PRIVATE_SLOT(d_func(), void _q_readyReadSlot()) + Q_PRIVATE_SLOT(d_func(), void _q_bytesWrittenSlot(qint64)) + Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer()) + Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer()) + friend class QSslSocketBackendPrivate; +}; + +#endif // QT_NO_OPENSSL + +QT_END_NAMESPACE + +#ifndef QT_NO_OPENSSL +Q_DECLARE_METATYPE(QList<QSslError>) +#endif + +QT_END_HEADER + +#endif diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp new file mode 100644 index 0000000000..78a78a26f6 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -0,0 +1,1459 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QSSLSOCKET_DEBUG + +#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/qelapsedtimer.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> +#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) +#define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file + PtrSecCertificateGetData QSslSocketPrivate::ptrSecCertificateGetData = 0; + PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0; + PtrSecTrustCopyAnchorCertificates QSslSocketPrivate::ptrSecTrustCopyAnchorCertificates = 0; +#elif defined(Q_OS_WIN) + PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0; + PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0; + PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0; +#elif defined(Q_OS_SYMBIAN) +#include <e32base.h> +#include <e32std.h> +#include <e32debug.h> +#include <QtCore/private/qcore_symbian_p.h> +#endif + +bool QSslSocketPrivate::s_libraryLoaded = false; +bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; +bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; + +/* \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 QOpenSslLocks +{ +public: + inline QOpenSslLocks() + : 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 ~QOpenSslLocks() + { + QMutexLocker locker(&locksLocker); + for (int i = 0; i < q_CRYPTO_num_locks(); ++i) + delete locks[i]; + delete [] locks; + + QSslSocketPrivate::deinitialize(); + } + 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(QOpenSslLocks, 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 (quintptr)QThread::currentThreadId(); +} +} // extern "C" + +QSslSocketBackendPrivate::QSslSocketBackendPrivate() + : ssl(0), + ctx(0), + pkey(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<QPair<int, int> > errors; +}; +Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList) +static int q_X509Callback(int ok, X509_STORE_CTX *ctx) +{ + if (!ok) { + // Store the error and at which depth the error was detected. + _q_sslErrorList()->errors << qMakePair<int, int>(ctx->error, ctx->error_depth); + } + // Always return OK to allow verification to continue. We're handle the + // errors gracefully after collecting all errors, after verification has + // completed. + return 1; +} + +bool QSslSocketBackendPrivate::initSslContext() +{ + Q_Q(QSslSocket); + + // Create and initialize SSL context. Accept SSLv2, SSLv3 and TLSv1. + bool client = (mode == QSslSocket::SslClientMode); + + bool reinitialized = false; +init_context: + switch (configuration.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::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()); + break; + case QSsl::TlsV1: + ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method()); + break; + } + if (!ctx) { + // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them + // by re-initializing the library. + if (!reinitialized) { + reinitialized = true; + if (q_SSL_library_init() == 1) + goto init_context; + } + + // ### Bad error code + q->setErrorString(QSslSocket::tr("Error creating SSL context (%1)").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Enable all bug workarounds. + 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; + int first = true; + QList<QSslCipher> ciphers = configuration.ciphers; + if (ciphers.isEmpty()) + ciphers = defaultCiphers(); + foreach (const QSslCipher &cipher, 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(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Add all our CAs to this store. + QList<QSslCertificate> expiredCerts; + foreach (const QSslCertificate &caCertificate, q->caCertificates()) { + // add expired certs later, so that the + // valid ones are used before the expired ones + if (! caCertificate.isValid()) { + expiredCerts.append(caCertificate); + } else { + q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); + } + } + + bool addExpiredCerts = true; +#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5) + //On Leopard SSL does not work if we add the expired certificates. + if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5) + addExpiredCerts = false; +#endif + // now add the expired certs + if (addExpiredCerts) { + foreach (const QSslCertificate &caCertificate, expiredCerts) { + q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); + } + } + + 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); + + if (!configuration.localCertificate.isNull()) { + // Require a private key as well. + if (configuration.privateKey.isNull()) { + q->setErrorString(QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(getErrorsFromOpenSsl())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Load certificate + if (!q_SSL_CTX_use_certificate(ctx, (X509 *)configuration.localCertificate.handle())) { + q->setErrorString(QSslSocket::tr("Error loading local certificate, %1").arg(getErrorsFromOpenSsl())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Load private key + pkey = q_EVP_PKEY_new(); + // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. + // this lead to a memory leak. Now we use the *_set1_* functions which do not + // take ownership of the RSA/DSA key instance because the QSslKey already has ownership. + if (configuration.privateKey.algorithm() == QSsl::Rsa) + q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle()); + else + q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle()); + if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) { + q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl())); + 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 certify public key, %1").arg(getErrorsFromOpenSsl())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + } + + // Initialize peer verification. + if (configuration.peerVerifyMode == QSslSocket::VerifyNone) { + q_SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0); + } else { + q_SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, q_X509Callback); + } + + // Set verification depth. + if (configuration.peerVerifyDepth != 0) + q_SSL_CTX_set_verify_depth(ctx, configuration.peerVerifyDepth); + + // Create and initialize SSL session + if (!(ssl = q_SSL_new(ctx))) { + // ### Bad error code + q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + 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(); + + // 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(getErrorsFromOpenSsl())); + 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 +*/ +void QSslSocketPrivate::deinitialize() +{ + q_CRYPTO_set_id_callback(0); + q_CRYPTO_set_locking_callback(0); +} + +/*! + \internal + + Does the minimum amount of initialization to determine whether SSL + is supported or not. +*/ + +bool QSslSocketPrivate::supportsSsl() +{ + return ensureLibraryLoaded(); +} + +bool QSslSocketPrivate::ensureLibraryLoaded() +{ + if (!q_resolveOpenSslSymbols()) + return false; + + // Check if the library itself needs to be initialized. + QMutexLocker locker(openssl_locks()->initLock()); + if (!s_libraryLoaded) { + s_libraryLoaded = 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(); + q_OpenSSL_add_all_algorithms(); + + // Initialize OpenSSL's random seed. + if (!q_RAND_status()) { + struct { + int msec; + int sec; + void *stack; + } randomish; + + int attempts = 500; + do { + if (attempts < 500) { +#ifdef Q_OS_UNIX + struct timespec ts = {0, 33333333}; + nanosleep(&ts, 0); +#else + Sleep(3); +#endif + randomish.msec = attempts; + } + randomish.stack = (void *)&randomish; + randomish.msec = QTime::currentTime().msec(); + randomish.sec = QTime::currentTime().second(); + q_RAND_seed((const char *)&randomish, sizeof(randomish)); + } while (!q_RAND_status() && --attempts); + if (!attempts) + return false; + } + } + return true; +} + +void QSslSocketPrivate::ensureCiphersAndCertsLoaded() +{ + QMutexLocker locker(openssl_locks()->initLock()); + if (s_loadedCiphersAndCerts) + return; + s_loadedCiphersAndCerts = true; + + resetDefaultCiphers(); + + //load symbols needed to receive certificates from system store +#if defined(Q_OS_MAC) + QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security"); + if (securityLib.load()) { + ptrSecCertificateGetData = (PtrSecCertificateGetData) securityLib.resolve("SecCertificateGetData"); + if (!ptrSecCertificateGetData) + qWarning("could not resolve symbols in security library"); // should never happen + + ptrSecTrustSettingsCopyCertificates = (PtrSecTrustSettingsCopyCertificates) securityLib.resolve("SecTrustSettingsCopyCertificates"); + if (!ptrSecTrustSettingsCopyCertificates) { // method was introduced in Leopard, use legacy method if it's not there + ptrSecTrustCopyAnchorCertificates = (PtrSecTrustCopyAnchorCertificates) securityLib.resolve("SecTrustCopyAnchorCertificates"); + if (!ptrSecTrustCopyAnchorCertificates) + qWarning("could not resolve symbols in security library"); // should never happen + } + } else { + qWarning("could not load security library"); + } +#elif defined(Q_OS_WIN) + HINSTANCE hLib = LoadLibraryW(L"Crypt32"); + if (hLib) { +#if defined(Q_OS_WINCE) + ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, L"CertOpenStore"); + ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, L"CertFindCertificateInStore"); + ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, L"CertCloseStore"); +#else + ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW"); + ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore"); + ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore"); +#endif + if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) + qWarning("could not resolve symbols in crypt32 library"); // should never happen + } 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 + // if on-demand loading was not enabled, load the certs now + if (!s_loadRootCertsOnDemand) + setDefaultCaCertificates(systemCaCertificates()); +} + +/*! + \internal + + Declared static in QSslSocketPrivate, makes sure the SSL libraries have + been initialized. +*/ + +void QSslSocketPrivate::ensureInitialized() +{ + if (!supportsSsl()) + return; + + ensureCiphersAndCertsLoaded(); +} + +/*! + \internal + + Declared static in QSslSocketPrivate, backend-dependent loading of + application-wide global ciphers. +*/ +void QSslSocketPrivate::resetDefaultCiphers() +{ + SSL_CTX *myCtx = q_SSL_CTX_new(q_SSLv23_client_method()); + SSL *mySsl = q_SSL_new(myCtx); + + QList<QSslCipher> ciphers; + + STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl); + for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { + if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { + if (cipher->valid) { + QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); + if (!ciph.isNull()) { + if (!ciph.name().toLower().startsWith(QLatin1String("adh"))) + ciphers << ciph; + } + } + } + } + + q_SSL_CTX_free(myCtx); + q_SSL_free(mySsl); + + setDefaultSupportedCiphers(ciphers); + setDefaultCiphers(ciphers); +} + +#if defined(Q_OS_SYMBIAN) + +CSymbianCertificateRetriever::CSymbianCertificateRetriever() : CActive(CActive::EPriorityStandard), + iCertificatePtr(0,0,0), iSequenceError(KErrNone) +{ +} + +CSymbianCertificateRetriever::~CSymbianCertificateRetriever() +{ + iThread.Close(); +} + +CSymbianCertificateRetriever* CSymbianCertificateRetriever::NewL() +{ + CSymbianCertificateRetriever* self = new (ELeave) CSymbianCertificateRetriever(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; +} + +int CSymbianCertificateRetriever::GetCertificates(QList<QByteArray> &certificates) +{ + iCertificates = &certificates; + + TRequestStatus status; + iThread.Logon(status); + iThread.Resume(); + User::WaitForRequest(status); + if (iThread.ExitType() == EExitKill) + return KErrDied; + else + return status.Int(); // Logon() completes with the thread's exit value +} + +void CSymbianCertificateRetriever::doThreadEntryL() +{ + CActiveScheduler* activeScheduler = new (ELeave) CActiveScheduler; + CleanupStack::PushL(activeScheduler); + CActiveScheduler::Install(activeScheduler); + + CActiveScheduler::Add(this); + + // These aren't deleted in the destructor so leaving the to CS is ok + iCertStore = CUnifiedCertStore::NewLC(qt_s60GetRFs(), EFalse); + iCertFilter = CCertAttributeFilter::NewLC(); + + // only interested in CA certs + iCertFilter->SetOwnerType(ECACertificate); + // only interested in X.509 format (we don't support WAP formats) + iCertFilter->SetFormat(EX509Certificate); + + // Kick off the sequence by initializing the cert store + iState = Initializing; + iCertStore->Initialize(iStatus); + SetActive(); + + CActiveScheduler::Start(); + + // Sequence complete, clean up + + // These MUST be cleaned up before the installed CActiveScheduler is destroyed and can't be left to the + // destructor of CSymbianCertificateRetriever. Otherwise the destructor of CActiveScheduler will get + // stuck. + iCertInfos.Close(); + CleanupStack::PopAndDestroy(3); // activeScheduler, iCertStore, iCertFilter +} + + +TInt CSymbianCertificateRetriever::ThreadEntryPoint(TAny* aParams) +{ + User::SetCritical(User::EProcessCritical); + CTrapCleanup* cleanupStack = CTrapCleanup::New(); + + CSymbianCertificateRetriever* self = (CSymbianCertificateRetriever*) aParams; + TRAPD(err, self->doThreadEntryL()); + delete cleanupStack; + + // doThreadEntryL() can leave only before the retrieval sequence is started + if (err) + return err; + else + return self->iSequenceError; // return any error that occurred during the retrieval +} + +void CSymbianCertificateRetriever::ConstructL() +{ + TInt err; + int i=0; + QString name(QLatin1String("CertWorkerThread-%1")); + //recently closed thread names remain in use for a while until all handles have been closed + //including users of RUndertaker + do { + err = iThread.Create(qt_QString2TPtrC(name.arg(i++)), + CSymbianCertificateRetriever::ThreadEntryPoint, 16384, NULL, this); + } while (err == KErrAlreadyExists); + User::LeaveIfError(err); +} + +void CSymbianCertificateRetriever::DoCancel() +{ + switch(iState) { + case Initializing: + iCertStore->CancelInitialize(); + break; + case Listing: + iCertStore->CancelList(); + break; + case RetrievingCertificates: + iCertStore->CancelGetCert(); + break; + } +} + +TInt CSymbianCertificateRetriever::RunError(TInt aError) +{ + // If something goes wrong in the sequence, abort the sequence + iSequenceError = aError; // this gets reported to the client in the TRequestStatus + CActiveScheduler::Stop(); + return KErrNone; +} + +void CSymbianCertificateRetriever::GetCertificateL() +{ + if (iCurrentCertIndex < iCertInfos.Count()) { + CCTCertInfo* certInfo = iCertInfos[iCurrentCertIndex++]; + iCertificateData = QByteArray(); + QT_TRYCATCH_LEAVING(iCertificateData.resize(certInfo->Size())); + iCertificatePtr.Set((TUint8*)iCertificateData.data(), 0, iCertificateData.size()); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "getting " << qt_TDesC2QString(certInfo->Label()) << " size=" << certInfo->Size(); + qDebug() << "format=" << certInfo->CertificateFormat(); + qDebug() << "ownertype=" << certInfo->CertificateOwnerType(); + qDebug() << "type=" << hex << certInfo->Type().iUid; +#endif + iCertStore->Retrieve(*certInfo, iCertificatePtr, iStatus); + iState = RetrievingCertificates; + SetActive(); + } else { + //reached end of list + CActiveScheduler::Stop(); + } +} + +void CSymbianCertificateRetriever::RunL() +{ +#ifdef QSSLSOCKET_DEBUG + qDebug() << "CSymbianCertificateRetriever::RunL status " << iStatus.Int() << " count " << iCertInfos.Count() << " index " << iCurrentCertIndex; +#endif + switch (iState) { + case Initializing: + User::LeaveIfError(iStatus.Int()); // initialise fail means pointless to continue + iState = Listing; + iCertStore->List(iCertInfos, *iCertFilter, iStatus); + SetActive(); + break; + + case Listing: + User::LeaveIfError(iStatus.Int()); // listing fail means pointless to continue + iCurrentCertIndex = 0; + GetCertificateL(); + break; + + case RetrievingCertificates: + if (iStatus.Int() == KErrNone) + iCertificates->append(iCertificateData); + else + qWarning() << "CSymbianCertificateRetriever: failed to retrieve a certificate, error " << iStatus.Int(); + GetCertificateL(); + break; + } +} +#endif // defined(Q_OS_SYMBIAN) + +QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() +{ + ensureInitialized(); +#ifdef QSSLSOCKET_DEBUG + QElapsedTimer timer; + timer.start(); +#endif + QList<QSslCertificate> systemCerts; +#if defined(Q_OS_MAC) + CFArrayRef cfCerts; + OSStatus status = 1; + + OSStatus SecCertificateGetData ( + SecCertificateRef certificate, + CSSM_DATA_PTR data + ); + + if (ptrSecCertificateGetData) { + if (ptrSecTrustSettingsCopyCertificates) + status = ptrSecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts); + else if (ptrSecTrustCopyAnchorCertificates) + status = ptrSecTrustCopyAnchorCertificates(&cfCerts); + if (!status) { + CFIndex size = CFArrayGetCount(cfCerts); + for (CFIndex i = 0; i < size; ++i) { + SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i); + CSSM_DATA data; + CSSM_DATA_PTR dataPtr = &data; + if (ptrSecCertificateGetData(cfCert, dataPtr)) { + qWarning("error retrieving a CA certificate from the system store"); + } else { + int len = data.Length; + char *rawData = reinterpret_cast<char *>(data.Data); + QByteArray rawCert(rawData, len); + systemCerts.append(QSslCertificate::fromData(rawCert, QSsl::Der)); + } + } + CFRelease(cfCerts); + } + else { + // no detailed error handling here + qWarning("could not retrieve system CA certificates"); + } + } +#elif defined(Q_OS_WIN) + if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) { + HCERTSTORE hSystemStore; +#if defined(Q_OS_WINCE) + hSystemStore = ptrCertOpenSystemStoreW(CERT_STORE_PROV_SYSTEM_W, + 0, + 0, + CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER, + L"ROOT"); +#else + hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT"); +#endif + if(hSystemStore) { + PCCERT_CONTEXT pc = NULL; + while(1) { + pc = ptrCertFindCertificateInStore( hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pc); + if(!pc) + break; + QByteArray der((const char *)(pc->pbCertEncoded), static_cast<int>(pc->cbCertEncoded)); + QSslCertificate cert(der, QSsl::Der); + systemCerts.append(cert); + } + ptrCertCloseStore(hSystemStore, 0); + } + } +#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + QSet<QString> certFiles; + QList<QByteArray> directories = unixRootCertDirectories(); + QDir currentDir; + QStringList nameFilters; + nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt"); + currentDir.setNameFilters(nameFilters); + for (int a = 0; a < directories.count(); a++) { + currentDir.setPath(QLatin1String(directories.at(a))); + QDirIterator it(currentDir); + while(it.hasNext()) { + it.next(); + // use canonical path here to not load the same certificate twice if symlinked + certFiles.insert(it.fileInfo().canonicalFilePath()); + } + } + QSetIterator<QString> it(certFiles); + while(it.hasNext()) { + systemCerts.append(QSslCertificate::fromPath(it.next())); + } + systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva + systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss + +#elif defined(Q_OS_SYMBIAN) + QList<QByteArray> certs; + QScopedPointer<CSymbianCertificateRetriever> retriever(CSymbianCertificateRetriever::NewL()); + + retriever->GetCertificates(certs); + foreach (const QByteArray &encodedCert, certs) { + QSslCertificate cert(encodedCert, QSsl::Der); + if (!cert.isNull()) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "imported certificate: " << cert.issuerInfo(QSslCertificate::CommonName); +#endif + systemCerts.append(cert); + } + } +#endif +#ifdef QSSLSOCKET_DEBUG + qDebug() << "systemCaCertificates retrieval time " << timer.elapsed() << "ms"; + qDebug() << "imported " << systemCerts.count() << " certificates"; +#endif + + return systemCerts; +} + +void QSslSocketBackendPrivate::startClientEncryption() +{ + if (!initSslContext()) { + // ### report error: internal OpenSSL failure + return; + } + + // Start connecting. This will place outgoing data in the BIO, so we + // follow up with calling transmit(). + startHandshake(); + transmit(); +} + +void QSslSocketBackendPrivate::startServerEncryption() +{ + if (!initSslContext()) { + // ### report error: internal OpenSSL failure + return; + } + + // Start connecting. This will place outgoing data in the BIO, so we + // follow up with calling transmit(). + startHandshake(); + transmit(); +} + +/*! + \internal + + Transmits encrypted data between the BIOs and the socket. +*/ +void QSslSocketBackendPrivate::transmit() +{ + Q_Q(QSslSocket); + + // If we don't have any SSL context, don't bother transmitting. + if (!ssl) + return; + + bool transmitting; + do { + transmitting = false; + + // If the connection is secure, we can transfer data from the write + // buffer (in plain text) to the write BIO through SSL_write. + if (connectionEncrypted && !writeBuffer.isEmpty()) { + qint64 totalBytesWritten = 0; + int nextDataBlockSize; + while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) { + int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize); + if (writtenBytes <= 0) { + // ### Better error handling. + q->setErrorString(QSslSocket::tr("Unable to write data: %1").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return; + } +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: encrypted" << writtenBytes << "bytes"; +#endif + writeBuffer.free(writtenBytes); + totalBytesWritten += writtenBytes; + + if (writtenBytes < nextDataBlockSize) { + // break out of the writing loop and try again after we had read + transmitting = true; + break; + } + } + + if (totalBytesWritten > 0) { + // Don't emit bytesWritten() recursively. + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q->bytesWritten(totalBytesWritten); + emittedBytesWritten = false; + } + } + } + + // Check if we've got any data to be written to the socket. + QVarLengthArray<char, 4096> data; + int pendingBytes; + while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0) { + // Read encrypted data from the write BIO into a buffer. + data.resize(pendingBytes); + int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes); + + // Write encrypted data from the buffer to the socket. + plainSocket->write(data.constData(), encryptedBytesRead); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: wrote" << encryptedBytesRead << "encrypted bytes to the socket"; +#endif + transmitting = true; + } + + // Check if we've got any data to be read from the socket. + if (!connectionEncrypted || !readBufferMaxSize || readBuffer.size() < readBufferMaxSize) + while ((pendingBytes = plainSocket->bytesAvailable()) > 0) { + // Read encrypted data from the socket into a buffer. + data.resize(pendingBytes); + // just peek() here because q_BIO_write could write less data than expected + int encryptedBytesRead = plainSocket->peek(data.data(), pendingBytes); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: read" << encryptedBytesRead << "encrypted bytes from the socket"; +#endif + // Write encrypted data from the buffer into the read BIO. + int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead); + + // do the actual read() here and throw away the results. + if (writtenToBio > 0) { + // ### TODO: make this cheaper by not making it memcpy. E.g. make it work with data=0x0 or make it work with seek + plainSocket->read(data.data(), writtenToBio); + } else { + // ### Better error handling. + q->setErrorString(QSslSocket::tr("Unable to decrypt data: %1").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return; + } + + transmitting = true; + } + + // If the connection isn't secured yet, this is the time to retry the + // connect / accept. + if (!connectionEncrypted) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: testing encryption"; +#endif + if (startHandshake()) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: encryption established"; +#endif + connectionEncrypted = true; + transmitting = true; + } else if (plainSocket->state() != QAbstractSocket::ConnectedState) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: connection lost"; +#endif + break; + } else { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: encryption not done yet"; +#endif + } + } + + // If the request is small and the remote host closes the transmission + // after sending, there's a chance that startHandshake() will already + // have triggered a shutdown. + if (!ssl) + continue; + + // We always read everything from the SSL decryption buffers, even if + // we have a readBufferMaxSize. There's no point in leaving data there + // just so that readBuffer.size() == readBufferMaxSize. + int readBytes = 0; + data.resize(4096); + ::memset(data.data(), 0, data.size()); + do { + // Don't use SSL_pending(). It's very unreliable. + if ((readBytes = q_SSL_read(ssl, data.data(), data.size())) > 0) { +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: decrypted" << readBytes << "bytes"; +#endif + char *ptr = readBuffer.reserve(readBytes); + ::memcpy(ptr, data.data(), readBytes); + + if (readyReadEmittedPointer) + *readyReadEmittedPointer = true; + emit q->readyRead(); + transmitting = true; + continue; + } + + // Error. + switch (q_SSL_get_error(ssl, readBytes)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + // Out of data. + break; + case SSL_ERROR_ZERO_RETURN: + // The remote host closed the connection. +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: remote disconnect"; +#endif + plainSocket->disconnectFromHost(); + break; + case SSL_ERROR_SYSCALL: // some IO error + case SSL_ERROR_SSL: // error in the SSL library + // we do not know exactly what the error is, nor whether we can recover from it, + // so just return to prevent an endless loop in the outer "while" statement + q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return; + default: + // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a + // BIO_s_connect() or BIO_s_accept(), which we do not call. + // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a + // SSL_CTX_set_client_cert_cb(), which we do not call. + // So this default case should never be triggered. + q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + break; + } + } while (ssl && readBytes > 0); + } while (ssl && ctx && transmitting); +} + +static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert) +{ + QSslError error; + switch (errorCode) { + case X509_V_OK: + // X509_V_OK is also reported if the peer had no certificate. + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break; + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break; + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + error = QSslError(QSslError::CertificateSignatureFailed, cert); break; + case X509_V_ERR_CERT_NOT_YET_VALID: + error = QSslError(QSslError::CertificateNotYetValid, cert); break; + case X509_V_ERR_CERT_HAS_EXPIRED: + error = QSslError(QSslError::CertificateExpired, cert); break; + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + error = QSslError(QSslError::InvalidNotBeforeField, cert); break; + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + error = QSslError(QSslError::InvalidNotAfterField, cert); break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + error = QSslError(QSslError::SelfSignedCertificate, cert); break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break; + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break; + case X509_V_ERR_CERT_REVOKED: + error = QSslError(QSslError::CertificateRevoked, cert); break; + case X509_V_ERR_INVALID_CA: + error = QSslError(QSslError::InvalidCaCertificate, cert); break; + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + error = QSslError(QSslError::PathLengthExceeded, cert); break; + case X509_V_ERR_INVALID_PURPOSE: + error = QSslError(QSslError::InvalidPurpose, cert); break; + case X509_V_ERR_CERT_UNTRUSTED: + error = QSslError(QSslError::CertificateUntrusted, cert); break; + case X509_V_ERR_CERT_REJECTED: + error = QSslError(QSslError::CertificateRejected, cert); break; + default: + error = QSslError(QSslError::UnspecifiedError, cert); break; + } + return error; +} + +bool QSslSocketBackendPrivate::startHandshake() +{ + Q_Q(QSslSocket); + + // Check if the connection has been established. Get all errors from the + // verification stage. + _q_sslErrorList()->mutex.lock(); + _q_sslErrorList()->errors.clear(); + int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl); + + const QList<QPair<int, int> > &lastErrors = _q_sslErrorList()->errors; + for (int i = 0; i < lastErrors.size(); ++i) { + const QPair<int, int> ¤tError = lastErrors.at(i); + // Initialize the peer certificate chain in order to find which certificate caused this error + if (configuration.peerCertificateChain.isEmpty()) + configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); + emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.first, + configuration.peerCertificateChain.value(currentError.second))); + if (q->state() != QAbstractSocket::ConnectedState) + break; + } + + errorList << lastErrors; + _q_sslErrorList()->mutex.unlock(); + + // Connection aborted during handshake phase. + if (q->state() != QAbstractSocket::ConnectedState) + return false; + + // Check if we're encrypted or not. + if (result <= 0) { + switch (q_SSL_get_error(ssl, result)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + // The handshake is not yet complete. + break; + default: + q->setErrorString(QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl())); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::startHandshake: error!" << q->errorString(); +#endif + emit q->error(QAbstractSocket::SslHandshakeFailedError); + q->abort(); + } + return false; + } + + // Store the peer certificate and chain. For clients, the peer certificate + // chain includes the peer certificate; for servers, it doesn't. Both the + // peer certificate and the chain may be empty if the peer didn't present + // any certificate. + if (configuration.peerCertificateChain.isEmpty()) + configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl)); + X509 *x509 = q_SSL_get_peer_certificate(ssl); + configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509); + q_X509_free(x509); + + // 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); + + // Check the peer certificate itself. First try the subject's common name + // (CN) as a wildcard, then try all alternate subject name DNS entries the + // same way. + if (!configuration.peerCertificate.isNull()) { + // but only if we're a client connecting to a server + // if we're the server, don't check CN + if (mode == QSslSocket::SslClientMode) { + QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); + QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); + + if (!isMatchingHostname(commonName.toLower(), peerName.toLower())) { + bool matched = false; + foreach (const QString &altName, configuration.peerCertificate + .alternateSubjectNames().values(QSsl::DnsEntry)) { + if (isMatchingHostname(altName.toLower(), peerName.toLower())) { + matched = true; + break; + } + } + + if (!matched) { + // No matches in common names or alternate names. + QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + } + } else { + // No peer certificate presented. Report as error if the socket + // expected one. + if (doVerifyPeer) { + QSslError error(QSslError::NoPeerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + + // Translate errors from the error list into QSslErrors. + for (int i = 0; i < errorList.size(); ++i) { + const QPair<int, int> &errorAndDepth = errorList.at(i); + int err = errorAndDepth.first; + int depth = errorAndDepth.second; + errors << _q_OpenSSL_to_QSslError(err, configuration.peerCertificateChain.value(depth)); + } + + if (!errors.isEmpty()) { + sslErrors = errors; + emit q->sslErrors(errors); + + bool doEmitSslError; + if (!ignoreErrorsList.empty()) { + // check whether the errors we got are all in the list of expected errors + // (applies only if the method QSslSocket::ignoreSslErrors(const QList<QSslError> &errors) + // was called) + doEmitSslError = false; + for (int a = 0; a < errors.count(); a++) { + if (!ignoreErrorsList.contains(errors.at(a))) { + doEmitSslError = true; + break; + } + } + } else { + // if QSslSocket::ignoreSslErrors(const QList<QSslError> &errors) was not called and + // we get an SSL error, emit a signal unless we ignored all errors (by calling + // QSslSocket::ignoreSslErrors() ) + doEmitSslError = !ignoreAllSslErrors; + } + // check whether we need to emit an SSL handshake error + if (doVerifyPeer && doEmitSslError) { + q->setErrorString(sslErrors.first().errorString()); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); + emit q->error(QAbstractSocket::SslHandshakeFailedError); + plainSocket->disconnectFromHost(); + return false; + } + } else { + sslErrors.clear(); + } + + // if we have a max read buffer size, reset the plain socket's to 1k + if (readBufferMaxSize) + plainSocket->setReadBufferSize(1024); + + connectionEncrypted = true; + emit q->encrypted(); + if (autoStartHandshake && pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } + return true; +} + +void QSslSocketBackendPrivate::disconnectFromHost() +{ + if (ssl) { + q_SSL_shutdown(ssl); + transmit(); + } + plainSocket->disconnectFromHost(); +} + +void QSslSocketBackendPrivate::disconnected() +{ + if (ssl) { + q_SSL_free(ssl); + ssl = 0; + } + if (ctx) { + q_SSL_CTX_free(ctx); + ctx = 0; + } + if (pkey) { + q_EVP_PKEY_free(pkey); + pkey = 0; + } + +} + +QSslCipher QSslSocketBackendPrivate::sessionCipher() const +{ + if (!ssl || !ctx) + return QSslCipher(); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + // FIXME This is fairly evil, but needed to keep source level compatibility + // with the OpenSSL 0.9.x implementation at maximum -- some other functions + // don't take a const SSL_CIPHER* when they should + SSL_CIPHER *sessionCipher = const_cast<SSL_CIPHER *>(q_SSL_get_current_cipher(ssl)); +#else + SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); +#endif + return sessionCipher ? QSslCipher_from_SSL_CIPHER(sessionCipher) : QSslCipher(); +} + +QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509) +{ + ensureInitialized(); + QList<QSslCertificate> certificates; + for (int i = 0; i < q_sk_X509_num(x509); ++i) { + if (X509 *entry = q_sk_X509_value(x509, i)) + certificates << QSslCertificatePrivate::QSslCertificate_from_X509(entry); + } + return certificates; +} + +QString QSslSocketBackendPrivate::getErrorsFromOpenSsl() +{ + QString errorString; + unsigned long errNum; + while((errNum = q_ERR_get_error())) { + if (! errorString.isEmpty()) + errorString.append(QLatin1String(", ")); + const char *error = q_ERR_error_string(errNum, NULL); + errorString.append(QString::fromAscii(error)); // error is ascii according to man ERR_error_string + } + return errorString; +} + +bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname) +{ + int wildcard = cn.indexOf(QLatin1Char('*')); + + // Check this is a wildcard cert, if not then just compare the strings + if (wildcard < 0) + return cn == hostname; + + int firstCnDot = cn.indexOf(QLatin1Char('.')); + int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1); + + // Check at least 3 components + if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length())) + return false; + + // Check * is last character of 1st component (ie. there's a following .) + if (wildcard+1 != firstCnDot) + return false; + + // Check only one star + if (cn.lastIndexOf(QLatin1Char('*')) != wildcard) + return false; + + // Check characters preceding * (if any) match + if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard))) + return false; + + // Check characters following first . match + if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot)) + return false; + + // Check if the hostname is an IP address, if so then wildcards are not allowed + QHostAddress addr(hostname); + if (!addr.isNull()) + return false; + + // Ok, I guess this was a wildcard CN and the hostname matches. + return true; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h new file mode 100644 index 0000000000..ca49fabc13 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_OPENSSL_P_H +#define QSSLSOCKET_OPENSSL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslsocket_p.h" + +#ifdef Q_OS_WIN +#include <qt_windows.h> +#if defined(OCSP_RESPONSE) +#undef OCSP_RESPONSE +#endif +#endif + +#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/pkcs12.h> +#include <openssl/pkcs7.h> +#include <openssl/rand.h> +#include <openssl/ssl.h> +#include <openssl/stack.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#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; +#endif + +QT_BEGIN_NAMESPACE + +class QSslSocketBackendPrivate : public QSslSocketPrivate +{ + Q_DECLARE_PUBLIC(QSslSocket) +public: + QSslSocketBackendPrivate(); + virtual ~QSslSocketBackendPrivate(); + + // SSL context + bool initSslContext(); + SSL *ssl; + SSL_CTX *ctx; + EVP_PKEY *pkey; + BIO *readBio; + BIO *writeBio; + SSL_SESSION *session; + X509_STORE *certificateStore; + X509_STORE_CTX *certificateStoreCtx; + QList<QPair<int, int> > errorList; + + // Platform specific functions + void startClientEncryption(); + void startServerEncryption(); + void transmit(); + bool startHandshake(); + void disconnectFromHost(); + void disconnected(); + QSslCipher sessionCipher() const; + + static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); + static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); + Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); + static QString getErrorsFromOpenSsl(); +}; + +#if defined(Q_OS_SYMBIAN) + +#include <QByteArray> +#include <e32base.h> +#include <f32file.h> +#include <unifiedcertstore.h> // link against certstore.lib +#include <ccertattributefilter.h> // link against ctframework.lib + +// The purpose of this class is to wrap the asynchronous API of Symbian certificate store to one +// synchronizable call. The user of this class needs to provide a TRequestStatus object which can +// be used with User::WaitForRequest() unlike with the calls of the certificate store API. +// A thread is used instead of a CActiveSchedulerWait scheme, because that would make the call +// asynchronous (other events might be processed during the call even though the call would be seemingly +// synchronous). + +class CSymbianCertificateRetriever : public CActive +{ +public: + static CSymbianCertificateRetriever* NewL(); + ~CSymbianCertificateRetriever(); + + int GetCertificates(QList<QByteArray> &aCertificates); + +private: + void ConstructL(); + CSymbianCertificateRetriever(); + static TInt ThreadEntryPoint(TAny* aParams); + void doThreadEntryL(); + void GetCertificateL(); + void DoCancel(); + void RunL(); + TInt RunError(TInt aError); + +private: + enum { + Initializing, + Listing, + RetrievingCertificates + } iState; + + RThread iThread; + CUnifiedCertStore* iCertStore; + RMPointerArray<CCTCertInfo> iCertInfos; + CCertAttributeFilter* iCertFilter; + TInt iCurrentCertIndex; + QByteArray iCertificateData; + TPtr8 iCertificatePtr; + QList<QByteArray>* iCertificates; + TInt iSequenceError; +}; + + +#endif + + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp new file mode 100644 index 0000000000..b1310ccb8d --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -0,0 +1,888 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslsocket_openssl_symbols_p.h" + +#ifdef Q_OS_WIN +# include <private/qsystemlibrary_p.h> +#else +# include <QtCore/qlibrary.h> +#endif +#include <QtCore/qmutex.h> +#include <private/qmutexpool_p.h> +#include <QtCore/qdatetime.h> +#if defined(Q_OS_UNIX) +#include <QtCore/qdir.h> +#endif + +QT_BEGIN_NAMESPACE + +/* + Note to maintainer: + ------------------- + + We load OpenSSL symbols dynamically. Because symbols are known to + disappear, and signatures sometimes change, between releases, we need to + be careful about how this is done. To ensure we don't end up dereferencing + null function pointers, and continue running even if certain functions are + missing, we define helper functions for each of the symbols we load from + OpenSSL, all prefixed with "q_" (declared in + qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect + directly, we call q_SSL_connect, which is a function that checks if the + actual SSL_connect fptr is null, and returns a failure if it is, or calls + SSL_connect if it isn't. + + This requires a somewhat tedious process of declaring each function we + want to call in OpenSSL thrice: once with the q_, in _p.h, once using the + DEFINEFUNC macros below, and once in the function that actually resolves + the symbols, below the DEFINEFUNC declarations below. + + There's one DEFINEFUNC macro declared for every number of arguments + exposed by OpenSSL (feel free to extend when needed). The easiest thing to + do is to find an existing entry that matches the arg count of the function + you want to import, and do the same. + + The first macro arg is the function return type. The second is the + verbatim name of the function/symbol. Then follows a list of N pairs of + argument types with a variable name, and just the variable name (char *a, + a, char *b, b, etc). Finally there's two arguments - a suitable return + statement for the error case (for an int function, return 0 or return -1 + is usually right). Then either just "return" or DUMMYARG, the latter being + for void functions. + + Note: Take into account that these macros and declarations are processed + at compile-time, and the result depends on the OpenSSL headers the + compiling host has installed, but the symbols are resolved at run-time, + possibly with a different version of OpenSSL. +*/ + +#ifdef SSLEAY_MACROS +DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return 0, return) +#endif +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) +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) +DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return 0, return) +DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return) +DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return) +DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return) +DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return) +DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG) +DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG) +DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG) +DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG) +#if OPENSSL_VERSION_NUMBER < 0x00908000L +DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, unsigned char **b, b, long c, c, return 0, return) +#else // 0.9.8 broke SC and BC by changing this signature. +DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return 0, return) +#endif +DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, return) +DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return) +DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return) +DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return) +DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG) +DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return 0, return) +DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return 0, return) +DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return) +DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return 0, return) +DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return) +#ifdef SSLEAY_MACROS +DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) +DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) +#else +DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) +DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) +#endif +DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) +DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return) +DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return) +DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG) +DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return) +DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) +DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) +DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG) +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG) +DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return) +#else +DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG) +DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) +#endif +DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return) +DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return) +DEFINEFUNC3(char *, SSL_CIPHER_description, SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return) +DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return) +#else +DEFINEFUNC(int, SSL_CTX_check_private_key, SSL_CTX *a, a, return -1, return) +#endif +DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return) +DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG) +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return 0, return) +#else +DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return 0, return) +#endif +DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return) +DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return) +DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG) +DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG) +DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return) +DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) +DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return) +DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return) +DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return) +DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return 0, return) +#else +DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, SSL *a, a, return 0, return) +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return) +#else +DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return) +#endif +DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return) +DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return 0, return) +DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) +#else +DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return) +#endif +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) +DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG) +DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return) +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) +#else +DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return) +DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return) +#ifndef SSLEAY_MACROS +DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return 0, return) +#endif +DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return 0, return) +DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG) +DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return 0, return) +DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return) +DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return 0, return) +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(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) +DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return) +DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG) +DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return) +DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return) +DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return 0, return) +#ifdef SSLEAY_MACROS +DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return) +DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return) +#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) \ + if (!(_q_##func = _q_PTR_##func(lib->resolve(#ordinal)))) \ + qWarning("QSslSocket: cannot resolve "#func); +#else +#define RESOLVEFUNC(func) \ + if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \ + && !(_q_##func = _q_PTR_##func(libs.second->resolve(#func)))) \ + qWarning("QSslSocket: cannot resolve "#func); +#endif + +#if !defined QT_LINKED_OPENSSL + +#ifdef QT_NO_LIBRARY +bool q_resolveOpenSslSymbols() +{ + qWarning("QSslSocket: unable to resolve symbols. " + "QT_NO_LIBRARY is defined which means runtime resolving of " + "libraries won't work."); + qWarning("Either compile Qt statically or with support for runtime resolving " + "of libraries."); + return false; +} +#else + +# ifdef Q_OS_UNIX +static bool libGreaterThan(const QString &lhs, const QString &rhs) +{ + QStringList lhsparts = lhs.split(QLatin1Char('.')); + QStringList rhsparts = rhs.split(QLatin1Char('.')); + Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1); + + for (int i = 1; i < rhsparts.count(); ++i) { + if (lhsparts.count() <= i) + // left hand side is shorter, so it's less than rhs + return false; + + bool ok = false; + int b = 0; + int a = lhsparts.at(i).toInt(&ok); + if (ok) + b = rhsparts.at(i).toInt(&ok); + if (ok) { + // both toInt succeeded + if (a == b) + continue; + return a > b; + } else { + // compare as strings; + if (lhsparts.at(i) == rhsparts.at(i)) + continue; + return lhsparts.at(i) > rhsparts.at(i); + } + } + + // they compared strictly equally so far + // lhs cannot be less than rhs + return true; +} + +static QStringList findAllLibSsl() +{ + QStringList paths; +# ifdef Q_OS_DARWIN + paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH")) + .split(QLatin1Char(':'), QString::SkipEmptyParts); +# else + paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH")) + .split(QLatin1Char(':'), QString::SkipEmptyParts); +# endif + paths << QLatin1String("/lib") << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib"); + + QStringList foundSsls; + foreach (const QString &path, paths) { + QDir dir = QDir(path); + QStringList entryList = dir.entryList(QStringList() << QLatin1String("libssl.*"), QDir::Files); + + qSort(entryList.begin(), entryList.end(), libGreaterThan); + foreach (const QString &entry, entryList) + foundSsls << path + QLatin1Char('/') + entry; + } + + return foundSsls; +} +# endif + +#ifdef Q_OS_WIN +static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32() +{ + QPair<QSystemLibrary*,QSystemLibrary*> pair; + pair.first = 0; + pair.second = 0; + + QSystemLibrary *ssleay32 = new QSystemLibrary(QLatin1String("ssleay32")); + if (!ssleay32->load(false)) { + // Cannot find ssleay32.dll + delete ssleay32; + return pair; + } + + QSystemLibrary *libeay32 = new QSystemLibrary(QLatin1String("libeay32")); + if (!libeay32->load(false)) { + delete ssleay32; + delete libeay32; + return pair; + } + + pair.first = ssleay32; + pair.second = libeay32; + return pair; +} +#else + +static QPair<QLibrary*, QLibrary*> loadOpenSsl() +{ + QPair<QLibrary*,QLibrary*> pair; + pair.first = 0; + pair.second = 0; + +# if defined(Q_OS_SYMBIAN) + QLibrary *libssl = new QLibrary(QLatin1String("libssl")); + if (!libssl->load()) { + // Cannot find ssleay32.dll + delete libssl; + return pair; + } + + QLibrary *libcrypto = new QLibrary(QLatin1String("libcrypto")); + if (!libcrypto->load()) { + delete libcrypto; + delete libssl; + return pair; + } + + pair.first = libssl; + pair.second = libcrypto; + return pair; +# elif defined(Q_OS_UNIX) + QLibrary *&libssl = pair.first; + QLibrary *&libcrypto = pair.second; + libssl = new QLibrary; + libcrypto = new QLibrary; + + // Try to find the libssl library on the system. + // + // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that + // is, libssl.so on most Unix systems. However, the .so file isn't present in + // user installations because it's considered a development file. + // + // The right thing to do is to load the library at the major version we know how + // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h) + // + // However, OpenSSL is a well-known case of binary-compatibility breakage. To + // avoid such problems, many system integrators and Linux distributions change + // the soname of the binary, letting the full version number be the soname. So + // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that + // reason, we will search a few common paths (see findAllLibSsl() above) in hopes + // we find one that works. + // + // It is important, however, to try the canonical name and the unversioned name + // without going through the loop. By not specifying a path, we let the system + // dlopen(3) function determine it for us. This will include any DT_RUNPATH or + // DT_RPATH tags on our library header as well as other system-specific search + // paths. See the man page for dlopen(3) on your system for more information. + +#ifdef Q_OS_OPENBSD + libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint); +#endif +#ifdef SHLIB_VERSION_NUMBER + // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER> + libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER)); + libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER)); + if (libcrypto->load() && libssl->load()) { + // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found + return pair; + } else { + libssl->unload(); + libcrypto->unload(); + } +#endif + + // second attempt: find the development files libssl.so and libcrypto.so + libssl->setFileNameAndVersion(QLatin1String("ssl"), -1); + libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1); + if (libcrypto->load() && libssl->load()) { + // libssl.so.0 and libcrypto.so.0 found + return pair; + } else { + libssl->unload(); + libcrypto->unload(); + } + + // third attempt: loop on the most common library paths and find libssl + QStringList sslList = findAllLibSsl(); + foreach (const QString &ssl, sslList) { + QString crypto = ssl; + crypto.replace(QLatin1String("ssl"), QLatin1String("crypto")); + libssl->setFileNameAndVersion(ssl, -1); + libcrypto->setFileNameAndVersion(crypto, -1); + if (libcrypto->load() && libssl->load()) { + // libssl.so.0 and libcrypto.so.0 found + return pair; + } else { + libssl->unload(); + libcrypto->unload(); + } + } + + // failed to load anything + delete libssl; + delete libcrypto; + libssl = libcrypto = 0; + return pair; + +# else + // not implemented for this platform yet + return pair; +# endif +} +#endif + +bool q_resolveOpenSslSymbols() +{ + static volatile bool symbolsResolved = false; + static volatile bool triedToResolveSymbols = false; +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_SSL_library_init)); +#endif + if (symbolsResolved) + return true; + if (triedToResolveSymbols) + return false; + triedToResolveSymbols = true; + +#ifdef Q_OS_WIN + QPair<QSystemLibrary *, QSystemLibrary *> libs = loadOpenSslWin32(); +#else + QPair<QLibrary *, QLibrary *> libs = loadOpenSsl(); +#endif + if (!libs.first || !libs.second) + // failed to load them + return false; + +#ifdef Q_OS_SYMBIAN +#ifdef SSLEAY_MACROS + RESOLVEFUNC(ASN1_dup, 125, libs.second ) +#endif + RESOLVEFUNC(ASN1_INTEGER_get, 48, libs.second ) + RESOLVEFUNC(ASN1_STRING_data, 71, libs.second ) + RESOLVEFUNC(ASN1_STRING_length, 76, libs.second ) + RESOLVEFUNC(BIO_ctrl, 184, libs.second ) + RESOLVEFUNC(BIO_free, 209, libs.second ) + RESOLVEFUNC(BIO_new, 222, libs.second ) + RESOLVEFUNC(BIO_new_mem_buf, 230, libs.second ) + RESOLVEFUNC(BIO_read, 244, libs.second ) + RESOLVEFUNC(BIO_s_mem, 251, libs.second ) + RESOLVEFUNC(BIO_write, 269, libs.second ) + RESOLVEFUNC(BN_num_bits, 387, libs.second ) + RESOLVEFUNC(CRYPTO_free, 469, libs.second ) + RESOLVEFUNC(CRYPTO_num_locks, 500, libs.second ) + RESOLVEFUNC(CRYPTO_set_id_callback, 513, libs.second ) + RESOLVEFUNC(CRYPTO_set_locking_callback, 516, libs.second ) + RESOLVEFUNC(DSA_free, 594, libs.second ) + RESOLVEFUNC(ERR_error_string, 744, libs.second ) + RESOLVEFUNC(ERR_get_error, 749, libs.second ) + RESOLVEFUNC(EVP_des_ede3_cbc, 919, libs.second ) + RESOLVEFUNC(EVP_PKEY_assign, 859, libs.second ) + RESOLVEFUNC(EVP_PKEY_set1_RSA, 880, libs.second ) + RESOLVEFUNC(EVP_PKEY_set1_DSA, 879, libs.second ) + RESOLVEFUNC(EVP_PKEY_free, 867, libs.second ) + RESOLVEFUNC(EVP_PKEY_get1_DSA, 869, libs.second ) + RESOLVEFUNC(EVP_PKEY_get1_RSA, 870, libs.second ) + RESOLVEFUNC(EVP_PKEY_new, 876, libs.second ) + RESOLVEFUNC(EVP_PKEY_type, 882, libs.second ) + RESOLVEFUNC(OBJ_nid2sn, 1036, libs.second ) + RESOLVEFUNC(OBJ_obj2nid, 1037, libs.second ) +#ifdef SSLEAY_MACROS // ### verify + RESOLVEFUNC(PEM_ASN1_read_bio, 1180, libs.second ) +#else + RESOLVEFUNC(PEM_read_bio_DSAPrivateKey, 1219, libs.second ) + RESOLVEFUNC(PEM_read_bio_RSAPrivateKey, 1228, libs.second ) + RESOLVEFUNC(PEM_write_bio_DSAPrivateKey, 1260, libs.second ) + RESOLVEFUNC(PEM_write_bio_RSAPrivateKey, 1271, libs.second ) +#endif + RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY, 1220, libs.second ) + RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY, 1230, libs.second ) + RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY, 1261, libs.second ) + RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY, 1273, libs.second ) + RESOLVEFUNC(RAND_seed, 1426, libs.second ) + RESOLVEFUNC(RAND_status, 1429, libs.second ) + RESOLVEFUNC(RSA_free, 1450, libs.second ) + RESOLVEFUNC(sk_free, 2571, libs.second ) + RESOLVEFUNC(sk_num, 2576, libs.second ) + RESOLVEFUNC(sk_pop_free, 2578, libs.second ) + RESOLVEFUNC(sk_value, 2585, libs.second ) + RESOLVEFUNC(SSL_CIPHER_description, 11, libs.first ) + RESOLVEFUNC(SSL_CTX_check_private_key, 21, libs.first ) + RESOLVEFUNC(SSL_CTX_ctrl, 22, libs.first ) + RESOLVEFUNC(SSL_CTX_free, 24, libs.first ) + RESOLVEFUNC(SSL_CTX_new, 35, libs.first ) + RESOLVEFUNC(SSL_CTX_set_cipher_list, 40, libs.first ) + RESOLVEFUNC(SSL_CTX_set_default_verify_paths, 44, libs.first ) + RESOLVEFUNC(SSL_CTX_set_verify, 56, libs.first ) + RESOLVEFUNC(SSL_CTX_set_verify_depth, 57, libs.first ) + RESOLVEFUNC(SSL_CTX_use_certificate, 64, libs.first ) + RESOLVEFUNC(SSL_CTX_use_certificate_file, 67, libs.first ) + RESOLVEFUNC(SSL_CTX_use_PrivateKey, 58, libs.first ) + RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey, 61, libs.first ) + RESOLVEFUNC(SSL_CTX_use_PrivateKey_file, 60, libs.first ) + RESOLVEFUNC(SSL_accept, 82, libs.first ) + RESOLVEFUNC(SSL_clear, 92, libs.first ) + RESOLVEFUNC(SSL_connect, 93, libs.first ) + RESOLVEFUNC(SSL_free, 99, libs.first ) + RESOLVEFUNC(SSL_get_ciphers, 104, libs.first ) + RESOLVEFUNC(SSL_get_current_cipher, 106, libs.first ) + RESOLVEFUNC(SSL_get_error, 110, libs.first ) + RESOLVEFUNC(SSL_get_peer_cert_chain, 117, libs.first ) + RESOLVEFUNC(SSL_get_peer_certificate, 118, libs.first ) + RESOLVEFUNC(SSL_get_verify_result, 132, libs.first ) + 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 ) + RESOLVEFUNC(SSL_set_connect_state, 152, libs.first ) + RESOLVEFUNC(SSL_shutdown, 173, libs.first ) + RESOLVEFUNC(SSL_write, 188, libs.first ) + RESOLVEFUNC(SSLv2_client_method, 192, libs.first ) + RESOLVEFUNC(SSLv3_client_method, 195, libs.first ) + RESOLVEFUNC(SSLv23_client_method, 189, libs.first ) + RESOLVEFUNC(TLSv1_client_method, 198, libs.first ) + RESOLVEFUNC(SSLv2_server_method, 194, libs.first ) + RESOLVEFUNC(SSLv3_server_method, 197, libs.first ) + RESOLVEFUNC(SSLv23_server_method, 191, libs.first ) + RESOLVEFUNC(TLSv1_server_method, 200, libs.first ) + RESOLVEFUNC(SSL_CTX_load_verify_locations, 34, libs.first ) + RESOLVEFUNC(X509_NAME_oneline, 1830, libs.second ) + RESOLVEFUNC(X509_PUBKEY_get, 1844, libs.second ) + RESOLVEFUNC(X509_STORE_free, 1939, libs.second ) + RESOLVEFUNC(X509_STORE_new, 1942, libs.second ) + RESOLVEFUNC(X509_STORE_add_cert, 1936, libs.second ) + RESOLVEFUNC(X509_STORE_CTX_free, 1907, libs.second ) + RESOLVEFUNC(X509_STORE_CTX_init, 1919, libs.second ) + RESOLVEFUNC(X509_STORE_CTX_new, 1920, libs.second ) + RESOLVEFUNC(X509_STORE_CTX_set_purpose, 1931, libs.second ) + RESOLVEFUNC(X509_cmp, 1992, libs.second ) +#ifndef SSLEAY_MACROS + RESOLVEFUNC(X509_dup, 1997, libs.second ) +#endif + RESOLVEFUNC(X509_EXTENSION_get_object, 1785, libs.second ) + RESOLVEFUNC(X509_free, 2001, libs.second ) + RESOLVEFUNC(X509_get_ext, 2012, libs.second ) + RESOLVEFUNC(X509_get_ext_count, 2016, libs.second ) + RESOLVEFUNC(X509_get_ext_d2i, 2017, libs.second ) + RESOLVEFUNC(X509_get_issuer_name, 2018, libs.second ) + RESOLVEFUNC(X509_get_subject_name, 2022, libs.second ) + RESOLVEFUNC(X509_verify_cert, 2069, libs.second ) + RESOLVEFUNC(d2i_X509, 2309, libs.second ) + RESOLVEFUNC(i2d_X509, 2489, libs.second ) +#ifdef SSLEAY_MACROS + RESOLVEFUNC(i2d_DSAPrivateKey, 2395, libs.second ) + RESOLVEFUNC(i2d_RSAPrivateKey, 2476, libs.second ) + RESOLVEFUNC(d2i_DSAPrivateKey, 2220, libs.second ) + RESOLVEFUNC(d2i_RSAPrivateKey, 2296, libs.second ) +#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) +#endif + RESOLVEFUNC(ASN1_INTEGER_get) + RESOLVEFUNC(ASN1_STRING_data) + RESOLVEFUNC(ASN1_STRING_length) + RESOLVEFUNC(BIO_ctrl) + RESOLVEFUNC(BIO_free) + RESOLVEFUNC(BIO_new) + RESOLVEFUNC(BIO_new_mem_buf) + RESOLVEFUNC(BIO_read) + RESOLVEFUNC(BIO_s_mem) + RESOLVEFUNC(BIO_write) + RESOLVEFUNC(BN_num_bits) + RESOLVEFUNC(CRYPTO_free) + RESOLVEFUNC(CRYPTO_num_locks) + RESOLVEFUNC(CRYPTO_set_id_callback) + RESOLVEFUNC(CRYPTO_set_locking_callback) + RESOLVEFUNC(DSA_free) + RESOLVEFUNC(ERR_error_string) + RESOLVEFUNC(ERR_get_error) + RESOLVEFUNC(EVP_des_ede3_cbc) + RESOLVEFUNC(EVP_PKEY_assign) + RESOLVEFUNC(EVP_PKEY_set1_RSA) + RESOLVEFUNC(EVP_PKEY_set1_DSA) + RESOLVEFUNC(EVP_PKEY_free) + RESOLVEFUNC(EVP_PKEY_get1_DSA) + RESOLVEFUNC(EVP_PKEY_get1_RSA) + RESOLVEFUNC(EVP_PKEY_new) + RESOLVEFUNC(EVP_PKEY_type) + RESOLVEFUNC(OBJ_nid2sn) + RESOLVEFUNC(OBJ_obj2nid) +#ifdef SSLEAY_MACROS // ### verify + RESOLVEFUNC(PEM_ASN1_read_bio) +#else + RESOLVEFUNC(PEM_read_bio_DSAPrivateKey) + RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) + RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) + RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) +#endif + RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY) + RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) + RESOLVEFUNC(RAND_seed) + RESOLVEFUNC(RAND_status) + RESOLVEFUNC(RSA_free) + RESOLVEFUNC(sk_free) + RESOLVEFUNC(sk_num) + RESOLVEFUNC(sk_pop_free) + RESOLVEFUNC(sk_value) + RESOLVEFUNC(SSL_CIPHER_description) + RESOLVEFUNC(SSL_CTX_check_private_key) + RESOLVEFUNC(SSL_CTX_ctrl) + RESOLVEFUNC(SSL_CTX_free) + RESOLVEFUNC(SSL_CTX_new) + RESOLVEFUNC(SSL_CTX_set_cipher_list) + RESOLVEFUNC(SSL_CTX_set_default_verify_paths) + RESOLVEFUNC(SSL_CTX_set_verify) + RESOLVEFUNC(SSL_CTX_set_verify_depth) + RESOLVEFUNC(SSL_CTX_use_certificate) + RESOLVEFUNC(SSL_CTX_use_certificate_file) + RESOLVEFUNC(SSL_CTX_use_PrivateKey) + RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey) + RESOLVEFUNC(SSL_CTX_use_PrivateKey_file) + RESOLVEFUNC(SSL_accept) + RESOLVEFUNC(SSL_clear) + RESOLVEFUNC(SSL_connect) + RESOLVEFUNC(SSL_free) + RESOLVEFUNC(SSL_get_ciphers) + RESOLVEFUNC(SSL_get_current_cipher) + RESOLVEFUNC(SSL_get_error) + RESOLVEFUNC(SSL_get_peer_cert_chain) + RESOLVEFUNC(SSL_get_peer_certificate) + RESOLVEFUNC(SSL_get_verify_result) + 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) + RESOLVEFUNC(SSL_set_connect_state) + RESOLVEFUNC(SSL_shutdown) + RESOLVEFUNC(SSL_write) + RESOLVEFUNC(SSLv2_client_method) + RESOLVEFUNC(SSLv3_client_method) + RESOLVEFUNC(SSLv23_client_method) + RESOLVEFUNC(TLSv1_client_method) + RESOLVEFUNC(SSLv2_server_method) + RESOLVEFUNC(SSLv3_server_method) + RESOLVEFUNC(SSLv23_server_method) + RESOLVEFUNC(TLSv1_server_method) + RESOLVEFUNC(X509_NAME_oneline) + RESOLVEFUNC(X509_PUBKEY_get) + RESOLVEFUNC(X509_STORE_free) + RESOLVEFUNC(X509_STORE_new) + RESOLVEFUNC(X509_STORE_add_cert) + RESOLVEFUNC(X509_STORE_CTX_free) + RESOLVEFUNC(X509_STORE_CTX_init) + RESOLVEFUNC(X509_STORE_CTX_new) + RESOLVEFUNC(X509_STORE_CTX_set_purpose) + RESOLVEFUNC(X509_cmp) +#ifndef SSLEAY_MACROS + RESOLVEFUNC(X509_dup) +#endif + RESOLVEFUNC(X509_EXTENSION_get_object) + RESOLVEFUNC(X509_free) + RESOLVEFUNC(X509_get_ext) + RESOLVEFUNC(X509_get_ext_count) + RESOLVEFUNC(X509_get_ext_d2i) + RESOLVEFUNC(X509_get_issuer_name) + RESOLVEFUNC(X509_get_subject_name) + RESOLVEFUNC(X509_verify_cert) + RESOLVEFUNC(d2i_X509) + RESOLVEFUNC(i2d_X509) +#ifdef SSLEAY_MACROS + RESOLVEFUNC(i2d_DSAPrivateKey) + RESOLVEFUNC(i2d_RSAPrivateKey) + RESOLVEFUNC(d2i_DSAPrivateKey) + RESOLVEFUNC(d2i_RSAPrivateKey) +#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; + delete libs.second; + return true; +} +#endif // QT_NO_LIBRARY + +#else // !defined QT_LINKED_OPENSSL + +bool q_resolveOpenSslSymbols() +{ +#ifdef QT_NO_OPENSSL + return false; +#endif + return true; +} +#endif // !defined QT_LINKED_OPENSSL + +//============================================================================== +// contributed by Jay Case of Sarvega, Inc.; http://sarvega.com/ +// Based on X509_cmp_time() for intitial buffer hacking. +//============================================================================== +QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime) +{ + size_t lTimeLength = aTime->length; + char *pString = (char *) aTime->data; + + if (aTime->type == V_ASN1_UTCTIME) { + + char lBuffer[24]; + char *pBuffer = lBuffer; + + if ((lTimeLength < 11) || (lTimeLength > 17)) + return QDateTime(); + + memcpy(pBuffer, pString, 10); + pBuffer += 10; + pString += 10; + + if ((*pString == 'Z') || (*pString == '-') || (*pString == '+')) { + *pBuffer++ = '0'; + *pBuffer++ = '0'; + } else { + *pBuffer++ = *pString++; + *pBuffer++ = *pString++; + // Skip any fractional seconds... + if (*pString == '.') { + pString++; + while ((*pString >= '0') && (*pString <= '9')) + pString++; + } + } + + *pBuffer++ = 'Z'; + *pBuffer++ = '\0'; + + time_t lSecondsFromUCT; + if (*pString == 'Z') { + lSecondsFromUCT = 0; + } else { + if ((*pString != '+') && (*pString != '-')) + return QDateTime(); + + lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60; + lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0'); + lSecondsFromUCT *= 60; + if (*pString == '-') + lSecondsFromUCT = -lSecondsFromUCT; + } + + tm lTime; + lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0'); + lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0'); + lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0'); + lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0'); + lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1; + lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0'); + if (lTime.tm_year < 50) + lTime.tm_year += 100; // RFC 2459 + + QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday); + QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec); + + QDateTime result(resDate, resTime, Qt::UTC); + result = result.addSecs(lSecondsFromUCT); + return result; + + } else if (aTime->type == V_ASN1_GENERALIZEDTIME) { + + if (lTimeLength < 15) + return QDateTime(); // hopefully never triggered + + // generalized time is always YYYYMMDDHHMMSSZ (RFC 2459, section 4.1.2.5.2) + tm lTime; + lTime.tm_sec = ((pString[12] - '0') * 10) + (pString[13] - '0'); + lTime.tm_min = ((pString[10] - '0') * 10) + (pString[11] - '0'); + lTime.tm_hour = ((pString[8] - '0') * 10) + (pString[9] - '0'); + lTime.tm_mday = ((pString[6] - '0') * 10) + (pString[7] - '0'); + lTime.tm_mon = (((pString[4] - '0') * 10) + (pString[5] - '0')); + lTime.tm_year = ((pString[0] - '0') * 1000) + ((pString[1] - '0') * 100) + + ((pString[2] - '0') * 10) + (pString[3] - '0'); + + QDate resDate(lTime.tm_year, lTime.tm_mon, lTime.tm_mday); + QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec); + + QDateTime result(resDate, resTime, Qt::UTC); + return result; + + } else { + qWarning("unsupported date format detected"); + return QDateTime(); + } + +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h new file mode 100644 index 0000000000..49830acc1e --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -0,0 +1,427 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H +#define QSSLSOCKET_OPENSSL_SYMBOLS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qsslsocket_openssl_p.h" + +QT_BEGIN_NAMESPACE + +#define DUMMYARG + +#if !defined QT_LINKED_OPENSSL +// **************** Shared declarations ****************** +// ret func(arg) + +# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a); \ + } + +// ret func(arg1, arg2) +# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func);\ + err; \ + } \ + funcret _q_##func(a, b); \ + } + +// ret func(arg1, arg2, arg3) +# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c); \ + } + +// ret func(arg1, arg2, arg3, arg4) +# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg5) +# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg6) +# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e, f); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7) +# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \ + if (!_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e, f, g); \ + } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9) +# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \ + typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \ + static _q_PTR_##func _q_##func = 0; \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \ + if (_q_##func) { \ + qWarning("QSslSocket: cannot call unresolved function "#func); \ + err; \ + } \ + funcret _q_##func(a, b, c, d, e, f, g, h, i); \ + } +// **************** Shared declarations ****************** + +#else // !defined QT_LINKED_OPENSSL + +// **************** Static declarations ****************** + +// ret func(arg) +# define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + ret q_##func(arg) { funcret func(a); } + +// ret func(arg1, arg2) +# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \ + ret q_##func(arg1, arg2) { funcret func(a, b); } + +// ret func(arg1, arg2, arg3) +# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ + ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); } + +// ret func(arg1, arg2, arg3, arg4) +# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); } + +// ret func(arg1, arg2, arg3, arg4, arg5) +# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); } + +// ret func(arg1, arg2, arg3, arg4, arg6) +# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7) +# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); } + +// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9) +# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \ + ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); } + +// **************** Static declarations ****************** + +#endif // !defined QT_LINKED_OPENSSL + +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); +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); +BIO *q_BIO_new_mem_buf(void *a, int b); +int q_BIO_read(BIO *a, void *b, int c); +BIO_METHOD *q_BIO_s_mem(); +int q_BIO_write(BIO *a, const void *b, int c); +int q_BN_num_bits(const BIGNUM *a); +int q_CRYPTO_num_locks(); +void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int)); +void q_CRYPTO_set_id_callback(unsigned long (*a)()); +void q_CRYPTO_free(void *a); +void q_DSA_free(DSA *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c); +#else +X509 *q_d2i_X509(X509 **a, unsigned char **b, long c); +#endif +char *q_ERR_error_string(unsigned long a, char *b); +unsigned long q_ERR_get_error(); +const EVP_CIPHER *q_EVP_des_ede3_cbc(); +int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c); +int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b); +int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); +void q_EVP_PKEY_free(EVP_PKEY *a); +RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a); +DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a); +int q_EVP_PKEY_type(int a); +EVP_PKEY *q_EVP_PKEY_new(); +int q_i2d_X509(X509 *a, unsigned char **b); +const char *q_OBJ_nid2sn(int a); +int q_OBJ_obj2nid(const ASN1_OBJECT *a); +#ifdef SSLEAY_MACROS +// ### verify +void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_password_cb *e, + void *f); +// ### ditto for write +#else +DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d); +RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d); +int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d, + int e, pem_password_cb *f, void *g); +int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d, + int e, pem_password_cb *f, void *g); +#endif +DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d); +RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d); +int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b); +int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b); +void q_RAND_seed(const void *a, int b); +int q_RAND_status(); +void q_RSA_free(RSA *a); +int q_sk_num(STACK *a); +void q_sk_pop_free(STACK *a, void (*b)(void *)); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +void q_sk_free(_STACK *a); +void * q_sk_value(STACK *a, int b); +#else +void q_sk_free(STACK *a); +char * q_sk_value(STACK *a, int b); +#endif +int q_SSL_accept(SSL *a); +int q_SSL_clear(SSL *a); +char *q_SSL_CIPHER_description(SSL_CIPHER *a, char *b, int c); +int q_SSL_connect(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +int q_SSL_CTX_check_private_key(const SSL_CTX *a); +#else +int q_SSL_CTX_check_private_key(SSL_CTX *a); +#endif +long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d); +void q_SSL_CTX_free(SSL_CTX *a); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a); +#else +SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a); +#endif +int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b); +int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a); +void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *)); +void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b); +int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b); +int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c); +int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b); +int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b); +int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c); +void q_SSL_free(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a); +#else +STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(SSL *a); +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); +#else +SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); +#endif +int q_SSL_get_error(SSL *a, int b); +STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); +X509 *q_SSL_get_peer_certificate(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +// 0.9.8 broke SC and BC by changing this function's signature. +long q_SSL_get_verify_result(const SSL *a); +#else +long q_SSL_get_verify_result(SSL *a); +#endif +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); +void q_SSL_set_connect_state(SSL *a); +int q_SSL_shutdown(SSL *a); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +const SSL_METHOD *q_SSLv2_client_method(); +const SSL_METHOD *q_SSLv3_client_method(); +const SSL_METHOD *q_SSLv23_client_method(); +const SSL_METHOD *q_TLSv1_client_method(); +const SSL_METHOD *q_SSLv2_server_method(); +const SSL_METHOD *q_SSLv3_server_method(); +const SSL_METHOD *q_SSLv23_server_method(); +const SSL_METHOD *q_TLSv1_server_method(); +#else +SSL_METHOD *q_SSLv2_client_method(); +SSL_METHOD *q_SSLv3_client_method(); +SSL_METHOD *q_SSLv23_client_method(); +SSL_METHOD *q_TLSv1_client_method(); +SSL_METHOD *q_SSLv2_server_method(); +SSL_METHOD *q_SSLv3_server_method(); +SSL_METHOD *q_SSLv23_server_method(); +SSL_METHOD *q_TLSv1_server_method(); +#endif +int q_SSL_write(SSL *a, const void *b, int c); +int q_X509_cmp(X509 *a, X509 *b); +#ifdef SSLEAY_MACROS +void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x); +#define q_X509_dup(x509) (X509 *)q_ASN1_dup((i2d_of_void *)q_i2d_X509, \ + (d2i_of_void *)q_d2i_X509,(char *)x509) +#else +X509 *q_X509_dup(X509 *a); +#endif +ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a); +void q_X509_free(X509 *a); +X509_EXTENSION *q_X509_get_ext(X509 *a, int b); +int q_X509_get_ext_count(X509 *a); +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); +EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a); +void q_X509_STORE_free(X509_STORE *store); +X509_STORE *q_X509_STORE_new(); +int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x); +void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx); +int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, + X509 *x509, STACK_OF(X509) *chain); +X509_STORE_CTX *q_X509_STORE_CTX_new(); +int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose); + +#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) +#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) +#ifdef SSLEAY_MACROS +int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); +int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp); +RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length); +DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); +#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \ + (RSA *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u) +#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \ + (DSA *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u) +#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#endif +#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL) +#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st) +#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i) +#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st)) +#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i)) +#define q_sk_X509_num(st) q_SKM_sk_num(X509, (st)) +#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i)) +#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num(SSL_CIPHER, (st)) +#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i)) +#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \ + q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509) +#define q_X509_get_notAfter(x) X509_get_notAfter(x) +#define q_X509_get_notBefore(x) X509_get_notBefore(x) +#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\ + (char *)(rsa)) +#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\ + (char *)(dsa)) +#ifdef OPENSSL_LOAD_CONF +#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf() +#else +#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_noconf() +#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; +QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime); + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h new file mode 100644 index 0000000000..4662c56ec4 --- /dev/null +++ b/src/network/ssl/qsslsocket_p.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_P_H +#define QSSLSOCKET_P_H + +#include "qsslsocket.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtcpsocket_p.h> +#include "qsslkey.h" +#include "qsslconfiguration_p.h" + +#include <QtCore/qstringlist.h> + +#include <private/qringbuffer_p.h> + +QT_BEGIN_NAMESPACE + +#if defined(Q_OS_MAC) +#include <Security/SecCertificate.h> +#include <CoreFoundation/CFArray.h> + typedef OSStatus (*PtrSecCertificateGetData)(SecCertificateRef, CSSM_DATA_PTR); + typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*); + typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*); +#elif defined(Q_OS_WIN) +#include <windows.h> +#include <wincrypt.h> +#ifndef HCRYPTPROV_LEGACY +#define HCRYPTPROV_LEGACY HCRYPTPROV +#endif +#if defined(Q_OS_WINCE) + typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(LPCSTR, DWORD, HCRYPTPROV_LEGACY, DWORD, const void*); +#else + typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(HCRYPTPROV_LEGACY, LPCWSTR); +#endif + typedef PCCERT_CONTEXT (WINAPI *PtrCertFindCertificateInStore)(HCERTSTORE, DWORD, DWORD, DWORD, const void*, PCCERT_CONTEXT); + typedef BOOL (WINAPI *PtrCertCloseStore)(HCERTSTORE, DWORD); +#endif + + + +class QSslSocketPrivate : public QTcpSocketPrivate +{ + Q_DECLARE_PUBLIC(QSslSocket) +public: + QSslSocketPrivate(); + virtual ~QSslSocketPrivate(); + + void init(); + bool initialized; + + QSslSocket::SslMode mode; + bool autoStartHandshake; + bool connectionEncrypted; + bool ignoreAllSslErrors; + QList<QSslError> ignoreErrorsList; + bool* readyReadEmittedPointer; + + QSslConfigurationPrivate configuration; + QList<QSslError> sslErrors; + + // if set, this hostname is used for certificate validation instead of the hostname + // that was used for connecting to. + QString verificationPeerName; + + bool allowRootCertOnDemandLoading; + + static bool supportsSsl(); + static void ensureInitialized(); + static void deinitialize(); + static QList<QSslCipher> defaultCiphers(); + static QList<QSslCipher> supportedCiphers(); + static void setDefaultCiphers(const QList<QSslCipher> &ciphers); + static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers); + static void resetDefaultCiphers(); + + static QList<QSslCertificate> defaultCaCertificates(); + static QList<QSslCertificate> systemCaCertificates(); + static void setDefaultCaCertificates(const QList<QSslCertificate> &certs); + static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax); + static void addDefaultCaCertificate(const QSslCertificate &cert); + static void addDefaultCaCertificates(const QList<QSslCertificate> &certs); + +#if defined(Q_OS_MAC) + static PtrSecCertificateGetData ptrSecCertificateGetData; + static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates; + static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates; +#elif defined(Q_OS_WIN) + static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW; + static PtrCertFindCertificateInStore ptrCertFindCertificateInStore; + static PtrCertCloseStore ptrCertCloseStore; +#endif + + // The socket itself, including private slots. + QTcpSocket *plainSocket; + void createPlainSocket(QIODevice::OpenMode openMode); + static void pauseSocketNotifiers(QSslSocket*); + static void resumeSocketNotifiers(QSslSocket*); + void _q_connectedSlot(); + void _q_hostFoundSlot(); + void _q_disconnectedSlot(); + void _q_stateChangedSlot(QAbstractSocket::SocketState); + void _q_errorSlot(QAbstractSocket::SocketError); + void _q_readyReadSlot(); + void _q_bytesWrittenSlot(qint64); + void _q_flushWriteBuffer(); + void _q_flushReadBuffer(); + + // Platform specific functions + virtual void startClientEncryption() = 0; + virtual void startServerEncryption() = 0; + virtual void transmit() = 0; + virtual void disconnectFromHost() = 0; + virtual void disconnected() = 0; + virtual QSslCipher sessionCipher() const = 0; + +private: + static bool ensureLibraryLoaded(); + static void ensureCiphersAndCertsLoaded(); + + static bool s_libraryLoaded; + static bool s_loadedCiphersAndCerts; +protected: + static bool s_loadRootCertsOnDemand; + static QList<QByteArray> unixRootCertDirectories(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri new file mode 100644 index 0000000000..8b2e2c1917 --- /dev/null +++ b/src/network/ssl/ssl.pri @@ -0,0 +1,36 @@ +# OpenSSL support; compile in QSslSocket. +contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { + + +symbian { + INCLUDEPATH *= $$OS_LAYER_SSL_SYSTEMINCLUDE +} else { + include($$QT_SOURCE_TREE/config.tests/unix/openssl/openssl.pri) +} + + HEADERS += ssl/qssl.h \ + ssl/qsslcertificate.h \ + ssl/qsslcertificate_p.h \ + ssl/qsslconfiguration.h \ + ssl/qsslconfiguration_p.h \ + ssl/qsslcipher.h \ + ssl/qsslcipher_p.h \ + ssl/qsslerror.h \ + ssl/qsslkey.h \ + ssl/qsslsocket.h \ + ssl/qsslsocket_openssl_p.h \ + ssl/qsslsocket_openssl_symbols_p.h \ + ssl/qsslsocket_p.h + SOURCES += ssl/qssl.cpp \ + ssl/qsslcertificate.cpp \ + ssl/qsslconfiguration.cpp \ + ssl/qsslcipher.cpp \ + ssl/qsslerror.cpp \ + ssl/qsslkey.cpp \ + ssl/qsslsocket.cpp \ + ssl/qsslsocket_openssl.cpp \ + ssl/qsslsocket_openssl_symbols.cpp + + # Add optional SSL libs + LIBS_PRIVATE += $$OPENSSL_LIBS +} |