diff options
Diffstat (limited to 'kio/misc/kntlm/kntlm.cpp')
-rw-r--r-- | kio/misc/kntlm/kntlm.cpp | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/kio/misc/kntlm/kntlm.cpp b/kio/misc/kntlm/kntlm.cpp new file mode 100644 index 000000000..20c9f2a0c --- /dev/null +++ b/kio/misc/kntlm/kntlm.cpp @@ -0,0 +1,389 @@ +/* This file is part of the KDE libraries + Copyright (c) 2004 Szombathelyi Gy�gy <gyurco@freemail.hu> + + The implementation is based on the documentation and sample code + at http://davenport.sourceforge.net/ntlm.html + The DES encryption functions are from libntlm + at http://josefsson.org/libntlm/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <string.h> + +#include <qdatetime.h> +#include <kapplication.h> +#include <kswap.h> +#include <kmdcodec.h> +#include <kdebug.h> + +#include "des.h" +#include "kntlm.h" + +QString KNTLM::getString( const QByteArray &buf, const SecBuf &secbuf, bool unicode ) +{ + //watch for buffer overflows + Q_UINT32 offset; + Q_UINT16 len; + offset = KFromToLittleEndian((Q_UINT32)secbuf.offset); + len = KFromToLittleEndian(secbuf.len); + if ( offset > buf.size() || + offset + len > buf.size() ) return QString::null; + + QString str; + const char *c = buf.data() + offset; + + if ( unicode ) { + str = UnicodeLE2QString( (QChar*) c, len >> 1 ); + } else { + str = QString::fromLatin1( c, len ); + } + return str; +} + +QByteArray KNTLM::getBuf( const QByteArray &buf, const SecBuf &secbuf ) +{ + QByteArray ret; + Q_UINT32 offset; + Q_UINT16 len; + offset = KFromToLittleEndian((Q_UINT32)secbuf.offset); + len = KFromToLittleEndian(secbuf.len); + //watch for buffer overflows + if ( offset > buf.size() || + offset + len > buf.size() ) return ret; + ret.duplicate( buf.data() + offset, buf.size() ); + return ret; +} + +void KNTLM::addString( QByteArray &buf, SecBuf &secbuf, const QString &str, bool unicode ) +{ + QByteArray tmp; + + if ( unicode ) { + tmp = QString2UnicodeLE( str ); + addBuf( buf, secbuf, tmp ); + } else { + const char *c; + c = str.latin1(); + tmp.setRawData( c, str.length() ); + addBuf( buf, secbuf, tmp ); + tmp.resetRawData( c, str.length() ); + } +} + +void KNTLM::addBuf( QByteArray &buf, SecBuf &secbuf, QByteArray &data ) +{ + Q_UINT32 offset; + Q_UINT16 len, maxlen; + offset = (buf.size() + 1) & 0xfffffffe; + len = data.size(); + maxlen = data.size(); + + secbuf.offset = KFromToLittleEndian((Q_UINT32)offset); + secbuf.len = KFromToLittleEndian(len); + secbuf.maxlen = KFromToLittleEndian(maxlen); + buf.resize( offset + len ); + memcpy( buf.data() + offset, data.data(), data.size() ); +} + +bool KNTLM::getNegotiate( QByteArray &negotiate, const QString &domain, const QString &workstation, Q_UINT32 flags ) +{ + QByteArray rbuf( sizeof(Negotiate) ); + + rbuf.fill( 0 ); + memcpy( rbuf.data(), "NTLMSSP", 8 ); + ((Negotiate*) rbuf.data())->msgType = KFromToLittleEndian( (Q_UINT32)1 ); + if ( !domain.isEmpty() ) { + flags |= Negotiate_Domain_Supplied; + addString( rbuf, ((Negotiate*) rbuf.data())->domain, domain ); + } + if ( !workstation.isEmpty() ) { + flags |= Negotiate_WS_Supplied; + addString( rbuf, ((Negotiate*) rbuf.data())->domain, workstation ); + } + ((Negotiate*) rbuf.data())->flags = KFromToLittleEndian( flags ); + negotiate = rbuf; + return true; +} + +bool KNTLM::getAuth( QByteArray &auth, const QByteArray &challenge, const QString &user, + const QString &password, const QString &domain, const QString &workstation, + bool forceNTLM, bool forceNTLMv2 ) +{ + QByteArray rbuf( sizeof(Auth) ); + Challenge *ch = (Challenge *) challenge.data(); + QByteArray response; + uint chsize = challenge.size(); + bool unicode = false; + QString dom; + + //challenge structure too small + if ( chsize < 32 ) return false; + + unicode = KFromToLittleEndian(ch->flags) & Negotiate_Unicode; + if ( domain.isEmpty() ) + dom = getString( challenge, ch->targetName, unicode ); + else + dom = domain; + + rbuf.fill( 0 ); + memcpy( rbuf.data(), "NTLMSSP", 8 ); + ((Auth*) rbuf.data())->msgType = KFromToLittleEndian( (Q_UINT32)3 ); + ((Auth*) rbuf.data())->flags = ch->flags; + QByteArray targetInfo = getBuf( challenge, ch->targetInfo ); + +// if ( forceNTLMv2 || (!targetInfo.isEmpty() && (KFromToLittleEndian(ch->flags) & Negotiate_Target_Info)) /* may support NTLMv2 */ ) { +// if ( KFromToLittleEndian(ch->flags) & Negotiate_NTLM ) { +// if ( targetInfo.isEmpty() ) return false; +// response = getNTLMv2Response( dom, user, password, targetInfo, ch->challengeData ); +// addBuf( rbuf, ((Auth*) rbuf.data())->ntResponse, response ); +// } else { +// if ( !forceNTLM ) { +// response = getLMv2Response( dom, user, password, ch->challengeData ); +// addBuf( rbuf, ((Auth*) rbuf.data())->lmResponse, response ); +// } else +// return false; +// } +// } else { //if no targetinfo structure and NTLMv2 or LMv2 not forced, try the older methods + + response = getNTLMResponse( password, ch->challengeData ); + addBuf( rbuf, ((Auth*) rbuf.data())->ntResponse, response ); + response = getLMResponse( password, ch->challengeData ); + addBuf( rbuf, ((Auth*) rbuf.data())->lmResponse, response ); +// } + if ( !dom.isEmpty() ) + addString( rbuf, ((Auth*) rbuf.data())->domain, dom, unicode ); + addString( rbuf, ((Auth*) rbuf.data())->user, user, unicode ); + if ( !workstation.isEmpty() ) + addString( rbuf, ((Auth*) rbuf.data())->workstation, workstation, unicode ); + + auth = rbuf; + + return true; +} + +QByteArray KNTLM::getLMResponse( const QString &password, const unsigned char *challenge ) +{ + QByteArray hash, answer; + + hash = lmHash( password ); + hash.resize( 21 ); + memset( hash.data() + 16, 0, 5 ); + answer = lmResponse( hash, challenge ); + hash.fill( 0 ); + return answer; +} + +QByteArray KNTLM::lmHash( const QString &password ) +{ + QByteArray keyBytes( 14 ); + QByteArray hash( 16 ); + DES_KEY ks; + const char *magic = "KGS!@#$%"; + + keyBytes.fill( 0 ); + strncpy( keyBytes.data(), password.upper().latin1(), 14 ); + + convertKey( (unsigned char*) keyBytes.data(), &ks ); + ntlm_des_ecb_encrypt( magic, 8, &ks, (unsigned char*) hash.data() ); + + convertKey( (unsigned char*) keyBytes.data() + 7, &ks ); + ntlm_des_ecb_encrypt( magic, 8, &ks, (unsigned char*) hash.data() + 8 ); + + keyBytes.fill( 0 ); + memset( &ks, 0, sizeof (ks) ); + + return hash; +} + +QByteArray KNTLM::lmResponse( const QByteArray &hash, const unsigned char *challenge ) +{ + DES_KEY ks; + QByteArray answer( 24 ); + + convertKey( (unsigned char*) hash.data(), &ks ); + ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() ); + + convertKey( (unsigned char*) hash.data() + 7, &ks ); + ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() + 8 ); + + convertKey( (unsigned char*) hash.data() + 14, &ks ); + ntlm_des_ecb_encrypt( challenge, 8, &ks, (unsigned char*) answer.data() + 16 ); + + memset( &ks, 0, sizeof (ks) ); + return answer; +} + +QByteArray KNTLM::getNTLMResponse( const QString &password, const unsigned char *challenge ) +{ + QByteArray hash, answer; + + hash = ntlmHash( password ); + hash.resize( 21 ); + memset( hash.data() + 16, 0, 5 ); + answer = lmResponse( hash, challenge ); + hash.fill( 0 ); + return answer; +} + +QByteArray KNTLM::ntlmHash( const QString &password ) +{ + KMD4::Digest digest; + QByteArray ret, unicode; + unicode = QString2UnicodeLE( password ); + + KMD4 md4( unicode ); + md4.rawDigest( digest ); + ret.duplicate( (const char*) digest, sizeof( digest ) ); + return ret; +} + +QByteArray KNTLM::getNTLMv2Response( const QString &target, const QString &user, + const QString &password, const QByteArray &targetInformation, + const unsigned char *challenge ) +{ + QByteArray hash = ntlmv2Hash( target, user, password ); + QByteArray blob = createBlob( targetInformation ); + return lmv2Response( hash, blob, challenge ); +} + +QByteArray KNTLM::getLMv2Response( const QString &target, const QString &user, + const QString &password, const unsigned char *challenge ) +{ + QByteArray hash = ntlmv2Hash( target, user, password ); + QByteArray clientChallenge( 8 ); + for ( uint i = 0; i<8; i++ ) { + clientChallenge.data()[i] = KApplication::random() % 0xff; + } + return lmv2Response( hash, clientChallenge, challenge ); +} + +QByteArray KNTLM::ntlmv2Hash( const QString &target, const QString &user, const QString &password ) +{ + QByteArray hash1 = ntlmHash( password ); + QByteArray key, ret; + QString id = user.upper() + target.upper(); + key = QString2UnicodeLE( id ); + ret = hmacMD5( key, hash1 ); + return ret; +} + +QByteArray KNTLM::lmv2Response( const QByteArray &hash, + const QByteArray &clientData, const unsigned char *challenge ) +{ + QByteArray data( 8 + clientData.size() ); + memcpy( data.data(), challenge, 8 ); + memcpy( data.data() + 8, clientData.data(), clientData.size() ); + QByteArray mac = hmacMD5( data, hash ); + mac.resize( 16 + clientData.size() ); + memcpy( mac.data() + 16, clientData.data(), clientData.size() ); + return mac; +} + +QByteArray KNTLM::createBlob( const QByteArray &targetinfo ) +{ + QByteArray blob( sizeof(Blob) + 4 + targetinfo.size() ); + blob.fill( 0 ); + + Blob *bl = (Blob *) blob.data(); + bl->signature = KFromToBigEndian( (Q_UINT32) 0x01010000 ); + Q_UINT64 now = QDateTime::currentDateTime().toTime_t(); + now += (Q_UINT64)3600*(Q_UINT64)24*(Q_UINT64)134774; + now *= (Q_UINT64)10000000; + bl->timestamp = KFromToLittleEndian( now ); + for ( uint i = 0; i<8; i++ ) { + bl->challenge[i] = KApplication::random() % 0xff; + } + memcpy( blob.data() + sizeof(Blob), targetinfo.data(), targetinfo.size() ); + return blob; +} + +QByteArray KNTLM::hmacMD5( const QByteArray &data, const QByteArray &key ) +{ + Q_UINT8 ipad[64], opad[64]; + KMD5::Digest digest; + QByteArray ret; + + memset( ipad, 0x36, sizeof(ipad) ); + memset( opad, 0x5c, sizeof(opad) ); + for ( int i = key.size()-1; i >= 0; i-- ) { + ipad[i] ^= key[i]; + opad[i] ^= key[i]; + } + + QByteArray content( data.size()+64 ); + memcpy( content.data(), ipad, 64 ); + memcpy( content.data() + 64, data.data(), data.size() ); + KMD5 md5( content ); + md5.rawDigest( digest ); + content.resize( sizeof(digest) + 64 ); + memcpy( content.data(), opad, 64 ); + memcpy( content.data() + 64, digest, sizeof(digest) ); + md5.reset(); + md5.update( content ); + md5.rawDigest( digest ); + + ret.duplicate( (const char*) digest, sizeof( digest ) ); + return ret; +} + +/* +* turns a 56 bit key into the 64 bit, odd parity key and sets the key. +* The key schedule ks is also set. +*/ +void KNTLM::convertKey( unsigned char *key_56, void* ks ) +{ + unsigned char key[8]; + + key[0] = key_56[0]; + key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); + key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); + key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); + key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); + key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); + key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); + key[7] = (key_56[6] << 1) & 0xFF; + + for ( uint i=0; i<8; i++ ) { + unsigned char b = key[i]; + bool needsParity = (((b>>7) ^ (b>>6) ^ (b>>5) ^ (b>>4) ^ (b>>3) ^ (b>>2) ^ (b>>1)) & 0x01) == 0; + if ( needsParity ) + key[i] |= 0x01; + else + key[i] &= 0xfe; + } + + ntlm_des_set_key ( (DES_KEY*) ks, (char*) &key, sizeof (key)); + + memset (&key, 0, sizeof (key)); +} + +QByteArray KNTLM::QString2UnicodeLE( const QString &target ) +{ + QByteArray unicode( target.length() * 2 ); + for ( uint i = 0; i < target.length(); i++ ) { + ((Q_UINT16*)unicode.data())[ i ] = KFromToLittleEndian( target[i].unicode() ); + } + return unicode; +} + +QString KNTLM::UnicodeLE2QString( const QChar* data, uint len ) +{ + QString ret; + for ( uint i = 0; i < len; i++ ) { + ret += KFromToLittleEndian( data[ i ].unicode() ); + } + return ret; +} |