Validate certificates when using SSL/TLS

This commit is contained in:
Dpeta 2022-09-02 05:34:37 +02:00
parent a3d047350b
commit a906b6a98e
5 changed files with 49 additions and 10 deletions

View file

@ -5,6 +5,11 @@
### Added ### Added
- Support for color via IRCv3 metadata draft. - 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 ### Fixed
- Error when setting quirk with PyQt5. - Error when setting quirk with PyQt5.

View file

@ -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? - 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 ## 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.

12
irc.py
View file

@ -4,6 +4,7 @@ import socket
import random import random
import time import time
import json import json
import ssl
try: try:
from PyQt6 import QtCore, QtGui from PyQt6 import QtCore, QtGui
@ -40,12 +41,13 @@ QString = str
# logging.basicConfig(level=logging.WARNING) # logging.basicConfig(level=logging.WARNING)
class PesterIRC(QtCore.QThread): class PesterIRC(QtCore.QThread):
def __init__(self, config, window): def __init__(self, config, window, verify_hostname=True):
QtCore.QThread.__init__(self) QtCore.QThread.__init__(self)
self.mainwindow = window self.mainwindow = window
self.config = config self.config = config
self.unresponsive = False self.unresponsive = False
self.registeredIRC = False self.registeredIRC = False
self.verify_hostname = verify_hostname
self.metadata_supported = False self.metadata_supported = False
self.stopIRC = None self.stopIRC = None
self.NickServ = services.NickServ() self.NickServ = services.NickServ()
@ -61,7 +63,12 @@ class PesterIRC(QtCore.QThread):
timeout=120) timeout=120)
self.cli.command_handler.parent = self self.cli.command_handler.parent = self
self.cli.command_handler.mainwindow = self.mainwindow 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() self.conn = self.cli.conn()
def run(self): def run(self):
try: try:
@ -395,6 +402,7 @@ class PesterIRC(QtCore.QThread):
chanInviteOnly = QtCore.pyqtSignal('QString') chanInviteOnly = QtCore.pyqtSignal('QString')
modesUpdated = QtCore.pyqtSignal('QString', 'QString') modesUpdated = QtCore.pyqtSignal('QString', 'QString')
connected = QtCore.pyqtSignal() connected = QtCore.pyqtSignal()
askToConnect = QtCore.pyqtSignal(Exception)
userPresentUpdate = QtCore.pyqtSignal('QString', 'QString', userPresentUpdate = QtCore.pyqtSignal('QString', 'QString',
'QString') 'QString')
cannotSendToChan = QtCore.pyqtSignal('QString', 'QString') cannotSendToChan = QtCore.pyqtSignal('QString', 'QString')

View file

@ -185,17 +185,17 @@ class IRCClient:
#raise se #raise se
self._end = True # This ok? 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 """ initiates the connection to the server set in self.host:self.port
""" """
PchumLog.info('connecting to %s:%s' % (self.host, self.port)) PchumLog.info('connecting to %s:%s' % (self.host, self.port))
if self.ssl == True: if self.ssl == True:
context = ssl.create_default_context() context = ssl.create_default_context()
# Checking relies on the system if verify_hostname == False:
context.check_hostname = False context.check_hostname = False
context.verify_mode = ssl.CERT_NONE context.verify_mode = ssl.CERT_NONE
bare_sock = socket.create_connection((self.host, self.port)) bare_sock = socket.create_connection((self.host, self.port))
self.socket = context.wrap_socket(bare_sock, self.socket = context.wrap_socket(bare_sock,
server_hostname=self.host, server_hostname=self.host,
@ -208,6 +208,11 @@ class IRCClient:
select.select([self.socket], [], []) select.select([self.socket], [], [])
except ssl.SSLWantWriteError: except ssl.SSLWantWriteError:
select.select([], [self.socket], []) 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()) PchumLog.info("secure sockets version is %s" % self.socket.version())

View file

@ -3694,6 +3694,26 @@ class PesterWindow(MovingWindow):
self.chooseServerWidged.show() self.chooseServerWidged.show()
self.chooseServerWidged.setFocus() 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') pcUpdate = QtCore.pyqtSignal('QString', 'QString')
closeToTraySignal = QtCore.pyqtSignal() closeToTraySignal = QtCore.pyqtSignal()
newConvoStarted = QtCore.pyqtSignal('QString', bool, name="newConvoStarted") newConvoStarted = QtCore.pyqtSignal('QString', bool, name="newConvoStarted")
@ -3982,6 +4002,7 @@ class MainProgram(QtCore.QObject):
(widget.disconnectIRC, irc.disconnectIRC), (widget.disconnectIRC, irc.disconnectIRC),
# Main window --> IRC # Main window --> IRC
(irc.connected, widget.connected), (irc.connected, widget.connected),
(irc.askToConnect, widget.connectAnyway),
(irc.moodUpdated, widget.updateMoodSlot), (irc.moodUpdated, widget.updateMoodSlot),
(irc.colorUpdated, widget.updateColorSlot), (irc.colorUpdated, widget.updateColorSlot),
(irc.messageReceived, widget.deliverMessage), (irc.messageReceived, widget.deliverMessage),
@ -4069,7 +4090,7 @@ class MainProgram(QtCore.QObject):
else: else:
self.restartIRC() self.restartIRC()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def restartIRC(self): def restartIRC(self, verify_hostname=True):
if hasattr(self, 'irc') and self.irc: if hasattr(self, 'irc') and self.irc:
self.disconnectWidgets(self.irc, self.widget) self.disconnectWidgets(self.irc, self.widget)
stop = self.irc.stopIRC stop = self.irc.stopIRC
@ -4077,7 +4098,7 @@ class MainProgram(QtCore.QObject):
else: else:
stop = None stop = None
if stop is 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.connectWidgets(self.irc, self.widget)
self.irc.start() self.irc.start()
if self.attempts == 1: if self.attempts == 1: