Skip to content

Commit

Permalink
optionally allow unverified certificates
Browse files Browse the repository at this point in the history
Toggles on and off strict host name verification in the standard python SSL
library. A change was introduced to Python's SSL verification behavior with
[PEP 0466](https://www.python.org/dev/peps/pep-0466/) which forces this
library to consider a set of changes to preserve backward compatability.

The intention of PEP 0466 is to improve SSL security for Python programmers
but this changes default behaviors. This change proposes keeping the default
behaviors undisturbed but forcing users and developers working in insecure
environments to become aware of the change and adjust their security measures
and code accordingly.

fixes: vmware#212, vmware#235, and vmware#179
  • Loading branch information
hartsock committed May 29, 2015
1 parent e26718f commit 3e12779
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 8 deletions.
20 changes: 14 additions & 6 deletions pyVim/connect.py
Expand Up @@ -34,6 +34,7 @@

import requests
from requests.auth import HTTPBasicAuth
import ssl

from pyVmomi import vim, vmodl, SoapStubAdapter, SessionOrientedStub
from pyVmomi.VmomiSupport import nsMap, versionIdMap, versionMap, IsChildVersion
Expand Down Expand Up @@ -179,7 +180,8 @@ def _doLogin(soapStub):

def Connect(host='localhost', port=443, user='root', pwd='',
service="hostd", adapter="SOAP", namespace=None, path="/sdk",
version=None, keyFile=None, certFile=None):
version=None, keyFile=None, certFile=None,
unverified=False):
"""
Connect to the specified server, login and return the service
instance object.
Expand Down Expand Up @@ -213,6 +215,8 @@ def Connect(host='localhost', port=443, user='root', pwd='',
@type keyFile: string
@param certFile: ssl cert file path
@type certFile: string
@param unverified: allow unsigned certificates
@type unverified: bool
"""
try:
info = re.match(_rx, host)
Expand All @@ -231,7 +235,7 @@ def Connect(host='localhost', port=443, user='root', pwd='',
elif not version:
version="vim.version.version6"
si, stub = __Login(host, port, user, pwd, service, adapter, version, path,
keyFile, certFile)
keyFile, certFile, unverified)
SetSi(si)

return si
Expand Down Expand Up @@ -266,7 +270,7 @@ def GetLocalTicket(si, user):
## connected service instance object.

def __Login(host, port, user, pwd, service, adapter, version, path,
keyFile, certFile):
keyFile, certFile, unverified=False):
"""
Private method that performs the actual Connect and returns a
connected service instance object.
Expand Down Expand Up @@ -299,7 +303,8 @@ def __Login(host, port, user, pwd, service, adapter, version, path,

# Create the SOAP stub adapter
stub = SoapStubAdapter(host, port, version=version, path=path,
certKeyFile=keyFile, certFile=certFile)
certKeyFile=keyFile, certFile=certFile,
unverified=unverified)

# Get Service instance
si = vim.ServiceInstance("ServiceInstance", stub)
Expand Down Expand Up @@ -532,7 +537,7 @@ def __FindSupportedVersion(protocol, server, port, path, preferredApiVersions):

def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd='',
service="hostd", path="/sdk",
preferredApiVersions=None):
preferredApiVersions=None, unverified=False):
"""
Determine the most preferred API version supported by the specified server,
then connect to the specified server using that API version, login and return
Expand Down Expand Up @@ -565,6 +570,8 @@ def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd=
specified, the list of versions support by pyVmomi will
be used.
@type preferredApiVersions: string or string list
@param unverified: set to true to allow for unsigned certs
@type unverified: bool
"""

if preferredApiVersions is None:
Expand All @@ -587,7 +594,8 @@ def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd=
service=service,
adapter='SOAP',
version=supportedVersion,
path=path)
path=path,
unverified=unverified)

def OpenUrlWithBasicAuth(url, user='root', pwd=''):
"""
Expand Down
35 changes: 33 additions & 2 deletions pyVmomi/SoapAdapter.py
Expand Up @@ -38,6 +38,9 @@

if PY3:
from io import StringIO

import warnings

from pyVmomi.VmomiSupport import *
from pyVmomi.StubAdapterAccessorImpl import StubAdapterAccessorMixin
import pyVmomi.Iso8601
Expand Down Expand Up @@ -1029,8 +1032,12 @@ def __init__(self, proxyPath):
# SSLTunnelConnection
def __call__(self, path, key_file=None, cert_file=None, **kwargs):
# Don't pass any keyword args that HTTPConnection won't understand.
# see: https://docs.python.org/3/library/http.client.html
for arg in kwargs.keys():
if arg not in ("port", "strict", "timeout", "source_address"):
if arg not in ("port", "strict", "timeout", "source_address",
"context", "check_hostname"):
warnings.warn("http_client.HTTPConnection does not understand"
"the argument {0}".format(arg))
del kwargs[arg]
tunnel = http_client.HTTPConnection(path, **kwargs)
tunnel.request('CONNECT', self.proxyPath)
Expand Down Expand Up @@ -1148,7 +1155,7 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
thumbprint=None, cacertsFile=None, version=None,
acceptCompressedResponses=True,
connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
samlToken=None):
samlToken=None, unverified=False):
if ns:
assert(version is None)
version = versionMap[ns]
Expand Down Expand Up @@ -1184,6 +1191,9 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
else:
self.thumbprint = None

self.unverified = unverified

# SSL connection actually occurs
if sslProxyPath:
self.scheme = SSLTunnelConnection(sslProxyPath)
elif httpProxyHost:
Expand Down Expand Up @@ -1213,6 +1223,21 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
self._acceptCompressedResponses = acceptCompressedResponses


def _set_unverified_https_context(self):
self._create_default_https_context = None
if hasattr(ssl, '_create_unverified_context'):
# hold the normal HTTPS context creation method aside
warnings.warn("unverified=True was set on vSphere connection")
self._create_default_https_context = ssl._create_default_https_context
ssl._create_default_https_context = ssl._create_unverified_context


def _restore_default_https_context(self):
if self._create_default_https_context is not None:
ssl._create_default_https_context = _create_default_https_context
self._create_default_https_context = None


# Context modifier used to modify the SOAP request.
# @param func The func that takes in the serialized message and modifies the
# the request. The func is appended to the requestModifierList and then
Expand All @@ -1237,6 +1262,9 @@ def requestModifier(self, func):
# deserialized object so that it's easier to distinguish an API error from
# a connection error.
def InvokeMethod(self, mo, info, args, outerStub=None):
if self.unverified:
self._set_unverified_https_context()

if outerStub is None:
outerStub = self

Expand Down Expand Up @@ -1302,6 +1330,9 @@ def InvokeMethod(self, mo, info, args, outerStub=None):
conn.close()
raise http_client.HTTPException("{0} {1}".format(resp.status, resp.reason))

if self.unverified:
self._restore_default_https_context()

## Clean up connection pool to throw away idle timed-out connections
# SoapStubAdapter lock must be acquired before this method is called.
def _CloseIdleConnections(self):
Expand Down

0 comments on commit 3e12779

Please sign in to comment.