From a906b6a98e6a7963dea3ecc4bb3b51e16c65b3b8 Mon Sep 17 00:00:00 2001 From: Dpeta Date: Fri, 2 Sep 2022 05:34:37 +0200 Subject: [PATCH] Validate certificates when using SSL/TLS --- CHANGELOG.md | 5 +++++ TODO.md | 2 +- irc.py | 12 ++++++++++-- oyoyo/client.py | 15 ++++++++++----- pesterchum.py | 25 +++++++++++++++++++++++-- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f65cf..07aeb73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ ### Added - Support for color via IRCv3 metadata draft. + - Prompt for connecting anyway when certificate validation fails. + +### Changed + - Reenabled server certificate validation when using SSL. + - Uses the system's local certificates, so might fail on some installations. ### Fixed - Error when setting quirk with PyQt5. diff --git a/TODO.md b/TODO.md index 1579217..0ecff04 100644 --- a/TODO.md +++ b/TODO.md @@ -14,4 +14,4 @@ - Console is hopelessly broken, it'd be easier to make a list of what commands *don't* cause it to crash. Does anyone use this? ## CHANGE - - When everything has been tested and more people have compatible versions, switch Pesterchum over to using metadata and message tags by default instead of wraping its protocol in PRIVMSG. The current method should stay availible for compatibility. + - When everything has been tested and more people have compatible versions, switch Pesterchum over to using metadata and message tags by default instead of wrapping its protocol in PRIVMSG. The current method should stay available for compatibility. diff --git a/irc.py b/irc.py index 9d0d3fe..67dec17 100644 --- a/irc.py +++ b/irc.py @@ -4,6 +4,7 @@ import socket import random import time import json +import ssl try: from PyQt6 import QtCore, QtGui @@ -40,12 +41,13 @@ QString = str # logging.basicConfig(level=logging.WARNING) class PesterIRC(QtCore.QThread): - def __init__(self, config, window): + def __init__(self, config, window, verify_hostname=True): QtCore.QThread.__init__(self) self.mainwindow = window self.config = config self.unresponsive = False self.registeredIRC = False + self.verify_hostname = verify_hostname self.metadata_supported = False self.stopIRC = None self.NickServ = services.NickServ() @@ -61,7 +63,12 @@ class PesterIRC(QtCore.QThread): timeout=120) self.cli.command_handler.parent = self self.cli.command_handler.mainwindow = self.mainwindow - self.cli.connect() + try: + self.cli.connect(self.verify_hostname) + except ssl.SSLCertVerificationError as e: + # Ask if users wants to connect anyway + self.askToConnect.emit(e) + raise e self.conn = self.cli.conn() def run(self): try: @@ -395,6 +402,7 @@ class PesterIRC(QtCore.QThread): chanInviteOnly = QtCore.pyqtSignal('QString') modesUpdated = QtCore.pyqtSignal('QString', 'QString') connected = QtCore.pyqtSignal() + askToConnect = QtCore.pyqtSignal(Exception) userPresentUpdate = QtCore.pyqtSignal('QString', 'QString', 'QString') cannotSendToChan = QtCore.pyqtSignal('QString', 'QString') diff --git a/oyoyo/client.py b/oyoyo/client.py index deb39f1..1563aec 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -185,17 +185,17 @@ class IRCClient: #raise se self._end = True # This ok? - def connect(self): + def connect(self, verify_hostname=True): """ initiates the connection to the server set in self.host:self.port """ PchumLog.info('connecting to %s:%s' % (self.host, self.port)) if self.ssl == True: context = ssl.create_default_context() - # Checking relies on the system - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - + if verify_hostname == False: + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + bare_sock = socket.create_connection((self.host, self.port)) self.socket = context.wrap_socket(bare_sock, server_hostname=self.host, @@ -208,6 +208,11 @@ class IRCClient: select.select([self.socket], [], []) except ssl.SSLWantWriteError: select.select([], [self.socket], []) + except ssl.SSLCertVerificationError as e: + # Disconnect for now + self.socket.close() + bare_sock.close() + raise e PchumLog.info("secure sockets version is %s" % self.socket.version()) diff --git a/pesterchum.py b/pesterchum.py index f1538b3..4d7b04b 100644 --- a/pesterchum.py +++ b/pesterchum.py @@ -3694,6 +3694,26 @@ class PesterWindow(MovingWindow): self.chooseServerWidged.show() self.chooseServerWidged.setFocus() + @QtCore.pyqtSlot(Exception) + def connectAnyway(self, e): + # Prompt user to connect anyway + msgbox = QtWidgets.QMessageBox() + msgbox.setStyleSheet("QMessageBox{ %s }" + % self.theme["main/defaultwindow/style"]) + msgbox.setIcon(QtWidgets.QMessageBox.Icon.Warning) + msgbox.setText("Server certificate validation failed") + msgbox.setInformativeText("Reason: \"%s (%s)\"" % (e.verify_message, e.verify_code) + + "\n\nConnect anyway?") + msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Yes + | QtWidgets.QMessageBox.StandardButton.No) + msgbox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.No) + ret = msgbox.exec() + if ret == QtWidgets.QMessageBox.StandardButton.Yes: + self.parent.restartIRC(verify_hostname=False) + else: + return False + + pcUpdate = QtCore.pyqtSignal('QString', 'QString') closeToTraySignal = QtCore.pyqtSignal() newConvoStarted = QtCore.pyqtSignal('QString', bool, name="newConvoStarted") @@ -3982,6 +4002,7 @@ class MainProgram(QtCore.QObject): (widget.disconnectIRC, irc.disconnectIRC), # Main window --> IRC (irc.connected, widget.connected), + (irc.askToConnect, widget.connectAnyway), (irc.moodUpdated, widget.updateMoodSlot), (irc.colorUpdated, widget.updateColorSlot), (irc.messageReceived, widget.deliverMessage), @@ -4069,7 +4090,7 @@ class MainProgram(QtCore.QObject): else: self.restartIRC() @QtCore.pyqtSlot() - def restartIRC(self): + def restartIRC(self, verify_hostname=True): if hasattr(self, 'irc') and self.irc: self.disconnectWidgets(self.irc, self.widget) stop = self.irc.stopIRC @@ -4077,7 +4098,7 @@ class MainProgram(QtCore.QObject): else: stop = None if stop is None: - self.irc = PesterIRC(self.widget.config, self.widget) + self.irc = PesterIRC(self.widget.config, self.widget, verify_hostname=verify_hostname) self.connectWidgets(self.irc, self.widget) self.irc.start() if self.attempts == 1: