Hi,
It seems that smtplib doesn't check if a certificate is valid (signed by
a trusted CA).
For my personal usage, I patched the starttls code in git-multimail:
only for starttls with smtplib.
This patch is inspired from
https://github.com/graingert/secure-smtplib/blob/master/src/secure_smtplib/__init__.py
It could be easy to add support cert check in for smtps (see
secure_smtplib).
This patch was tested only on git-multimail (v1.2)
It introduces two new options:
- multimailhook.smtpcheckcert (default false)
- multimailhook.smtpcacerts (default
/etc/ssl/certs/ca-certificates.crt)
Best regards,
Simon P.
diff --git a/git-multimail/git_multimail.py b/git-multimail/git_multimail.py
index fae5c91..b49ed9d 100755
--- a/git-multimail/git_multimail.py
+++ b/git-multimail/git_multimail.py
@@ -57,6 +57,7 @@ import subprocess
import shlex
import optparse
import smtplib
+import ssl
import time
import cgi
@@ -1945,6 +1946,7 @@ class SMTPMailer(Mailer):
smtpservertimeout=10.0, smtpserverdebuglevel=0,
smtpencryption='none',
smtpuser='', smtppass='',
+ smtpcacerts='/etc/ssl/certs/ca-certificates.crt',smtpcheckcert=False
):
if not envelopesender:
sys.stderr.write(
@@ -1974,13 +1976,43 @@ class SMTPMailer(Mailer):
if self.security == 'none':
self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout)
elif self.security == 'ssl':
+ if smtpcheckcert:
+ msg = "Checking certificate is not supported for ssl, prefer starttls"
+ raise smtplib.SMTPException(msg)
self.smtp = call(smtplib.SMTP_SSL, self.smtpserver, timeout=self.smtpservertimeout)
elif self.security == 'tls':
if ':' not in self.smtpserver:
self.smtpserver += ':587' # default port for TLS
self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout)
- self.smtp.ehlo()
- self.smtp.starttls()
+ if smtpcheckcert:
+ # inspired form:
+ # https://github.com/graingert/secure-smtplib/blob/master/src/secure_smtplib/__init__.py
+ # but add the path to trusted ca, and force ceritficate verification.
+ self.smtp.ehlo_or_helo_if_needed()
+ if not self.smtp.has_extn("starttls"):
+ msg = "STARTTLS extension not supported by server"
+ raise smtplib.SMTPException(msg)
+ (resp, reply) = self.smtp.docmd("STARTTLS")
+ if resp == 220:
+ self.smtp.sock = ssl.wrap_socket(
+ self.smtp.sock,
+ ca_certs=smtpcacerts,
+ cert_reqs=ssl.CERT_REQUIRED
+ )
+ if not hasattr(self.smtp.sock, "read"):
+ # using httplib.FakeSocket with Python 2.5.x or earlier
+ self.smtp.sock.read = self.smtp.sock.recv
+ self.smtp.file = smtplib.SSLFakeFile(self.smtp.sock)
+ self.smtp.helo_resp = None
+ self.smtp.ehlo_resp = None
+ self.smtp.esmtp_features = {}
+ self.smtp.does_esmtp = 0
+ else:
+ msg = "Wrong answer to the STARTTLS command"
+ raise smtplib.SMTPException(msg)
+ else:
+ self.smtp.ehlo()
+ self.smtp.starttls()
self.smtp.ehlo()
else:
sys.stdout.write('*** Error: Control reached an invalid option. ***')
@@ -3500,6 +3532,8 @@ def choose_mailer(config, environment):
smtpencryption = config.get('smtpencryption', default='none')
smtpuser = config.get('smtpuser', default='')
smtppass = config.get('smtppass', default='')
+ smtpcacerts = config.get('smtpcacerts', default='/etc/ssl/certs/ca-certificates.crt')
+ smtpcheckcert = config.get_bool('smtpcheckcert', default='false')
mailer = SMTPMailer(
envelopesender=(environment.get_sender() or environment.get_fromaddr()),
smtpserver=smtpserver, smtpservertimeout=smtpservertimeout,
@@ -3507,6 +3541,8 @@ def choose_mailer(config, environment):
smtpencryption=smtpencryption,
smtpuser=smtpuser,
smtppass=smtppass,
+ smtpcacerts=smtpcacerts,
+ smtpcheckcert=smtpcheckcert
)
elif mailer == 'sendmail':
command = config.get('sendmailcommand')