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')