From 45e7860b2c4740c23adc53f6798f6c2ecd19560e Mon Sep 17 00:00:00 2001 From: Tremolo4 Date: Sun, 23 Jun 2019 05:14:39 +0200 Subject: [PATCH] TLS: use fullchain file if available. According to RFC 5246, nginx docs etc. we can expect the server certificate (leaf) to be the first one in the fullchain.pem file. This is indeed the case for all Let's Encrypt certs that I've seen so far. Also adjust mechanism that checks for updated certificates: now consider the most recent timestamp of any supported .pem file. --- syncplay/protocols.py | 2 +- syncplay/server.py | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/syncplay/protocols.py b/syncplay/protocols.py index 3bf0902..ac81b1e 100755 --- a/syncplay/protocols.py +++ b/syncplay/protocols.py @@ -688,7 +688,7 @@ class SyncServerProtocol(JSONCommandProtocol): if "send" in inquiry: if not self.isLogged() and self._factory.serverAcceptsTLS: lastEditCertTime = self._factory.checkLastEditCertTime() - if lastEditCertTime is not None and lastEditCertTime != self._factory.lastEditCertTime: + if lastEditCertTime and lastEditCertTime != self._factory.lastEditCertTime: self._factory.updateTLSContextFactory() if self._factory.options is not None: self.sendTLS({"startTLS": "true"}) diff --git a/syncplay/server.py b/syncplay/server.py index 7bf50f5..bdcbfb6 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -235,15 +235,30 @@ class SyncFactory(Factory): def _allowTLSconnections(self, path): try: - privKey = open(path+'/privkey.pem', 'rt').read() - certif = open(path+'/cert.pem', 'rt').read() - chain = open(path+'/chain.pem', 'rt').read() + # load private key + with open(os.path.join(path, 'privkey.pem'), 'rt') as f: + print("TLS: Loading privkey.pem ...") + privKeyPySSL = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read()) - self.lastEditCertTime = os.path.getmtime(path+'/cert.pem') + # fullchain.pem is expected to contain the server cert as the first + # followed by any required intermediate certs + # if fullchain.pem doesn't exist, we expect cert.pem to contain the server cert + # and chain.pem to contain any additional required intermediate certs - privKeyPySSL = crypto.load_privatekey(crypto.FILETYPE_PEM, privKey) - certifPySSL = crypto.load_certificate(crypto.FILETYPE_PEM, certif) - chainPySSL = [crypto.load_certificate(crypto.FILETYPE_PEM, cert) for cert in self._splitPEMCerts(chain)] + fullchainExists = os.path.isfile(os.path.join(path, 'fullchain.pem')) + chainFile = 'fullchain.pem' if fullchainExists else 'chain.pem' + with open(os.path.join(path, chainFile), 'rt') as f: + print("TLS: Loading %s ..." % chainFile) + chainPySSL = [crypto.load_certificate(crypto.FILETYPE_PEM, cert) for cert in self._splitPEMCerts(f.read())] + + if fullchainExists: + certifPySSL = chainPySSL.pop(0) + else: + with open(os.path.join(path, 'cert.pem'), 'rt') as f: + print("TLS: Loading cert.pem ...") + certifPySSL = crypto.load_certificate(crypto.FILETYPE_PEM, f.read()) + + self.lastEditCertTime = self.checkLastEditCertTime() cipherListString = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:"\ "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:"\ @@ -272,10 +287,14 @@ class SyncFactory(Factory): print("TLS support is not enabled.") def checkLastEditCertTime(self): + outTime = 0 try: - outTime = os.path.getmtime(self.certPath+'/cert.pem') + for f in ['privkey.pem', 'cert.pem', 'chain.pem', 'fullchain.pem']: + path = os.path.join(self.certPath, f) + if os.path.isfile(path): + outTime = max(outTime, os.path.getmtime(path)) except: - outTime = None + pass return outTime def updateTLSContextFactory(self):