1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <qauthenticator.h>
41 #include <qauthenticator_p.h>
42 #include <qdebug.h>
43 #include <qloggingcategory.h>
44 #include <qhash.h>
45 #include <qbytearray.h>
46 #include <qcryptographichash.h>
47 #include <qiodevice.h>
48 #include <qdatastream.h>
49 #include <qendian.h>
50 #include <qstring.h>
51 #include <qdatetime.h>
52 #include <qrandom.h>
53 
54 #ifdef Q_OS_WIN
55 #include <qmutex.h>
56 #include <rpc.h>
57 #endif
58 
59 #if QT_CONFIG(sspi) // SSPI
60 #define SECURITY_WIN32 1
61 #include <security.h>
62 #elif QT_CONFIG(gssapi) // GSSAPI
63 #if defined(Q_OS_DARWIN)
64 #include <GSS/GSS.h>
65 #else
66 #include <gssapi/gssapi.h>
67 #endif // Q_OS_DARWIN
68 #endif // Q_CONFIG(sspi)
69 
70 QT_BEGIN_NAMESPACE
71 
72 Q_DECLARE_LOGGING_CATEGORY(lcAuthenticator);
73 Q_LOGGING_CATEGORY(lcAuthenticator, "qt.network.authenticator");
74 
75 static QByteArray qNtlmPhase1();
76 static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
77 #if QT_CONFIG(sspi) // SSPI
78 static bool q_SSPI_library_load();
79 static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
80                                const QString& host);
81 static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
82                                 const QString& host, const QByteArray& challenge = QByteArray());
83 #elif QT_CONFIG(gssapi) // GSSAPI
84 static bool qGssapiTestGetCredentials(const QString &host);
85 static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString& host);
86 static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx,
87                                   const QByteArray& challenge = QByteArray());
88 #endif // gssapi
89 
90 /*!
91   \class QAuthenticator
92   \brief The QAuthenticator class provides an authentication object.
93   \since 4.3
94 
95   \reentrant
96   \ingroup network
97   \inmodule QtNetwork
98 
99   The QAuthenticator class is usually used in the
100   \l{QNetworkAccessManager::}{authenticationRequired()} and
101   \l{QNetworkAccessManager::}{proxyAuthenticationRequired()} signals of QNetworkAccessManager and
102   QAbstractSocket. The class provides a way to pass back the required
103   authentication information to the socket when accessing services that
104   require authentication.
105 
106   QAuthenticator supports the following authentication methods:
107   \list
108     \li Basic
109     \li NTLM version 2
110     \li Digest-MD5
111     \li SPNEGO/Negotiate
112   \endlist
113 
114   \target qauthenticator-options
115   \section1 Options
116 
117   In addition to the username and password required for authentication, a
118   QAuthenticator object can also contain additional options. The
119   options() function can be used to query incoming options sent by
120   the server; the setOption() function can
121   be used to set outgoing options, to be processed by the authenticator
122   calculation. The options accepted and provided depend on the authentication
123   type (see method()).
124 
125   The following tables list known incoming options as well as accepted
126   outgoing options. The list of incoming options is not exhaustive, since
127   servers may include additional information at any time. The list of
128   outgoing options is exhaustive, however, and no unknown options will be
129   treated or sent back to the server.
130 
131   \section2 Basic
132 
133   \table
134     \header \li Option \li Direction \li Type \li Description
135     \row \li \tt{realm} \li Incoming \li QString \li Contains the realm of the authentication, the same as realm()
136   \endtable
137 
138   The Basic authentication mechanism supports no outgoing options.
139 
140   \section2 NTLM version 2
141 
142   The NTLM authentication mechanism currently supports no incoming or outgoing options.
143   On Windows, if no \a user has been set, domain\\user credentials will be searched for on the
144   local system to enable Single-Sign-On functionality.
145 
146   \section2 Digest-MD5
147 
148   \table
149     \header \li Option \li Direction \li Type \li Description
150     \row \li \tt{realm} \li Incoming \li QString \li Contains the realm of the authentication, the same as realm()
151   \endtable
152 
153   The Digest-MD5 authentication mechanism supports no outgoing options.
154 
155   \section2 SPNEGO/Negotiate
156 
157   This authentication mechanism currently supports no incoming or outgoing options.
158 
159   \sa QSslSocket
160 */
161 
162 
163 /*!
164   Constructs an empty authentication object.
165 */
QAuthenticator()166 QAuthenticator::QAuthenticator()
167     : d(nullptr)
168 {
169 }
170 
171 /*!
172   Destructs the object.
173 */
~QAuthenticator()174 QAuthenticator::~QAuthenticator()
175 {
176     if (d)
177         delete d;
178 }
179 
180 /*!
181     Constructs a copy of \a other.
182 */
QAuthenticator(const QAuthenticator & other)183 QAuthenticator::QAuthenticator(const QAuthenticator &other)
184     : d(nullptr)
185 {
186     if (other.d)
187         *this = other;
188 }
189 
190 /*!
191     Assigns the contents of \a other to this authenticator.
192 */
operator =(const QAuthenticator & other)193 QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
194 {
195     if (d == other.d)
196         return *this;
197 
198     // Do not share the d since challange reponse/based changes
199     // could corrupt the internal store and different network requests
200     // can utilize different types of proxies.
201     detach();
202     if (other.d) {
203         d->user = other.d->user;
204         d->userDomain = other.d->userDomain;
205         d->workstation = other.d->workstation;
206         d->extractedUser = other.d->extractedUser;
207         d->password = other.d->password;
208         d->realm = other.d->realm;
209         d->method = other.d->method;
210         d->options = other.d->options;
211     } else if (d->phase == QAuthenticatorPrivate::Start) {
212         delete d;
213         d = nullptr;
214     }
215     return *this;
216 }
217 
218 /*!
219     Returns \c true if this authenticator is identical to \a other; otherwise
220     returns \c false.
221 */
operator ==(const QAuthenticator & other) const222 bool QAuthenticator::operator==(const QAuthenticator &other) const
223 {
224     if (d == other.d)
225         return true;
226     if (!d || !other.d)
227         return false;
228     return d->user == other.d->user
229         && d->password == other.d->password
230         && d->realm == other.d->realm
231         && d->method == other.d->method
232         && d->options == other.d->options;
233 }
234 
235 /*!
236     \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const
237 
238     Returns \c true if this authenticator is different from \a other; otherwise
239     returns \c false.
240 */
241 
242 /*!
243   Returns the user used for authentication.
244 */
user() const245 QString QAuthenticator::user() const
246 {
247     return d ? d->user : QString();
248 }
249 
250 /*!
251   Sets the \a user used for authentication.
252 
253   \sa QNetworkAccessManager::authenticationRequired()
254 */
setUser(const QString & user)255 void QAuthenticator::setUser(const QString &user)
256 {
257     if (!d || d->user != user) {
258         detach();
259         d->user = user;
260         d->updateCredentials();
261     }
262 }
263 
264 /*!
265   Returns the password used for authentication.
266 */
password() const267 QString QAuthenticator::password() const
268 {
269     return d ? d->password : QString();
270 }
271 
272 /*!
273   Sets the \a password used for authentication.
274 
275   \sa QNetworkAccessManager::authenticationRequired()
276 */
setPassword(const QString & password)277 void QAuthenticator::setPassword(const QString &password)
278 {
279     if (!d || d->password != password) {
280         detach();
281         d->password = password;
282     }
283 }
284 
285 /*!
286   \internal
287 */
detach()288 void QAuthenticator::detach()
289 {
290     if (!d) {
291         d = new QAuthenticatorPrivate;
292         return;
293     }
294 
295     if (d->phase == QAuthenticatorPrivate::Done)
296         d->phase = QAuthenticatorPrivate::Start;
297 }
298 
299 /*!
300   Returns the realm requiring authentication.
301 */
realm() const302 QString QAuthenticator::realm() const
303 {
304     return d ? d->realm : QString();
305 }
306 
307 /*!
308   \internal
309 */
setRealm(const QString & realm)310 void QAuthenticator::setRealm(const QString &realm)
311 {
312     if (!d || d->realm != realm) {
313         detach();
314         d->realm = realm;
315     }
316 }
317 
318 /*!
319     \since 4.7
320     Returns the value related to option \a opt if it was set by the server.
321     See the \l{QAuthenticator#qauthenticator-options}{Options section} for
322     more information on incoming options.
323     If option \a opt isn't found, an invalid QVariant will be returned.
324 
325     \sa options(), {QAuthenticator#qauthenticator-options}{QAuthenticator options}
326 */
option(const QString & opt) const327 QVariant QAuthenticator::option(const QString &opt) const
328 {
329     return d ? d->options.value(opt) : QVariant();
330 }
331 
332 /*!
333     \since 4.7
334     Returns all incoming options set in this QAuthenticator object by parsing
335     the server reply. See the \l{QAuthenticator#qauthenticator-options}{Options section}
336     for more information on incoming options.
337 
338     \sa option(), {QAuthenticator#qauthenticator-options}{QAuthenticator options}
339 */
options() const340 QVariantHash QAuthenticator::options() const
341 {
342     return d ? d->options : QVariantHash();
343 }
344 
345 /*!
346     \since 4.7
347 
348     Sets the outgoing option \a opt to value \a value.
349     See the \l{QAuthenticator#qauthenticator-options}{Options section} for more information on outgoing options.
350 
351     \sa options(), option(), {QAuthenticator#qauthenticator-options}{QAuthenticator options}
352 */
setOption(const QString & opt,const QVariant & value)353 void QAuthenticator::setOption(const QString &opt, const QVariant &value)
354 {
355     if (option(opt) != value) {
356         detach();
357         d->options.insert(opt, value);
358     }
359 }
360 
361 
362 /*!
363     Returns \c true if the object has not been initialized. Returns
364     \c false if non-const member functions have been called, or
365     the content was constructed or copied from another initialized
366     QAuthenticator object.
367 */
isNull() const368 bool QAuthenticator::isNull() const
369 {
370     return !d;
371 }
372 
373 #if QT_CONFIG(sspi) // SSPI
374 class QSSPIWindowsHandles
375 {
376 public:
377     CredHandle credHandle;
378     CtxtHandle ctxHandle;
379 };
380 #elif QT_CONFIG(gssapi) // GSSAPI
381 class QGssApiHandles
382 {
383 public:
384     gss_ctx_id_t gssCtx = nullptr;
385     gss_name_t targetName;
386 };
387 #endif // gssapi
388 
389 
QAuthenticatorPrivate()390 QAuthenticatorPrivate::QAuthenticatorPrivate()
391     : method(None)
392     , hasFailed(false)
393     , phase(Start)
394     , nonceCount(0)
395 {
396     cnonce = QCryptographicHash::hash(QByteArray::number(QRandomGenerator::system()->generate64(), 16),
397                                       QCryptographicHash::Md5).toHex();
398     nonceCount = 0;
399 }
400 
401 QAuthenticatorPrivate::~QAuthenticatorPrivate() = default;
402 
updateCredentials()403 void QAuthenticatorPrivate::updateCredentials()
404 {
405     int separatorPosn = 0;
406 
407     switch (method) {
408     case QAuthenticatorPrivate::Ntlm:
409         if ((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) {
410             //domain name is present
411             realm.clear();
412             userDomain = user.left(separatorPosn);
413             extractedUser = user.mid(separatorPosn + 1);
414         } else {
415             extractedUser = user;
416             realm.clear();
417             userDomain.clear();
418         }
419         break;
420     default:
421         userDomain.clear();
422         break;
423     }
424 }
425 
parseHttpResponse(const QList<QPair<QByteArray,QByteArray>> & values,bool isProxy,const QString & host)426 void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy, const QString &host)
427 {
428 #if !QT_CONFIG(gssapi)
429     Q_UNUSED(host);
430 #endif
431     const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
432 
433     method = None;
434     /*
435       Fun from the HTTP 1.1 specs, that we currently ignore:
436 
437       User agents are advised to take special care in parsing the WWW-
438       Authenticate field value as it might contain more than one challenge,
439       or if more than one WWW-Authenticate header field is provided, the
440       contents of a challenge itself can contain a comma-separated list of
441       authentication parameters.
442     */
443 
444     QByteArray headerVal;
445     for (int i = 0; i < values.size(); ++i) {
446         const QPair<QByteArray, QByteArray> &current = values.at(i);
447         if (current.first.compare(search, Qt::CaseInsensitive) != 0)
448             continue;
449         QByteArray str = current.second.toLower();
450         if (method < Basic && str.startsWith("basic")) {
451             method = Basic;
452             headerVal = current.second.mid(6);
453         } else if (method < Ntlm && str.startsWith("ntlm")) {
454             method = Ntlm;
455             headerVal = current.second.mid(5);
456         } else if (method < DigestMd5 && str.startsWith("digest")) {
457             method = DigestMd5;
458             headerVal = current.second.mid(7);
459         } else if (method < Negotiate && str.startsWith("negotiate")) {
460 #if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
461 #if QT_CONFIG(gssapi)
462             // For GSSAPI there needs to be a KDC set up for the host (afaict).
463             // So let's only conditionally use it if we can fetch the credentials.
464             // Sadly it's a bit slow because it requires a DNS lookup.
465             if (!qGssapiTestGetCredentials(host))
466                 continue;
467 #endif
468             method = Negotiate;
469             headerVal = current.second.mid(10);
470 #endif
471         }
472     }
473 
474     // Reparse credentials since we know the method now
475     updateCredentials();
476     challenge = headerVal.trimmed();
477     QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
478 
479     // Sets phase to Start if this updates our realm and sets the two locations where we store
480     // realm
481     auto privSetRealm = [this](QString newRealm) {
482         if (newRealm != realm) {
483             if (phase == Done)
484                 phase = Start;
485             realm = newRealm;
486             this->options[QLatin1String("realm")] = realm;
487         }
488     };
489 
490     switch(method) {
491     case Basic:
492         privSetRealm(QString::fromLatin1(options.value("realm")));
493         if (user.isEmpty() && password.isEmpty())
494             phase = Done;
495         break;
496     case Ntlm:
497     case Negotiate:
498         // work is done in calculateResponse()
499         break;
500     case DigestMd5: {
501         privSetRealm(QString::fromLatin1(options.value("realm")));
502         if (options.value("stale").compare("true", Qt::CaseInsensitive) == 0) {
503             phase = Start;
504             nonceCount = 0;
505         }
506         if (user.isEmpty() && password.isEmpty())
507             phase = Done;
508         break;
509     }
510     default:
511         realm.clear();
512         challenge = QByteArray();
513         phase = Invalid;
514     }
515 }
516 
calculateResponse(const QByteArray & requestMethod,const QByteArray & path,const QString & host)517 QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path, const QString& host)
518 {
519 #if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
520     Q_UNUSED(host);
521 #endif
522     QByteArray response;
523     const char* methodString = nullptr;
524     switch(method) {
525     case QAuthenticatorPrivate::None:
526         methodString = "";
527         phase = Done;
528         break;
529     case QAuthenticatorPrivate::Basic:
530         methodString = "Basic";
531         response = user.toLatin1() + ':' + password.toLatin1();
532         response = response.toBase64();
533         phase = Done;
534         break;
535     case QAuthenticatorPrivate::DigestMd5:
536         methodString = "Digest";
537         response = digestMd5Response(challenge, requestMethod, path);
538         phase = Done;
539         break;
540     case QAuthenticatorPrivate::Ntlm:
541         methodString = "NTLM";
542         if (challenge.isEmpty()) {
543 #if QT_CONFIG(sspi) // SSPI
544             QByteArray phase1Token;
545             if (user.isEmpty()) { // Only pull from system if no user was specified in authenticator
546                 phase1Token = qSspiStartup(this, method, host);
547             } else if (!q_SSPI_library_load()) {
548                 // Since we're not running qSspiStartup we have to make sure the library is loaded
549                 qWarning("Failed to load the SSPI libraries");
550                 return "";
551             }
552             if (!phase1Token.isEmpty()) {
553                 response = phase1Token.toBase64();
554                 phase = Phase2;
555             } else
556 #endif
557             {
558                 response = qNtlmPhase1().toBase64();
559                 if (user.isEmpty())
560                     phase = Done;
561                 else
562                     phase = Phase2;
563             }
564         } else {
565 #if QT_CONFIG(sspi) // SSPI
566             QByteArray phase3Token;
567             if (sspiWindowsHandles)
568                 phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
569             if (!phase3Token.isEmpty()) {
570                 response = phase3Token.toBase64();
571                 phase = Done;
572             } else
573 #endif
574             {
575                 response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
576                 phase = Done;
577             }
578             challenge = "";
579         }
580 
581         break;
582     case QAuthenticatorPrivate::Negotiate:
583         methodString = "Negotiate";
584         if (challenge.isEmpty()) {
585             QByteArray phase1Token;
586 #if QT_CONFIG(sspi) // SSPI
587             phase1Token = qSspiStartup(this, method, host);
588 #elif QT_CONFIG(gssapi) // GSSAPI
589             phase1Token = qGssapiStartup(this, host);
590 #endif
591 
592             if (!phase1Token.isEmpty()) {
593                 response = phase1Token.toBase64();
594                 phase = Phase2;
595             } else {
596                 phase = Done;
597                 return "";
598             }
599         } else {
600             QByteArray phase3Token;
601 #if QT_CONFIG(sspi) // SSPI
602             phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
603 #elif QT_CONFIG(gssapi) // GSSAPI
604             phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
605 #endif
606             if (!phase3Token.isEmpty()) {
607                 response = phase3Token.toBase64();
608                 phase = Done;
609                 challenge = "";
610             } else {
611                 phase = Done;
612                 return "";
613             }
614         }
615 
616         break;
617     }
618 
619     return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
620 }
621 
622 
623 // ---------------------------- Digest Md5 code ----------------------------------------
624 
parseDigestAuthenticationChallenge(const QByteArray & challenge)625 QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge)
626 {
627     QHash<QByteArray, QByteArray> options;
628     // parse the challenge
629     const char *d = challenge.constData();
630     const char *end = d + challenge.length();
631     while (d < end) {
632         while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
633             ++d;
634         const char *start = d;
635         while (d < end && *d != '=')
636             ++d;
637         QByteArray key = QByteArray(start, d - start);
638         ++d;
639         if (d >= end)
640             break;
641         bool quote = (*d == '"');
642         if (quote)
643             ++d;
644         if (d >= end)
645             break;
646         start = d;
647         QByteArray value;
648         while (d < end) {
649             bool backslash = false;
650             if (*d == '\\' && d < end - 1) {
651                 ++d;
652                 backslash = true;
653             }
654             if (!backslash) {
655                 if (quote) {
656                     if (*d == '"')
657                         break;
658                 } else {
659                     if (*d == ',')
660                         break;
661                 }
662             }
663             value += *d;
664             ++d;
665         }
666         while (d < end && *d != ',')
667             ++d;
668         ++d;
669         options[key] = value;
670     }
671 
672     QByteArray qop = options.value("qop");
673     if (!qop.isEmpty()) {
674         QList<QByteArray> qopoptions = qop.split(',');
675         if (!qopoptions.contains("auth"))
676             return QHash<QByteArray, QByteArray>();
677         // #### can't do auth-int currently
678 //         if (qop.contains("auth-int"))
679 //             qop = "auth-int";
680 //         else if (qop.contains("auth"))
681 //             qop = "auth";
682 //         else
683 //             qop = QByteArray();
684         options["qop"] = "auth";
685     }
686 
687     return options;
688 }
689 
690 /*
691   Digest MD5 implementation
692 
693   Code taken from RFC 2617
694 
695   Currently we don't support the full SASL authentication mechanism (which includes cyphers)
696 */
697 
698 
699 /* calculate request-digest/response-digest as per HTTP Digest spec */
digestMd5ResponseHelper(const QByteArray & alg,const QByteArray & userName,const QByteArray & realm,const QByteArray & password,const QByteArray & nonce,const QByteArray & nonceCount,const QByteArray & cNonce,const QByteArray & qop,const QByteArray & method,const QByteArray & digestUri,const QByteArray & hEntity)700 static QByteArray digestMd5ResponseHelper(
701     const QByteArray &alg,
702     const QByteArray &userName,
703     const QByteArray &realm,
704     const QByteArray &password,
705     const QByteArray &nonce,       /* nonce from server */
706     const QByteArray &nonceCount,  /* 8 hex digits */
707     const QByteArray &cNonce,      /* client nonce */
708     const QByteArray &qop,         /* qop-value: "", "auth", "auth-int" */
709     const QByteArray &method,      /* method from the request */
710     const QByteArray &digestUri,   /* requested URL */
711     const QByteArray &hEntity       /* H(entity body) if qop="auth-int" */
712     )
713 {
714     QCryptographicHash hash(QCryptographicHash::Md5);
715     hash.addData(userName);
716     hash.addData(":", 1);
717     hash.addData(realm);
718     hash.addData(":", 1);
719     hash.addData(password);
720     QByteArray ha1 = hash.result();
721     if (alg.compare("md5-sess", Qt::CaseInsensitive) == 0) {
722         hash.reset();
723         // RFC 2617 contains an error, it was:
724         // hash.addData(ha1);
725         // but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it
726         // must be the following line:
727         hash.addData(ha1.toHex());
728         hash.addData(":", 1);
729         hash.addData(nonce);
730         hash.addData(":", 1);
731         hash.addData(cNonce);
732         ha1 = hash.result();
733     };
734     ha1 = ha1.toHex();
735 
736     // calculate H(A2)
737     hash.reset();
738     hash.addData(method);
739     hash.addData(":", 1);
740     hash.addData(digestUri);
741     if (qop.compare("auth-int", Qt::CaseInsensitive) == 0) {
742         hash.addData(":", 1);
743         hash.addData(hEntity);
744     }
745     QByteArray ha2hex = hash.result().toHex();
746 
747     // calculate response
748     hash.reset();
749     hash.addData(ha1);
750     hash.addData(":", 1);
751     hash.addData(nonce);
752     hash.addData(":", 1);
753     if (!qop.isNull()) {
754         hash.addData(nonceCount);
755         hash.addData(":", 1);
756         hash.addData(cNonce);
757         hash.addData(":", 1);
758         hash.addData(qop);
759         hash.addData(":", 1);
760     }
761     hash.addData(ha2hex);
762     return hash.result().toHex();
763 }
764 
digestMd5Response(const QByteArray & challenge,const QByteArray & method,const QByteArray & path)765 QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path)
766 {
767     QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
768 
769     ++nonceCount;
770     QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
771     while (nonceCountString.length() < 8)
772         nonceCountString.prepend('0');
773 
774     QByteArray nonce = options.value("nonce");
775     QByteArray opaque = options.value("opaque");
776     QByteArray qop = options.value("qop");
777 
778 //    qDebug() << "calculating digest: method=" << method << "path=" << path;
779     QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(),
780                                               realm.toLatin1(), password.toLatin1(),
781                                               nonce, nonceCountString,
782                                               cnonce, qop, method,
783                                               path, QByteArray());
784 
785 
786     QByteArray credentials;
787     credentials += "username=\"" + user.toLatin1() + "\", ";
788     credentials += "realm=\"" + realm.toLatin1() + "\", ";
789     credentials += "nonce=\"" + nonce + "\", ";
790     credentials += "uri=\"" + path + "\", ";
791     if (!opaque.isEmpty())
792         credentials += "opaque=\"" + opaque + "\", ";
793     credentials += "response=\"" + response + '"';
794     if (!options.value("algorithm").isEmpty())
795         credentials += ", algorithm=" + options.value("algorithm");
796     if (!options.value("qop").isEmpty()) {
797         credentials += ", qop=" + qop + ", ";
798         credentials += "nc=" + nonceCountString + ", ";
799         credentials += "cnonce=\"" + cnonce + '"';
800     }
801 
802     return credentials;
803 }
804 
805 // ---------------------------- End of Digest Md5 code ---------------------------------
806 
807 
808 // ---------------------------- NTLM code ----------------------------------------------
809 
810 /*
811  * NTLM message flags.
812  *
813  * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
814  *
815  * This software is released under the MIT license.
816  */
817 
818 /*
819  * Indicates that Unicode strings are supported for use in security
820  * buffer data.
821  */
822 #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
823 
824 /*
825  * Indicates that OEM strings are supported for use in security buffer data.
826  */
827 #define NTLMSSP_NEGOTIATE_OEM 0x00000002
828 
829 /*
830  * Requests that the server's authentication realm be included in the
831  * Type 2 message.
832  */
833 #define NTLMSSP_REQUEST_TARGET 0x00000004
834 
835 /*
836  * Specifies that authenticated communication between the client and server
837  * should carry a digital signature (message integrity).
838  */
839 #define NTLMSSP_NEGOTIATE_SIGN 0x00000010
840 
841 /*
842  * Specifies that authenticated communication between the client and server
843  * should be encrypted (message confidentiality).
844  */
845 #define NTLMSSP_NEGOTIATE_SEAL 0x00000020
846 
847 /*
848  * Indicates that datagram authentication is being used.
849  */
850 #define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
851 
852 /*
853  * Indicates that the LAN Manager session key should be
854  * used for signing and sealing authenticated communications.
855  */
856 #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
857 
858 /*
859  * Indicates that NTLM authentication is being used.
860  */
861 #define NTLMSSP_NEGOTIATE_NTLM 0x00000200
862 
863 /*
864  * Sent by the client in the Type 1 message to indicate that the name of the
865  * domain in which the client workstation has membership is included in the
866  * message. This is used by the server to determine whether the client is
867  * eligible for local authentication.
868  */
869 #define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
870 
871 /*
872  * Sent by the client in the Type 1 message to indicate that the client
873  * workstation's name is included in the message. This is used by the server
874  * to determine whether the client is eligible for local authentication.
875  */
876 #define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
877 
878 /*
879  * Sent by the server to indicate that the server and client are on the same
880  * machine. Implies that the client may use the established local credentials
881  * for authentication instead of calculating a response to the challenge.
882  */
883 #define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
884 
885 /*
886  * Indicates that authenticated communication between the client and server
887  * should be signed with a "dummy" signature.
888  */
889 #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
890 
891 /*
892  * Sent by the server in the Type 2 message to indicate that the target
893  * authentication realm is a domain.
894  */
895 #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
896 
897 /*
898  * Sent by the server in the Type 2 message to indicate that the target
899  * authentication realm is a server.
900  */
901 #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
902 
903 /*
904  * Sent by the server in the Type 2 message to indicate that the target
905  * authentication realm is a share. Presumably, this is for share-level
906  * authentication. Usage is unclear.
907  */
908 #define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
909 
910 /*
911  * Indicates that the NTLM2 signing and sealing scheme should be used for
912  * protecting authenticated communications. Note that this refers to a
913  * particular session security scheme, and is not related to the use of
914  * NTLMv2 authentication.
915  */
916 #define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
917 
918 /*
919  * Sent by the server in the Type 2 message to indicate that it is including
920  * a Target Information block in the message. The Target Information block
921  * is used in the calculation of the NTLMv2 response.
922  */
923 #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
924 
925 /*
926  * Indicates that 128-bit encryption is supported.
927  */
928 #define NTLMSSP_NEGOTIATE_128 0x20000000
929 
930 /*
931  * Indicates that the client will provide an encrypted master session key in
932  * the "Session Key" field of the Type 3 message. This is used in signing and
933  * sealing, and is RC4-encrypted using the previous session key as the
934  * encryption key.
935  */
936 #define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
937 
938 /*
939  * Indicates that 56-bit encryption is supported.
940  */
941 #define NTLMSSP_NEGOTIATE_56 0x80000000
942 
943 /*
944  * AvId values
945  */
946 #define AVTIMESTAMP 7
947 
948 
949 //************************Global variables***************************
950 
951 const int blockSize = 64; //As per RFC2104 Block-size is 512 bits
952 const quint8 respversion = 1;
953 const quint8 hirespversion = 1;
954 
955 /* usage:
956    // fill up ctx with what we know.
957    QByteArray response = qNtlmPhase1(ctx);
958    // send response (b64 encoded??)
959    // get response from server (b64 decode?)
960    Phase2Block pb;
961    qNtlmDecodePhase2(response, pb);
962    response = qNtlmPhase3(ctx, pb);
963    // send response (b64 encoded??)
964 */
965 
966 /*
967    TODO:
968     - Fix unicode handling
969     - add v2 handling
970 */
971 
972 class QNtlmBuffer {
973 public:
QNtlmBuffer()974     QNtlmBuffer() : len(0), maxLen(0), offset(0) {}
975     quint16 len;
976     quint16 maxLen;
977     quint32 offset;
978     enum { Size = 8 };
979 };
980 
981 class QNtlmPhase1BlockBase
982 {
983 public:
984     char magic[8];
985     quint32 type;
986     quint32 flags;
987     QNtlmBuffer domain;
988     QNtlmBuffer workstation;
989     enum { Size = 32 };
990 };
991 
992 // ################# check paddings
993 class QNtlmPhase2BlockBase
994 {
995 public:
996     char magic[8];
997     quint32 type;
998     QNtlmBuffer targetName;
999     quint32 flags;
1000     unsigned char challenge[8];
1001     quint32 context[2];
1002     QNtlmBuffer targetInfo;
1003     enum { Size = 48 };
1004 };
1005 
1006 class QNtlmPhase3BlockBase {
1007 public:
1008     char magic[8];
1009     quint32 type;
1010     QNtlmBuffer lmResponse;
1011     QNtlmBuffer ntlmResponse;
1012     QNtlmBuffer domain;
1013     QNtlmBuffer user;
1014     QNtlmBuffer workstation;
1015     QNtlmBuffer sessionKey;
1016     quint32 flags;
1017     enum { Size = 64 };
1018 };
1019 
qStreamNtlmBuffer(QDataStream & ds,const QByteArray & s)1020 static void qStreamNtlmBuffer(QDataStream& ds, const QByteArray& s)
1021 {
1022     ds.writeRawData(s.constData(), s.size());
1023 }
1024 
1025 
qStreamNtlmString(QDataStream & ds,const QString & s,bool unicode)1026 static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode)
1027 {
1028     if (!unicode) {
1029         qStreamNtlmBuffer(ds, s.toLatin1());
1030         return;
1031     }
1032     const ushort *d = s.utf16();
1033     for (int i = 0; i < s.length(); ++i)
1034         ds << d[i];
1035 }
1036 
1037 
1038 
qEncodeNtlmBuffer(QNtlmBuffer & buf,int offset,const QByteArray & s)1039 static int qEncodeNtlmBuffer(QNtlmBuffer& buf, int offset, const QByteArray& s)
1040 {
1041     buf.len = s.size();
1042     buf.maxLen = buf.len;
1043     buf.offset = (offset + 1) & ~1;
1044     return buf.offset + buf.len;
1045 }
1046 
1047 
qEncodeNtlmString(QNtlmBuffer & buf,int offset,const QString & s,bool unicode)1048 static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode)
1049 {
1050     if (!unicode)
1051         return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
1052     buf.len = 2 * s.length();
1053     buf.maxLen = buf.len;
1054     buf.offset = (offset + 1) & ~1;
1055     return buf.offset + buf.len;
1056 }
1057 
1058 
operator <<(QDataStream & s,const QNtlmBuffer & b)1059 static QDataStream& operator<<(QDataStream& s, const QNtlmBuffer& b)
1060 {
1061     s << b.len << b.maxLen << b.offset;
1062     return s;
1063 }
1064 
operator >>(QDataStream & s,QNtlmBuffer & b)1065 static QDataStream& operator>>(QDataStream& s, QNtlmBuffer& b)
1066 {
1067     s >> b.len >> b.maxLen >> b.offset;
1068     return s;
1069 }
1070 
1071 
1072 class QNtlmPhase1Block : public QNtlmPhase1BlockBase
1073 {  // request
1074 public:
QNtlmPhase1Block()1075     QNtlmPhase1Block() {
1076         qstrncpy(magic, "NTLMSSP", 8);
1077         type = 1;
1078         flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM2;
1079     }
1080 
1081     // extracted
1082     QString domainStr, workstationStr;
1083 };
1084 
1085 
1086 class QNtlmPhase2Block : public QNtlmPhase2BlockBase
1087 {  // challenge
1088 public:
QNtlmPhase2Block()1089     QNtlmPhase2Block() {
1090         magic[0] = 0;
1091         type = 0xffffffff;
1092     }
1093 
1094     // extracted
1095     QString targetNameStr, targetInfoStr;
1096     QByteArray targetInfoBuff;
1097 };
1098 
1099 
1100 
1101 class QNtlmPhase3Block : public QNtlmPhase3BlockBase {  // response
1102 public:
QNtlmPhase3Block()1103     QNtlmPhase3Block() {
1104         qstrncpy(magic, "NTLMSSP", 8);
1105         type = 3;
1106         flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO;
1107     }
1108 
1109     // extracted
1110     QByteArray lmResponseBuf, ntlmResponseBuf;
1111     QString domainStr, userStr, workstationStr, sessionKeyStr;
1112     QByteArray v2Hash;
1113 };
1114 
1115 
operator <<(QDataStream & s,const QNtlmPhase1Block & b)1116 static QDataStream& operator<<(QDataStream& s, const QNtlmPhase1Block& b) {
1117     bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
1118 
1119     s.writeRawData(b.magic, sizeof(b.magic));
1120     s << b.type;
1121     s << b.flags;
1122     s << b.domain;
1123     s << b.workstation;
1124     if (!b.domainStr.isEmpty())
1125         qStreamNtlmString(s, b.domainStr, unicode);
1126     if (!b.workstationStr.isEmpty())
1127         qStreamNtlmString(s, b.workstationStr, unicode);
1128     return s;
1129 }
1130 
1131 
operator <<(QDataStream & s,const QNtlmPhase3Block & b)1132 static QDataStream& operator<<(QDataStream& s, const QNtlmPhase3Block& b) {
1133     bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
1134     s.writeRawData(b.magic, sizeof(b.magic));
1135     s << b.type;
1136     s << b.lmResponse;
1137     s << b.ntlmResponse;
1138     s << b.domain;
1139     s << b.user;
1140     s << b.workstation;
1141     s << b.sessionKey;
1142     s << b.flags;
1143 
1144     if (!b.domainStr.isEmpty())
1145         qStreamNtlmString(s, b.domainStr, unicode);
1146 
1147     qStreamNtlmString(s, b.userStr, unicode);
1148 
1149     if (!b.workstationStr.isEmpty())
1150         qStreamNtlmString(s, b.workstationStr, unicode);
1151 
1152     // Send auth info
1153     qStreamNtlmBuffer(s, b.lmResponseBuf);
1154     qStreamNtlmBuffer(s, b.ntlmResponseBuf);
1155 
1156 
1157     return s;
1158 }
1159 
1160 
qNtlmPhase1()1161 static QByteArray qNtlmPhase1()
1162 {
1163     QByteArray rc;
1164     QDataStream ds(&rc, QIODevice::WriteOnly);
1165     ds.setByteOrder(QDataStream::LittleEndian);
1166     QNtlmPhase1Block pb;
1167     ds << pb;
1168     return rc;
1169 }
1170 
1171 
qStringAsUcs2Le(const QString & src)1172 static QByteArray qStringAsUcs2Le(const QString& src)
1173 {
1174     QByteArray rc(2*src.length(), 0);
1175     const unsigned short *s = src.utf16();
1176     unsigned short *d = (unsigned short*)rc.data();
1177     for (int i = 0; i < src.length(); ++i) {
1178         d[i] = qToLittleEndian(s[i]);
1179     }
1180     return rc;
1181 }
1182 
1183 
qStringFromUcs2Le(QByteArray src)1184 static QString qStringFromUcs2Le(QByteArray src)
1185 {
1186     Q_ASSERT(src.size() % 2 == 0);
1187     unsigned short *d = (unsigned short*)src.data();
1188     for (int i = 0; i < src.length() / 2; ++i) {
1189         d[i] = qFromLittleEndian(d[i]);
1190     }
1191     return QString((const QChar *)src.data(), src.size()/2);
1192 }
1193 
1194 
1195 /*********************************************************************
1196 * Function Name: qEncodeHmacMd5
1197 * Params:
1198 *    key:   Type - QByteArray
1199 *         - It is the Authentication key
1200 *    message:   Type - QByteArray
1201 *         - This is the actual message which will be encoded
1202 *           using HMacMd5 hash algorithm
1203 *
1204 * Return Value:
1205 *    hmacDigest:   Type - QByteArray
1206 *
1207 * Description:
1208 *    This function will be used to encode the input message using
1209 *    HMacMd5 hash algorithm.
1210 *
1211 *    As per the RFC2104 the HMacMd5 algorithm can be specified
1212 *        ---------------------------------------
1213 *         MD5(K XOR opad, MD5(K XOR ipad, text))
1214 *        ---------------------------------------
1215 *
1216 *********************************************************************/
qEncodeHmacMd5(QByteArray & key,const QByteArray & message)1217 QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message)
1218 {
1219     Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check");
1220     Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check");
1221 
1222     QCryptographicHash hash(QCryptographicHash::Md5);
1223     QByteArray hMsg;
1224 
1225     QByteArray iKeyPad(blockSize, 0x36);
1226     QByteArray oKeyPad(blockSize, 0x5c);
1227 
1228     hash.reset();
1229     // Adjust the key length to blockSize
1230 
1231     if(blockSize < key.length()) {
1232         hash.addData(key);
1233         key = hash.result(); //MD5 will always return 16 bytes length output
1234     }
1235 
1236     //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms)
1237     //key size can be max of Block size only
1238     key = key.leftJustified(blockSize,0,true);
1239 
1240     //iKeyPad, oKeyPad and key are all of same size "blockSize"
1241 
1242     //xor of iKeyPad with Key and store the result into iKeyPad
1243     for(int i = 0; i<key.size();i++) {
1244         iKeyPad[i] = key[i]^iKeyPad[i];
1245     }
1246 
1247     //xor of oKeyPad with Key and store the result into oKeyPad
1248     for(int i = 0; i<key.size();i++) {
1249         oKeyPad[i] = key[i]^oKeyPad[i];
1250     }
1251 
1252     iKeyPad.append(message); // (K0 xor ipad) || text
1253 
1254     hash.reset();
1255     hash.addData(iKeyPad);
1256     hMsg = hash.result();
1257                     //Digest gen after pass-1: H((K0 xor ipad)||text)
1258 
1259     QByteArray hmacDigest;
1260     oKeyPad.append(hMsg);
1261     hash.reset();
1262     hash.addData(oKeyPad);
1263     hmacDigest = hash.result();
1264                     // H((K0 xor opad )|| H((K0 xor ipad) || text))
1265 
1266     /*hmacDigest should not be less than half the length of the HMAC output
1267       (to match the birthday attack bound) and not less than 80 bits
1268       (a suitable lower bound on the number of bits that need to be
1269       predicted by an attacker).
1270       Refer RFC 2104 for more details on truncation part */
1271 
1272     /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec
1273       (RFC 2104) also says digest length should be 16 bytes*/
1274     return hmacDigest;
1275 }
1276 
qCreatev2Hash(const QAuthenticatorPrivate * ctx,QNtlmPhase3Block * phase3)1277 static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx,
1278                                 QNtlmPhase3Block *phase3)
1279 {
1280     Q_ASSERT(phase3 != nullptr);
1281     // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated
1282     // only once and stored and reused
1283     if(phase3->v2Hash.size() == 0) {
1284         QCryptographicHash md4(QCryptographicHash::Md4);
1285         QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
1286         md4.addData(passUnicode.data(), passUnicode.size());
1287 
1288         QByteArray hashKey = md4.result();
1289         Q_ASSERT(hashKey.size() == 16);
1290         // Assuming the user and domain is always unicode in challenge
1291         QByteArray message =
1292                 qStringAsUcs2Le(ctx->extractedUser.toUpper()) +
1293                 qStringAsUcs2Le(phase3->domainStr);
1294 
1295         phase3->v2Hash = qEncodeHmacMd5(hashKey, message);
1296     }
1297     return phase3->v2Hash;
1298 }
1299 
clientChallenge(const QAuthenticatorPrivate * ctx)1300 static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx)
1301 {
1302     Q_ASSERT(ctx->cnonce.size() >= 8);
1303     QByteArray clientCh = ctx->cnonce.right(8);
1304     return clientCh;
1305 }
1306 
1307 // caller has to ensure a valid targetInfoBuff
qExtractServerTime(const QByteArray & targetInfoBuff)1308 static QByteArray qExtractServerTime(const QByteArray& targetInfoBuff)
1309 {
1310     QByteArray timeArray;
1311     QDataStream ds(targetInfoBuff);
1312     ds.setByteOrder(QDataStream::LittleEndian);
1313 
1314     quint16 avId;
1315     quint16 avLen;
1316 
1317     ds >> avId;
1318     ds >> avLen;
1319     while(avId != 0) {
1320         if(avId == AVTIMESTAMP) {
1321             timeArray.resize(avLen);
1322             //avLen size of QByteArray is allocated
1323             ds.readRawData(timeArray.data(), avLen);
1324             break;
1325         }
1326         ds.skipRawData(avLen);
1327         ds >> avId;
1328         ds >> avLen;
1329     }
1330     return timeArray;
1331 }
1332 
qEncodeNtlmv2Response(const QAuthenticatorPrivate * ctx,const QNtlmPhase2Block & ch,QNtlmPhase3Block * phase3)1333 static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx,
1334                                         const QNtlmPhase2Block& ch,
1335                                         QNtlmPhase3Block *phase3)
1336 {
1337     Q_ASSERT(phase3 != nullptr);
1338     // return value stored in phase3
1339     qCreatev2Hash(ctx, phase3);
1340 
1341     QByteArray temp;
1342     QDataStream ds(&temp, QIODevice::WriteOnly);
1343     ds.setByteOrder(QDataStream::LittleEndian);
1344 
1345     ds << respversion;
1346     ds << hirespversion;
1347 
1348     //Reserved
1349     QByteArray reserved1(6, 0);
1350     ds.writeRawData(reserved1.constData(), reserved1.size());
1351 
1352     quint64 time = 0;
1353     QByteArray timeArray;
1354 
1355     if(ch.targetInfo.len)
1356     {
1357         timeArray = qExtractServerTime(ch.targetInfoBuff);
1358     }
1359 
1360     //if server sends time, use it instead of current time
1361     if(timeArray.size()) {
1362         ds.writeRawData(timeArray.constData(), timeArray.size());
1363     } else {
1364         // number of seconds between 1601 and the epoch (1970)
1365         // 369 years, 89 leap years
1366         // ((369 * 365) + 89) * 24 * 3600 = 11644473600
1367         time = QDateTime::currentSecsSinceEpoch() + 11644473600;
1368 
1369         // represented as 100 nano seconds
1370         time = time * Q_UINT64_C(10000000);
1371         ds << time;
1372     }
1373 
1374     //8 byte client challenge
1375     QByteArray clientCh = clientChallenge(ctx);
1376     ds.writeRawData(clientCh.constData(), clientCh.size());
1377 
1378     //Reserved
1379     QByteArray reserved2(4, 0);
1380     ds.writeRawData(reserved2.constData(), reserved2.size());
1381 
1382     if (ch.targetInfo.len > 0) {
1383         ds.writeRawData(ch.targetInfoBuff.constData(),
1384                         ch.targetInfoBuff.size());
1385     }
1386 
1387     //Reserved
1388     QByteArray reserved3(4, 0);
1389     ds.writeRawData(reserved3.constData(), reserved3.size());
1390 
1391     QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
1392     message.append(temp);
1393 
1394     QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1395     ntChallengeResp.append(temp);
1396 
1397     return ntChallengeResp;
1398 }
1399 
qEncodeLmv2Response(const QAuthenticatorPrivate * ctx,const QNtlmPhase2Block & ch,QNtlmPhase3Block * phase3)1400 static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx,
1401                                       const QNtlmPhase2Block& ch,
1402                                       QNtlmPhase3Block *phase3)
1403 {
1404     Q_ASSERT(phase3 != nullptr);
1405     // return value stored in phase3
1406     qCreatev2Hash(ctx, phase3);
1407 
1408     QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
1409     QByteArray clientCh = clientChallenge(ctx);
1410 
1411     message.append(clientCh);
1412 
1413     QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1414     lmChallengeResp.append(clientCh);
1415 
1416     return lmChallengeResp;
1417 }
1418 
qNtlmDecodePhase2(const QByteArray & data,QNtlmPhase2Block & ch)1419 static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch)
1420 {
1421     Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase));
1422     if (data.size() < QNtlmPhase2BlockBase::Size)
1423         return false;
1424 
1425 
1426     QDataStream ds(data);
1427     ds.setByteOrder(QDataStream::LittleEndian);
1428     if (ds.readRawData(ch.magic, 8) < 8)
1429         return false;
1430     if (strncmp(ch.magic, "NTLMSSP", 8) != 0)
1431         return false;
1432 
1433     ds >> ch.type;
1434     if (ch.type != 2)
1435         return false;
1436 
1437     ds >> ch.targetName;
1438     ds >> ch.flags;
1439     if (ds.readRawData((char *)ch.challenge, 8) < 8)
1440         return false;
1441     ds >> ch.context[0] >> ch.context[1];
1442     ds >> ch.targetInfo;
1443 
1444     if (ch.targetName.len > 0) {
1445         if (ch.targetName.len + ch.targetName.offset > (unsigned)data.size())
1446             return false;
1447 
1448         ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
1449     }
1450 
1451     if (ch.targetInfo.len > 0) {
1452         if (ch.targetInfo.len + ch.targetInfo.offset > (unsigned)data.size())
1453             return false;
1454 
1455         ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len);
1456     }
1457 
1458     return true;
1459 }
1460 
1461 
qNtlmPhase3(QAuthenticatorPrivate * ctx,const QByteArray & phase2data)1462 static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data)
1463 {
1464     QNtlmPhase2Block ch;
1465     if (!qNtlmDecodePhase2(phase2data, ch))
1466         return QByteArray();
1467 
1468     QByteArray rc;
1469     QDataStream ds(&rc, QIODevice::WriteOnly);
1470     ds.setByteOrder(QDataStream::LittleEndian);
1471     QNtlmPhase3Block pb;
1472 
1473     // set NTLMv2
1474     if (ch.flags & NTLMSSP_NEGOTIATE_NTLM2)
1475         pb.flags |= NTLMSSP_NEGOTIATE_NTLM2;
1476 
1477     // set Always Sign
1478     if (ch.flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
1479         pb.flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
1480 
1481     bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE;
1482 
1483     if (unicode)
1484         pb.flags |= NTLMSSP_NEGOTIATE_UNICODE;
1485     else
1486         pb.flags |= NTLMSSP_NEGOTIATE_OEM;
1487 
1488 
1489     int offset = QNtlmPhase3BlockBase::Size;
1490     Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase));
1491 
1492     // for kerberos style user@domain logins, NTLM domain string should be left empty
1493     if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(QLatin1Char('@'))) {
1494         offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
1495         pb.domainStr = ch.targetNameStr;
1496     } else {
1497         offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode);
1498         pb.domainStr = ctx->userDomain;
1499     }
1500 
1501     offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode);
1502     pb.userStr = ctx->extractedUser;
1503 
1504     offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
1505     pb.workstationStr = ctx->workstation;
1506 
1507     // Get LM response
1508     if (ch.targetInfo.len > 0) {
1509         pb.lmResponseBuf = QByteArray();
1510     } else {
1511         pb.lmResponseBuf = qEncodeLmv2Response(ctx, ch, &pb);
1512     }
1513     offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf);
1514 
1515     // Get NTLM response
1516     pb.ntlmResponseBuf = qEncodeNtlmv2Response(ctx, ch, &pb);
1517     offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf);
1518 
1519 
1520     // Encode and send
1521     ds << pb;
1522 
1523     return rc;
1524 }
1525 
1526 // ---------------------------- End of NTLM code ---------------------------------------
1527 
1528 #if QT_CONFIG(sspi) // SSPI
1529 // ---------------------------- SSPI code ----------------------------------------------
1530 // See http://davenport.sourceforge.net/ntlm.html
1531 // and libcurl http_ntlm.c
1532 
1533 // Handle of secur32.dll
1534 static HMODULE securityDLLHandle = nullptr;
1535 // Pointer to SSPI dispatch table
1536 static PSecurityFunctionTable pSecurityFunctionTable = nullptr;
1537 
q_SSPI_library_load()1538 static bool q_SSPI_library_load()
1539 {
1540     static QBasicMutex mutex;
1541     QMutexLocker l(&mutex);
1542 
1543     // Initialize security interface
1544     if (pSecurityFunctionTable == nullptr) {
1545         securityDLLHandle = LoadLibrary(L"secur32.dll");
1546         if (securityDLLHandle != nullptr) {
1547             INIT_SECURITY_INTERFACE pInitSecurityInterface =
1548                 reinterpret_cast<INIT_SECURITY_INTERFACE>(
1549                     reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW")));
1550             if (pInitSecurityInterface != nullptr)
1551                 pSecurityFunctionTable = pInitSecurityInterface();
1552         }
1553     }
1554 
1555     if (pSecurityFunctionTable == nullptr)
1556         return false;
1557 
1558     return true;
1559 }
1560 
qSspiStartup(QAuthenticatorPrivate * ctx,QAuthenticatorPrivate::Method method,const QString & host)1561 static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
1562                                const QString& host)
1563 {
1564     if (!q_SSPI_library_load())
1565         return QByteArray();
1566 
1567     TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
1568 
1569     if (!ctx->sspiWindowsHandles)
1570         ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
1571     memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
1572 
1573     SEC_WINNT_AUTH_IDENTITY auth;
1574     auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1575     bool useAuth = false;
1576     if (method == QAuthenticatorPrivate::Negotiate && !ctx->user.isEmpty()) {
1577         auth.Domain = const_cast<ushort *>(ctx->userDomain.utf16());
1578         auth.DomainLength = ctx->userDomain.size();
1579         auth.User = const_cast<ushort *>(ctx->user.utf16());
1580         auth.UserLength = ctx->user.size();
1581         auth.Password = const_cast<ushort *>(ctx->password.utf16());
1582         auth.PasswordLength = ctx->password.size();
1583         useAuth = true;
1584     }
1585 
1586     // Acquire our credentials handle
1587     SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
1588             nullptr,
1589             (SEC_WCHAR *)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
1590             SECPKG_CRED_OUTBOUND, nullptr, useAuth ? &auth : nullptr, nullptr, nullptr,
1591             &ctx->sspiWindowsHandles->credHandle, &expiry
1592     );
1593     if (secStatus != SEC_E_OK) {
1594         ctx->sspiWindowsHandles.reset(nullptr);
1595         return QByteArray();
1596     }
1597 
1598     return qSspiContinue(ctx, method, host);
1599 }
1600 
qSspiContinue(QAuthenticatorPrivate * ctx,QAuthenticatorPrivate::Method method,const QString & host,const QByteArray & challenge)1601 static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
1602                                   const QString &host, const QByteArray &challenge)
1603 {
1604     QByteArray result;
1605     SecBuffer challengeBuf;
1606     SecBuffer responseBuf;
1607     SecBufferDesc challengeDesc;
1608     SecBufferDesc responseDesc;
1609     unsigned long attrs;
1610     TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
1611 
1612     if (!challenge.isEmpty())
1613     {
1614         // Setup the challenge "input" security buffer
1615         challengeDesc.ulVersion = SECBUFFER_VERSION;
1616         challengeDesc.cBuffers  = 1;
1617         challengeDesc.pBuffers  = &challengeBuf;
1618         challengeBuf.BufferType = SECBUFFER_TOKEN;
1619         challengeBuf.pvBuffer   = (PVOID)(challenge.data());
1620         challengeBuf.cbBuffer   = challenge.length();
1621     }
1622 
1623     // Setup the response "output" security buffer
1624     responseDesc.ulVersion = SECBUFFER_VERSION;
1625     responseDesc.cBuffers  = 1;
1626     responseDesc.pBuffers  = &responseBuf;
1627     responseBuf.BufferType = SECBUFFER_TOKEN;
1628     responseBuf.pvBuffer   = nullptr;
1629     responseBuf.cbBuffer   = 0;
1630 
1631     // Calculate target (SPN for Negotiate, empty for NTLM)
1632     std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
1633                                 ? QLatin1String("HTTP/") + host : QString()).toStdWString();
1634 
1635     // Generate our challenge-response message
1636     SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
1637                 &ctx->sspiWindowsHandles->credHandle,
1638                 !challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle : nullptr,
1639                 const_cast<wchar_t*>(targetNameW.data()),
1640                 ISC_REQ_ALLOCATE_MEMORY,
1641                 0, SECURITY_NATIVE_DREP,
1642                 !challenge.isEmpty() ? &challengeDesc : nullptr,
1643                 0, &ctx->sspiWindowsHandles->ctxHandle,
1644                 &responseDesc, &attrs,
1645                 &expiry
1646     );
1647 
1648     if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
1649         secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle,
1650                                                               &responseDesc);
1651     }
1652 
1653     if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) {
1654         pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle);
1655         pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle);
1656         ctx->sspiWindowsHandles.reset(nullptr);
1657     }
1658 
1659     result = QByteArray((const char*)responseBuf.pvBuffer, responseBuf.cbBuffer);
1660     pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer);
1661 
1662     return result;
1663 }
1664 
1665 // ---------------------------- End of SSPI code ---------------------------------------
1666 
1667 #elif QT_CONFIG(gssapi) // GSSAPI
1668 
1669 // ---------------------------- GSSAPI code ----------------------------------------------
1670 // See postgres src/interfaces/libpq/fe-auth.c
1671 
1672 // Fetch all errors of a specific type
q_GSSAPI_error_int(const char * message,OM_uint32 stat,int type)1673 static void q_GSSAPI_error_int(const char *message, OM_uint32 stat, int type)
1674 {
1675     OM_uint32 minStat, msgCtx = 0;
1676     gss_buffer_desc msg;
1677 
1678     do {
1679         gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
1680         qCDebug(lcAuthenticator) << message << ": " << reinterpret_cast<const char*>(msg.value);
1681         gss_release_buffer(&minStat, &msg);
1682     } while (msgCtx);
1683 }
1684 
1685 // GSSAPI errors contain two parts; extract both
q_GSSAPI_error(const char * message,OM_uint32 majStat,OM_uint32 minStat)1686 static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 minStat)
1687 {
1688     // Fetch major error codes
1689     q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE);
1690 
1691     // Add the minor codes as well
1692     q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
1693 }
1694 
qGSsapiGetServiceName(const QString & host)1695 static gss_name_t qGSsapiGetServiceName(const QString &host)
1696 {
1697     QByteArray serviceName = "HTTPS@" + host.toLocal8Bit();
1698     gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()};
1699 
1700     gss_name_t importedName;
1701     OM_uint32 minStat;
1702     OM_uint32 majStat = gss_import_name(&minStat, &nameDesc,
1703                               GSS_C_NT_HOSTBASED_SERVICE, &importedName);
1704 
1705     if (majStat != GSS_S_COMPLETE) {
1706         q_GSSAPI_error("gss_import_name error", majStat, minStat);
1707         return nullptr;
1708     }
1709     return importedName;
1710 }
1711 
1712 // Send initial GSS authentication token
qGssapiStartup(QAuthenticatorPrivate * ctx,const QString & host)1713 static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString &host)
1714 {
1715     if (!ctx->gssApiHandles)
1716         ctx->gssApiHandles.reset(new QGssApiHandles);
1717 
1718     // Convert target name to internal form
1719     gss_name_t name = qGSsapiGetServiceName(host);
1720     if (name == nullptr) {
1721         ctx->gssApiHandles.reset(nullptr);
1722         return QByteArray();
1723     }
1724     ctx->gssApiHandles->targetName = name;
1725 
1726     // Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet
1727     ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
1728     return qGssapiContinue(ctx);
1729 }
1730 
1731 // Continue GSS authentication with next token as needed
qGssapiContinue(QAuthenticatorPrivate * ctx,const QByteArray & challenge)1732 static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& challenge)
1733 {
1734     OM_uint32 majStat, minStat, ignored;
1735     QByteArray result;
1736     gss_buffer_desc inBuf = {0, nullptr}; // GSS input token
1737     gss_buffer_desc outBuf; // GSS output token
1738 
1739     if (!challenge.isEmpty()) {
1740         inBuf.value = const_cast<char*>(challenge.data());
1741         inBuf.length = challenge.length();
1742     }
1743 
1744     majStat = gss_init_sec_context(&minStat,
1745                                    GSS_C_NO_CREDENTIAL,
1746                                    &ctx->gssApiHandles->gssCtx,
1747                                    ctx->gssApiHandles->targetName,
1748                                    GSS_C_NO_OID,
1749                                    GSS_C_MUTUAL_FLAG,
1750                                    0,
1751                                    GSS_C_NO_CHANNEL_BINDINGS,
1752                                    challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf,
1753                                    nullptr,
1754                                    &outBuf,
1755                                    nullptr,
1756                                    nullptr);
1757 
1758     if (outBuf.length != 0)
1759         result = QByteArray(reinterpret_cast<const char*>(outBuf.value), outBuf.length);
1760     gss_release_buffer(&ignored, &outBuf);
1761 
1762     if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) {
1763         q_GSSAPI_error("gss_init_sec_context error", majStat, minStat);
1764         gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1765         if (ctx->gssApiHandles->gssCtx)
1766             gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER);
1767         ctx->gssApiHandles.reset(nullptr);
1768     }
1769 
1770     if (majStat == GSS_S_COMPLETE) {
1771         gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1772         ctx->gssApiHandles.reset(nullptr);
1773     }
1774 
1775     return result;
1776 }
1777 
qGssapiTestGetCredentials(const QString & host)1778 static bool qGssapiTestGetCredentials(const QString &host)
1779 {
1780     gss_name_t serviceName = qGSsapiGetServiceName(host);
1781     if (!serviceName)
1782         return false; // Something was wrong with the service name, so skip this
1783     OM_uint32 minStat;
1784     gss_cred_id_t cred;
1785     OM_uint32 majStat = gss_acquire_cred(&minStat, serviceName, GSS_C_INDEFINITE,
1786                                          GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred, nullptr,
1787                                          nullptr);
1788 
1789     OM_uint32 ignored;
1790     gss_release_name(&ignored, &serviceName);
1791     gss_release_cred(&ignored, &cred);
1792 
1793     if (majStat != GSS_S_COMPLETE) {
1794         q_GSSAPI_error("gss_acquire_cred", majStat, minStat);
1795         return false;
1796     }
1797     return true;
1798 }
1799 
1800 // ---------------------------- End of GSSAPI code ----------------------------------------------
1801 
1802 #endif // gssapi
1803 
1804 QT_END_NAMESPACE
1805