Skip to content

Commit e3a9f1a

Browse files
author
Pablo Sole
committed
Fixes #255 -- Add verification methods to the Connection object
1 parent 1265b06 commit e3a9f1a

File tree

3 files changed

+119
-1
lines changed

3 files changed

+119
-1
lines changed

doc/api/ssl.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Context, Connection.
2727
VERIFY_PEER
2828
VERIFY_FAIL_IF_NO_PEER_CERT
2929
30-
These constants represent the verification mode used by the Context
30+
These constants represent the verification mode used by the Context and Connection
3131
object's :py:meth:`set_verify` method.
3232

3333

src/OpenSSL/SSL.py

+68
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,8 @@ def __init__(self, context, socket=None):
15621562
_lib.SSL_set_mode(self._ssl, _lib.SSL_MODE_AUTO_RETRY)
15631563
self._context = context
15641564
self._app_data = None
1565+
self._verify_helper = None
1566+
self._verify_callback = None
15651567

15661568
# References to strings used for Next Protocol Negotiation. OpenSSL's
15671569
# header files suggest that these might get copied at some point, but
@@ -1609,6 +1611,8 @@ def __getattr__(self, name):
16091611
return getattr(self._socket, name)
16101612

16111613
def _raise_ssl_error(self, ssl, result):
1614+
if self._verify_helper is not None:
1615+
self._verify_helper.raise_if_problem()
16121616
if self._context._verify_helper is not None:
16131617
self._context._verify_helper.raise_if_problem()
16141618
if self._context._npn_advertise_helper is not None:
@@ -2497,6 +2501,70 @@ def request_ocsp(self):
24972501
)
24982502
_openssl_assert(rc == 1)
24992503

2504+
def set_verify(self, mode, callback):
2505+
"""
2506+
Set the verification flags for this Connection object to *mode* and specify
2507+
that *callback* should be used for verification callbacks.
2508+
2509+
While a Connection will inherit the verification config from its Context,
2510+
it is also possible to change it once the Connection has been instantiated
2511+
already.
2512+
2513+
:param mode: The verify mode, this should be one of
2514+
:const:`VERIFY_NONE` and :const:`VERIFY_PEER`. If
2515+
:const:`VERIFY_PEER` is used, *mode* can be OR:ed with
2516+
:const:`VERIFY_FAIL_IF_NO_PEER_CERT` and
2517+
:const:`VERIFY_CLIENT_ONCE` to further control the behaviour.
2518+
:param callback: The Python callback to use. This should take five
2519+
arguments: A Connection object, an X509 object, and three integer
2520+
variables, which are in turn potential error number, error depth
2521+
and return code. *callback* should return True if verification
2522+
passes and False otherwise.
2523+
:return: None
2524+
2525+
See SSL_set_verify(3SSL) for further details.
2526+
"""
2527+
if not isinstance(mode, integer_types):
2528+
raise TypeError("mode must be an integer")
2529+
2530+
if not callable(callback):
2531+
raise TypeError("callback must be callable")
2532+
2533+
self._verify_helper = _VerifyHelper(callback)
2534+
self._verify_callback = self._verify_helper.callback
2535+
_lib.SSL_set_verify(self._ssl, mode, self._verify_callback)
2536+
2537+
def set_verify_depth(self, depth):
2538+
"""
2539+
Set the maximum depth for the certificate chain verification that shall
2540+
be allowed for this Connection object.
2541+
2542+
:param depth: An integer specifying the verify depth
2543+
:return: None
2544+
"""
2545+
if not isinstance(depth, integer_types):
2546+
raise TypeError("depth must be an integer")
2547+
2548+
_lib.SSL_set_verify_depth(self._ssl, depth)
2549+
2550+
def get_verify_mode(self):
2551+
"""
2552+
Retrieve the Connection object's verify mode, as set by
2553+
:meth:`set_verify`.
2554+
2555+
:return: The verify mode
2556+
"""
2557+
return _lib.SSL_get_verify_mode(self._ssl)
2558+
2559+
def get_verify_depth(self):
2560+
"""
2561+
Retrieve the Connection object's verify depth, as set by
2562+
:meth:`set_verify_depth`.
2563+
2564+
:return: The verify depth
2565+
"""
2566+
return _lib.SSL_get_verify_depth(self._ssl)
2567+
25002568

25012569
# This is similar to the initialization calls at the end of OpenSSL/crypto.py
25022570
# but is exercised mostly by the Context initializer.

tests/test_ssl.py

+50
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,17 @@ def test_verify_depth(self):
798798
context.set_verify_depth(11)
799799
assert context.get_verify_depth() == 11
800800

801+
def test_connection_verify_depth(self):
802+
"""
803+
`Connection.set_verify_depth` sets the number of certificates in
804+
a chain to follow before giving up. The value can be retrieved with
805+
`Connection.get_verify_depth`.
806+
"""
807+
context = Context(TLSv1_METHOD)
808+
connection = Connection(context, None)
809+
connection.set_verify_depth(11)
810+
assert connection.get_verify_depth() == 11
811+
801812
def _write_encrypted_pem(self, passphrase, tmpfile):
802813
"""
803814
Write a new private key out to a new file, encrypted using the given
@@ -1285,6 +1296,33 @@ def verify_callback(*args):
12851296

12861297
assert "silly verify failure" == str(exc.value)
12871298

1299+
def test_set_verify_callback_in_connection_object(self):
1300+
"""
1301+
The first argument passed to the verify callback is the
1302+
`Connection` instance for which verification is taking place.
1303+
"""
1304+
serverContext = Context(TLSv1_METHOD)
1305+
serverContext.use_privatekey(
1306+
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
1307+
serverContext.use_certificate(
1308+
load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
1309+
serverConnection = Connection(serverContext, None)
1310+
1311+
class VerifyCallback(object):
1312+
def callback(self, connection, *args):
1313+
self.connection = connection
1314+
return 1
1315+
1316+
verify = VerifyCallback()
1317+
clientContext = Context(TLSv1_METHOD)
1318+
clientConnection = Connection(clientContext, None)
1319+
clientConnection.set_verify(VERIFY_PEER, verify.callback)
1320+
clientConnection.set_connect_state()
1321+
1322+
handshake_in_memory(clientConnection, serverConnection)
1323+
1324+
assert verify.connection is clientConnection
1325+
12881326
def test_add_extra_chain_cert(self, tmpdir):
12891327
"""
12901328
`Context.add_extra_chain_cert` accepts an `X509`
@@ -1418,6 +1456,18 @@ def test_set_verify_mode(self):
14181456
VERIFY_PEER | VERIFY_CLIENT_ONCE, lambda *args: None)
14191457
assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
14201458

1459+
def test_connection_get_verify_mode(self):
1460+
"""
1461+
`Connection.get_verify_mode` returns the verify mode flags previously
1462+
passed to `Connection.set_verify`.
1463+
"""
1464+
context = Context(TLSv1_METHOD)
1465+
connection = Connection(context, None)
1466+
assert connection.get_verify_mode() == 0
1467+
connection.set_verify(
1468+
VERIFY_PEER | VERIFY_CLIENT_ONCE, lambda *args: None)
1469+
assert connection.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
1470+
14211471
@pytest.mark.parametrize('mode', [None, 1.0, object(), 'mode'])
14221472
def test_set_verify_wrong_mode_arg(self, mode):
14231473
"""

0 commit comments

Comments
 (0)