diff options
| author | 2010-02-15 19:57:33 +0000 | |
|---|---|---|
| committer | 2010-02-15 19:57:33 +0000 | |
| commit | c592bab456c2acf4aea91d1a3716d78d4775f312 (patch) | |
| tree | 1a7c191046f4993bfe181c19143d15a043ac90fe /src | |
| parent | Aliaseditor code cleaning (diff) | |
| download | KVIrc-c592bab456c2acf4aea91d1a3716d78d4775f312.tar.gz KVIrc-c592bab456c2acf4aea91d1a3716d78d4775f312.tar.bz2 KVIrc-c592bab456c2acf4aea91d1a3716d78d4775f312.zip | |
reworked sasl stuff, added dh-blowfish auth method
git-svn-id: https://svn.kvirc.de/svn/trunk/kvirc@3970 17fca916-40b9-46aa-a4ea-0a15b648b75c
Diffstat (limited to 'src')
| -rw-r--r-- | src/kvilib/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/kvilib/net/kvi_sasl.cpp | 198 | ||||
| -rw-r--r-- | src/kvilib/net/kvi_sasl.h | 61 | ||||
| -rw-r--r-- | src/kvirc/kernel/kvi_ircconnection.cpp | 64 | ||||
| -rw-r--r-- | src/kvirc/kernel/kvi_ircconnection.h | 7 | ||||
| -rw-r--r-- | src/kvirc/sparser/kvi_numeric.h | 4 | ||||
| -rw-r--r-- | src/kvirc/sparser/kvi_sp_literal.cpp | 8 |
7 files changed, 325 insertions, 18 deletions
diff --git a/src/kvilib/CMakeLists.txt b/src/kvilib/CMakeLists.txt index 6e89f40d5..20c5baac9 100644 --- a/src/kvilib/CMakeLists.txt +++ b/src/kvilib/CMakeLists.txt @@ -105,6 +105,7 @@ SET(kvilib_SRCS net/kvi_dns.cpp net/kvi_http.cpp net/kvi_netutils.cpp + net/kvi_sasl.cpp net/kvi_socket.cpp net/kvi_ssl.cpp net/kvi_url.cpp diff --git a/src/kvilib/net/kvi_sasl.cpp b/src/kvilib/net/kvi_sasl.cpp new file mode 100644 index 000000000..66e719e22 --- /dev/null +++ b/src/kvilib/net/kvi_sasl.cpp @@ -0,0 +1,198 @@ +//============================================================================= +// +// File : kvi_sasl.cpp +// Creation date : Mon Feb 14 2010 19:36:12 CEST by Fabio Bas +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2010 Fabio Bas (ctrlaltca at gmail dot com) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + + + +#include "kvi_sasl.h" + +#include "kvi_malloc.h" + +#ifdef COMPILE_SSL_SUPPORT + +#include <openssl/blowfish.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/err.h> +#include <arpa/inet.h> + +#endif + + +namespace KviSASL +{ + + bool plainMethod(KviStr & szIn, KviStr & szOut, QByteArray & baNick, QByteArray & baPass) + { + if(szIn=="+") + { + int answerLen = 3 + (2 * baNick.size()) + baPass.size(); + char * answer = (char *) kvi_malloc(answerLen); + char * answer2 = answer; + + memcpy(answer, baNick.data(), baNick.size()); + answer+=baNick.size(); + memset(answer, 0, 1); + answer++; + + memcpy(answer, baNick.data(), baNick.size()); + answer+=baNick.size(); + memset(answer, 0, 1); + answer++; + + memcpy(answer, baPass.data(), baPass.size()); + answer+=baPass.size(); + memset(answer, 0, 1); + answer++; + + szOut.bufferToBase64(answer2,answerLen); + kvi_free(answer2); + + return true; + } + return false; + } + +#ifdef COMPILE_SSL_SUPPORT + bool dh_blowfishMethod(KviStr & szIn, KviStr & szOut, QByteArray & baNick, QByteArray & baPass) + { + /* + * The format of the auth token is quite complex; the server sends us 3 strings: + * p - a prime number + * g - a generator number, tipically 2 or 5 are used. + * y - the server-generated public key + * These 3 strings are null-terminated and codified as pascal strings (they are prefixed + * with a 16-bit lenght identifiedr in "network" big-endian order) + * Then, the 3 strings are concatenated and base-64 encoded. + */ + + BF_KEY key; + quint16 size, pKlen; + int secretLen; + unsigned char *secret = NULL, *pubKey = NULL; + char * tmpBuf; + DH * dh = DH_new(); + int len = szIn.base64ToBuffer(&tmpBuf,false); + + if(len < 7) return false; + + // extract p + size = ntohs(*(unsigned int*)tmpBuf); + tmpBuf+=2; + len-=2; + if(size > len) return false; + + if(!(dh->p = BN_bin2bn((unsigned char*) tmpBuf, size, NULL))) + return false; + + tmpBuf+=size; + len-=size; + + // extract g + size = ntohs(*(unsigned int*)tmpBuf); + tmpBuf+=2; + len-=2; + if(size > len) return false; + + if(!(dh->g = BN_bin2bn((unsigned char*) tmpBuf, size, NULL))) + return false; + + tmpBuf+=size; + len-=size; + + // extract y + size = ntohs(*(unsigned int*)tmpBuf); + tmpBuf+=2; + len-=2; + if(size > len) return false; + + // create our keys and extract shared secret + // note: any memory checking tool (as valgrind) will complain on this call. blame openssl + if(!DH_generate_key(dh)) + return false; + + secret=(unsigned char *) kvi_malloc(DH_size(dh)); + // note: any memory checking tool (as valgrind) will complain on this call. blame openssl + if(-1 == (secretLen = DH_compute_key(secret, BN_bin2bn((unsigned char*) tmpBuf, size, NULL), dh))) + return false; + + pKlen=BN_num_bytes(dh->pub_key); + pubKey = (unsigned char *) kvi_malloc(pKlen); + BN_bn2bin(dh->pub_key, pubKey); + + //create crypto buffers + int passLen = baPass.size() + ((8 -( baPass.size() % 8)) % 8); + int passC = 0; + unsigned char *passIn = (unsigned char *) kvi_malloc(passLen); + unsigned char *passOut = (unsigned char *) kvi_malloc(passLen); + + memset(passIn, 0, passLen); + memset(passOut, 0, passLen); + memcpy(passIn, baPass.data(), baPass.size()); + + // crypt our password + BF_set_key(&key, secretLen, secret); + + for (passC=0; passC < passLen; passC += 8) + BF_ecb_encrypt(passIn + passC, passOut + passC, &key, BF_ENCRYPT); + + /* + * Build up the answer + * The format of the auth answer is quite complex, and formed by the byte concatenation of: + * 1) a 16 bit integer containing the byte length of our public key + * 2) our public key + * 3) our username (nickname), null-terminated + * 4) our password, crypted using blowfish in ecb mode and the dh secret as the blowfish key + * Then, the answer is to be base64 encoded. + */ + + int answerLen = 2 + pKlen + baNick.size() + 1 + passLen; + char * answer = (char *) malloc(answerLen); + char * answer2 = answer; + *((unsigned int *)answer) = htons(pKlen); + answer+=2; + memcpy(answer, pubKey, pKlen); + answer+=pKlen; + memcpy(answer, baNick.data(), baNick.size()); + answer+=baNick.size(); + memset(answer, 0, 1); + answer++; + memcpy(answer, passOut, passLen); + szOut.bufferToBase64(answer2,answerLen); + + //clean up + kvi_free(secret); + kvi_free(pubKey); + kvi_free(passIn); + kvi_free(passOut); + + return true; + } +#else + bool dh_blowfishMethod(KviStr & szIn, KviStr & szOut, QByteArray & baNick, QByteArray & baPass) + { + return false; + } +#endif + +}; diff --git a/src/kvilib/net/kvi_sasl.h b/src/kvilib/net/kvi_sasl.h new file mode 100644 index 000000000..9dd660e88 --- /dev/null +++ b/src/kvilib/net/kvi_sasl.h @@ -0,0 +1,61 @@ +#ifndef _KVI_SASL_H_ +#define _KVI_SASL_H_ +//============================================================================= +// +// File : kvi_sasl.h +// Creation date : Mon Feb 14 2010 19:36:12 CEST by Fabio Bas +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2010 Fabio Bas (ctrlaltca at gmail dot com) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_string.h" + +/** +* \namespace KviSASL +* \brief This namespace implement some SASL authentication methods. +* +* Currently implementhed methods are PLAIN and DH-BLOWFISH +*/ + +namespace KviSASL +{ + /** + * \brief Create the auth message for PLAIN authentication + * \param szIn The server-provided token + * \param szOut A KviStr that will be filled with the authentication message + * \param baNick The username + * \param baPass The password + * \return bool + */ + extern KVILIB_API bool plainMethod(KviStr & szIn, KviStr & szOut, QByteArray & baNick, QByteArray & baPass); + + /** + * \brief Create the auth message for DH-BLOWFISH authentication + * \param szIn The server-provided token + * \param szOut A KviStr that will be filled with the authentication message + * \param baNick The username + * \param baPass The password + * \return bool + */ + extern KVILIB_API bool dh_blowfishMethod(KviStr & szIn, KviStr & szOut, QByteArray & baNick, QByteArray & baPass); + +}; + +#endif //_KVI_SASL_H_ diff --git a/src/kvirc/kernel/kvi_ircconnection.cpp b/src/kvirc/kernel/kvi_ircconnection.cpp index a614e2eeb..260dd5a25 100644 --- a/src/kvirc/kernel/kvi_ircconnection.cpp +++ b/src/kvirc/kernel/kvi_ircconnection.cpp @@ -66,6 +66,7 @@ #include "kvi_mirccntrl.h" #include "kvi_useridentity.h" #include "kvi_identityprofile.h" +#include "kvi_sasl.h" #include <QTimer> #include <QTextCodec> @@ -348,7 +349,6 @@ void KviIrcConnection::linkEstabilished() void KviIrcConnection::handleCapLs() { - qDebug("handleCapLs"); m_pStateData->setInsideCapLs(false); // STARTTLS support: this has to be checked first because it could imply @@ -385,7 +385,7 @@ void KviIrcConnection::handleCapLs() void KviIrcConnection::handleCapAck() { - qDebug("handleCapAck"); + bool bUsed=false; //SASL if(KVI_OPTION_BOOL(KviOption_boolUseSaslIfAvailable) && @@ -393,14 +393,57 @@ void KviIrcConnection::handleCapAck() serverInfo()->enabledCaps().contains("sasl",Qt::CaseInsensitive) ) { + bUsed=true; + +#ifdef COMPILE_SSL_SUPPORT + sendFmtData("AUTHENTICATE DH-BLOWFISH"); +#else sendFmtData("AUTHENTICATE PLAIN"); - //server will send us a "AUTHENTICATE +" answer; we won't wait for this extra step - QByteArray szAuth = encodeText(target()->server()->saslNick()); - szAuth.append('\0'); - szAuth.append(encodeText(target()->server()->saslNick())); - szAuth.append('\0'); - szAuth.append(encodeText(target()->server()->saslPass())); - sendFmtData("AUTHENTICATE %s",szAuth.toBase64().data()); +#endif + } + + + if(!bUsed) endCapLs(); +} + +void KviIrcConnection::handleAuthenticate(KviStr & szAuth) +{ + //SASL + if(KVI_OPTION_BOOL(KviOption_boolUseSaslIfAvailable) && + target()->server()->enabledSASL() && + serverInfo()->enabledCaps().contains("sasl",Qt::CaseInsensitive) + ) + { + QByteArray szNick = encodeText(target()->server()->saslNick()); + QByteArray szPass = encodeText(target()->server()->saslPass()); + if(szAuth=="+") + { + //PLAIN + KviStr szOut; + if(KviSASL::plainMethod(szAuth, + szOut, + szNick, + szPass + )) + { + sendFmtData("AUTHENTICATE %s",szOut.ptr()); + } else { + sendFmtData("AUTHENTICATE *"); + } + } else { + //DH-BLOWFISH sasl auth + KviStr szOut; + if(KviSASL::dh_blowfishMethod(szAuth, + szOut, + szNick, + szPass + )) + { + sendFmtData("AUTHENTICATE %s",szOut.ptr()); + } else { + sendFmtData("AUTHENTICATE *"); + } + } } endCapLs(); @@ -408,19 +451,16 @@ void KviIrcConnection::handleCapAck() void KviIrcConnection::handleCapNak() { - qDebug("handleCapNak"); endCapLs(); } void KviIrcConnection::endCapLs() { - sendFmtData("CAP END"); loginToIrcServer(); } void KviIrcConnection::handleFailedCapLs() { - qDebug("handleFailedCapLs"); m_pStateData->setInsideCapLs(false); loginToIrcServer(); } diff --git a/src/kvirc/kernel/kvi_ircconnection.h b/src/kvirc/kernel/kvi_ircconnection.h index 0928244e3..ee8b4efc0 100644 --- a/src/kvirc/kernel/kvi_ircconnection.h +++ b/src/kvirc/kernel/kvi_ircconnection.h @@ -68,6 +68,7 @@ class KviNotifyListManager; class KviDns; class KviUserIdentity; class KviIdentityProfileSet; +class KviStr; /** @@ -723,6 +724,12 @@ protected: void serverInfoReceived(const QString & szServerName, const QString & szUserModes, const QString & szChanModes); /** + * \brief Called when AUTHENTICATE answer is received + * \return void + */ + void handleAuthenticate(KviStr & szResponse); + + /** * \brief Called when CAP LS answer is received * \return void */ diff --git a/src/kvirc/sparser/kvi_numeric.h b/src/kvirc/sparser/kvi_numeric.h index 4692d87be..a09ecd5e2 100644 --- a/src/kvirc/sparser/kvi_numeric.h +++ b/src/kvirc/sparser/kvi_numeric.h @@ -376,9 +376,9 @@ #define RPL_SASLLOGIN 900 /* :jaguar.test 900 jilles jilles!jilles@localhost.stack.nl jilles :You are now logged in as jilles. */ #define RPL_SASLSUCCESS 903 /* :jaguar.test 903 jilles :SASL authentication successful */ #define RPL_SASLFAILED 904 /* :lindbohm.freenode.net 904 * :SASL authentication failed */ -#define RPL_SASLERROR 905 // alternative error message +#define RPL_SASLERROR 905 // sasl message too long #define RPL_SASLABORT 906 // sasl authentication aborted -#define RPL_SASLALREADYAUTH 907 // can't authenticate: already authenticated +#define RPL_SASLALREADYAUTH 907 // You have already completed SASL authentication ///* 303 */ RPL_ISON, ":", ///* 304 */ RPL_TEXT, (char *)NULL, diff --git a/src/kvirc/sparser/kvi_sp_literal.cpp b/src/kvirc/sparser/kvi_sp_literal.cpp index 24ff39e8e..ed6b0e282 100644 --- a/src/kvirc/sparser/kvi_sp_literal.cpp +++ b/src/kvirc/sparser/kvi_sp_literal.cpp @@ -1994,7 +1994,6 @@ void KviServerParser::parseLiteralCap(KviIrcMessage *msg) // Client2server subcommands: // LIST, LS, REQ, CLEAR, END - debug("Parsing literal CAP..."); QString szPrefix = msg->connection()->decodeText(msg->safePrefix()); QString szCmd = msg->connection()->decodeText(msg->safeParam(1)); QString szProtocols = msg->connection()->decodeText(msg->safeParam(2)); @@ -2038,8 +2037,9 @@ void KviServerParser::parseLiteralCap(KviIrcMessage *msg) } } -void KviServerParser::parseLiteralAuthenticate(KviIrcMessage *) +void KviServerParser::parseLiteralAuthenticate(KviIrcMessage *msg) { - // :prefix AUTHENTICATE + - // we don't wait for this when authenticating, so no real handling is needed here + // :AUTHENTICATE + + KviStr szAuth(msg->safeParam(0)); + msg->connection()->handleAuthenticate(szAuth); }
\ No newline at end of file |
