From: Marcus Eggenberger Date: Sun, 30 Mar 2008 13:41:13 +0000 (+0000) Subject: Implemented SSL support for client <-> core connection. X-Git-Tag: 0.2.0-alpha5~49 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=b79832bf9c4c21b05629cfd2fdbd008ad690572f;ds=sidebyside Implemented SSL support for client <-> core connection. To make this work the core needs a ssl certificate in pem format using an rsa key. This certificate needs to reside in ~/.quassel/quasselCert.pem If you don't have a certificate, you can create one using openssl: openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout ~/.quassel/quasselCert.pem -out ~/.quassel/quasselCert.pem --- diff --git a/src/client/clientsyncer.cpp b/src/client/clientsyncer.cpp index 9534b6fd..952860a6 100644 --- a/src/client/clientsyncer.cpp +++ b/src/client/clientsyncer.cpp @@ -87,6 +87,7 @@ void ClientSyncer::coreHasData() { } void ClientSyncer::coreSocketError(QAbstractSocket::SocketError) { + qDebug() << "coreSocketError" << socket << socket->errorString(); emit connectionError(socket->errorString()); socket->deleteLater(); } @@ -120,7 +121,13 @@ void ClientSyncer::connectToCore(const QVariantMap &conn) { //clientMode = RemoteCore; //emit coreConnectionMsg(tr("Connecting...")); Q_ASSERT(!socket); + +#ifndef QT_NO_OPENSSL + QSslSocket *sock = new QSslSocket(Client::instance()); +#else QTcpSocket *sock = new QTcpSocket(Client::instance()); +#endif + if(conn.contains("useProxy") && conn["useProxy"].toBool()) { QNetworkProxy proxy((QNetworkProxy::ProxyType)conn["proxyType"].toInt(), conn["proxyHost"].toString(), conn["proxyPort"].toUInt(), conn["proxyUser"].toString(), conn["proxyPassword"].toString()); sock->setProxy(proxy); @@ -145,7 +152,8 @@ void ClientSyncer::coreSocketConnected() { clientInit["ClientVersion"] = Global::quasselVersion; clientInit["ClientDate"] = Global::quasselDate; clientInit["ClientBuild"] = Global::quasselBuild; // this is a minimum, since we probably won't update for every commit - clientInit["UseSsl"] = false; // FIXME implement SSL + clientInit["UseSsl"] = coreConnectionInfo["useSsl"]; + SignalProxy::writeDataToDevice(socket, clientInit); } @@ -172,6 +180,27 @@ void ClientSyncer::clientInitAck(const QVariantMap &msg) { return; } emit connectionMsg(msg["CoreInfo"].toString()); + if(coreConnectionInfo["useSsl"].toBool()) { + if(msg["SupportSsl"].toBool()) { + QSslSocket *sslSocket = qobject_cast(socket); + if(sslSocket) { + connect(sslSocket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); + sslSocket->startClientEncryption(); + emit encrypted(true); + } else { + emit connectionError(tr("This client is built without SSL Support!
Disable the usage of SSL in the account settings.")); + emit encrypted(false); + disconnectFromCore(); + return; + } + } else { + emit connectionError(tr("The Quassel Core you are trying to connect to does not support SSL!
If you want to connect anyways, disable the usage of SSL in the account settings.")); + emit encrypted(false); + disconnectFromCore(); + return; + } + } + if(!msg["Configured"].toBool()) { // start wizard emit startCoreSetup(msg["StorageBackends"].toList()); @@ -330,3 +359,12 @@ void ClientSyncer::checkSyncState() { } } +void ClientSyncer::sslErrors(const QList &errors) { + qDebug() << "SSL Errors:"; + foreach(QSslError err, errors) + qDebug() << " " << err; + + QSslSocket *socket = qobject_cast(sender()); + if(socket) + socket->ignoreSslErrors(); +} diff --git a/src/client/clientsyncer.h b/src/client/clientsyncer.h index 95aee59f..5ea2ae42 100644 --- a/src/client/clientsyncer.h +++ b/src/client/clientsyncer.h @@ -24,6 +24,7 @@ #include #include #include +#include #include class IrcUser; @@ -55,6 +56,7 @@ class ClientSyncer : public QObject { void coreSetupSuccess(); void coreSetupFailed(const QString &error); + void encrypted(bool); public slots: void connectToCore(const QVariantMap &); @@ -83,7 +85,8 @@ class ClientSyncer : public QObject { void sessionStateReceived(const QVariantMap &state); void doCoreSetup(const QVariant &setupData); - + void sslErrors(const QList &errors); + private: QPointer socket; quint32 blockSize; diff --git a/src/common/util.cpp b/src/common/util.cpp index e47882ab..aeccf678 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -138,3 +138,18 @@ QByteArray methodName(const QMetaMethod &method) { QByteArray sig(method.signature()); return sig.left(sig.indexOf("(")); } + +QDir quasselDir() { + // kinda ugly, but I currently see no other way to do that +#ifdef Q_OS_WIN32 + QString quasselDir = QDir::homePath() + qgetenv("APPDATA") + "/quassel/"; +#else + QString quasselDir = QDir::homePath() + "/.quassel/"; +#endif + + QDir qDir(quasselDir); + if(!qDir.exists(quasselDir)) + qDir.mkpath(quasselDir); + + return qDir; +} diff --git a/src/common/util.h b/src/common/util.h index 9354620a..0b372b6a 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -21,6 +21,7 @@ #ifndef _UTIL_H_ #define _UTIL_H_ +#include #include #include #include @@ -51,4 +52,6 @@ uint editingDistance(const QString &s1, const QString &s2); QByteArray methodName(const QMetaMethod &method); +QDir quasselDir(); + #endif diff --git a/src/core/core.cpp b/src/core/core.cpp index 20dc4494..50bfa739 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -30,6 +30,8 @@ #include "sqlitestorage.h" #include "network.h" +#include "util.h" + Core *Core::instanceptr = 0; QMutex Core::mutex; @@ -337,6 +339,8 @@ void Core::incomingConnection() { QTcpSocket *socket = server.nextPendingConnection(); connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + QVariantMap clientInfo; blocksizes.insert(socket, (quint32)0); qDebug() << "Client connected from" << qPrintable(socket->peerAddress().toString()); @@ -381,7 +385,12 @@ void Core::processClientMessage(QTcpSocket *socket, const QVariantMap &msg) { "Up %3d%4h%5m (since %6)").arg(Global::quasselVersion).arg(Global::quasselBuild) .arg(updays).arg(uphours,2,10,QChar('0')).arg(upmins,2,10,QChar('0')).arg(startTime.toString(Qt::TextDate)); - reply["SupportSsl"] = false; + SslServer *sslServer = qobject_cast(&server); + QSslSocket *sslSocket = qobject_cast(socket); + bool supportSsl = (bool)sslServer && (bool)sslSocket && sslServer->certIsValid(); + reply["SupportSsl"] = supportSsl; + // switch to ssl after client has been informed about our capabilities (see below) + reply["LoginEnabled"] = true; // Just version information -- check it! @@ -412,6 +421,15 @@ void Core::processClientMessage(QTcpSocket *socket, const QVariantMap &msg) { clientInfo[socket] = msg; // store for future reference reply["MsgType"] = "ClientInitAck"; SignalProxy::writeDataToDevice(socket, reply); + + // after we told the client that we are ssl capable we switch to ssl mode + if(supportSsl && msg["UseSsl"].toBool()) { + qDebug() << "Starting TLS for Client:" << qPrintable(socket->peerAddress().toString()); + connect(sslSocket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); + sslSocket->startServerEncryption(); + } + + } else { // for the rest, we need an initialized connection if(!clientInfo.contains(socket)) { @@ -496,3 +514,16 @@ SessionThread *Core::createSession(UserId uid, bool restore) { sess->start(); return sess; } + +void Core::sslErrors(const QList &errors) { + Q_UNUSED(errors); + QSslSocket *socket = qobject_cast(sender()); + if(socket) + socket->ignoreSslErrors(); +} + +void Core::socketError(QAbstractSocket::SocketError err) { + QAbstractSocket *socket = qobject_cast(sender()); + if(socket && err != QAbstractSocket::RemoteHostClosedError) + qDebug() << "Core::socketError()" << socket << err << socket->errorString(); +} diff --git a/src/core/core.h b/src/core/core.h index 19d6541f..277c43cc 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -26,13 +26,14 @@ #include #include #include -#include #include +#include #include "bufferinfo.h" #include "message.h" #include "global.h" #include "sessionthread.h" +#include "sslserver.h" #include "types.h" class CoreSession; @@ -270,6 +271,9 @@ class Core : public QObject { bool initStorage(QVariantMap dbSettings, bool setup = false); + void sslErrors(const QList &errors); + void socketError(QAbstractSocket::SocketError); + private: Core(); ~Core(); @@ -289,7 +293,12 @@ class Core : public QObject { Storage *storage; QTimer _storageSyncTimer; - QTcpServer server; // TODO: implement SSL +#ifndef QT_NO_OPENSSL + SslServer server; +#else + QTcpServer server; +#endif + QHash blocksizes; QHash clientInfo; diff --git a/src/core/core.pri b/src/core/core.pri index 722c5301..78087d1a 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -1,6 +1,6 @@ DEPMOD = common QT_MOD = core network sql script SRCS = core.cpp corebacklogmanager.cpp corebufferviewconfig.cpp corebufferviewmanager.cpp coresession.cpp coresettings.cpp networkconnection.cpp sqlitestorage.cpp abstractsqlstorage.cpp storage.cpp basichandler.cpp \ - ircserverhandler.cpp userinputhandler.cpp ctcphandler.cpp coreusersettings.cpp sessionthread.cpp + ircserverhandler.cpp userinputhandler.cpp ctcphandler.cpp coreusersettings.cpp sessionthread.cpp sslserver.cpp HDRS = core.h corebacklogmanager.h corebufferviewconfig.h corebufferviewmanager.h coresession.h coresettings.h networkconnection.h sqlitestorage.h abstractsqlstorage.h storage.h basichandler.h \ - ircserverhandler.h userinputhandler.h ctcphandler.h coreusersettings.h sessionthread.h + ircserverhandler.h userinputhandler.h ctcphandler.h coreusersettings.h sessionthread.h sslserver.h diff --git a/src/core/sqlitestorage.cpp b/src/core/sqlitestorage.cpp index fb904524..2121f79f 100644 --- a/src/core/sqlitestorage.cpp +++ b/src/core/sqlitestorage.cpp @@ -24,6 +24,8 @@ #include "network.h" +#include "util.h" + SqliteStorage::SqliteStorage(QObject *parent) : AbstractSqlStorage(parent) { @@ -723,18 +725,7 @@ QList SqliteStorage::requestMsgRange(UserId user, BufferId bufferId, in } QString SqliteStorage::backlogFile() { - // kinda ugly, but I currently see no other way to do that -#ifdef Q_OS_WIN32 - QString quasselDir = QDir::homePath() + qgetenv("APPDATA") + "\\quassel\\"; -#else - QString quasselDir = QDir::homePath() + "/.quassel/"; -#endif - - QDir qDir(quasselDir); - if(!qDir.exists(quasselDir)) - qDir.mkpath(quasselDir); - - return quasselDir + "quassel-storage.sqlite"; + return quasselDir().absolutePath() + "/quassel-storage.sqlite"; } diff --git a/src/qtui/coreconnectdlg.cpp b/src/qtui/coreconnectdlg.cpp index fd4e5900..b4225efc 100644 --- a/src/qtui/coreconnectdlg.cpp +++ b/src/qtui/coreconnectdlg.cpp @@ -63,6 +63,7 @@ CoreConnectDlg::CoreConnectDlg(QWidget *parent, bool autoconnect) : QDialog(pare connect(clientSyncer, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)),this, SLOT(initPhaseSocketState(QAbstractSocket::SocketState))); connect(clientSyncer, SIGNAL(connectionError(const QString &)), this, SLOT(initPhaseError(const QString &))); connect(clientSyncer, SIGNAL(connectionMsg(const QString &)), this, SLOT(initPhaseMsg(const QString &))); + connect(clientSyncer, SIGNAL(encrypted(bool)), this, SLOT(encrypted(bool))); connect(clientSyncer, SIGNAL(startLogin()), this, SLOT(startLogin())); connect(clientSyncer, SIGNAL(loginFailed(const QString &)), this, SLOT(loginFailed(const QString &))); connect(clientSyncer, SIGNAL(loginSuccess()), this, SLOT(startSync())); @@ -209,6 +210,7 @@ void CoreConnectDlg::on_accountButtonBox_accepted() { /*** Phase One: initializing the core connection ***/ void CoreConnectDlg::connectToCore() { + ui.secureConnection->hide(); ui.connectIcon->setPixmap(QPixmap::fromImage(QImage(":/22x22/actions/network-disconnect"))); ui.connectLabel->setText(tr("Connect to %1").arg(accountData["Host"].toString())); ui.coreInfoLabel->setText(""); @@ -224,6 +226,7 @@ void CoreConnectDlg::connectToCore() { void CoreConnectDlg::initPhaseError(const QString &error) { doingAutoConnect = false; + ui.secureConnection->hide(); ui.connectIcon->setPixmap(QPixmap::fromImage(QImage(":/22x22/status/dialog-error"))); //ui.connectLabel->setBrush(QBrush("red")); ui.connectLabel->setText(tr("
Connection to %1 failed!
").arg(accountData["Host"].toString())); @@ -239,6 +242,13 @@ void CoreConnectDlg::initPhaseMsg(const QString &msg) { ui.coreInfoLabel->setText(msg); } +void CoreConnectDlg::encrypted(bool useSsl) { + if(useSsl) + ui.secureConnection->show(); + else + ui.secureConnection->hide(); +} + void CoreConnectDlg::initPhaseSocketState(QAbstractSocket::SocketState state) { QString s; QString host = accountData["Host"].toString(); @@ -469,6 +479,7 @@ CoreAccountEditDlg::CoreAccountEditDlg(AccountId id, const QVariantMap &acct, co ui.port->setValue(acct["Port"].toUInt()); ui.useInternal->setChecked(acct["UseInternal"].toBool()); ui.accountName->setText(acct["AccountName"].toString()); + ui.useSsl->setChecked(account["useSsl"].toBool()); ui.useProxy->setChecked(account["useProxy"].toBool()); ui.proxyHost->setText(account["proxyHost"].toString()); ui.proxyPort->setValue(account["proxyPort"].toUInt()); @@ -485,6 +496,7 @@ QVariantMap CoreAccountEditDlg::accountData() { account["Host"] = ui.host->text().trimmed(); account["Port"] = ui.port->value(); account["UseInternal"] = ui.useInternal->isChecked(); + account["useSsl"] = ui.useSsl->isChecked(); account["useProxy"] = ui.useProxy->isChecked(); account["proxyHost"] = ui.proxyHost->text().trimmed(); account["proxyPort"] = ui.proxyPort->value(); diff --git a/src/qtui/coreconnectdlg.h b/src/qtui/coreconnectdlg.h index 42638e51..08772472 100644 --- a/src/qtui/coreconnectdlg.h +++ b/src/qtui/coreconnectdlg.h @@ -61,6 +61,7 @@ class CoreConnectDlg : public QDialog { void initPhaseError(const QString &error); void initPhaseMsg(const QString &msg); void initPhaseSocketState(QAbstractSocket::SocketState); + void encrypted(bool); /*** Phase Two: Login ***/ void startLogin(); diff --git a/src/qtui/ui/coreaccounteditdlg.ui b/src/qtui/ui/coreaccounteditdlg.ui index 5dda97de..30364a2b 100644 --- a/src/qtui/ui/coreaccounteditdlg.ui +++ b/src/qtui/ui/coreaccounteditdlg.ui @@ -104,10 +104,7 @@ - - - false - + Use secure connection (SSL) diff --git a/src/qtui/ui/coreconnectdlg.ui b/src/qtui/ui/coreconnectdlg.ui index d5a41a62..8204ac9c 100644 --- a/src/qtui/ui/coreconnectdlg.ui +++ b/src/qtui/ui/coreconnectdlg.ui @@ -28,7 +28,7 @@ - 0 + 1 @@ -207,6 +207,16 @@ space + + + + + + + :/22x22/actions/oxygen/22x22/actions/document-encrypt.png + + + diff --git a/version.inc b/version.inc index 45df4574..fdbab3c0 100644 --- a/version.inc +++ b/version.inc @@ -4,8 +4,8 @@ { using namespace Global; quasselVersion = "0.2.0-alpha4-pre"; - quasselDate = "2008-03-29"; - quasselBuild = 664; + quasselDate = "2008-03-30"; + quasselBuild = 668; //! Minimum client build number the core needs clientBuildNeeded = 642;