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 }
KDE 4.7 API Reference