• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

kntlm.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (c) 2004 Szombathelyi Gy�gy <gyurco@freemail.hu>
00003 
00004    The implementation is based on the documentation and sample code
00005    at http://davenport.sourceforge.net/ntlm.html
00006    The DES encryption functions are from libntlm
00007    at http://josefsson.org/libntlm/
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License version 2 as published by the Free Software Foundation.
00012 
00013    This library is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this library; see the file COPYING.LIB.  If not, write to
00020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021    Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "kntlm.h"
00025 #include "des.h"
00026 
00027 #include <cstring>
00028 
00029 #include <QtCore/QDate>
00030 #include <QtCore/QtEndian>
00031 #include <QtCore/QCryptographicHash>
00032 
00033 #include <krandom.h>
00034 #include <kdebug.h>
00035 
00036 static const char NTLM_SIGNATURE[] = "NTLMSSP";
00037 
00038 static QByteArray QString2UnicodeLE (const QString &target)
00039 {
00040     QByteArray unicode (target.length() * 2, 0);
00041 
00042     for (int i = 0; i < target.length(); i++) {
00043         ((quint16 *) unicode.data()) [ i ] = qToLittleEndian (target[i].unicode());
00044     }
00045 
00046     return unicode;
00047 }
00048 
00049 static QString UnicodeLE2QString (const QChar *data, uint len)
00050 {
00051     QString ret;
00052 
00053     for (uint i = 0; i < len; i++) {
00054         ret += qFromLittleEndian (data[ i ].unicode());
00055     }
00056 
00057     return ret;
00058 }
00059 
00060 static QByteArray getBuf (const QByteArray &buf, const KNTLM::SecBuf &secbuf)
00061 {
00062     quint32 offset = qFromLittleEndian ( (quint32) secbuf.offset);
00063     quint16 len = qFromLittleEndian (secbuf.len);
00064 
00065     //watch for buffer overflows
00066     if (offset > (quint32) buf.size() || offset + len > (quint32) buf.size()) {
00067         return QByteArray();
00068     }
00069 
00070     return QByteArray (buf.data() + offset, buf.size());
00071 }
00072 
00073 static void addBuf (QByteArray &buf, KNTLM::SecBuf &secbuf, const QByteArray &data)
00074 {
00075     quint32 offset = (buf.size() + 1) & 0xfffffffe;
00076     quint16 len = data.size();
00077     quint16 maxlen = data.size();
00078 
00079     secbuf.offset = qToLittleEndian ( (quint32) offset);
00080     secbuf.len = qToLittleEndian (len);
00081     secbuf.maxlen = qToLittleEndian (maxlen);
00082     buf.resize (offset + len);
00083     memcpy (buf.data() + offset, data.data(), data.size());
00084 }
00085 
00086 static QString getString (const QByteArray &buf, const KNTLM::SecBuf &secbuf, bool unicode)
00087 {
00088     //watch for buffer overflows
00089     quint32 offset = qFromLittleEndian ( (quint32) secbuf.offset);
00090     quint16 len = qFromLittleEndian (secbuf.len);
00091 
00092     if (offset > (quint32) buf.size() || offset + len > (quint32) buf.size()) {
00093         return QString();
00094     }
00095 
00096     const char *c = buf.data() + offset;
00097 
00098     if (unicode) {
00099         return UnicodeLE2QString ( (QChar *) c, len >> 1);
00100     }
00101 
00102     return QString::fromLatin1 (c, len);
00103 }
00104 
00105 static void addString (QByteArray &buf, KNTLM::SecBuf &secbuf, const QString &str, bool unicode = false)
00106 {
00107     if (unicode) {
00108         addBuf (buf, secbuf, QString2UnicodeLE (str));
00109         return;
00110     }
00111 
00112     addBuf (buf, secbuf, str.toLatin1());
00113 }
00114 
00115 /*
00116 * turns a 56 bit key into the 64 bit, odd parity key and sets the key.
00117 * The key schedule ks is also set.
00118 */
00119 static void convertKey (unsigned char *key_56, void *ks)
00120 {
00121     unsigned char key[8];
00122 
00123     key[0] = key_56[0];
00124     key[1] = ( (key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
00125     key[2] = ( (key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
00126     key[3] = ( (key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
00127     key[4] = ( (key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
00128     key[5] = ( (key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
00129     key[6] = ( (key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
00130     key[7] = (key_56[6] << 1) & 0xFF;
00131 
00132     for (uint i = 0; i < 8; i++) {
00133         unsigned char b = key[i];
00134         bool needsParity = ((((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0);
00135 
00136         if (needsParity) {
00137             key[i] |= 0x01;
00138         } else {
00139             key[i] &= 0xfe;
00140         }
00141     }
00142 
00143     ntlm_des_set_key ( (DES_KEY *) ks, (char *) &key, sizeof (key));
00144     memset (&key, 0, sizeof (key));
00145 }
00146 
00147 static QByteArray createBlob (const QByteArray &targetinfo)
00148 {
00149     QByteArray blob (sizeof (KNTLM::Blob) + 4 + targetinfo.size(), 0);
00150 
00151     KNTLM::Blob *bl = (KNTLM::Blob *) blob.data();
00152     bl->signature = qToBigEndian ( (quint32) 0x01010000);
00153     quint64 now = QDateTime::currentDateTime().toTime_t();
00154     now += (quint64) 3600 * (quint64) 24 * (quint64) 134774;
00155     now *= (quint64) 10000000;
00156     bl->timestamp = qToLittleEndian (now);
00157 
00158     for (uint i = 0; i < 8; i++) {
00159         bl->challenge[i] = KRandom::random() % 0xff;
00160     }
00161 
00162     memcpy (blob.data() + sizeof (KNTLM::Blob), targetinfo.data(), targetinfo.size());
00163     return blob;
00164 }
00165 
00166 static QByteArray hmacMD5 (const QByteArray &data, const QByteArray &key)
00167 {
00168     quint8 ipad[64], opad[64];
00169     QByteArray ret;
00170 
00171     memset (ipad, 0x36, sizeof (ipad));
00172     memset (opad, 0x5c, sizeof (opad));
00173 
00174     for (int i = key.size() - 1; i >= 0; i--) {
00175         ipad[i] ^= key[i];
00176         opad[i] ^= key[i];
00177     }
00178 
00179     QByteArray content (data.size() + 64, 0);
00180     memcpy (content.data(), ipad, 64);
00181     memcpy (content.data() + 64, data.data(), data.size());
00182 
00183     QCryptographicHash md5 (QCryptographicHash::Md5);
00184     md5.addData (content);
00185     content.resize (64);
00186     memcpy (content.data(), opad, 64);
00187     content += md5.result();
00188 
00189     md5.reset();
00190     md5.addData (content);
00191 
00192     return md5.result();
00193 }
00194 
00195 
00196 /*************************************** KNTLM implementation ***************************************/
00197 
00198 bool KNTLM::getNegotiate (QByteArray &negotiate, const QString &domain, const QString &workstation, quint32 flags)
00199 {
00200     QByteArray rbuf (sizeof (Negotiate), 0);
00201 
00202     memcpy (rbuf.data(), NTLM_SIGNATURE, sizeof (NTLM_SIGNATURE));
00203     ((Negotiate *) rbuf.data())->msgType = qToLittleEndian ( (quint32) 1);
00204 
00205     if (!domain.isEmpty()) {
00206         flags |= Negotiate_Domain_Supplied;
00207         addString (rbuf, ((Negotiate *) rbuf.data())->domain, domain);
00208     }
00209 
00210     if (!workstation.isEmpty()) {
00211         flags |= Negotiate_WS_Supplied;
00212         addString (rbuf, ((Negotiate *) rbuf.data())->workstation, workstation);
00213     }
00214 
00215     ((Negotiate *) rbuf.data())->flags = qToLittleEndian (flags);
00216     negotiate = rbuf;
00217     return true;
00218 }
00219 
00220 bool KNTLM::getAuth (QByteArray &auth, const QByteArray &challenge,
00221                      const QString &user, const QString &password, const QString &domain,
00222                      const QString &workstation, AuthFlags authflags)
00223 {
00224     QByteArray rbuf (sizeof (Auth), 0);
00225     Challenge *ch = (Challenge *) challenge.data();
00226     QByteArray response;
00227     uint chsize = challenge.size();
00228     bool unicode = false;
00229     QString dom;
00230 
00231     //challenge structure too small
00232     if (chsize < 32) {
00233         return false;
00234     }
00235 
00236     unicode = qFromLittleEndian (ch->flags) & Negotiate_Unicode;
00237 
00238     if (domain.isEmpty()) {
00239         dom = getString (challenge, ch->targetName, unicode);
00240     } else {
00241         dom = domain;
00242     }
00243 
00244     memcpy (rbuf.data(), NTLM_SIGNATURE, sizeof (NTLM_SIGNATURE));
00245     ((Auth *) rbuf.data())->msgType = qToLittleEndian ( (quint32) 3);
00246     ((Auth *) rbuf.data())->flags = ch->flags;
00247     QByteArray targetInfo = getBuf (challenge, ch->targetInfo);
00248 
00249 
00250     if (!(authflags & Force_V1) &&
00251         ((authflags & Force_V2) ||
00252         (!targetInfo.isEmpty() && (qFromLittleEndian(ch->flags) & Negotiate_Target_Info))) /* may support NTLMv2 */) {
00253         bool ret = false;
00254 
00255         if (qFromLittleEndian (ch->flags) & Negotiate_NTLM) {
00256             if (targetInfo.isEmpty())
00257                 return false;
00258 
00259             response = getNTLMv2Response (dom, user, password, targetInfo, ch->challengeData);
00260             addBuf (rbuf, ((Auth *) rbuf.data())->ntResponse, response);
00261             ret = true;
00262         }
00263 
00264         if (authflags & Add_LM) {
00265             response = getLMv2Response (dom, user, password, ch->challengeData);
00266             addBuf (rbuf, ((Auth *) rbuf.data())->lmResponse, response);
00267             ret = true;
00268         }
00269 
00270         if (!ret) {
00271             return false;
00272         }
00273     } else { //if no targetinfo structure and NTLMv2 or LMv2 not forced, or v1 forced, try the older methods
00274         bool ret = false;
00275 
00276         if (qFromLittleEndian (ch->flags) & Negotiate_NTLM) {
00277             response = getNTLMResponse (password, ch->challengeData);
00278             addBuf (rbuf, ((Auth *) rbuf.data())->ntResponse, response);
00279             ret = true;
00280         }
00281 
00282         if (authflags & Add_LM) {
00283             response = getLMResponse (password, ch->challengeData);
00284             addBuf (rbuf, ((Auth *) rbuf.data())->lmResponse, response);
00285             ret = true;
00286         }
00287 
00288         if (!ret) {
00289             return false;
00290         }
00291     }
00292 
00293     if (!dom.isEmpty()) {
00294         addString (rbuf, ((Auth *) rbuf.data())->domain, dom, unicode);
00295     }
00296 
00297     addString (rbuf, ((Auth *) rbuf.data())->user, user, unicode);
00298 
00299     if (!workstation.isEmpty()) {
00300         addString (rbuf, ((Auth *) rbuf.data())->workstation, workstation, unicode);
00301     }
00302 
00303     auth = rbuf;
00304     return true;
00305 }
00306 
00307 QByteArray KNTLM::getLMResponse (const QString &password, const unsigned char *challenge)
00308 {
00309     QByteArray hash, answer;
00310 
00311     hash = lmHash (password);
00312     hash.resize (21);
00313     memset (hash.data() + 16, 0, 5);
00314     answer = lmResponse (hash, challenge);
00315     hash.fill (0);
00316     return answer;
00317 }
00318 
00319 QByteArray KNTLM::lmHash (const QString &password)
00320 {
00321     QByteArray keyBytes (14, 0);
00322     QByteArray hash (16, 0);
00323     DES_KEY ks;
00324     const char *magic = "KGS!@#$%";
00325 
00326     strncpy (keyBytes.data(), password.toUpper().toLatin1(), 14);
00327 
00328     convertKey ( (unsigned char *) keyBytes.data(), &ks);
00329     ntlm_des_ecb_encrypt (magic, 8, &ks, (unsigned char *) hash.data());
00330 
00331     convertKey ( (unsigned char *) keyBytes.data() + 7, &ks);
00332     ntlm_des_ecb_encrypt (magic, 8, &ks, (unsigned char *) hash.data() + 8);
00333 
00334     keyBytes.fill (0);
00335     memset (&ks, 0, sizeof (ks));
00336 
00337     return hash;
00338 }
00339 
00340 QByteArray KNTLM::lmResponse (const QByteArray &hash, const unsigned char *challenge)
00341 {
00342     DES_KEY ks;
00343     QByteArray answer (24, 0);
00344 
00345     convertKey ( (unsigned char *) hash.data(), &ks);
00346     ntlm_des_ecb_encrypt (challenge, 8, &ks, (unsigned char *) answer.data());
00347 
00348     convertKey ( (unsigned char *) hash.data() + 7, &ks);
00349     ntlm_des_ecb_encrypt (challenge, 8, &ks, (unsigned char *) answer.data() + 8);
00350 
00351     convertKey ( (unsigned char *) hash.data() + 14, &ks);
00352     ntlm_des_ecb_encrypt (challenge, 8, &ks, (unsigned char *) answer.data() + 16);
00353 
00354     memset (&ks, 0, sizeof (ks));
00355     return answer;
00356 }
00357 
00358 QByteArray KNTLM::getNTLMResponse (const QString &password, const unsigned char *challenge)
00359 {
00360     QByteArray hash = ntlmHash (password);
00361     hash.resize (21);
00362     memset (hash.data() + 16, 0, 5);
00363     QByteArray answer = lmResponse (hash, challenge);
00364     hash.fill (0);
00365     return answer;
00366 }
00367 
00368 QByteArray KNTLM::ntlmHash (const QString &password)
00369 {
00370     QByteArray unicode;
00371     unicode = QString2UnicodeLE (password);
00372 
00373     return QCryptographicHash::hash (unicode, QCryptographicHash::Md4);
00374 }
00375 
00376 QByteArray KNTLM::getNTLMv2Response (const QString &target, const QString &user,
00377                                      const QString &password, const QByteArray &targetInformation,
00378                                      const unsigned char *challenge)
00379 {
00380     QByteArray hash = ntlmv2Hash (target, user, password);
00381     QByteArray blob = createBlob (targetInformation);
00382     return lmv2Response (hash, blob, challenge);
00383 }
00384 
00385 QByteArray KNTLM::getLMv2Response (const QString &target, const QString &user,
00386                                    const QString &password, const unsigned char *challenge)
00387 {
00388     QByteArray hash = ntlmv2Hash (target, user, password);
00389     QByteArray clientChallenge (8, 0);
00390 
00391     for (uint i = 0; i < 8; i++) {
00392         clientChallenge.data() [i] = KRandom::random() % 0xff;
00393     }
00394 
00395     return lmv2Response (hash, clientChallenge, challenge);
00396 }
00397 
00398 QByteArray KNTLM::ntlmv2Hash (const QString &target, const QString &user, const QString &password)
00399 {
00400     const QByteArray hash = ntlmHash (password);
00401     const QByteArray key = QString2UnicodeLE (user.toUpper() + target);
00402     return hmacMD5 (key, hash);
00403 }
00404 
00405 QByteArray KNTLM::lmv2Response (const QByteArray &hash,
00406                                 const QByteArray &clientData, const unsigned char *challenge)
00407 {
00408     QByteArray data (8 + clientData.size(), 0);
00409     memcpy (data.data(), challenge, 8);
00410     memcpy (data.data() + 8, clientData.data(), clientData.size());
00411 
00412     QByteArray mac = hmacMD5 (data, hash);
00413     mac.resize (16 + clientData.size());
00414     memcpy (mac.data() + 16, clientData.data(), clientData.size());
00415     return mac;
00416 }

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal