src/network/kernel/qauthenticator.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <qauthenticator.h>
       
    43 #include <qauthenticator_p.h>
       
    44 #include <qdebug.h>
       
    45 #include <qhash.h>
       
    46 #include <qbytearray.h>
       
    47 #include <qcryptographichash.h>
       
    48 #include <qhttp.h>
       
    49 #include <qiodevice.h>
       
    50 #include <qdatastream.h>
       
    51 #include <qendian.h>
       
    52 #include <qstring.h>
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 #include "../../3rdparty/des/des.cpp"
       
    57 
       
    58 static QByteArray qNtlmPhase1();
       
    59 static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
       
    60 
       
    61 /*!
       
    62   \class QAuthenticator
       
    63   \brief The QAuthenticator class provides an authentication object.
       
    64   \since 4.3
       
    65 
       
    66   \reentrant
       
    67   \ingroup network
       
    68   \inmodule QtNetwork
       
    69 
       
    70   The QAuthenticator class is usually used in the
       
    71   \l{QNetworkAccessManager::}{authenticationRequired()} and
       
    72   \l{QNetworkAccessManager::}{proxyAuthenticationRequired()} signals of QNetworkAccessManager and
       
    73   QAbstractSocket. The class provides a way to pass back the required
       
    74   authentication information to the socket when accessing services that
       
    75   require authentication.
       
    76 
       
    77   QAuthenticator supports the following authentication methods:
       
    78   \list
       
    79     \o Basic
       
    80     \o NTLM version 1
       
    81     \o Digest-MD5
       
    82   \endlist
       
    83 
       
    84   Note that, in particular, NTLM version 2 is not supported.
       
    85 
       
    86   \sa QSslSocket
       
    87 */
       
    88 
       
    89 
       
    90 /*!
       
    91   Constructs an empty authentication object
       
    92 */
       
    93 QAuthenticator::QAuthenticator()
       
    94     : d(0)
       
    95 {
       
    96 }
       
    97 
       
    98 /*!
       
    99   Destructs the object
       
   100 */
       
   101 QAuthenticator::~QAuthenticator()
       
   102 {
       
   103     if (d && !d->ref.deref())
       
   104         delete d;
       
   105 }
       
   106 
       
   107 /*!
       
   108     Constructs a copy of \a other.
       
   109 */
       
   110 QAuthenticator::QAuthenticator(const QAuthenticator &other)
       
   111     : d(other.d)
       
   112 {
       
   113     if (d)
       
   114         d->ref.ref();
       
   115 }
       
   116 
       
   117 /*!
       
   118     Assigns the contents of \a other to this authenticator.
       
   119 */
       
   120 QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
       
   121 {
       
   122     if (d == other.d)
       
   123         return *this;
       
   124     detach();
       
   125     d->user = other.d->user;
       
   126     d->password = other.d->password;
       
   127     return *this;
       
   128 }
       
   129 
       
   130 /*!
       
   131     Returns true if this authenticator is identical to \a other; otherwise
       
   132     returns false.
       
   133 */
       
   134 bool QAuthenticator::operator==(const QAuthenticator &other) const
       
   135 {
       
   136     if (d == other.d)
       
   137         return true;
       
   138     return d->user == other.d->user
       
   139         && d->password == other.d->password
       
   140         && d->realm == other.d->realm
       
   141         && d->method == other.d->method;
       
   142 }
       
   143 
       
   144 /*!
       
   145     \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const
       
   146 
       
   147     Returns true if this authenticator is different from \a other; otherwise
       
   148     returns false.
       
   149 */
       
   150 
       
   151 /*!
       
   152   returns the user used for authentication.
       
   153 */
       
   154 QString QAuthenticator::user() const
       
   155 {
       
   156     return d ? d->user : QString();
       
   157 }
       
   158 
       
   159 /*!
       
   160   Sets the \a user used for authentication.
       
   161 */
       
   162 void QAuthenticator::setUser(const QString &user)
       
   163 {
       
   164     detach();
       
   165     d->user = user;
       
   166 }
       
   167 
       
   168 /*!
       
   169   returns the password used for authentication.
       
   170 */
       
   171 QString QAuthenticator::password() const
       
   172 {
       
   173     return d ? d->password : QString();
       
   174 }
       
   175 
       
   176 /*!
       
   177   Sets the \a password used for authentication.
       
   178 */
       
   179 void QAuthenticator::setPassword(const QString &password)
       
   180 {
       
   181     detach();
       
   182     d->password = password;
       
   183 }
       
   184 
       
   185 /*!
       
   186   \internal
       
   187 */
       
   188 void QAuthenticator::detach()
       
   189 {
       
   190     if (!d) {
       
   191         d = new QAuthenticatorPrivate;
       
   192         d->ref = 1;
       
   193         return;
       
   194     }
       
   195 
       
   196     qAtomicDetach(d);
       
   197     d->phase = QAuthenticatorPrivate::Start;
       
   198 }
       
   199 
       
   200 /*!
       
   201   returns the realm requiring authentication.
       
   202 */
       
   203 QString QAuthenticator::realm() const
       
   204 {
       
   205     return d ? d->realm : QString();
       
   206 }
       
   207 
       
   208 
       
   209 /*!
       
   210   returns true if the authenticator is null.
       
   211 */
       
   212 bool QAuthenticator::isNull() const
       
   213 {
       
   214     return !d;
       
   215 }
       
   216 
       
   217 QAuthenticatorPrivate::QAuthenticatorPrivate()
       
   218     : ref(0)
       
   219     , method(None)
       
   220     , phase(Start)
       
   221     , nonceCount(0)
       
   222 {
       
   223     cnonce = QCryptographicHash::hash(QByteArray::number(qrand(), 16) + QByteArray::number(qrand(), 16),
       
   224                                       QCryptographicHash::Md5).toHex();
       
   225     nonceCount = 0;
       
   226 }
       
   227 
       
   228 #ifndef QT_NO_HTTP
       
   229 void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy)
       
   230 {
       
   231     QList<QPair<QString, QString> > values = header.values();
       
   232     const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
       
   233 
       
   234     method = None;
       
   235     /*
       
   236       Fun from the HTTP 1.1 specs, that we currently ignore:
       
   237 
       
   238       User agents are advised to take special care in parsing the WWW-
       
   239       Authenticate field value as it might contain more than one challenge,
       
   240       or if more than one WWW-Authenticate header field is provided, the
       
   241       contents of a challenge itself can contain a comma-separated list of
       
   242       authentication parameters.
       
   243     */
       
   244 
       
   245     QString headerVal;
       
   246     for (int i = 0; i < values.size(); ++i) {
       
   247         const QPair<QString, QString> &current = values.at(i);
       
   248         if (current.first.toLower() != QLatin1String(search))
       
   249             continue;
       
   250         QString str = current.second;
       
   251         if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) {
       
   252             method = Basic; headerVal = str.mid(6);
       
   253         } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) {
       
   254             method = Ntlm;
       
   255             headerVal = str.mid(5);
       
   256         } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) {
       
   257             method = DigestMd5;
       
   258             headerVal = str.mid(7);
       
   259         }
       
   260     }
       
   261 
       
   262     challenge = headerVal.trimmed().toLatin1();
       
   263     QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
       
   264 
       
   265     switch(method) {
       
   266     case Basic:
       
   267         realm = QString::fromLatin1(options.value("realm"));
       
   268         if (user.isEmpty())
       
   269             phase = Done;
       
   270         break;
       
   271     case Ntlm:
       
   272         // #### extract from header
       
   273         realm = QString();
       
   274         break;
       
   275     case DigestMd5: {
       
   276         realm = QString::fromLatin1(options.value("realm"));
       
   277         if (options.value("stale").toLower() == "true")
       
   278             phase = Start;
       
   279         if (user.isEmpty())
       
   280             phase = Done;
       
   281         break;
       
   282     }
       
   283     default:
       
   284         realm = QString();
       
   285         challenge = QByteArray();
       
   286         phase = Invalid;
       
   287     }
       
   288 }
       
   289 #endif
       
   290 
       
   291 QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
       
   292 {
       
   293     QByteArray response;
       
   294     const char *methodString = 0;
       
   295     switch(method) {
       
   296     case QAuthenticatorPrivate::None:
       
   297         methodString = "";
       
   298         phase = Done;
       
   299         break;
       
   300     case QAuthenticatorPrivate::Plain:
       
   301         response = '\0' + user.toUtf8() + '\0' + password.toUtf8();
       
   302         phase = Done;
       
   303         break;
       
   304     case QAuthenticatorPrivate::Basic:
       
   305         methodString = "Basic ";
       
   306         response = user.toLatin1() + ':' + password.toLatin1();
       
   307         response = response.toBase64();
       
   308         phase = Done;
       
   309         break;
       
   310     case QAuthenticatorPrivate::Login:
       
   311         if (challenge.contains("VXNlciBOYW1lAA==")) {
       
   312             response = user.toUtf8().toBase64();
       
   313             phase = Phase2;
       
   314         } else if (challenge.contains("UGFzc3dvcmQA")) {
       
   315             response = password.toUtf8().toBase64();
       
   316             phase = Done;
       
   317         }
       
   318         break;
       
   319     case QAuthenticatorPrivate::CramMd5:
       
   320         break;
       
   321     case QAuthenticatorPrivate::DigestMd5:
       
   322         methodString = "Digest ";
       
   323         response = digestMd5Response(challenge, requestMethod, path);
       
   324         phase = Done;
       
   325         break;
       
   326     case QAuthenticatorPrivate::Ntlm:
       
   327         methodString = "NTLM ";
       
   328         if (challenge.isEmpty()) {
       
   329             response = qNtlmPhase1().toBase64();
       
   330             if (user.isEmpty())
       
   331                 phase = Done;
       
   332             else
       
   333                 phase = Phase2;
       
   334         } else {
       
   335             response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
       
   336             phase = Done;
       
   337         }
       
   338 
       
   339         break;
       
   340     }
       
   341     return QByteArray(methodString) + response;
       
   342 }
       
   343 
       
   344 
       
   345 // ---------------------------- Digest Md5 code ----------------------------------------
       
   346 
       
   347 QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge)
       
   348 {
       
   349     QHash<QByteArray, QByteArray> options;
       
   350     // parse the challenge
       
   351     const char *d = challenge.constData();
       
   352     const char *end = d + challenge.length();
       
   353     while (d < end) {
       
   354         while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
       
   355             ++d;
       
   356         const char *start = d;
       
   357         while (d < end && *d != '=')
       
   358             ++d;
       
   359         QByteArray key = QByteArray(start, d - start);
       
   360         ++d;
       
   361         if (d >= end)
       
   362             break;
       
   363         bool quote = (*d == '"');
       
   364         if (quote)
       
   365             ++d;
       
   366         if (d >= end)
       
   367             break;
       
   368         start = d;
       
   369         QByteArray value;
       
   370         while (d < end) {
       
   371             bool backslash = false;
       
   372             if (*d == '\\' && d < end - 1) {
       
   373                 ++d;
       
   374                 backslash = true;
       
   375             }
       
   376             if (!backslash) {
       
   377                 if (quote) {
       
   378                     if (*d == '"')
       
   379                         break;
       
   380                 } else {
       
   381                     if (*d == ',')
       
   382                         break;
       
   383                 }
       
   384             }
       
   385             value += *d;
       
   386             ++d;
       
   387         }
       
   388         while (d < end && *d != ',')
       
   389             ++d;
       
   390         ++d;
       
   391         options[key] = value;
       
   392     }
       
   393 
       
   394     QByteArray qop = options.value("qop");
       
   395     if (!qop.isEmpty()) {
       
   396         QList<QByteArray> qopoptions = qop.split(',');
       
   397         if (!qopoptions.contains("auth"))
       
   398             return QHash<QByteArray, QByteArray>();
       
   399         // #### can't do auth-int currently
       
   400 //         if (qop.contains("auth-int"))
       
   401 //             qop = "auth-int";
       
   402 //         else if (qop.contains("auth"))
       
   403 //             qop = "auth";
       
   404 //         else
       
   405 //             qop = QByteArray();
       
   406         options["qop"] = "auth";
       
   407     }
       
   408 
       
   409     return options;
       
   410 }
       
   411 
       
   412 /*
       
   413   Digest MD5 implementation
       
   414 
       
   415   Code taken from RFC 2617
       
   416 
       
   417   Currently we don't support the full SASL authentication mechanism (which includes cyphers)
       
   418 */
       
   419 
       
   420 
       
   421 /* calculate request-digest/response-digest as per HTTP Digest spec */
       
   422 static QByteArray digestMd5ResponseHelper(
       
   423     const QByteArray &alg,
       
   424     const QByteArray &userName,
       
   425     const QByteArray &realm,
       
   426     const QByteArray &password,
       
   427     const QByteArray &nonce,       /* nonce from server */
       
   428     const QByteArray &nonceCount,  /* 8 hex digits */
       
   429     const QByteArray &cNonce,      /* client nonce */
       
   430     const QByteArray &qop,         /* qop-value: "", "auth", "auth-int" */
       
   431     const QByteArray &method,      /* method from the request */
       
   432     const QByteArray &digestUri,   /* requested URL */
       
   433     const QByteArray &hEntity       /* H(entity body) if qop="auth-int" */
       
   434     )
       
   435 {
       
   436     QCryptographicHash hash(QCryptographicHash::Md5);
       
   437     hash.addData(userName);
       
   438     hash.addData(":", 1);
       
   439     hash.addData(realm);
       
   440     hash.addData(":", 1);
       
   441     hash.addData(password);
       
   442     QByteArray ha1 = hash.result();
       
   443     if (alg.toLower() == "md5-sess") {
       
   444         hash.reset();
       
   445         // RFC 2617 contains an error, it was:
       
   446         // hash.addData(ha1);
       
   447         // but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it
       
   448         // must be the following line:
       
   449         hash.addData(ha1.toHex());
       
   450         hash.addData(":", 1);
       
   451         hash.addData(nonce);
       
   452         hash.addData(":", 1);
       
   453         hash.addData(cNonce);
       
   454         ha1 = hash.result();
       
   455     };
       
   456     ha1 = ha1.toHex();
       
   457 
       
   458     // calculate H(A2)
       
   459     hash.reset();
       
   460     hash.addData(method);
       
   461     hash.addData(":", 1);
       
   462     hash.addData(digestUri);
       
   463     if (qop.toLower() == "auth-int") {
       
   464         hash.addData(":", 1);
       
   465         hash.addData(hEntity);
       
   466     }
       
   467     QByteArray ha2hex = hash.result().toHex();
       
   468 
       
   469     // calculate response
       
   470     hash.reset();
       
   471     hash.addData(ha1);
       
   472     hash.addData(":", 1);
       
   473     hash.addData(nonce);
       
   474     hash.addData(":", 1);
       
   475     if (!qop.isNull()) {
       
   476         hash.addData(nonceCount);
       
   477         hash.addData(":", 1);
       
   478         hash.addData(cNonce);
       
   479         hash.addData(":", 1);
       
   480         hash.addData(qop);
       
   481         hash.addData(":", 1);
       
   482     }
       
   483     hash.addData(ha2hex);
       
   484     return hash.result().toHex();
       
   485 }
       
   486 
       
   487 QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path)
       
   488 {
       
   489     QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
       
   490 
       
   491     ++nonceCount;
       
   492     QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
       
   493     while (nonceCountString.length() < 8)
       
   494         nonceCountString.prepend('0');
       
   495 
       
   496     QByteArray nonce = options.value("nonce");
       
   497     QByteArray opaque = options.value("opaque");
       
   498     QByteArray qop = options.value("qop");
       
   499 
       
   500 //    qDebug() << "calculating digest: method=" << method << "path=" << path;
       
   501     QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(),
       
   502                                               realm.toLatin1(), password.toLatin1(),
       
   503                                               nonce, nonceCountString,
       
   504                                               cnonce, qop, method,
       
   505                                               path, QByteArray());
       
   506 
       
   507 
       
   508     QByteArray credentials;
       
   509     credentials += "username=\"" + user.toLatin1() + "\", ";
       
   510     credentials += "realm=\"" + realm.toLatin1() + "\", ";
       
   511     credentials += "nonce=\"" + nonce + "\", ";
       
   512     credentials += "uri=\"" + path + "\", ";
       
   513     if (!opaque.isEmpty())
       
   514         credentials += "opaque=\"" + opaque + "\", ";
       
   515     credentials += "response=\"" + response + '\"';
       
   516     if (!options.value("algorithm").isEmpty())
       
   517         credentials += ", algorithm=" + options.value("algorithm");
       
   518     if (!options.value("qop").isEmpty()) {
       
   519         credentials += ", qop=" + qop + ", ";
       
   520         credentials += "nc=" + nonceCountString + ", ";
       
   521         credentials += "cnonce=\"" + cnonce + '\"';
       
   522     }
       
   523 
       
   524     return credentials;
       
   525 }
       
   526 
       
   527 // ---------------------------- Digest Md5 code ----------------------------------------
       
   528 
       
   529 
       
   530 
       
   531 /*
       
   532  * NTLM message flags.
       
   533  *
       
   534  * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
       
   535  *
       
   536  * This software is released under the MIT license.
       
   537  */
       
   538 
       
   539 /*
       
   540  * Indicates that Unicode strings are supported for use in security
       
   541  * buffer data.
       
   542  */
       
   543 #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
       
   544 
       
   545 /*
       
   546  * Indicates that OEM strings are supported for use in security buffer data.
       
   547  */
       
   548 #define NTLMSSP_NEGOTIATE_OEM 0x00000002
       
   549 
       
   550 /*
       
   551  * Requests that the server's authentication realm be included in the
       
   552  * Type 2 message.
       
   553  */
       
   554 #define NTLMSSP_REQUEST_TARGET 0x00000004
       
   555 
       
   556 /*
       
   557  * Specifies that authenticated communication between the client and server
       
   558  * should carry a digital signature (message integrity).
       
   559  */
       
   560 #define NTLMSSP_NEGOTIATE_SIGN 0x00000010
       
   561 
       
   562 /*
       
   563  * Specifies that authenticated communication between the client and server
       
   564  * should be encrypted (message confidentiality).
       
   565  */
       
   566 #define NTLMSSP_NEGOTIATE_SEAL 0x00000020
       
   567 
       
   568 /*
       
   569  * Indicates that datagram authentication is being used.
       
   570  */
       
   571 #define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
       
   572 
       
   573 /*
       
   574  * Indicates that the LAN Manager session key should be
       
   575  * used for signing and sealing authenticated communications.
       
   576  */
       
   577 #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
       
   578 
       
   579 /*
       
   580  * Indicates that NTLM authentication is being used.
       
   581  */
       
   582 #define NTLMSSP_NEGOTIATE_NTLM 0x00000200
       
   583 
       
   584 /*
       
   585  * Sent by the client in the Type 1 message to indicate that the name of the
       
   586  * domain in which the client workstation has membership is included in the
       
   587  * message. This is used by the server to determine whether the client is
       
   588  * eligible for local authentication.
       
   589  */
       
   590 #define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
       
   591 
       
   592 /*
       
   593  * Sent by the client in the Type 1 message to indicate that the client
       
   594  * workstation's name is included in the message. This is used by the server
       
   595  * to determine whether the client is eligible for local authentication.
       
   596  */
       
   597 #define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
       
   598 
       
   599 /*
       
   600  * Sent by the server to indicate that the server and client are on the same
       
   601  * machine. Implies that the client may use the established local credentials
       
   602  * for authentication instead of calculating a response to the challenge.
       
   603  */
       
   604 #define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
       
   605 
       
   606 /*
       
   607  * Indicates that authenticated communication between the client and server
       
   608  * should be signed with a "dummy" signature.
       
   609  */
       
   610 #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
       
   611 
       
   612 /*
       
   613  * Sent by the server in the Type 2 message to indicate that the target
       
   614  * authentication realm is a domain.
       
   615  */
       
   616 #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
       
   617 
       
   618 /*
       
   619  * Sent by the server in the Type 2 message to indicate that the target
       
   620  * authentication realm is a server.
       
   621  */
       
   622 #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
       
   623 
       
   624 /*
       
   625  * Sent by the server in the Type 2 message to indicate that the target
       
   626  * authentication realm is a share. Presumably, this is for share-level
       
   627  * authentication. Usage is unclear.
       
   628  */
       
   629 #define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
       
   630 
       
   631 /*
       
   632  * Indicates that the NTLM2 signing and sealing scheme should be used for
       
   633  * protecting authenticated communications. Note that this refers to a
       
   634  * particular session security scheme, and is not related to the use of
       
   635  * NTLMv2 authentication.
       
   636  */
       
   637 #define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
       
   638 
       
   639 /*
       
   640  * Sent by the server in the Type 2 message to indicate that it is including
       
   641  * a Target Information block in the message. The Target Information block
       
   642  * is used in the calculation of the NTLMv2 response.
       
   643  */
       
   644 #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
       
   645 
       
   646 /*
       
   647  * Indicates that 128-bit encryption is supported.
       
   648  */
       
   649 #define NTLMSSP_NEGOTIATE_128 0x20000000
       
   650 
       
   651 /*
       
   652  * Indicates that the client will provide an encrypted master session key in
       
   653  * the "Session Key" field of the Type 3 message. This is used in signing and
       
   654  * sealing, and is RC4-encrypted using the previous session key as the
       
   655  * encryption key.
       
   656  */
       
   657 #define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
       
   658 
       
   659 /*
       
   660  * Indicates that 56-bit encryption is supported.
       
   661  */
       
   662 #define NTLMSSP_NEGOTIATE_56 0x80000000
       
   663 
       
   664 
       
   665 /* usage:
       
   666    // fill up ctx with what we know.
       
   667    QByteArray response = qNtlmPhase1(ctx);
       
   668    // send response (b64 encoded??)
       
   669    // get response from server (b64 decode?)
       
   670    Phase2Block pb;
       
   671    qNtlmDecodePhase2(response, pb);
       
   672    response = qNtlmPhase3(ctx, pb);
       
   673    // send response (b64 encoded??)
       
   674 */
       
   675 
       
   676 /*
       
   677    TODO:
       
   678     - Fix unicode handling
       
   679     - add v2 handling
       
   680 */
       
   681 
       
   682 class QNtlmBuffer {
       
   683 public:
       
   684     QNtlmBuffer() : len(0), maxLen(0), offset(0) {}
       
   685     quint16 len;
       
   686     quint16 maxLen;
       
   687     quint32 offset;
       
   688     enum { Size = 8 };
       
   689 };
       
   690 
       
   691 class QNtlmPhase1BlockBase
       
   692 {
       
   693 public:
       
   694     char magic[8];
       
   695     quint32 type;
       
   696     quint32 flags;
       
   697     QNtlmBuffer domain;
       
   698     QNtlmBuffer workstation;
       
   699     enum { Size = 32 };
       
   700 };
       
   701 
       
   702 // ################# check paddings
       
   703 class QNtlmPhase2BlockBase
       
   704 {
       
   705 public:
       
   706     char magic[8];
       
   707     quint32 type;
       
   708     QNtlmBuffer targetName;
       
   709     quint32 flags;
       
   710     unsigned char challenge[8];
       
   711     quint32 context[2];
       
   712     QNtlmBuffer targetInfo;
       
   713     enum { Size = 48 };
       
   714 };
       
   715 
       
   716 class QNtlmPhase3BlockBase {
       
   717 public:
       
   718     char magic[8];
       
   719     quint32 type;
       
   720     QNtlmBuffer lmResponse;
       
   721     QNtlmBuffer ntlmResponse;
       
   722     QNtlmBuffer domain;
       
   723     QNtlmBuffer user;
       
   724     QNtlmBuffer workstation;
       
   725     QNtlmBuffer sessionKey;
       
   726     quint32 flags;
       
   727     enum { Size = 64 };
       
   728 };
       
   729 
       
   730 static void qStreamNtlmBuffer(QDataStream& ds, const QByteArray& s)
       
   731 {
       
   732     ds.writeRawData(s.constData(), s.size());
       
   733 }
       
   734 
       
   735 
       
   736 static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode)
       
   737 {
       
   738     if (!unicode) {
       
   739         qStreamNtlmBuffer(ds, s.toLatin1());
       
   740         return;
       
   741     }
       
   742     const ushort *d = s.utf16();
       
   743     for (int i = 0; i < s.length(); ++i)
       
   744         ds << d[i];
       
   745 }
       
   746 
       
   747 
       
   748 
       
   749 static int qEncodeNtlmBuffer(QNtlmBuffer& buf, int offset, const QByteArray& s)
       
   750 {
       
   751     buf.len = s.size();
       
   752     buf.maxLen = buf.len;
       
   753     buf.offset = (offset + 1) & ~1;
       
   754     return buf.offset + buf.len;
       
   755 }
       
   756 
       
   757 
       
   758 static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode)
       
   759 {
       
   760     if (!unicode)
       
   761         return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
       
   762     buf.len = 2 * s.length();
       
   763     buf.maxLen = buf.len;
       
   764     buf.offset = (offset + 1) & ~1;
       
   765     return buf.offset + buf.len;
       
   766 }
       
   767 
       
   768 
       
   769 static QDataStream& operator<<(QDataStream& s, const QNtlmBuffer& b)
       
   770 {
       
   771     s << b.len << b.maxLen << b.offset;
       
   772     return s;
       
   773 }
       
   774 
       
   775 static QDataStream& operator>>(QDataStream& s, QNtlmBuffer& b)
       
   776 {
       
   777     s >> b.len >> b.maxLen >> b.offset;
       
   778     return s;
       
   779 }
       
   780 
       
   781 
       
   782 class QNtlmPhase1Block : public QNtlmPhase1BlockBase
       
   783 {  // request
       
   784 public:
       
   785     QNtlmPhase1Block() {
       
   786         qstrncpy(magic, "NTLMSSP", 8);
       
   787         type = 1;
       
   788         flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET;
       
   789     }
       
   790 
       
   791     // extracted
       
   792     QString domainStr, workstationStr;
       
   793 };
       
   794 
       
   795 
       
   796 class QNtlmPhase2Block : public QNtlmPhase2BlockBase
       
   797 {  // challenge
       
   798 public:
       
   799     QNtlmPhase2Block() {
       
   800         magic[0] = 0;
       
   801         type = 0xffffffff;
       
   802     }
       
   803 
       
   804     // extracted
       
   805     QString targetNameStr, targetInfoStr;
       
   806 };
       
   807 
       
   808 
       
   809 
       
   810 class QNtlmPhase3Block : public QNtlmPhase3BlockBase {  // response
       
   811 public:
       
   812     QNtlmPhase3Block() {
       
   813         qstrncpy(magic, "NTLMSSP", 8);
       
   814         type = 3;
       
   815         flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO;
       
   816     }
       
   817 
       
   818     // extracted
       
   819     QByteArray lmResponseBuf, ntlmResponseBuf;
       
   820     QString domainStr, userStr, workstationStr, sessionKeyStr;
       
   821 };
       
   822 
       
   823 
       
   824 static QDataStream& operator<<(QDataStream& s, const QNtlmPhase1Block& b) {
       
   825     bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
       
   826 
       
   827     s.writeRawData(b.magic, sizeof(b.magic));
       
   828     s << b.type;
       
   829     s << b.flags;
       
   830     s << b.domain;
       
   831     s << b.workstation;
       
   832     if (!b.domainStr.isEmpty())
       
   833         qStreamNtlmString(s, b.domainStr, unicode);
       
   834     if (!b.workstationStr.isEmpty())
       
   835         qStreamNtlmString(s, b.workstationStr, unicode);
       
   836     return s;
       
   837 }
       
   838 
       
   839 
       
   840 static QDataStream& operator<<(QDataStream& s, const QNtlmPhase3Block& b) {
       
   841     bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
       
   842     s.writeRawData(b.magic, sizeof(b.magic));
       
   843     s << b.type;
       
   844     s << b.lmResponse;
       
   845     s << b.ntlmResponse;
       
   846     s << b.domain;
       
   847     s << b.user;
       
   848     s << b.workstation;
       
   849     s << b.sessionKey;
       
   850     s << b.flags;
       
   851 
       
   852     if (!b.domainStr.isEmpty())
       
   853         qStreamNtlmString(s, b.domainStr, unicode);
       
   854 
       
   855     qStreamNtlmString(s, b.userStr, unicode);
       
   856 
       
   857     if (!b.workstationStr.isEmpty())
       
   858         qStreamNtlmString(s, b.workstationStr, unicode);
       
   859 
       
   860     // Send auth info
       
   861     qStreamNtlmBuffer(s, b.lmResponseBuf);
       
   862     qStreamNtlmBuffer(s, b.ntlmResponseBuf);
       
   863 
       
   864 
       
   865     return s;
       
   866 }
       
   867 
       
   868 
       
   869 static QByteArray qNtlmPhase1()
       
   870 {
       
   871     QByteArray rc;
       
   872     QDataStream ds(&rc, QIODevice::WriteOnly);
       
   873     ds.setByteOrder(QDataStream::LittleEndian);
       
   874     QNtlmPhase1Block pb;
       
   875     ds << pb;
       
   876     return rc;
       
   877 }
       
   878 
       
   879 
       
   880 static QByteArray qStringAsUcs2Le(const QString& src)
       
   881 {
       
   882     QByteArray rc(2*src.length(), 0);
       
   883     const unsigned short *s = src.utf16();
       
   884     unsigned short *d = (unsigned short*)rc.data();
       
   885     for (int i = 0; i < src.length(); ++i) {
       
   886         d[i] = qToLittleEndian(s[i]);
       
   887     }
       
   888     return rc;
       
   889 }
       
   890 
       
   891 
       
   892 static QString qStringFromUcs2Le(const QByteArray& src)
       
   893 {
       
   894     Q_ASSERT(src.size() % 2 == 0);
       
   895     unsigned short *d = (unsigned short*)src.data();
       
   896     for (int i = 0; i < src.length() / 2; ++i) {
       
   897         d[i] = qFromLittleEndian(d[i]);
       
   898     }
       
   899     return QString((const QChar *)src.data(), src.size()/2);
       
   900 }
       
   901 
       
   902 
       
   903 static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch)
       
   904 {
       
   905     QCryptographicHash md4(QCryptographicHash::Md4);
       
   906     QByteArray asUcs2Le = qStringAsUcs2Le(ctx->password);
       
   907     md4.addData(asUcs2Le.data(), asUcs2Le.size());
       
   908 
       
   909     unsigned char md4hash[22];
       
   910     memset(md4hash, 0, sizeof(md4hash));
       
   911     QByteArray hash = md4.result();
       
   912     Q_ASSERT(hash.size() == 16);
       
   913     memcpy(md4hash, hash.constData(), 16);
       
   914 
       
   915     QByteArray rc(24, 0);
       
   916     deshash((unsigned char *)rc.data(), md4hash, (unsigned char *)ch.challenge);
       
   917     deshash((unsigned char *)rc.data() + 8, md4hash + 7, (unsigned char *)ch.challenge);
       
   918     deshash((unsigned char *)rc.data() + 16, md4hash + 14, (unsigned char *)ch.challenge);
       
   919 
       
   920     hash.fill(0);
       
   921     return rc;
       
   922 }
       
   923 
       
   924 
       
   925 static QByteArray qEncodeLmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch)
       
   926 {
       
   927     QByteArray hash(21, 0);
       
   928     QByteArray key(14, 0);
       
   929     qstrncpy(key.data(), ctx->password.toUpper().toLatin1(), 14);
       
   930     const char *block = "KGS!@#$%";
       
   931 
       
   932     deshash((unsigned char *)hash.data(), (unsigned char *)key.data(), (unsigned char *)block);
       
   933     deshash((unsigned char *)hash.data() + 8, (unsigned char *)key.data() + 7, (unsigned char *)block);
       
   934     key.fill(0);
       
   935 
       
   936     QByteArray rc(24, 0);
       
   937     deshash((unsigned char *)rc.data(), (unsigned char *)hash.data(), ch.challenge);
       
   938     deshash((unsigned char *)rc.data() + 8, (unsigned char *)hash.data() + 7, ch.challenge);
       
   939     deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge);
       
   940 
       
   941     hash.fill(0);
       
   942     return rc;
       
   943 }
       
   944 
       
   945 
       
   946 static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch)
       
   947 {
       
   948     Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase));
       
   949     if (data.size() < QNtlmPhase2BlockBase::Size)
       
   950         return false;
       
   951 
       
   952 
       
   953     QDataStream ds(data);
       
   954     ds.setByteOrder(QDataStream::LittleEndian);
       
   955     if (ds.readRawData(ch.magic, 8) < 8)
       
   956         return false;
       
   957     if (strncmp(ch.magic, "NTLMSSP", 8) != 0)
       
   958         return false;
       
   959 
       
   960     ds >> ch.type;
       
   961     if (ch.type != 2)
       
   962         return false;
       
   963 
       
   964     ds >> ch.targetName;
       
   965     ds >> ch.flags;
       
   966     if (ds.readRawData((char *)ch.challenge, 8) < 8)
       
   967         return false;
       
   968     ds >> ch.context[0] >> ch.context[1];
       
   969     ds >> ch.targetInfo;
       
   970 
       
   971     if (ch.targetName.len > 0) {
       
   972         if (ch.targetName.len + ch.targetName.offset >= (unsigned)data.size())
       
   973             return false;
       
   974 
       
   975         ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
       
   976     }
       
   977 
       
   978     if (ch.targetInfo.len > 0) {
       
   979         // UNUSED right now
       
   980     }
       
   981 
       
   982     return true;
       
   983 }
       
   984 
       
   985 
       
   986 static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data)
       
   987 {
       
   988     QNtlmPhase2Block ch;
       
   989     if (!qNtlmDecodePhase2(phase2data, ch))
       
   990         return QByteArray();
       
   991 
       
   992     QByteArray rc;
       
   993     QDataStream ds(&rc, QIODevice::WriteOnly);
       
   994     ds.setByteOrder(QDataStream::LittleEndian);
       
   995     QNtlmPhase3Block pb;
       
   996 
       
   997     bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE;
       
   998 
       
   999     ctx->realm = ch.targetNameStr;
       
  1000 
       
  1001     pb.flags = NTLMSSP_NEGOTIATE_NTLM;
       
  1002     if (unicode)
       
  1003         pb.flags |= NTLMSSP_NEGOTIATE_UNICODE;
       
  1004     else
       
  1005         pb.flags |= NTLMSSP_NEGOTIATE_OEM;
       
  1006 
       
  1007 
       
  1008     int offset = QNtlmPhase3BlockBase::Size;
       
  1009     Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase));
       
  1010     
       
  1011     offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode);
       
  1012     pb.domainStr = ctx->realm;
       
  1013     offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode);
       
  1014     pb.userStr = ctx->user;
       
  1015 
       
  1016     offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
       
  1017     pb.workstationStr = ctx->workstation;
       
  1018 
       
  1019     // Get LM response
       
  1020     pb.lmResponseBuf = qEncodeLmResponse(ctx, ch);
       
  1021     offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf);
       
  1022 
       
  1023     // Get NTLM response
       
  1024     pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch);
       
  1025     offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf);
       
  1026 
       
  1027 
       
  1028     // Encode and send
       
  1029     ds << pb;
       
  1030 
       
  1031     return rc;
       
  1032 }
       
  1033 
       
  1034 
       
  1035 
       
  1036 QT_END_NAMESPACE