summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorQt Continuous Integration System <qt-info@nokia.com>2010-07-10 11:23:07 (GMT)
committerQt Continuous Integration System <qt-info@nokia.com>2010-07-10 11:23:07 (GMT)
commit8f84ccc8d16383e722f07578dbfa2b15c2b24431 (patch)
treeda2c590d7071ba535c41af00fe8ba310c8303e09 /src/network
parentcf907cb8ab5ecb95d964762c1cce586fa2207dff (diff)
parent6988f2bb36dafc2223c326b6fd21e49586b1379b (diff)
downloadQt-8f84ccc8d16383e722f07578dbfa2b15c2b24431.zip
Qt-8f84ccc8d16383e722f07578dbfa2b15c2b24431.tar.gz
Qt-8f84ccc8d16383e722f07578dbfa2b15c2b24431.tar.bz2
Merge branch '4.7' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into 4.7-integration
* '4.7' of scm.dev.nokia.troll.no:qt/oslo-staging-1: (59 commits) Doc: Cleaning style and adding support for Creator Add NTLMv2 authentication support to QAuthenticator. QIODPLB: Sync behavior of ungetBlock() and ungetChar() QFileIconProvider: Load icons on demand. Doc: fixing offline style Fix handling of SSL certificates with wildcard domain names Doc: fixing examples link doc: Fixed several qdoc warnings. doc: Fixed several qdoc warnings. SSL library loading: load system libs only once Revert "SSL backend: load libraries for certificates only once" doc: Fixed several qdoc warnings. doc: Fixed several qdoc warnings. doc: Fixed several qdoc warnings. doc: Fixed several qdoc warnings. doc: Fixed several qdoc warnings. Doc: Adding support for Qt Creator style doc: Fixed last of the declarative/QML qdoc warnings. qdoc: Marked some missing declarative properties and functions as \internal. qhelpgenerator: Fix namespace syntax checking. ...
Diffstat (limited to 'src/network')
-rw-r--r--src/network/kernel/qauthenticator.cpp286
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp75
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h1
3 files changed, 335 insertions, 27 deletions
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index e4023c8..e7442c0 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
@@ -162,7 +164,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);
+ }
}
/*!
@@ -264,16 +277,17 @@ void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header,
switch(method) {
case Basic:
- realm = QString::fromLatin1(options.value("realm"));
+ if(realm.isEmpty())
+ 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())
+ realm = QString::fromLatin1(options.value("realm"));
if (options.value("stale").toLower() == "true")
phase = Start;
if (user.isEmpty())
@@ -661,6 +675,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 +831,7 @@ public:
// extracted
QString targetNameStr, targetInfoStr;
+ QByteArray targetInfoBuff;
};
@@ -818,6 +847,7 @@ public:
// extracted
QByteArray lmResponseBuf, ntlmResponseBuf;
QString domainStr, userStr, workstationStr, sessionKeyStr;
+ QByteArray v2Hash;
};
@@ -899,7 +929,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 +971,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 +1231,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 +1254,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 +1269,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 +1277,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/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 1d794ae..a5da391 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();
}
/*!
@@ -1105,17 +1106,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.lower(), peerName.lower())) {
bool matched = false;
foreach (const QString &altName, configuration.peerCertificate
.alternateSubjectNames().values(QSsl::DnsEntry)) {
- regexp.setPattern(altName);
- if (regexp.exactMatch(peerName)) {
+ if (isMatchingHostname(altName.lower(), peerName.lower())) {
matched = true;
break;
}
}
+
if (!matched) {
// No matches in common names or alternate names.
QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
@@ -1245,4 +1245,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)