Merge pull request #478 from tianhao64/master

Fix thumbprint verification
This commit is contained in:
Tianhao He
2016-11-14 17:40:11 -08:00
committed by GitHub
8 changed files with 67 additions and 59 deletions

View File

@@ -942,8 +942,6 @@ class SoapStubAdapterBase(StubAdapterBase):
## Subclass of HTTPConnection that connects over a Unix domain socket ## Subclass of HTTPConnection that connects over a Unix domain socket
## instead of a TCP port. The path of the socket is passed in place of ## instead of a TCP port. The path of the socket is passed in place of
## the hostname. Fairly gross but does the job. ## the hostname. Fairly gross but does the job.
# NOTE (hartsock): rewrite this class as a wrapper, see HTTPSConnectionWrapper
# below for a guide.
class UnixSocketConnection(http_client.HTTPConnection): class UnixSocketConnection(http_client.HTTPConnection):
# The HTTPConnection ctor expects a single argument, which it interprets # The HTTPConnection ctor expects a single argument, which it interprets
# as the host to connect to; for UnixSocketConnection, we instead interpret # as the host to connect to; for UnixSocketConnection, we instead interpret
@@ -999,20 +997,14 @@ except ImportError:
wrappedSocket = socket.ssl(rawSocket, keyfile, certfile) wrappedSocket = socket.ssl(rawSocket, keyfile, certfile)
return http_client.FakeSocket(rawSocket, wrappedSocket) return http_client.FakeSocket(rawSocket, wrappedSocket)
## https connection wrapper
## Internal version of https connection
# #
# NOTE (hartsock): do not override core library types or implementations
# directly because this makes brittle code that is too easy to break and
# closely tied to implementation details we do not control. Instead, wrap
# the core object to introduce additional behaviors.
#
# Purpose:
# Support ssl.wrap_socket params which are missing from httplib # Support ssl.wrap_socket params which are missing from httplib
# HTTPSConnection (e.g. ca_certs) # HTTPSConnection (e.g. ca_certs)
# Note: Only works iff the ssl params are passing in as kwargs # Note: Only works if the ssl params are passing in as kwargs
class HTTPSConnectionWrapper(object): class _HTTPSConnection(http_client.HTTPSConnection):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
wrapped = http_client.HTTPSConnection(*args, **kwargs)
# Extract ssl.wrap_socket param unknown to httplib.HTTPSConnection, # Extract ssl.wrap_socket param unknown to httplib.HTTPSConnection,
# and push back the params in connect() # and push back the params in connect()
self._sslArgs = {} self._sslArgs = {}
@@ -1022,14 +1014,15 @@ class HTTPSConnectionWrapper(object):
"ciphers"]: "ciphers"]:
if key in tmpKwargs: if key in tmpKwargs:
self._sslArgs[key] = tmpKwargs.pop(key) self._sslArgs[key] = tmpKwargs.pop(key)
self._wrapped = wrapped http_client.HTTPSConnection.__init__(self, *args, **tmpKwargs)
## Override connect to allow us to pass in additional ssl paramters to ## Override connect to allow us to pass in additional ssl paramters to
# ssl.wrap_socket (e.g. cert_reqs, ca_certs for ca cert verification) # ssl.wrap_socket (e.g. cert_reqs, ca_certs for ca cert verification)
def connect(self, wrapped): def connect(self):
if len(self._sslArgs) == 0 or hasattr(self, '_baseclass'): if len(self._sslArgs) == 0:
# No override # No override
return wrapped.connect http_client.HTTPSConnection.connect(self)
return
# Big hack. We have to copy and paste the httplib connect fn for # Big hack. We have to copy and paste the httplib connect fn for
# each python version in order to handle extra ssl paramters. Yuk! # each python version in order to handle extra ssl paramters. Yuk!
@@ -1037,30 +1030,34 @@ class HTTPSConnectionWrapper(object):
# Python 2.7 # Python 2.7
sock = socket.create_connection((self.host, self.port), sock = socket.create_connection((self.host, self.port),
self.timeout, self.source_address) self.timeout, self.source_address)
if wrapped._tunnel_host: if self._tunnel_host:
wrapped.sock = sock self.sock = sock
wrapped._tunnel() self._tunnel()
wrapped.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs) self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
**self._sslArgs)
elif hasattr(self, "timeout"): elif hasattr(self, "timeout"):
# Python 2.6 # Python 2.6
sock = socket.create_connection((self.host, self.port), self.timeout) sock = socket.create_connection((self.host, self.port), self.timeout)
wrapped.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs) self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
**self._sslArgs)
else:
# Unknown python version. Do nothing
http_client.HTTPSConnection.connect(self)
return
return wrapped.connect # TODO: Additional verification of peer cert if needed
# cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE)
# TODO: Additional verification of peer cert if needed # ca_certs = self._sslArgs.get("ca_certs", None)
#cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE) # if cert_reqs != ssl.CERT_NONE and ca_certs:
#ca_certs = self._sslArgs.get("ca_certs", None) # if hasattr(self.sock, "getpeercert"):
#if cert_reqs != ssl.CERT_NONE and ca_certs: # # TODO: verify peer cert
# if hasattr(self.sock, "getpeercert"): # dercert = self.sock.getpeercert(False)
# # TODO: verify peer cert # # pemcert = ssl.DER_cert_to_PEM_cert(dercert)
# dercert = self.sock.getpeercert(False)
# # pemcert = ssl.DER_cert_to_PEM_cert(dercert)
def __getattr__(self, item): def __getattr__(self, item):
if item == 'connect': if item == 'connect':
return self.connect(self._wrapped) return self.connect(self._wrapped)
return getattr(self._wrapped, item) return getattr(self._wrapped, item)
## Stand-in for the HTTPSConnection class that will connect to a proxy and ## Stand-in for the HTTPSConnection class that will connect to a proxy and
## issue a CONNECT command to start an SSL tunnel. ## issue a CONNECT command to start an SSL tunnel.
@@ -1222,10 +1219,10 @@ class SoapStubAdapter(SoapStubAdapterBase):
if urlpath not in ('', '/'): if urlpath not in ('', '/'):
path = urlpath path = urlpath
self.scheme = scheme == "http" and http_client.HTTPConnection \ self.scheme = scheme == "http" and http_client.HTTPConnection \
or scheme == "https" and HTTPSConnectionWrapper or scheme == "https" and _HTTPSConnection
else: else:
port, self.scheme = port < 0 and (-port, http_client.HTTPConnection) \ port, self.scheme = port < 0 and (-port, http_client.HTTPConnection) \
or (port, HTTPSConnectionWrapper) or (port, _HTTPSConnection)
if host.find(':') != -1: # is IPv6? if host.find(':') != -1: # is IPv6?
host = '[' + host + ']' host = '[' + host + ']'
self.host = '{0}:{1}'.format(host, port) self.host = '{0}:{1}'.format(host, port)
@@ -1243,7 +1240,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
self.scheme = SSLTunnelConnection(sslProxyPath) self.scheme = SSLTunnelConnection(sslProxyPath)
self.is_ssl_tunnel = True self.is_ssl_tunnel = True
elif httpProxyHost: elif httpProxyHost:
if self.scheme == HTTPSConnectionWrapper: if self.scheme == _HTTPSConnection:
self.scheme = SSLTunnelConnection(self.host) self.scheme = SSLTunnelConnection(self.host)
self.is_ssl_tunnel = True self.is_ssl_tunnel = True
else: else:
@@ -1279,7 +1276,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
# depend on the behavior that close() still leaves the socket semi-functional. # depend on the behavior that close() still leaves the socket semi-functional.
if sys.version_info[:2] < (2,7): if sys.version_info[:2] < (2,7):
def _CloseConnection(self, conn): def _CloseConnection(self, conn):
if self.scheme == HTTPSConnectionWrapper and conn.sock: if self.scheme == _HTTPSConnection and conn.sock:
conn.sock.shutdown(socket.SHUT_RDWR) conn.sock.shutdown(socket.SHUT_RDWR)
conn.close() conn.close()
else: else:

View File

@@ -14,9 +14,14 @@
# limitations under the License. # limitations under the License.
import logging import logging
import os import os
import unittest
import vcr
import socket import socket
import unittest
import vcr
from vcr import config
from vcr.stubs import VCRHTTPSConnection
from pyVmomi import SoapAdapter
def tests_resource_path(local_path=''): def tests_resource_path(local_path=''):
@@ -33,6 +38,8 @@ def monkey_patch_vcrpy():
vcr.stubs.VCRFakeSocket = socket.socket vcr.stubs.VCRFakeSocket = socket.socket
class VCRTestBase(unittest.TestCase): class VCRTestBase(unittest.TestCase):
my_vcr = config.VCR(
custom_patches=((SoapAdapter, '_HTTPSConnection', VCRHTTPSConnection),))
def setUp(self): def setUp(self):
monkey_patch_vcrpy() monkey_patch_vcrpy()

View File

@@ -15,7 +15,6 @@
import tests import tests
import unittest import unittest
import vcr
from pyVim import connect from pyVim import connect
from pyVmomi import vim from pyVmomi import vim
@@ -23,7 +22,7 @@ from pyVmomi import vim
class ConnectionTests(tests.VCRTestBase): class ConnectionTests(tests.VCRTestBase):
@vcr.use_cassette('basic_connection.yaml', @tests.VCRTestBase.my_vcr.use_cassette('basic_connection.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='none') record_mode='none')
def test_basic_connection(self): def test_basic_connection(self):
@@ -40,7 +39,7 @@ class ConnectionTests(tests.VCRTestBase):
self.assertTrue(session_id is not None) self.assertTrue(session_id is not None)
self.assertEqual('52b5395a-85c2-9902-7835-13a9b77e1fec', session_id) self.assertEqual('52b5395a-85c2-9902-7835-13a9b77e1fec', session_id)
@vcr.use_cassette('sspi_connection.yaml', @tests.VCRTestBase.my_vcr.use_cassette('sspi_connection.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='none') record_mode='none')
def test_sspi_connection(self): def test_sspi_connection(self):
@@ -57,7 +56,7 @@ class ConnectionTests(tests.VCRTestBase):
self.assertTrue(session_id is not None) self.assertTrue(session_id is not None)
self.assertEqual('52b5395a-85c2-9902-7835-13a9b77e1fec', session_id) self.assertEqual('52b5395a-85c2-9902-7835-13a9b77e1fec', session_id)
@vcr.use_cassette('basic_connection_bad_password.yaml', @tests.VCRTestBase.my_vcr.use_cassette('basic_connection_bad_password.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='none') record_mode='none')
def test_basic_connection_bad_password(self): def test_basic_connection_bad_password(self):
@@ -68,7 +67,7 @@ class ConnectionTests(tests.VCRTestBase):
self.assertRaises(vim.fault.InvalidLogin, should_fail) self.assertRaises(vim.fault.InvalidLogin, should_fail)
@vcr.use_cassette('smart_connection.yaml', @tests.VCRTestBase.my_vcr.use_cassette('smart_connection.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='none') record_mode='none')
def test_smart_connection(self): def test_smart_connection(self):
@@ -84,13 +83,13 @@ class ConnectionTests(tests.VCRTestBase):
def test_disconnect_on_no_connection(self): def test_disconnect_on_no_connection(self):
connect.Disconnect(None) connect.Disconnect(None)
@vcr.use_cassette('ssl_tunnel.yaml', @tests.VCRTestBase.my_vcr.use_cassette('ssl_tunnel.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='none') record_mode='none')
def test_ssl_tunnel(self): def test_ssl_tunnel(self):
connect.SoapStubAdapter('sdkTunnel', 8089, httpProxyHost='vcsa').GetConnection() connect.SoapStubAdapter('sdkTunnel', 8089, httpProxyHost='vcsa').GetConnection()
@vcr.use_cassette('ssl_tunnel_http_failure.yaml', @tests.VCRTestBase.my_vcr.use_cassette('ssl_tunnel_http_failure.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='none') record_mode='none')
def test_ssl_tunnel_http_failure(self): def test_ssl_tunnel_http_failure(self):

View File

@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import tests import tests
import vcr
from pyVim import connect from pyVim import connect
from pyVmomi import vim from pyVmomi import vim
@@ -21,7 +20,7 @@ from pyVmomi import vim
class ContainerViewTests(tests.VCRTestBase): class ContainerViewTests(tests.VCRTestBase):
@vcr.use_cassette('basic_container_view.yaml', @tests.VCRTestBase.my_vcr.use_cassette('basic_container_view.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='once') record_mode='once')
def test_basic_container_view(self): def test_basic_container_view(self):

View File

@@ -16,15 +16,18 @@ from datetime import datetime
from datetime import timedelta from datetime import timedelta
import tests import tests
import vcr
from pyVim import connect from pyVim import connect
from pyVmomi.Iso8601 import TZManager from pyVmomi.Iso8601 import TZManager
from pyVmomi import SoapAdapter
from vcr.stubs import VCRHTTPSConnection
from vcr import config
class Iso8601Tests(tests.VCRTestBase): class Iso8601Tests(tests.VCRTestBase):
@vcr.use_cassette('test_vm_config_iso8601.yaml', @tests.VCRTestBase.my_vcr.use_cassette('test_vm_config_iso8601.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='once') record_mode='once')
def test_vm_config_iso8601(self): def test_vm_config_iso8601(self):
@@ -76,7 +79,9 @@ class Iso8601Tests(tests.VCRTestBase):
return False return False
return True return True
my_vcr = vcr.VCR() my_vcr = config.VCR(
custom_patches=(
(SoapAdapter, '_HTTPSConnection', VCRHTTPSConnection),))
my_vcr.register_matcher('document', check_date_time_value) my_vcr.register_matcher('document', check_date_time_value)
# NOTE (hartsock): the `match_on` option is altered to use the # NOTE (hartsock): the `match_on` option is altered to use the

View File

@@ -15,14 +15,13 @@
from __future__ import print_function from __future__ import print_function
import tests import tests
import vcr
from pyVim import connect from pyVim import connect
class ManagedObjectTests(tests.VCRTestBase): class ManagedObjectTests(tests.VCRTestBase):
@vcr.use_cassette('root_folder_parent.yaml', @tests.VCRTestBase.my_vcr.use_cassette('root_folder_parent.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='once') record_mode='once')
def test_root_folder_parent(self): def test_root_folder_parent(self):

View File

@@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import tests import tests
import vcr from vcr.stubs import VCRHTTPSConnection
from vcr import config
from pyVmomi import SoapAdapter from pyVmomi import SoapAdapter
from pyVmomi import SoapStubAdapter from pyVmomi import SoapStubAdapter
@@ -22,6 +22,7 @@ from pyVmomi import vim
from pyVmomi.VmomiSupport import GetRequestContext from pyVmomi.VmomiSupport import GetRequestContext
class SerializerTests(tests.VCRTestBase): class SerializerTests(tests.VCRTestBase):
def test_serialize_object(self): def test_serialize_object(self):
val = vim.vm.device.VirtualDeviceSpec.FileOperation() val = vim.vm.device.VirtualDeviceSpec.FileOperation()
@@ -40,7 +41,9 @@ class SerializerTests(tests.VCRTestBase):
SoapAdapter.Serialize(pc, version='vim.version.version10') SoapAdapter.Serialize(pc, version='vim.version.version10')
def _base_serialize_test(self, soap_creator, request_matcher): def _base_serialize_test(self, soap_creator, request_matcher):
my_vcr = vcr.VCR() my_vcr = config.VCR(
custom_patches=(
(SoapAdapter, '_HTTPSConnection', VCRHTTPSConnection),))
my_vcr.register_matcher('request_matcher', request_matcher) my_vcr.register_matcher('request_matcher', request_matcher)
with my_vcr.use_cassette( with my_vcr.use_cassette(

View File

@@ -15,7 +15,6 @@
from __future__ import print_function from __future__ import print_function
import tests import tests
import vcr
from pyVim import connect from pyVim import connect
from pyVmomi import vim from pyVmomi import vim
@@ -23,7 +22,7 @@ from pyVmomi import vim
class VirtualMachineTests(tests.VCRTestBase): class VirtualMachineTests(tests.VCRTestBase):
@vcr.use_cassette('vm_nic_data.yaml', @tests.VCRTestBase.my_vcr.use_cassette('vm_nic_data.yaml',
cassette_library_dir=tests.fixtures_path, cassette_library_dir=tests.fixtures_path,
record_mode='never') record_mode='never')
def test_vm_nic_data(self): def test_vm_nic_data(self):