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