Skip to content

Commit

Permalink
Merge pull request vmware#478 from tianhao64/master
Browse files Browse the repository at this point in the history
Fix thumbprint verification
  • Loading branch information
tianhao64 committed Nov 15, 2016
2 parents 3cbb7e4 + 21a5d52 commit 46f5eca
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 58 deletions.
73 changes: 35 additions & 38 deletions pyVmomi/SoapAdapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -942,8 +942,6 @@ def SerializeRequest(self, mo, info, args):
## 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
## 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):
# The HTTPConnection ctor expects a single argument, which it interprets
# as the host to connect to; for UnixSocketConnection, we instead interpret
Expand Down Expand Up @@ -999,20 +997,14 @@ def _SocketWrapper(rawSocket, keyfile, certfile, *args, **kwargs):
wrappedSocket = socket.ssl(rawSocket, keyfile, certfile)
return http_client.FakeSocket(rawSocket, wrappedSocket)

## https connection wrapper
#
# 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.

## Internal version of https connection
#
# Purpose:
# Support ssl.wrap_socket params which are missing from httplib
# HTTPSConnection (e.g. ca_certs)
# Note: Only works iff the ssl params are passing in as kwargs
class HTTPSConnectionWrapper(object):
# Note: Only works if the ssl params are passing in as kwargs
class _HTTPSConnection(http_client.HTTPSConnection):
def __init__(self, *args, **kwargs):
wrapped = http_client.HTTPSConnection(*args, **kwargs)
# Extract ssl.wrap_socket param unknown to httplib.HTTPSConnection,
# and push back the params in connect()
self._sslArgs = {}
Expand All @@ -1022,45 +1014,50 @@ def __init__(self, *args, **kwargs):
"ciphers"]:
if key in tmpKwargs:
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
# ssl.wrap_socket (e.g. cert_reqs, ca_certs for ca cert verification)
def connect(self, wrapped):
if len(self._sslArgs) == 0 or hasattr(self, '_baseclass'):
def connect(self):
if len(self._sslArgs) == 0:
# No override
return wrapped.connect
http_client.HTTPSConnection.connect(self)
return

# Big hack. We have to copy and paste the httplib connect fn for
# each python version in order to handle extra ssl paramters. Yuk!
if hasattr(self, "source_address"):
# Python 2.7
sock = socket.create_connection((self.host, self.port),
self.timeout, self.source_address)
if wrapped._tunnel_host:
wrapped.sock = sock
wrapped._tunnel()
wrapped.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs)
if self._tunnel_host:
self.sock = sock
self._tunnel()
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
**self._sslArgs)
elif hasattr(self, "timeout"):
# Python 2.6
sock = socket.create_connection((self.host, self.port), self.timeout)
wrapped.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs)

return wrapped.connect
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

# TODO: Additional verification of peer cert if needed
#cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE)
#ca_certs = self._sslArgs.get("ca_certs", None)
#if cert_reqs != ssl.CERT_NONE and ca_certs:
# if hasattr(self.sock, "getpeercert"):
# # TODO: verify peer cert
# dercert = self.sock.getpeercert(False)
# # pemcert = ssl.DER_cert_to_PEM_cert(dercert)
# TODO: Additional verification of peer cert if needed
# cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE)
# ca_certs = self._sslArgs.get("ca_certs", None)
# if cert_reqs != ssl.CERT_NONE and ca_certs:
# if hasattr(self.sock, "getpeercert"):
# # TODO: verify peer cert
# dercert = self.sock.getpeercert(False)
# # pemcert = ssl.DER_cert_to_PEM_cert(dercert)

def __getattr__(self, item):
if item == 'connect':
return self.connect(self._wrapped)
return getattr(self._wrapped, item)
if item == 'connect':
return self.connect(self._wrapped)
return getattr(self._wrapped, item)

## Stand-in for the HTTPSConnection class that will connect to a proxy and
## issue a CONNECT command to start an SSL tunnel.
Expand Down Expand Up @@ -1222,10 +1219,10 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
if urlpath not in ('', '/'):
path = urlpath
self.scheme = scheme == "http" and http_client.HTTPConnection \
or scheme == "https" and HTTPSConnectionWrapper
or scheme == "https" and _HTTPSConnection
else:
port, self.scheme = port < 0 and (-port, http_client.HTTPConnection) \
or (port, HTTPSConnectionWrapper)
or (port, _HTTPSConnection)
if host.find(':') != -1: # is IPv6?
host = '[' + host + ']'
self.host = '{0}:{1}'.format(host, port)
Expand All @@ -1243,7 +1240,7 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
self.scheme = SSLTunnelConnection(sslProxyPath)
self.is_ssl_tunnel = True
elif httpProxyHost:
if self.scheme == HTTPSConnectionWrapper:
if self.scheme == _HTTPSConnection:
self.scheme = SSLTunnelConnection(self.host)
self.is_ssl_tunnel = True
else:
Expand Down Expand Up @@ -1279,7 +1276,7 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
# depend on the behavior that close() still leaves the socket semi-functional.
if sys.version_info[:2] < (2,7):
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.close()
else:
Expand Down
9 changes: 8 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
# limitations under the License.
import logging
import os
import socket
import unittest

import vcr
import socket
from vcr import config
from vcr.stubs import VCRHTTPSConnection

from pyVmomi import SoapAdapter


def tests_resource_path(local_path=''):
Expand All @@ -33,6 +38,8 @@ def monkey_patch_vcrpy():
vcr.stubs.VCRFakeSocket = socket.socket

class VCRTestBase(unittest.TestCase):
my_vcr = config.VCR(
custom_patches=((SoapAdapter, '_HTTPSConnection', VCRHTTPSConnection),))

def setUp(self):
monkey_patch_vcrpy()
Expand Down
13 changes: 6 additions & 7 deletions tests/test_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@

import tests
import unittest
import vcr

from pyVim import connect
from pyVmomi import vim


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,
record_mode='none')
def test_basic_connection(self):
Expand All @@ -40,7 +39,7 @@ def test_basic_connection(self):
self.assertTrue(session_id is not None)
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,
record_mode='none')
def test_sspi_connection(self):
Expand All @@ -57,7 +56,7 @@ def test_sspi_connection(self):
self.assertTrue(session_id is not None)
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,
record_mode='none')
def test_basic_connection_bad_password(self):
Expand All @@ -68,7 +67,7 @@ def 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,
record_mode='none')
def test_smart_connection(self):
Expand All @@ -84,13 +83,13 @@ def test_smart_connection(self):
def test_disconnect_on_no_connection(self):
connect.Disconnect(None)

@vcr.use_cassette('ssl_tunnel.yaml',
@tests.VCRTestBase.my_vcr.use_cassette('ssl_tunnel.yaml',
cassette_library_dir=tests.fixtures_path,
record_mode='none')
def test_ssl_tunnel(self):
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,
record_mode='none')
def test_ssl_tunnel_http_failure(self):
Expand Down
3 changes: 1 addition & 2 deletions tests/test_container_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import tests
import vcr

from pyVim import connect
from pyVmomi import vim


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,
record_mode='once')
def test_basic_container_view(self):
Expand Down
11 changes: 8 additions & 3 deletions tests/test_iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
from datetime import timedelta

import tests
import vcr

from pyVim import connect
from pyVmomi.Iso8601 import TZManager
from pyVmomi import SoapAdapter

from vcr.stubs import VCRHTTPSConnection
from vcr import config


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,
record_mode='once')
def test_vm_config_iso8601(self):
Expand Down Expand Up @@ -76,7 +79,9 @@ def check_date_time_value(r1, r2):
return False
return True

my_vcr = vcr.VCR()
my_vcr = config.VCR(
custom_patches=(
(SoapAdapter, '_HTTPSConnection', VCRHTTPSConnection),))
my_vcr.register_matcher('document', check_date_time_value)

# NOTE (hartsock): the `match_on` option is altered to use the
Expand Down
3 changes: 1 addition & 2 deletions tests/test_managed_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
from __future__ import print_function

import tests
import vcr

from pyVim import connect


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,
record_mode='once')
def test_root_folder_parent(self):
Expand Down
9 changes: 6 additions & 3 deletions tests/test_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import tests
import vcr

from vcr.stubs import VCRHTTPSConnection
from vcr import config

from pyVmomi import SoapAdapter
from pyVmomi import SoapStubAdapter
from pyVmomi import vim
from pyVmomi.VmomiSupport import GetRequestContext



class SerializerTests(tests.VCRTestBase):
def test_serialize_object(self):
val = vim.vm.device.VirtualDeviceSpec.FileOperation()
Expand All @@ -40,7 +41,9 @@ def test_serialize_float(self):
SoapAdapter.Serialize(pc, version='vim.version.version10')

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)

with my_vcr.use_cassette(
Expand Down
3 changes: 1 addition & 2 deletions tests/test_virtual_machine_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
from __future__ import print_function

import tests
import vcr

from pyVim import connect
from pyVmomi import vim


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,
record_mode='never')
def test_vm_nic_data(self):
Expand Down

0 comments on commit 46f5eca

Please sign in to comment.