Skip to content

Commit

Permalink
pyVmomi pinned certificates support
Browse files Browse the repository at this point in the history
New:
SoapStubAdapter and the connect.py wrappers now allows passing a serverPemCert parameter.
serverPemCert is an ASCII string of a PEM-encoded SSL certificate of the host to which a connection is attempted. A replacement of thumbprint. If both fields are set, thumbprint should match serverPemCert.
CertificateMismatchException is thrown when there's a mismatch.

If the standard SSL verifications fails
	if serverPemCert or thumbprint is provided try to connect with an unverified connection and try to match the peer certificate
	else fail

pyVmomi now has a single point of establishing a server connection SoapAdapter._Connect()

Breaking changes in SoapAdapter.py:
	HTTPProxyConnection is removed because it is unnecessary as the connection logic is now streamlined
	SSLTunnelConnection is removed and replaced by _SSLTunnelConnection which inherits Python's standard HTTPSConnection.
	UnixSocketConnection is removed and replaced by _UnixSocketConnection which inherits Python's standard HTTPConnection.
  • Loading branch information
ddraganov committed Jul 31, 2024
1 parent e4fd849 commit 9a8956f
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 86 deletions.
41 changes: 38 additions & 3 deletions pyVim/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def Connect(host='localhost',
httpProxyHost=None,
httpProxyPort=80,
thumbprint=None,
serverPemCert=None,
sslContext=None,
httpConnectionTimeout=None,
connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
Expand Down Expand Up @@ -260,8 +261,17 @@ def Connect(host='localhost',
@type httpProxyHost: string
@param httpProxyPort The proxy server port.
@type httpProxyPort: int
@param thumbprint: host cert thumbprint
@param thumbprint: **** Deprecated. Use serverPemCert instead.
If both fields are set, thumbprint should match
serverPemCert.
The SHA1/SHA256/SHA512 thumbprint of the server's
SSL certificate.
Some use a thumbprint of the form xx:xx:xx..:xx.
We ignore the ":" characters.
@type thumbprint: string
@param serverPemCert: PEM-encoded SSL certificate of the
host to which we are connecting.
@type serverPemCert: string
@param sslContext: SSL Context describing the various SSL options. It is only
supported in Python 2.7.9 or higher.
@type sslContext: SSL.Context
Expand Down Expand Up @@ -324,6 +334,7 @@ def Connect(host='localhost',
httpProxyHost,
httpProxyPort,
thumbprint,
serverPemCert,
sslContext,
httpConnectionTimeout,
connectionPoolTimeout,
Expand Down Expand Up @@ -395,6 +406,7 @@ def __Login(host,
httpProxyHost,
httpProxyPort,
thumbprint,
serverPemCert,
sslContext,
httpConnectionTimeout,
connectionPoolTimeout,
Expand Down Expand Up @@ -430,8 +442,17 @@ def __Login(host,
@type httpProxyHost: string
@param httpProxyPort The proxy server port.
@type httpProxyPort: int
@param thumbprint: host cert thumbprint
@param thumbprint: **** Deprecated. Use serverPemCert instead.
If both fields are set, thumbprint should match
serverPemCert.
The SHA1/SHA256/SHA512 thumbprint of the server's
SSL certificate.
Some use a thumbprint of the form xx:xx:xx..:xx.
We ignore the ":" characters.
@type thumbprint: string
@param serverPemCert: PEM-encoded SSL certificate of the
host to which we are connecting.
@type serverPemCert: string
@param sslContext: SSL Context describing the various SSL options. It is only
supported in Python 2.7.9 or higher.
@type sslContext: SSL.Context
Expand Down Expand Up @@ -479,6 +500,7 @@ def __Login(host,
httpProxyHost=httpProxyHost,
httpProxyPort=httpProxyPort,
thumbprint=thumbprint,
serverPemCert=serverPemCert,
sslContext=sslContext,
httpConnectionTimeout=httpConnectionTimeout,
connectionPoolTimeout=connectionPoolTimeout,
Expand Down Expand Up @@ -814,6 +836,7 @@ def SmartStubAdapter(host='localhost',
httpProxyPort=80,
sslProxyPath=None,
thumbprint=None,
serverPemCert=None,
cacertsFile=None,
preferredApiVersions=None,
acceptCompressedResponses=True,
Expand Down Expand Up @@ -872,6 +895,7 @@ def SmartStubAdapter(host='localhost',
httpProxyPort=httpProxyPort,
sslProxyPath=sslProxyPath,
thumbprint=thumbprint,
serverPemCert=serverPemCert,
cacertsFile=cacertsFile,
version=supportedVersion,
acceptCompressedResponses=acceptCompressedResponses,
Expand All @@ -896,6 +920,7 @@ def SmartConnect(protocol='https',
httpProxyHost=None,
httpProxyPort=80,
thumbprint=None,
serverPemCert=None,
sslContext=None,
httpConnectionTimeout=None,
connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
Expand Down Expand Up @@ -948,8 +973,17 @@ def SmartConnect(protocol='https',
@type httpProxyHost: string
@param httpProxyPort The proxy server port.
@type httpProxyPort: int
@param thumbprint: host cert thumbprint
@param thumbprint: **** Deprecated. Use serverPemCert instead.
If both fields are set, thumbprint should match
serverPemCert.
The SHA1/SHA256/SHA512 thumbprint of the server's
SSL certificate.
Some use a thumbprint of the form xx:xx:xx..:xx.
We ignore the ":" characters.
@type thumbprint: string
@param serverPemCert: PEM-encoded SSL certificate of the
host to which we are connecting.
@type serverPemCert: string
@param sslContext: SSL Context describing the various SSL options. It is only
supported in Python 2.7.9 or higher.
@type sslContext: SSL.Context
Expand Down Expand Up @@ -1005,6 +1039,7 @@ def SmartConnect(protocol='https',
httpProxyHost=httpProxyHost,
httpProxyPort=httpProxyPort,
thumbprint=thumbprint,
serverPemCert=serverPemCert,
sslContext=sslContext,
httpConnectionTimeout=httpConnectionTimeout,
connectionPoolTimeout=connectionPoolTimeout,
Expand Down
38 changes: 38 additions & 0 deletions pyVmomi/Security.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Client security module.

import hashlib
import ssl

_isSha1Enabled = True
_isSha256Enabled = True
Expand All @@ -25,6 +26,17 @@ def SetSha512Enabled(state):
_isSha512Enabled = state


"""
Verify that a thumbprint matches a certificate
:param derCert: DER-encoded SSL certificate
:type derCert: str
:param thumbprint: SHA1/SHA256/SHA512 thumbprint
of an SSL certificate
:type thumbprint: str
:returns: None
:raises ThumbprintMismatchException
"""
def VerifyCertThumbprint(derCert, thumbprint):
thumbprint_len = len(thumbprint)
if thumbprint_len == 40 and _isSha1Enabled:
Expand All @@ -49,3 +61,29 @@ def __init__(self, expected, actual):

self.expected = expected
self.actual = actual


"""
Verify that two PEM certificates match
:param actualCert: PEM-encoded SSL certificate
:type actualCert: str
:param expectedCert: PEM-encoded SSL certificate
:type actualCert: str
:returns: None
:raises CertificateMismatchException
"""
def VerifyCert(actualCert, expectedCert):
actualCert = actualCert.strip()
expectedCert = expectedCert.strip()
if actualCert != expectedCert:
raise CertificateMismatchException(expectedCert, actualCert)


class CertificateMismatchException(Exception):
def __init__(self, expected, actual):
Exception.__init__(self, "Certificate mismatch. Expected: \n{0}, "
"actual: \n{1}".format(expected, actual))

self.expected = expected
self.actual = actual
Loading

0 comments on commit 9a8956f

Please sign in to comment.