• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 #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 }

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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