summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2013-11-21 02:40:15 (GMT)
committerChristian Heimes <christian@cheimes.de>2013-11-21 02:40:15 (GMT)
commitbd3a7f90b51c6bd7d057bad9d7addd0bed8a6e7d (patch)
tree15f4994108480a9d51bdc38c444c532cb7e11474
parentefff7060f893c3a06543ab955696663a64eb2109 (diff)
downloadcpython-bd3a7f90b51c6bd7d057bad9d7addd0bed8a6e7d.zip
cpython-bd3a7f90b51c6bd7d057bad9d7addd0bed8a6e7d.tar.gz
cpython-bd3a7f90b51c6bd7d057bad9d7addd0bed8a6e7d.tar.bz2
Issue #18379: SSLSocket.getpeercert() returns CA issuer AIA fields, OCSP
and CRL distribution points.
-rw-r--r--Doc/library/ssl.rst4
-rw-r--r--Lib/test/test_ssl.py8
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_ssl.c152
4 files changed, 165 insertions, 2 deletions
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 943a8f8..ce37c95 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -733,6 +733,10 @@ SSL sockets also have the following additional methods and attributes:
.. versionchanged:: 3.4
:exc:`ValueError` is raised when the handshake isn't done.
+ .. versionchanged:: 3.4
+ The returned dictionary includes additional X509v3 extension items
+ such as ``crlDistributionPoints``, ``caIssuers`` and ``OCSP`` URIs.
+
.. method:: SSLSocket.cipher()
Returns a three-value tuple containing the name of the cipher being used, the
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index b4eef36..8016728 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -212,6 +212,12 @@ class BasicSocketTests(unittest.TestCase):
(('DNS', 'projects.developer.nokia.com'),
('DNS', 'projects.forum.nokia.com'))
)
+ # extra OCSP and AIA fields
+ self.assertEqual(p['OCSP'], ('http://ocsp.verisign.com',))
+ self.assertEqual(p['caIssuers'],
+ ('http://SVRIntl-G3-aia.verisign.com/SVRIntlG3.cer',))
+ self.assertEqual(p['crlDistributionPoints'],
+ ('http://SVRIntl-G3-crl.verisign.com/SVRIntlG3.crl',))
def test_parse_cert_CVE_2013_4238(self):
p = ssl._ssl._test_decode_cert(NULLBYTECERT)
@@ -905,6 +911,7 @@ class ContextTests(unittest.TestCase):
'notAfter': asn1time('Mar 29 12:29:49 2033 GMT'),
'notBefore': asn1time('Mar 30 12:29:49 2003 GMT'),
'serialNumber': '00',
+ 'crlDistributionPoints': ('https://www.cacert.org/revoke.crl',),
'subject': ((('organizationName', 'Root CA'),),
(('organizationalUnitName', 'http://www.cacert.org'),),
(('commonName', 'CA Cert Signing Authority'),),
@@ -1269,7 +1276,6 @@ class NetworkedTests(unittest.TestCase):
s.close()
self.assertEqual(len(ctx.get_ca_certs()), 1)
-
try:
import threading
except ImportError:
diff --git a/Misc/NEWS b/Misc/NEWS
index a41a5d0..443802b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -59,6 +59,9 @@ Core and Builtins
Library
-------
+- Issue #18379: SSLSocket.getpeercert() returns CA issuer AIA fields, OCSP
+ and CRL distribution points.
+
- Issue #18138: Implement cadata argument of SSLContext.load_verify_location()
to load CA certificates and CRL from memory. It supports PEM and DER
encoded strings.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index c61c38b..8f3cc71 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -965,6 +965,120 @@ _get_peer_alt_names (X509 *certificate) {
}
static PyObject *
+_get_aia_uri(X509 *certificate, int nid) {
+ PyObject *lst = NULL, *ostr = NULL;
+ int i, result;
+ AUTHORITY_INFO_ACCESS *info;
+
+ info = X509_get_ext_d2i(certificate, NID_info_access, NULL, NULL);
+ if ((info == NULL) || (sk_ACCESS_DESCRIPTION_num(info) == 0)) {
+ return Py_None;
+ }
+
+ if ((lst = PyList_New(0)) == NULL) {
+ goto fail;
+ }
+
+ for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) {
+ ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i);
+ ASN1_IA5STRING *uri;
+
+ if ((OBJ_obj2nid(ad->method) != nid) ||
+ (ad->location->type != GEN_URI)) {
+ continue;
+ }
+ uri = ad->location->d.uniformResourceIdentifier;
+ ostr = PyUnicode_FromStringAndSize((char *)uri->data,
+ uri->length);
+ if (ostr == NULL) {
+ goto fail;
+ }
+ result = PyList_Append(lst, ostr);
+ Py_DECREF(ostr);
+ if (result < 0) {
+ goto fail;
+ }
+ }
+ AUTHORITY_INFO_ACCESS_free(info);
+
+ /* convert to tuple or None */
+ if (PyList_Size(lst) == 0) {
+ Py_DECREF(lst);
+ return Py_None;
+ } else {
+ PyObject *tup;
+ tup = PyList_AsTuple(lst);
+ Py_DECREF(lst);
+ return tup;
+ }
+
+ fail:
+ AUTHORITY_INFO_ACCESS_free(info);
+ Py_DECREF(lst);
+ return NULL;
+}
+
+static PyObject *
+_get_crl_dp(X509 *certificate) {
+ STACK_OF(DIST_POINT) *dps;
+ int i, j, result;
+ PyObject *lst;
+
+ /* Calls x509v3_cache_extensions and sets up crldp */
+ X509_check_ca(certificate);
+ dps = certificate->crldp;
+ if (dps == NULL) {
+ return Py_None;
+ }
+
+ if ((lst = PyList_New(0)) == NULL) {
+ return NULL;
+ }
+
+ for (i=0; i < sk_DIST_POINT_num(dps); i++) {
+ DIST_POINT *dp;
+ STACK_OF(GENERAL_NAME) *gns;
+
+ dp = sk_DIST_POINT_value(dps, i);
+ gns = dp->distpoint->name.fullname;
+
+ for (j=0; j < sk_GENERAL_NAME_num(gns); j++) {
+ GENERAL_NAME *gn;
+ ASN1_IA5STRING *uri;
+ PyObject *ouri;
+
+ gn = sk_GENERAL_NAME_value(gns, j);
+ if (gn->type != GEN_URI) {
+ continue;
+ }
+ uri = gn->d.uniformResourceIdentifier;
+ ouri = PyUnicode_FromStringAndSize((char *)uri->data,
+ uri->length);
+ if (ouri == NULL) {
+ Py_DECREF(lst);
+ return NULL;
+ }
+ result = PyList_Append(lst, ouri);
+ Py_DECREF(ouri);
+ if (result < 0) {
+ Py_DECREF(lst);
+ return NULL;
+ }
+ }
+ }
+ /* convert to tuple or None */
+ if (PyList_Size(lst) == 0) {
+ Py_DECREF(lst);
+ return Py_None;
+ } else {
+ PyObject *tup;
+ tup = PyList_AsTuple(lst);
+ Py_DECREF(lst);
+ return tup;
+ }
+}
+
+static PyObject *
_decode_certificate(X509 *certificate) {
PyObject *retval = NULL;
@@ -974,9 +1088,10 @@ _decode_certificate(X509 *certificate) {
PyObject *issuer;
PyObject *version;
PyObject *sn_obj;
+ PyObject *obj;
ASN1_INTEGER *serialNumber;
char buf[2048];
- int len;
+ int len, result;
ASN1_TIME *notBefore, *notAfter;
PyObject *pnotBefore, *pnotAfter;
@@ -1082,6 +1197,41 @@ _decode_certificate(X509 *certificate) {
Py_DECREF(peer_alt_names);
}
+ /* Authority Information Access: OCSP URIs */
+ obj = _get_aia_uri(certificate, NID_ad_OCSP);
+ if (obj == NULL) {
+ goto fail1;
+ } else if (obj != Py_None) {
+ result = PyDict_SetItemString(retval, "OCSP", obj);
+ Py_DECREF(obj);
+ if (result < 0) {
+ goto fail1;
+ }
+ }
+
+ obj = _get_aia_uri(certificate, NID_ad_ca_issuers);
+ if (obj == NULL) {
+ goto fail1;
+ } else if (obj != Py_None) {
+ result = PyDict_SetItemString(retval, "caIssuers", obj);
+ Py_DECREF(obj);
+ if (result < 0) {
+ goto fail1;
+ }
+ }
+
+ /* CDP (CRL distribution points) */
+ obj = _get_crl_dp(certificate);
+ if (obj == NULL) {
+ goto fail1;
+ } else if (obj != Py_None) {
+ result = PyDict_SetItemString(retval, "crlDistributionPoints", obj);
+ Py_DECREF(obj);
+ if (result < 0) {
+ goto fail1;
+ }
+ }
+
BIO_free(biobuf);
return retval;