Issue #18379: SSLSocket.getpeercert() returns CA issuer AIA fields, OCSP

and CRL distribution points.
This commit is contained in:
Christian Heimes 2013-11-21 03:40:15 +01:00
parent efff7060f8
commit bd3a7f90b5
4 changed files with 165 additions and 2 deletions

View file

@ -733,6 +733,10 @@ SSL sockets also have the following additional methods and attributes:
.. versionchanged:: 3.4 .. versionchanged:: 3.4
:exc:`ValueError` is raised when the handshake isn't done. :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() .. method:: SSLSocket.cipher()
Returns a three-value tuple containing the name of the cipher being used, the Returns a three-value tuple containing the name of the cipher being used, the

View file

@ -212,6 +212,12 @@ class BasicSocketTests(unittest.TestCase):
(('DNS', 'projects.developer.nokia.com'), (('DNS', 'projects.developer.nokia.com'),
('DNS', 'projects.forum.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): def test_parse_cert_CVE_2013_4238(self):
p = ssl._ssl._test_decode_cert(NULLBYTECERT) p = ssl._ssl._test_decode_cert(NULLBYTECERT)
@ -905,6 +911,7 @@ class ContextTests(unittest.TestCase):
'notAfter': asn1time('Mar 29 12:29:49 2033 GMT'), 'notAfter': asn1time('Mar 29 12:29:49 2033 GMT'),
'notBefore': asn1time('Mar 30 12:29:49 2003 GMT'), 'notBefore': asn1time('Mar 30 12:29:49 2003 GMT'),
'serialNumber': '00', 'serialNumber': '00',
'crlDistributionPoints': ('https://www.cacert.org/revoke.crl',),
'subject': ((('organizationName', 'Root CA'),), 'subject': ((('organizationName', 'Root CA'),),
(('organizationalUnitName', 'http://www.cacert.org'),), (('organizationalUnitName', 'http://www.cacert.org'),),
(('commonName', 'CA Cert Signing Authority'),), (('commonName', 'CA Cert Signing Authority'),),
@ -1269,7 +1276,6 @@ class NetworkedTests(unittest.TestCase):
s.close() s.close()
self.assertEqual(len(ctx.get_ca_certs()), 1) self.assertEqual(len(ctx.get_ca_certs()), 1)
try: try:
import threading import threading
except ImportError: except ImportError:

View file

@ -59,6 +59,9 @@ Core and Builtins
Library 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() - Issue #18138: Implement cadata argument of SSLContext.load_verify_location()
to load CA certificates and CRL from memory. It supports PEM and DER to load CA certificates and CRL from memory. It supports PEM and DER
encoded strings. encoded strings.

View file

@ -964,6 +964,120 @@ _get_peer_alt_names (X509 *certificate) {
return NULL; return NULL;
} }
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 * static PyObject *
_decode_certificate(X509 *certificate) { _decode_certificate(X509 *certificate) {
@ -974,9 +1088,10 @@ _decode_certificate(X509 *certificate) {
PyObject *issuer; PyObject *issuer;
PyObject *version; PyObject *version;
PyObject *sn_obj; PyObject *sn_obj;
PyObject *obj;
ASN1_INTEGER *serialNumber; ASN1_INTEGER *serialNumber;
char buf[2048]; char buf[2048];
int len; int len, result;
ASN1_TIME *notBefore, *notAfter; ASN1_TIME *notBefore, *notAfter;
PyObject *pnotBefore, *pnotAfter; PyObject *pnotBefore, *pnotAfter;
@ -1082,6 +1197,41 @@ _decode_certificate(X509 *certificate) {
Py_DECREF(peer_alt_names); 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); BIO_free(biobuf);
return retval; return retval;