cpython: 9adcb61ea741 (original) (raw)
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -372,21 +372,45 @@ Certificate handling
.. versionadded:: 3.4
-.. function:: enum_cert_store(store_name, cert_type='certificate')
+.. function:: enum_certificates(store_name)
Retrieve certificates from Windows' system cert store. store_name may be
one of CA
, ROOT
or MY
. Windows may provide additional cert
- stores, too. cert_type is either
certificate
for X.509 certificates - or
crl
for X.509 certificate revocation lists.
- The function returns a list of (bytes, encoding_type) tuples. The
- encoding_type flag can be interpreted with :const:
X509_ASN_ENCODING
or - :const:
PKCS_7_ASN_ENCODING
.
- The function returns a list of (cert_bytes, encoding_type, trust) tuples.
- The encoding_type specifies the encoding of cert_bytes. It is either
- :const:
x509_asn
for X.509 ASN.1 data or :const:pkcs_7_asn
for - PKCS#7 ASN.1 data. Trust specifies the purpose of the certificate as a set
- of OIDS or exactly
True
if the certificate is trustworthy for all - purposes. +
- Example:: +
>>> ssl.enum_certificates("CA")[](#l1.28)
[(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),[](#l1.29)
(b'data...', 'x509_asn', True)][](#l1.30)
Availability: Windows. .. versionadded:: 3.4 +.. function:: enum_crls(store_name) +
- Retrieve CRLs from Windows' system cert store. store_name may be
- one of
CA
,ROOT
orMY
. Windows may provide additional cert - stores, too. +
- The function returns a list of (cert_bytes, encoding_type, trust) tuples.
- The encoding_type specifies the encoding of cert_bytes. It is either
- :const:
x509_asn
for X.509 ASN.1 data or :const:pkcs_7_asn
for - PKCS#7 ASN.1 data. +
- Availability: Windows. +
- .. versionadded:: 3.4 +
+ Constants ^^^^^^^^^ @@ -657,15 +681,6 @@ Constants .. versionadded:: 3.4 -.. data:: X509_ASN_ENCODING
PKCS_7_ASN_ENCODING[](#l1.60)
--- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -144,7 +144,7 @@ else: _PROTOCOL_NAMES[PROTOCOL_TLSv1_2] = "TLSv1.2" if sys.platform == "win32":
from socket import getnameinfo as _getnameinfo from socket import socket, AF_INET, SOCK_STREAM, create_connection
--- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -528,29 +528,44 @@ class BasicSocketTests(unittest.TestCase self.assertEqual(paths.cafile, CERTFILE) self.assertEqual(paths.capath, CAPATH)
- @unittest.skipUnless(sys.platform == "win32", "Windows specific")
- def test_enum_certificates(self):
self.assertTrue(ssl.enum_certificates("CA"))[](#l3.9)
self.assertTrue(ssl.enum_certificates("ROOT"))[](#l3.10)
self.assertRaises(TypeError, ssl.enum_certificates)[](#l3.12)
self.assertRaises(WindowsError, ssl.enum_certificates, "")[](#l3.13)
names = set()[](#l3.15)
ca = ssl.enum_certificates("CA")[](#l3.16)
self.assertIsInstance(ca, list)[](#l3.17)
for element in ca:[](#l3.18)
self.assertIsInstance(element, tuple)[](#l3.19)
self.assertEqual(len(element), 3)[](#l3.20)
cert, enc, trust = element[](#l3.21)
self.assertIsInstance(cert, bytes)[](#l3.22)
self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})[](#l3.23)
self.assertIsInstance(trust, (set, bool))[](#l3.24)
if isinstance(trust, set):[](#l3.25)
names.update(trust)[](#l3.26)
serverAuth = "1.3.6.1.5.5.7.3.1"[](#l3.28)
self.assertIn(serverAuth, names)[](#l3.29)
@unittest.skipUnless(sys.platform == "win32", "Windows specific")
- def test_enum_cert_store(self):
self.assertEqual(ssl.X509_ASN_ENCODING, 1)[](#l3.33)
self.assertEqual(ssl.PKCS_7_ASN_ENCODING, 0x00010000)[](#l3.34)
self.assertEqual(ssl.enum_cert_store("CA"),[](#l3.36)
ssl.enum_cert_store("CA", "certificate"))[](#l3.37)
ssl.enum_cert_store("CA", "crl")[](#l3.38)
self.assertEqual(ssl.enum_cert_store("ROOT"),[](#l3.39)
ssl.enum_cert_store("ROOT", "certificate"))[](#l3.40)
ssl.enum_cert_store("ROOT", "crl")[](#l3.41)
- def test_enum_crls(self):
self.assertTrue(ssl.enum_crls("CA"))[](#l3.43)
self.assertRaises(TypeError, ssl.enum_crls)[](#l3.44)
self.assertRaises(WindowsError, ssl.enum_crls, "")[](#l3.45)
self.assertRaises(TypeError, ssl.enum_cert_store)[](#l3.47)
self.assertRaises(WindowsError, ssl.enum_cert_store, "")[](#l3.48)
self.assertRaises(ValueError, ssl.enum_cert_store, "CA", "wrong")[](#l3.49)
crls = ssl.enum_crls("CA")[](#l3.50)
self.assertIsInstance(crls, list)[](#l3.51)
for element in crls:[](#l3.52)
self.assertIsInstance(element, tuple)[](#l3.53)
self.assertEqual(len(element), 2)[](#l3.54)
self.assertIsInstance(element[0], bytes)[](#l3.55)
self.assertIn(element[1], {"x509_asn", "pkcs_7_asn"})[](#l3.56)
ca = ssl.enum_cert_store("CA")[](#l3.58)
self.assertIsInstance(ca, list)[](#l3.59)
self.assertIsInstance(ca[0], tuple)[](#l3.60)
self.assertEqual(len(ca[0]), 2)[](#l3.61)
self.assertIsInstance(ca[0][0], bytes)[](#l3.62)
self.assertIsInstance(ca[0][1], int)[](#l3.63)
def test_asn1object(self): expected = (129, 'serverAuth', 'TLS Web Server Authentication',
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -59,6 +59,10 @@ Core and Builtins Library ------- +- Issue #17134: Finalize interface to Windows' certificate store. Cert and
- CRL enumeration are now two functions. enum_certificates() also returns
- purpose flags as set of OIDs. +
- Issue #19555: Restore sysconfig.get_config_var('SO'), with a DeprecationWarning pointing people at $EXT_SUFFIX.
--- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3422,130 +3422,258 @@ PySSL_nid2obj(PyObject *self, PyObject return result; } - #ifdef _MSC_VER -PyDoc_STRVAR(PySSL_enum_cert_store_doc, -"enum_cert_store(store_name, cert_type='certificate') -> []\n[](#l5.10) + +static PyObject +certEncodingType(DWORD encodingType) +{
- if (x509_asn == NULL) {
x509_asn = PyUnicode_InternFromString("x509_asn");[](#l5.19)
if (x509_asn == NULL)[](#l5.20)
return NULL;[](#l5.21)
- }
- if (pkcs_7_asn == NULL) {
pkcs_7_asn = PyUnicode_InternFromString("pkcs_7_asn");[](#l5.24)
if (pkcs_7_asn == NULL)[](#l5.25)
return NULL;[](#l5.26)
- }
- switch(encodingType) {
- case X509_ASN_ENCODING:
Py_INCREF(x509_asn);[](#l5.30)
return x509_asn;[](#l5.31)
- case PKCS_7_ASN_ENCODING:
Py_INCREF(pkcs_7_asn);[](#l5.33)
return pkcs_7_asn;[](#l5.34)
- default:
return PyLong_FromLong(encodingType);[](#l5.36)
- }
+} + +static PyObject* +parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags) +{
- if (!CertGetEnhancedKeyUsage(pCertCtx, flags, NULL, &size)) {
error = GetLastError();[](#l5.48)
if (error == CRYPT_E_NOT_FOUND) {[](#l5.49)
Py_RETURN_TRUE;[](#l5.50)
}[](#l5.51)
return PyErr_SetFromWindowsErr(error);[](#l5.52)
- }
- usage = (CERT_ENHKEY_USAGE*)PyMem_Malloc(size);
- if (usage == NULL) {
return PyErr_NoMemory();[](#l5.57)
- }
- /* Now get the actual enhanced usage property */
- if (!CertGetEnhancedKeyUsage(pCertCtx, flags, usage, &size)) {
PyMem_Free(usage);[](#l5.62)
error = GetLastError();[](#l5.63)
if (error == CRYPT_E_NOT_FOUND) {[](#l5.64)
Py_RETURN_TRUE;[](#l5.65)
}[](#l5.66)
return PyErr_SetFromWindowsErr(error);[](#l5.67)
- }
- retval = PySet_New(NULL);
- if (retval == NULL) {
goto error;[](#l5.71)
- }
- for (i = 0; i < usage->cUsageIdentifier; ++i) {
if (usage->rgpszUsageIdentifier[i]) {[](#l5.74)
PyObject *oid;[](#l5.75)
int err;[](#l5.76)
oid = PyUnicode_FromString(usage->rgpszUsageIdentifier[i]);[](#l5.77)
if (oid == NULL) {[](#l5.78)
Py_CLEAR(retval);[](#l5.79)
goto error;[](#l5.80)
}[](#l5.81)
err = PySet_Add(retval, oid);[](#l5.82)
Py_DECREF(oid);[](#l5.83)
if (err == -1) {[](#l5.84)
Py_CLEAR(retval);[](#l5.85)
goto error;[](#l5.86)
}[](#l5.87)
}[](#l5.88)
- }
- error:
- PyMem_Free(usage);
- return retval;
+} + +PyDoc_STRVAR(PySSL_enum_certificates_doc, +"enum_certificates(store_name) -> []\n[](#l5.96) \n[](#l5.97) Retrieve certificates from Windows' cert store. store_name may be one of\n[](#l5.98) 'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n[](#l5.99) -cert_type must be either 'certificate' or 'crl'.\n[](#l5.100) +The function returns a list of (bytes, encoding_type, trust) tuples. The\n[](#l5.101) +encoding_type flag can be interpreted with X509_ASN_ENCODING or\n[](#l5.102) +PKCS_7_ASN_ENCODING. The trust setting is either a set of OIDs or the\n[](#l5.103) +boolean True."); + +static PyObject * +PySSL_enum_certificates(PyObject *self, PyObject *args, PyObject *kwds) +{
- char *kwlist[] = {"store_name", NULL};
- char *store_name;
- HCERTSTORE hStore = NULL;
- PCCERT_CONTEXT pCertCtx = NULL;
- PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL;
- PyObject *result = NULL;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_certificates",
kwlist, &store_name)) {[](#l5.117)
return NULL;[](#l5.118)
- }
- result = PyList_New(0);
- if (result == NULL) {
return NULL;[](#l5.122)
- }
- hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name);
- if (hStore == NULL) {
Py_DECREF(result);[](#l5.126)
return PyErr_SetFromWindowsErr(GetLastError());[](#l5.127)
- }
- while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) {
cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded,[](#l5.131)
pCertCtx->cbCertEncoded);[](#l5.132)
if (!cert) {[](#l5.133)
Py_CLEAR(result);[](#l5.134)
break;[](#l5.135)
}[](#l5.136)
if ((enc = certEncodingType(pCertCtx->dwCertEncodingType)) == NULL) {[](#l5.137)
Py_CLEAR(result);[](#l5.138)
break;[](#l5.139)
}[](#l5.140)
keyusage = parseKeyUsage(pCertCtx, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG);[](#l5.141)
if (keyusage == Py_True) {[](#l5.142)
Py_DECREF(keyusage);[](#l5.143)
keyusage = parseKeyUsage(pCertCtx, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG);[](#l5.144)
}[](#l5.145)
if (keyusage == NULL) {[](#l5.146)
Py_CLEAR(result);[](#l5.147)
break;[](#l5.148)
}[](#l5.149)
if ((tup = PyTuple_New(3)) == NULL) {[](#l5.150)
Py_CLEAR(result);[](#l5.151)
break;[](#l5.152)
}[](#l5.153)
PyTuple_SET_ITEM(tup, 0, cert);[](#l5.154)
cert = NULL;[](#l5.155)
PyTuple_SET_ITEM(tup, 1, enc);[](#l5.156)
enc = NULL;[](#l5.157)
PyTuple_SET_ITEM(tup, 2, keyusage);[](#l5.158)
keyusage = NULL;[](#l5.159)
if (PyList_Append(result, tup) < 0) {[](#l5.160)
Py_CLEAR(result);[](#l5.161)
break;[](#l5.162)
}[](#l5.163)
Py_CLEAR(tup);[](#l5.164)
- }
- if (pCertCtx) {
/* loop ended with an error, need to clean up context manually */[](#l5.167)
CertFreeCertificateContext(pCertCtx);[](#l5.168)
- }
- /* In error cases cert, enc and tup may not be NULL */
- Py_XDECREF(cert);
- Py_XDECREF(enc);
- Py_XDECREF(keyusage);
- Py_XDECREF(tup);
- if (!CertCloseStore(hStore, 0)) {
/* This error case might shadow another exception.*/[](#l5.178)
Py_XDECREF(result);[](#l5.179)
return PyErr_SetFromWindowsErr(GetLastError());[](#l5.180)
- }
- return result;
+} + +PyDoc_STRVAR(PySSL_enum_crls_doc, +"enum_crls(store_name) -> []\n[](#l5.186) +\n[](#l5.187) +Retrieve CRLs from Windows' cert store. store_name may be one of\n[](#l5.188) +'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n[](#l5.189) The function returns a list of (bytes, encoding_type) tuples. The\n[](#l5.190) encoding_type flag can be interpreted with X509_ASN_ENCODING or\n[](#l5.191) PKCS_7_ASN_ENCODING."); static PyObject * -PySSL_enum_cert_store(PyObject *self, PyObject *args, PyObject *kwds) +PySSL_enum_crls(PyObject *self, PyObject *args, PyObject *kwds) {
- PCCRL_CONTEXT pCrlCtx = NULL;
- PyObject *crl = NULL, *enc = NULL, *tup = NULL; PyObject *result = NULL;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_cert_store",
kwlist, &store_name, &cert_type)) {[](#l5.210)
return NULL;[](#l5.211)
- }
- if ((strcmp(cert_type, "certificate") != 0) &&
(strcmp(cert_type, "crl") != 0)) {[](#l5.215)
return PyErr_Format(PyExc_ValueError,[](#l5.216)
"cert_type must be 'certificate' or 'crl', "[](#l5.217)
"not %.100s", cert_type);[](#l5.218)
- }
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_crls",
} -kwlist, &store_name)) {[](#l5.224) return NULL;[](#l5.225)
- result = PyList_New(0);
- if (result == NULL) {
return NULL;[](#l5.231)
- }
- hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name);
- if (hStore == NULL) { Py_DECREF(result); return PyErr_SetFromWindowsErr(GetLastError()); }
- if (strcmp(cert_type, "certificate") == 0) {
PCCERT_CONTEXT pCertCtx = NULL;[](#l5.240)
while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) {[](#l5.241)
cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded,[](#l5.242)
pCertCtx->cbCertEncoded);[](#l5.243)
if (!cert) {[](#l5.244)
ok = 0;[](#l5.245)
break;[](#l5.246)
}[](#l5.247)
if ((enc = PyLong_FromLong(pCertCtx->dwCertEncodingType)) == NULL) {[](#l5.248)
ok = 0;[](#l5.249)
break;[](#l5.250)
}[](#l5.251)
if ((tup = PyTuple_New(2)) == NULL) {[](#l5.252)
ok = 0;[](#l5.253)
break;[](#l5.254)
}[](#l5.255)
PyTuple_SET_ITEM(tup, 0, cert); cert = NULL;[](#l5.256)
PyTuple_SET_ITEM(tup, 1, enc); enc = NULL;[](#l5.257)
if (PyList_Append(result, tup) < 0) {[](#l5.259)
ok = 0;[](#l5.260)
break;[](#l5.261)
}[](#l5.262)
Py_CLEAR(tup);[](#l5.263)
- while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) {
crl = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded,[](#l5.265)
pCrlCtx->cbCrlEncoded);[](#l5.266)
if (!crl) {[](#l5.267)
Py_CLEAR(result);[](#l5.268)
break;[](#l5.269) }[](#l5.270)
if (pCertCtx) {[](#l5.271)
/* loop ended with an error, need to clean up context manually */[](#l5.272)
CertFreeCertificateContext(pCertCtx);[](#l5.273)
if ((enc = certEncodingType(pCrlCtx->dwCertEncodingType)) == NULL) {[](#l5.274)
Py_CLEAR(result);[](#l5.275)
break;[](#l5.276)
}[](#l5.277)
if ((tup = PyTuple_New(2)) == NULL) {[](#l5.278)
Py_CLEAR(result);[](#l5.279)
break;[](#l5.280) }[](#l5.281)
- } else {
PCCRL_CONTEXT pCrlCtx = NULL;[](#l5.283)
while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) {[](#l5.284)
cert = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded,[](#l5.285)
pCrlCtx->cbCrlEncoded);[](#l5.286)
if (!cert) {[](#l5.287)
ok = 0;[](#l5.288)
break;[](#l5.289)
}[](#l5.290)
if ((enc = PyLong_FromLong(pCrlCtx->dwCertEncodingType)) == NULL) {[](#l5.291)
ok = 0;[](#l5.292)
break;[](#l5.293)
}[](#l5.294)
if ((tup = PyTuple_New(2)) == NULL) {[](#l5.295)
ok = 0;[](#l5.296)
break;[](#l5.297)
}[](#l5.298)
PyTuple_SET_ITEM(tup, 0, cert); cert = NULL;[](#l5.299)
PyTuple_SET_ITEM(tup, 1, enc); enc = NULL;[](#l5.300)
if (PyList_Append(result, tup) < 0) {[](#l5.302)
ok = 0;[](#l5.303)
break;[](#l5.304)
}[](#l5.305)
Py_CLEAR(tup);[](#l5.306)
PyTuple_SET_ITEM(tup, 0, crl);[](#l5.307)
crl = NULL;[](#l5.308)
PyTuple_SET_ITEM(tup, 1, enc);[](#l5.309)
enc = NULL;[](#l5.310)
if (PyList_Append(result, tup) < 0) {[](#l5.312)
Py_CLEAR(result);[](#l5.313)
break;[](#l5.314) }[](#l5.315)
if (pCrlCtx) {[](#l5.316)
/* loop ended with an error, need to clean up context manually */[](#l5.317)
CertFreeCRLContext(pCrlCtx);[](#l5.318)
}[](#l5.319)
Py_CLEAR(tup);[](#l5.320)
- }
- if (pCrlCtx) {
/* loop ended with an error, need to clean up context manually */[](#l5.323)
} /* In error cases cert, enc and tup may not be NULL */CertFreeCRLContext(pCrlCtx);[](#l5.324)
- Py_XDECREF(crl); Py_XDECREF(enc); Py_XDECREF(tup); if (!CertCloseStore(hStore, 0)) { /* This error case might shadow another exception.*/
Py_DECREF(result);[](#l5.335)
- if (ok) {
return result;[](#l5.340)
- } else {
Py_DECREF(result);[](#l5.342)
return NULL;[](#l5.343)
- }
} -#endif + +#endif /* _MSC_VER / / List of functions exported by this module. */ @@ -3567,8 +3695,10 @@ static PyMethodDef PySSL_methods[] = { {"get_default_verify_paths", (PyCFunction)PySSL_get_default_verify_paths, METH_NOARGS, PySSL_get_default_verify_paths_doc}, #ifdef _MSC_VER
- {"enum_cert_store", (PyCFunction)PySSL_enum_cert_store,
METH_VARARGS | METH_KEYWORDS, PySSL_enum_cert_store_doc},[](#l5.358)
- {"enum_certificates", (PyCFunction)PySSL_enum_certificates,
METH_VARARGS | METH_KEYWORDS, PySSL_enum_certificates_doc},[](#l5.360)
- {"enum_crls", (PyCFunction)PySSL_enum_crls,
METH_VARARGS | METH_KEYWORDS, PySSL_enum_crls_doc},[](#l5.362)
#endif {"txt2obj", (PyCFunction)PySSL_txt2obj, METH_VARARGS | METH_KEYWORDS, PySSL_txt2obj_doc}, @@ -3811,12 +3941,6 @@ PyInit__ssl(void) PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", X509_V_FLAG_X509_STRICT); -#ifdef _MSC_VER
- /* Windows dwCertEncodingType */
- PyModule_AddIntMacro(m, X509_ASN_ENCODING);
- PyModule_AddIntMacro(m, PKCS_7_ASN_ENCODING);
-#endif - /* Alert Descriptions from ssl.h / / note RESERVED constants no longer intended for use have been removed / / http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 */