diff options
| author | Victor Shyba <victor.shyba@gmail.com> | 2015-05-27 12:49:44 -0300 | 
|---|---|---|
| committer | Victor Shyba <victor.shyba@gmail.com> | 2015-06-02 19:25:54 -0300 | 
| commit | c071c69e1b5a0d897674a1f7adc6ff32f19400ff (patch) | |
| tree | 2a04564aa2ea8f257fc1b3e5a846a6b3cf7a43d7 | |
| parent | 7826a96e526a450380917f9b89e3714576ca50b7 (diff) | |
[bug] Use BrowserLikePolicyForHTTPS for checking
While testing the way that its implemented now, I found out that no check
is being made on certificate attributes against the host.
I found this simple way of creating a BrowserLikePolicyForHTTPS using
a self signed cert and it worked on my test. I used test_https from
Soledad for checking this (which we are fixing on another branch).
Also, we don't want to depend on twisted for other things than
leap.common.http.
| -rw-r--r-- | pkg/requirements.pip | 2 | ||||
| -rw-r--r-- | setup.py | 7 | ||||
| -rw-r--r-- | src/leap/common/certs.py | 17 | ||||
| -rw-r--r-- | src/leap/common/http.py | 70 | 
4 files changed, 42 insertions, 54 deletions
| diff --git a/pkg/requirements.pip b/pkg/requirements.pip index f875344..1977cc8 100644 --- a/pkg/requirements.pip +++ b/pkg/requirements.pip @@ -2,8 +2,6 @@ jsonschema  #<=0.8 -- are we done with this conflict?  dirspec  pyopenssl  python-dateutil -Twisted>=12.1 -zope.interface  pyzmq  txzmq @@ -138,4 +138,11 @@ setup(      tests_require=tests_requirements,      include_package_data=True,      zip_safe=False, + +    extras_require={ +        # needed for leap.common.http +        #  service_identity needed for propper hostname identification, +        #  see http://twistedmatrix.com/documents/current/core/howto/ssl.html +        'Twisted':  ["Twisted>=14.0.2", "service_identity", "zope.insterface"] +    },  ) diff --git a/src/leap/common/certs.py b/src/leap/common/certs.py index db513f6..c8e0743 100644 --- a/src/leap/common/certs.py +++ b/src/leap/common/certs.py @@ -178,3 +178,20 @@ def should_redownload(certfile, now=time.gmtime):          return True      return False + + +def get_compatible_ssl_context_factory(cert_path=None): +    import twisted +    cert = None +    if twisted.version.base() > '14.0.1': +        from twisted.web.client import BrowserLikePolicyForHTTPS +        from twisted.internet import ssl +        if cert_path: +            cert = ssl.Certificate.loadPEM(open(cert_path).read()) +        policy = BrowserLikePolicyForHTTPS(cert) +        return policy +    else: +        raise Exception((""" +            Twisted 14.0.2 is needed in order to have secure Client Web SSL Contexts, not %s +            See: http://twistedmatrix.com/trac/ticket/7647 +            """) % (twisted.version.base())) diff --git a/src/leap/common/http.py b/src/leap/common/http.py index 39f01ba..1dc5642 100644 --- a/src/leap/common/http.py +++ b/src/leap/common/http.py @@ -18,22 +18,28 @@  Twisted HTTP/HTTPS client.  """ -import os +try: +    import twisted +except ImportError: +    print "*******" +    print "Twisted is needed to use leap.common.http module" +    print "" +    print "Install the extra requirement of the package:" +    print "$ pip install leap.common[Twisted]" +    import sys +    sys.exit(1) -from zope.interface import implements -from OpenSSL.crypto import load_certificate -from OpenSSL.crypto import FILETYPE_PEM +from leap.common.certs import get_compatible_ssl_context_factory + +from zope.interface import implements  from twisted.internet import reactor -from twisted.internet.ssl import ClientContextFactory -from twisted.internet.ssl import CertificateOptions  from twisted.internet.defer import succeed  from twisted.web.client import Agent  from twisted.web.client import HTTPConnectionPool  from twisted.web.client import readBody -from twisted.web.client import BrowserLikePolicyForHTTPS  from twisted.web.http_headers import Headers  from twisted.web.iweb import IBodyProducer @@ -55,33 +61,12 @@ class HTTPClient(object):          self._pool = HTTPConnectionPool(reactor, persistent=True)          self._pool.maxPersistentPerHost = 10 -        if cert_file: -            cert = self._load_cert(cert_file) -            self._agent = Agent( -                reactor, -                HTTPClient.ClientContextFactory(cert), -                pool=self._pool) -        else: -            # trust the system's CAs -            self._agent = Agent( -                reactor, -                BrowserLikePolicyForHTTPS(), -                pool=self._pool) - -    def _load_cert(self, cert_file): -        """ -        Load a X509 certificate from a file. - -        :param cert_file: The path to the certificate file. -        :type cert_file: str +        policy = get_compatible_ssl_context_factory(cert_file) -        :return: The X509 certificate. -        :rtype: OpenSSL.crypto.X509 -        """ -        if os.path.exists(cert_file): -            with open(cert_file) as f: -                data = f.read() -                return load_certificate(FILETYPE_PEM, data) +        self._agent = Agent( +            reactor, +            policy, +            pool=self._pool)      def request(self, url, method='GET', body=None, headers={}):          """ @@ -106,25 +91,6 @@ class HTTPClient(object):          d.addCallback(readBody)          return d -    class ClientContextFactory(ClientContextFactory): -        """ -        A context factory that will verify the server's certificate against a -        given CA certificate. -        """ - -        def __init__(self, cacert): -            """ -            Initialize the context factory. - -            :param cacert: The CA certificate. -            :type cacert: OpenSSL.crypto.X509 -            """ -            self._cacert = cacert - -        def getContext(self, hostname, port): -            opts = CertificateOptions(verify=True, caCerts=[self._cacert]) -            return opts.getContext() -      class StringBodyProducer(object):          """          A producer that writes the body of a request to a consumer. | 
