
On Sun, 26 Aug 2007 20:26:44 +0200, Dirk Loss <lists@dirk-loss.de> wrote:
Hi,
following Eli Criffields nice example [1] I implemented a small SSL server with Twisted. My server should not only verify the client certificate, but also check the Common Name (CN) against a whitelist. All this should happen before any user data is exchanged.
Verifying the client certificate worked nicely, but I couldn't access its contents: transport.getPeerCertificate() always returned 'None'. Apparently Eli had the same problem [2].
After some testing with PyOpenSSL now I think I have found a solution:
Before we can get the client certificate, we have to make sure that the SSL handshake has taken place. (If it hasn't, there simply is no client certificate to deal with yet.) This can be done by calling the do_handshake() method of the underlying socket. The SSL handshake takes some time so we will have to try several times.
This is basically a bug in Twisted's SSL support. I forget if there's a way to fix it using PyOpenSSL or if it's a limitation of the bindings.
Here's an (incomplete) example showing the interesting part: - - cut --- import OpenSSL
class MyProtocol(Protocol):
def connectionMade(self):
# Make sure that SSL handshake has taken place while True: try: self.transport.socket.do_handshake() break except OpenSSL.SSL.WantReadError: pass
clientCert = self.transport.getPeerCertificate() if clientCert is None: log.msg("No client cert available.") else: subject = clientCert.get_subject() log.msg("Subject: %s" % subject) log.msg("Common Name: %s" % subject.CN) - - cut ---
If you see a nicer way to wait for the SSL handshake please let me know. Using time.sleep() didn't work for me.
This solution has at least two related problems: * it will block the reactor until the handshake for that client completes, which means no other I/O will occur and none other application code will be able to run. This might be fine for your application, but in general it's not a very good thing. * if a malicious client connects, they can just never complete the handshake and your server will hang in that loop indefinitely.
Side note: Getting the certificate in a dataReceived() instead of connectionMade() works without manually doing the handshake. I think this is because the underlying PyOpenSSL recv() method handles the handshake for us. But at least for my purpose it makes more sense to verify the client cert right upon connection, before any user data is exchanged.
The ideal solution would be to fix the bug in Twisted's SSL support so that connectionMade is called at the right time. Another possible solution might be to do your verification using the SSL context object. CertificateOptions might give you some ideas about how to do this: http://twistedmatrix.com/documents/current/api/twisted.internet.ssl.Certific... Jean-Paul