From: Manuel Nickschas Date: Tue, 19 Feb 2013 22:27:25 +0000 (-0800) Subject: Merge pull request #5 from Tucos/feat-keyx X-Git-Tag: 0.9-beta1~18 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=04315f46a16fc3627218377071e008b6b9744992;hp=-c Merge pull request #5 from Tucos/feat-keyx Add support for key exchange --- 04315f46a16fc3627218377071e008b6b9744992 diff --combined src/common/CMakeLists.txt index fe1d4f97,7bdf1c55..c247eb34 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@@ -17,7 -17,7 +17,7 @@@ set(SOURCE eventmanager.cpp identity.cpp ignorelistmanager.cpp - internalconnection.cpp + internalpeer.cpp ircchannel.cpp ircevent.cpp irclisthelper.cpp @@@ -29,13 -29,13 +29,13 @@@ networkconfig.cpp networkevent.cpp quassel.cpp - remoteconnection.cpp + remotepeer.cpp settings.cpp signalproxy.cpp syncableobject.cpp util.cpp - protocols/legacy/legacyconnection.cpp + protocols/legacy/legacypeer.cpp ) set(MOC_HDRS @@@ -49,18 -49,18 +49,18 @@@ eventmanager.h identity.h ignorelistmanager.h - internalconnection.h + internalpeer.h ircchannel.h irclisthelper.h ircuser.h network.h networkconfig.h - remoteconnection.h + remotepeer.h settings.h signalproxy.h syncableobject.h - protocols/legacy/legacyconnection.h + protocols/legacy/legacypeer.h ) set(HEADERS ${MOC_HDRS} @@@ -74,10 -74,14 +74,15 @@@ networkevent.h logger.h message.h + protocol.h types.h util.h) + if (HAVE_QCA2) + set(SOURCES ${SOURCES} keyevent.cpp) + set(HEADERS ${HEADERS} keyevent.h) + endif(HAVE_QCA2) + if(APPLE) set(SOURCES ${SOURCES} mac_utils.cpp) set(HEADERS ${HEADERS} mac_utils.h) diff --combined src/common/eventmanager.h index 3dbd7262,12411495..edebe617 --- a/src/common/eventmanager.h +++ b/src/common/eventmanager.h @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -113,6 -113,10 +113,10 @@@ public CtcpEvent = 0x00050000, CtcpEventFlush + + #ifdef HAVE_QCA2 + ,KeyEvent = 0x00060000 + #endif }; EventManager(QObject *parent = 0); diff --combined src/core/coreircchannel.cpp index a7e1b6c6,98995bff..99d18815 --- a/src/core/coreircchannel.cpp +++ b/src/core/coreircchannel.cpp @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -59,13 -59,6 +59,6 @@@ void CoreIrcChannel::setEncrypted(bool if (topic().isEmpty()) return; - QByteArray key = qobject_cast(network())->cipherKey(name()); - if (key.isEmpty()) - return; - - if (!cipher()->setKey(key)) - return; - QByteArray decrypted = cipher()->decryptTopic(topic().toAscii()); setTopic(decodeString(decrypted)); } diff --combined src/core/corenetwork.cpp index b734312b,512ac104..101f5276 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -310,7 -310,7 +310,7 @@@ void CoreNetwork::removeChannelKey(cons #ifdef HAVE_QCA2 - Cipher *CoreNetwork::cipher(const QString &target) const + Cipher *CoreNetwork::cipher(const QString &target) { if (target.isEmpty()) return 0; @@@ -318,39 -318,51 +318,51 @@@ if (!Cipher::neededFeaturesAvailable()) return 0; - QByteArray key = cipherKey(target); - if (key.isEmpty()) - return 0; - CoreIrcChannel *channel = qobject_cast(ircChannel(target)); if (channel) { - if (channel->cipher()->setKey(key)) - return channel->cipher(); + return channel->cipher(); } - else { - CoreIrcUser *user = qobject_cast(ircUser(target)); - if (user && user->cipher()->setKey(key)) - return user->cipher(); + CoreIrcUser *user = qobject_cast(ircUser(target)); + if (user) { + return user->cipher(); + } else if (!isChannelName(target)) { + return qobject_cast(newIrcUser(target))->cipher(); } return 0; } - QByteArray CoreNetwork::cipherKey(const QString &recipient) const + QByteArray CoreNetwork::cipherKey(const QString &target) const { - return _cipherKeys.value(recipient.toLower(), QByteArray()); + CoreIrcChannel *c = qobject_cast(ircChannel(target)); + if (c) + return c->cipher()->key(); + + CoreIrcUser *u = qobject_cast(ircUser(target)); + if (u) + return u->cipher()->key(); + + return QByteArray(); } - void CoreNetwork::setCipherKey(const QString &recipient, const QByteArray &key) + void CoreNetwork::setCipherKey(const QString &target, const QByteArray &key) { - if (!key.isEmpty()) - _cipherKeys[recipient.toLower()] = key; - else - _cipherKeys.remove(recipient.toLower()); - } + CoreIrcChannel *c = qobject_cast(ircChannel(target)); + if (c) { + c->setEncrypted(c->cipher()->setKey(key)); + return; + } + CoreIrcUser *u = qobject_cast(ircUser(target)); + if (!u && !isChannelName(target)) + u = qobject_cast(newIrcUser(target)); + if (u) { + u->setEncrypted(u->cipher()->setKey(key)); + return; + } + } #endif /* HAVE_QCA2 */ bool CoreNetwork::setAutoWhoDone(const QString &channel) diff --combined src/core/corenetwork.h index 47575782,7b6048a3..3b31f0de --- a/src/core/corenetwork.h +++ b/src/core/corenetwork.h @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -120,7 -120,7 +120,7 @@@ public slots // Blowfish stuff #ifdef HAVE_QCA2 - Cipher *cipher(const QString &recipient) const; + Cipher *cipher(const QString &recipient); QByteArray cipherKey(const QString &recipient) const; void setCipherKey(const QString &recipient, const QByteArray &key); #endif diff --combined src/core/coresessioneventprocessor.cpp index e2edd083,1e9bf050..692257a5 --- a/src/core/coresessioneventprocessor.cpp +++ b/src/core/coresessioneventprocessor.cpp @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -30,6 -30,10 +30,10 @@@ #include "netsplit.h" #include "quassel.h" + #ifdef HAVE_QCA2 + # include "keyevent.h" + #endif + CoreSessionEventProcessor::CoreSessionEventProcessor(CoreSession *session) : BasicHandler("handleCtcp", session), _coreSession(session) @@@ -112,22 -116,14 +116,22 @@@ void CoreSessionEventProcessor::process CoreNetwork *net = coreNetwork(e); - QString construct = net->saslAccount(); - construct.append(QChar(QChar::Null)); - construct.append(net->saslAccount()); - construct.append(QChar(QChar::Null)); - construct.append(net->saslPassword()); - QByteArray saslData = QByteArray(construct.toAscii().toBase64()); - saslData.prepend("AUTHENTICATE "); - net->putRawLine(saslData); +#ifdef HAVE_SSL + if (net->identityPtr()->sslCert().isNull()) { +#endif + QString construct = net->saslAccount(); + construct.append(QChar(QChar::Null)); + construct.append(net->saslAccount()); + construct.append(QChar(QChar::Null)); + construct.append(net->saslPassword()); + QByteArray saslData = QByteArray(construct.toAscii().toBase64()); + saslData.prepend("AUTHENTICATE "); + net->putRawLine(saslData); +#ifdef HAVE_SSL + } else { + net->putRawLine("AUTHENTICATE +"); + } +#endif } @@@ -137,19 -133,9 +141,19 @@@ void CoreSessionEventProcessor::process // additional CAP messages (ls, multi-prefix, et cetera). if (e->params().count() == 3) { - if (e->params().at(2) == "sasl") { + if (e->params().at(2).startsWith("sasl")) { // Freenode (at least) sends "sasl " with a trailing space for some reason! // FIXME use event - coreNetwork(e)->putRawLine(coreNetwork(e)->serverEncode("AUTHENTICATE PLAIN")); // Only working with PLAIN atm, blowfish later + // if the current identity has a cert set, use SASL EXTERNAL +#ifdef HAVE_SSL + if (!coreNetwork(e)->identityPtr()->sslCert().isNull()) { + coreNetwork(e)->putRawLine(coreNetwork(e)->serverEncode("AUTHENTICATE EXTERNAL")); + } else { +#endif + // Only working with PLAIN atm, blowfish later + coreNetwork(e)->putRawLine(coreNetwork(e)->serverEncode("AUTHENTICATE PLAIN")); +#ifdef HAVE_SSL + } +#endif } } } @@@ -436,6 -422,42 +440,42 @@@ void CoreSessionEventProcessor::process } + #ifdef HAVE_QCA2 + void CoreSessionEventProcessor::processKeyEvent(KeyEvent *e) + { + if (!Cipher::neededFeaturesAvailable()) { + emit newEvent(new MessageEvent(Message::Error, e->network(), tr("Unable to perform key exchange."), e->prefix(), e->target(), Message::None, e->timestamp())); + return; + } + CoreNetwork *net = qobject_cast(e->network()); + Cipher *c = net->cipher(e->target()); + if (!c) // happens when there is no CoreIrcChannel for the target (i.e. never?) + return; + + if (e->exchangeType() == KeyEvent::Init) { + QByteArray pubKey = c->parseInitKeyX(e->key()); + if (pubKey.isEmpty()) { + emit newEvent(new MessageEvent(Message::Error, e->network(), tr("Unable to parse the DH1080_INIT. Key exchange failed."), e->prefix(), e->target(), Message::None, e->timestamp())); + return; + } else { + net->setCipherKey(e->target(), c->key()); + emit newEvent(new MessageEvent(Message::Info, e->network(), tr("Your key is set and messages will be encrypted."), e->prefix(), e->target(), Message::None, e->timestamp())); + QList p; + p << net->serverEncode(e->target()) << net->serverEncode("DH1080_FINISH ")+pubKey; + net->putCmd("NOTICE", p); + } + } else { + if (c->parseFinishKeyX(e->key())) { + net->setCipherKey(e->target(), c->key()); + emit newEvent(new MessageEvent(Message::Info, e->network(), tr("Your key is set and messages will be encrypted."), e->prefix(), e->target(), Message::None, e->timestamp())); + } else { + emit newEvent(new MessageEvent(Message::Info, e->network(), tr("Failed to parse DH1080_FINISH. Key exchange failed."), e->prefix(), e->target(), Message::None, e->timestamp())); + } + } + } + #endif + + /* RPL_WELCOME */ void CoreSessionEventProcessor::processIrcEvent001(IrcEvent *e) { diff --combined src/core/coresessioneventprocessor.h index 680917ac,07bb7d85..6a163c6c --- a/src/core/coresessioneventprocessor.h +++ b/src/core/coresessioneventprocessor.h @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -31,6 -31,10 +31,10 @@@ class IrcEvent class IrcEventNumeric; class Netsplit; + #ifdef HAVE_QCA2 + class KeyEvent; + #endif + class CoreSessionEventProcessor : public BasicHandler { Q_OBJECT @@@ -55,6 -59,9 +59,9 @@@ public Q_INVOKABLE void processIrcEventQuit(IrcEvent *event); Q_INVOKABLE void lateProcessIrcEventQuit(IrcEvent *event); Q_INVOKABLE void processIrcEventTopic(IrcEvent *event); + #ifdef HAVE_QCA2 + Q_INVOKABLE void processKeyEvent(KeyEvent *event); + #endif Q_INVOKABLE void processIrcEvent001(IrcEvent *event); // RPL_WELCOME Q_INVOKABLE void processIrcEvent005(IrcEvent *event); // RPL_ISUPPORT diff --combined src/core/coreuserinputhandler.cpp index 05424a6b,bb677c38..53f4c2e0 --- a/src/core/coreuserinputhandler.cpp +++ b/src/core/coreuserinputhandler.cpp @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -207,14 -207,6 +207,6 @@@ void CoreUserInputHandler::handleDelkey } network()->setCipherKey(target, QByteArray()); - - if (network()->isChannelName(target) && network()->channels().contains(target)) { - qobject_cast(network()->ircChannel(target))->setEncrypted(false); - } - else if (network()->nicks().contains(target)) { - qobject_cast(network()->ircUser(target))->setEncrypted(false); - } - emit displayMsg(Message::Info, bufferInfo.bufferName(), tr("The key for %1 has been deleted.").arg(target)); #else @@@ -327,6 -319,50 +319,50 @@@ void CoreUserInputHandler::handleJoin(c } + void CoreUserInputHandler::handleKeyx(const BufferInfo &bufferInfo, const QString &msg) + { + #ifdef HAVE_QCA2 + if (!bufferInfo.isValid()) + return; + + if (!Cipher::neededFeaturesAvailable()) + return; + + QStringList parms = msg.split(' ', QString::SkipEmptyParts); + + if (parms.count() == 0 && !bufferInfo.bufferName().isEmpty()) + parms.prepend(bufferInfo.bufferName()); + else if (parms.count() != 1) { + emit displayMsg(Message::Info, bufferInfo.bufferName(), + tr("[usage] /keyx [] Initiates a DH1080 key exchange with the target.")); + return; + } + + QString target = parms.at(0); + + Cipher *cipher = network()->cipher(target); + if (!cipher) // happens when there is no CoreIrcChannel for the target + return; + + QByteArray pubKey = cipher->initKeyExchange(); + if (pubKey.isEmpty()) + emit displayMsg(Message::Error, bufferInfo.bufferName(), tr("Failed to initiate key exchange with %1.").arg(target)); + else { + QList params; + params << serverEncode(target) << serverEncode("DH1080_INIT ") + pubKey; + emit putCmd("NOTICE", params); + emit displayMsg(Message::Info, bufferInfo.bufferName(), tr("Initiated key exchange with %1.").arg(target)); + } + #else + Q_UNUSED(msg) + emit displayMsg(Message::Error, bufferInfo.bufferName(), tr("Error: Setting an encryption key requires Quassel to have been built " + "with support for the Qt Cryptographic Architecture (QCA) library. " + "Contact your distributor about a Quassel package with QCA " + "support, or rebuild Quassel with QCA present.")); + #endif + } + + void CoreUserInputHandler::handleKick(const BufferInfo &bufferInfo, const QString &msg) { QString nick = msg.section(' ', 0, 0, QString::SectionSkipEmpty); @@@ -560,14 -596,8 +596,8 @@@ void CoreUserInputHandler::handleSetkey QString target = parms.at(0); QByteArray key = parms.at(1).toLocal8Bit(); - network()->setCipherKey(target, key); - if (network()->isChannelName(target) && network()->channels().contains(target)) - qobject_cast(network()->ircChannel(target))->setEncrypted(true); - else if (network()->nicks().contains(target)) - qobject_cast(network()->ircUser(target))->setEncrypted(true); - emit displayMsg(Message::Info, bufferInfo.bufferName(), tr("The key for %1 has been set.").arg(target)); #else Q_UNUSED(msg) @@@ -716,7 -746,7 +746,7 @@@ void CoreUserInputHandler::putPrivmsg(c QByteArray crypted = message.left(splitPos); bool isEncrypted = false; #ifdef HAVE_QCA2 - if (cipher && !message.isEmpty()) { + if (cipher && !cipher->key().isEmpty() && !message.isEmpty()) { isEncrypted = cipher->encrypt(crypted); } #endif @@@ -797,7 -827,7 +827,7 @@@ QByteArray CoreUserInputHandler::encryp return message_; Cipher *cipher = network()->cipher(target); - if (!cipher) + if (!cipher || cipher->key().isEmpty()) return message_; QByteArray message = message_; diff --combined src/core/coreuserinputhandler.h index b6ebac1b,288e2e86..ccd9bd2a --- a/src/core/coreuserinputhandler.h +++ b/src/core/coreuserinputhandler.h @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -48,6 -48,7 +48,7 @@@ public slots void handleDevoice(const BufferInfo &bufferInfo, const QString &text); void handleInvite(const BufferInfo &bufferInfo, const QString &text); void handleJoin(const BufferInfo &bufferInfo, const QString &text); + void handleKeyx(const BufferInfo &bufferInfo, const QString &text); void handleKick(const BufferInfo &bufferInfo, const QString &text); void handleKill(const BufferInfo &bufferInfo, const QString &text); void handleList(const BufferInfo &bufferInfo, const QString &text); diff --combined src/core/ircparser.cpp index e69336fb,44e6cd19..f3db4fce --- a/src/core/ircparser.cpp +++ b/src/core/ircparser.cpp @@@ -1,5 -1,5 +1,5 @@@ /*************************************************************************** - * Copyright (C) 2005-2012 by the Quassel Project * + * Copyright (C) 2005-2013 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@@ -28,6 -28,7 +28,7 @@@ #ifdef HAVE_QCA2 # include "cipher.h" + # include "keyevent.h" #endif IrcParser::IrcParser(CoreSession *session) : @@@ -58,7 -59,7 +59,7 @@@ QByteArray IrcParser::decrypt(Network * return message; Cipher *cipher = qobject_cast(network)->cipher(bufferName); - if (!cipher) + if (!cipher || cipher->key().isEmpty()) return message; return isTopic ? cipher->decryptTopic(message) : cipher->decrypt(message); @@@ -228,7 -229,16 +229,16 @@@ void IrcParser::processNetworkIncoming( if (!net->isChannelName(target)) target = nickFromMask(prefix); } - events << new IrcEventRawMessage(EventManager::IrcEventRawNotice, net, params[1], prefix, target, e->timestamp()); + + #ifdef HAVE_QCA2 + // Handle DH1080 key exchange + if (params[1].startsWith("DH1080_INIT")) { + events << new KeyEvent(EventManager::KeyEvent, net, prefix, target, KeyEvent::Init, params[1].mid(12)); + } else if (params[1].startsWith("DH1080_FINISH")) { + events << new KeyEvent(EventManager::KeyEvent, net, prefix, target, KeyEvent::Finish, params[1].mid(14)); + } else + #endif + events << new IrcEventRawMessage(EventManager::IrcEventRawNotice, net, params[1], prefix, target, e->timestamp()); } } break;