summaryrefslogtreecommitdiffstats
path: root/kio/misc/kntlm/kntlm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kio/misc/kntlm/kntlm.cpp')
-rw-r--r--kio/misc/kntlm/kntlm.cpp389
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;
+}