[tor-commits] [flashproxy/master] Use M2Crypto connect in place of Python ssl.wrap_socket.

dcf at torproject.org dcf at torproject.org
Thu Aug 1 03:13:30 UTC 2013


commit 454b735af24797ef639affd69c933d1d28717764
Author: David Fifield <david at bamsoftware.com>
Date:   Thu Jul 11 14:33:52 2013 -0700

    Use M2Crypto connect in place of Python ssl.wrap_socket.
    
    ssl.wrap_socket doesn't provide enough access to the certificate chain;
    you can do getpeercert but you can't get at intermediate certificates.
    
    I tried to copy the important OpenSSL calls from newPySSLObject in
    http://svn.python.org/projects/python/trunk/Modules/_ssl.c, which is
    what gets called indirectly when you do ssl.wrap_socket.
---
 facilitator/facilitator-email-poller |   37 +++++++++++++++++++++++++++------
 flashproxy-reg-appspot               |   38 ++++++++++++++++++++++++++++------
 flashproxy-reg-email                 |   19 +++++++++++------
 3 files changed, 76 insertions(+), 18 deletions(-)

diff --git a/facilitator/facilitator-email-poller b/facilitator/facilitator-email-poller
index 2b03799..a2b5cd6 100755
--- a/facilitator/facilitator-email-poller
+++ b/facilitator/facilitator-email-poller
@@ -19,7 +19,7 @@ import time
 import fac
 
 from hashlib import sha1
-from M2Crypto import X509
+from M2Crypto import SSL, X509
 
 DEFAULT_IMAP_HOST = "imap.gmail.com"
 DEFAULT_IMAP_PORT = 993
@@ -83,15 +83,41 @@ class options(object):
     imaplib_debug = False
     use_certificate_pin = True
 
+# Like socket.create_connection in that it tries resolving different address
+# families, but doesn't connect the socket.
+def create_socket(address, timeout = None, source_address = None):
+    host, port = address
+    addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
+    if not addrs:
+        raise error("getaddrinfo returns an empty list")
+    err = None
+    for addr in addrs:
+        try:
+            s = socket.socket(addr[0], addr[1], addr[2])
+            if timeout is not None and type(timeout) == float:
+                s.settimeout(timeout)
+            if source_address is not None:
+                s.bind(source_address)
+            return s
+        except Exception, e:
+            err = e
+    raise err
+
 class IMAP4_SSL_REQUIRED(imaplib.IMAP4_SSL):
     """A subclass of of IMAP4_SSL that uses ssl_version=ssl.PROTOCOL_TLSv1 and
     cert_reqs=ssl.CERT_REQUIRED."""
     def open(self, host = "", port = imaplib.IMAP4_SSL_PORT):
+        ctx = SSL.Context("tlsv1")
+        ctx.set_verify(SSL.verify_peer, 2)
+        ret = ctx.load_verify_locations(self.certfile)
+        assert ret == 1
+
         self.host = host
         self.port = port
-        self.sock = socket.create_connection((host, port))
-        self.sslobj = ssl.wrap_socket(self.sock, ssl_version=ssl.PROTOCOL_TLSv1,
-            cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.certfile)
+        self.sock = create_socket((self.host, self.port))
+
+        self.sslobj = SSL.Connection(ctx, self.sock)
+        self.sslobj.connect((self.host, self.port))
         self.file = self.sslobj.makefile('rb')
 
 def usage(f = sys.stdout):
@@ -337,8 +363,7 @@ def imap_login():
         ca_certs_file.close()
 
     # Check that the public key is what we expect.
-    cert_der = imap.ssl().getpeercert(binary_form=True)
-    cert = X509.load_cert_string(cert_der, format=X509.FORMAT_DER)
+    cert = imap.ssl().get_peer_cert()
     pubkey_der = cert.get_pubkey().as_der()
     pubkey_digest = sha1(pubkey_der).digest()
 
diff --git a/flashproxy-reg-appspot b/flashproxy-reg-appspot
index 16d0e7f..fc960e9 100755
--- a/flashproxy-reg-appspot
+++ b/flashproxy-reg-appspot
@@ -16,9 +16,10 @@ import urllib2
 from hashlib import sha1
 
 try:
-    from M2Crypto import X509
+    from M2Crypto import SSL, X509
 except ImportError:
     # Defer the error reporting so that --help works even without M2Crypto.
+    SSL = None
     X509 = None
 
 DEFAULT_REMOTE_ADDRESS = None
@@ -187,29 +188,54 @@ def generate_url(addr):
         raise ValueError("%s exited with status %d" % (FLASHPROXY_REG_URL, p.returncode))
     return stdout.strip()
 
+# Like socket.create_connection in that it tries resolving different address
+# families, but doesn't connect the socket.
+def create_socket(address, timeout = None, source_address = None):
+    host, port = address
+    addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
+    if not addrs:
+        raise error("getaddrinfo returns an empty list")
+    err = None
+    for addr in addrs:
+        try:
+            s = socket.socket(addr[0], addr[1], addr[2])
+            if timeout is not None and type(timeout) == float:
+                s.settimeout(timeout)
+            if source_address is not None:
+                s.bind(source_address)
+            return s
+        except Exception, e:
+            err = e
+    raise err
+
 # Certificate validation and pinning for urllib2. Inspired by
 # http://web.archive.org/web/20110125104752/http://www.muchtooscrawled.com/2010/03/https-certificate-verification-in-python-with-urllib2/.
 
 class PinHTTPSConnection(httplib.HTTPSConnection):
     def connect(self):
-        sock = socket.create_connection((self.host, self.port), self.timeout, self.source_address)
+        sock = create_socket((self.host, self.port), self.timeout, self.source_address)
         if self._tunnel_host:
             self.sock = sock
             self._tunnel()
 
+        ctx = SSL.Context("tlsv1")
+        ctx.set_verify(SSL.verify_peer, 2)
+
         ca_certs_fd, ca_certs_path = tempfile.mkstemp(prefix="flashproxy-reg-appspot-",
             dir=get_state_dir(), suffix=".crt")
         try:
             os.write(ca_certs_fd, CA_CERTS)
             os.close(ca_certs_fd)
-            self.sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLSv1,
-                cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_certs_path)
+            ret = ctx.load_verify_locations(ca_certs_path)
+            assert ret == 1
         finally:
             os.unlink(ca_certs_path)
 
+        self.sock = SSL.Connection(ctx, sock)
+        self.sock.connect((self.host, self.port))
+
         # Check that the public key is what we expect.
-        cert_der = self.sock.getpeercert(binary_form=True)
-        cert = X509.load_cert_string(cert_der, format=X509.FORMAT_DER)
+        cert = self.sock.get_peer_cert()
         pubkey_der = cert.get_pubkey().as_der()
         pubkey_digest = sha1(pubkey_der).digest()
 
diff --git a/flashproxy-reg-email b/flashproxy-reg-email
index 2d3cba9..a3c27ad 100755
--- a/flashproxy-reg-email
+++ b/flashproxy-reg-email
@@ -13,7 +13,7 @@ import tempfile
 from hashlib import sha1
 
 try:
-    from M2Crypto import BIO, RSA, X509
+    from M2Crypto import BIO, RSA, SSL, X509
 except ImportError:
     # Defer the error reporting so that --help works even without M2Crypto.
     X509 = None
@@ -257,6 +257,9 @@ if options.debug:
     smtp.set_debuglevel(1)
 
 try:
+    ctx = SSL.Context("tlsv1")
+    ctx.set_verify(SSL.verify_peer, 2)
+
     ca_certs_fd, ca_certs_path = tempfile.mkstemp(prefix="flashproxy-reg-email-",
         dir=get_state_dir(), suffix=".crt")
     try:
@@ -270,15 +273,19 @@ try:
         code, msg = smtp.docmd("STARTTLS")
         if code != 220:
             raise ValueError("Got code %d after STARTTLS" % code)
-        smtp.sock = ssl.wrap_socket(smtp.sock, ssl_version=ssl.PROTOCOL_TLSv1,
-            cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_certs_path)
-        smtp.file = smtp.sock.makefile()
+        ret = ctx.load_verify_locations(ca_certs_path)
+        assert ret == 1
     finally:
         os.unlink(ca_certs_path)
 
+    smtp.sock = SSL.Connection(ctx, smtp.sock)
+    smtp.sock.setup_ssl()
+    smtp.sock.set_connect_state()
+    smtp.sock.connect_ssl()
+    smtp.file = smtp.sock.makefile()
+
     # Check that the public key is what we expect.
-    cert_der = smtp.sock.getpeercert(binary_form=True)
-    cert = X509.load_cert_string(cert_der, format=X509.FORMAT_DER)
+    cert = smtp.sock.get_peer_cert()
     pubkey_der = cert.get_pubkey().as_der()
     pubkey_digest = sha1(pubkey_der).digest()
 





More information about the tor-commits mailing list