summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2013-11-22 00:51:30 (GMT)
committerChristian Heimes <christian@cheimes.de>2013-11-22 00:51:30 (GMT)
commit44109d7de7f24b681c973880aa3e1d39042078fd (patch)
treefb012b20f74b2bd6898154bf2055e355b4b85a2d
parentd06289254280f55536a765b1b5c36e6160b6c8e6 (diff)
downloadcpython-44109d7de7f24b681c973880aa3e1d39042078fd.zip
cpython-44109d7de7f24b681c973880aa3e1d39042078fd.tar.gz
cpython-44109d7de7f24b681c973880aa3e1d39042078fd.tar.bz2
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.
-rw-r--r--Doc/library/ssl.rst45
-rw-r--r--Lib/ssl.py2
-rw-r--r--Lib/test/test_ssl.py57
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/_ssl.c314
5 files changed, 290 insertions, 132 deletions
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 99386c0..7685ade 100644
--- 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.
+ stores, too.
- 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")
+ [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),
+ (b'data...', 'x509_asn', True)]
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`` or ``MY``. 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
-
- Encoding flags for :func:`enum_cert_store`.
-
- Availability: Windows.
-
- .. versionadded:: 3.4
-
SSL Sockets
-----------
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 7ce097f..d4c7bad 100644
--- 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 _ssl import enum_cert_store, X509_ASN_ENCODING, PKCS_7_ASN_ENCODING
+ from _ssl import enum_certificates, enum_crls
from socket import getnameinfo as _getnameinfo
from socket import socket, AF_INET, SOCK_STREAM, create_connection
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 9996ff1..2190d9f 100644
--- 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_cert_store(self):
- self.assertEqual(ssl.X509_ASN_ENCODING, 1)
- self.assertEqual(ssl.PKCS_7_ASN_ENCODING, 0x00010000)
-
- self.assertEqual(ssl.enum_cert_store("CA"),
- ssl.enum_cert_store("CA", "certificate"))
- ssl.enum_cert_store("CA", "crl")
- self.assertEqual(ssl.enum_cert_store("ROOT"),
- ssl.enum_cert_store("ROOT", "certificate"))
- ssl.enum_cert_store("ROOT", "crl")
-
- self.assertRaises(TypeError, ssl.enum_cert_store)
- self.assertRaises(WindowsError, ssl.enum_cert_store, "")
- self.assertRaises(ValueError, ssl.enum_cert_store, "CA", "wrong")
-
- ca = ssl.enum_cert_store("CA")
+ def test_enum_certificates(self):
+ self.assertTrue(ssl.enum_certificates("CA"))
+ self.assertTrue(ssl.enum_certificates("ROOT"))
+
+ self.assertRaises(TypeError, ssl.enum_certificates)
+ self.assertRaises(WindowsError, ssl.enum_certificates, "")
+
+ names = set()
+ ca = ssl.enum_certificates("CA")
self.assertIsInstance(ca, list)
- self.assertIsInstance(ca[0], tuple)
- self.assertEqual(len(ca[0]), 2)
- self.assertIsInstance(ca[0][0], bytes)
- self.assertIsInstance(ca[0][1], int)
+ for element in ca:
+ self.assertIsInstance(element, tuple)
+ self.assertEqual(len(element), 3)
+ cert, enc, trust = element
+ self.assertIsInstance(cert, bytes)
+ self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})
+ self.assertIsInstance(trust, (set, bool))
+ if isinstance(trust, set):
+ names.update(trust)
+
+ serverAuth = "1.3.6.1.5.5.7.3.1"
+ self.assertIn(serverAuth, names)
+
+ @unittest.skipUnless(sys.platform == "win32", "Windows specific")
+ def test_enum_crls(self):
+ self.assertTrue(ssl.enum_crls("CA"))
+ self.assertRaises(TypeError, ssl.enum_crls)
+ self.assertRaises(WindowsError, ssl.enum_crls, "")
+
+ crls = ssl.enum_crls("CA")
+ self.assertIsInstance(crls, list)
+ for element in crls:
+ self.assertIsInstance(element, tuple)
+ self.assertEqual(len(element), 2)
+ self.assertIsInstance(element[0], bytes)
+ self.assertIn(element[1], {"x509_asn", "pkcs_7_asn"})
+
def test_asn1object(self):
expected = (129, 'serverAuth', 'TLS Web Server Authentication',
diff --git a/Misc/NEWS b/Misc/NEWS
index 295b456..b5916f4 100644
--- 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.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 69b1eef..4768e44 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -3422,130 +3422,258 @@ PySSL_nid2obj(PyObject *self, PyObject *args)
return result;
}
-
#ifdef _MSC_VER
-PyDoc_STRVAR(PySSL_enum_cert_store_doc,
-"enum_cert_store(store_name, cert_type='certificate') -> []\n\
+
+static PyObject*
+certEncodingType(DWORD encodingType)
+{
+ static PyObject *x509_asn = NULL;
+ static PyObject *pkcs_7_asn = NULL;
+
+ if (x509_asn == NULL) {
+ x509_asn = PyUnicode_InternFromString("x509_asn");
+ if (x509_asn == NULL)
+ return NULL;
+ }
+ if (pkcs_7_asn == NULL) {
+ pkcs_7_asn = PyUnicode_InternFromString("pkcs_7_asn");
+ if (pkcs_7_asn == NULL)
+ return NULL;
+ }
+ switch(encodingType) {
+ case X509_ASN_ENCODING:
+ Py_INCREF(x509_asn);
+ return x509_asn;
+ case PKCS_7_ASN_ENCODING:
+ Py_INCREF(pkcs_7_asn);
+ return pkcs_7_asn;
+ default:
+ return PyLong_FromLong(encodingType);
+ }
+}
+
+static PyObject*
+parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags)
+{
+ CERT_ENHKEY_USAGE *usage;
+ DWORD size, error, i;
+ PyObject *retval;
+
+ if (!CertGetEnhancedKeyUsage(pCertCtx, flags, NULL, &size)) {
+ error = GetLastError();
+ if (error == CRYPT_E_NOT_FOUND) {
+ Py_RETURN_TRUE;
+ }
+ return PyErr_SetFromWindowsErr(error);
+ }
+
+ usage = (CERT_ENHKEY_USAGE*)PyMem_Malloc(size);
+ if (usage == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ /* Now get the actual enhanced usage property */
+ if (!CertGetEnhancedKeyUsage(pCertCtx, flags, usage, &size)) {
+ PyMem_Free(usage);
+ error = GetLastError();
+ if (error == CRYPT_E_NOT_FOUND) {
+ Py_RETURN_TRUE;
+ }
+ return PyErr_SetFromWindowsErr(error);
+ }
+ retval = PySet_New(NULL);
+ if (retval == NULL) {
+ goto error;
+ }
+ for (i = 0; i < usage->cUsageIdentifier; ++i) {
+ if (usage->rgpszUsageIdentifier[i]) {
+ PyObject *oid;
+ int err;
+ oid = PyUnicode_FromString(usage->rgpszUsageIdentifier[i]);
+ if (oid == NULL) {
+ Py_CLEAR(retval);
+ goto error;
+ }
+ err = PySet_Add(retval, oid);
+ Py_DECREF(oid);
+ if (err == -1) {
+ Py_CLEAR(retval);
+ goto error;
+ }
+ }
+ }
+ error:
+ PyMem_Free(usage);
+ return retval;
+}
+
+PyDoc_STRVAR(PySSL_enum_certificates_doc,
+"enum_certificates(store_name) -> []\n\
\n\
Retrieve certificates from Windows' cert store. store_name may be one of\n\
'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\
-cert_type must be either 'certificate' or 'crl'.\n\
-The function returns a list of (bytes, encoding_type) tuples. The\n\
+The function returns a list of (bytes, encoding_type, trust) tuples. The\n\
encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\
-PKCS_7_ASN_ENCODING.");
+PKCS_7_ASN_ENCODING. The trust setting is either a set of OIDs or the\n\
+boolean True.");
static PyObject *
-PySSL_enum_cert_store(PyObject *self, PyObject *args, PyObject *kwds)
+PySSL_enum_certificates(PyObject *self, PyObject *args, PyObject *kwds)
{
- char *kwlist[] = {"store_name", "cert_type", NULL};
+ char *kwlist[] = {"store_name", NULL};
char *store_name;
- char *cert_type = "certificate";
HCERTSTORE hStore = NULL;
+ PCCERT_CONTEXT pCertCtx = NULL;
+ PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL;
PyObject *result = NULL;
- PyObject *tup = NULL, *cert = NULL, *enc = NULL;
- int ok = 1;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_cert_store",
- kwlist, &store_name, &cert_type)) {
- return NULL;
- }
- if ((strcmp(cert_type, "certificate") != 0) &&
- (strcmp(cert_type, "crl") != 0)) {
- return PyErr_Format(PyExc_ValueError,
- "cert_type must be 'certificate' or 'crl', "
- "not %.100s", cert_type);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_certificates",
+ kwlist, &store_name)) {
+ return NULL;
}
-
- if ((result = PyList_New(0)) == NULL) {
+ result = PyList_New(0);
+ if (result == NULL) {
return NULL;
}
-
- if ((hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name)) == NULL) {
+ 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;
- while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) {
- cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded,
- pCertCtx->cbCertEncoded);
- if (!cert) {
- ok = 0;
- break;
- }
- if ((enc = PyLong_FromLong(pCertCtx->dwCertEncodingType)) == NULL) {
- ok = 0;
- break;
- }
- if ((tup = PyTuple_New(2)) == NULL) {
- ok = 0;
- break;
- }
- PyTuple_SET_ITEM(tup, 0, cert); cert = NULL;
- PyTuple_SET_ITEM(tup, 1, enc); enc = NULL;
-
- if (PyList_Append(result, tup) < 0) {
- ok = 0;
- break;
- }
- Py_CLEAR(tup);
+ while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) {
+ cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded,
+ pCertCtx->cbCertEncoded);
+ if (!cert) {
+ Py_CLEAR(result);
+ break;
}
- if (pCertCtx) {
- /* loop ended with an error, need to clean up context manually */
- CertFreeCertificateContext(pCertCtx);
+ if ((enc = certEncodingType(pCertCtx->dwCertEncodingType)) == NULL) {
+ Py_CLEAR(result);
+ break;
}
- } else {
- PCCRL_CONTEXT pCrlCtx = NULL;
- while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) {
- cert = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded,
- pCrlCtx->cbCrlEncoded);
- if (!cert) {
- ok = 0;
- break;
- }
- if ((enc = PyLong_FromLong(pCrlCtx->dwCertEncodingType)) == NULL) {
- ok = 0;
- break;
- }
- if ((tup = PyTuple_New(2)) == NULL) {
- ok = 0;
- break;
- }
- PyTuple_SET_ITEM(tup, 0, cert); cert = NULL;
- PyTuple_SET_ITEM(tup, 1, enc); enc = NULL;
-
- if (PyList_Append(result, tup) < 0) {
- ok = 0;
- break;
- }
- Py_CLEAR(tup);
+ keyusage = parseKeyUsage(pCertCtx, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG);
+ if (keyusage == Py_True) {
+ Py_DECREF(keyusage);
+ keyusage = parseKeyUsage(pCertCtx, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG);
+ }
+ if (keyusage == NULL) {
+ Py_CLEAR(result);
+ break;
+ }
+ if ((tup = PyTuple_New(3)) == NULL) {
+ Py_CLEAR(result);
+ break;
}
- if (pCrlCtx) {
- /* loop ended with an error, need to clean up context manually */
- CertFreeCRLContext(pCrlCtx);
+ PyTuple_SET_ITEM(tup, 0, cert);
+ cert = NULL;
+ PyTuple_SET_ITEM(tup, 1, enc);
+ enc = NULL;
+ PyTuple_SET_ITEM(tup, 2, keyusage);
+ keyusage = NULL;
+ if (PyList_Append(result, tup) < 0) {
+ Py_CLEAR(result);
+ break;
}
+ Py_CLEAR(tup);
+ }
+ if (pCertCtx) {
+ /* loop ended with an error, need to clean up context manually */
+ CertFreeCertificateContext(pCertCtx);
}
/* 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.*/
- Py_DECREF(result);
+ Py_XDECREF(result);
return PyErr_SetFromWindowsErr(GetLastError());
}
- if (ok) {
- return result;
- } else {
- Py_DECREF(result);
+ return result;
+}
+
+PyDoc_STRVAR(PySSL_enum_crls_doc,
+"enum_crls(store_name) -> []\n\
+\n\
+Retrieve CRLs from Windows' cert store. store_name may be one of\n\
+'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\
+The function returns a list of (bytes, encoding_type) tuples. The\n\
+encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\
+PKCS_7_ASN_ENCODING.");
+
+static PyObject *
+PySSL_enum_crls(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {"store_name", NULL};
+ char *store_name;
+ HCERTSTORE hStore = NULL;
+ PCCRL_CONTEXT pCrlCtx = NULL;
+ PyObject *crl = NULL, *enc = NULL, *tup = NULL;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_crls",
+ kwlist, &store_name)) {
+ return NULL;
+ }
+ result = PyList_New(0);
+ if (result == NULL) {
return NULL;
}
+ hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name);
+ if (hStore == NULL) {
+ Py_DECREF(result);
+ return PyErr_SetFromWindowsErr(GetLastError());
+ }
+
+ while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) {
+ crl = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded,
+ pCrlCtx->cbCrlEncoded);
+ if (!crl) {
+ Py_CLEAR(result);
+ break;
+ }
+ if ((enc = certEncodingType(pCrlCtx->dwCertEncodingType)) == NULL) {
+ Py_CLEAR(result);
+ break;
+ }
+ if ((tup = PyTuple_New(2)) == NULL) {
+ Py_CLEAR(result);
+ break;
+ }
+ PyTuple_SET_ITEM(tup, 0, crl);
+ crl = NULL;
+ PyTuple_SET_ITEM(tup, 1, enc);
+ enc = NULL;
+
+ if (PyList_Append(result, tup) < 0) {
+ Py_CLEAR(result);
+ break;
+ }
+ Py_CLEAR(tup);
+ }
+ if (pCrlCtx) {
+ /* loop ended with an error, need to clean up context manually */
+ CertFreeCRLContext(pCrlCtx);
+ }
+
+ /* In error cases cert, enc and tup may not be NULL */
+ Py_XDECREF(crl);
+ Py_XDECREF(enc);
+ Py_XDECREF(tup);
+
+ if (!CertCloseStore(hStore, 0)) {
+ /* This error case might shadow another exception.*/
+ Py_XDECREF(result);
+ return PyErr_SetFromWindowsErr(GetLastError());
+ }
+ return result;
}
-#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},
+ {"enum_certificates", (PyCFunction)PySSL_enum_certificates,
+ METH_VARARGS | METH_KEYWORDS, PySSL_enum_certificates_doc},
+ {"enum_crls", (PyCFunction)PySSL_enum_crls,
+ METH_VARARGS | METH_KEYWORDS, PySSL_enum_crls_doc},
#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 */