kio Library API Documentation

kntlm.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (c) 2004 Szombathelyi György <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., 59 Temple Place - Suite 330, 00021 Boston, MA 02111-1307, USA. 00022 */ 00023 00024 #include <string.h> 00025 00026 #include <qdatetime.h> 00027 #include <kapplication.h> 00028 #include <kswap.h> 00029 #include <kmdcodec.h> 00030 #include <kdebug.h> 00031 00032 #include "des.h" 00033 #include "kntlm.h" 00034 00035 QString KNTLM::getString( const QByteArray &buf, const SecBuf &secbuf, bool unicode ) 00036 { 00037 //watch for buffer overflows 00038 if ( secbuf.offset > buf.size() || 00039 secbuf.offset + secbuf.len > buf.size() ) return QString::null; 00040 00041 QString str; 00042 const char *c = buf.data() + secbuf.offset; 00043 00044 if ( unicode ) { 00045 str = UnicodeLE2QString( (QChar*) c, secbuf.len >> 1 ); 00046 } else { 00047 str = QString::fromLatin1( c, secbuf.len ); 00048 } 00049 return str; 00050 } 00051 00052 QByteArray KNTLM::getBuf( const QByteArray &buf, const SecBuf &secbuf ) 00053 { 00054 QByteArray ret; 00055 //watch for buffer overflows 00056 if ( secbuf.offset > buf.size() || 00057 secbuf.offset + secbuf.len > buf.size() ) return ret; 00058 ret.duplicate( buf.data() + secbuf.offset, buf.size() ); 00059 return ret; 00060 } 00061 00062 void KNTLM::addString( QByteArray &buf, SecBuf &secbuf, const QString &str, bool unicode ) 00063 { 00064 QByteArray tmp; 00065 00066 if ( unicode ) { 00067 tmp = QString2UnicodeLE( str ); 00068 addBuf( buf, secbuf, tmp ); 00069 } else { 00070 const char *c; 00071 c = str.latin1(); 00072 tmp.setRawData( c, str.length() ); 00073 addBuf( buf, secbuf, tmp ); 00074 tmp.resetRawData( c, str.length() ); 00075 } 00076 } 00077 00078 void KNTLM::addBuf( QByteArray &buf, SecBuf &secbuf, QByteArray &data ) 00079 { 00080 secbuf.offset = buf.size(); 00081 secbuf.len = data.size(); 00082 secbuf.maxlen = data.size(); 00083 buf.resize( buf.size() + data.size() ); 00084 memcpy( buf.data() + secbuf.offset, data.data(), data.size() ); 00085 } 00086 00087 bool KNTLM::getNegotiate( QByteArray &negotiate, const QString &domain, const QString &workstation, Q_UINT32 flags ) 00088 { 00089 QByteArray rbuf( sizeof(Negotiate) ); 00090 00091 rbuf.fill( 0 ); 00092 memcpy( rbuf.data(), "NTLMSSP", 8 ); 00093 ((Negotiate*) rbuf.data())->msgType = KFromToLittleEndian( (Q_UINT32)1 ); 00094 if ( !domain.isEmpty() ) { 00095 flags |= Negotiate_Domain_Supplied; 00096 addString( rbuf, ((Negotiate*) rbuf.data())->domain, domain ); 00097 } 00098 if ( !workstation.isEmpty() ) { 00099 flags |= Negotiate_WS_Supplied; 00100 addString( rbuf, ((Negotiate*) rbuf.data())->domain, workstation ); 00101 } 00102 ((Negotiate*) rbuf.data())->flags = KFromToLittleEndian( flags ); 00103 negotiate = rbuf; 00104 return true; 00105 } 00106 00107 bool KNTLM::getAuth( QByteArray &auth, const QByteArray &challenge, const QString &user, 00108 const QString &password, const QString &domain, const QString &workstation, 00109 bool forceNTLM, bool forceNTLMv2 ) 00110 { 00111 QByteArray rbuf( sizeof(Auth) ); 00112 Challenge *ch = (Challenge *) challenge.data(); 00113 QByteArray response; 00114 uint chsize = challenge.size(); 00115 bool unicode = false; 00116 QString dom; 00117 00118 //challenge structure too small 00119 if ( chsize < 32 ) return false; 00120 00121 unicode = ch->flags & Negotiate_Unicode; 00122 if ( domain.isEmpty() ) 00123 dom = getString( challenge, ch->targetName, unicode ); 00124 else 00125 dom = domain; 00126 00127 rbuf.fill( 0 ); 00128 memcpy( rbuf.data(), "NTLMSSP", 8 ); 00129 ((Auth*) rbuf.data())->msgType = KFromToLittleEndian( (Q_UINT32)3 ); 00130 ((Auth*) rbuf.data())->flags = ch->flags; 00131 QByteArray targetInfo = getBuf( challenge, ch->targetInfo ); 00132 00133 if ( forceNTLMv2 || !targetInfo.isEmpty() /* may support NTLMv2 */ ) { 00134 if ( ch->flags & Negotiate_NTLM ) { 00135 if ( targetInfo.isEmpty() ) return false; 00136 response = getNTLMv2Response( dom, user, password, targetInfo, ch->challengeData ); 00137 addBuf( rbuf, ((Auth*) rbuf.data())->ntResponse, response ); 00138 } else { 00139 if ( !forceNTLM ) { 00140 response = getLMv2Response( dom, user, password, ch->challengeData ); 00141 addBuf( rbuf, ((Auth*) rbuf.data())->lmResponse, response ); 00142 } else 00143 return false; 00144 } 00145 } else { //if no targetinfo structure and NTLMv2 or LMv2 not forced, try the older methods 00146 if ( ch->flags & Negotiate_NTLM ) { 00147 response = getNTLMResponse( password, ch->challengeData ); 00148 addBuf( rbuf, ((Auth*) rbuf.data())->ntResponse, response ); 00149 } else { 00150 if ( !forceNTLM ) { 00151 response = getLMResponse( password, ch->challengeData ); 00152 addBuf( rbuf, ((Auth*) rbuf.data())->lmResponse, response ); 00153 } else 00154 return false; 00155 } 00156 } 00157 if ( !dom.isEmpty() ) 00158 addString( rbuf, ((Auth*) rbuf.data())->domain, dom, unicode ); 00159 addString( rbuf, ((Auth*) rbuf.data())->user, user, unicode ); 00160 if ( !workstation.isEmpty() ) 00161 addString( rbuf, ((Auth*) rbuf.data())->workstation, workstation, unicode ); 00162 00163 auth = rbuf; 00164 00165 return true; 00166 } 00167 00168 QByteArray KNTLM::getLMResponse( const QString &password, const unsigned char *challenge ) 00169 { 00170 QByteArray hash, answer; 00171 00172 hash = lmHash( password ); 00173 hash.resize( 21 ); 00174 memset( hash.data() + 16, 0, 5 ); 00175 answer = lmResponse( hash, challenge ); 00176 hash.fill( 0 ); 00177 return answer; 00178 } 00179 00180 QByteArray KNTLM::lmHash( const QString &password ) 00181 { 00182 QByteArray keyBytes( 14 ); 00183 QByteArray hash( 16 ); 00184 DES_KEY ks; 00185 const char *magic = "KGS!@#$%"; 00186 00187 keyBytes.fill( 0 ); 00188 strncpy( keyBytes.data(), password.upper().latin1(), 14 ); 00189 00190 convertKey( (unsigned char*) keyBytes.data(), &ks ); 00191 ntlm_des_ecb_encrypt( magic, 8, &ks, (unsigned char*) hash.data() ); 00192 00193 convertKey( (unsigned char*) keyBytes.data() + 7, &ks ); 00194 ntlm_des_ecb_encrypt( magic, 8, &ks, (unsigned char*) hash.data() + 8 ); 00195 00196 keyBytes.fill( 0 ); 00197 memset( &ks, 0, sizeof (ks) ); 00198 00199 return hash; 00200 } 00201 00202 QByteArray KNTLM::lmResponse( const QByteArray &hash, const unsigned char *challenge ) 00203 { 00204 DES_KEY ks; 00205 QByteArray answer( 24 ); 00206 00207 convertKey( (unsigned char*) hash.data(), &ks ); 00208 ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() ); 00209 00210 convertKey( (unsigned char*) hash.data() + 7, &ks ); 00211 ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() + 8 ); 00212 00213 convertKey( (unsigned char*) hash.data() + 14, &ks ); 00214 ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() + 16 ); 00215 00216 memset( &ks, 0, sizeof (ks) ); 00217 return answer; 00218 } 00219 00220 QByteArray KNTLM::getNTLMResponse( const QString &password, const unsigned char *challenge ) 00221 { 00222 QByteArray hash, answer; 00223 00224 hash = ntlmHash( password ); 00225 hash.resize( 21 ); 00226 memset( hash.data() + 16, 0, 5 ); 00227 answer = lmResponse( hash, challenge ); 00228 hash.fill( 0 ); 00229 return answer; 00230 } 00231 00232 QByteArray KNTLM::ntlmHash( const QString &password ) 00233 { 00234 KMD4::Digest digest; 00235 QByteArray ret, unicode; 00236 unicode = QString2UnicodeLE( password ); 00237 00238 KMD4 md4( unicode ); 00239 md4.rawDigest( digest ); 00240 ret.duplicate( (const char*) digest, sizeof( digest ) ); 00241 return ret; 00242 } 00243 00244 QByteArray KNTLM::getNTLMv2Response( const QString &target, const QString &user, 00245 const QString &password, const QByteArray &targetInformation, 00246 const unsigned char *challenge ) 00247 { 00248 QByteArray hash = ntlmv2Hash( target, user, password ); 00249 QByteArray blob = createBlob( targetInformation ); 00250 return lmv2Response( hash, blob, challenge ); 00251 } 00252 00253 QByteArray KNTLM::getLMv2Response( const QString &target, const QString &user, 00254 const QString &password, const unsigned char *challenge ) 00255 { 00256 QByteArray hash = ntlmv2Hash( target, user, password ); 00257 QByteArray clientChallenge( 8 ); 00258 for ( uint i = 0; i<8; i++ ) { 00259 clientChallenge.data()[i] = KApplication::random() % 0xff; 00260 } 00261 return lmv2Response( hash, clientChallenge, challenge ); 00262 } 00263 00264 QByteArray KNTLM::ntlmv2Hash( const QString &target, const QString &user, const QString &password ) 00265 { 00266 QByteArray hash1 = ntlmHash( password ); 00267 QByteArray key, ret; 00268 QString id = user.upper() + target.upper(); 00269 key = QString2UnicodeLE( id ); 00270 ret = hmacMD5( key, hash1 ); 00271 return ret; 00272 } 00273 00274 QByteArray KNTLM::lmv2Response( const QByteArray &hash, 00275 const QByteArray &clientData, const unsigned char *challenge ) 00276 { 00277 QByteArray data( 8 + clientData.size() ); 00278 memcpy( data.data(), challenge, 8 ); 00279 memcpy( data.data() + 8, clientData.data(), clientData.size() ); 00280 QByteArray mac = hmacMD5( data, hash ); 00281 mac.resize( 16 + clientData.size() ); 00282 memcpy( mac.data() + 16, clientData.data(), clientData.size() ); 00283 return mac; 00284 } 00285 00286 QByteArray KNTLM::createBlob( const QByteArray &targetinfo ) 00287 { 00288 QByteArray blob( sizeof(Blob) + 4 + targetinfo.size() ); 00289 blob.fill( 0 ); 00290 00291 Blob *bl = (Blob *) blob.data(); 00292 bl->signature = KFromToBigEndian( (Q_UINT32) 0x01010000 ); 00293 Q_UINT64 now = QDateTime::currentDateTime().toTime_t(); 00294 now += (Q_UINT64)3600*(Q_UINT64)24*(Q_UINT64)134774; 00295 now *= (Q_UINT64)10000000; 00296 bl->timestamp = KFromToLittleEndian( now ); 00297 for ( uint i = 0; i<8; i++ ) { 00298 bl->challenge[i] = KApplication::random() % 0xff; 00299 } 00300 memcpy( blob.data() + sizeof(Blob), targetinfo.data(), targetinfo.size() ); 00301 return blob; 00302 } 00303 00304 QByteArray KNTLM::hmacMD5( const QByteArray &data, const QByteArray &key ) 00305 { 00306 Q_UINT8 ipad[64], opad[64]; 00307 KMD5::Digest digest; 00308 QByteArray ret; 00309 00310 memset( ipad, 0x36, sizeof(ipad) ); 00311 memset( opad, 0x5c, sizeof(opad) ); 00312 for ( int i = key.size()-1; i >= 0; i-- ) { 00313 ipad[i] ^= key[i]; 00314 opad[i] ^= key[i]; 00315 } 00316 00317 QByteArray content( data.size()+64 ); 00318 memcpy( content.data(), ipad, 64 ); 00319 memcpy( content.data() + 64, data.data(), data.size() ); 00320 KMD5 md5( content ); 00321 md5.rawDigest( digest ); 00322 content.resize( sizeof(digest) + 64 ); 00323 memcpy( content.data(), opad, 64 ); 00324 memcpy( content.data() + 64, digest, sizeof(digest) ); 00325 md5.reset(); 00326 md5.update( content ); 00327 md5.rawDigest( digest ); 00328 00329 ret.duplicate( (const char*) digest, sizeof( digest ) ); 00330 return ret; 00331 } 00332 00333 /* 00334 * turns a 56 bit key into the 64 bit, odd parity key and sets the key. 00335 * The key schedule ks is also set. 00336 */ 00337 void KNTLM::convertKey( unsigned char *key_56, void* ks ) 00338 { 00339 unsigned char key[8]; 00340 00341 key[0] = key_56[0]; 00342 key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); 00343 key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); 00344 key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); 00345 key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); 00346 key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); 00347 key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); 00348 key[7] = (key_56[6] << 1) & 0xFF; 00349 00350 for ( uint i=0; i<8; i++ ) { 00351 unsigned char b = key[i]; 00352 bool needsParity = (((b>>7) ^ (b>>6) ^ (b>>5) ^ (b>>4) ^ (b>>3) ^ (b>>2) ^ (b>>1)) & 0x01) == 0; 00353 if ( needsParity ) 00354 key[i] |= 0x01; 00355 else 00356 key[i] &= 0xfe; 00357 } 00358 00359 ntlm_des_set_key ( (DES_KEY*) ks, (char*) &key, sizeof (key)); 00360 00361 memset (&key, 0, sizeof (key)); 00362 } 00363 00364 QByteArray KNTLM::QString2UnicodeLE( const QString &target ) 00365 { 00366 QByteArray unicode( target.length() * 2 ); 00367 for ( uint i = 0; i < target.length(); i++ ) { 00368 ((Q_UINT16*)unicode.data())[ i ] = KFromToLittleEndian( target[i].unicode() ); 00369 } 00370 return unicode; 00371 } 00372 00373 QString KNTLM::UnicodeLE2QString( const QChar* data, uint len ) 00374 { 00375 QString ret; 00376 for ( uint i = 0; i < len; i++ ) { 00377 ret += KFromToLittleEndian( data[ i ].unicode() ); 00378 } 00379 return ret; 00380 }
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:20:26 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003