I'm using imaplib and poplib to perform email collection using IMAPS and POP3S for a secure connection. But from what I've been able to determine, neither library uses a CA to confirm the validity of the certificate received. It this true? If it is, is it possible to set imaplib or poplib to use a CA?
If it's not true and they do use a CA, can someone please tell me how imaplib/poplib do it?
Thanks.
A quick check of imaplib.py shows that it uses ssl.wrap_socket() to implement the IMAP_SSL() call. The call to wrap_socket() call only provides 3-parameters, and does not pass the required parameter ca_cert which is what you need to validate the CA.
You could inherit from IMAP4_SSL, and override the open() method to pass in the required ca_cert. Check out http://docs.python.org/library/ssl.html for more info.
Perhaps something like:
class IMAP4_SSL_CA_CHECKER(IMAP4_SSL):
def open(self, host = '', port = IMAP4_SSL_PORT, ca_certs = None):
self.host = host
self.port = port
self.sock = socket.create_connection((host, port))
self.sslobj = ssl.wrap_socket(self.sock, self.keyfile,
self.certificate, ca_certs=ca_certs)
self.file = self.sslobj.makefile('rb')
One more thought has occurred to me to mention. The python ssl library is built atop OpenSSL. If you are going to start requiring the server to provide certificates and that they be valid, you will quickly run into issues on various flavors of Unix related to the certificate store.
If you happen to be working on a system that already has Mozilla/Firefox installed, the cert store will likely be setup correctly. But it not, you're going to struggle for a few days trying to get this to work correctly.
This link helped us immensely: http://www.madboa.com/geek/openssl/
Particularly pay attention to this link: http://www.madboa.com/geek/openssl/#verify-system
Any developers working w/ openssl should bookmark that site. It's a bit on the terse side, but every single entry is worth it's weight in gold!
Because IMAP4SSL.open is called from IMAP.init the Solution above does not help, because the user does not call open().
You can overwrite IMAP.init to...
Short: Extending only the parameter for open() is not enough.
I used Injection:
def IMAP4SSL_open(self, host = '', port = imaplib.IMAP4_SSL_PORT):
... own implementation ...
wrap_socket( ... cert_reqs=ssl.CERT_REQUIRED ... )
imaplib.IMAP4_SSL.__dict__['open']=IMAP4SSL_open
I'm currently building something in this direction.
The following code adds starttls
to IMAP. Simply call server.starttls()
after connecting. Be sure to connect to the normal IMAP Port.
import imaplib,ssl
def IMAP_starttls(self, keyfile=None, certfile=None,cert_reqs=ssl.CERT_NONE,ca_certs=None):
if not 'STARTTLS' in self.capabilities:
raise self.error("STARTTLS extension not supported by server.")
(resp, reply) = self._simple_command("STARTTLS")
self.sock = ssl.wrap_socket(self.sock, keyfile, certfile,cert_reqs=cert_reqs,ca_certs=ca_certs)
self.file = self.sock.makefile('rb')
imaplib.IMAP4.__dict__['starttls']=IMAP_starttls
imaplib.Commands['STARTTLS']=('NONAUTH',)
PS: I wanted to add this as a comment, but code was to long for a comment.