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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
171
172
173QAuthenticator::QAuthenticator()
179
180
181QAuthenticator::~QAuthenticator()
188
189
190QAuthenticator::QAuthenticator(
const QAuthenticator &other)
198
199
200QAuthenticator &QAuthenticator::operator=(
const QAuthenticator &other)
210 d->user = other.d->user;
211 d->userDomain = other.d->userDomain;
212 d->workstation = other.d->workstation;
213 d->extractedUser = other.d->extractedUser;
214 d->password = other.d->password;
215 d->realm = other.d->realm;
216 d->method = other.d->method;
217 d->options = other.d->options;
218 }
else if (d->phase == QAuthenticatorPrivate::Start) {
226
227
228
229bool QAuthenticator::operator==(
const QAuthenticator &other)
const
235 return d->user == other.d->user
236 && d->password == other.d->password
237 && d->realm == other.d->realm
238 && d->method == other.d->method
239 && d->options == other.d->options;
243
244
245
246
247
250
251
252QString QAuthenticator::user()
const
254 return d ? d->user : QString();
258
259
260
261
262void QAuthenticator::setUser(
const QString &user)
264 if (!d || d->user != user) {
267 d->updateCredentials();
272
273
274QString QAuthenticator::password()
const
276 return d ? d->password : QString();
280
281
282
283
284void QAuthenticator::setPassword(
const QString &password)
286 if (!d || d->password != password) {
288 d->password = password;
293
294
295void QAuthenticator::detach()
298 d =
new QAuthenticatorPrivate;
302 if (d->phase == QAuthenticatorPrivate::Done)
303 d->phase = QAuthenticatorPrivate::Start;
307
308
309QString QAuthenticator::realm()
const
311 return d ? d->realm : QString();
315
316
317void QAuthenticator::setRealm(
const QString &realm)
319 if (!d || d->realm != realm) {
326
327
328
329
330
331
332
333
334QVariant QAuthenticator::option(
const QString &opt)
const
336 return d ? d->options.value(opt) : QVariant();
340
341
342
343
344
345
346
347QVariantHash QAuthenticator::options()
const
349 return d ? d->options : QVariantHash();
353
354
355
356
357
358
359
360void QAuthenticator::setOption(
const QString &opt,
const QVariant &value)
362 if (option(opt) != value) {
364 d->options.insert(opt, value);
370
371
372
373
374
375bool QAuthenticator::isNull()
const
381
382
383
384
385
387void QAuthenticator::clear()
390 d =
new QAuthenticatorPrivate;
392 *d = QAuthenticatorPrivate();
394 d->phase = QAuthenticatorPrivate::Done;
398class QSSPIWindowsHandles
401 CredHandle credHandle;
402 CtxtHandle ctxHandle;
404#elif QT_CONFIG(gssapi)
408 gss_ctx_id_t gssCtx =
nullptr;
409 gss_name_t targetName;
414QAuthenticatorPrivate::QAuthenticatorPrivate()
420 cnonce = QCryptographicHash::hash(QByteArray::number(QRandomGenerator::system()->generate64(), 16),
421 QCryptographicHash::Md5).toHex();
425QAuthenticatorPrivate::~QAuthenticatorPrivate() =
default;
427void QAuthenticatorPrivate::updateCredentials()
429 int separatorPosn = 0;
432 case QAuthenticatorPrivate::Ntlm:
433 if ((separatorPosn = user.indexOf(
"\\"_L1)) != -1) {
436 userDomain = user.left(separatorPosn);
437 extractedUser = user.mid(separatorPosn + 1);
439 extractedUser = user;
450bool QAuthenticatorPrivate::isMethodSupported(QByteArrayView method)
452 Q_ASSERT(!method.startsWith(
' '));
453 auto separator = method.indexOf(
' ');
455 method = method.first(separator);
456 const auto isSupported = [method](QByteArrayView reference) {
457 return method.compare(reference, Qt::CaseInsensitive) == 0;
459 static const char methods[][10] = {
463#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
467 return std::any_of(methods, methods + std::size(methods), isSupported);
472 auto opts = QAuthenticatorPrivate::parseDigestAuthenticationChallenge(value);
473 if (
auto it = opts.constFind(
"algorithm"); it != opts.cend()) {
479 auto view = QByteArrayView(alg).first(3);
480 return view.compare(
"MD5", Qt::CaseInsensitive) == 0;
485void QAuthenticatorPrivate::parseHttpResponse(
const QHttpHeaders &headers,
486 bool isProxy, QStringView host)
488#if !QT_CONFIG(gssapi)
491 const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
492 : QHttpHeaders::WellKnownHeader::WWWAuthenticate;
496
497
498
499
500
501
502
503
505 QByteArrayView headerVal;
506 for (
const auto ¤t : headers.values(search)) {
507 const QLatin1StringView str(current);
508 if (method < Basic && str.startsWith(
"basic"_L1, Qt::CaseInsensitive)) {
510 headerVal = QByteArrayView(current).mid(6);
511 }
else if (method < Ntlm && str.startsWith(
"ntlm"_L1, Qt::CaseInsensitive)) {
513 headerVal = QByteArrayView(current).mid(5);
514 }
else if (method < DigestMd5 && str.startsWith(
"digest"_L1, Qt::CaseInsensitive)) {
516 if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
520 headerVal = QByteArrayView(current).mid(7);
521 }
else if (method < Negotiate && str.startsWith(
"negotiate"_L1, Qt::CaseInsensitive)) {
522#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
527 if (!qGssapiTestGetCredentials(host))
531 headerVal = QByteArrayView(current).mid(10);
538 challenge = headerVal.trimmed().toByteArray();
539 QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
543 auto privSetRealm = [
this](QString newRealm) {
544 if (newRealm != realm) {
548 this->options[
"realm"_L1] = realm;
554 privSetRealm(QString::fromLatin1(options.value(
"realm")));
555 if (user.isEmpty() && password.isEmpty())
563 privSetRealm(QString::fromLatin1(options.value(
"realm")));
564 if (options.value(
"stale").compare(
"true", Qt::CaseInsensitive) == 0) {
568 if (user.isEmpty() && password.isEmpty())
574 challenge = QByteArray();
579QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod,
580 QByteArrayView path, QStringView host)
582#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
586 QByteArrayView methodString;
588 case QAuthenticatorPrivate::None:
591 case QAuthenticatorPrivate::Basic:
592 methodString =
"Basic";
593 response = (user +
':'_L1 + password).toLatin1().toBase64();
596 case QAuthenticatorPrivate::DigestMd5:
597 methodString =
"Digest";
598 response = digestMd5Response(challenge, requestMethod, path);
601 case QAuthenticatorPrivate::Ntlm:
602 methodString =
"NTLM";
603 if (challenge.isEmpty()) {
605 QByteArray phase1Token;
606 if (user.isEmpty()) {
607 phase1Token = qSspiStartup(
this, method, host);
608 }
else if (!q_SSPI_library_load()) {
610 qWarning(
"Failed to load the SSPI libraries");
613 if (!phase1Token.isEmpty()) {
614 response = phase1Token.toBase64();
619 response = qNtlmPhase1().toBase64();
627 QByteArray phase3Token;
628 if (sspiWindowsHandles)
629 phase3Token = qSspiContinue(
this, method, host, QByteArray::fromBase64(challenge));
630 if (!phase3Token.isEmpty()) {
631 response = phase3Token.toBase64();
636 response = qNtlmPhase3(
this, QByteArray::fromBase64(challenge)).toBase64();
643 case QAuthenticatorPrivate::Negotiate:
644 methodString =
"Negotiate";
645 if (challenge.isEmpty()) {
646 QByteArray phase1Token;
648 phase1Token = qSspiStartup(
this, method, host);
649#elif QT_CONFIG(gssapi)
650 phase1Token = qGssapiStartup(
this, host);
653 if (!phase1Token.isEmpty()) {
654 response = phase1Token.toBase64();
661 QByteArray phase3Token;
663 if (sspiWindowsHandles)
664 phase3Token = qSspiContinue(
this, method, host, QByteArray::fromBase64(challenge));
665#elif QT_CONFIG(gssapi)
667 phase3Token = qGssapiContinue(
this, QByteArray::fromBase64(challenge));
669 if (!phase3Token.isEmpty()) {
670 response = phase3Token.toBase64();
682 return methodString +
' ' + response;
690 for (
auto element : QLatin1StringView(data).tokenize(
','_L1)) {
691 if (element ==
"auth"_L1)
697QHash<QByteArray, QByteArray>
698QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challenge)
700 QHash<QByteArray, QByteArray> options;
702 const char *d = challenge.data();
703 const char *end = d + challenge.size();
705 while (d < end && (*d ==
' ' || *d ==
'\n' || *d ==
'\r'))
707 const char *start = d;
708 while (d < end && *d !=
'=')
712 QByteArrayView key = QByteArrayView(start, d - start);
716 bool quote = (*d ==
'"');
723 bool backslash =
false;
724 if (*d ==
'\\' && d < end - 1) {
740 while (d < end && *d !=
',')
744 options[key.toByteArray()] = std::move(value);
747 QByteArray qop = options.value(
"qop");
748 if (!qop.isEmpty()) {
749 if (!containsAuth(qop))
750 return QHash<QByteArray, QByteArray>();
758 options[
"qop"] =
"auth";
765
766
767
768
769
770
776 QByteArrayView userName,
777 QByteArrayView realm,
778 QByteArrayView password,
779 QByteArrayView nonce,
780 QByteArrayView nonceCount,
781 QByteArrayView cNonce,
783 QByteArrayView method,
784 QByteArrayView digestUri,
785 QByteArrayView hEntity
789 hash.addData(userName);
793 hash.addData(password);
795 if (alg.compare(
"md5-sess", Qt::CaseInsensitive) == 0) {
801 hash.addData(ha1.toHex());
805 hash.addData(cNonce);
812 hash.addData(method);
814 hash.addData(digestUri);
815 if (qop.compare(
"auth-int", Qt::CaseInsensitive) == 0) {
817 hash.addData(hEntity);
828 hash.addData(nonceCount);
830 hash.addData(cNonce);
835 hash.addData(ha2hex);
836 return hash.result().toHex();
839QByteArray QAuthenticatorPrivate::digestMd5Response(QByteArrayView challenge, QByteArrayView method,
842 QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
845 QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
846 while (nonceCountString.size() < 8)
847 nonceCountString.prepend(
'0');
849 QByteArray nonce = options.value(
"nonce");
850 QByteArray opaque = options.value(
"opaque");
851 QByteArray qop = options.value(
"qop");
854 QByteArray response = digestMd5ResponseHelper(options.value(
"algorithm"), user.toLatin1(),
855 realm.toLatin1(), password.toLatin1(),
856 nonce, nonceCountString,
861 QByteArray credentials;
862 credentials +=
"username=\"" + user.toLatin1() +
"\", ";
863 credentials +=
"realm=\"" + realm.toLatin1() +
"\", ";
864 credentials +=
"nonce=\"" + nonce +
"\", ";
865 credentials +=
"uri=\"" + path +
"\", ";
866 if (!opaque.isEmpty())
867 credentials +=
"opaque=\"" + opaque +
"\", ";
868 credentials +=
"response=\"" + response +
'"';
869 if (!options.value(
"algorithm").isEmpty())
870 credentials +=
", algorithm=" + options.value(
"algorithm");
871 if (!options.value(
"qop").isEmpty()) {
872 credentials +=
", qop=" + qop +
", ";
873 credentials +=
"nc=" + nonceCountString +
", ";
874 credentials +=
"cnonce=\"" + cnonce +
'"';
886
887
888
889
890
891
894
895
896
897#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
900
901
902#define NTLMSSP_NEGOTIATE_OEM 0x00000002
905
906
907
908#define NTLMSSP_REQUEST_TARGET 0x00000004
911
912
913
914#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
917
918
919
920#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
923
924
925#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
928
929
930
931#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
934
935
936#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
939
940
941
942
943
944#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
947
948
949
950
951#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
954
955
956
957
958#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
961
962
963
964#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
967
968
969
970#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
973
974
975
976#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
979
980
981
982
983#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
986
987
988
989
990
991#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
994
995
996
997
998#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
1001
1002
1003#define NTLMSSP_NEGOTIATE_128 0x20000000
1006
1007
1008
1009
1010
1011#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
1014
1015
1016#define NTLMSSP_NEGOTIATE_56 0x80000000
1019
1020
1021#define AVTIMESTAMP 7
1033
1034
1035
1036
1037
1038
1039
1040
1041
1054 ds.writeRawData(s.constData(), s.size());
1061 qStreamNtlmBuffer(ds, s.toLatin1());
1066 ds << quint16(ch.unicode());
1074 buf.maxLen = buf.len;
1075 buf.offset = (offset + 1) & ~1;
1076 return buf.offset + buf.len;
1083 return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
1084 buf.len = 2 * s.size();
1085 buf.maxLen = buf.len;
1086 buf.offset = (offset + 1) & ~1;
1087 return buf.offset + buf.len;
1093 s << b.len << b.maxLen << b.offset;
1099 s >> b.len >> b.maxLen >> b.offset;
1107 char magic[8] = {
'N',
'T',
'L',
'M',
'S',
'S',
'P',
'\0'};
1140 char magic[8] = {
'N',
'T',
'L',
'M',
'S',
'S',
'P',
'\0'};
1166 if (!b.domainStr.isEmpty())
1167 qStreamNtlmString(s, b.domainStr, unicode);
1168 if (!b.workstationStr.isEmpty())
1169 qStreamNtlmString(s, b.workstationStr, unicode);
1179 s << b.ntlmResponse;
1186 if (!b.domainStr.isEmpty())
1187 qStreamNtlmString(s, b.domainStr, unicode);
1189 qStreamNtlmString(s, b.userStr, unicode);
1191 if (!b.workstationStr.isEmpty())
1192 qStreamNtlmString(s, b.workstationStr, unicode);
1195 qStreamNtlmBuffer(s, b.lmResponseBuf);
1196 qStreamNtlmBuffer(s, b.ntlmResponseBuf);
1206 QDataStream ds(&rc, QIODevice::WriteOnly);
1207 ds.setByteOrder(QDataStream::LittleEndian);
1217 unsigned short *d = (
unsigned short*)rc.data();
1218 for (QChar ch : src)
1219 *d++ = qToLittleEndian(quint16(ch.unicode()));
1227 Q_ASSERT(src.size() % 2 == 0);
1228 unsigned short *d = (
unsigned short*)src.data();
1229 for (
int i = 0; i < src.size() / 2; ++i) {
1230 d[i] = qFromLittleEndian(d[i]);
1232 return QString((
const QChar *)src.data(), src.size()/2);
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1260 Q_ASSERT_X(!(message.isEmpty()),
"qEncodeHmacMd5",
"Empty message check");
1261 Q_ASSERT_X(!(key.isEmpty()),
"qEncodeHmacMd5",
"Empty key check");
1273 key = hash.result();
1278 key = key.leftJustified(
blockSize,0,
true);
1283 for(
int i = 0; i<key.size();i++) {
1284 iKeyPad[i] = key[i]^iKeyPad[i];
1288 for(
int i = 0; i<key.size();i++) {
1289 oKeyPad[i] = key[i]^oKeyPad[i];
1292 iKeyPad.append(message);
1295 hash.addData(iKeyPad);
1296 QByteArrayView hMsg = hash.resultView();
1300 oKeyPad.append(hMsg);
1302 hash.addData(oKeyPad);
1303 hmacDigest = hash.result();
1307
1308
1309
1310
1313
1320 Q_ASSERT(phase3 !=
nullptr);
1323 if (phase3->v2Hash.size() == 0) {
1325 QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
1326 md4.addData(passUnicode);
1329 Q_ASSERT(hashKey.size() == 16);
1332 qStringAsUcs2Le(ctx->extractedUser.toUpper()) +
1333 qStringAsUcs2Le(phase3->domainStr);
1335 phase3->v2Hash = qEncodeHmacMd5(hashKey, message);
1337 return phase3->v2Hash;
1342 Q_ASSERT(ctx->cnonce.size() >= 8);
1351 const char *ptr = targetInfoBuff.constBegin();
1352 const char *end = targetInfoBuff.constEnd();
1356 while (end - ptr >= 4) {
1357 avId = qFromLittleEndian<quint16>(ptr + 0);
1358 avLen = qFromLittleEndian<quint16>(ptr + 2);
1362 if (avLen != NtlmFileTimeSize)
1367 timeArray.assign(ptr, ptr + NtlmFileTimeSize);
1371 if (avLen > end - ptr)
1382 Q_ASSERT(phase3 !=
nullptr);
1387 QDataStream ds(&temp, QIODevice::WriteOnly);
1388 ds.setByteOrder(QDataStream::LittleEndian);
1391 ds << hirespversion;
1395 ds.writeRawData(reserved1.constData(), reserved1.size());
1400 if (ch.targetInfo.len)
1402 timeArray = qExtractServerTime(ch.targetInfoBuff);
1406 if (timeArray.size()) {
1407 ds.writeRawData(timeArray.constData(), timeArray.size());
1412 time = QDateTime::currentSecsSinceEpoch() + 11644473600;
1415 time = time * Q_UINT64_C(10000000);
1421 ds.writeRawData(clientCh.constData(), clientCh.size());
1425 ds.writeRawData(reserved2.constData(), reserved2.size());
1427 if (ch.targetInfo.len > 0) {
1428 ds.writeRawData(ch.targetInfoBuff.constData(),
1429 ch.targetInfoBuff.size());
1434 ds.writeRawData(reserved3.constData(), reserved3.size());
1437 message.append(temp);
1439 QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1440 ntChallengeResp.append(temp);
1442 return ntChallengeResp;
1449 Q_ASSERT(phase3 !=
nullptr);
1456 message.append(clientCh);
1458 QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
1459 lmChallengeResp.append(clientCh);
1461 return lmChallengeResp;
1470 QDataStream ds(data);
1471 ds.setByteOrder(QDataStream::LittleEndian);
1472 if (ds.readRawData(ch
.magic, 8) < 8)
1474 if (strncmp(ch
.magic,
"NTLMSSP", 8) != 0)
1481 ds >> ch.targetName;
1483 if (ds.readRawData((
char *)ch
.challenge, 8) < 8)
1485 ds >> ch.context[0] >> ch.context[1];
1486 ds >> ch.targetInfo;
1488 if (ch.targetName.len > 0) {
1490 if (qAddOverflow(qsizetype(ch.targetName.offset), qsizetype(ch.targetName.len), &total))
1492 if (total > data.size())
1495 ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
1498 if (ch.targetInfo.len > 0) {
1500 if (qAddOverflow(qsizetype(ch.targetInfo.offset), qsizetype(ch.targetInfo.len), &total))
1502 if (total > data.size())
1505 ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len);
1519 QDataStream ds(&rc, QIODevice::WriteOnly);
1520 ds.setByteOrder(QDataStream::LittleEndian);
1542 if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(u'@')) {
1543 offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
1544 pb.domainStr = ch.targetNameStr;
1546 offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode);
1547 pb.domainStr = ctx->userDomain;
1550 offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode);
1551 pb.userStr = ctx->extractedUser;
1553 offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
1554 pb.workstationStr = ctx->workstation;
1557 if (ch.targetInfo.len > 0) {
1562 offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf);
1566 offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf);
1583static PSecurityFunctionTableW pSecurityFunctionTable =
nullptr;
1585static bool q_SSPI_library_load()
1587 Q_CONSTINIT
static QBasicMutex mutex;
1588 QMutexLocker l(&mutex);
1590 if (pSecurityFunctionTable ==
nullptr)
1591 pSecurityFunctionTable = InitSecurityInterfaceW();
1593 if (pSecurityFunctionTable ==
nullptr)
1599static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
1602 if (!q_SSPI_library_load())
1603 return QByteArray();
1607 if (!ctx->sspiWindowsHandles)
1608 ctx->sspiWindowsHandles.reset(
new QSSPIWindowsHandles);
1609 SecInvalidateHandle(&ctx->sspiWindowsHandles->credHandle);
1610 SecInvalidateHandle(&ctx->sspiWindowsHandles->ctxHandle);
1612 SEC_WINNT_AUTH_IDENTITY auth;
1613 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1614 bool useAuth =
false;
1615 if (method == QAuthenticatorPrivate::Negotiate && !ctx->user.isEmpty()) {
1616 auth.Domain =
const_cast<ushort *>(
reinterpret_cast<
const ushort *>(ctx->userDomain.constData()));
1617 auth.DomainLength = ctx->userDomain.size();
1618 auth.User =
const_cast<ushort *>(
reinterpret_cast<
const ushort *>(ctx->user.constData()));
1619 auth.UserLength = ctx->user.size();
1620 auth.Password =
const_cast<ushort *>(
reinterpret_cast<
const ushort *>(ctx->password.constData()));
1621 auth.PasswordLength = ctx->password.size();
1626 SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
1628 (SEC_WCHAR *)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
1629 SECPKG_CRED_OUTBOUND,
nullptr, useAuth ? &auth :
nullptr,
nullptr,
nullptr,
1630 &ctx->sspiWindowsHandles->credHandle, &expiry
1632 if (secStatus != SEC_E_OK) {
1633 ctx->sspiWindowsHandles.reset(
nullptr);
1634 return QByteArray();
1637 return qSspiContinue(ctx, method, host);
1640static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
1641 QStringView host, QByteArrayView challenge)
1644 SecBuffer challengeBuf;
1645 SecBuffer responseBuf;
1646 SecBufferDesc challengeDesc;
1647 SecBufferDesc responseDesc;
1648 unsigned long attrs;
1651 if (!challenge.isEmpty())
1654 challengeDesc.ulVersion = SECBUFFER_VERSION;
1655 challengeDesc.cBuffers = 1;
1656 challengeDesc.pBuffers = &challengeBuf;
1657 challengeBuf.BufferType = SECBUFFER_TOKEN;
1658 challengeBuf.pvBuffer = (PVOID)(challenge.data());
1659 challengeBuf.cbBuffer = challenge.length();
1663 responseDesc.ulVersion = SECBUFFER_VERSION;
1664 responseDesc.cBuffers = 1;
1665 responseDesc.pBuffers = &responseBuf;
1666 responseBuf.BufferType = SECBUFFER_TOKEN;
1667 responseBuf.pvBuffer =
nullptr;
1668 responseBuf.cbBuffer = 0;
1671 QString targetName = ctx->options.value(
"spn"_L1).toString();
1672 if (targetName.isEmpty())
1673 targetName =
"HTTP/"_L1 + host;
1674 const std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
1675 ? targetName : QString()).toStdWString();
1678 SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
1679 &ctx->sspiWindowsHandles->credHandle,
1680 !challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle :
nullptr,
1681 const_cast<
wchar_t*>(targetNameW.data()),
1682 ISC_REQ_ALLOCATE_MEMORY,
1683 0, SECURITY_NATIVE_DREP,
1684 !challenge.isEmpty() ? &challengeDesc :
nullptr,
1685 0, &ctx->sspiWindowsHandles->ctxHandle,
1686 &responseDesc, &attrs,
1690 if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
1691 secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle,
1695 if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) {
1696 pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle);
1697 pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle);
1698 ctx->sspiWindowsHandles.reset(
nullptr);
1701 result = QByteArray((
const char*)responseBuf.pvBuffer, responseBuf.cbBuffer);
1702 pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer);
1709#elif QT_CONFIG(gssapi)
1715static void q_GSSAPI_error_int(
const char *message, OM_uint32 stat,
int type)
1717 OM_uint32 minStat, msgCtx = 0;
1718 gss_buffer_desc msg;
1721 gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
1722 qCDebug(lcAuthenticator) << message <<
": " <<
reinterpret_cast<
const char*>(msg.value);
1723 gss_release_buffer(&minStat, &msg);
1728static void q_GSSAPI_error(
const char *message, OM_uint32 majStat, OM_uint32 minStat)
1731 q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE);
1734 q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
1737static gss_name_t qGSsapiGetServiceName(QStringView host)
1739 QByteArray serviceName =
"HTTPS@" + host.toLocal8Bit();
1740 gss_buffer_desc nameDesc = {
static_cast<std::size_t>(serviceName.size()), serviceName.data()};
1742 gss_name_t importedName;
1744 OM_uint32 majStat = gss_import_name(&minStat, &nameDesc,
1745 GSS_C_NT_HOSTBASED_SERVICE, &importedName);
1747 if (majStat != GSS_S_COMPLETE) {
1748 q_GSSAPI_error(
"gss_import_name error", majStat, minStat);
1751 return importedName;
1755static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host)
1757 if (!ctx->gssApiHandles)
1758 ctx->gssApiHandles.reset(
new QGssApiHandles);
1761 gss_name_t name = qGSsapiGetServiceName(host);
1762 if (name ==
nullptr) {
1763 ctx->gssApiHandles.reset(
nullptr);
1764 return QByteArray();
1766 ctx->gssApiHandles->targetName = name;
1769 ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
1770 return qGssapiContinue(ctx);
1774static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge)
1776 OM_uint32 majStat, minStat, ignored;
1778 gss_buffer_desc inBuf = {0,
nullptr};
1779 gss_buffer_desc outBuf;
1781 if (!challenge.isEmpty()) {
1782 inBuf.value =
const_cast<
char*>(challenge.data());
1783 inBuf.length = challenge.size();
1786 majStat = gss_init_sec_context(&minStat,
1787 GSS_C_NO_CREDENTIAL,
1788 &ctx->gssApiHandles->gssCtx,
1789 ctx->gssApiHandles->targetName,
1793 GSS_C_NO_CHANNEL_BINDINGS,
1794 challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf,
1800 if (outBuf.length != 0)
1801 result = QByteArray(
reinterpret_cast<
const char*>(outBuf.value), outBuf.length);
1802 gss_release_buffer(&ignored, &outBuf);
1804 if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) {
1805 q_GSSAPI_error(
"gss_init_sec_context error", majStat, minStat);
1806 gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1807 if (ctx->gssApiHandles->gssCtx)
1808 gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER);
1809 ctx->gssApiHandles.reset(
nullptr);
1812 if (majStat == GSS_S_COMPLETE) {
1813 gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
1814 ctx->gssApiHandles.reset(
nullptr);
1820static bool qGssapiTestGetCredentials(QStringView host)
1822 gss_name_t serviceName = qGSsapiGetServiceName(host);
1827 OM_uint32 majStat = gss_acquire_cred(&minStat, serviceName, GSS_C_INDEFINITE,
1828 GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred,
nullptr,
1832 gss_release_name(&ignored, &serviceName);
1833 gss_release_cred(&ignored, &cred);
1835 if (majStat != GSS_S_COMPLETE) {
1836 q_GSSAPI_error(
"gss_acquire_cred", majStat, minStat);
1848#include "moc_qauthenticator.cpp"
QByteArray targetInfoBuff
unsigned char challenge[8]
QByteArray ntlmResponseBuf
Combined button and popup list for selecting options.
static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx)
static QByteArray qNtlmPhase1()
#define NTLMSSP_NEGOTIATE_NTLM2
#define NTLMSSP_NEGOTIATE_TARGET_INFO
static constexpr quint16 NtlmFileTimeSize
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)