📄 qauthenticator.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtNetwork module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include <qauthenticator.h>#include <qauthenticator_p.h>#include <qdebug.h>#include <qhash.h>#include <qbytearray.h>#include <qcryptographichash.h>#include <qhttp.h>#include <qdatastream.h>#include <qendian.h>#include <qstring.h>#include <../3rdparty/des/des.cpp>static QByteArray qNtlmPhase1();static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);/*! \class QAuthenticator \brief The QAuthenticator class provides an authentication object. \since 4.3 \reentrant \ingroup io \module network The QAuthenticator class is usually used in the \l{QHttp::}{authenticationRequired()} and \l{QHttp::}{proxyAuthenticationRequired()} signals of QHttp and QAbstractSocket. The class provides a way to pass back the required authentication information to the socket when accessing services that require authentication. \sa QSslSocket*//*! Constructs an empty authentication object*/QAuthenticator::QAuthenticator() : d(0){}/*! Destructs the object*/QAuthenticator::~QAuthenticator(){ if (d && !d->ref.deref()) delete d;}/*! Constructs a copy of \a other.*/QAuthenticator::QAuthenticator(const QAuthenticator &other) : d(other.d){ if (d) d->ref.ref();}/*! Assigns the contents of \a other to this authenticator.*/QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other){ if (d == other.d) return *this; detach(); d->user = other.d->user; d->password = other.d->password; return *this;}/*! Returns true if this authenticator is identical to \a other; otherwise returns false.*/bool QAuthenticator::operator==(const QAuthenticator &other) const{ if (d == other.d) return true; return d->user == other.d->user && d->password == other.d->password && d->realm == other.d->realm && d->method == other.d->method;}/*! \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const Returns true if this authenticator is different from \a other; otherwise returns false.*//*! returns the user used for authentication.*/QString QAuthenticator::user() const{ return d ? d->user : QString();}/*! Sets the \a user used for authentication.*/void QAuthenticator::setUser(const QString &user){ detach(); d->user = user;}/*! returns the password used for authentication.*/QString QAuthenticator::password() const{ return d ? d->password : QString();}/*! Sets the \a password used for authentication.*/void QAuthenticator::setPassword(const QString &password){ detach(); d->password = password;}/*! \internal*/void QAuthenticator::detach(){ if (!d) { d = new QAuthenticatorPrivate; d->ref.ref(); return; } if (d->ref.ref() != 1) { QAuthenticatorPrivate *x = new QAuthenticatorPrivate(*d); x = qAtomicSetPtr(&d, x); if (!x->ref.deref()) delete x; } d->phase = QAuthenticatorPrivate::Start;}/*! returns the realm requiring authentication.*/QString QAuthenticator::realm() const{ return d->realm;}/*! returns true if the authenticator is null.*/bool QAuthenticator::isNull() const{ return !d;}QAuthenticatorPrivate::QAuthenticatorPrivate() : ref(0) , method(None) , phase(Start) , nonceCount(0){ cnonce = QCryptographicHash::hash(QByteArray::number(qrand(), 16) + QByteArray::number(qrand(), 16), QCryptographicHash::Md5).toHex(); nonceCount = 0;}#ifndef QT_NO_HTTPvoid QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy){ QList<QPair<QString, QString> > values = header.values(); const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; method = None; /* Fun from the HTTP 1.1 specs, that we currently ignore: User agents are advised to take special care in parsing the WWW- Authenticate field value as it might contain more than one challenge, or if more than one WWW-Authenticate header field is provided, the contents of a challenge itself can contain a comma-separated list of authentication parameters. */ QString headerVal; for (int i = 0; i < values.size(); ++i) { const QPair<QString, QString> ¤t = values.at(i); if (current.first.toLower() != QLatin1String(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)) { method = Ntlm; headerVal = str.mid(5); } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) { method = DigestMd5; headerVal = str.mid(7); } } challenge = headerVal.trimmed().toLatin1(); QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); switch(method) { case Basic: realm = QString::fromLatin1(options.value("realm")); if (user.isEmpty()) phase = Done; break; case Ntlm: // #### extract from header realm = QString(); break; case DigestMd5: { realm = QString::fromLatin1(options.value("realm")); if (options.value("stale").toLower() == "true") phase = Start; if (user.isEmpty()) phase = Done; break; } default: realm = QString(); challenge = QByteArray(); }}#endifQByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path){ QByteArray response; const char *methodString = 0; switch(method) { case QAuthenticatorPrivate::None: methodString = ""; phase = Done; break; case QAuthenticatorPrivate::Plain: response = '\0' + user.toUtf8() + '\0' + password.toUtf8(); phase = Done; break; case QAuthenticatorPrivate::Basic: methodString = "Basic "; response = user.toLatin1() + ':' + password.toLatin1(); response = response.toBase64(); phase = Done; break; case QAuthenticatorPrivate::Login: if (challenge.contains("VXNlciBOYW1lAA==")) { response = user.toUtf8().toBase64(); phase = Phase2; } else if (challenge.contains("UGFzc3dvcmQA")) { response = password.toUtf8().toBase64(); phase = Done; } break; case QAuthenticatorPrivate::CramMd5: break; case QAuthenticatorPrivate::DigestMd5: methodString = "Digest "; response = digestMd5Response(challenge, requestMethod, path); phase = Done; break; case QAuthenticatorPrivate::Ntlm: methodString = "NTLM "; if (challenge.isEmpty()) { response = qNtlmPhase1().toBase64(); if (user.isEmpty()) phase = Done; else phase = Phase2; } else { response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64(); phase = Done; } break; } return QByteArray(methodString) + response;}// ---------------------------- Digest Md5 code ----------------------------------------QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge){ QHash<QByteArray, QByteArray> options; // parse the challenge const char *d = challenge.constData(); const char *end = d + challenge.length(); while (d < end) { while (d < end && (*d == ' ' || *d == '\n' || *d == '\r')) ++d; const char *start = d; while (d < end && *d != '=') ++d; QByteArray key = QByteArray(start, d - start); ++d; if (d >= end) break; bool quote = (*d == '"'); if (quote) ++d; if (d >= end) break; start = d; QByteArray value; while (d < end) { bool backslash = false; if (*d == '\\' && d < end - 1) { ++d; backslash = true; } if (!backslash) { if (quote) { if (*d == '"') break; } else { if (*d == ',') break; } } value += *d; ++d; } while (d < end && *d != ',') ++d; ++d; options[key] = value; } QByteArray qop = options.value("qop"); if (!qop.isEmpty()) { QList<QByteArray> qopoptions = qop.split(','); if (!qopoptions.contains("auth")) return QHash<QByteArray, QByteArray>(); // #### can't do auth-int currently// if (qop.contains("auth-int"))// qop = "auth-int";// else if (qop.contains("auth"))// qop = "auth";// else// qop = QByteArray(); options["qop"] = "auth"; } return options;}/* Digest MD5 implementation Code taken from RFC 2617 Currently we don't support the full SASL authentication mechanism (which includes cyphers)*//* calculate request-digest/response-digest as per HTTP Digest spec */static QByteArray digestMd5Response( const QByteArray &alg, const QByteArray &userName, const QByteArray &realm, const QByteArray &password, const QByteArray &nonce, /* nonce from server */ const QByteArray &nonceCount, /* 8 hex digits */ const QByteArray &cNonce, /* client nonce */ const QByteArray &qop, /* qop-value: "", "auth", "auth-int" */ const QByteArray &method, /* method from the request */ const QByteArray &digestUri, /* requested URL */ const QByteArray &hEntity /* H(entity body) if qop="auth-int" */ ){ QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(userName); hash.addData(":", 1); hash.addData(realm); hash.addData(":", 1); hash.addData(password); QByteArray ha1 = hash.result(); if (alg.toLower() == "md5-sess") { hash.reset(); hash.addData(ha1); hash.addData(":", 1); hash.addData(nonce); hash.addData(":", 1); hash.addData(cNonce); ha1 = hash.result(); }; ha1 = ha1.toHex(); // calculate H(A2) hash.reset(); hash.addData(method); hash.addData(":", 1); hash.addData(digestUri); if (qop.toLower() == "auth-int") { hash.addData(":", 1); hash.addData(hEntity); } QByteArray ha2hex = hash.result().toHex(); // calculate response hash.reset(); hash.addData(ha1); hash.addData(":", 1); hash.addData(nonce); hash.addData(":", 1); if (!qop.isNull()) { hash.addData(nonceCount); hash.addData(":", 1); hash.addData(cNonce); hash.addData(":", 1); hash.addData(qop); hash.addData(":", 1); } hash.addData(ha2hex); return hash.result().toHex();}QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path){ QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge); ++nonceCount; QByteArray nonceCountString = QByteArray::number(nonceCount, 16); while (nonceCountString.length() < 8) nonceCountString.prepend('0'); QByteArray nonce = options.value("nonce"); QByteArray opaque = options.value("opaque"); QByteArray qop = options.value("qop"); qDebug() << "calculating digest: method=" << method << "path=" << path; QByteArray response = ::digestMd5Response(options.value("algorithm"), user.toLatin1(), realm.toLatin1(), password.toLatin1(), nonce, nonceCountString, cnonce, qop, method, path, QByteArray()); QByteArray credentials; credentials += "username=\"" + user.toLatin1() + "\", "; credentials += "realm=\"" + realm.toLatin1() + "\", "; credentials += "nonce=\"" + nonce + "\", "; credentials += "uri=\"" + path + "\", "; if (!opaque.isEmpty()) credentials += "opaque=\"" + opaque + "\", "; credentials += "response=\"" + response + "\""; if (!options.value("algorithm").isEmpty()) credentials += ", algorithm=" + options.value("algorithm"); if (!options.value("qop").isEmpty()) { credentials += ", qop=" + qop + ", "; credentials += "nc=" + nonceCountString + ", "; credentials += "cnonce=\"" + cnonce + "\""; } return credentials;}// ---------------------------- Digest Md5 code ----------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -