ssl_context.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. # Copyright 2014-present MongoDB, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you
  4. # may not use this file except in compliance with the License. You
  5. # may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  12. # implied. See the License for the specific language governing
  13. # permissions and limitations under the License.
  14. """A fake SSLContext implementation."""
  15. import ssl as _ssl
  16. import sys as _sys
  17. # PROTOCOL_TLS_CLIENT is Python 3.6+
  18. PROTOCOL_SSLv23 = getattr(_ssl, "PROTOCOL_TLS_CLIENT", _ssl.PROTOCOL_SSLv23)
  19. # Python 2.7.9+
  20. OP_NO_SSLv2 = getattr(_ssl, "OP_NO_SSLv2", 0)
  21. # Python 2.7.9+
  22. OP_NO_SSLv3 = getattr(_ssl, "OP_NO_SSLv3", 0)
  23. # Python 2.7.9+, OpenSSL 1.0.0+
  24. OP_NO_COMPRESSION = getattr(_ssl, "OP_NO_COMPRESSION", 0)
  25. # Python 3.7+, OpenSSL 1.1.0h+
  26. OP_NO_RENEGOTIATION = getattr(_ssl, "OP_NO_RENEGOTIATION", 0)
  27. # Python 2.7.9+
  28. HAS_SNI = getattr(_ssl, "HAS_SNI", False)
  29. IS_PYOPENSSL = False
  30. # Base Exception class
  31. SSLError = _ssl.SSLError
  32. try:
  33. # CPython 2.7.9+
  34. from ssl import SSLContext
  35. if hasattr(_ssl, "VERIFY_CRL_CHECK_LEAF"):
  36. from ssl import VERIFY_CRL_CHECK_LEAF
  37. # Python 3.7 uses OpenSSL's hostname matching implementation
  38. # making it the obvious version to start using SSLConext.check_hostname.
  39. # Python 3.6 might have been a good version, but it suffers
  40. # from https://bugs.python.org/issue32185.
  41. # We'll use our bundled match_hostname for older Python
  42. # versions, which also supports IP address matching
  43. # with Python < 3.5.
  44. CHECK_HOSTNAME_SAFE = _sys.version_info[:2] >= (3, 7)
  45. except ImportError:
  46. from pymongo.errors import ConfigurationError
  47. class SSLContext(object):
  48. """A fake SSLContext.
  49. This implements an API similar to ssl.SSLContext from python 3.2
  50. but does not implement methods or properties that would be
  51. incompatible with ssl.wrap_socket from python 2.7 < 2.7.9.
  52. You must pass protocol which must be one of the PROTOCOL_* constants
  53. defined in the ssl module. ssl.PROTOCOL_SSLv23 is recommended for maximum
  54. interoperability.
  55. """
  56. __slots__ = ('_cafile', '_certfile',
  57. '_keyfile', '_protocol', '_verify_mode')
  58. def __init__(self, protocol):
  59. self._cafile = None
  60. self._certfile = None
  61. self._keyfile = None
  62. self._protocol = protocol
  63. self._verify_mode = _ssl.CERT_NONE
  64. @property
  65. def protocol(self):
  66. """The protocol version chosen when constructing the context.
  67. This attribute is read-only.
  68. """
  69. return self._protocol
  70. def __get_verify_mode(self):
  71. """Whether to try to verify other peers' certificates and how to
  72. behave if verification fails. This attribute must be one of
  73. ssl.CERT_NONE, ssl.CERT_OPTIONAL or ssl.CERT_REQUIRED.
  74. """
  75. return self._verify_mode
  76. def __set_verify_mode(self, value):
  77. """Setter for verify_mode."""
  78. self._verify_mode = value
  79. verify_mode = property(__get_verify_mode, __set_verify_mode)
  80. def load_cert_chain(self, certfile, keyfile=None, password=None):
  81. """Load a private key and the corresponding certificate. The certfile
  82. string must be the path to a single file in PEM format containing the
  83. certificate as well as any number of CA certificates needed to
  84. establish the certificate's authenticity. The keyfile string, if
  85. present, must point to a file containing the private key. Otherwise
  86. the private key will be taken from certfile as well.
  87. """
  88. if password is not None:
  89. raise ConfigurationError(
  90. "Support for ssl_pem_passphrase requires "
  91. "python 2.7.9+ (pypy 2.5.1+), python 3 or "
  92. "PyOpenSSL")
  93. self._certfile = certfile
  94. self._keyfile = keyfile
  95. def load_verify_locations(self, cafile=None, dummy=None):
  96. """Load a set of "certification authority"(CA) certificates used to
  97. validate other peers' certificates when `~verify_mode` is other than
  98. ssl.CERT_NONE.
  99. """
  100. self._cafile = cafile
  101. def wrap_socket(self, sock, server_side=False,
  102. do_handshake_on_connect=True,
  103. suppress_ragged_eofs=True, dummy=None):
  104. """Wrap an existing Python socket sock and return an ssl.SSLSocket
  105. object.
  106. """
  107. return _ssl.wrap_socket(sock, keyfile=self._keyfile,
  108. certfile=self._certfile,
  109. server_side=server_side,
  110. cert_reqs=self._verify_mode,
  111. ssl_version=self._protocol,
  112. ca_certs=self._cafile,
  113. do_handshake_on_connect=do_handshake_on_connect,
  114. suppress_ragged_eofs=suppress_ragged_eofs)