---------- Forwarded message ---------- From: dave b <db.pub.m...@gmail.com> Date: 1 December 2010 13:59 Subject: Re: Due offlineimap absence of certificate validation issue -- Debian BTS#603450 To: John Goerzen <jgoer...@complete.org> Cc: Jan Lieskovsky <jlies...@redhat.com>, Christoph Höger <choe...@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()]