Skip to content

Commit

Permalink
* Added test case for SSL verification of peer failing
Browse files Browse the repository at this point in the history
 * ''Really'' making this the 0.1.0 release this time :)

git-svn-id: http://proj.badc.rl.ac.uk/svn/ndg-security/trunk/ndg_httpsclient@7993 051b1e3e-aa0c-0410-b6c2-bfbade6052be
  • Loading branch information
pjkersha committed Jan 17, 2012
1 parent 4cd8e56 commit 03488d2
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 12 deletions.
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@
# Licence: BSD - See LICENCE file for details
recursive-include ndg/httpsclient/test *.crt *.key *.sh README
recursive-include documentation Makefile
include README
12 changes: 10 additions & 2 deletions ndg/httpsclient/https.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,26 @@ class HTTPSConnection(HTTPConnection):
default_ssl_method = SSL.SSLv23_METHOD

def __init__(self, host, port=None, strict=None,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, ssl_context=None):
HTTPConnection.__init__(self, host, port, strict, timeout)
if not hasattr(self, 'ssl_context'):
self.ssl_context = None

if ssl_context is not None:
if not isinstance(ssl_context, SSL.Context):
raise TypeError('Expecting OpenSSL.SSL.Context type for "'
'ssl_context" keyword; got %r instead' %
ssl_context)

self.ssl_context = ssl_context

def connect(self):
"""Create SSL socket and connect to peer
"""
if getattr(self, 'ssl_context', None):
if not isinstance(self.ssl_context, SSL.Context):
raise TypeError('Expecting OpenSSL.SSL.Context type for "'
'ssl_context" keyword; got %r instead' %
'ssl_context" attribute; got %r instead' %
self.ssl_context)
ssl_context = self.ssl_context
else:
Expand Down
5 changes: 3 additions & 2 deletions ndg/httpsclient/ssl_context_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ def make_ssl_context_from_config(ssl_config=False, url=None):


def make_ssl_context(key_file=None, cert_file=None, pem_file=None, ca_dir=None,
verify_peer=False, url=None):
verify_peer=False, url=None, method=SSL.SSLv23_METHOD):
"""
Creates SSL context containing certificate and key file locations.
"""
ssl_context = SSL.Context(SSL.SSLv23_METHOD)
ssl_context = SSL.Context(method)

# Key file defaults to certificate file if present.
if cert_file:
ssl_context.use_certificate_file(cert_file)
Expand Down
6 changes: 3 additions & 3 deletions ndg/httpsclient/ssl_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ def close(self):
underlying socket"""
try:
self.__ssl_conn.shutdown()
except SSL.Error, e:
except SSL.Error:
# Make errors on shutdown non-fatal
log.warning('Connection shutdown failed: %r', e)

pass
self.__ssl_conn.close()

def set_shutdown(self, mode):
Expand Down
18 changes: 16 additions & 2 deletions ndg/httpsclient/test/README
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@ The unit tests expect to connect to a simple HTTPS server listening on port

$ ./scripts/openssl_https_server.sh

Unit tests
----------
Run for example,

$ python ./test_urllib2.py

Troubleshooting
---------------
Run it from *this* directory. Also ensure it is has execute bits set. e.g.
* Run the openssl script from *this* directory.
* Also ensure it is has execute bits set. e.g.

$ chmod 755 ./scripts/openssl_https_server.sh

* You may need to set the no_proxy environment variable if you have a HTTPS
proxy in place:

$ export no_proxy=localhost


$ chmod 755 ./scripts/openssl_https_server.sh
1 change: 1 addition & 0 deletions ndg/httpsclient/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Constants(object):
TEST_URI2 = 'https://%s:%d' % (HOSTNAME, PORT2)

UNITTEST_DIR = os.path.dirname(os.path.abspath(__file__))
CACERT_DIR = os.path.join(UNITTEST_DIR, 'pki', 'ca')
SSL_CERT_FILENAME = 'localhost.crt'
SSL_CERT_FILEPATH = os.path.join(UNITTEST_DIR, 'pki', SSL_CERT_FILENAME)
SSL_PRIKEY_FILENAME = 'localhost.key'
Expand Down
41 changes: 41 additions & 0 deletions ndg/httpsclient/test/test_https.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
__revision__ = '$Id$'
import logging
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)
import unittest
import socket

from OpenSSL import SSL

from ndg.httpsclient.test import Constants
from ndg.httpsclient.https import HTTPSConnection

Expand All @@ -33,6 +36,44 @@ def test02_open_fails(self):
conn = HTTPSConnection(Constants.HOSTNAME, port=Constants.PORT2)
self.failUnlessRaises(socket.error, conn.connect)

def test03_ssl_verification_of_peer_fails(self):
ctx = SSL.Context(SSL.SSLv3_METHOD)

def verify_callback(conn, x509, errnum, errdepth, preverify_ok):
log.debug('SSL peer certificate verification failed for %r',
x509.get_subject())
return preverify_ok

ctx.set_verify(SSL.VERIFY_PEER, verify_callback)
ctx.set_verify_depth(9)

# Set bad location - unit test dir has no CA certs to verify with
ctx.load_verify_locations(None, Constants.UNITTEST_DIR)

conn = HTTPSConnection(Constants.HOSTNAME, port=Constants.PORT,
ssl_context=ctx)
conn.connect()
self.failUnlessRaises(SSL.Error, conn.request, 'GET', '/')

def test03_ssl_verification_of_peer_succeeds(self):
ctx = SSL.Context(SSL.SSLv3_METHOD)

verify_callback = lambda conn, x509, errnum, errdepth, preverify_ok: \
preverify_ok

ctx.set_verify(SSL.VERIFY_PEER, verify_callback)
ctx.set_verify_depth(9)

# Set correct location for CA certs to verify with
ctx.load_verify_locations(None, Constants.CACERT_DIR)

conn = HTTPSConnection(Constants.HOSTNAME, port=Constants.PORT,
ssl_context=ctx)
conn.connect()
conn.request('GET', '/')
resp = conn.getresponse()
print('Response = %s' % resp.read())


if __name__ == "__main__":
unittest.main()
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
Prerequisites
=============
This has been developed and tested for Python 2.6 and 2.7 with pyOpenSSL. Note
that proxy support is only available from Python 2.6.2 onwards.
This has been developed and tested for Python 2.6 and 2.7 with pyOpenSSL 0.13.
Note that proxy support is only available from Python 2.6.2 onwards.
Installation
============
Expand Down Expand Up @@ -48,6 +48,8 @@
description='Provides enhanced HTTPS support for httplib and urllib2 using '
'PyOpenSSL',
author='Richard Wilkinson and Philip Kershaw',
author_email='Philip.Kershaw@stfc.ac.uk',
url='http://ndg-security.ceda.ac.uk/wiki/ndg_httpsclient/',
long_description=_long_description,
license='BSD - See LICENCE file for details',
namespace_packages=['ndg'],
Expand Down

0 comments on commit 03488d2

Please sign in to comment.