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

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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