calibre-smtp: Verify relay server TLS certificates by default. New option --dont-verify-server-sertificate to restore old behavior.

This commit is contained in:
Kovid Goyal 2018-06-26 10:14:04 +05:30
parent 0fd8a9aee9
commit 37582afb76
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 23 additions and 7 deletions

View File

@ -129,7 +129,7 @@ def sendmail_direct(from_, to, msg, timeout, localhost, verbose,
def sendmail(msg, from_, to, localhost=None, verbose=0, timeout=None,
relay=None, username=None, password=None, encryption='TLS',
port=-1, debug_output=None):
port=-1, debug_output=None, verify_server_cert=False, cafile=None):
if relay is None:
for x in to:
return sendmail_direct(from_, x, msg, timeout, localhost, verbose)
@ -146,7 +146,11 @@ def sendmail(msg, from_, to, localhost=None, verbose=0, timeout=None,
port = 25 if encryption != 'SSL' else 465
s.connect(relay, port)
if encryption == 'TLS':
s.starttls()
context = None
if verify_server_cert:
import ssl
context = ssl.create_default_context(cafile=cafile)
s.starttls(context=context)
s.ehlo()
if username is not None and password is not None:
if encryption == 'SSL':
@ -205,6 +209,14 @@ are only used in the SMTP negotiation, the message headers are not modified.
choices=['TLS', 'SSL', 'NONE'],
help=_('Encryption method to use when connecting to relay. Choices are '
'TLS, SSL and NONE. Default is TLS. WARNING: Choosing NONE is highly insecure'))
r('--dont-verify-server-certificate', help=_(
'Do not verify the server certificate when connecting using TLS. This used'
' to be the default behavior in calibre versions before 3.27. If you are using'
' a relay with a self-signed or otherwise invalid certificate, you can use this option to restore'
' the pre 3.27 behavior'))
r('--cafile', help=_(
'Path to a file of concatenated CA certificates in PEM format, used to verify the'
' server certificate when using TLS. By default, the system CA certificates are used.'))
parser.add_option('-o', '--outbox', help=_('Path to maildir folder to store '
'failed email messages in.'))
parser.add_option('-f', '--fork', default=False, action='store_true',
@ -285,7 +297,7 @@ def main(args=sys.argv):
sendmail(msg, efrom, eto, localhost=opts.localhost, verbose=opts.verbose,
timeout=opts.timeout, relay=opts.relay, username=opts.username,
password=opts.password, port=opts.port,
encryption=opts.encryption_method)
encryption=opts.encryption_method, verify_server_cert=not opts.dont_verify_server_certificate, cafile=opts.cafile)
except:
if outbox is not None:
outbox.add(msg)

View File

@ -265,6 +265,7 @@ class SMTP:
sys.stderr. You should pass in a print function of your own to control
where debug output is written.
"""
self._host = host
self.timeout = timeout
self.debug = debug_to
self.esmtp_features = {}
@ -331,6 +332,7 @@ class SMTP:
port = self.default_port
if self.debuglevel > 0:
self.debug('connect:', (host, port))
self._host = host
self.sock = self._get_socket(host, port, self.timeout)
(code, msg) = self.getreply()
if self.debuglevel > 0:
@ -385,8 +387,7 @@ class SMTP:
line = self.file.readline(_MAXLINE + 1)
except socket.error as e:
self.close()
raise SMTPServerDisconnected("Connection unexpectedly closed: " +
str(e))
raise SMTPServerDisconnected("Connection unexpectedly closed: " + str(e))
if line == '':
self.close()
raise SMTPServerDisconnected("Connection unexpectedly closed")
@ -647,7 +648,7 @@ class SMTP:
raise SMTPAuthenticationError(code, resp)
return (code, resp)
def starttls(self, keyfile=None, certfile=None):
def starttls(self, context=None):
"""Puts the connection to the SMTP server into TLS mode.
If there has been no previous EHLO or HELO command this session, this
@ -671,7 +672,10 @@ class SMTP:
if resp == 220:
if not _have_ssl:
raise RuntimeError("No SSL support included in this Python")
self.sock = ssl.wrap_socket(self.sock, keyfile, certfile)
if context is None:
self.sock = ssl.wrap_socket(self.sock)
else:
self.sock = context.wrap_socket(self.sock, server_hostname=self._host)
self.file = SSLFakeFile(self.sock)
# RFC 3207:
# The client MUST discard any knowledge obtained from