diff --git a/offlineimap.conf b/offlineimap.conf index fccceab..fabb52b 100644 --- a/offlineimap.conf +++ b/offlineimap.conf @@ -325,6 +325,13 @@ ssl = yes #cert_fingerprint = +# SSL version (optional) +# It is best to leave this unset, in which case the correct version will be +# automatically detected. In rare cases, it may be necessary to specify a +# particular version from: tls1, ssl2, ssl3, ssl23 (SSLv2 or SSLv3) + +# sslversion = ssl23 + # Specify the port. If not specified, use a default port. # remoteport = 993 diff --git a/offlineimap/imaplib2.py b/offlineimap/imaplib2.py index 8138d6c..b7e0d22 100644 --- a/offlineimap/imaplib2.py +++ b/offlineimap/imaplib2.py @@ -39,7 +39,8 @@ Timeout handling further improved by Ethan Glasser-Camp Dece Time2Internaldate() patch to match RFC2060 specification of English month names from bugs.python.org/issue11024 March 2011. starttls() bug fixed with the help of Sebastian Spaeth April 2011. Threads now set the "daemon" flag (suggested by offlineimap-project) April 2011. -Single quoting introduced with the help of Vladimir Marek August 2011.""" +Single quoting introduced with the help of Vladimir Marek August 2011. +Support for specifying SSL version by Ryan Kavanagh July 2013.""" __author__ = "Piers Lauder " __URL__ = "http://imaplib2.sourceforge.net" __license__ = "Python License" @@ -460,7 +461,20 @@ class IMAP4(object): cert_reqs = ssl.CERT_REQUIRED else: cert_reqs = ssl.CERT_NONE - self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ca_certs=self.ca_certs, cert_reqs=cert_reqs) + + if self.ssl_version == "tls1": + ssl_version = ssl.PROTOCOL_TLSv1 + elif self.ssl_version == "ssl2": + ssl_version = ssl.PROTOCOL_SSLv2 + elif self.ssl_version == "ssl3": + ssl_version = ssl.PROTOCOL_SSLv3 + elif self.ssl_version == "ssl23" or self.ssl_version is None: + ssl_version = ssl.PROTOCOL_SSLv23 + else: + raise socket.sslerror("Invalid SSL version requested: %s", + self.ssl_version) + + self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, ca_certs=self.ca_certs, cert_reqs=cert_reqs, ssl_version=ssl_version) ssl_exc = ssl.SSLError self.read_fd = self.sock.fileno() except ImportError: @@ -1040,8 +1054,8 @@ class IMAP4(object): return self._simple_command(name, sort_criteria, charset, *search_criteria, **kw) - def starttls(self, keyfile=None, certfile=None, ca_certs=None, cert_verify_cb=None, **kw): - """(typ, [data]) = starttls(keyfile=None, certfile=None, ca_certs=None, cert_verify_cb=None) + def starttls(self, keyfile=None, certfile=None, ca_certs=None, cert_verify_cb=None, ssl_version="ssl23", **kw): + """(typ, [data]) = starttls(keyfile=None, certfile=None, ca_certs=None, cert_verify_cb=None, ssl_version="ssl23") Start TLS negotiation as per RFC 2595.""" name = 'STARTTLS' @@ -1076,6 +1090,7 @@ class IMAP4(object): self.certfile = certfile self.ca_certs = ca_certs self.cert_verify_cb = cert_verify_cb + self.ssl_version = ssl_version try: self.ssl_wrap_socket() @@ -1972,7 +1987,7 @@ class IMAP4_SSL(IMAP4): """IMAP4 client class over SSL connection Instantiate with: - IMAP4_SSL(host=None, port=None, keyfile=None, certfile=None, debug=None, debug_file=None, identifier=None, timeout=None) + IMAP4_SSL(host=None, port=None, keyfile=None, certfile=None, ssl_version="ssl23", debug=None, debug_file=None, identifier=None, timeout=None) host - host's name (default: localhost); port - port number (default: standard IMAP4 SSL port); @@ -1980,6 +1995,7 @@ class IMAP4_SSL(IMAP4): certfile - PEM formatted certificate chain file (default: None); ca_certs - PEM formatted certificate chain file used to validate server certificates (default: None); cert_verify_cb - function to verify authenticity of server certificates (default: None); + ssl_version - SSL version to use (default: "ssl23", choose from: "tls1","ssl2","ssl3","ssl23"); debug - debug level (default: 0 - no debug); debug_file - debug stream (default: sys.stderr); identifier - thread identifier prefix (default: host); @@ -1990,11 +2006,12 @@ class IMAP4_SSL(IMAP4): """ - def __init__(self, host=None, port=None, keyfile=None, certfile=None, ca_certs=None, cert_verify_cb=None, debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None): + def __init__(self, host=None, port=None, keyfile=None, certfile=None, ca_certs=None, cert_verify_cb=None, ssl_version="ssl23", debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None): self.keyfile = keyfile self.certfile = certfile self.ca_certs = ca_certs self.cert_verify_cb = cert_verify_cb + self.ssl_version = ssl_version IMAP4.__init__(self, host, port, debug, debug_file, identifier, timeout, debug_buf_lvl) diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py index 22c5c16..c8f70d6 100644 --- a/offlineimap/imapserver.py +++ b/offlineimap/imapserver.py @@ -65,6 +65,7 @@ class IMAPServer: self.sslclientcert = repos.getsslclientcert() self.sslclientkey = repos.getsslclientkey() self.sslcacertfile = repos.getsslcacertfile() + self.sslversion = repos.getsslversion() if self.sslcacertfile is None: self.verifycert = None # disable cert verification self.delim = None @@ -211,6 +212,7 @@ class IMAPServer: self.sslclientcert, self.sslcacertfile, self.verifycert, + self.sslversion, timeout=socket.getdefaulttimeout(), fingerprint=fingerprint ) diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index be8c858..7ccabfa 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -172,6 +172,9 @@ class IMAPRepository(BaseRepository): % (self.name, cacertfile)) return cacertfile + def getsslversion(self): + return self.getconf('ssl_version', None) + def get_ssl_fingerprint(self): return self.getconf('cert_fingerprint', None)