aboutsummaryrefslogtreecommitdiff
path: root/src/OpenSSL/SSL.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/OpenSSL/SSL.py')
-rw-r--r--src/OpenSSL/SSL.py755
1 files changed, 340 insertions, 415 deletions
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
index 0687fc3..e5fea1e 100644
--- a/src/OpenSSL/SSL.py
+++ b/src/OpenSSL/SSL.py
@@ -6,16 +6,13 @@ from itertools import count, chain
from weakref import WeakValueDictionary
from errno import errorcode
-from cryptography.utils import deprecated
-
-from six import (
- binary_type as _binary_type, integer_types as integer_types, int2byte,
- indexbytes)
+from six import integer_types, int2byte, indexbytes
from OpenSSL._util import (
UNSPECIFIED as _UNSPECIFIED,
exception_from_error_queue as _exception_from_error_queue,
ffi as _ffi,
+ from_buffer as _from_buffer,
lib as _lib,
make_assert as _make_assert,
native as _native,
@@ -25,99 +22,108 @@ from OpenSSL._util import (
)
from OpenSSL.crypto import (
- FILETYPE_PEM, _PassphraseHelper, PKey, X509Name, X509, X509Store)
+ FILETYPE_PEM,
+ _PassphraseHelper,
+ PKey,
+ X509Name,
+ X509,
+ X509Store,
+)
__all__ = [
- 'OPENSSL_VERSION_NUMBER',
- 'SSLEAY_VERSION',
- 'SSLEAY_CFLAGS',
- 'SSLEAY_PLATFORM',
- 'SSLEAY_DIR',
- 'SSLEAY_BUILT_ON',
- 'SENT_SHUTDOWN',
- 'RECEIVED_SHUTDOWN',
- 'SSLv2_METHOD',
- 'SSLv3_METHOD',
- 'SSLv23_METHOD',
- 'TLSv1_METHOD',
- 'TLSv1_1_METHOD',
- 'TLSv1_2_METHOD',
- 'OP_NO_SSLv2',
- 'OP_NO_SSLv3',
- 'OP_NO_TLSv1',
- 'OP_NO_TLSv1_1',
- 'OP_NO_TLSv1_2',
- 'MODE_RELEASE_BUFFERS',
- 'OP_SINGLE_DH_USE',
- 'OP_SINGLE_ECDH_USE',
- 'OP_EPHEMERAL_RSA',
- 'OP_MICROSOFT_SESS_ID_BUG',
- 'OP_NETSCAPE_CHALLENGE_BUG',
- 'OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG',
- 'OP_SSLREF2_REUSE_CERT_TYPE_BUG',
- 'OP_MICROSOFT_BIG_SSLV3_BUFFER',
- 'OP_MSIE_SSLV2_RSA_PADDING',
- 'OP_SSLEAY_080_CLIENT_DH_BUG',
- 'OP_TLS_D5_BUG',
- 'OP_TLS_BLOCK_PADDING_BUG',
- 'OP_DONT_INSERT_EMPTY_FRAGMENTS',
- 'OP_CIPHER_SERVER_PREFERENCE',
- 'OP_TLS_ROLLBACK_BUG',
- 'OP_PKCS1_CHECK_1',
- 'OP_PKCS1_CHECK_2',
- 'OP_NETSCAPE_CA_DN_BUG',
- 'OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG',
- 'OP_NO_COMPRESSION',
- 'OP_NO_QUERY_MTU',
- 'OP_COOKIE_EXCHANGE',
- 'OP_NO_TICKET',
- 'OP_ALL',
- 'VERIFY_PEER',
- 'VERIFY_FAIL_IF_NO_PEER_CERT',
- 'VERIFY_CLIENT_ONCE',
- 'VERIFY_NONE',
- 'SESS_CACHE_OFF',
- 'SESS_CACHE_CLIENT',
- 'SESS_CACHE_SERVER',
- 'SESS_CACHE_BOTH',
- 'SESS_CACHE_NO_AUTO_CLEAR',
- 'SESS_CACHE_NO_INTERNAL_LOOKUP',
- 'SESS_CACHE_NO_INTERNAL_STORE',
- 'SESS_CACHE_NO_INTERNAL',
- 'SSL_ST_CONNECT',
- 'SSL_ST_ACCEPT',
- 'SSL_ST_MASK',
- 'SSL_CB_LOOP',
- 'SSL_CB_EXIT',
- 'SSL_CB_READ',
- 'SSL_CB_WRITE',
- 'SSL_CB_ALERT',
- 'SSL_CB_READ_ALERT',
- 'SSL_CB_WRITE_ALERT',
- 'SSL_CB_ACCEPT_LOOP',
- 'SSL_CB_ACCEPT_EXIT',
- 'SSL_CB_CONNECT_LOOP',
- 'SSL_CB_CONNECT_EXIT',
- 'SSL_CB_HANDSHAKE_START',
- 'SSL_CB_HANDSHAKE_DONE',
- 'Error',
- 'WantReadError',
- 'WantWriteError',
- 'WantX509LookupError',
- 'ZeroReturnError',
- 'SysCallError',
- 'SSLeay_version',
- 'Session',
- 'Context',
- 'Connection'
+ "OPENSSL_VERSION_NUMBER",
+ "SSLEAY_VERSION",
+ "SSLEAY_CFLAGS",
+ "SSLEAY_PLATFORM",
+ "SSLEAY_DIR",
+ "SSLEAY_BUILT_ON",
+ "SENT_SHUTDOWN",
+ "RECEIVED_SHUTDOWN",
+ "SSLv2_METHOD",
+ "SSLv3_METHOD",
+ "SSLv23_METHOD",
+ "TLSv1_METHOD",
+ "TLSv1_1_METHOD",
+ "TLSv1_2_METHOD",
+ "OP_NO_SSLv2",
+ "OP_NO_SSLv3",
+ "OP_NO_TLSv1",
+ "OP_NO_TLSv1_1",
+ "OP_NO_TLSv1_2",
+ "OP_NO_TLSv1_3",
+ "MODE_RELEASE_BUFFERS",
+ "OP_SINGLE_DH_USE",
+ "OP_SINGLE_ECDH_USE",
+ "OP_EPHEMERAL_RSA",
+ "OP_MICROSOFT_SESS_ID_BUG",
+ "OP_NETSCAPE_CHALLENGE_BUG",
+ "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",
+ "OP_SSLREF2_REUSE_CERT_TYPE_BUG",
+ "OP_MICROSOFT_BIG_SSLV3_BUFFER",
+ "OP_MSIE_SSLV2_RSA_PADDING",
+ "OP_SSLEAY_080_CLIENT_DH_BUG",
+ "OP_TLS_D5_BUG",
+ "OP_TLS_BLOCK_PADDING_BUG",
+ "OP_DONT_INSERT_EMPTY_FRAGMENTS",
+ "OP_CIPHER_SERVER_PREFERENCE",
+ "OP_TLS_ROLLBACK_BUG",
+ "OP_PKCS1_CHECK_1",
+ "OP_PKCS1_CHECK_2",
+ "OP_NETSCAPE_CA_DN_BUG",
+ "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",
+ "OP_NO_COMPRESSION",
+ "OP_NO_QUERY_MTU",
+ "OP_COOKIE_EXCHANGE",
+ "OP_NO_TICKET",
+ "OP_ALL",
+ "VERIFY_PEER",
+ "VERIFY_FAIL_IF_NO_PEER_CERT",
+ "VERIFY_CLIENT_ONCE",
+ "VERIFY_NONE",
+ "SESS_CACHE_OFF",
+ "SESS_CACHE_CLIENT",
+ "SESS_CACHE_SERVER",
+ "SESS_CACHE_BOTH",
+ "SESS_CACHE_NO_AUTO_CLEAR",
+ "SESS_CACHE_NO_INTERNAL_LOOKUP",
+ "SESS_CACHE_NO_INTERNAL_STORE",
+ "SESS_CACHE_NO_INTERNAL",
+ "SSL_ST_CONNECT",
+ "SSL_ST_ACCEPT",
+ "SSL_ST_MASK",
+ "SSL_CB_LOOP",
+ "SSL_CB_EXIT",
+ "SSL_CB_READ",
+ "SSL_CB_WRITE",
+ "SSL_CB_ALERT",
+ "SSL_CB_READ_ALERT",
+ "SSL_CB_WRITE_ALERT",
+ "SSL_CB_ACCEPT_LOOP",
+ "SSL_CB_ACCEPT_EXIT",
+ "SSL_CB_CONNECT_LOOP",
+ "SSL_CB_CONNECT_EXIT",
+ "SSL_CB_HANDSHAKE_START",
+ "SSL_CB_HANDSHAKE_DONE",
+ "Error",
+ "WantReadError",
+ "WantWriteError",
+ "WantX509LookupError",
+ "ZeroReturnError",
+ "SysCallError",
+ "SSLeay_version",
+ "Session",
+ "Context",
+ "Connection",
]
try:
_buffer = buffer
except NameError:
+
class _buffer(object):
pass
+
OPENSSL_VERSION_NUMBER = _lib.OPENSSL_VERSION_NUMBER
SSLEAY_VERSION = _lib.SSLEAY_VERSION
SSLEAY_CFLAGS = _lib.SSLEAY_CFLAGS
@@ -140,6 +146,10 @@ OP_NO_SSLv3 = _lib.SSL_OP_NO_SSLv3
OP_NO_TLSv1 = _lib.SSL_OP_NO_TLSv1
OP_NO_TLSv1_1 = _lib.SSL_OP_NO_TLSv1_1
OP_NO_TLSv1_2 = _lib.SSL_OP_NO_TLSv1_2
+try:
+ OP_NO_TLSv1_3 = _lib.SSL_OP_NO_TLSv1_3
+except AttributeError:
+ pass
MODE_RELEASE_BUFFERS = _lib.SSL_MODE_RELEASE_BUFFERS
@@ -191,17 +201,6 @@ SESS_CACHE_NO_INTERNAL = _lib.SSL_SESS_CACHE_NO_INTERNAL
SSL_ST_CONNECT = _lib.SSL_ST_CONNECT
SSL_ST_ACCEPT = _lib.SSL_ST_ACCEPT
SSL_ST_MASK = _lib.SSL_ST_MASK
-if _lib.Cryptography_HAS_SSL_ST:
- SSL_ST_INIT = _lib.SSL_ST_INIT
- SSL_ST_BEFORE = _lib.SSL_ST_BEFORE
- SSL_ST_OK = _lib.SSL_ST_OK
- SSL_ST_RENEGOTIATE = _lib.SSL_ST_RENEGOTIATE
- __all__.extend([
- 'SSL_ST_INIT',
- 'SSL_ST_BEFORE',
- 'SSL_ST_OK',
- 'SSL_ST_RENEGOTIATE',
- ])
SSL_CB_LOOP = _lib.SSL_CB_LOOP
SSL_CB_EXIT = _lib.SSL_CB_EXIT
@@ -330,97 +329,11 @@ class _VerifyHelper(_CallbackExceptionHelper):
return 0
self.callback = _ffi.callback(
- "int (*)(int, X509_STORE_CTX *)", wrapper)
-
-
-class _NpnAdvertiseHelper(_CallbackExceptionHelper):
- """
- Wrap a callback such that it can be used as an NPN advertisement callback.
- """
-
- def __init__(self, callback):
- _CallbackExceptionHelper.__init__(self)
-
- @wraps(callback)
- def wrapper(ssl, out, outlen, arg):
- try:
- conn = Connection._reverse_mapping[ssl]
- protos = callback(conn)
-
- # Join the protocols into a Python bytestring, length-prefixing
- # each element.
- protostr = b''.join(
- chain.from_iterable((int2byte(len(p)), p) for p in protos)
- )
-
- # Save our callback arguments on the connection object. This is
- # done to make sure that they don't get freed before OpenSSL
- # uses them. Then, return them appropriately in the output
- # parameters.
- conn._npn_advertise_callback_args = [
- _ffi.new("unsigned int *", len(protostr)),
- _ffi.new("unsigned char[]", protostr),
- ]
- outlen[0] = conn._npn_advertise_callback_args[0][0]
- out[0] = conn._npn_advertise_callback_args[1]
- return 0
- except Exception as e:
- self._problems.append(e)
- return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
-
- self.callback = _ffi.callback(
- "int (*)(SSL *, const unsigned char **, unsigned int *, void *)",
- wrapper
+ "int (*)(int, X509_STORE_CTX *)", wrapper
)
-class _NpnSelectHelper(_CallbackExceptionHelper):
- """
- Wrap a callback such that it can be used as an NPN selection callback.
- """
-
- def __init__(self, callback):
- _CallbackExceptionHelper.__init__(self)
-
- @wraps(callback)
- def wrapper(ssl, out, outlen, in_, inlen, arg):
- try:
- conn = Connection._reverse_mapping[ssl]
-
- # The string passed to us is actually made up of multiple
- # length-prefixed bytestrings. We need to split that into a
- # list.
- instr = _ffi.buffer(in_, inlen)[:]
- protolist = []
- while instr:
- length = indexbytes(instr, 0)
- proto = instr[1:length + 1]
- protolist.append(proto)
- instr = instr[length + 1:]
-
- # Call the callback
- outstr = callback(conn, protolist)
-
- # Save our callback arguments on the connection object. This is
- # done to make sure that they don't get freed before OpenSSL
- # uses them. Then, return them appropriately in the output
- # parameters.
- conn._npn_select_callback_args = [
- _ffi.new("unsigned char *", len(outstr)),
- _ffi.new("unsigned char[]", outstr),
- ]
- outlen[0] = conn._npn_select_callback_args[0][0]
- out[0] = conn._npn_select_callback_args[1]
- return 0
- except Exception as e:
- self._problems.append(e)
- return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
-
- self.callback = _ffi.callback(
- ("int (*)(SSL *, unsigned char **, unsigned char *, "
- "const unsigned char *, unsigned int, void *)"),
- wrapper
- )
+NO_OVERLAPPING_PROTOCOLS = object()
class _ALPNSelectHelper(_CallbackExceptionHelper):
@@ -443,34 +356,44 @@ class _ALPNSelectHelper(_CallbackExceptionHelper):
protolist = []
while instr:
encoded_len = indexbytes(instr, 0)
- proto = instr[1:encoded_len + 1]
+ proto = instr[1 : encoded_len + 1]
protolist.append(proto)
- instr = instr[encoded_len + 1:]
+ instr = instr[encoded_len + 1 :]
# Call the callback
- outstr = callback(conn, protolist)
-
- if not isinstance(outstr, _binary_type):
- raise TypeError("ALPN callback must return a bytestring.")
+ outbytes = callback(conn, protolist)
+ any_accepted = True
+ if outbytes is NO_OVERLAPPING_PROTOCOLS:
+ outbytes = b""
+ any_accepted = False
+ elif not isinstance(outbytes, bytes):
+ raise TypeError(
+ "ALPN callback must return a bytestring or the "
+ "special NO_OVERLAPPING_PROTOCOLS sentinel value."
+ )
# Save our callback arguments on the connection object to make
# sure that they don't get freed before OpenSSL can use them.
# Then, return them in the appropriate output parameters.
conn._alpn_select_callback_args = [
- _ffi.new("unsigned char *", len(outstr)),
- _ffi.new("unsigned char[]", outstr),
+ _ffi.new("unsigned char *", len(outbytes)),
+ _ffi.new("unsigned char[]", outbytes),
]
outlen[0] = conn._alpn_select_callback_args[0][0]
out[0] = conn._alpn_select_callback_args[1]
- return 0
+ if not any_accepted:
+ return _lib.SSL_TLSEXT_ERR_NOACK
+ return _lib.SSL_TLSEXT_ERR_OK
except Exception as e:
self._problems.append(e)
- return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
+ return _lib.SSL_TLSEXT_ERR_ALERT_FATAL
self.callback = _ffi.callback(
- ("int (*)(SSL *, unsigned char **, unsigned char *, "
- "const unsigned char *, unsigned int, void *)"),
- wrapper
+ (
+ "int (*)(SSL *, unsigned char **, unsigned char *, "
+ "const unsigned char *, unsigned int, void *)"
+ ),
+ wrapper,
)
@@ -513,7 +436,7 @@ class _OCSPServerCallbackHelper(_CallbackExceptionHelper):
# Call the callback.
ocsp_data = callback(conn, data)
- if not isinstance(ocsp_data, _binary_type):
+ if not isinstance(ocsp_data, bytes):
raise TypeError("OCSP callback must return a bytestring.")
# If the OCSP data was provided, we will pass it to OpenSSL.
@@ -582,7 +505,7 @@ class _OCSPClientCallbackHelper(_CallbackExceptionHelper):
ocsp_len = _lib.SSL_get_tlsext_status_ocsp_resp(ssl, ocsp_ptr)
if ocsp_len < 0:
# No OCSP data.
- ocsp_data = b''
+ ocsp_data = b""
else:
# Copy the OCSP data, then pass it to the callback.
ocsp_data = _ffi.buffer(ocsp_ptr[0], ocsp_len)[:]
@@ -614,7 +537,8 @@ def _asFileDescriptor(obj):
raise TypeError("argument must be an int, or have a fileno() method.")
elif fd < 0:
raise ValueError(
- "file descriptor cannot be a negative integer (%i)" % (fd,))
+ "file descriptor cannot be a negative integer (%i)" % (fd,)
+ )
return fd
@@ -638,11 +562,14 @@ def _make_requires(flag, error):
``Cryptography_HAS_NEXTPROTONEG``.
:param error: The string to be used in the exception if the flag is false.
"""
+
def _requires_decorator(func):
if not flag:
+
@wraps(func)
def explode(*args, **kwargs):
raise NotImplementedError(error)
+
return explode
else:
return func
@@ -650,18 +577,13 @@ def _make_requires(flag, error):
return _requires_decorator
-_requires_npn = _make_requires(
- _lib.Cryptography_HAS_NEXTPROTONEG, "NPN not available"
-)
-
-
_requires_alpn = _make_requires(
_lib.Cryptography_HAS_ALPN, "ALPN not available"
)
-_requires_sni = _make_requires(
- _lib.Cryptography_HAS_TLSEXT_HOSTNAME, "SNI not available"
+_requires_keylog = _make_requires(
+ getattr(_lib, "Cryptography_HAS_KEYLOG", None), "Key logging not available"
)
@@ -673,6 +595,7 @@ class Session(object):
.. versionadded:: 0.14
"""
+
pass
@@ -684,6 +607,7 @@ class Context(object):
:param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or
TLSv1_METHOD.
"""
+
_methods = {
SSLv2_METHOD: "SSLv2_method",
SSLv3_METHOD: "SSLv3_method",
@@ -695,7 +619,8 @@ class Context(object):
_methods = dict(
(identifier, getattr(_lib, name))
for (identifier, name) in _methods.items()
- if getattr(_lib, name, None) is not None)
+ if getattr(_lib, name, None) is not None
+ )
def __init__(self, method):
if not isinstance(method, integer_types):
@@ -713,14 +638,11 @@ class Context(object):
_openssl_assert(context != _ffi.NULL)
context = _ffi.gc(context, _lib.SSL_CTX_free)
- # If SSL_CTX_set_ecdh_auto is available then set it so the ECDH curve
- # will be auto-selected. This function was added in 1.0.2 and made a
- # noop in 1.1.0+ (where it is set automatically).
- try:
- res = _lib.SSL_CTX_set_ecdh_auto(context, 1)
- _openssl_assert(res == 1)
- except AttributeError:
- pass
+ # Set SSL_CTX_set_ecdh_auto so that the ECDH curve will be
+ # auto-selected. This function was added in 1.0.2 and made a noop in
+ # 1.1.0+ (where it is set automatically).
+ res = _lib.SSL_CTX_set_ecdh_auto(context, 1)
+ _openssl_assert(res == 1)
self._context = context
self._passphrase_helper = None
@@ -729,12 +651,9 @@ class Context(object):
self._verify_helper = None
self._verify_callback = None
self._info_callback = None
+ self._keylog_callback = None
self._tlsext_servername_callback = None
self._app_data = None
- self._npn_advertise_helper = None
- self._npn_advertise_callback = None
- self._npn_select_helper = None
- self._npn_select_callback = None
self._alpn_select_helper = None
self._alpn_select_callback = None
self._ocsp_helper = None
@@ -779,8 +698,10 @@ class Context(object):
@wraps(callback)
def wrapper(size, verify, userdata):
return callback(size, verify, self._passphrase_userdata)
+
return _PassphraseHelper(
- FILETYPE_PEM, wrapper, more_args=True, truncate=True)
+ FILETYPE_PEM, wrapper, more_args=True, truncate=True
+ )
def set_passwd_cb(self, callback, userdata=None):
"""
@@ -807,7 +728,8 @@ class Context(object):
self._passphrase_helper = self._wrap_callback(callback)
self._passphrase_callback = self._passphrase_helper.callback
_lib.SSL_CTX_set_default_passwd_cb(
- self._context, self._passphrase_callback)
+ self._context, self._passphrase_callback
+ )
self._passphrase_userdata = userdata
def set_default_verify_paths(self):
@@ -837,9 +759,9 @@ class Context(object):
# First we'll check to see if any env vars have been set. If so,
# we won't try to do anything else because the user has set the path
# themselves.
- dir_env_var = _ffi.string(
- _lib.X509_get_default_cert_dir_env()
- ).decode("ascii")
+ dir_env_var = _ffi.string(_lib.X509_get_default_cert_dir_env()).decode(
+ "ascii"
+ )
file_env_var = _ffi.string(
_lib.X509_get_default_cert_file_env()
).decode("ascii")
@@ -850,13 +772,12 @@ class Context(object):
# to the exact values we use in our manylinux1 builds. If they are
# then we know to load the fallbacks
if (
- default_dir == _CRYPTOGRAPHY_MANYLINUX1_CA_DIR and
- default_file == _CRYPTOGRAPHY_MANYLINUX1_CA_FILE
+ default_dir == _CRYPTOGRAPHY_MANYLINUX1_CA_DIR
+ and default_file == _CRYPTOGRAPHY_MANYLINUX1_CA_FILE
):
# This is manylinux1, let's load our fallback paths
self._fallback_default_verify_paths(
- _CERTIFICATE_FILE_LOCATIONS,
- _CERTIFICATE_PATH_LOCATIONS
+ _CERTIFICATE_FILE_LOCATIONS, _CERTIFICATE_PATH_LOCATIONS
)
def _check_env_vars_set(self, dir_env_var, file_env_var):
@@ -866,8 +787,8 @@ class Context(object):
:return: bool
"""
return (
- os.environ.get(file_env_var) is not None or
- os.environ.get(dir_env_var) is not None
+ os.environ.get(file_env_var) is not None
+ or os.environ.get(dir_env_var) is not None
)
def _fallback_default_verify_paths(self, file_path, dir_path):
@@ -985,7 +906,8 @@ class Context(object):
raise TypeError("filetype must be an integer")
use_result = _lib.SSL_CTX_use_PrivateKey_file(
- self._context, keyfile, filetype)
+ self._context, keyfile, filetype
+ )
if not use_result:
self._raise_passphrase_exception()
@@ -1041,11 +963,8 @@ class Context(object):
"""
buf = _text_to_bytes_and_warn("buf", buf)
_openssl_assert(
- _lib.SSL_CTX_set_session_id_context(
- self._context,
- buf,
- len(buf),
- ) == 1
+ _lib.SSL_CTX_set_session_id_context(self._context, buf, len(buf))
+ == 1
)
def set_session_cache_mode(self, mode):
@@ -1075,21 +994,22 @@ class Context(object):
"""
return _lib.SSL_CTX_get_session_cache_mode(self._context)
- def set_verify(self, mode, callback):
+ def set_verify(self, mode, callback=None):
"""
- et the verification flags for this Context object to *mode* and specify
- that *callback* should be used for verification callbacks.
+ Set the verification flags for this Context object to *mode* and
+ specify that *callback* should be used for verification callbacks.
:param mode: The verify mode, this should be one of
:const:`VERIFY_NONE` and :const:`VERIFY_PEER`. If
:const:`VERIFY_PEER` is used, *mode* can be OR:ed with
:const:`VERIFY_FAIL_IF_NO_PEER_CERT` and
:const:`VERIFY_CLIENT_ONCE` to further control the behaviour.
- :param callback: The Python callback to use. This should take five
- arguments: A Connection object, an X509 object, and three integer
- variables, which are in turn potential error number, error depth
- and return code. *callback* should return True if verification
- passes and False otherwise.
+ :param callback: The optional Python verification callback to use.
+ This should take five arguments: A Connection object, an X509
+ object, and three integer variables, which are in turn potential
+ error number, error depth and return code. *callback* should
+ return True if verification passes and False otherwise.
+ If omitted, OpenSSL's default verification is used.
:return: None
See SSL_CTX_set_verify(3SSL) for further details.
@@ -1097,12 +1017,17 @@ class Context(object):
if not isinstance(mode, integer_types):
raise TypeError("mode must be an integer")
- if not callable(callback):
- raise TypeError("callback must be callable")
+ if callback is None:
+ self._verify_helper = None
+ self._verify_callback = None
+ _lib.SSL_CTX_set_verify(self._context, mode, _ffi.NULL)
+ else:
+ if not callable(callback):
+ raise TypeError("callback must be callable")
- self._verify_helper = _VerifyHelper(callback)
- self._verify_callback = self._verify_helper.callback
- _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback)
+ self._verify_helper = _VerifyHelper(callback)
+ self._verify_callback = self._verify_helper.callback
+ _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback)
def set_verify_depth(self, depth):
"""
@@ -1153,7 +1078,8 @@ class Context(object):
dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
dh = _ffi.gc(dh, _lib.DH_free)
- _lib.SSL_CTX_set_tmp_dh(self._context, dh)
+ res = _lib.SSL_CTX_set_tmp_dh(self._context, dh)
+ _openssl_assert(res == 1)
def set_tmp_ecdh(self, curve):
"""
@@ -1191,13 +1117,20 @@ class Context(object):
# invalid cipher string is passed, but without the following check
# for the TLS 1.3 specific cipher suites it would never error.
tmpconn = Connection(self, None)
- _openssl_assert(
- tmpconn.get_cipher_list() != [
- 'TLS_AES_256_GCM_SHA384',
- 'TLS_CHACHA20_POLY1305_SHA256',
- 'TLS_AES_128_GCM_SHA256'
- ]
- )
+ if tmpconn.get_cipher_list() == [
+ "TLS_AES_256_GCM_SHA384",
+ "TLS_CHACHA20_POLY1305_SHA256",
+ "TLS_AES_128_GCM_SHA256",
+ ]:
+ raise Error(
+ [
+ (
+ "SSL routines",
+ "SSL_CTX_set_cipher_list",
+ "no cipher match",
+ ),
+ ],
+ )
def set_client_ca_list(self, certificate_authorities):
"""
@@ -1220,9 +1153,7 @@ class Context(object):
if not isinstance(ca_name, X509Name):
raise TypeError(
"client CAs must be X509Name objects, not %s "
- "objects" % (
- type(ca_name).__name__,
- )
+ "objects" % (type(ca_name).__name__,)
)
copy = _lib.X509_NAME_dup(ca_name._name)
_openssl_assert(copy != _ffi.NULL)
@@ -1253,7 +1184,8 @@ class Context(object):
raise TypeError("certificate_authority must be an X509 instance")
add_result = _lib.SSL_CTX_add_client_CA(
- self._context, certificate_authority._x509)
+ self._context, certificate_authority._x509
+ )
_openssl_assert(add_result == 1)
def set_timeout(self, timeout):
@@ -1291,13 +1223,41 @@ class Context(object):
function call.
:return: None
"""
+
@wraps(callback)
def wrapper(ssl, where, return_code):
callback(Connection._reverse_mapping[ssl], where, return_code)
+
self._info_callback = _ffi.callback(
- "void (*)(const SSL *, int, int)", wrapper)
+ "void (*)(const SSL *, int, int)", wrapper
+ )
_lib.SSL_CTX_set_info_callback(self._context, self._info_callback)
+ @_requires_keylog
+ def set_keylog_callback(self, callback):
+ """
+ Set the TLS key logging callback to *callback*. This function will be
+ called whenever TLS key material is generated or received, in order
+ to allow applications to store this keying material for debugging
+ purposes.
+
+ :param callback: The Python callback to use. This should take two
+ arguments: a Connection object and a bytestring that contains
+ the key material in the format used by NSS for its SSLKEYLOGFILE
+ debugging output.
+ :return: None
+ """
+
+ @wraps(callback)
+ def wrapper(ssl, line):
+ line = _ffi.string(line)
+ callback(Connection._reverse_mapping[ssl], line)
+
+ self._keylog_callback = _ffi.callback(
+ "void (*)(const SSL *, const char *)", wrapper
+ )
+ _lib.SSL_CTX_set_keylog_callback(self._context, self._keylog_callback)
+
def get_app_data(self):
"""
Get the application data (supplied via :meth:`set_app_data()`)
@@ -1358,7 +1318,6 @@ class Context(object):
return _lib.SSL_CTX_set_mode(self._context, mode)
- @_requires_sni
def set_tlsext_servername_callback(self, callback):
"""
Specify a callback function to be called when clients specify a server
@@ -1369,15 +1328,18 @@ class Context(object):
.. versionadded:: 0.13
"""
+
@wraps(callback)
def wrapper(ssl, alert, arg):
callback(Connection._reverse_mapping[ssl])
return 0
self._tlsext_servername_callback = _ffi.callback(
- "int (*)(SSL *, int *, void *)", wrapper)
+ "int (*)(SSL *, int *, void *)", wrapper
+ )
_lib.SSL_CTX_set_tlsext_servername_callback(
- self._context, self._tlsext_servername_callback)
+ self._context, self._tlsext_servername_callback
+ )
def set_tlsext_use_srtp(self, profiles):
"""
@@ -1394,43 +1356,6 @@ class Context(object):
_lib.SSL_CTX_set_tlsext_use_srtp(self._context, profiles) == 0
)
- @_requires_npn
- def set_npn_advertise_callback(self, callback):
- """
- Specify a callback function that will be called when offering `Next
- Protocol Negotiation
- <https://technotes.googlecode.com/git/nextprotoneg.html>`_ as a server.
-
- :param callback: The callback function. It will be invoked with one
- argument, the :class:`Connection` instance. It should return a
- list of bytestrings representing the advertised protocols, like
- ``[b'http/1.1', b'spdy/2']``.
-
- .. versionadded:: 0.15
- """
- self._npn_advertise_helper = _NpnAdvertiseHelper(callback)
- self._npn_advertise_callback = self._npn_advertise_helper.callback
- _lib.SSL_CTX_set_next_protos_advertised_cb(
- self._context, self._npn_advertise_callback, _ffi.NULL)
-
- @_requires_npn
- def set_npn_select_callback(self, callback):
- """
- Specify a callback function that will be called when a server offers
- Next Protocol Negotiation options.
-
- :param callback: The callback function. It will be invoked with two
- arguments: the Connection, and a list of offered protocols as
- bytestrings, e.g. ``[b'http/1.1', b'spdy/2']``. It should return
- one of those bytestrings, the chosen protocol.
-
- .. versionadded:: 0.15
- """
- self._npn_select_helper = _NpnSelectHelper(callback)
- self._npn_select_callback = self._npn_select_helper.callback
- _lib.SSL_CTX_set_next_proto_select_cb(
- self._context, self._npn_select_callback, _ffi.NULL)
-
@_requires_alpn
def set_alpn_protos(self, protos):
"""
@@ -1444,7 +1369,7 @@ class Context(object):
"""
# Take the list of protocols and join them together, prefixing them
# with their lengths.
- protostr = b''.join(
+ protostr = b"".join(
chain.from_iterable((int2byte(len(p)), p) for p in protos)
)
@@ -1461,13 +1386,18 @@ class Context(object):
:param callback: The callback function. It will be invoked with two
arguments: the Connection, and a list of offered protocols as
- bytestrings, e.g ``[b'http/1.1', b'spdy/2']``. It should return
- one of those bytestrings, the chosen protocol.
+ bytestrings, e.g ``[b'http/1.1', b'spdy/2']``. It can return
+ one of those bytestrings to indicate the chosen protocol, the
+ empty bytestring to terminate the TLS connection, or the
+ :py:obj:`NO_OVERLAPPING_PROTOCOLS` to indicate that no offered
+ protocol was selected, but that the connection should not be
+ aborted.
"""
self._alpn_select_helper = _ALPNSelectHelper(callback)
self._alpn_select_callback = self._alpn_select_helper.callback
_lib.SSL_CTX_set_alpn_select_cb(
- self._context, self._alpn_select_callback, _ffi.NULL)
+ self._context, self._alpn_select_callback, _ffi.NULL
+ )
def _set_ocsp_callback(self, helper, data):
"""
@@ -1528,15 +1458,7 @@ class Context(object):
self._set_ocsp_callback(helper, data)
-ContextType = deprecated(
- Context, __name__,
- "ContextType has been deprecated, use Context instead", DeprecationWarning
-)
-
-
class Connection(object):
- """
- """
_reverse_mapping = WeakValueDictionary()
def __init__(self, context, socket=None):
@@ -1560,19 +1482,18 @@ class Connection(object):
self._context = context
self._app_data = None
- # References to strings used for Next Protocol Negotiation. OpenSSL's
- # header files suggest that these might get copied at some point, but
- # doesn't specify when, so we store them here to make sure they don't
- # get freed before OpenSSL uses them.
- self._npn_advertise_callback_args = None
- self._npn_select_callback_args = None
-
# References to strings used for Application Layer Protocol
# Negotiation. These strings get copied at some point but it's well
# after the callback returns, so we have to hang them somewhere to
# avoid them getting freed.
self._alpn_select_callback_args = None
+ # Reference the verify_callback of the Context. This ensures that if
+ # set_verify is called again after the SSL object has been created we
+ # do not point to a dangling reference
+ self._verify_helper = context._verify_helper
+ self._verify_callback = context._verify_callback
+
self._reverse_mapping[self._ssl] = self
if socket is None:
@@ -1590,7 +1511,8 @@ class Connection(object):
self._from_ssl = None
self._socket = socket
set_result = _lib.SSL_set_fd(
- self._ssl, _asFileDescriptor(self._socket))
+ self._ssl, _asFileDescriptor(self._socket)
+ )
_openssl_assert(set_result == 1)
def __getattr__(self, name):
@@ -1599,19 +1521,16 @@ class Connection(object):
on the Connection object.
"""
if self._socket is None:
- raise AttributeError("'%s' object has no attribute '%s'" % (
- self.__class__.__name__, name
- ))
+ raise AttributeError(
+ "'%s' object has no attribute '%s'"
+ % (self.__class__.__name__, name)
+ )
else:
return getattr(self._socket, name)
def _raise_ssl_error(self, ssl, result):
if self._context._verify_helper is not None:
self._context._verify_helper.raise_if_problem()
- if self._context._npn_advertise_helper is not None:
- self._context._npn_advertise_helper.raise_if_problem()
- if self._context._npn_select_helper is not None:
- self._context._npn_select_helper.raise_if_problem()
if self._context._alpn_select_helper is not None:
self._context._alpn_select_helper.raise_if_problem()
if self._context._ocsp_helper is not None:
@@ -1666,7 +1585,6 @@ class Connection(object):
_lib.SSL_set_SSL_CTX(self._ssl, context._context)
self._context = context
- @_requires_sni
def get_servername(self):
"""
Retrieve the servername extension value if provided in the client hello
@@ -1684,7 +1602,6 @@ class Connection(object):
return _ffi.string(name)
- @_requires_sni
def set_tlsext_host_name(self, name):
"""
Set the value of the servername extension to send in the client hello.
@@ -1724,18 +1641,18 @@ class Connection(object):
# Backward compatibility
buf = _text_to_bytes_and_warn("buf", buf)
- if isinstance(buf, memoryview):
- buf = buf.tobytes()
- if isinstance(buf, _buffer):
- buf = str(buf)
- if not isinstance(buf, bytes):
- raise TypeError("data must be a memoryview, buffer or byte string")
- if len(buf) > 2147483647:
- raise ValueError("Cannot send more than 2**31-1 bytes at once.")
+ with _from_buffer(buf) as data:
+ # check len(buf) instead of len(data) for testability
+ if len(buf) > 2147483647:
+ raise ValueError(
+ "Cannot send more than 2**31-1 bytes at once."
+ )
+
+ result = _lib.SSL_write(self._ssl, data, len(data))
+ self._raise_ssl_error(self._ssl, result)
+
+ return result
- result = _lib.SSL_write(self._ssl, buf, len(buf))
- self._raise_ssl_error(self._ssl, result)
- return result
write = send
def sendall(self, buf, flags=0):
@@ -1751,28 +1668,22 @@ class Connection(object):
"""
buf = _text_to_bytes_and_warn("buf", buf)
- if isinstance(buf, memoryview):
- buf = buf.tobytes()
- if isinstance(buf, _buffer):
- buf = str(buf)
- if not isinstance(buf, bytes):
- raise TypeError("buf must be a memoryview, buffer or byte string")
-
- left_to_send = len(buf)
- total_sent = 0
- data = _ffi.new("char[]", buf)
-
- while left_to_send:
- # SSL_write's num arg is an int,
- # so we cannot send more than 2**31-1 bytes at once.
- result = _lib.SSL_write(
- self._ssl,
- data + total_sent,
- min(left_to_send, 2147483647)
- )
- self._raise_ssl_error(self._ssl, result)
- total_sent += result
- left_to_send -= result
+ with _from_buffer(buf) as data:
+
+ left_to_send = len(buf)
+ total_sent = 0
+
+ while left_to_send:
+ # SSL_write's num arg is an int,
+ # so we cannot send more than 2**31-1 bytes at once.
+ result = _lib.SSL_write(
+ self._ssl, data + total_sent, min(left_to_send, 2147483647)
+ )
+ self._raise_ssl_error(self._ssl, result)
+ total_sent += result
+ left_to_send -= result
+
+ return total_sent
def recv(self, bufsiz, flags=None):
"""
@@ -1790,6 +1701,7 @@ class Connection(object):
result = _lib.SSL_read(self._ssl, buf, bufsiz)
self._raise_ssl_error(self._ssl, result)
return _ffi.buffer(buf, result)[:]
+
read = recv
def recv_into(self, buffer, nbytes=None, flags=None):
@@ -1886,10 +1798,11 @@ class Connection(object):
if self._into_ssl is None:
raise TypeError("Connection sock was not None")
- result = _lib.BIO_write(self._into_ssl, buf, len(buf))
- if result <= 0:
- self._handle_bio_errors(self._into_ssl, result)
- return result
+ with _from_buffer(buf) as data:
+ result = _lib.BIO_write(self._into_ssl, data, len(data))
+ if result <= 0:
+ self._handle_bio_errors(self._into_ssl, result)
+ return result
def renegotiate(self):
"""
@@ -1906,7 +1819,7 @@ class Connection(object):
def do_handshake(self):
"""
Perform an SSL handshake (usually called after :meth:`renegotiate` or
- one of :meth:`set_accept_state` or :meth:`set_accept_state`). This can
+ one of :meth:`set_accept_state` or :meth:`set_connect_state`). This can
raise the same exceptions as :meth:`send` and :meth:`recv`.
:return: None.
@@ -2055,7 +1968,8 @@ class Connection(object):
:raise: NotImplementedError
"""
raise NotImplementedError(
- "Cannot make file object of OpenSSL.SSL.Connection")
+ "Cannot make file object of OpenSSL.SSL.Connection"
+ )
def get_app_data(self):
"""
@@ -2114,7 +2028,7 @@ class Connection(object):
if session == _ffi.NULL:
return None
length = _lib.SSL_get_server_random(self._ssl, _ffi.NULL, 0)
- assert length > 0
+ _openssl_assert(length > 0)
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_get_server_random(self._ssl, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2130,7 +2044,7 @@ class Connection(object):
return None
length = _lib.SSL_get_client_random(self._ssl, _ffi.NULL, 0)
- assert length > 0
+ _openssl_assert(length > 0)
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_get_client_random(self._ssl, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2146,7 +2060,7 @@ class Connection(object):
return None
length = _lib.SSL_SESSION_get_master_key(session, _ffi.NULL, 0)
- assert length > 0
+ _openssl_assert(length > 0)
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_SESSION_get_master_key(session, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2168,10 +2082,16 @@ class Connection(object):
context_buf = context
context_len = len(context)
use_context = 1
- success = _lib.SSL_export_keying_material(self._ssl, outp, olen,
- label, len(label),
- context_buf, context_len,
- use_context)
+ success = _lib.SSL_export_keying_material(
+ self._ssl,
+ outp,
+ olen,
+ label,
+ len(label),
+ context_buf,
+ context_len,
+ use_context,
+ )
_openssl_assert(success == 1)
return _ffi.buffer(outp, olen)[:]
@@ -2207,6 +2127,22 @@ class Connection(object):
return X509._from_raw_x509_ptr(cert)
return None
+ @staticmethod
+ def _cert_stack_to_list(cert_stack):
+ """
+ Internal helper to convert a STACK_OF(X509) to a list of X509
+ instances.
+ """
+ result = []
+ for i in range(_lib.sk_X509_num(cert_stack)):
+ cert = _lib.sk_X509_value(cert_stack, i)
+ _openssl_assert(cert != _ffi.NULL)
+ res = _lib.X509_up_ref(cert)
+ _openssl_assert(res >= 1)
+ pycert = X509._from_raw_x509_ptr(cert)
+ result.append(pycert)
+ return result
+
def get_peer_cert_chain(self):
"""
Retrieve the other side's certificate (if any)
@@ -2218,13 +2154,26 @@ class Connection(object):
if cert_stack == _ffi.NULL:
return None
- result = []
- for i in range(_lib.sk_X509_num(cert_stack)):
- # TODO could incref instead of dup here
- cert = _lib.X509_dup(_lib.sk_X509_value(cert_stack, i))
- pycert = X509._from_raw_x509_ptr(cert)
- result.append(pycert)
- return result
+ return self._cert_stack_to_list(cert_stack)
+
+ def get_verified_chain(self):
+ """
+ Retrieve the verified certificate chain of the peer including the
+ peer's end entity certificate. It must be called after a session has
+ been successfully established. If peer verification was not successful
+ the chain may be incomplete, invalid, or None.
+
+ :return: A list of X509 instances giving the peer's verified
+ certificate chain, or None if it does not have one.
+
+ .. versionadded:: 20.0
+ """
+ # OpenSSL 1.1+
+ cert_stack = _lib.SSL_get0_verified_chain(self._ssl)
+ if cert_stack == _ffi.NULL:
+ return None
+
+ return self._cert_stack_to_list(cert_stack)
def want_read(self):
"""
@@ -2292,8 +2241,7 @@ class Connection(object):
raise TypeError("session must be a Session instance")
result = _lib.SSL_set_session(self._ssl, session._session)
- if not result:
- _raise_current_error()
+ _openssl_assert(result == 1)
def _get_finished_message(self, function):
"""
@@ -2426,23 +2374,6 @@ class Connection(object):
version = _lib.SSL_version(self._ssl)
return version
- @_requires_npn
- def get_next_proto_negotiated(self):
- """
- Get the protocol that was negotiated by NPN.
-
- :returns: A bytestring of the protocol name. If no protocol has been
- negotiated yet, returns an empty string.
-
- .. versionadded:: 0.15
- """
- data = _ffi.new("unsigned char **")
- data_len = _ffi.new("unsigned int *")
-
- _lib.SSL_get0_next_proto_negotiated(self._ssl, data, data_len)
-
- return _ffi.buffer(data[0], data_len[0])[:]
-
@_requires_alpn
def set_alpn_protos(self, protos):
"""
@@ -2456,7 +2387,7 @@ class Connection(object):
"""
# Take the list of protocols and join them together, prefixing them
# with their lengths.
- protostr = b''.join(
+ protostr = b"".join(
chain.from_iterable((int2byte(len(p)), p) for p in protos)
)
@@ -2479,7 +2410,7 @@ class Connection(object):
_lib.SSL_get0_alpn_selected(self._ssl, data, data_len)
if not data_len:
- return b''
+ return b""
return _ffi.buffer(data[0], data_len[0])[:]
@@ -2496,12 +2427,6 @@ class Connection(object):
_openssl_assert(rc == 1)
-ConnectionType = deprecated(
- Connection, __name__,
- "ConnectionType has been deprecated, use Connection instead",
- DeprecationWarning
-)
-
# This is similar to the initialization calls at the end of OpenSSL/crypto.py
# but is exercised mostly by the Context initializer.
_lib.SSL_library_init()