diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 9 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend.cpp | 8 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.cpp | 8 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.h | 2 | ||||
-rw-r--r-- | src/network/kernel/qauthenticator.cpp | 408 | ||||
-rw-r--r-- | src/network/kernel/qauthenticator.h | 5 | ||||
-rw-r--r-- | src/network/kernel/qauthenticator_p.h | 6 | ||||
-rw-r--r-- | src/network/socket/qtcpserver.cpp | 2 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 89 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_p.h | 1 |
11 files changed, 483 insertions, 59 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 1afabec..9e2b85e 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -286,13 +286,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket resend = false; //create the response header to be used with QAuthenticatorPrivate. - QHttpResponseHeader responseHeader; QList<QPair<QByteArray, QByteArray> > fields = reply->header(); - QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin(); - while (it != fields.constEnd()) { - responseHeader.addValue(QString::fromLatin1(it->first), QString::fromUtf8(it->second)); - it++; - } + //find out the type of authentication protocol requested. QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy); if (authMethod != QAuthenticatorPrivate::None) { @@ -310,7 +305,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket if (auth->isNull()) auth->detach(); QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth); - priv->parseHttpResponse(responseHeader, isProxy); + priv->parseHttpResponse(fields, isProxy); if (priv->phase == QAuthenticatorPrivate::Done) { if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) { diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index a6c5c02..f617244 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -944,10 +944,10 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m checkForRedirect(status); - emit metaDataChanged(); - - // invoke this asynchronously, else Arora/QtDemoBrowser don't like cached downloads - // see task 250221 / 251801 + // This needs to be emitted in the event loop because it can be reached at + // the direct code path of qnam.get(...) before the user has a chance + // to connect any signals. + QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection); qRegisterMetaType<QIODevice*>("QIODevice*"); QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents)); diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index bccfec1..fa592c2 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -230,6 +230,14 @@ QT_BEGIN_NAMESPACE \since 4.7 + \omitvalue MaximumDownloadBufferSizeAttribute + \since 4.7 + \internal + + \omitvalue DownloadBufferAttribute + \since 4.7 + \internal + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index d2945c4..cdadf0f 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -82,6 +82,8 @@ public: CookieLoadControlAttribute, AuthenticationReuseAttribute, CookieSaveControlAttribute, + MaximumDownloadBufferSizeAttribute, // internal + DownloadBufferAttribute, // internal User = 1000, UserMax = 32767 diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index e4023c8..d61c686 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -50,6 +50,8 @@ #include <qdatastream.h> #include <qendian.h> #include <qstring.h> +#include <qdatetime.h> + QT_BEGIN_NAMESPACE @@ -83,6 +85,44 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas Note that, in particular, NTLM version 2 is not supported. + \section1 Options + + In addition to the username and password required for authentication, a + QAuthenticator object can also contain additional options. The + options() function can be used to query incoming options sent by + the server; the setOption() function can + be used to set outgoing options, to be processed by the authenticator + calculation. The options accepted and provided depend on the authentication + type (see method()). + + The following tables list known incoming options as well as accepted + outgoing options. The list of incoming options is not exhaustive, since + servers may include additional information at any time. The list of + outgoing options is exhaustive, however, and no unknown options will be + treated or sent back to the server. + + \section2 Basic + + \table + \header \o Option \o Direction \o Description + \row \o \tt{realm} \o Incoming \o Contains the realm of the authentication, the same as realm() + \endtable + + The Basic authentication mechanism supports no outgoing options. + + \section2 NTLM version 1 + + The NTLM authentication mechanism currently supports no incoming or outgoing options. + + \section2 Digest-MD5 + + \table + \header \o Option \o Direction \o Description + \row \o \tt{realm} \o Incoming \o Contains the realm of the authentication, the same as realm() + \endtable + + The Digest-MD5 authentication mechanism supports no outgoing options. + \sa QSslSocket */ @@ -138,7 +178,8 @@ bool QAuthenticator::operator==(const QAuthenticator &other) const return d->user == other.d->user && d->password == other.d->password && d->realm == other.d->realm - && d->method == other.d->method; + && d->method == other.d->method + && d->options == other.d->options; } /*! @@ -162,7 +203,18 @@ QString QAuthenticator::user() const void QAuthenticator::setUser(const QString &user) { detach(); - d->user = user; + + int separatorPosn = 0; + separatorPosn = user.indexOf(QLatin1String("\\")); + + if (separatorPosn == -1) { + //No domain name present + d->user = user; + } else { + //domain name is present + d->realm = user.left(separatorPosn); + d->user = user.mid(separatorPosn+1); + } } /*! @@ -205,9 +257,49 @@ QString QAuthenticator::realm() const return d ? d->realm : QString(); } +/*! + \since 4.7 + Returns the value related to option \a opt if it was set by the server. + See \l{QAuthenticator#Options} for more information on incoming options. + If option \a opt isn't found, an invalid QVariant will be returned. + + \sa options(), QAuthenticator#Options +*/ +QVariant QAuthenticator::option(const QString &opt) const +{ + return d ? d->options.value(opt) : QVariant(); +} /*! - returns true if the authenticator is null. + \since 4.7 + Returns all incoming options set in this QAuthenticator object by parsing + the server reply. See \l{QAuthenticator#Options} for more information + on incoming options. + + \sa option(), QAuthenticator#Options +*/ +QVariantHash QAuthenticator::options() const +{ + return d ? d->options : QVariantHash(); +} + +/*! + \since 4.7 + + Sets the outgoing option \a opt to value \a value. + See \l{QAuthenticator#Options} for more information on outgoing options. + + \sa options(), option(), QAuthenticator#Options +*/ +void QAuthenticator::setOption(const QString &opt, const QVariant &value) +{ + detach(); + d->options.insert(opt, value); +} + + +/*! + Returns true if the authenticator is null. */ bool QAuthenticator::isNull() const { @@ -228,7 +320,20 @@ QAuthenticatorPrivate::QAuthenticatorPrivate() #ifndef QT_NO_HTTP void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy) { - QList<QPair<QString, QString> > values = header.values(); + const QList<QPair<QString, QString> > values = header.values(); + QList<QPair<QByteArray, QByteArray> > rawValues; + + QList<QPair<QString, QString> >::const_iterator it, end; + for (it = values.constBegin(), end = values.constEnd(); it != end; ++it) + rawValues.append(qMakePair(it->first.toLatin1(), it->second.toUtf8())); + + // continue in byte array form + parseHttpResponse(rawValues, isProxy); +} +#endif + +void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy) +{ const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; method = None; @@ -242,38 +347,40 @@ void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, authentication parameters. */ - QString headerVal; + QByteArray headerVal; for (int i = 0; i < values.size(); ++i) { - const QPair<QString, QString> ¤t = values.at(i); - if (current.first.toLower() != QLatin1String(search)) + const QPair<QByteArray, QByteArray> ¤t = values.at(i); + if (current.first.toLower() != search) continue; - QString str = current.second; - if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) { - method = Basic; headerVal = str.mid(6); - } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) { + QByteArray str = current.second.toLower(); + if (method < Basic && str.startsWith("basic")) { + method = Basic; + headerVal = current.second.mid(6); + } else if (method < Ntlm && str.startsWith("ntlm")) { method = Ntlm; - headerVal = str.mid(5); - } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) { + headerVal = current.second.mid(5); + } else if (method < DigestMd5 && str.startsWith("digest")) { method = DigestMd5; - headerVal = str.mid(7); + headerVal = current.second.mid(7); } } - challenge = headerVal.trimmed().toLatin1(); + challenge = headerVal.trimmed(); QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); switch(method) { case Basic: - realm = QString::fromLatin1(options.value("realm")); + if(realm.isEmpty()) + this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); if (user.isEmpty()) phase = Done; break; case Ntlm: // #### extract from header - realm.clear(); break; case DigestMd5: { - realm = QString::fromLatin1(options.value("realm")); + if(realm.isEmpty()) + this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); if (options.value("stale").toLower() == "true") phase = Start; if (user.isEmpty()) @@ -286,7 +393,6 @@ void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, phase = Invalid; } } -#endif QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path) { @@ -661,6 +767,20 @@ QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, */ #define NTLMSSP_NEGOTIATE_56 0x80000000 +/* + * AvId values + */ +#define AVTIMESTAMP 7 + +//#define NTLMV1_CLIENT + + +//************************Global variables*************************** + +const int blockSize = 64; //As per RFC2104 Block-size is 512 bits +const int nDigestLen = 16; //Trunctaion Length of the Hmac-Md5 digest +const quint8 respversion = 1; +const quint8 hirespversion = 1; /* usage: // fill up ctx with what we know. @@ -803,6 +923,7 @@ public: // extracted QString targetNameStr, targetInfoStr; + QByteArray targetInfoBuff; }; @@ -818,6 +939,7 @@ public: // extracted QByteArray lmResponseBuf, ntlmResponseBuf; QString domainStr, userStr, workstationStr, sessionKeyStr; + QByteArray v2Hash; }; @@ -899,7 +1021,7 @@ static QString qStringFromUcs2Le(const QByteArray& src) return QString((const QChar *)src.data(), src.size()/2); } - +#ifdef NTLMV1_CLIENT static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch) { QCryptographicHash md4(QCryptographicHash::Md4); @@ -941,7 +1063,232 @@ static QByteArray qEncodeLmResponse(const QAuthenticatorPrivate *ctx, const QNtl hash.fill(0); return rc; } +#endif + +/********************************************************************* +* Function Name: qEncodeHmacMd5 +* Params: +* key: Type - QByteArray +* - It is the Authentication key +* message: Type - QByteArray +* - This is the actual message which will be encoded +* using HMacMd5 hash algorithm +* +* Return Value: +* hmacDigest: Type - QByteArray +* +* Description: +* This function will be used to encode the input message using +* HMacMd5 hash algorithm. +* +* As per the RFC2104 the HMacMd5 algorithm can be specified +* --------------------------------------- +* MD5(K XOR opad, MD5(K XOR ipad, text)) +* --------------------------------------- +* +*********************************************************************/ +QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message) +{ + Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check"); + Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check"); + + QCryptographicHash hash(QCryptographicHash::Md5); + QByteArray hMsg; + + QByteArray iKeyPad(blockSize, 0x36); + QByteArray oKeyPad(blockSize, 0x5c); + + hash.reset(); + // Adjust the key length to blockSize + + if(blockSize < key.length()) { + hash.addData(key); + key = hash.result(); //MD5 will always return 16 bytes length output + } + + //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms) + //key size can be max of Block size only + key = key.leftJustified(blockSize,0,true); + + //iKeyPad, oKeyPad and key are all of same size "blockSize" + + //xor of iKeyPad with Key and store the result into iKeyPad + for(int i = 0; i<key.size();i++) { + iKeyPad[i] = key[i]^iKeyPad[i]; + } + + //xor of oKeyPad with Key and store the result into oKeyPad + for(int i = 0; i<key.size();i++) { + oKeyPad[i] = key[i]^oKeyPad[i]; + } + + iKeyPad.append(message); // (K0 xor ipad) || text + + hash.reset(); + hash.addData(iKeyPad); + hMsg = hash.result(); + //Digest gen after pass-1: H((K0 xor ipad)||text) + + QByteArray hmacDigest; + oKeyPad.append(hMsg); + hash.reset(); + hash.addData(oKeyPad); + hmacDigest = hash.result(); + // H((K0 xor opad )|| H((K0 xor ipad) || text)) + + /*hmacDigest should not be less than half the length of the HMAC output + (to match the birthday attack bound) and not less than 80 bits + (a suitable lower bound on the number of bits that need to be + predicted by an attacker). + Refer RFC 2104 for more details on truncation part */ + + /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec + (RFC 2104) also says digest length should be 16 bytes*/ + return hmacDigest; +} + +static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx, + QNtlmPhase3Block *phase3) +{ + Q_ASSERT(phase3 != 0); + // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated + // only once and stored and reused + if(phase3->v2Hash.size() == 0) { + QCryptographicHash md4(QCryptographicHash::Md4); + QByteArray passUnicode = qStringAsUcs2Le(ctx->password); + md4.addData(passUnicode.data(), passUnicode.size()); + + QByteArray hashKey = md4.result(); + Q_ASSERT(hashKey.size() == 16); + // Assuming the user and domain is always unicode in challenge + QByteArray message = + qStringAsUcs2Le(ctx->user.toUpper()) + + qStringAsUcs2Le(ctx->realm); + + phase3->v2Hash = qEncodeHmacMd5(hashKey, message); + } + return phase3->v2Hash; +} + +static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx) +{ + Q_ASSERT(ctx->cnonce.size() >= 8); + QByteArray clientCh = ctx->cnonce.right(8); + return clientCh; +} + +// caller has to ensure a valid targetInfoBuff +static bool qExtractServerTime(const QByteArray& targetInfoBuff, + quint64 *serverTime) +{ + Q_ASSERT(serverTime != 0); + bool retValue = false; + QDataStream ds(targetInfoBuff); + ds.setByteOrder(QDataStream::LittleEndian); + + quint16 avId; + quint16 avLen; + + ds >> avId; + ds >> avLen; + while(avId != 0) { + if(avId == AVTIMESTAMP) { + QByteArray timeArray(avLen, 0); + //avLen size of QByteArray is allocated + ds.readRawData(timeArray.data(), avLen); + bool ok; + *serverTime = timeArray.toHex().toLongLong(&ok, 16); + retValue = true; + break; + } + ds.skipRawData(avLen); + ds >> avId; + ds >> avLen; + } + return retValue; +} + +static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx, + const QNtlmPhase2Block& ch, + QNtlmPhase3Block *phase3) +{ + Q_ASSERT(phase3 != 0); + // return value stored in phase3 + qCreatev2Hash(ctx, phase3); + + QByteArray temp; + QDataStream ds(&temp, QIODevice::WriteOnly); + ds.setByteOrder(QDataStream::LittleEndian); + + ds << respversion; + ds << hirespversion; + //Reserved + QByteArray reserved1(6, 0); + ds.writeRawData(reserved1.constData(), reserved1.size()); + + quint64 time = 0; + + //if server sends time, use it instead of current time + if(!(ch.targetInfo.len && qExtractServerTime(ch.targetInfoBuff, &time))) { + QDateTime currentTime(QDate::currentDate(), + QTime::currentTime(), Qt::UTC); + + // number of seconds between 1601 and epoc(1970) + // 369 years, 89 leap years + // ((369 * 365) + 89) * 24 * 3600 = 11644473600 + + time = Q_UINT64_C(currentTime.toTime_t() + 11644473600); + + // represented as 100 nano seconds + time = Q_UINT64_C(time * 10000000); + } + ds << time; + + //8 byte client challenge + QByteArray clientCh = clientChallenge(ctx); + ds.writeRawData(clientCh.constData(), clientCh.size()); + + //Reserved + QByteArray reserved2(4, 0); + ds.writeRawData(reserved2.constData(), reserved2.size()); + + if (ch.targetInfo.len > 0) { + ds.writeRawData(ch.targetInfoBuff.constData(), + ch.targetInfoBuff.size()); + } + + //Reserved + QByteArray reserved3(4, 0); + ds.writeRawData(reserved3.constData(), reserved3.size()); + + QByteArray message((const char*)ch.challenge, sizeof(ch.challenge)); + message.append(temp); + + QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message); + ntChallengeResp.append(temp); + + return ntChallengeResp; +} + +static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx, + const QNtlmPhase2Block& ch, + QNtlmPhase3Block *phase3) +{ + Q_ASSERT(phase3 != 0); + // return value stored in phase3 + qCreatev2Hash(ctx, phase3); + + QByteArray message((const char*)ch.challenge, sizeof(ch.challenge)); + QByteArray clientCh = clientChallenge(ctx); + + message.append(clientCh); + + QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message); + lmChallengeResp.append(clientCh); + + return lmChallengeResp; +} static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch) { @@ -976,7 +1323,10 @@ static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch) } if (ch.targetInfo.len > 0) { - // UNUSED right now + if (ch.targetInfo.len + ch.targetInfo.offset > (unsigned)data.size()) + return false; + + ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len); } return true; @@ -996,7 +1346,8 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE; - ctx->realm = ch.targetNameStr; + if(ctx->realm.isEmpty()) + ctx->realm = ch.targetNameStr; pb.flags = NTLMSSP_NEGOTIATE_NTLM; if (unicode) @@ -1010,6 +1361,7 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); pb.domainStr = ctx->realm; + offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); pb.userStr = ctx->user; @@ -1017,11 +1369,23 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas pb.workstationStr = ctx->workstation; // Get LM response +#ifdef NTLMV1_CLIENT pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); +#else + if (ch.targetInfo.len > 0) { + pb.lmResponseBuf = QByteArray(); + } else { + pb.lmResponseBuf = qEncodeLmv2Response(ctx, ch, &pb); + } +#endif offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); // Get NTLM response +#ifdef NTLMV1_CLIENT pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); +#else + pb.ntlmResponseBuf = qEncodeNtlmv2Response(ctx, ch, &pb); +#endif offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf); diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h index 13ce593..983b7c0 100644 --- a/src/network/kernel/qauthenticator.h +++ b/src/network/kernel/qauthenticator.h @@ -43,6 +43,7 @@ #define QAUTHENTICATOR_H #include <QtCore/qstring.h> +#include <QtCore/qvariant.h> QT_BEGIN_HEADER @@ -73,6 +74,10 @@ public: QString realm() const; + QVariant option(const QString &opt) const; + QVariantHash options() const; + void setOption(const QString &opt, const QVariant &value); + bool isNull() const; void detach(); private: diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h index e9ce9ac..1096601 100644 --- a/src/network/kernel/qauthenticator_p.h +++ b/src/network/kernel/qauthenticator_p.h @@ -57,12 +57,13 @@ #include <qbytearray.h> #include <qstring.h> #include <qauthenticator.h> +#include <qvariant.h> QT_BEGIN_NAMESPACE class QHttpResponseHeader; -class QAuthenticatorPrivate +class Q_AUTOTEST_EXPORT QAuthenticatorPrivate { public: enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 }; @@ -71,7 +72,7 @@ public: QAtomicInt ref; QString user; QString password; - QHash<QByteArray, QByteArray> options; + QVariantHash options; Method method; QString realm; QByteArray challenge; @@ -102,6 +103,7 @@ public: #ifndef QT_NO_HTTP void parseHttpResponse(const QHttpResponseHeader &, bool isProxy); #endif + void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy); }; diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp index 55f926d..0640c7c 100644 --- a/src/network/socket/qtcpserver.cpp +++ b/src/network/socket/qtcpserver.cpp @@ -577,7 +577,7 @@ void QTcpServer::incomingConnection(int socketDescriptor) /*! This function is called by QTcpServer::incomingConnection() - to add a socket to the list of pending incoming connections. + to add the \a socket to the list of pending incoming connections. \note Don't forget to call this member from reimplemented incomingConnection() if you do not want to break the diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index f85fa84..f73068e 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -574,7 +574,7 @@ void QSslSocket::setProtocol(QSsl::SslProtocol protocol) certificate is valid. The default mode is AutoVerifyPeer, which tells QSslSocket to use - VerifyPeer for clients, QueryPeer for clients. + VerifyPeer for clients and QueryPeer for servers. \sa setPeerVerifyMode(), peerVerifyDepth(), mode() */ @@ -594,7 +594,7 @@ QSslSocket::PeerVerifyMode QSslSocket::peerVerifyMode() const certificate is valid. The default mode is AutoVerifyPeer, which tells QSslSocket to use - VerifyPeer for clients, QueryPeer for clients. + VerifyPeer for clients and QueryPeer for servers. Setting this mode after encryption has started has no effect on the current connection. diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 1d794ae..c297eea 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -475,27 +475,12 @@ bool QSslSocketPrivate::ensureLibraryLoaded() void QSslSocketPrivate::ensureCiphersAndCertsLoaded() { + QMutexLocker locker(openssl_locks()->initLock()); if (s_loadedCiphersAndCerts) return; s_loadedCiphersAndCerts = true; resetDefaultCiphers(); - setDefaultCaCertificates(systemCaCertificates()); -} - -/*! - \internal - - Declared static in QSslSocketPrivate, makes sure the SSL libraries have - been initialized. -*/ - -void QSslSocketPrivate::ensureInitialized() -{ - if (!supportsSsl()) - return; - - ensureCiphersAndCertsLoaded(); //load symbols needed to receive certificates from system store #if defined(Q_OS_MAC) @@ -532,6 +517,22 @@ void QSslSocketPrivate::ensureInitialized() qWarning("could not load crypt32 library"); // should never happen } #endif + setDefaultCaCertificates(systemCaCertificates()); +} + +/*! + \internal + + Declared static in QSslSocketPrivate, makes sure the SSL libraries have + been initialized. +*/ + +void QSslSocketPrivate::ensureInitialized() +{ + if (!supportsSsl()) + return; + + ensureCiphersAndCertsLoaded(); } /*! @@ -970,8 +971,20 @@ void QSslSocketBackendPrivate::transmit() #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(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return; default: - // ### Handle errors better. + // 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(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::UnknownSocketError); emit q->error(QAbstractSocket::UnknownSocketError); @@ -1105,17 +1118,16 @@ bool QSslSocketBackendPrivate::startHandshake() QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); - QRegExp regexp(commonName, Qt::CaseInsensitive, QRegExp::Wildcard); - if (!regexp.exactMatch(peerName)) { + if (!isMatchingHostname(commonName.toLower(), peerName.toLower())) { bool matched = false; foreach (const QString &altName, configuration.peerCertificate .alternateSubjectNames().values(QSsl::DnsEntry)) { - regexp.setPattern(altName); - if (regexp.exactMatch(peerName)) { + 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); @@ -1245,4 +1257,39 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates return certificates; } +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; + + // 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 index 987dfae..7a4e6b6 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -116,6 +116,7 @@ public: 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); }; #if defined(Q_OS_SYMBIAN) |