Make SSL work again for CoreConnection
authorManuel Nickschas <sputnick@quassel-irc.org>
Sat, 28 Nov 2009 20:00:29 +0000 (21:00 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sat, 28 Nov 2009 23:39:42 +0000 (00:39 +0100)
This reintroduces and improves SSL support, and comes complete with new shiny dialogs.

src/client/coreconnection.cpp
src/client/coreconnection.h
src/qtui/mainwin.cpp
src/qtui/mainwin.h

index 0b342ed..a60fae3 100644 (file)
@@ -103,6 +103,16 @@ void CoreConnection::resetConnection() {
   setProgressMaximum(-1); // disable
   setState(Disconnected);
   emit connectionMsg(tr("Disconnected from core."));
   setProgressMaximum(-1); // disable
   setState(Disconnected);
   emit connectionMsg(tr("Disconnected from core."));
+  emit encrypted(false);
+}
+
+bool CoreConnection::isEncrypted() const {
+#ifndef HAVE_SSL
+  return false;
+#else
+  QSslSocket *sock = qobject_cast<QSslSocket *>(_socket);
+  return isConnected() && sock && sock->isEncrypted();
+#endif
 }
 
 void CoreConnection::socketStateChanged(QAbstractSocket::SocketState socketState) {
 }
 
 void CoreConnection::socketStateChanged(QAbstractSocket::SocketState socketState) {
@@ -164,15 +174,6 @@ void CoreConnection::setState(ConnectionState state) {
   }
 }
 
   }
 }
 
-void CoreConnection::setWarningsHandler(const char *slot) {
-  resetWarningsHandler();
-  connect(this, SIGNAL(handleIgnoreWarnings(bool)), this, slot);
-}
-
-void CoreConnection::resetWarningsHandler() {
-  disconnect(this, SIGNAL(handleIgnoreWarnings(bool)), this, 0);
-}
-
 void CoreConnection::coreSocketError(QAbstractSocket::SocketError) {
   qDebug() << "coreSocketError" << _socket << _socket->errorString();
   emit connectionError(_socket->errorString());
 void CoreConnection::coreSocketError(QAbstractSocket::SocketError) {
   qDebug() << "coreSocketError" << _socket << _socket->errorString();
   emit connectionError(_socket->errorString());
@@ -289,13 +290,24 @@ void CoreConnection::connectToCurrentAccount() {
     return;
   }
 
     return;
   }
 
+  CoreAccountSettings s;
+
   Q_ASSERT(!_socket);
 #ifdef HAVE_SSL
   QSslSocket *sock = new QSslSocket(Client::instance());
   Q_ASSERT(!_socket);
 #ifdef HAVE_SSL
   QSslSocket *sock = new QSslSocket(Client::instance());
+  // make sure the warning is shown if we happen to connect without SSL support later
+  s.setAccountValue("ShowNoClientSslWarning", true);
 #else
   if(_account.useSsl()) {
 #else
   if(_account.useSsl()) {
-    emit connectionError(tr("<b>This client is built without SSL Support!</b><br />Disable the usage of SSL in the account settings."));
-    return;
+    if(s.accountValue("ShowNoClientSslWarning", true).toBool()) {
+      bool accepted = false;
+      emit handleNoSslInClient(&accepted);
+      if(!accepted) {
+        emit connectionError(tr("Unencrypted connection canceled"));
+        return;
+      }
+      s.setAccountValue("ShowNoClientSslWarning", false);
+    }
   }
   QTcpSocket *sock = new QTcpSocket(Client::instance());
 #endif
   }
   QTcpSocket *sock = new QTcpSocket(Client::instance());
 #endif
@@ -355,27 +367,86 @@ void CoreConnection::clientInitAck(const QVariantMap &msg) {
 #endif
 
   _coreMsgBuffer = msg;
 #endif
 
   _coreMsgBuffer = msg;
+
 #ifdef HAVE_SSL
 #ifdef HAVE_SSL
+  CoreAccountSettings s;
   if(currentAccount().useSsl()) {
     if(msg["SupportSsl"].toBool()) {
   if(currentAccount().useSsl()) {
     if(msg["SupportSsl"].toBool()) {
+      // Make sure the warning is shown next time we don't have SSL in the core
+      s.setAccountValue("ShowNoCoreSslWarning", true);
+
       QSslSocket *sslSocket = qobject_cast<QSslSocket *>(_socket);
       Q_ASSERT(sslSocket);
       QSslSocket *sslSocket = qobject_cast<QSslSocket *>(_socket);
       Q_ASSERT(sslSocket);
-      connect(sslSocket, SIGNAL(encrypted()), this, SLOT(sslSocketEncrypted()));
-      connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrors(const QList<QSslError> &)));
-
+      connect(sslSocket, SIGNAL(encrypted()), SLOT(sslSocketEncrypted()));
+      connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(sslErrors()));
       sslSocket->startClientEncryption();
     } else {
       sslSocket->startClientEncryption();
     } else {
-      emit connectionError(tr("<b>The Quassel Core you are trying to connect to does not support SSL!</b><br />If you want to connect anyways, disable the usage of SSL in the account settings."));
-      disconnectFromCore();
+      if(s.accountValue("ShowNoCoreSslWarning", true).toBool()) {
+        bool accepted = false;
+        emit handleNoSslInCore(&accepted);
+        if(!accepted) {
+          emit connectionError(tr("Unencrypted connection canceled"));
+          disconnectFromCore();
+          return;
+        }
+        s.setAccountValue("ShowNoCoreSslWarning", false);
+        s.setAccountValue("SslCert", QString());
+      }
+      connectionReady();
     }
     return;
   }
 #endif
   // if we use SSL we wait for the next step until every SSL warning has been cleared
   connectionReady();
     }
     return;
   }
 #endif
   // if we use SSL we wait for the next step until every SSL warning has been cleared
   connectionReady();
+}
 
 
+#ifdef HAVE_SSL
+
+void CoreConnection::sslSocketEncrypted() {
+  QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
+  Q_ASSERT(socket);
+
+  if(!socket->sslErrors().count()) {
+    // Cert is valid, so we don't want to store it as known
+    // That way, a warning will appear in case it becomes invalid at some point
+    CoreAccountSettings s;
+    s.setAccountValue("SSLCert", QString());
+  }
+
+  emit encrypted(true);
+  connectionReady();
+}
+
+void CoreConnection::sslErrors() {
+  QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
+  Q_ASSERT(socket);
+
+  CoreAccountSettings s;
+  QByteArray knownDigest = s.accountValue("SslCert").toByteArray();
+
+  if(knownDigest != socket->peerCertificate().digest()) {
+    bool accepted = false;
+    bool permanently = false;
+    emit handleSslErrors(socket, &accepted, &permanently);
+
+    if(!accepted) {
+      emit connectionError(tr("Unencrypted connection canceled"));
+      disconnectFromCore();
+      return;
+    }
+
+    if(permanently)
+      s.setAccountValue("SslCert", socket->peerCertificate().digest());
+    else
+      s.setAccountValue("SslCert", QString());
+  }
+
+  socket->ignoreSslErrors();
 }
 
 }
 
+#endif /* HAVE_SSL */
+
 void CoreConnection::connectionReady() {
   if(!_coreMsgBuffer["Configured"].toBool()) {
     // start wizard
 void CoreConnection::connectionReady() {
   if(!_coreMsgBuffer["Configured"].toBool()) {
     // start wizard
@@ -384,7 +455,6 @@ void CoreConnection::connectionReady() {
     loginToCore();
   }
   _coreMsgBuffer.clear();
     loginToCore();
   }
   _coreMsgBuffer.clear();
-  resetWarningsHandler();
 }
 
 void CoreConnection::loginToCore(const QString &prevError) {
 }
 
 void CoreConnection::loginToCore(const QString &prevError) {
index f1afab9..2ef7d1a 100644 (file)
@@ -58,20 +58,25 @@ public:
   inline bool isConnected() const;
   inline CoreAccount currentAccount() const;
 
   inline bool isConnected() const;
   inline CoreAccount currentAccount() const;
 
+  bool isEncrypted() const;
+
   inline int progressMinimum() const;
   inline int progressMaximum() const;
   inline int progressValue() const;
   inline QString progressText() const;
 
   inline int progressMinimum() const;
   inline int progressMaximum() const;
   inline int progressValue() const;
   inline QString progressText() const;
 
+#ifdef HAVE_SSL
+  inline const QSslSocket *sslSocket() const;
+#endif
+
 public slots:
   bool connectToCore(AccountId = 0);
   void reconnectToCore();
   void disconnectFromCore();
 
 public slots:
   bool connectToCore(AccountId = 0);
   void reconnectToCore();
   void disconnectFromCore();
 
-//  void useInternalCore();
-
 signals:
   void stateChanged(CoreConnection::ConnectionState);
 signals:
   void stateChanged(CoreConnection::ConnectionState);
+  void encrypted(bool isEncrypted = true);
   void synchronized();
 
   void connectionError(const QString &errorMsg);
   void synchronized();
 
   void connectionError(const QString &errorMsg);
@@ -87,10 +92,13 @@ signals:
   void startInternalCore();
   void connectToInternalCore(SignalProxy *proxy);
 
   void startInternalCore();
   void connectToInternalCore(SignalProxy *proxy);
 
-  // This signal MUST be handled synchronously!
+  // These signals MUST be handled synchronously!
   void userAuthenticationRequired(CoreAccount *, bool *valid, const QString &errorMessage = QString());
   void userAuthenticationRequired(CoreAccount *, bool *valid, const QString &errorMessage = QString());
-
-  void handleIgnoreWarnings(bool permanently);
+  void handleNoSslInClient(bool *accepted);
+  void handleNoSslInCore(bool *accepted);
+#ifdef HAVE_SSL
+  void handleSslErrors(const QSslSocket *socket, bool *accepted, bool *permanently);
+#endif
 
 private slots:
   void connectToCurrentAccount();
 
 private slots:
   void connectToCurrentAccount();
@@ -111,8 +119,6 @@ private slots:
   void internalSessionStateReceived(const QVariant &packedState);
   void sessionStateReceived(const QVariantMap &state);
 
   void internalSessionStateReceived(const QVariant &packedState);
   void sessionStateReceived(const QVariantMap &state);
 
-  void setWarningsHandler(const char *slot);
-  void resetWarningsHandler();
   void resetConnection();
   void connectionReady();
   //void doCoreSetup(const QVariant &setupData);
   void resetConnection();
   void connectionReady();
   //void doCoreSetup(const QVariant &setupData);
@@ -130,6 +136,11 @@ private slots:
   void setState(QAbstractSocket::SocketState socketState);
   void setState(ConnectionState state);
 
   void setState(QAbstractSocket::SocketState socketState);
   void setState(ConnectionState state);
 
+#ifdef HAVE_SSL
+  void sslSocketEncrypted();
+  void sslErrors();
+#endif
+
 private:
   CoreAccountModel *_model;
   CoreAccount _account;
 private:
   CoreAccountModel *_model;
   CoreAccount _account;
@@ -162,4 +173,8 @@ bool CoreConnection::isConnected() const { return state() >= Connected; }
 CoreAccount CoreConnection::currentAccount() const { return _account; }
 CoreAccountModel *CoreConnection::accountModel() const { return _model; }
 
 CoreAccount CoreConnection::currentAccount() const { return _account; }
 CoreAccountModel *CoreConnection::accountModel() const { return _model; }
 
+#ifdef HAVE_SSL
+const QSslSocket *CoreConnection::sslSocket() const { return qobject_cast<QSslSocket *>(_socket); }
+#endif
+
 #endif
 #endif
index ed467cd..c779210 100644 (file)
 #  include "knotificationbackend.h"
 #endif /* HAVE_KDE */
 
 #  include "knotificationbackend.h"
 #endif /* HAVE_KDE */
 
+#ifdef HAVE_SSL
+#  include "sslinfodlg.h"
+#endif
+
 #ifdef HAVE_INDICATEQT
   #include "indicatornotificationbackend.h"
 #endif
 #ifdef HAVE_INDICATEQT
   #include "indicatornotificationbackend.h"
 #endif
@@ -159,6 +163,11 @@ void MainWin::init() {
   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId)));
   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
   connect(Client::coreConnection(), SIGNAL(userAuthenticationRequired(CoreAccount *, bool *, QString)), SLOT(userAuthenticationRequired(CoreAccount *, bool *, QString)));
   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId)));
   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
   connect(Client::coreConnection(), SIGNAL(userAuthenticationRequired(CoreAccount *, bool *, QString)), SLOT(userAuthenticationRequired(CoreAccount *, bool *, QString)));
+  connect(Client::coreConnection(), SIGNAL(handleNoSslInClient(bool*)), SLOT(handleNoSslInClient(bool *)));
+  connect(Client::coreConnection(), SIGNAL(handleNoSslInCore(bool*)), SLOT(handleNoSslInCore(bool *)));
+#ifdef HAVE_SSL
+  connect(Client::coreConnection(), SIGNAL(handleSslErrors(const QSslSocket *, bool *, bool *)), SLOT(handleSslErrors(const QSslSocket *, bool *, bool *)));
+#endif
 
   // Setup Dock Areas
   setDockNestingEnabled(true);
 
   // Setup Dock Areas
   setDockNestingEnabled(true);
@@ -832,6 +841,70 @@ void MainWin::startInternalCore() {
 
 }
 
 
 }
 
+void MainWin::userAuthenticationRequired(CoreAccount *account, bool *valid, const QString &errorMessage) {
+  Q_UNUSED(errorMessage)
+  CoreConnectAuthDlg dlg(account, this);
+  *valid = (dlg.exec() == QDialog::Accepted);
+}
+
+void MainWin::handleNoSslInClient(bool *accepted) {
+  QMessageBox box(QMessageBox::Warning, tr("Unencrypted Connection"), tr("<b>Your client does not support SSL encryption</b>"),
+                  QMessageBox::Ignore|QMessageBox::Cancel, this);
+  box.setInformativeText(tr("Sensitive data, like passwords, will be transmitted unencrypted to your Quassel core."));
+  box.setDefaultButton(QMessageBox::Ignore);
+  *accepted = box.exec() == QMessageBox::Ignore;
+}
+
+void MainWin::handleNoSslInCore(bool *accepted) {
+  QMessageBox box(QMessageBox::Warning, tr("Unencrypted Connection"), tr("<b>Your core does not support SSL encryption</b>"),
+                  QMessageBox::Ignore|QMessageBox::Cancel, this);
+  box.setInformativeText(tr("Sensitive data, like passwords, will be transmitted unencrypted to your Quassel core."));
+  box.setDefaultButton(QMessageBox::Ignore);
+  *accepted = box.exec() == QMessageBox::Ignore;
+
+}
+
+#ifdef HAVE_SSL
+
+void MainWin::handleSslErrors(const QSslSocket *socket, bool *accepted, bool *permanently) {
+  QString errorString = "<ul>";
+  foreach(const QSslError error, socket->sslErrors())
+    errorString += QString("<li>%1</li>").arg(error.errorString());
+  errorString += "</ul>";
+
+  QMessageBox box(QMessageBox::Warning,
+                  tr("Untrusted Security Certificate"),
+                  tr("<b>The SSL certificate provided by the core at %1 is untrusted for the following reasons:</b>").arg(socket->peerName()),
+                  QMessageBox::Cancel, this);
+  box.setInformativeText(errorString);
+  box.addButton(tr("Continue"), QMessageBox::AcceptRole);
+  box.setDefaultButton(box.addButton(tr("Show Certificate"), QMessageBox::HelpRole));
+
+  QMessageBox::ButtonRole role;
+  do {
+    box.exec();
+    role = box.buttonRole(box.clickedButton());
+    if(role == QMessageBox::HelpRole) {
+      SslInfoDlg dlg(socket, this);
+      dlg.exec();
+    }
+  } while(role == QMessageBox::HelpRole);
+
+  *accepted = role == QMessageBox::AcceptRole;
+  if(*accepted) {
+    QMessageBox box2(QMessageBox::Warning,
+                     tr("Untrusted Security Certificate"),
+                     tr("Would you like to accept this certificate forever without being prompted?"),
+                     0, this);
+    box2.setDefaultButton(box2.addButton(tr("Current Session Only"), QMessageBox::NoRole));
+    box2.addButton(tr("Forever"), QMessageBox::YesRole);
+    box2.exec();
+    *permanently =  box2.buttonRole(box2.clickedButton()) == QMessageBox::YesRole;
+  }
+}
+
+#endif /* HAVE_SSL */
+
 void MainWin::showCoreConnectionDlg() {
   CoreConnectDlg dlg(this);
   if(dlg.exec() == QDialog::Accepted) {
 void MainWin::showCoreConnectionDlg() {
   CoreConnectDlg dlg(this);
   if(dlg.exec() == QDialog::Accepted) {
@@ -841,12 +914,6 @@ void MainWin::showCoreConnectionDlg() {
   }
 }
 
   }
 }
 
-void MainWin::userAuthenticationRequired(CoreAccount *account, bool *valid, const QString &errorMessage) {
-  Q_UNUSED(errorMessage)
-  CoreConnectAuthDlg dlg(account, this);
-  *valid = (dlg.exec() == QDialog::Accepted);
-}
-
 void MainWin::showChannelList(NetworkId netId) {
   ChannelListDlg *channelListDlg = new ChannelListDlg();
 
 void MainWin::showChannelList(NetworkId netId) {
   ChannelListDlg *channelListDlg = new ChannelListDlg();
 
index d14a7c6..5f15fa5 100644 (file)
@@ -119,8 +119,6 @@ class MainWin
     void messagesInserted(const QModelIndex &parent, int start, int end);
     void showAboutDlg();
     void showChannelList(NetworkId netId = NetworkId());
     void messagesInserted(const QModelIndex &parent, int start, int end);
     void showAboutDlg();
     void showChannelList(NetworkId netId = NetworkId());
-    void startInternalCore();
-    void userAuthenticationRequired(CoreAccount *, bool *valid, const QString &errorMessage);
     void showCoreConnectionDlg();
     void showCoreInfoDlg();
     void showAwayLog();
     void showCoreConnectionDlg();
     void showCoreInfoDlg();
     void showAwayLog();
@@ -130,6 +128,14 @@ class MainWin
 #ifdef HAVE_KDE
     void showShortcutsDlg();
 #endif
 #ifdef HAVE_KDE
     void showShortcutsDlg();
 #endif
+    void startInternalCore();
+    void userAuthenticationRequired(CoreAccount *, bool *valid, const QString &errorMessage);
+    void handleNoSslInClient(bool *accepted);
+    void handleNoSslInCore(bool *accepted);
+#ifdef HAVE_SSL
+    void handleSslErrors(const QSslSocket *socket, bool *accepted, bool *permanently);
+#endif
+
     void on_actionConfigureNetworks_triggered();
     void on_actionConfigureViews_triggered();
     void on_actionLockLayout_toggled(bool lock);
     void on_actionConfigureNetworks_triggered();
     void on_actionConfigureViews_triggered();
     void on_actionLockLayout_toggled(bool lock);