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

kio

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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