File indexing completed on 2024-04-21 15:00:43

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2004 Szombathelyi György <gyurco@freemail.hu>
0004 
0005     The implementation is based on the documentation and sample code
0006     at https://davenport.sourceforge.net/ntlm.html
0007     The DES encryption functions are from libntlm
0008     at https://gitlab.com/gsasl/libntlm/
0009 
0010     SPDX-License-Identifier: LGPL-2.0-only
0011 */
0012 
0013 #include "kntlm.h"
0014 #include "des.h"
0015 
0016 #include <cstring>
0017 
0018 #include <QCryptographicHash>
0019 #include <QDate>
0020 #include <QRandomGenerator>
0021 #include <QtEndian>
0022 
0023 static const char NTLM_SIGNATURE[] = "NTLMSSP";
0024 
0025 static constexpr int NTLM_BLOB_SIZE = 28;
0026 
0027 static QByteArray QString2UnicodeLE(const QString &target)
0028 {
0029     QByteArray unicode(target.length() * 2, 0);
0030 
0031     for (int i = 0; i < target.length(); i++) {
0032         ((quint16 *)unicode.data())[i] = qToLittleEndian(target[i].unicode());
0033     }
0034 
0035     return unicode;
0036 }
0037 
0038 static QString UnicodeLE2QString(const QChar *data, uint len)
0039 {
0040     QString ret;
0041 
0042     for (uint i = 0; i < len; i++) {
0043         ret += qFromLittleEndian(data[i].unicode());
0044     }
0045 
0046     return ret;
0047 }
0048 
0049 static QByteArray getBuf(const QByteArray &buf, const KNTLM::SecBuf &secbuf)
0050 {
0051     quint32 offset = qFromLittleEndian(secbuf.offset);
0052     quint16 len = qFromLittleEndian(secbuf.len);
0053 
0054     // watch for buffer overflows
0055     if (offset > (quint32)buf.size() || offset + len > (quint32)buf.size()) {
0056         return QByteArray();
0057     }
0058 
0059     return QByteArray(buf.data() + offset, len);
0060 }
0061 
0062 static void addBuf(QByteArray &buf, KNTLM::SecBuf &secbuf, const QByteArray &data)
0063 {
0064     quint32 offset = (buf.size() + 1) & 0xfffffffe;
0065     quint16 len = data.size();
0066     quint16 maxlen = data.size();
0067 
0068     secbuf.offset = qToLittleEndian((quint32)offset);
0069     secbuf.len = qToLittleEndian(len);
0070     secbuf.maxlen = qToLittleEndian(maxlen);
0071     buf.resize(offset + len);
0072     memcpy(buf.data() + offset, data.data(), data.size());
0073 }
0074 
0075 static QString getString(const QByteArray &buf, const KNTLM::SecBuf &secbuf, bool unicode)
0076 {
0077     // watch for buffer overflows
0078     quint32 offset = qFromLittleEndian((quint32)secbuf.offset);
0079     quint16 len = qFromLittleEndian(secbuf.len);
0080 
0081     if (offset > (quint32)buf.size() || offset + len > (quint32)buf.size()) {
0082         return QString();
0083     }
0084 
0085     const char *c = buf.data() + offset;
0086 
0087     if (unicode) {
0088         return UnicodeLE2QString((QChar *)c, len >> 1);
0089     }
0090 
0091     return QString::fromLatin1(c, len);
0092 }
0093 
0094 static void addString(QByteArray &buf, KNTLM::SecBuf &secbuf, const QString &str, bool unicode = false)
0095 {
0096     if (unicode) {
0097         addBuf(buf, secbuf, QString2UnicodeLE(str));
0098         return;
0099     }
0100 
0101     addBuf(buf, secbuf, str.toLatin1());
0102 }
0103 
0104 /*
0105  * turns a 56 bit key into the 64 bit, odd parity key and sets the key.
0106  * The key schedule ks is also set.
0107  */
0108 static void convertKey(unsigned char *key_56, void *ks)
0109 {
0110     unsigned char key[8];
0111 
0112     key[0] = key_56[0];
0113     key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
0114     key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
0115     key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
0116     key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
0117     key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
0118     key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
0119     key[7] = (key_56[6] << 1) & 0xFF;
0120 
0121     for (unsigned char &b : key) {
0122         const bool needsParity = ((((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0);
0123 
0124         if (needsParity) {
0125             b |= 0x01;
0126         } else {
0127             b &= 0xfe;
0128         }
0129     }
0130 
0131     ntlm_des_set_key((DES_KEY *)ks, (char *)&key, sizeof(key));
0132     memset(&key, 0, sizeof(key));
0133 }
0134 
0135 static QByteArray createBlob(const QByteArray &targetinfo)
0136 {
0137     QByteArray blob(NTLM_BLOB_SIZE + 4 + targetinfo.size(), 0);
0138 
0139     KNTLM::Blob *bl = (KNTLM::Blob *)blob.data();
0140     bl->signature = qToBigEndian((quint32)0x01010000);
0141     quint64 now = QDateTime::currentSecsSinceEpoch();
0142     now += (quint64)3600 * (quint64)24 * (quint64)134774;
0143     now *= (quint64)10000000;
0144     bl->timestamp = qToLittleEndian(now);
0145 
0146     for (unsigned char &b : bl->challenge) {
0147         b = QRandomGenerator::global()->bounded(0xff);
0148     }
0149 
0150     memcpy(blob.data() + NTLM_BLOB_SIZE, targetinfo.data(), targetinfo.size());
0151     return blob;
0152 }
0153 
0154 static QByteArray hmacMD5(const QByteArray &data, const QByteArray &key)
0155 {
0156     QByteArray ipad(64, 0x36);
0157     QByteArray opad(64, 0x5c);
0158 
0159     Q_ASSERT(key.size() <= 64);
0160 
0161     for (int i = qMin(key.size(), 64) - 1; i >= 0; i--) {
0162         ipad.data()[i] ^= key[i];
0163         opad.data()[i] ^= key[i];
0164     }
0165 
0166     QByteArray content(ipad + data);
0167 
0168     QCryptographicHash md5(QCryptographicHash::Md5);
0169     md5.addData(content);
0170     content = opad + md5.result();
0171 
0172     md5.reset();
0173     md5.addData(content);
0174 
0175     return md5.result();
0176 }
0177 
0178 /*************************************** KNTLM implementation ***************************************/
0179 
0180 bool KNTLM::getNegotiate(QByteArray &negotiate, const QString &domain, const QString &workstation, quint32 flags)
0181 {
0182     QByteArray rbuf(sizeof(Negotiate), 0);
0183 
0184     memcpy(rbuf.data(), NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
0185     ((Negotiate *)rbuf.data())->msgType = qToLittleEndian((quint32)1);
0186 
0187     if (!domain.isEmpty()) {
0188         flags |= Negotiate_Domain_Supplied;
0189         addString(rbuf, ((Negotiate *)rbuf.data())->domain, domain);
0190     }
0191 
0192     if (!workstation.isEmpty()) {
0193         flags |= Negotiate_WS_Supplied;
0194         addString(rbuf, ((Negotiate *)rbuf.data())->workstation, workstation);
0195     }
0196 
0197     ((Negotiate *)rbuf.data())->flags = qToLittleEndian(flags);
0198     negotiate = rbuf;
0199     return true;
0200 }
0201 
0202 bool KNTLM::getAuth(QByteArray &auth,
0203                     const QByteArray &challenge,
0204                     const QString &user,
0205                     const QString &password,
0206                     const QString &domain,
0207                     const QString &workstation,
0208                     AuthFlags authflags)
0209 {
0210     QByteArray rbuf(sizeof(Auth), 0);
0211     Challenge *ch = (Challenge *)challenge.data();
0212     QByteArray response;
0213     const uint chsize = challenge.size();
0214     bool unicode = false;
0215     QString dom;
0216 
0217     // challenge structure too small
0218     if (chsize < 32) {
0219         return false;
0220     }
0221 
0222     unicode = qFromLittleEndian(ch->flags) & Negotiate_Unicode;
0223 
0224     // If the domain is NULL (i.e. QString()) use the target domain. If the domain is empty
0225     // (i.e. QString("")) use an empty domain.
0226     if (domain.isNull()) {
0227         dom = getString(challenge, ch->targetName, unicode);
0228     } else {
0229         dom = domain;
0230     }
0231 
0232     memcpy(rbuf.data(), NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
0233     ((Auth *)rbuf.data())->msgType = qToLittleEndian((quint32)3);
0234     ((Auth *)rbuf.data())->flags = ch->flags;
0235     QByteArray targetInfo;
0236     if (chsize >= sizeof(Challenge)) {
0237         targetInfo = getBuf(challenge, ch->targetInfo);
0238     }
0239 
0240     if (!(authflags & Force_V1)
0241         && ((authflags & Force_V2) //
0242             || (!targetInfo.isEmpty() && (qFromLittleEndian(ch->flags) & Negotiate_Target_Info))) /* may support NTLMv2 */) {
0243         bool ret = false;
0244 
0245         if (qFromLittleEndian(ch->flags) & Negotiate_NTLM) {
0246             if (targetInfo.isEmpty()) {
0247                 return false;
0248             }
0249 
0250             response = getNTLMv2Response(dom, user, password, targetInfo, ch->challengeData);
0251             addBuf(rbuf, ((Auth *)rbuf.data())->ntResponse, response);
0252             ret = true;
0253         }
0254 
0255         if (authflags & Add_LM) {
0256             response = getLMv2Response(dom, user, password, ch->challengeData);
0257             addBuf(rbuf, ((Auth *)rbuf.data())->lmResponse, response);
0258             ret = true;
0259         }
0260 
0261         if (!ret) {
0262             return false;
0263         }
0264     } else { // if no targetinfo structure and NTLMv2 or LMv2 not forced, or v1 forced, try the older methods
0265         bool ret = false;
0266 
0267         if (qFromLittleEndian(ch->flags) & Negotiate_NTLM) {
0268             response = getNTLMResponse(password, ch->challengeData);
0269             addBuf(rbuf, ((Auth *)rbuf.data())->ntResponse, response);
0270             ret = true;
0271         }
0272 
0273         if (authflags & Add_LM) {
0274             response = getLMResponse(password, ch->challengeData);
0275             addBuf(rbuf, ((Auth *)rbuf.data())->lmResponse, response);
0276             ret = true;
0277         }
0278 
0279         if (!ret) {
0280             return false;
0281         }
0282     }
0283 
0284     if (!dom.isEmpty()) {
0285         addString(rbuf, ((Auth *)rbuf.data())->domain, dom, unicode);
0286     }
0287 
0288     addString(rbuf, ((Auth *)rbuf.data())->user, user, unicode);
0289 
0290     if (!workstation.isEmpty()) {
0291         addString(rbuf, ((Auth *)rbuf.data())->workstation, workstation, unicode);
0292     }
0293 
0294     auth = rbuf;
0295     return true;
0296 }
0297 
0298 QByteArray KNTLM::getLMResponse(const QString &password, const unsigned char *challenge)
0299 {
0300     QByteArray hash;
0301     QByteArray answer;
0302 
0303     hash = lmHash(password);
0304     hash.resize(21);
0305     memset(hash.data() + 16, 0, 5);
0306     answer = lmResponse(hash, challenge);
0307     hash.fill(0);
0308     return answer;
0309 }
0310 
0311 QByteArray KNTLM::lmHash(const QString &password)
0312 {
0313     QByteArray keyBytes(14, 0);
0314     QByteArray hash(16, 0);
0315     DES_KEY ks;
0316     const char *magic = "KGS!@#$%";
0317 
0318     strncpy(keyBytes.data(), password.toUpper().toLocal8Bit().constData(), 14);
0319 
0320     convertKey((unsigned char *)keyBytes.data(), &ks);
0321     ntlm_des_ecb_encrypt(magic, 8, &ks, (unsigned char *)hash.data());
0322 
0323     convertKey((unsigned char *)keyBytes.data() + 7, &ks);
0324     ntlm_des_ecb_encrypt(magic, 8, &ks, (unsigned char *)hash.data() + 8);
0325 
0326     keyBytes.fill(0);
0327     memset(&ks, 0, sizeof(ks));
0328 
0329     return hash;
0330 }
0331 
0332 QByteArray KNTLM::lmResponse(const QByteArray &hash, const unsigned char *challenge)
0333 {
0334     DES_KEY ks;
0335     QByteArray answer(24, 0);
0336 
0337     convertKey((unsigned char *)hash.data(), &ks);
0338     ntlm_des_ecb_encrypt(challenge, 8, &ks, (unsigned char *)answer.data());
0339 
0340     convertKey((unsigned char *)hash.data() + 7, &ks);
0341     ntlm_des_ecb_encrypt(challenge, 8, &ks, (unsigned char *)answer.data() + 8);
0342 
0343     convertKey((unsigned char *)hash.data() + 14, &ks);
0344     ntlm_des_ecb_encrypt(challenge, 8, &ks, (unsigned char *)answer.data() + 16);
0345 
0346     memset(&ks, 0, sizeof(ks));
0347     return answer;
0348 }
0349 
0350 QByteArray KNTLM::getNTLMResponse(const QString &password, const unsigned char *challenge)
0351 {
0352     QByteArray hash = ntlmHash(password);
0353     hash.resize(21);
0354     memset(hash.data() + 16, 0, 5);
0355     QByteArray answer = lmResponse(hash, challenge);
0356     hash.fill(0);
0357     return answer;
0358 }
0359 
0360 QByteArray KNTLM::ntlmHash(const QString &password)
0361 {
0362     QByteArray unicode;
0363     unicode = QString2UnicodeLE(password);
0364 
0365     return QCryptographicHash::hash(unicode, QCryptographicHash::Md4);
0366 }
0367 
0368 QByteArray KNTLM::getNTLMv2Response(const QString &target,
0369                                     const QString &user,
0370                                     const QString &password,
0371                                     const QByteArray &targetInformation,
0372                                     const unsigned char *challenge)
0373 {
0374     QByteArray hash = ntlmv2Hash(target, user, password);
0375     QByteArray blob = createBlob(targetInformation);
0376     return lmv2Response(hash, blob, challenge);
0377 }
0378 
0379 QByteArray KNTLM::getLMv2Response(const QString &target, const QString &user, const QString &password, const unsigned char *challenge)
0380 {
0381     QByteArray hash = ntlmv2Hash(target, user, password);
0382     QByteArray clientChallenge(8, 0);
0383 
0384     for (uint i = 0; i < 8; i++) {
0385         clientChallenge.data()[i] = QRandomGenerator::global()->bounded(0xff);
0386     }
0387 
0388     return lmv2Response(hash, clientChallenge, challenge);
0389 }
0390 
0391 QByteArray KNTLM::ntlmv2Hash(const QString &target, const QString &user, const QString &password)
0392 {
0393     const QByteArray hash = ntlmHash(password);
0394     const QByteArray key = QString2UnicodeLE(user.toUpper() + target);
0395     return hmacMD5(key, hash);
0396 }
0397 
0398 QByteArray KNTLM::lmv2Response(const QByteArray &hash, const QByteArray &clientData, const unsigned char *challenge)
0399 {
0400     QByteArray data(8 + clientData.size(), 0);
0401     memcpy(data.data(), challenge, 8);
0402     memcpy(data.data() + 8, clientData.data(), clientData.size());
0403 
0404     QByteArray mac = hmacMD5(data, hash);
0405     mac.resize(16 + clientData.size());
0406     memcpy(mac.data() + 16, clientData.data(), clientData.size());
0407     return mac;
0408 }