kio Library: kntlm.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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 }

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:01 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003