5#include <qauthenticator.h>
6#include <qauthenticator_p.h>
8#include <qloggingcategory.h>
10#include <qbytearray.h>
11#include <qcryptographichash.h>
13#include <qdatastream.h>
18#include <QtNetwork/qhttpheaders.h>
26#define SECURITY_WIN32 1
28#elif QT_CONFIG(gssapi)
29#if defined(Q_OS_DARWIN)
32#include <gssapi/gssapi.h>
38using namespace Qt::StringLiterals;
46static bool q_SSPI_library_load();
47static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
49static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
50 QStringView host, QByteArrayView challenge = {});
51#elif QT_CONFIG(gssapi)
52static bool qGssapiTestGetCredentials(QStringView host);
53static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host);
54static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge = {});
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
152
153
154QAuthenticator::QAuthenticator()
160
161
162QAuthenticator::~QAuthenticator()
169
170
171QAuthenticator::QAuthenticator(
const QAuthenticator &other)
179
180
181QAuthenticator &QAuthenticator::operator=(
const QAuthenticator &other)
191 d->user = other.d->user;
192 d->userDomain = other.d->userDomain;
193 d->workstation = other.d->workstation;
194 d->extractedUser = other.d->extractedUser;
195 d->password = other.d->password;
196 d->realm = other.d->realm;
197 d->method = other.d->method;
198 d->options = other.d->options;
199 }
else if (d->phase == QAuthenticatorPrivate::Start) {
207
208
209
210bool QAuthenticator::operator==(
const QAuthenticator &other)
const
216 return d->user == other.d->user
217 && d->password == other.d->password
218 && d->realm == other.d->realm
219 && d->method == other.d->method
220 && d->options == other.d->options;
224
225
226
227
228
231
232
233QString QAuthenticator::user()
const
235 return d ? d->user : QString();
239
240
241
242
243void QAuthenticator::setUser(
const QString &user)
245 if (!d || d->user != user) {
248 d->updateCredentials();
253
254
255QString QAuthenticator::password()
const
257 return d ? d->password : QString();
261
262
263
264
265void QAuthenticator::setPassword(
const QString &password)
267 if (!d || d->password != password) {
269 d->password = password;
274
275
276void QAuthenticator::detach()
279 d =
new QAuthenticatorPrivate;
283 if (d->phase == QAuthenticatorPrivate::Done)
284 d->phase = QAuthenticatorPrivate::Start;
288
289
290QString QAuthenticator::realm()
const
292 return d ? d->realm : QString();
296
297
298void QAuthenticator::setRealm(
const QString &realm)
300 if (!d || d->realm != realm) {
307
308
309
310
311
312
313
314
315QVariant QAuthenticator::option(
const QString &opt)
const
317 return d ? d->options.value(opt) : QVariant();
321
322
323
324
325
326
327
328QVariantHash QAuthenticator::options()
const
330 return d ? d->options : QVariantHash();
334
335
336
337
338
339
340
341void QAuthenticator::setOption(
const QString &opt,
const QVariant &value)
343 if (option(opt) != value) {
345 d->options.insert(opt, value);
351
352
353
354
355
356bool QAuthenticator::isNull()
const
362
363
364
365
366
368void QAuthenticator::clear()
noexcept
370 *d = QAuthenticatorPrivate();
371 d->phase = QAuthenticatorPrivate::Done;
375class QSSPIWindowsHandles
378 CredHandle credHandle;
379 CtxtHandle ctxHandle;
381#elif QT_CONFIG(gssapi)
385 gss_ctx_id_t gssCtx =
nullptr;
386 gss_name_t targetName;
391QAuthenticatorPrivate::QAuthenticatorPrivate()
397 cnonce = QCryptographicHash::hash(QByteArray::number(QRandomGenerator::system()->generate64(), 16),
398 QCryptographicHash::Md5).toHex();
402QAuthenticatorPrivate::~QAuthenticatorPrivate() =
default;
404void QAuthenticatorPrivate::updateCredentials()
406 int separatorPosn = 0;
409 case QAuthenticatorPrivate::Ntlm:
410 if ((separatorPosn = user.indexOf(
"\\"_L1)) != -1) {
413 userDomain = user.left(separatorPosn);
414 extractedUser = user.mid(separatorPosn + 1);
416 extractedUser = user;
427bool QAuthenticatorPrivate::isMethodSupported(QByteArrayView method)
429 Q_ASSERT(!method.startsWith(
' '));
430 auto separator = method.indexOf(
' ');
432 method = method.first(separator);
433 const auto isSupported = [method](QByteArrayView reference) {
434 return method.compare(reference, Qt::CaseInsensitive) == 0;
436 static const char methods[][10] = {
440#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
444 return std::any_of(methods, methods + std::size(methods), isSupported);
449 auto opts = QAuthenticatorPrivate::parseDigestAuthenticationChallenge(value);
450 if (
auto it = opts.constFind(
"algorithm"); it != opts.cend()) {
456 auto view = QByteArrayView(alg).first(3);
457 return view.compare(
"MD5", Qt::CaseInsensitive) == 0;
462void QAuthenticatorPrivate::parseHttpResponse(
const QHttpHeaders &headers,
463 bool isProxy, QStringView host)
465#if !QT_CONFIG(gssapi)
468 const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
469 : QHttpHeaders::WellKnownHeader::WWWAuthenticate;
473
474
475
476
477
478
479
480
482 QByteArrayView headerVal;
483 for (
const auto ¤t : headers.values(search)) {
484 const QLatin1StringView str(current);
485 if (method < Basic && str.startsWith(
"basic"_L1, Qt::CaseInsensitive)) {
487 headerVal = QByteArrayView(current).mid(6);
488 }
else if (method < Ntlm && str.startsWith(
"ntlm"_L1, Qt::CaseInsensitive)) {
490 headerVal = QByteArrayView(current).mid(5);
491 }
else if (method < DigestMd5 && str.startsWith(
"digest"_L1, Qt::CaseInsensitive)) {
493 if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
497 headerVal = QByteArrayView(current).mid(7);
498 }
else if (method < Negotiate && str.startsWith(
"negotiate"_L1, Qt::CaseInsensitive)) {
499#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
504 if (!qGssapiTestGetCredentials(host))
508 headerVal = QByteArrayView(current).mid(10);
515 challenge = headerVal.trimmed().toByteArray();
516 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
520 auto privSetRealm = [
this](QString newRealm) {
521 if (newRealm != realm) {
525 this->options[
"realm"_L1] = realm;
531 privSetRealm(QString::fromLatin1(options.value(
"realm")));
532 if (user.isEmpty() && password.isEmpty())
540 privSetRealm(QString::fromLatin1(options.value(
"realm")));
541 if (options.value(
"stale").compare(
"true", Qt::CaseInsensitive) == 0) {
545 if (user.isEmpty() && password.isEmpty())
551 challenge = QByteArray();
556QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod,
557 QByteArrayView path, QStringView host)
559#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
563 QByteArrayView methodString;
565 case QAuthenticatorPrivate::None:
568 case QAuthenticatorPrivate::Basic:
569 methodString =
"Basic";
570 response = (user +
':'_L1 + password).toLatin1().toBase64();
573 case QAuthenticatorPrivate::DigestMd5:
574 methodString =
"Digest";
575 response = digestMd5Response(challenge, requestMethod, path);
578 case QAuthenticatorPrivate::Ntlm:
579 methodString =
"NTLM";
580 if (challenge.isEmpty()) {
582 QByteArray phase1Token;
583 if (user.isEmpty()) {
584 phase1Token = qSspiStartup(
this, method, host);
585 }
else if (!q_SSPI_library_load()) {
587 qWarning(
"Failed to load the SSPI libraries");
590 if (!phase1Token.isEmpty()) {
591 response = phase1Token.toBase64();
596 response = qNtlmPhase1().toBase64();
604 QByteArray phase3Token;
605 if (sspiWindowsHandles)
606 phase3Token = qSspiContinue(
this, method, host, QByteArray::fromBase64(challenge));
607 if (!phase3Token.isEmpty()) {
608 response = phase3Token.toBase64();
613 response = qNtlmPhase3(
this, QByteArray::fromBase64(challenge)).toBase64();
620 case QAuthenticatorPrivate::Negotiate:
621 methodString =
"Negotiate";
622 if (challenge.isEmpty()) {
623 QByteArray phase1Token;
625 phase1Token = qSspiStartup(
this, method, host);
626#elif QT_CONFIG(gssapi)
627 phase1Token = qGssapiStartup(
this, host);
630 if (!phase1Token.isEmpty()) {
631 response = phase1Token.toBase64();
638 QByteArray phase3Token;
640 if (sspiWindowsHandles)
641 phase3Token = qSspiContinue(
this, method, host, QByteArray::fromBase64(challenge));
642#elif QT_CONFIG(gssapi)
644 phase3Token = qGssapiContinue(
this, QByteArray::fromBase64(challenge));
646 if (!phase3Token.isEmpty()) {
647 response = phase3Token.toBase64();
659 return methodString +
' ' + response;
667 for (
auto element : QLatin1StringView(data).tokenize(
','_L1)) {
668 if (element ==
"auth"_L1)
674QHash<QByteArray, QByteArray>
675QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challenge)
677 QHash<QByteArray, QByteArray> options;
679 const char *d = challenge.data();
680 const char *end = d + challenge.size();
682 while (d < end && (*d ==
' ' || *d ==
'\n' || *d ==
'\r'))
684 const char *start = d;
685 while (d < end && *d !=
'=')
687 QByteArrayView key = QByteArrayView(start, d - start);
691 bool quote = (*d ==
'"');
698 bool backslash =
false;
699 if (*d ==
'\\' && d < end - 1) {
715 while (d < end && *d !=
',')
718 options[key.toByteArray()] = std::move(value);
721 QByteArray qop = options.value(
"qop");
722 if (!qop.isEmpty()) {
723 if (!containsAuth(qop))
724 return QHash<QByteArray, QByteArray>();
732 options[
"qop"] =
"auth";
739
740
741
742
743
744
750 QByteArrayView userName,
751 QByteArrayView realm,
752 QByteArrayView password,
753 QByteArrayView nonce,
754 QByteArrayView nonceCount,
755 QByteArrayView cNonce,
757 QByteArrayView method,
758 QByteArrayView digestUri,
759 QByteArrayView hEntity
763 hash.addData(userName);
767 hash.addData(password);
769 if (alg.compare(
"md5-sess", Qt::CaseInsensitive) == 0) {
775 hash.addData(ha1.toHex());
779 hash.addData(cNonce);
786 hash.addData(method);
788 hash.addData(digestUri);
789 if (qop.compare(
"auth-int", Qt::CaseInsensitive) == 0) {
791 hash.addData(hEntity);
802 hash.addData(nonceCount);
804 hash.addData(cNonce);
809 hash.addData(ha2hex);
810 return hash.result().toHex();
813QByteArray QAuthenticatorPrivate::digestMd5Response(QByteArrayView challenge, QByteArrayView method,
816 QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
819 QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
820 while (nonceCountString.size() < 8)
821 nonceCountString.prepend(
'0');
823 QByteArray nonce = options.value(
"nonce");
824 QByteArray opaque = options.value(
"opaque");
825 QByteArray qop = options.value(
"qop");
828 QByteArray response = digestMd5ResponseHelper(options.value(
"algorithm"), user.toLatin1(),
829 realm.toLatin1(), password.toLatin1(),
830 nonce, nonceCountString,
835 QByteArray credentials;
836 credentials +=
"username=\"" + user.toLatin1() +
"\", ";
837 credentials +=
"realm=\"" + realm.toLatin1() +
"\", ";
838 credentials +=
"nonce=\"" + nonce +
"\", ";
839 credentials +=
"uri=\"" + path +
"\", ";
840 if (!opaque.isEmpty())
841 credentials +=
"opaque=\"" + opaque +
"\", ";
842 credentials +=
"response=\"" + response +
'"';
843 if (!options.value(
"algorithm").isEmpty())
844 credentials +=
", algorithm=" + options.value(
"algorithm");
845 if (!options.value(
"qop").isEmpty()) {
846 credentials +=
", qop=" + qop +
", ";
847 credentials +=
"nc=" + nonceCountString +
", ";
848 credentials +=
"cnonce=\"" + cnonce +
'"';
860
861
862
863
864
865
868
869
870
871#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
874
875
876#define NTLMSSP_NEGOTIATE_OEM 0x00000002
879
880
881
882#define NTLMSSP_REQUEST_TARGET 0x00000004
885
886
887
888#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
891
892
893
894#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
897
898
899#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
902
903
904
905#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
908
909
910#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
913
914
915
916
917
918#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
921
922
923
924
925#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
928
929
930
931
932#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
935
936
937
938#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
941
942
943
944#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
947
948
949
950#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
953
954
955
956
957#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
960
961
962
963
964
965#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
968
969
970
971
972#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
975
976
977#define NTLMSSP_NEGOTIATE_128 0x20000000
980
981
982
983
984
985#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
988
989
990#define NTLMSSP_NEGOTIATE_56 0x80000000
993
994
1005
1006
1007
1008
1009
1010
1011
1012
1013
1016
1017
1018
1019
1032 ds.writeRawData(s.constData(), s.size());
1039 qStreamNtlmBuffer(ds, s.toLatin1());
1044 ds << quint16(ch.unicode());
1052 buf.maxLen = buf.len;
1053 buf.offset = (offset + 1) & ~1;
1054 return buf.offset + buf.len;
1061 return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
1062 buf.len = 2 * s.size();
1063 buf.maxLen = buf.len;
1064 buf.offset = (offset + 1) & ~1;
1065 return buf.offset + buf.len;
1071 s << b.len << b.maxLen << b.offset;
1077 s >> b.len >> b.maxLen >> b.offset;
1085 char magic[8] = {
'N',
'T',
'L',
'M',
'S',
'S',
'P',
'\0'};
1118 char magic[8] = {
'N',
'T',
'L',
'M',
'S',
'S',
'P',
'\0'};
1144 if (!b.domainStr.isEmpty())
1145 qStreamNtlmString(s, b.domainStr, unicode);
1146 if (!b.workstationStr.isEmpty())
1147 qStreamNtlmString(s, b.workstationStr, unicode);
1157 s << b.ntlmResponse;
1164 if (!b.domainStr.isEmpty())
1165 qStreamNtlmString(s, b.domainStr, unicode);
1167 qStreamNtlmString(s, b.userStr, unicode);
1169 if (!b.workstationStr.isEmpty())
1170 qStreamNtlmString(s, b.workstationStr, unicode);
1173 qStreamNtlmBuffer(s, b.lmResponseBuf);
1174 qStreamNtlmBuffer(s, b.ntlmResponseBuf);
1184 QDataStream ds(&rc, QIODevice::WriteOnly);
1185 ds.setByteOrder(QDataStream::LittleEndian);
1195 unsigned short *d = (
unsigned short*)rc.data();
1196 for (QChar ch : src)
1197 *d++ = qToLittleEndian(quint16(ch.unicode()));
1205 Q_ASSERT(src.size() % 2 == 0);
1206 unsigned short *d = (
unsigned short*)src.data();
1207 for (
int i = 0; i < src.size() / 2; ++i) {
1208 d[i] = qFromLittleEndian(d[i]);
1210 return QString((
const QChar *)src.data(), src.size()/2);
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1238 Q_ASSERT_X(!(message.isEmpty()),
"qEncodeHmacMd5",
"Empty message check");
1239 Q_ASSERT_X(!(key.isEmpty()),
"qEncodeHmacMd5",
"Empty key check");
1251 key = hash.result();
1256 key = key.leftJustified(
blockSize,0,
true);
1261 for(
int i = 0; i<key.size();i++) {
1262 iKeyPad[i] = key[i]^iKeyPad[i];
1266 for(
int i = 0; i<key.size();i++) {
1267 oKeyPad[i] = key[i]^oKeyPad[i];
1270 iKeyPad.append(message);
1273 hash.addData(iKeyPad);
1274 QByteArrayView hMsg = hash.resultView();
1278 oKeyPad.append(hMsg);
1280 hash.addData(oKeyPad);
1281 hmacDigest = hash.result();
1285
1286
1287
1288
1291
1298 Q_ASSERT(phase3 !=
nullptr);
1301 if (phase3->v2Hash.size() == 0) {
1303 QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
1304 md4.addData(passUnicode);
1307 Q_ASSERT(hashKey.size() == 16);
1310 qStringAsUcs2Le(ctx->extractedUser.toUpper()) +
1311 qStringAsUcs2Le(phase3->domainStr);
1313 phase3->v2Hash = qEncodeHmacMd5(hashKey, message);
1315 return phase3->v2Hash;
1320 Q_ASSERT(ctx->cnonce.size() >= 8);
1329 QDataStream ds(targetInfoBuff);
1330 ds.setByteOrder(QDataStream::LittleEndian);
1339 timeArray.resize(avLen);
1341 ds.readRawData(timeArray.data(), avLen);
1344 ds.skipRawData(avLen);
1355 Q_ASSERT(phase3 !=
nullptr);
1360 QDataStream ds(&temp, QIODevice::WriteOnly);
1361 ds.setByteOrder(QDataStream::LittleEndian);
1364 ds << hirespversion;
1368 ds.writeRawData(reserved1.constData(), reserved1.size());
1373 if (ch.targetInfo.len)
1375 timeArray = qExtractServerTime(ch.targetInfoBuff);
1379 if (timeArray.size()) {
1380 ds.writeRawData(timeArray.constData(), timeArray.size());
1385 time = QDateTime::currentSecsSinceEpoch() + 11644473600;
1388 time = time * Q_UINT64_C(10000000);
1394 ds.writeRawData(clientCh.constData(), clientCh.size());
1398 ds.writeRawData(reserved2.constData(), reserved2.size());
1400 if (ch.targetInfo.len > 0) {
1401 ds.writeRawData(ch.targetInfoBuff.constData(),
1402 ch.targetInfoBuff.size());
1407 ds.writeRawData(reserved3.constData(), reserved3.size());
1410 message.append(temp);
1412 QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1413 ntChallengeResp.append(temp);
1415 return ntChallengeResp;
1422 Q_ASSERT(phase3 !=
nullptr);
1429 message.append(clientCh);
1431 QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1432 lmChallengeResp.append(clientCh);
1434 return lmChallengeResp;
1443 QDataStream ds(data);
1444 ds.setByteOrder(QDataStream::LittleEndian);
1445 if (ds.readRawData(ch
.magic, 8) < 8)
1447 if (strncmp(ch
.magic,
"NTLMSSP", 8) != 0)
1454 ds >> ch.targetName;
1456 if (ds.readRawData((
char *)ch
.challenge, 8) < 8)
1458 ds >> ch.context[0] >> ch.context[1];
1459 ds >> ch.targetInfo;
1461 if (ch.targetName.len > 0) {
1462 if (qsizetype(ch.targetName.len + ch.targetName.offset) > data.size())
1465 ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
1468 if (ch.targetInfo.len > 0) {
1469 if (ch.targetInfo.len + ch.targetInfo.offset > (
unsigned)data.size())
1472 ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len);
1486 QDataStream ds(&rc, QIODevice::WriteOnly);
1487 ds.setByteOrder(QDataStream::LittleEndian);
1509 if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(u'@')) {
1510 offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
1511 pb.domainStr = ch.targetNameStr;
1513 offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode);
1514 pb.domainStr = ctx->userDomain;
1517 offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode);
1518 pb.userStr = ctx->extractedUser;
1520 offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
1521 pb.workstationStr = ctx->workstation;
1524 if (ch.targetInfo.len > 0) {
1529 offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf);
1533 offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf);
1550static PSecurityFunctionTableW pSecurityFunctionTable =
nullptr;
1552static bool q_SSPI_library_load()
1554 Q_CONSTINIT
static QBasicMutex mutex;
1555 QMutexLocker l(&mutex);
1557 if (pSecurityFunctionTable ==
nullptr)
1558 pSecurityFunctionTable = InitSecurityInterfaceW();
1560 if (pSecurityFunctionTable ==
nullptr)
1566static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
1569 if (!q_SSPI_library_load())
1570 return QByteArray();
1574 if (!ctx->sspiWindowsHandles)
1575 ctx->sspiWindowsHandles.reset(
new QSSPIWindowsHandles);
1576 SecInvalidateHandle(&ctx->sspiWindowsHandles->credHandle);
1577 SecInvalidateHandle(&ctx->sspiWindowsHandles->ctxHandle);
1579 SEC_WINNT_AUTH_IDENTITY auth;
1580 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1581 bool useAuth =
false;
1582 if (method == QAuthenticatorPrivate::Negotiate && !ctx->user.isEmpty()) {
1583 auth.Domain =
const_cast<ushort *>(
reinterpret_cast<
const ushort *>(ctx->userDomain.constData()));
1584 auth.DomainLength = ctx->userDomain.size();
1585 auth.User =
const_cast<ushort *>(
reinterpret_cast<
const ushort *>(ctx->user.constData()));
1586 auth.UserLength = ctx->user.size();
1587 auth.Password =
const_cast<ushort *>(
reinterpret_cast<
const ushort *>(ctx->password.constData()));
1588 auth.PasswordLength = ctx->password.size();
1593 SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
1595 (SEC_WCHAR *)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
1596 SECPKG_CRED_OUTBOUND,
nullptr, useAuth ? &auth :
nullptr,
nullptr,
nullptr,
1597 &ctx->sspiWindowsHandles->credHandle, &expiry
1599 if (secStatus != SEC_E_OK) {
1600 ctx->sspiWindowsHandles.reset(
nullptr);
1601 return QByteArray();
1604 return qSspiContinue(ctx, method, host);
1607static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
1608 QStringView host, QByteArrayView challenge)
1611 SecBuffer challengeBuf;
1612 SecBuffer responseBuf;
1613 SecBufferDesc challengeDesc;
1614 SecBufferDesc responseDesc;
1615 unsigned long attrs;
1618 if (!challenge.isEmpty())
1621 challengeDesc.ulVersion = SECBUFFER_VERSION;
1622 challengeDesc.cBuffers = 1;
1623 challengeDesc.pBuffers = &challengeBuf;
1624 challengeBuf.BufferType = SECBUFFER_TOKEN;
1625 challengeBuf.pvBuffer = (PVOID)(challenge.data());
1626 challengeBuf.cbBuffer = challenge.length();
1630 responseDesc.ulVersion = SECBUFFER_VERSION;
1631 responseDesc.cBuffers = 1;
1632 responseDesc.pBuffers = &responseBuf;
1633 responseBuf.BufferType = SECBUFFER_TOKEN;
1634 responseBuf.pvBuffer =
nullptr;
1635 responseBuf.cbBuffer = 0;
1638 QString targetName = ctx->options.value(
"spn"_L1).toString();
1639 if (targetName.isEmpty())
1640 targetName =
"HTTP/"_L1 + host;
1641 const std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
1642 ? targetName : QString()).toStdWString();
1645 SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
1646 &ctx->sspiWindowsHandles->credHandle,
1647 !challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle :
nullptr,
1648 const_cast<
wchar_t*>(targetNameW.data()),
1649 ISC_REQ_ALLOCATE_MEMORY,
1650 0, SECURITY_NATIVE_DREP,
1651 !challenge.isEmpty() ? &challengeDesc :
nullptr,
1652 0, &ctx->sspiWindowsHandles->ctxHandle,
1653 &responseDesc, &attrs,
1657 if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
1658 secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle,
1662 if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) {
1663 pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle);
1664 pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle);
1665 ctx->sspiWindowsHandles.reset(
nullptr);
1668 result = QByteArray((
const char*)responseBuf.pvBuffer, responseBuf.cbBuffer);
1669 pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer);
1676#elif QT_CONFIG(gssapi)
1682static void q_GSSAPI_error_int(
const char *message, OM_uint32 stat,
int type)
1684 OM_uint32 minStat, msgCtx = 0;
1685 gss_buffer_desc msg;
1688 gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
1689 qCDebug(lcAuthenticator) << message <<
": " <<
reinterpret_cast<
const char*>(msg.value);
1690 gss_release_buffer(&minStat, &msg);
1695static void q_GSSAPI_error(
const char *message, OM_uint32 majStat, OM_uint32 minStat)
1698 q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE);
1701 q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
1704static gss_name_t qGSsapiGetServiceName(QStringView host)
1706 QByteArray serviceName =
"HTTPS@" + host.toLocal8Bit();
1707 gss_buffer_desc nameDesc = {
static_cast<std::size_t>(serviceName.size()), serviceName.data()};
1709 gss_name_t importedName;
1711 OM_uint32 majStat = gss_import_name(&minStat, &nameDesc,
1712 GSS_C_NT_HOSTBASED_SERVICE, &importedName);
1714 if (majStat != GSS_S_COMPLETE) {
1715 q_GSSAPI_error(
"gss_import_name error", majStat, minStat);
1718 return importedName;
1722static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host)
1724 if (!ctx->gssApiHandles)
1725 ctx->gssApiHandles.reset(
new QGssApiHandles);
1728 gss_name_t name = qGSsapiGetServiceName(host);
1729 if (name ==
nullptr) {
1730 ctx->gssApiHandles.reset(
nullptr);
1731 return QByteArray();
1733 ctx->gssApiHandles->targetName = name;
1736 ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
1737 return qGssapiContinue(ctx);
1741static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge)
1743 OM_uint32 majStat, minStat, ignored;
1745 gss_buffer_desc inBuf = {0,
nullptr};
1746 gss_buffer_desc outBuf;
1748 if (!challenge.isEmpty()) {
1749 inBuf.value =
const_cast<
char*>(challenge.data());
1750 inBuf.length = challenge.size();
1753 majStat = gss_init_sec_context(&minStat,
1754 GSS_C_NO_CREDENTIAL,
1755 &ctx->gssApiHandles->gssCtx,
1756 ctx->gssApiHandles->targetName,
1760 GSS_C_NO_CHANNEL_BINDINGS,
1761 challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf,
1767 if (outBuf.length != 0)
1768 result = QByteArray(
reinterpret_cast<
const char*>(outBuf.value), outBuf.length);
1769 gss_release_buffer(&ignored, &outBuf);
1771 if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) {
1772 q_GSSAPI_error(
"gss_init_sec_context error", majStat, minStat);
1773 gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1774 if (ctx->gssApiHandles->gssCtx)
1775 gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER);
1776 ctx->gssApiHandles.reset(
nullptr);
1779 if (majStat == GSS_S_COMPLETE) {
1780 gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1781 ctx->gssApiHandles.reset(
nullptr);
1787static bool qGssapiTestGetCredentials(QStringView host)
1789 gss_name_t serviceName = qGSsapiGetServiceName(host);
1794 OM_uint32 majStat = gss_acquire_cred(&minStat, serviceName, GSS_C_INDEFINITE,
1795 GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred,
nullptr,
1799 gss_release_name(&ignored, &serviceName);
1800 gss_release_cred(&ignored, &cred);
1802 if (majStat != GSS_S_COMPLETE) {
1803 q_GSSAPI_error(
"gss_acquire_cred", majStat, minStat);
1815#include "moc_qauthenticator.cpp"
QByteArray targetInfoBuff
unsigned char challenge[8]
QByteArray ntlmResponseBuf
static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx)
static QByteArray qNtlmPhase1()
#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
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN
static bool qNtlmDecodePhase2(const QByteArray &data, QNtlmPhase2Block &ch)
#define Q_LOGGING_CATEGORY(name,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)