Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qauthenticator.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qauthenticator.h>
5#include <qauthenticator_p.h>
6#include <qdebug.h>
7#include <qloggingcategory.h>
8#include <qhash.h>
9#include <qbytearray.h>
10#include <qcryptographichash.h>
11#include <qiodevice.h>
12#include <qdatastream.h>
13#include <qendian.h>
14#include <qstring.h>
15#include <qdatetime.h>
16#include <qrandom.h>
17#include <QtNetwork/qhttpheaders.h>
18
19#ifdef Q_OS_WIN
20#include <qmutex.h>
21#include <rpc.h>
22#endif
23
24#if QT_CONFIG(sspi) // SSPI
25#define SECURITY_WIN32 1
26#include <security.h>
27#elif QT_CONFIG(gssapi) // GSSAPI
28#if defined(Q_OS_DARWIN)
29#include <GSS/GSS.h>
30#else
31#include <gssapi/gssapi.h>
32#endif // Q_OS_DARWIN
33#endif // Q_CONFIG(sspi)
34
36
37using namespace Qt::StringLiterals;
38
40Q_LOGGING_CATEGORY(lcAuthenticator, "qt.network.authenticator");
41
42static QByteArray qNtlmPhase1();
44#if QT_CONFIG(sspi) // SSPI
45static bool q_SSPI_library_load();
47 QStringView host);
49 QStringView host, QByteArrayView challenge = {});
50#elif QT_CONFIG(gssapi) // GSSAPI
51static bool qGssapiTestGetCredentials(QStringView host);
52static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host);
53static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge = {});
54#endif // gssapi
55
157
162{
163 if (d)
164 delete d;
165}
166
171 : d(nullptr)
172{
173 if (other.d)
174 *this = other;
175}
176
181{
182 if (d == other.d)
183 return *this;
184
185 // Do not share the d since challenge response/based changes
186 // could corrupt the internal store and different network requests
187 // can utilize different types of proxies.
188 detach();
189 if (other.d) {
190 d->user = other.d->user;
191 d->userDomain = other.d->userDomain;
192 d->workstation = other.d->workstation;
193 d->extractedUser = other.d->extractedUser;
194 d->password = other.d->password;
195 d->realm = other.d->realm;
196 d->method = other.d->method;
197 d->options = other.d->options;
198 } else if (d->phase == QAuthenticatorPrivate::Start) {
199 delete d;
200 d = nullptr;
201 }
202 return *this;
203}
204
210{
211 if (d == other.d)
212 return true;
213 if (!d || !other.d)
214 return false;
215 return d->user == other.d->user
216 && d->password == other.d->password
217 && d->realm == other.d->realm
218 && d->method == other.d->method
219 && d->options == other.d->options;
220}
221
233{
234 return d ? d->user : QString();
235}
236
243{
244 if (!d || d->user != user) {
245 detach();
246 d->user = user;
248 }
249}
250
255{
256 return d ? d->password : QString();
257}
258
265{
266 if (!d || d->password != password) {
267 detach();
268 d->password = password;
269 }
270}
271
276{
277 if (!d) {
278 d = new QAuthenticatorPrivate;
279 return;
280 }
281
284}
285
290{
291 return d ? d->realm : QString();
292}
293
298{
299 if (!d || d->realm != realm) {
300 detach();
301 d->realm = realm;
302 }
303}
304
315{
316 return d ? d->options.value(opt) : QVariant();
317}
318
328{
329 return d ? d->options : QVariantHash();
330}
331
341{
342 if (option(opt) != value) {
343 detach();
344 d->options.insert(opt, value);
345 }
346}
347
348
356{
357 return !d;
358}
359
360#if QT_CONFIG(sspi) // SSPI
361class QSSPIWindowsHandles
362{
363public:
364 CredHandle credHandle;
365 CtxtHandle ctxHandle;
366};
367#elif QT_CONFIG(gssapi) // GSSAPI
368class QGssApiHandles
369{
370public:
371 gss_ctx_id_t gssCtx = nullptr;
372 gss_name_t targetName;
373};
374#endif // gssapi
375
376
378 : method(None)
379 , hasFailed(false)
380 , phase(Start)
381 , nonceCount(0)
382{
385 nonceCount = 0;
386}
387
389
391{
392 int separatorPosn = 0;
393
394 switch (method) {
396 if ((separatorPosn = user.indexOf("\\"_L1)) != -1) {
397 //domain name is present
398 realm.clear();
399 userDomain = user.left(separatorPosn);
400 extractedUser = user.mid(separatorPosn + 1);
401 } else {
403 realm.clear();
405 }
406 break;
407 default:
409 break;
410 }
411}
412
414{
415 Q_ASSERT(!method.startsWith(' ')); // This should be trimmed during parsing
416 auto separator = method.indexOf(' ');
417 if (separator != -1)
418 method = method.first(separator);
419 const auto isSupported = [method](QByteArrayView reference) {
420 return method.compare(reference, Qt::CaseInsensitive) == 0;
421 };
422 static const char methods[][10] = {
423 "basic",
424 "ntlm",
425 "digest",
426#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
427 "negotiate",
428#endif
429 };
430 return std::any_of(methods, methods + std::size(methods), isSupported);
431}
432
434{
436 if (auto it = opts.constFind("algorithm"); it != opts.cend()) {
437 QByteArray alg = it.value();
438 if (alg.size() < 3)
439 return false;
440 // Just compare the first 3 characters, that way we match other subvariants as well, such as
441 // "MD5-sess"
442 auto view = QByteArrayView(alg).first(3);
443 return view.compare("MD5", Qt::CaseInsensitive) == 0;
444 }
445 return true; // assume it's ok if algorithm is not specified
446}
447
449 bool isProxy, QStringView host)
450{
451#if !QT_CONFIG(gssapi)
452 Q_UNUSED(host);
453#endif
454 const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
456
457 method = None;
458 /*
459 Fun from the HTTP 1.1 specs, that we currently ignore:
460
461 User agents are advised to take special care in parsing the WWW-
462 Authenticate field value as it might contain more than one challenge,
463 or if more than one WWW-Authenticate header field is provided, the
464 contents of a challenge itself can contain a comma-separated list of
465 authentication parameters.
466 */
467
468 QByteArrayView headerVal;
469 for (const auto &current : headers.values(search)) {
470 const QLatin1StringView str(current);
471 if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) {
472 method = Basic;
473 headerVal = QByteArrayView(current).mid(6);
474 } else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) {
475 method = Ntlm;
476 headerVal = QByteArrayView(current).mid(5);
477 } else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) {
478 // Make sure the algorithm is actually MD5 before committing to it:
479 if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
480 continue;
481
483 headerVal = QByteArrayView(current).mid(7);
484 } else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) {
485#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
486#if QT_CONFIG(gssapi)
487 // For GSSAPI there needs to be a KDC set up for the host (afaict).
488 // So let's only conditionally use it if we can fetch the credentials.
489 // Sadly it's a bit slow because it requires a DNS lookup.
490 if (!qGssapiTestGetCredentials(host))
491 continue;
492#endif
494 headerVal = QByteArrayView(current).mid(10);
495#endif
496 }
497 }
498
499 // Reparse credentials since we know the method now
501 challenge = headerVal.trimmed().toByteArray();
502 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
503
504 // Sets phase to Start if this updates our realm and sets the two locations where we store
505 // realm
506 auto privSetRealm = [this](QString newRealm) {
507 if (newRealm != realm) {
508 if (phase == Done)
509 phase = Start;
510 realm = newRealm;
511 this->options["realm"_L1] = realm;
512 }
513 };
514
515 switch(method) {
516 case Basic:
517 privSetRealm(QString::fromLatin1(options.value("realm")));
518 if (user.isEmpty() && password.isEmpty())
519 phase = Done;
520 break;
521 case Ntlm:
522 case Negotiate:
523 // work is done in calculateResponse()
524 break;
525 case DigestMd5: {
526 privSetRealm(QString::fromLatin1(options.value("realm")));
527 if (options.value("stale").compare("true", Qt::CaseInsensitive) == 0) {
528 phase = Start;
529 nonceCount = 0;
530 }
531 if (user.isEmpty() && password.isEmpty())
532 phase = Done;
533 break;
534 }
535 default:
536 realm.clear();
538 phase = Invalid;
539 }
540}
541
544{
545#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
546 Q_UNUSED(host);
547#endif
548 QByteArray response;
549 QByteArrayView methodString;
550 switch(method) {
552 phase = Done;
553 break;
555 methodString = "Basic";
556 response = (user + ':'_L1 + password).toLatin1().toBase64();
557 phase = Done;
558 break;
560 methodString = "Digest";
561 response = digestMd5Response(challenge, requestMethod, path);
562 phase = Done;
563 break;
565 methodString = "NTLM";
566 if (challenge.isEmpty()) {
567#if QT_CONFIG(sspi) // SSPI
568 QByteArray phase1Token;
569 if (user.isEmpty()) { // Only pull from system if no user was specified in authenticator
570 phase1Token = qSspiStartup(this, method, host);
571 } else if (!q_SSPI_library_load()) {
572 // Since we're not running qSspiStartup we have to make sure the library is loaded
573 qWarning("Failed to load the SSPI libraries");
574 return "";
575 }
576 if (!phase1Token.isEmpty()) {
577 response = phase1Token.toBase64();
578 phase = Phase2;
579 } else
580#endif
581 {
582 response = qNtlmPhase1().toBase64();
583 if (user.isEmpty())
584 phase = Done;
585 else
586 phase = Phase2;
587 }
588 } else {
589#if QT_CONFIG(sspi) // SSPI
590 QByteArray phase3Token;
591 if (sspiWindowsHandles)
592 phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
593 if (!phase3Token.isEmpty()) {
594 response = phase3Token.toBase64();
595 phase = Done;
596 } else
597#endif
598 {
599 response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
600 phase = Done;
601 }
602 challenge = "";
603 }
604
605 break;
607 methodString = "Negotiate";
608 if (challenge.isEmpty()) {
609 QByteArray phase1Token;
610#if QT_CONFIG(sspi) // SSPI
611 phase1Token = qSspiStartup(this, method, host);
612#elif QT_CONFIG(gssapi) // GSSAPI
613 phase1Token = qGssapiStartup(this, host);
614#endif
615
616 if (!phase1Token.isEmpty()) {
617 response = phase1Token.toBase64();
618 phase = Phase2;
619 } else {
620 phase = Done;
621 return "";
622 }
623 } else {
624 QByteArray phase3Token;
625#if QT_CONFIG(sspi) // SSPI
626 if (sspiWindowsHandles)
627 phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
628#elif QT_CONFIG(gssapi) // GSSAPI
629 if (gssApiHandles)
630 phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
631#endif
632 if (!phase3Token.isEmpty()) {
633 response = phase3Token.toBase64();
634 phase = Done;
635 challenge = "";
636 } else {
637 phase = Done;
638 return "";
639 }
640 }
641
642 break;
643 }
644
645 return methodString + ' ' + response;
646}
647
648
649// ---------------------------- Digest Md5 code ----------------------------------------
650
652{
653 for (auto element : QLatin1StringView(data).tokenize(','_L1)) {
654 if (element == "auth"_L1)
655 return true;
656 }
657 return false;
658}
659
660QHash<QByteArray, QByteArray>
662{
663 QHash<QByteArray, QByteArray> options;
664 // parse the challenge
665 const char *d = challenge.data();
666 const char *end = d + challenge.size();
667 while (d < end) {
668 while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
669 ++d;
670 const char *start = d;
671 while (d < end && *d != '=')
672 ++d;
674 ++d;
675 if (d >= end)
676 break;
677 bool quote = (*d == '"');
678 if (quote)
679 ++d;
680 if (d >= end)
681 break;
683 while (d < end) {
684 bool backslash = false;
685 if (*d == '\\' && d < end - 1) {
686 ++d;
687 backslash = true;
688 }
689 if (!backslash) {
690 if (quote) {
691 if (*d == '"')
692 break;
693 } else {
694 if (*d == ',')
695 break;
696 }
697 }
698 value += *d;
699 ++d;
700 }
701 while (d < end && *d != ',')
702 ++d;
703 ++d;
704 options[key.toByteArray()] = std::move(value);
705 }
706
707 QByteArray qop = options.value("qop");
708 if (!qop.isEmpty()) {
709 if (!containsAuth(qop))
710 return QHash<QByteArray, QByteArray>();
711 // #### can't do auth-int currently
712// if (qop.contains("auth-int"))
713// qop = "auth-int";
714// else if (qop.contains("auth"))
715// qop = "auth";
716// else
717// qop = QByteArray();
718 options["qop"] = "auth";
719 }
720
721 return options;
722}
723
724/*
725 Digest MD5 implementation
726
727 Code taken from RFC 2617
728
729 Currently we don't support the full SASL authentication mechanism (which includes cyphers)
730*/
731
732
733/* calculate request-digest/response-digest as per HTTP Digest spec */
735 QByteArrayView alg,
736 QByteArrayView userName,
737 QByteArrayView realm,
738 QByteArrayView password,
739 QByteArrayView nonce, /* nonce from server */
740 QByteArrayView nonceCount, /* 8 hex digits */
741 QByteArrayView cNonce, /* client nonce */
742 QByteArrayView qop, /* qop-value: "", "auth", "auth-int" */
743 QByteArrayView method, /* method from the request */
744 QByteArrayView digestUri, /* requested URL */
745 QByteArrayView hEntity /* H(entity body) if qop="auth-int" */
746 )
747{
749 hash.addData(userName);
750 hash.addData(":");
751 hash.addData(realm);
752 hash.addData(":");
753 hash.addData(password);
754 QByteArray ha1 = hash.result();
755 if (alg.compare("md5-sess", Qt::CaseInsensitive) == 0) {
756 hash.reset();
757 // RFC 2617 contains an error, it was:
758 // hash.addData(ha1);
759 // but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it
760 // must be the following line:
761 hash.addData(ha1.toHex());
762 hash.addData(":");
763 hash.addData(nonce);
764 hash.addData(":");
765 hash.addData(cNonce);
766 ha1 = hash.result();
767 };
768 ha1 = ha1.toHex();
769
770 // calculate H(A2)
771 hash.reset();
772 hash.addData(method);
773 hash.addData(":");
774 hash.addData(digestUri);
775 if (qop.compare("auth-int", Qt::CaseInsensitive) == 0) {
776 hash.addData(":");
777 hash.addData(hEntity);
778 }
779 QByteArray ha2hex = hash.result().toHex();
780
781 // calculate response
782 hash.reset();
783 hash.addData(ha1);
784 hash.addData(":");
785 hash.addData(nonce);
786 hash.addData(":");
787 if (!qop.isNull()) {
788 hash.addData(nonceCount);
789 hash.addData(":");
790 hash.addData(cNonce);
791 hash.addData(":");
792 hash.addData(qop);
793 hash.addData(":");
794 }
795 hash.addData(ha2hex);
796 return hash.result().toHex();
797}
798
801{
802 QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
803
804 ++nonceCount;
805 QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
806 while (nonceCountString.size() < 8)
807 nonceCountString.prepend('0');
808
809 QByteArray nonce = options.value("nonce");
810 QByteArray opaque = options.value("opaque");
811 QByteArray qop = options.value("qop");
812
813// qDebug() << "calculating digest: method=" << method << "path=" << path;
814 QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(),
816 nonce, nonceCountString,
817 cnonce, qop, method,
818 path, QByteArray());
819
820
821 QByteArray credentials;
822 credentials += "username=\"" + user.toLatin1() + "\", ";
823 credentials += "realm=\"" + realm.toLatin1() + "\", ";
824 credentials += "nonce=\"" + nonce + "\", ";
825 credentials += "uri=\"" + path + "\", ";
826 if (!opaque.isEmpty())
827 credentials += "opaque=\"" + opaque + "\", ";
828 credentials += "response=\"" + response + '"';
829 if (!options.value("algorithm").isEmpty())
830 credentials += ", algorithm=" + options.value("algorithm");
831 if (!options.value("qop").isEmpty()) {
832 credentials += ", qop=" + qop + ", ";
833 credentials += "nc=" + nonceCountString + ", ";
834 credentials += "cnonce=\"" + cnonce + '"';
835 }
836
837 return credentials;
838}
839
840// ---------------------------- End of Digest Md5 code ---------------------------------
841
842
843// ---------------------------- NTLM code ----------------------------------------------
844
845/*
846 * NTLM message flags.
847 *
848 * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
849 *
850 * This software is released under the MIT license.
851 */
852
853/*
854 * Indicates that Unicode strings are supported for use in security
855 * buffer data.
856 */
857#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
858
859/*
860 * Indicates that OEM strings are supported for use in security buffer data.
861 */
862#define NTLMSSP_NEGOTIATE_OEM 0x00000002
863
864/*
865 * Requests that the server's authentication realm be included in the
866 * Type 2 message.
867 */
868#define NTLMSSP_REQUEST_TARGET 0x00000004
869
870/*
871 * Specifies that authenticated communication between the client and server
872 * should carry a digital signature (message integrity).
873 */
874#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
875
876/*
877 * Specifies that authenticated communication between the client and server
878 * should be encrypted (message confidentiality).
879 */
880#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
881
882/*
883 * Indicates that datagram authentication is being used.
884 */
885#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
886
887/*
888 * Indicates that the LAN Manager session key should be
889 * used for signing and sealing authenticated communications.
890 */
891#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
892
893/*
894 * Indicates that NTLM authentication is being used.
895 */
896#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
897
898/*
899 * Sent by the client in the Type 1 message to indicate that the name of the
900 * domain in which the client workstation has membership is included in the
901 * message. This is used by the server to determine whether the client is
902 * eligible for local authentication.
903 */
904#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
905
906/*
907 * Sent by the client in the Type 1 message to indicate that the client
908 * workstation's name is included in the message. This is used by the server
909 * to determine whether the client is eligible for local authentication.
910 */
911#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
912
913/*
914 * Sent by the server to indicate that the server and client are on the same
915 * machine. Implies that the client may use the established local credentials
916 * for authentication instead of calculating a response to the challenge.
917 */
918#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
919
920/*
921 * Indicates that authenticated communication between the client and server
922 * should be signed with a "dummy" signature.
923 */
924#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
925
926/*
927 * Sent by the server in the Type 2 message to indicate that the target
928 * authentication realm is a domain.
929 */
930#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
931
932/*
933 * Sent by the server in the Type 2 message to indicate that the target
934 * authentication realm is a server.
935 */
936#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
937
938/*
939 * Sent by the server in the Type 2 message to indicate that the target
940 * authentication realm is a share. Presumably, this is for share-level
941 * authentication. Usage is unclear.
942 */
943#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
944
945/*
946 * Indicates that the NTLM2 signing and sealing scheme should be used for
947 * protecting authenticated communications. Note that this refers to a
948 * particular session security scheme, and is not related to the use of
949 * NTLMv2 authentication.
950 */
951#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
952
953/*
954 * Sent by the server in the Type 2 message to indicate that it is including
955 * a Target Information block in the message. The Target Information block
956 * is used in the calculation of the NTLMv2 response.
957 */
958#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
959
960/*
961 * Indicates that 128-bit encryption is supported.
962 */
963#define NTLMSSP_NEGOTIATE_128 0x20000000
964
965/*
966 * Indicates that the client will provide an encrypted master session key in
967 * the "Session Key" field of the Type 3 message. This is used in signing and
968 * sealing, and is RC4-encrypted using the previous session key as the
969 * encryption key.
970 */
971#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
972
973/*
974 * Indicates that 56-bit encryption is supported.
975 */
976#define NTLMSSP_NEGOTIATE_56 0x80000000
977
978/*
979 * AvId values
980 */
981#define AVTIMESTAMP 7
982
983
984//************************Global variables***************************
985
986const int blockSize = 64; //As per RFC2104 Block-size is 512 bits
989
990/* usage:
991 // fill up ctx with what we know.
992 QByteArray response = qNtlmPhase1(ctx);
993 // send response (b64 encoded??)
994 // get response from server (b64 decode?)
995 Phase2Block pb;
996 qNtlmDecodePhase2(response, pb);
997 response = qNtlmPhase3(ctx, pb);
998 // send response (b64 encoded??)
999*/
1000
1001/*
1002 TODO:
1003 - Fix unicode handling
1004 - add v2 handling
1005*/
1006
1008public:
1009 QNtlmBuffer() : len(0), maxLen(0), offset(0) {}
1013 enum { Size = 8 };
1014};
1015
1026
1027// ################# check paddings
1040
1054
1056{
1057 ds.writeRawData(s.constData(), s.size());
1058}
1059
1060
1061static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode)
1062{
1063 if (!unicode) {
1064 qStreamNtlmBuffer(ds, s.toLatin1());
1065 return;
1066 }
1067
1068 for (QChar ch : s)
1069 ds << quint16(ch.unicode());
1070}
1071
1072
1073
1075{
1076 buf.len = s.size();
1077 buf.maxLen = buf.len;
1078 buf.offset = (offset + 1) & ~1;
1079 return buf.offset + buf.len;
1080}
1081
1082
1083static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode)
1084{
1085 if (!unicode)
1086 return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
1087 buf.len = 2 * s.size();
1088 buf.maxLen = buf.len;
1089 buf.offset = (offset + 1) & ~1;
1090 return buf.offset + buf.len;
1091}
1092
1093
1095{
1096 s << b.len << b.maxLen << b.offset;
1097 return s;
1098}
1099
1101{
1102 s >> b.len >> b.maxLen >> b.offset;
1103 return s;
1104}
1105
1106
1119
1120
1122{ // challenge
1123public:
1125 magic[0] = 0;
1126 type = 0xffffffff;
1127 }
1128
1129 // extracted
1132};
1133
1134
1135
1149
1150
1152 bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
1153
1154 s.writeRawData(b.magic, sizeof(b.magic));
1155 s << b.type;
1156 s << b.flags;
1157 s << b.domain;
1158 s << b.workstation;
1159 if (!b.domainStr.isEmpty())
1160 qStreamNtlmString(s, b.domainStr, unicode);
1161 if (!b.workstationStr.isEmpty())
1162 qStreamNtlmString(s, b.workstationStr, unicode);
1163 return s;
1164}
1165
1166
1168 bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
1169 s.writeRawData(b.magic, sizeof(b.magic));
1170 s << b.type;
1171 s << b.lmResponse;
1172 s << b.ntlmResponse;
1173 s << b.domain;
1174 s << b.user;
1175 s << b.workstation;
1176 s << b.sessionKey;
1177 s << b.flags;
1178
1179 if (!b.domainStr.isEmpty())
1180 qStreamNtlmString(s, b.domainStr, unicode);
1181
1182 qStreamNtlmString(s, b.userStr, unicode);
1183
1184 if (!b.workstationStr.isEmpty())
1185 qStreamNtlmString(s, b.workstationStr, unicode);
1186
1187 // Send auth info
1188 qStreamNtlmBuffer(s, b.lmResponseBuf);
1189 qStreamNtlmBuffer(s, b.ntlmResponseBuf);
1190
1191
1192 return s;
1193}
1194
1195
1197{
1198 QByteArray rc;
1202 ds << pb;
1203 return rc;
1204}
1205
1206
1208{
1209 QByteArray rc(2*src.size(), 0);
1210 unsigned short *d = (unsigned short*)rc.data();
1211 for (QChar ch : src)
1212 *d++ = qToLittleEndian(quint16(ch.unicode()));
1213
1214 return rc;
1215}
1216
1217
1219{
1220 Q_ASSERT(src.size() % 2 == 0);
1221 unsigned short *d = (unsigned short*)src.data();
1222 for (int i = 0; i < src.size() / 2; ++i) {
1223 d[i] = qFromLittleEndian(d[i]);
1224 }
1225 return QString((const QChar *)src.data(), src.size()/2);
1226}
1227
1228
1229/*********************************************************************
1230* Function Name: qEncodeHmacMd5
1231* Params:
1232* key: Type - QByteArray
1233* - It is the Authentication key
1234* message: Type - QByteArray
1235* - This is the actual message which will be encoded
1236* using HMacMd5 hash algorithm
1237*
1238* Return Value:
1239* hmacDigest: Type - QByteArray
1240*
1241* Description:
1242* This function will be used to encode the input message using
1243* HMacMd5 hash algorithm.
1244*
1245* As per the RFC2104 the HMacMd5 algorithm can be specified
1246* ---------------------------------------
1247* MD5(K XOR opad, MD5(K XOR ipad, text))
1248* ---------------------------------------
1249*
1250*********************************************************************/
1252{
1253 Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check");
1254 Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check");
1255
1257
1258 QByteArray iKeyPad(blockSize, 0x36);
1259 QByteArray oKeyPad(blockSize, 0x5c);
1260
1261 hash.reset();
1262 // Adjust the key length to blockSize
1263
1264 if (blockSize < key.size()) {
1265 hash.addData(key);
1266 key = hash.result(); //MD5 will always return 16 bytes length output
1267 }
1268
1269 //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms)
1270 //key size can be max of Block size only
1271 key = key.leftJustified(blockSize,0,true);
1272
1273 //iKeyPad, oKeyPad and key are all of same size "blockSize"
1274
1275 //xor of iKeyPad with Key and store the result into iKeyPad
1276 for(int i = 0; i<key.size();i++) {
1277 iKeyPad[i] = key[i]^iKeyPad[i];
1278 }
1279
1280 //xor of oKeyPad with Key and store the result into oKeyPad
1281 for(int i = 0; i<key.size();i++) {
1282 oKeyPad[i] = key[i]^oKeyPad[i];
1283 }
1284
1285 iKeyPad.append(message); // (K0 xor ipad) || text
1286
1287 hash.reset();
1288 hash.addData(iKeyPad);
1289 QByteArrayView hMsg = hash.resultView();
1290 //Digest gen after pass-1: H((K0 xor ipad)||text)
1291
1292 QByteArray hmacDigest;
1293 oKeyPad.append(hMsg);
1294 hash.reset();
1295 hash.addData(oKeyPad);
1296 hmacDigest = hash.result();
1297 // H((K0 xor opad )|| H((K0 xor ipad) || text))
1298
1299 /*hmacDigest should not be less than half the length of the HMAC output
1300 (to match the birthday attack bound) and not less than 80 bits
1301 (a suitable lower bound on the number of bits that need to be
1302 predicted by an attacker).
1303 Refer RFC 2104 for more details on truncation part */
1304
1305 /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec
1306 (RFC 2104) also says digest length should be 16 bytes*/
1307 return hmacDigest;
1308}
1309
1311 QNtlmPhase3Block *phase3)
1312{
1313 Q_ASSERT(phase3 != nullptr);
1314 // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated
1315 // only once and stored and reused
1316 if (phase3->v2Hash.size() == 0) {
1318 QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
1319 md4.addData(passUnicode);
1320
1321 QByteArray hashKey = md4.result();
1322 Q_ASSERT(hashKey.size() == 16);
1323 // Assuming the user and domain is always unicode in challenge
1325 qStringAsUcs2Le(ctx->extractedUser.toUpper()) +
1326 qStringAsUcs2Le(phase3->domainStr);
1327
1328 phase3->v2Hash = qEncodeHmacMd5(hashKey, message);
1329 }
1330 return phase3->v2Hash;
1331}
1332
1334{
1335 Q_ASSERT(ctx->cnonce.size() >= 8);
1336 QByteArray clientCh = ctx->cnonce.right(8);
1337 return clientCh;
1338}
1339
1340// caller has to ensure a valid targetInfoBuff
1341static QByteArray qExtractServerTime(const QByteArray& targetInfoBuff)
1342{
1343 QByteArray timeArray;
1344 QDataStream ds(targetInfoBuff);
1346
1347 quint16 avId;
1348 quint16 avLen;
1349
1350 ds >> avId;
1351 ds >> avLen;
1352 while(avId != 0) {
1353 if (avId == AVTIMESTAMP) {
1354 timeArray.resize(avLen);
1355 //avLen size of QByteArray is allocated
1356 ds.readRawData(timeArray.data(), avLen);
1357 break;
1358 }
1359 ds.skipRawData(avLen);
1360 ds >> avId;
1361 ds >> avLen;
1362 }
1363 return timeArray;
1364}
1365
1367 const QNtlmPhase2Block& ch,
1368 QNtlmPhase3Block *phase3)
1369{
1370 Q_ASSERT(phase3 != nullptr);
1371 // return value stored in phase3
1372 qCreatev2Hash(ctx, phase3);
1373
1374 QByteArray temp;
1377
1378 ds << respversion;
1379 ds << hirespversion;
1380
1381 //Reserved
1382 QByteArray reserved1(6, 0);
1383 ds.writeRawData(reserved1.constData(), reserved1.size());
1384
1385 quint64 time = 0;
1386 QByteArray timeArray;
1387
1388 if (ch.targetInfo.len)
1389 {
1390 timeArray = qExtractServerTime(ch.targetInfoBuff);
1391 }
1392
1393 //if server sends time, use it instead of current time
1394 if (timeArray.size()) {
1395 ds.writeRawData(timeArray.constData(), timeArray.size());
1396 } else {
1397 // number of seconds between 1601 and the epoch (1970)
1398 // 369 years, 89 leap years
1399 // ((369 * 365) + 89) * 24 * 3600 = 11644473600
1400 time = QDateTime::currentSecsSinceEpoch() + 11644473600;
1401
1402 // represented as 100 nano seconds
1403 time = time * Q_UINT64_C(10000000);
1404 ds << time;
1405 }
1406
1407 //8 byte client challenge
1408 QByteArray clientCh = clientChallenge(ctx);
1409 ds.writeRawData(clientCh.constData(), clientCh.size());
1410
1411 //Reserved
1412 QByteArray reserved2(4, 0);
1413 ds.writeRawData(reserved2.constData(), reserved2.size());
1414
1415 if (ch.targetInfo.len > 0) {
1416 ds.writeRawData(ch.targetInfoBuff.constData(),
1417 ch.targetInfoBuff.size());
1418 }
1419
1420 //Reserved
1421 QByteArray reserved3(4, 0);
1422 ds.writeRawData(reserved3.constData(), reserved3.size());
1423
1424 QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
1425 message.append(temp);
1426
1427 QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1428 ntChallengeResp.append(temp);
1429
1430 return ntChallengeResp;
1431}
1432
1434 const QNtlmPhase2Block& ch,
1435 QNtlmPhase3Block *phase3)
1436{
1437 Q_ASSERT(phase3 != nullptr);
1438 // return value stored in phase3
1439 qCreatev2Hash(ctx, phase3);
1440
1441 QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
1442 QByteArray clientCh = clientChallenge(ctx);
1443
1444 message.append(clientCh);
1445
1446 QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1447 lmChallengeResp.append(clientCh);
1448
1449 return lmChallengeResp;
1450}
1451
1453{
1455 if (data.size() < QNtlmPhase2BlockBase::Size)
1456 return false;
1457
1458
1459 QDataStream ds(data);
1461 if (ds.readRawData(ch.magic, 8) < 8)
1462 return false;
1463 if (strncmp(ch.magic, "NTLMSSP", 8) != 0)
1464 return false;
1465
1466 ds >> ch.type;
1467 if (ch.type != 2)
1468 return false;
1469
1470 ds >> ch.targetName;
1471 ds >> ch.flags;
1472 if (ds.readRawData((char *)ch.challenge, 8) < 8)
1473 return false;
1474 ds >> ch.context[0] >> ch.context[1];
1475 ds >> ch.targetInfo;
1476
1477 if (ch.targetName.len > 0) {
1478 if (qsizetype(ch.targetName.len + ch.targetName.offset) > data.size())
1479 return false;
1480
1481 ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
1482 }
1483
1484 if (ch.targetInfo.len > 0) {
1485 if (ch.targetInfo.len + ch.targetInfo.offset > (unsigned)data.size())
1486 return false;
1487
1488 ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len);
1489 }
1490
1491 return true;
1492}
1493
1494
1496{
1498 if (!qNtlmDecodePhase2(phase2data, ch))
1499 return QByteArray();
1500
1501 QByteArray rc;
1505
1506 // set NTLMv2
1507 if (ch.flags & NTLMSSP_NEGOTIATE_NTLM2)
1509
1510 // set Always Sign
1513
1514 bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE;
1515
1516 if (unicode)
1518 else
1520
1521
1524
1525 // for kerberos style user@domain logins, NTLM domain string should be left empty
1526 if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(u'@')) {
1527 offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
1528 pb.domainStr = ch.targetNameStr;
1529 } else {
1530 offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode);
1531 pb.domainStr = ctx->userDomain;
1532 }
1533
1534 offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode);
1535 pb.userStr = ctx->extractedUser;
1536
1537 offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
1538 pb.workstationStr = ctx->workstation;
1539
1540 // Get LM response
1541 if (ch.targetInfo.len > 0) {
1543 } else {
1545 }
1547
1548 // Get NTLM response
1551
1552
1553 // Encode and send
1554 ds << pb;
1555
1556 return rc;
1557}
1558
1559// ---------------------------- End of NTLM code ---------------------------------------
1560
1561#if QT_CONFIG(sspi) // SSPI
1562// ---------------------------- SSPI code ----------------------------------------------
1563// See http://davenport.sourceforge.net/ntlm.html
1564// and libcurl http_ntlm.c
1565
1566// Pointer to SSPI dispatch table
1567static PSecurityFunctionTableW pSecurityFunctionTable = nullptr;
1568
1569static bool q_SSPI_library_load()
1570{
1571 Q_CONSTINIT static QBasicMutex mutex;
1572 QMutexLocker l(&mutex);
1573
1574 if (pSecurityFunctionTable == nullptr)
1575 pSecurityFunctionTable = InitSecurityInterfaceW();
1576
1577 if (pSecurityFunctionTable == nullptr)
1578 return false;
1579
1580 return true;
1581}
1582
1584 QStringView host)
1585{
1586 if (!q_SSPI_library_load())
1587 return QByteArray();
1588
1589 TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
1590
1591 if (!ctx->sspiWindowsHandles)
1592 ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
1593 SecInvalidateHandle(&ctx->sspiWindowsHandles->credHandle);
1594 SecInvalidateHandle(&ctx->sspiWindowsHandles->ctxHandle);
1595
1596 SEC_WINNT_AUTH_IDENTITY auth;
1597 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1598 bool useAuth = false;
1599 if (method == QAuthenticatorPrivate::Negotiate && !ctx->user.isEmpty()) {
1600 auth.Domain = const_cast<ushort *>(reinterpret_cast<const ushort *>(ctx->userDomain.constData()));
1601 auth.DomainLength = ctx->userDomain.size();
1602 auth.User = const_cast<ushort *>(reinterpret_cast<const ushort *>(ctx->user.constData()));
1603 auth.UserLength = ctx->user.size();
1604 auth.Password = const_cast<ushort *>(reinterpret_cast<const ushort *>(ctx->password.constData()));
1605 auth.PasswordLength = ctx->password.size();
1606 useAuth = true;
1607 }
1608
1609 // Acquire our credentials handle
1610 SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
1611 nullptr,
1612 (SEC_WCHAR *)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
1613 SECPKG_CRED_OUTBOUND, nullptr, useAuth ? &auth : nullptr, nullptr, nullptr,
1614 &ctx->sspiWindowsHandles->credHandle, &expiry
1615 );
1616 if (secStatus != SEC_E_OK) {
1617 ctx->sspiWindowsHandles.reset(nullptr);
1618 return QByteArray();
1619 }
1620
1621 return qSspiContinue(ctx, method, host);
1622}
1623
1625 QStringView host, QByteArrayView challenge)
1626{
1628 SecBuffer challengeBuf;
1629 SecBuffer responseBuf;
1630 SecBufferDesc challengeDesc;
1631 SecBufferDesc responseDesc;
1632 unsigned long attrs;
1633 TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
1634
1635 if (!challenge.isEmpty())
1636 {
1637 // Setup the challenge "input" security buffer
1638 challengeDesc.ulVersion = SECBUFFER_VERSION;
1639 challengeDesc.cBuffers = 1;
1640 challengeDesc.pBuffers = &challengeBuf;
1641 challengeBuf.BufferType = SECBUFFER_TOKEN;
1642 challengeBuf.pvBuffer = (PVOID)(challenge.data());
1643 challengeBuf.cbBuffer = challenge.length();
1644 }
1645
1646 // Setup the response "output" security buffer
1647 responseDesc.ulVersion = SECBUFFER_VERSION;
1648 responseDesc.cBuffers = 1;
1649 responseDesc.pBuffers = &responseBuf;
1650 responseBuf.BufferType = SECBUFFER_TOKEN;
1651 responseBuf.pvBuffer = nullptr;
1652 responseBuf.cbBuffer = 0;
1653
1654 // Calculate target (SPN for Negotiate, empty for NTLM)
1655 QString targetName = ctx->options.value("spn"_L1).toString();
1656 if (targetName.isEmpty())
1657 targetName = "HTTP/"_L1 + host;
1658 const std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
1659 ? targetName : QString()).toStdWString();
1660
1661 // Generate our challenge-response message
1662 SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
1663 &ctx->sspiWindowsHandles->credHandle,
1664 !challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle : nullptr,
1665 const_cast<wchar_t*>(targetNameW.data()),
1666 ISC_REQ_ALLOCATE_MEMORY,
1667 0, SECURITY_NATIVE_DREP,
1668 !challenge.isEmpty() ? &challengeDesc : nullptr,
1669 0, &ctx->sspiWindowsHandles->ctxHandle,
1670 &responseDesc, &attrs,
1671 &expiry
1672 );
1673
1674 if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
1675 secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle,
1676 &responseDesc);
1677 }
1678
1679 if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) {
1680 pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle);
1681 pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle);
1682 ctx->sspiWindowsHandles.reset(nullptr);
1683 }
1684
1685 result = QByteArray((const char*)responseBuf.pvBuffer, responseBuf.cbBuffer);
1686 pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer);
1687
1688 return result;
1689}
1690
1691// ---------------------------- End of SSPI code ---------------------------------------
1692
1693#elif QT_CONFIG(gssapi) // GSSAPI
1694
1695// ---------------------------- GSSAPI code ----------------------------------------------
1696// See postgres src/interfaces/libpq/fe-auth.c
1697
1698// Fetch all errors of a specific type
1699static void q_GSSAPI_error_int(const char *message, OM_uint32 stat, int type)
1700{
1701 OM_uint32 minStat, msgCtx = 0;
1702 gss_buffer_desc msg;
1703
1704 do {
1705 gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
1706 qCDebug(lcAuthenticator) << message << ": " << reinterpret_cast<const char*>(msg.value);
1707 gss_release_buffer(&minStat, &msg);
1708 } while (msgCtx);
1709}
1710
1711// GSSAPI errors contain two parts; extract both
1712static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 minStat)
1713{
1714 // Fetch major error codes
1715 q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE);
1716
1717 // Add the minor codes as well
1718 q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
1719}
1720
1721static gss_name_t qGSsapiGetServiceName(QStringView host)
1722{
1723 QByteArray serviceName = "HTTPS@" + host.toLocal8Bit();
1724 gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()};
1725
1726 gss_name_t importedName;
1727 OM_uint32 minStat;
1728 OM_uint32 majStat = gss_import_name(&minStat, &nameDesc,
1729 GSS_C_NT_HOSTBASED_SERVICE, &importedName);
1730
1731 if (majStat != GSS_S_COMPLETE) {
1732 q_GSSAPI_error("gss_import_name error", majStat, minStat);
1733 return nullptr;
1734 }
1735 return importedName;
1736}
1737
1738// Send initial GSS authentication token
1739static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host)
1740{
1741 if (!ctx->gssApiHandles)
1742 ctx->gssApiHandles.reset(new QGssApiHandles);
1743
1744 // Convert target name to internal form
1745 gss_name_t name = qGSsapiGetServiceName(host);
1746 if (name == nullptr) {
1747 ctx->gssApiHandles.reset(nullptr);
1748 return QByteArray();
1749 }
1750 ctx->gssApiHandles->targetName = name;
1751
1752 // Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet
1753 ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
1754 return qGssapiContinue(ctx);
1755}
1756
1757// Continue GSS authentication with next token as needed
1758static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge)
1759{
1760 OM_uint32 majStat, minStat, ignored;
1762 gss_buffer_desc inBuf = {0, nullptr}; // GSS input token
1763 gss_buffer_desc outBuf; // GSS output token
1764
1765 if (!challenge.isEmpty()) {
1766 inBuf.value = const_cast<char*>(challenge.data());
1767 inBuf.length = challenge.size();
1768 }
1769
1770 majStat = gss_init_sec_context(&minStat,
1771 GSS_C_NO_CREDENTIAL,
1772 &ctx->gssApiHandles->gssCtx,
1773 ctx->gssApiHandles->targetName,
1774 GSS_C_NO_OID,
1775 GSS_C_MUTUAL_FLAG,
1776 0,
1777 GSS_C_NO_CHANNEL_BINDINGS,
1778 challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf,
1779 nullptr,
1780 &outBuf,
1781 nullptr,
1782 nullptr);
1783
1784 if (outBuf.length != 0)
1785 result = QByteArray(reinterpret_cast<const char*>(outBuf.value), outBuf.length);
1786 gss_release_buffer(&ignored, &outBuf);
1787
1788 if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) {
1789 q_GSSAPI_error("gss_init_sec_context error", majStat, minStat);
1790 gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1791 if (ctx->gssApiHandles->gssCtx)
1792 gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER);
1793 ctx->gssApiHandles.reset(nullptr);
1794 }
1795
1796 if (majStat == GSS_S_COMPLETE) {
1797 gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1798 ctx->gssApiHandles.reset(nullptr);
1799 }
1800
1801 return result;
1802}
1803
1804static bool qGssapiTestGetCredentials(QStringView host)
1805{
1806 gss_name_t serviceName = qGSsapiGetServiceName(host);
1807 if (!serviceName)
1808 return false; // Something was wrong with the service name, so skip this
1809 OM_uint32 minStat;
1810 gss_cred_id_t cred;
1811 OM_uint32 majStat = gss_acquire_cred(&minStat, serviceName, GSS_C_INDEFINITE,
1812 GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred, nullptr,
1813 nullptr);
1814
1815 OM_uint32 ignored;
1816 gss_release_name(&ignored, &serviceName);
1817 gss_release_cred(&ignored, &cred);
1818
1819 if (majStat != GSS_S_COMPLETE) {
1820 q_GSSAPI_error("gss_acquire_cred", majStat, minStat);
1821 return false;
1822 }
1823 return true;
1824}
1825
1826// ---------------------------- End of GSSAPI code ----------------------------------------------
1827
1828#endif // gssapi
1829
static JNINativeMethod methods[]
static QHash< QByteArray, QByteArray > parseDigestAuthenticationChallenge(QByteArrayView challenge)
QByteArray calculateResponse(QByteArrayView method, QByteArrayView path, QStringView host)
QByteArray digestMd5Response(QByteArrayView challenge, QByteArrayView method, QByteArrayView path)
void parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host)
static bool isMethodSupported(QByteArrayView method)
The QAuthenticator class provides an authentication object.
QString user() const
Returns the user used for authentication.
void setOption(const QString &opt, const QVariant &value)
~QAuthenticator()
Destructs the object.
void setPassword(const QString &password)
Sets the password used for authentication.
QAuthenticator & operator=(const QAuthenticator &other)
Assigns the contents of other to this authenticator.
QVariant option(const QString &opt) const
QAuthenticator()
Constructs an empty authentication object.
QString password() const
Returns the password used for authentication.
QString realm() const
Returns the realm requiring authentication.
bool isNull() const
Returns true if the object has not been initialized.
void setUser(const QString &user)
Sets the user used for authentication.
bool operator==(const QAuthenticator &other) const
Returns true if this authenticator is identical to other; otherwise returns false.
friend class QAuthenticatorPrivate
void setRealm(const QString &realm)
QVariantHash options() const
constexpr qsizetype length() const noexcept
constexpr QByteArrayView mid(qsizetype pos, qsizetype n=-1) const
constexpr QByteArrayView first(qsizetype n) const
constexpr bool isEmpty() const noexcept
constexpr qsizetype size() const noexcept
constexpr const_pointer data() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
QByteArray trimmed() const &
Definition qbytearray.h:262
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
QByteArray right(qsizetype n) const &
Definition qbytearray.h:181
static QByteArray fromBase64(const QByteArray &base64, Base64Options options=Base64Encoding)
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QByteArray toBase64(Base64Options options=Base64Encoding) const
\inmodule QtCore
static QByteArray hash(QByteArrayView data, Algorithm method)
Returns the hash of data using method.
\inmodule QtCore\reentrant
Definition qdatastream.h:46
qint64 readRawData(char *, qint64 len)
Reads at most len bytes from the stream into s and returns the number of bytes read.
qint64 skipRawData(qint64 len)
qint64 writeRawData(const char *, qint64 len)
Writes len bytes from s to the stream.
void setByteOrder(ByteOrder)
Sets the serialization byte order to bo.
static qint64 currentSecsSinceEpoch() noexcept
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
Q_NETWORK_EXPORT QList< QByteArray > values(QAnyStringView name) const
Returns the values of header name in a list.
constexpr auto tokenize(Needle &&needle, Flags...flags) const noexcept(noexcept(qTokenize(std::declval< const QLatin1StringView & >(), std::forward< Needle >(needle), flags...))) -> decltype(qTokenize(*this, std::forward< Needle >(needle), flags...))
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
unsigned char challenge[8]
static Q_DECL_CONST_FUNCTION QRandomGenerator * system()
\threadsafe
Definition qrandom.h:270
const_iterator cend() const noexcept
Definition qset.h:142
const_iterator constFind(const T &value) const
Definition qset.h:161
\inmodule QtCore
Definition qstringview.h:78
QByteArray toLocal8Bit() const
Returns a local 8-bit representation of the string as a QByteArray.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
QByteArray toLatin1() const &
Definition qstring.h:630
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
\inmodule QtCore
Definition qvariant.h:65
static QPartialOrdering compare(const QVariant &lhs, const QVariant &rhs)
Compares the objects at lhs and rhs for ordering.
EGLContext ctx
QHash< int, QWidget * > hash
[35multi]
QString str
[2]
QSet< QString >::iterator it
QStyleOptionButton opt
Combined button and popup list for selecting options.
@ CaseInsensitive
static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx)
const quint8 respversion
static QByteArray qNtlmPhase1()
static QDataStream & operator<<(QDataStream &s, const QNtlmBuffer &b)
#define NTLMSSP_NEGOTIATE_NTLM2
#define NTLMSSP_NEGOTIATE_TARGET_INFO
static QByteArray qStringAsUcs2Le(const QString &src)
static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block &ch, QNtlmPhase3Block *phase3)
static bool verifyDigestMD5(QByteArrayView value)
static bool containsAuth(QByteArrayView data)
static int qEncodeNtlmString(QNtlmBuffer &buf, int offset, const QString &s, bool unicode)
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray &phase2data)
QByteArray qEncodeHmacMd5(QByteArray &key, QByteArrayView message)
#define NTLMSSP_NEGOTIATE_OEM
static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block &ch, QNtlmPhase3Block *phase3)
static QByteArray digestMd5ResponseHelper(QByteArrayView alg, QByteArrayView userName, QByteArrayView realm, QByteArrayView password, QByteArrayView nonce, QByteArrayView nonceCount, QByteArrayView cNonce, QByteArrayView qop, QByteArrayView method, QByteArrayView digestUri, QByteArrayView hEntity)
static QDataStream & operator>>(QDataStream &s, QNtlmBuffer &b)
static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx, QNtlmPhase3Block *phase3)
static void qStreamNtlmBuffer(QDataStream &ds, const QByteArray &s)
#define NTLMSSP_REQUEST_TARGET
static QString qStringFromUcs2Le(QByteArray src)
static void qStreamNtlmString(QDataStream &ds, const QString &s, bool unicode)
#define NTLMSSP_NEGOTIATE_NTLM
static QByteArray qExtractServerTime(const QByteArray &targetInfoBuff)
#define NTLMSSP_NEGOTIATE_UNICODE
static int qEncodeNtlmBuffer(QNtlmBuffer &buf, int offset, const QByteArray &s)
const quint8 hirespversion
const int blockSize
#define AVTIMESTAMP
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN
static bool qNtlmDecodePhase2(const QByteArray &data, QNtlmPhase2Block &ch)
Q_CORE_EXPORT char * qstrncpy(char *dst, const char *src, size_t len)
QHash< QString, QVariant > QVariantHash
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
static struct AttrInfo attrs[]
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr T qFromLittleEndian(T source)
Definition qendian.h:178
constexpr T qToLittleEndian(T source)
Definition qendian.h:176
@ None
Definition qhash.cpp:531
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLboolean GLboolean GLboolean b
GLuint64 key
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
GLint reference
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLuint GLsizei const GLchar * message
GLuint start
GLenum GLuint GLintptr offset
GLuint name
GLdouble s
[6]
Definition qopenglext.h:235
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLuint GLenum option
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define Q_UNUSED(x)
#define Q_UINT64_C(c)
Definition qtypes.h:58
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned short ushort
Definition qtypes.h:33
unsigned char quint8
Definition qtypes.h:46
static QString quote(const QString &str)
QObject::connect nullptr
QMutex mutex
[2]
QSharedPointer< T > other(t)
[5]
QQuickView * view
[0]