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 <string.h> 00028 00029 #include <QtCore/QDate> 00030 #include <QtCore/qendian.h> 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 static QString UnicodeLE2QString( const QChar* data, uint len ); 00040 00041 static QByteArray getBuf( const QByteArray &buf, const KNTLM::SecBuf &secbuf ); 00042 static void addBuf( QByteArray &buf, KNTLM::SecBuf &secbuf, const QByteArray &data ); 00043 static QString getString( const QByteArray &buf, const KNTLM::SecBuf &secbuf, bool unicode ); 00044 static void addString( QByteArray &buf, KNTLM::SecBuf &secbuf, const QString &str, bool unicode = false ); 00045 static void convertKey( unsigned char *key_56, void* ks ); 00046 static QByteArray createBlob( const QByteArray &targetinfo ); 00047 static QByteArray hmacMD5( const QByteArray &data, const QByteArray &key ); 00048 00049 QString getString( const QByteArray &buf, const KNTLM::SecBuf &secbuf, bool unicode ) 00050 { 00051 //watch for buffer overflows 00052 quint32 offset; 00053 quint16 len; 00054 offset = qFromLittleEndian((quint32)secbuf.offset); 00055 len = qFromLittleEndian(secbuf.len); 00056 if ( offset > (quint32)buf.size() || 00057 offset + len > (quint32)buf.size() ) return QString(); 00058 00059 QString str; 00060 const char *c = buf.data() + offset; 00061 00062 if ( unicode ) { 00063 str = UnicodeLE2QString( (QChar*) c, len >> 1 ); 00064 } else { 00065 str = QString::fromLatin1( c, len ); 00066 } 00067 return str; 00068 } 00069 00070 QByteArray getBuf( const QByteArray &buf, const KNTLM::SecBuf &secbuf ) 00071 { 00072 quint32 offset; 00073 quint16 len; 00074 offset = qFromLittleEndian((quint32)secbuf.offset); 00075 len = qFromLittleEndian(secbuf.len); 00076 //watch for buffer overflows 00077 if ( offset > (quint32)buf.size() || 00078 offset + len > (quint32)buf.size() ) return QByteArray(); 00079 return QByteArray( buf.data() + offset, buf.size() ); 00080 } 00081 00082 void addString( QByteArray &buf, KNTLM::SecBuf &secbuf, const QString &str, bool unicode ) 00083 { 00084 if ( unicode ) { 00085 addBuf( buf, secbuf, QString2UnicodeLE( str ) ); 00086 } else { 00087 addBuf( buf, secbuf, str.toLatin1() ); 00088 } 00089 } 00090 00091 void addBuf( QByteArray &buf, KNTLM::SecBuf &secbuf, const QByteArray &data ) 00092 { 00093 quint32 offset; 00094 quint16 len, maxlen; 00095 offset = (buf.size() + 1) & 0xfffffffe; 00096 len = data.size(); 00097 maxlen = data.size(); 00098 00099 secbuf.offset = qToLittleEndian((quint32)offset); 00100 secbuf.len = qToLittleEndian(len); 00101 secbuf.maxlen = qToLittleEndian(maxlen); 00102 buf.resize( offset + len ); 00103 memcpy( buf.data() + offset, data.data(), data.size() ); 00104 } 00105 00106 bool KNTLM::getNegotiate( QByteArray &negotiate, const QString &domain, const QString &workstation, quint32 flags ) 00107 { 00108 QByteArray rbuf( sizeof(Negotiate), 0 ); 00109 00110 memcpy( rbuf.data(), NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE) ); 00111 ((Negotiate*) rbuf.data())->msgType = qToLittleEndian( (quint32)1 ); 00112 if ( !domain.isEmpty() ) { 00113 flags |= Negotiate_Domain_Supplied; 00114 addString( rbuf, ((Negotiate*) rbuf.data())->domain, domain ); 00115 } 00116 if ( !workstation.isEmpty() ) { 00117 flags |= Negotiate_WS_Supplied; 00118 addString( rbuf, ((Negotiate*) rbuf.data())->workstation, workstation ); 00119 } 00120 ((Negotiate*) rbuf.data())->flags = qToLittleEndian( flags ); 00121 negotiate = rbuf; 00122 return true; 00123 } 00124 00125 bool KNTLM::getAuth( QByteArray &auth, const QByteArray &challenge, 00126 const QString &user, const QString &password, const QString &domain, 00127 const QString &workstation, AuthFlags authflags ) 00128 { 00129 QByteArray rbuf( sizeof(Auth), 0 ); 00130 Challenge *ch = (Challenge *) challenge.data(); 00131 QByteArray response; 00132 uint chsize = challenge.size(); 00133 bool unicode = false; 00134 QString dom; 00135 00136 //challenge structure too small 00137 if ( chsize < 32 ) return false; 00138 00139 unicode = qFromLittleEndian(ch->flags) & Negotiate_Unicode; 00140 if ( domain.isEmpty() ) 00141 dom = getString( challenge, ch->targetName, unicode ); 00142 else 00143 dom = domain; 00144 00145 memcpy( rbuf.data(), NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE) ); 00146 ((Auth*) rbuf.data())->msgType = qToLittleEndian( (quint32)3 ); 00147 ((Auth*) rbuf.data())->flags = ch->flags; 00148 QByteArray targetInfo = getBuf( challenge, ch->targetInfo ); 00149 00150 if ( ((authflags & Force_V2) && !(authflags & Force_V1)) || 00151 (!targetInfo.isEmpty() && (qFromLittleEndian(ch->flags) & Negotiate_Target_Info)) /* may support NTLMv2 */ ) { 00152 bool ret = false; 00153 if ( qFromLittleEndian(ch->flags) & Negotiate_NTLM ) { 00154 if ( targetInfo.isEmpty() ) return false; 00155 response = getNTLMv2Response( dom, user, password, targetInfo, ch->challengeData ); 00156 addBuf( rbuf, ((Auth*) rbuf.data())->ntResponse, response ); 00157 ret = true; 00158 } 00159 if ( authflags & Add_LM ) { 00160 response = getLMv2Response( dom, user, password, ch->challengeData ); 00161 addBuf( rbuf, ((Auth*) rbuf.data())->lmResponse, response ); 00162 ret = true; 00163 } 00164 if ( !ret ) return false; 00165 } else { //if no targetinfo structure and NTLMv2 or LMv2 not forced, or v1 forced, try the older methods 00166 bool ret = false; 00167 if ( qFromLittleEndian(ch->flags) & Negotiate_NTLM ) { 00168 response = getNTLMResponse( password, ch->challengeData ); 00169 addBuf( rbuf, ((Auth*) rbuf.data())->ntResponse, response ); 00170 ret = true; 00171 } 00172 if ( authflags & Add_LM ) { 00173 response = getLMResponse( password, ch->challengeData ); 00174 addBuf( rbuf, ((Auth*) rbuf.data())->lmResponse, response ); 00175 ret = true; 00176 } 00177 if ( !ret ) return false; 00178 } 00179 if ( !dom.isEmpty() ) 00180 addString( rbuf, ((Auth*) rbuf.data())->domain, dom, unicode ); 00181 addString( rbuf, ((Auth*) rbuf.data())->user, user, unicode ); 00182 if ( !workstation.isEmpty() ) 00183 addString( rbuf, ((Auth*) rbuf.data())->workstation, workstation, unicode ); 00184 00185 auth = rbuf; 00186 00187 return true; 00188 } 00189 00190 QByteArray KNTLM::getLMResponse( const QString &password, const unsigned char *challenge ) 00191 { 00192 QByteArray hash, answer; 00193 00194 hash = lmHash( password ); 00195 hash.resize( 21 ); 00196 memset( hash.data() + 16, 0, 5 ); 00197 answer = lmResponse( hash, challenge ); 00198 hash.fill( 0 ); 00199 return answer; 00200 } 00201 00202 QByteArray KNTLM::lmHash( const QString &password ) 00203 { 00204 QByteArray keyBytes( 14, 0 ); 00205 QByteArray hash( 16, 0 ); 00206 DES_KEY ks; 00207 const char *magic = "KGS!@#$%"; 00208 00209 strncpy( keyBytes.data(), password.toUpper().toLatin1(), 14 ); 00210 00211 convertKey( (unsigned char*) keyBytes.data(), &ks ); 00212 ntlm_des_ecb_encrypt( magic, 8, &ks, (unsigned char*) hash.data() ); 00213 00214 convertKey( (unsigned char*) keyBytes.data() + 7, &ks ); 00215 ntlm_des_ecb_encrypt( magic, 8, &ks, (unsigned char*) hash.data() + 8 ); 00216 00217 keyBytes.fill( 0 ); 00218 memset( &ks, 0, sizeof (ks) ); 00219 00220 return hash; 00221 } 00222 00223 QByteArray KNTLM::lmResponse( const QByteArray &hash, const unsigned char *challenge ) 00224 { 00225 DES_KEY ks; 00226 QByteArray answer( 24, 0 ); 00227 00228 convertKey( (unsigned char*) hash.data(), &ks ); 00229 ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() ); 00230 00231 convertKey( (unsigned char*) hash.data() + 7, &ks ); 00232 ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() + 8 ); 00233 00234 convertKey( (unsigned char*) hash.data() + 14, &ks ); 00235 ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() + 16 ); 00236 00237 memset( &ks, 0, sizeof (ks) ); 00238 return answer; 00239 } 00240 00241 QByteArray KNTLM::getNTLMResponse( const QString &password, const unsigned char *challenge ) 00242 { 00243 QByteArray hash, answer; 00244 00245 hash = ntlmHash( password ); 00246 hash.resize( 21 ); 00247 memset( hash.data() + 16, 0, 5 ); 00248 answer = lmResponse( hash, challenge ); 00249 hash.fill( 0 ); 00250 return answer; 00251 } 00252 00253 QByteArray KNTLM::ntlmHash( const QString &password ) 00254 { 00255 QByteArray unicode; 00256 unicode = QString2UnicodeLE( password ); 00257 00258 return QCryptographicHash::hash(unicode, QCryptographicHash::Md4); 00259 } 00260 00261 QByteArray KNTLM::getNTLMv2Response( const QString &target, const QString &user, 00262 const QString &password, const QByteArray &targetInformation, 00263 const unsigned char *challenge ) 00264 { 00265 QByteArray hash = ntlmv2Hash( target, user, password ); 00266 QByteArray blob = createBlob( targetInformation ); 00267 return lmv2Response( hash, blob, challenge ); 00268 } 00269 00270 QByteArray KNTLM::getLMv2Response( const QString &target, const QString &user, 00271 const QString &password, const unsigned char *challenge ) 00272 { 00273 QByteArray hash = ntlmv2Hash( target, user, password ); 00274 QByteArray clientChallenge( 8, 0 ); 00275 for ( uint i = 0; i<8; i++ ) { 00276 clientChallenge.data()[i] = KRandom::random() % 0xff; 00277 } 00278 return lmv2Response( hash, clientChallenge, challenge ); 00279 } 00280 00281 QByteArray KNTLM::ntlmv2Hash( const QString &target, const QString &user, const QString &password ) 00282 { 00283 const QByteArray hash = ntlmHash( password ); 00284 const QByteArray key = QString2UnicodeLE( user.toUpper() + target ); 00285 return hmacMD5( key, hash ); 00286 } 00287 00288 QByteArray KNTLM::lmv2Response( const QByteArray &hash, 00289 const QByteArray &clientData, const unsigned char *challenge ) 00290 { 00291 QByteArray data( 8 + clientData.size(), 0 ); 00292 memcpy( data.data(), challenge, 8 ); 00293 memcpy( data.data() + 8, clientData.data(), clientData.size() ); 00294 QByteArray mac = hmacMD5( data, hash ); 00295 mac.resize( 16 + clientData.size() ); 00296 memcpy( mac.data() + 16, clientData.data(), clientData.size() ); 00297 return mac; 00298 } 00299 00300 QByteArray createBlob( const QByteArray &targetinfo ) 00301 { 00302 QByteArray blob( sizeof(KNTLM::Blob) + 4 + targetinfo.size(), 0 ); 00303 00304 KNTLM::Blob *bl = (KNTLM::Blob *) blob.data(); 00305 bl->signature = qToBigEndian( (quint32) 0x01010000 ); 00306 quint64 now = QDateTime::currentDateTime().toTime_t(); 00307 now += (quint64)3600*(quint64)24*(quint64)134774; 00308 now *= (quint64)10000000; 00309 bl->timestamp = qToLittleEndian( now ); 00310 for ( uint i = 0; i<8; i++ ) { 00311 bl->challenge[i] = KRandom::random() % 0xff; 00312 } 00313 memcpy( blob.data() + sizeof(KNTLM::Blob), targetinfo.data(), targetinfo.size() ); 00314 return blob; 00315 } 00316 00317 QByteArray hmacMD5( const QByteArray &data, const QByteArray &key ) 00318 { 00319 quint8 ipad[64], opad[64]; 00320 QByteArray ret; 00321 00322 memset( ipad, 0x36, sizeof(ipad) ); 00323 memset( opad, 0x5c, sizeof(opad) ); 00324 for ( int i = key.size()-1; i >= 0; i-- ) { 00325 ipad[i] ^= key[i]; 00326 opad[i] ^= key[i]; 00327 } 00328 00329 QByteArray content( data.size()+64, 0 ); 00330 memcpy( content.data(), ipad, 64 ); 00331 memcpy( content.data() + 64, data.data(), data.size() ); 00332 00333 QCryptographicHash md5(QCryptographicHash::Md5); 00334 md5.addData(content); 00335 content.resize(64); 00336 memcpy( content.data(), opad, 64 ); 00337 content += md5.result(); 00338 00339 md5.reset(); 00340 md5.addData(content); 00341 00342 return md5.result(); 00343 } 00344 00345 /* 00346 * turns a 56 bit key into the 64 bit, odd parity key and sets the key. 00347 * The key schedule ks is also set. 00348 */ 00349 void convertKey( unsigned char *key_56, void* ks ) 00350 { 00351 unsigned char key[8]; 00352 00353 key[0] = key_56[0]; 00354 key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); 00355 key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); 00356 key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); 00357 key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); 00358 key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); 00359 key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); 00360 key[7] = (key_56[6] << 1) & 0xFF; 00361 00362 for ( uint i=0; i<8; i++ ) { 00363 unsigned char b = key[i]; 00364 bool needsParity = (((b>>7) ^ (b>>6) ^ (b>>5) ^ (b>>4) ^ (b>>3) ^ (b>>2) ^ (b>>1)) & 0x01) == 0; 00365 if ( needsParity ) 00366 key[i] |= 0x01; 00367 else 00368 key[i] &= 0xfe; 00369 } 00370 00371 ntlm_des_set_key ( (DES_KEY*) ks, (char*) &key, sizeof (key)); 00372 00373 memset (&key, 0, sizeof (key)); 00374 } 00375 00376 QByteArray QString2UnicodeLE( const QString &target ) 00377 { 00378 QByteArray unicode( target.length() * 2, 0 ); 00379 for ( int i = 0; i < target.length(); i++ ) { 00380 ((quint16*)unicode.data())[ i ] = qToLittleEndian( target[i].unicode() ); 00381 } 00382 return unicode; 00383 } 00384 00385 QString UnicodeLE2QString( const QChar* data, uint len ) 00386 { 00387 QString ret; 00388 for ( uint i = 0; i < len; i++ ) { 00389 ret += qFromLittleEndian( data[ i ].unicode() ); 00390 } 00391 return ret; 00392 }
KDE 4.6 API Reference