mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
calibre-smtp: Verify relay server TLS certificates by default. New option --dont-verify-server-sertificate to restore old behavior.
This commit is contained in:
parent
0fd8a9aee9
commit
37582afb76
@ -129,7 +129,7 @@ def sendmail_direct(from_, to, msg, timeout, localhost, verbose,
|
|||||||
|
|
||||||
def sendmail(msg, from_, to, localhost=None, verbose=0, timeout=None,
|
def sendmail(msg, from_, to, localhost=None, verbose=0, timeout=None,
|
||||||
relay=None, username=None, password=None, encryption='TLS',
|
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:
|
if relay is None:
|
||||||
for x in to:
|
for x in to:
|
||||||
return sendmail_direct(from_, x, msg, timeout, localhost, verbose)
|
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
|
port = 25 if encryption != 'SSL' else 465
|
||||||
s.connect(relay, port)
|
s.connect(relay, port)
|
||||||
if encryption == 'TLS':
|
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()
|
s.ehlo()
|
||||||
if username is not None and password is not None:
|
if username is not None and password is not None:
|
||||||
if encryption == 'SSL':
|
if encryption == 'SSL':
|
||||||
@ -205,6 +209,14 @@ are only used in the SMTP negotiation, the message headers are not modified.
|
|||||||
choices=['TLS', 'SSL', 'NONE'],
|
choices=['TLS', 'SSL', 'NONE'],
|
||||||
help=_('Encryption method to use when connecting to relay. Choices are '
|
help=_('Encryption method to use when connecting to relay. Choices are '
|
||||||
'TLS, SSL and NONE. Default is TLS. WARNING: Choosing NONE is highly insecure'))
|
'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 '
|
parser.add_option('-o', '--outbox', help=_('Path to maildir folder to store '
|
||||||
'failed email messages in.'))
|
'failed email messages in.'))
|
||||||
parser.add_option('-f', '--fork', default=False, action='store_true',
|
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,
|
sendmail(msg, efrom, eto, localhost=opts.localhost, verbose=opts.verbose,
|
||||||
timeout=opts.timeout, relay=opts.relay, username=opts.username,
|
timeout=opts.timeout, relay=opts.relay, username=opts.username,
|
||||||
password=opts.password, port=opts.port,
|
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:
|
except:
|
||||||
if outbox is not None:
|
if outbox is not None:
|
||||||
outbox.add(msg)
|
outbox.add(msg)
|
||||||
|
@ -265,6 +265,7 @@ class SMTP:
|
|||||||
sys.stderr. You should pass in a print function of your own to control
|
sys.stderr. You should pass in a print function of your own to control
|
||||||
where debug output is written.
|
where debug output is written.
|
||||||
"""
|
"""
|
||||||
|
self._host = host
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.debug = debug_to
|
self.debug = debug_to
|
||||||
self.esmtp_features = {}
|
self.esmtp_features = {}
|
||||||
@ -331,6 +332,7 @@ class SMTP:
|
|||||||
port = self.default_port
|
port = self.default_port
|
||||||
if self.debuglevel > 0:
|
if self.debuglevel > 0:
|
||||||
self.debug('connect:', (host, port))
|
self.debug('connect:', (host, port))
|
||||||
|
self._host = host
|
||||||
self.sock = self._get_socket(host, port, self.timeout)
|
self.sock = self._get_socket(host, port, self.timeout)
|
||||||
(code, msg) = self.getreply()
|
(code, msg) = self.getreply()
|
||||||
if self.debuglevel > 0:
|
if self.debuglevel > 0:
|
||||||
@ -385,8 +387,7 @@ class SMTP:
|
|||||||
line = self.file.readline(_MAXLINE + 1)
|
line = self.file.readline(_MAXLINE + 1)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
self.close()
|
self.close()
|
||||||
raise SMTPServerDisconnected("Connection unexpectedly closed: " +
|
raise SMTPServerDisconnected("Connection unexpectedly closed: " + str(e))
|
||||||
str(e))
|
|
||||||
if line == '':
|
if line == '':
|
||||||
self.close()
|
self.close()
|
||||||
raise SMTPServerDisconnected("Connection unexpectedly closed")
|
raise SMTPServerDisconnected("Connection unexpectedly closed")
|
||||||
@ -647,7 +648,7 @@ class SMTP:
|
|||||||
raise SMTPAuthenticationError(code, resp)
|
raise SMTPAuthenticationError(code, resp)
|
||||||
return (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.
|
"""Puts the connection to the SMTP server into TLS mode.
|
||||||
|
|
||||||
If there has been no previous EHLO or HELO command this session, this
|
If there has been no previous EHLO or HELO command this session, this
|
||||||
@ -671,7 +672,10 @@ class SMTP:
|
|||||||
if resp == 220:
|
if resp == 220:
|
||||||
if not _have_ssl:
|
if not _have_ssl:
|
||||||
raise RuntimeError("No SSL support included in this Python")
|
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)
|
self.file = SSLFakeFile(self.sock)
|
||||||
# RFC 3207:
|
# RFC 3207:
|
||||||
# The client MUST discard any knowledge obtained from
|
# The client MUST discard any knowledge obtained from
|
||||||
|
Loading…
x
Reference in New Issue
Block a user