Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2258779

Browse files
committed
Issue #8813: Add SSLContext.verify_flags to change the verification flags
of the context in order to enable certification revocation list (CRL) checks or strict X509 rules.
1 parent e079edd commit 2258779

7 files changed

Lines changed: 179 additions & 1 deletion

File tree

Doc/library/ssl.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,38 @@ Constants
423423
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
424424
value of the ``ca_certs`` parameter to :func:`wrap_socket`.
425425

426+
.. data:: VERIFY_DEFAULT
427+
428+
Possible value for :attr:`SSLContext.verify_flags`. In this mode,
429+
certificate revocation lists (CRLs) are not checked. By default OpenSSL
430+
does neither require nor verify CRLs.
431+
432+
.. versionadded:: 3.4
433+
434+
.. data:: VERIFY_CRL_CHECK_LEAF
435+
436+
Possible value for :attr:`SSLContext.verify_flags`. In this mode, only the
437+
peer cert is check but non of the intermediate CA certificates. The mode
438+
requires a valid CRL that is signed by the peer cert's issuer (its direct
439+
ancestor CA). If no proper has been loaded
440+
:attr:`SSLContext.load_verify_locations`, validation will fail.
441+
442+
.. versionadded:: 3.4
443+
444+
.. data:: VERIFY_CRL_CHECK_CHAIN
445+
446+
Possible value for :attr:`SSLContext.verify_flags`. In this mode, CRLs of
447+
all certificates in the peer cert chain are checked.
448+
449+
.. versionadded:: 3.4
450+
451+
.. data:: VERIFY_X509_STRICT
452+
453+
Possible value for :attr:`SSLContext.verify_flags` to disable workarounds
454+
for broken X.509 certificates.
455+
456+
.. versionadded:: 3.4
457+
426458
.. data:: PROTOCOL_SSLv2
427459

428460
Selects SSL version 2 as the channel encryption protocol.
@@ -862,6 +894,10 @@ to speed up repeated connections from the same clients.
862894
other peers' certificates when :data:`verify_mode` is other than
863895
:data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified.
864896

897+
This method can also load certification revocation lists (CRLs) in PEM or
898+
or DER format. In order to make use of CRLs, :attr:`SSLContext.verify_flags`
899+
must be configured properly.
900+
865901
The *cafile* string, if present, is the path to a file of concatenated
866902
CA certificates in PEM format. See the discussion of
867903
:ref:`ssl-certificates` for more information about how to arrange the
@@ -880,6 +916,7 @@ to speed up repeated connections from the same clients.
880916
.. versionchanged:: 3.4
881917
New optional argument *cadata*
882918

919+
883920
.. method:: SSLContext.get_ca_certs(binary_form=False)
884921

885922
Get a list of loaded "certification authority" (CA) certificates. If the
@@ -1057,6 +1094,14 @@ to speed up repeated connections from the same clients.
10571094
The protocol version chosen when constructing the context. This attribute
10581095
is read-only.
10591096

1097+
.. attribute:: SSLContext.verify_flags
1098+
1099+
The flags for certificate verification operations. You can set flags like
1100+
:data:`VERIFY_CRL_CHECK_LEAF` by ORing them together. By default OpenSSL
1101+
does neither require nor verify certificate revocation lists (CRLs).
1102+
1103+
.. versionadded:: 3.4
1104+
10601105
.. attribute:: SSLContext.verify_mode
10611106

10621107
Whether to try to verify other peers' certificates and how to behave

Lib/ssl.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@
102102
SSLSyscallError, SSLEOFError,
103103
)
104104
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
105+
from _ssl import (VERIFY_DEFAULT, VERIFY_CRL_CHECK_LEAF, VERIFY_CRL_CHECK_CHAIN,
106+
VERIFY_X509_STRICT)
105107
from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj
106108
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
107109

Lib/test/make_ssl_certs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
[ CA_default ]
2929
dir = cadir
3030
database = $dir/index.txt
31+
crlnumber = $dir/crl.txt
3132
default_md = sha1
3233
default_days = 3600
34+
default_crl_days = 3600
3335
certificate = pycacert.pem
3436
private_key = pycakey.pem
3537
serial = $dir/serial
@@ -112,6 +114,8 @@ def make_ca():
112114
os.mkdir(TMP_CADIR)
113115
with open(os.path.join('cadir','index.txt'),'a+') as f:
114116
pass # empty file
117+
with open(os.path.join('cadir','crl.txt'),'a+') as f:
118+
r.write("00")
115119
with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
116120
f.write('unique_subject = no')
117121

@@ -129,6 +133,8 @@ def make_ca():
129133
'-keyfile', 'pycakey.pem', '-days', '3650',
130134
'-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ]
131135
check_call(['openssl'] + args)
136+
args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl']
137+
check_call(['openssl'] + args)
132138

133139
if __name__ == '__main__':
134140
os.chdir(here)

Lib/test/revocation.crl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-----BEGIN X509 CRL-----
2+
MIIBpjCBjwIBATANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJYWTEmMCQGA1UE
3+
CgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNVBAMMDW91ci1j
4+
YS1zZXJ2ZXIXDTEzMTEyMTE3MDg0N1oXDTIzMDkzMDE3MDg0N1qgDjAMMAoGA1Ud
5+
FAQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQCNJXC2mVKauEeN3LlQ3ZtM5gkH3ExH
6+
+i4bmJjtJn497WwvvoIeUdrmVXgJQR93RtV37hZwN0SXMLlNmUZPH4rHhihayw4m
7+
unCzVj/OhCCY7/TPjKuJ1O/0XhaLBpBVjQN7R/1ujoRKbSia/CD3vcn7Fqxzw7LK
8+
fSRCKRGTj1CZiuxrphtFchwALXSiFDy9mr2ZKhImcyq1PydfgEzU78APpOkMQsIC
9+
UNJ/cf3c9emzf+dUtcMEcejQ3mynBo4eIGg1EW42bz4q4hSjzQlKcBV0muw5qXhc
10+
HOxH2iTFhQ7SrvVuK/dM14rYM4B5mSX3nRC1kNmXpS9j3wJDhuwmjHed
11+
-----END X509 CRL-----

Lib/test/test_ssl.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ def data_file(*name):
4848
CAFILE_CACERT = data_file("capath", "5ed36f99.0")
4949

5050

51+
# empty CRL
52+
CRLFILE = data_file("revocation.crl")
53+
5154
# Two keys and certs signed by the same CA (for SNI tests)
5255
SIGNED_CERTFILE = data_file("keycert3.pem")
5356
SIGNED_CERTFILE2 = data_file("keycert4.pem")
@@ -631,7 +634,7 @@ def test_options(self):
631634
with self.assertRaises(ValueError):
632635
ctx.options = 0
633636

634-
def test_verify(self):
637+
def test_verify_mode(self):
635638
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
636639
# Default value
637640
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
@@ -646,6 +649,23 @@ def test_verify(self):
646649
with self.assertRaises(ValueError):
647650
ctx.verify_mode = 42
648651

652+
def test_verify_flags(self):
653+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
654+
# default value by OpenSSL
655+
self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT)
656+
ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF
657+
self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_LEAF)
658+
ctx.verify_flags = ssl.VERIFY_CRL_CHECK_CHAIN
659+
self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_CHAIN)
660+
ctx.verify_flags = ssl.VERIFY_DEFAULT
661+
self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT)
662+
# supports any value
663+
ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF | ssl.VERIFY_X509_STRICT
664+
self.assertEqual(ctx.verify_flags,
665+
ssl.VERIFY_CRL_CHECK_LEAF | ssl.VERIFY_X509_STRICT)
666+
with self.assertRaises(TypeError):
667+
ctx.verify_flags = None
668+
649669
def test_load_cert_chain(self):
650670
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
651671
# Combined key and cert in a single file
@@ -1771,6 +1791,47 @@ def test_getpeercert(self):
17711791
self.assertLess(before, after)
17721792
s.close()
17731793

1794+
def test_crl_check(self):
1795+
if support.verbose:
1796+
sys.stdout.write("\n")
1797+
1798+
server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
1799+
server_context.load_cert_chain(SIGNED_CERTFILE)
1800+
1801+
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
1802+
context.verify_mode = ssl.CERT_REQUIRED
1803+
context.load_verify_locations(SIGNING_CA)
1804+
context.verify_mode = ssl.CERT_REQUIRED
1805+
context.verify_flags = ssl.VERIFY_DEFAULT
1806+
1807+
# VERIFY_DEFAULT should pass
1808+
server = ThreadedEchoServer(context=server_context, chatty=True)
1809+
with server:
1810+
with context.wrap_socket(socket.socket()) as s:
1811+
s.connect((HOST, server.port))
1812+
cert = s.getpeercert()
1813+
self.assertTrue(cert, "Can't get peer certificate.")
1814+
1815+
# VERIFY_CRL_CHECK_LEAF without a loaded CRL file fails
1816+
context.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF
1817+
1818+
server = ThreadedEchoServer(context=server_context, chatty=True)
1819+
with server:
1820+
with context.wrap_socket(socket.socket()) as s:
1821+
with self.assertRaisesRegex(ssl.SSLError,
1822+
"certificate verify failed"):
1823+
s.connect((HOST, server.port))
1824+
1825+
# now load a CRL file. The CRL file is signed by the CA.
1826+
context.load_verify_locations(CRLFILE)
1827+
1828+
server = ThreadedEchoServer(context=server_context, chatty=True)
1829+
with server:
1830+
with context.wrap_socket(socket.socket()) as s:
1831+
s.connect((HOST, server.port))
1832+
cert = s.getpeercert()
1833+
self.assertTrue(cert, "Can't get peer certificate.")
1834+
17741835
def test_empty_cert(self):
17751836
"""Connecting with an empty cert file"""
17761837
bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir,

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ Core and Builtins
5959
Library
6060
-------
6161

62+
- Issue #8813: Add SSLContext.verify_flags to change the verification flags
63+
of the context in order to enable certification revocation list (CRL)
64+
checks or strict X509 rules.
65+
6266
- Issue #18294: Fix the zlib module to make it 64-bit safe.
6367

6468
- Issue #19682: Fix compatibility issue with old version of OpenSSL that

Modules/_ssl.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,44 @@ set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
22302230
return 0;
22312231
}
22322232

2233+
static PyObject *
2234+
get_verify_flags(PySSLContext *self, void *c)
2235+
{
2236+
X509_STORE *store;
2237+
unsigned long flags;
2238+
2239+
store = SSL_CTX_get_cert_store(self->ctx);
2240+
flags = X509_VERIFY_PARAM_get_flags(store->param);
2241+
return PyLong_FromUnsignedLong(flags);
2242+
}
2243+
2244+
static int
2245+
set_verify_flags(PySSLContext *self, PyObject *arg, void *c)
2246+
{
2247+
X509_STORE *store;
2248+
unsigned long new_flags, flags, set, clear;
2249+
2250+
if (!PyArg_Parse(arg, "k", &new_flags))
2251+
return -1;
2252+
store = SSL_CTX_get_cert_store(self->ctx);
2253+
flags = X509_VERIFY_PARAM_get_flags(store->param);
2254+
clear = flags & ~new_flags;
2255+
set = ~flags & new_flags;
2256+
if (clear) {
2257+
if (!X509_VERIFY_PARAM_clear_flags(store->param, clear)) {
2258+
_setSSLError(NULL, 0, __FILE__, __LINE__);
2259+
return -1;
2260+
}
2261+
}
2262+
if (set) {
2263+
if (!X509_VERIFY_PARAM_set_flags(store->param, set)) {
2264+
_setSSLError(NULL, 0, __FILE__, __LINE__);
2265+
return -1;
2266+
}
2267+
}
2268+
return 0;
2269+
}
2270+
22332271
static PyObject *
22342272
get_options(PySSLContext *self, void *c)
22352273
{
@@ -3048,6 +3086,8 @@ get_ca_certs(PySSLContext *self, PyObject *args)
30483086
static PyGetSetDef context_getsetlist[] = {
30493087
{"options", (getter) get_options,
30503088
(setter) set_options, NULL},
3089+
{"verify_flags", (getter) get_verify_flags,
3090+
(setter) set_verify_flags, NULL},
30513091
{"verify_mode", (getter) get_verify_mode,
30523092
(setter) set_verify_mode, NULL},
30533093
{NULL}, /* sentinel */
@@ -3761,6 +3801,15 @@ PyInit__ssl(void)
37613801
PY_SSL_CERT_OPTIONAL);
37623802
PyModule_AddIntConstant(m, "CERT_REQUIRED",
37633803
PY_SSL_CERT_REQUIRED);
3804+
/* CRL verification for verification_flags */
3805+
PyModule_AddIntConstant(m, "VERIFY_DEFAULT",
3806+
0);
3807+
PyModule_AddIntConstant(m, "VERIFY_CRL_CHECK_LEAF",
3808+
X509_V_FLAG_CRL_CHECK);
3809+
PyModule_AddIntConstant(m, "VERIFY_CRL_CHECK_CHAIN",
3810+
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
3811+
PyModule_AddIntConstant(m, "VERIFY_X509_STRICT",
3812+
X509_V_FLAG_X509_STRICT);
37643813

37653814
#ifdef _MSC_VER
37663815
/* Windows dwCertEncodingType */

0 commit comments

Comments
 (0)