Bug#603450: Fwd: Due offlineimap absence of certificate validation issue -- Debian BTS#603450
---------- Forwarded message ----------
From: dave b <db.pub.mail@gmail.com>
Date: 1 December 2010 13:59
Subject: Re: Due offlineimap absence of certificate validation issue
-- Debian BTS#603450
To: John Goerzen <jgoerzen@complete.org>
Cc: Jan Lieskovsky <jlieskov@redhat.com>, Christoph Höger
<choeger@cs.tu-berlin.de>
Here have a patch!
This obviously will break connecting to hosts which use a self-signed
certificate.
Perhaps some one else can fix this when they want it fixed ;) ?
I tested using the following config:
# Sample minimal config file. Copy this to ~/.offlineimaprc and edit to
# suit to get started fast.
[general]
accounts = Test
[Account Test]
localrepository = Local
remoterepository = Remote
[Repository Local]
type = Maildir
localfolders = ~/Test
[Repository Remote]
type = IMAP
ssl = yes
remotehost = imap.gmail.com # this should work
#remotehost = 74.125.39.109 # this should fail
#remotehost = $putselfsignedserveraddresshere #this should fail
remoteuser = jgoerzen
For these hosts (listed here) the expected outcome matched the actual
outcome when I tried offlineimap.
diff --git a/offlineimap/imaplibutil.py b/offlineimap/imaplibutil.py
index a60242b..3df92c2 100644
--- a/offlineimap/imaplibutil.py
+++ b/offlineimap/imaplibutil.py
@@ -27,6 +27,7 @@ try:
import ssl
ssl_wrap = ssl.wrap_socket
except ImportError:
+ print e
ssl_wrap = socket.ssl
class IMAP4_Tunnel(IMAP4):
@@ -62,7 +63,7 @@ class IMAP4_Tunnel(IMAP4):
self.infd.close()
self.outfd.close()
self.process.wait()
-
+
class sslwrapper:
def __init__(self, sslsock):
self.sslsock = sslsock
@@ -144,6 +145,27 @@ def new_open(self, host = '', port = IMAP4_PORT):
raise socket.error(last_error)
self.file = self.sock.makefile('rb')
+
+def _verifycert(cert, hostname):
+ '''Verify that cert (in socket.getpeercert() format) matches hostname.
+ CRLs and subjectAltName are not handled.
+
+ Returns error message if any problems are found and None on success.
+ '''
+ if not cert:
+ return ('no certificate received')
+ dnsname = hostname.lower()
+ for s in cert.get('subject', []):
+ key, value = s[0]
+ if key == 'commonName':
+ certname = value.lower()
+ if (certname == dnsname or
+ '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
+ return None
+ return ('certificate is for %s') % certname
+ return ('no commonName found in certificate')
+
+
def new_open_ssl(self, host = '', port = IMAP4_SSL_PORT):
"""Setup connection to remote server on "host:port".
(default: localhost:standard IMAP4 SSL port).
@@ -171,7 +193,10 @@ def new_open_ssl(self, host = '', port = IMAP4_SSL_PORT):
if last_error != 0:
# FIXME
raise socket.error(last_error)
- self.sslobj = ssl_wrap(self.sock, self.keyfile, self.certfile)
+ self.sslobj = ssl_wrap(self.sock, self.keyfile, self.certfile, cert_reqs=ssl.CERT_REQUIRED, ca_certs="/etc/ssl/certs/ca-certificates.crt")
+ msg = _verifycert(self.sslobj.getpeercert(), host)
+ if msg:
+ raise socket.error(('%s certificate error: %s') % (host, msg) )
self.sslobj = sslwrapper(self.sslobj)
mustquote = re.compile(r"[^\w!#$%&'+,.:;<=>?^`|~-]")
diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py
index 74f1a27..a4925b1 100644
--- a/offlineimap/imapserver.py
+++ b/offlineimap/imapserver.py
@@ -85,7 +85,7 @@ class UsefulIMAP4_SSL(UsefulIMAPMixIn, imaplibutil.WrappedIMAP4_SSL):
imaplibutil.new_open_ssl(self, host, port)
# This is the same hack as above, to be used in the case of an SSL
- # connexion.
+ # connection.
def read(self, size):
if (system() == 'Darwin') and (size>0) :
@@ -191,7 +191,7 @@ class IMAPServer:
try:
if self.gss_step == self.GSS_STATE_STEP:
if not self.gss_vc:
- rc, self.gss_vc = kerberos.authGSSClientInit('imap@' +
+ rc, self.gss_vc = kerberos.authGSSClientInit('imap@' +
self.hostname)
response = kerberos.authGSSClientResponse(self.gss_vc)
rc = kerberos.authGSSClientStep(self.gss_vc, data)
@@ -243,7 +243,7 @@ class IMAPServer:
self.lastowner[imapobj] = thread.get_ident()
self.connectionlock.release()
return imapobj
-
+
self.connectionlock.release() # Release until need to modify data
""" Must be careful here that if we fail we should bail out gracefully
@@ -328,7 +328,7 @@ class IMAPServer:
if(self.connectionlock.locked()):
self.connectionlock.release()
raise
-
+
def connectionwait(self):
"""Waits until there is a connection available. Note that between
the time that a connection becomes available and the time it is
@@ -378,7 +378,7 @@ class IMAPServer:
ui.debug('imap', 'keepalive: connectionlock released')
threads = []
imapobjs = []
-
+
for i in range(numconnections):
ui.debug('imap', 'keepalive: processing connection %d of %d' % (i, numconnections))
imapobj = self.acquireconnection()
@@ -424,7 +424,7 @@ class ConfigedIMAPServer(IMAPServer):
reference = self.repos.getreference()
server = None
password = None
-
+
if repository.getname() in passwordhash:
password = passwordhash[repository.getname()]
Reply to: