]> granicus.if.org Git - python/commitdiff
Issue #17134: Add ssl.enum_cert_store() as interface to Windows' cert store.
authorChristian Heimes <christian@cheimes.de>
Sun, 9 Jun 2013 17:03:31 +0000 (19:03 +0200)
committerChristian Heimes <christian@cheimes.de>
Sun, 9 Jun 2013 17:03:31 +0000 (19:03 +0200)
Doc/library/ssl.rst
Lib/ssl.py
Lib/test/test_ssl.py
Misc/NEWS
Modules/_ssl.c
PC/VS9.0/_socket.vcproj
PCbuild/_ssl.vcxproj

index 398169bb561c9f81cdfd37dfacc26599947628f7..3816aca8288ba33772b82b0457dc0fa778df7c65 100644 (file)
@@ -359,6 +359,20 @@ Certificate handling
 
    .. versionadded:: 3.4
 
+.. function:: enum_cert_store(store_name, cert_type='certificate')
+
+   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`.
+
+   Availability: Windows.
+
+   .. versionadded:: 3.4
 
 Constants
 ^^^^^^^^^
@@ -598,6 +612,15 @@ 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
 -----------
index 5d48f1be6266b2bd29bde79c53768f167d6d84a7..d78d96d160e7ee56033ddc8dcad7ccf408b23211 100644 (file)
@@ -89,6 +89,7 @@ ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY
 
 import textwrap
 import re
+import sys
 import os
 import collections
 
@@ -139,6 +140,9 @@ else:
     _PROTOCOL_NAMES[PROTOCOL_TLSv1_1] = "TLSv1.1"
     _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 socket import getnameinfo as _getnameinfo
 from socket import socket, AF_INET, SOCK_STREAM, create_connection
 import base64        # for DER-to-PEM translation
index 6cecc17f2fd8b3c6e890d250a6f942f4ccb4fe80..08a07bab80f40b2ebccd1ea18bfd623ebb2908c0 100644 (file)
@@ -407,6 +407,29 @@ class BasicSocketTests(unittest.TestCase):
             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")
+        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)
+
 class ContextTests(unittest.TestCase):
 
     @skip_if_broken_ubuntu_ssl
index fd54ea6440502dab3896078c2b181777f4f24ff5..bada8783950bceeed3dd4846d9797a8a894738d6 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -115,6 +115,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #17134: Add ssl.enum_cert_store() as interface to Windows' cert store.
+
 - Issue #18143: Implement ssl.get_default_verify_paths() in order to debug
   the default locations for cafile and capath.
 
index 999dd0953321d2f84247da7c29dc53d461300fe1..5959f18ab0216818f84da75d0a663c3148315b5b 100644 (file)
@@ -2801,7 +2801,129 @@ get_default_verify_paths(PyObject *self)
     return NULL;
 }
 
+#ifdef _MSC_VER
+PyDoc_STRVAR(PySSL_enum_cert_store_doc,
+"enum_cert_store(store_name, cert_type='certificate') -> []\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\
+encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\
+PKCS_7_ASN_ENCODING.");
+
+static PyObject *
+PySSL_enum_cert_store(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    char *kwlist[] = {"store_name", "cert_type", NULL};
+    char *store_name;
+    char *cert_type = "certificate";
+    HCERTSTORE hStore = 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 ((result = PyList_New(0)) == NULL) {
+        return NULL;
+    }
+
+    if ((hStore = CertOpenSystemStore(NULL, store_name)) == 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);
+        }
+        if (pCertCtx) {
+            /* loop ended with an error, need to clean up context manually */
+            CertFreeCertificateContext(pCertCtx);
+        }
+    } 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);
+        }
+        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(cert);
+    Py_XDECREF(enc);
+    Py_XDECREF(tup);
+
+    if (!CertCloseStore(hStore, 0)) {
+        /* This error case might shadow another exception.*/
+        Py_DECREF(result);
+        return PyErr_SetFromWindowsErr(GetLastError());
+    }
+    if (ok) {
+        return result;
+    } else {
+        Py_DECREF(result);
+        return NULL;
+    }
+}
+#endif
 
 /* List of functions exported by this module. */
 
@@ -2822,6 +2944,10 @@ static PyMethodDef PySSL_methods[] = {
 #endif
     {"get_default_verify_paths", (PyCFunction)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},
+#endif
     {NULL,                  NULL}            /* Sentinel */
 };
 
@@ -3034,6 +3160,12 @@ PyInit__ssl(void)
     PyModule_AddIntConstant(m, "CERT_REQUIRED",
                             PY_SSL_CERT_REQUIRED);
 
+#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 */
index ff1f6d4e3f57bc1a690b61bdab4bec47ec486ff5..8c7979c4ca5b18c932bc273f546b4d83392cfb7e 100644 (file)
@@ -54,7 +54,7 @@
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                        />\r
                        <Tool\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                        />\r
                        <Tool\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                        />\r
                        <Tool\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                        />\r
                        <Tool\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                        />\r
                        <Tool\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                                TargetMachine="17"\r
                        />\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                        />\r
                        <Tool\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="ws2_32.lib"\r
+                               AdditionalDependencies="ws2_32.lib;crypt32.lib"\r
                                BaseAddress="0x1e1D0000"\r
                                TargetMachine="17"\r
                        />\r
index ae71d19dc5eab2c3d722d2502a5b25d19ac4e8c5..7378794b3d9e4e8ac275d422eb9c5e3d2edf20a3 100644 (file)
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'">
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'">
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
   </ItemDefinitionGroup>
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out32\libeay32.lib;$(opensslDir)\out32\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'">
       </Command>
     </PreLinkEvent>
     <Link>
-      <AdditionalDependencies>ws2_32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>ws2_32.lib;crypt32.lib;$(opensslDir)\out64\libeay32.lib;$(opensslDir)\out64\ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
   </ItemDefinitionGroup>