Skip to content

Commit

Permalink
ssl: hostname mismatch was checked only once (python2 fix)
Browse files Browse the repository at this point in the history
  • Loading branch information
temoto committed Feb 3, 2017
1 parent 3d436df commit 40cbdcc
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 19 deletions.
8 changes: 5 additions & 3 deletions python2/httplib2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,12 @@
socks = None

# Build the appropriate socket wrapper for ssl
ssl_SSLError = None
ssl_CertificateError = None
try:
import ssl # python 2.6
ssl_SSLError = ssl.SSLError
ssl_CertificateError = ssl.CertificateError
def _ssl_wrap_socket(sock, key_file, cert_file, disable_validation,
ca_certs, ssl_version, hostname):
if disable_validation:
Expand All @@ -91,7 +94,6 @@ def _ssl_wrap_socket(sock, key_file, cert_file, disable_validation,
cert_reqs=cert_reqs, ca_certs=ca_certs,
ssl_version=ssl_version)
except (AttributeError, ImportError):
ssl_SSLError = None
def _ssl_wrap_socket(sock, key_file, cert_file, disable_validation,
ca_certs, ssl_version, hostname):
if not disable_validation:
Expand Down Expand Up @@ -1066,7 +1068,7 @@ def connect(self):
raise CertificateHostnameMismatch(
'Server presented certificate that does not match '
'host %s: %s' % (hostname, cert), hostname, cert)
except ssl_SSLError, e:
except (ssl_SSLError, ssl_CertificateError, CertificateHostnameMismatch), e:
if sock:
sock.close()
if self.sock:
Expand All @@ -1076,7 +1078,7 @@ def connect(self):
# to get at more detailed error information, in particular
# whether the error is due to certificate validation or
# something else (such as SSL protocol mismatch).
if e.errno == ssl.SSL_ERROR_SSL:
if getattr(e, 'errno', None) == ssl.SSL_ERROR_SSL:
raise SSLHandshakeError(e)
else:
raise
Expand Down
51 changes: 35 additions & 16 deletions python2/httplib2/test/test_ssl_context.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
#!/usr/bin/python2
#!/usr/bin/env python2
import BaseHTTPServer
import logging
import os.path
import unittest
import ssl
import sys
import unittest

import httplib2

from httplib2.test import miniserver


logger = logging.getLogger(__name__)


class KeepAliveHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""
Request handler that keeps the HTTP connection open, so that the test can
Expand All @@ -28,27 +30,23 @@ def log_message(self, s, *args):
# output via logging so nose can catch it
logger.info(s, *args)


class HttpsContextTest(unittest.TestCase):
def setUp(self):
if sys.version_info < (2, 7, 9):
return
if hasattr(self, "skipTest"):
self.skipTest("SSLContext requires Python 2.7.9")
else:
return

self.httpd, self.port = miniserver.start_server(
KeepAliveHandler, True)
self.ca_certs_path = os.path.join(os.path.dirname(__file__), 'server.pem')
self.httpd, self.port = miniserver.start_server(KeepAliveHandler, True)

def tearDown(self):
self.httpd.shutdown()

def testHttpsContext(self):
if sys.version_info < (2, 7, 9):
if hasattr(unittest, "skipTest"):
self.skipTest("SSLContext requires Python 2.7.9")# Python 2.7.0
else:
return
import ssl

client = httplib2.Http(
ca_certs=os.path.join(os.path.dirname(__file__), 'server.pem'))
client = httplib2.Http(ca_certs=self.ca_certs_path)

# Establish connection to local server
client.request('https://localhost:%d/' % (self.port))
Expand All @@ -61,6 +59,27 @@ def testHttpsContext(self):
self.assertIsInstance(conn.sock.context, ssl.SSLContext)
self.assertTrue(conn.sock.context.check_hostname)
self.assertEqual(conn.sock.server_hostname, 'localhost')
self.assertEqual(conn.sock.context.check_hostname, True)
self.assertEqual(conn.sock.context.verify_mode, ssl.CERT_REQUIRED)
self.assertEqual(conn.sock.context.protocol, ssl.PROTOCOL_SSLv23)

def test_ssl_hostname_mismatch_repeat(self):
# https://github.com/httplib2/httplib2/issues/5

# FIXME(temoto): as of 2017-01-05 this is only a reference code, not useful test.
# Because it doesn't provoke described error on my machine.
# Instead `SSLContext.wrap_socket` raises `ssl.CertificateError`
# which was also added to original patch.

# url host is intentionally different, we provoke ssl hostname mismatch error
url = 'https://127.0.0.1:%d/' % (self.port,)
http = httplib2.Http(ca_certs=self.ca_certs_path, proxy_info=None)

def once():
try:
http.request(url)
assert False, 'expected certificate hostname mismatch error'
except Exception as e:
print('%s errno=%s' % (repr(e), getattr(e, 'errno', None)))

once()
once()

0 comments on commit 40cbdcc

Please sign in to comment.