crypt32: Add implementation of a root store that reads trusted CA certs from a local file or directory

Juan Lang juan.lang at gmail.com
Fri Aug 17 14:11:31 CDT 2007


This patch is independent from the cleanup series of 4 patches.  It's
more of an RFC.

This approach makes the Root store read-only for certificates, and
allows CRLs to be added to it (but not deleted.)  This part I like.

It also reads certs from local files or directories - also good.

What I like less is that it gets the path to read from from the
registry.  While I suggested this, and comments seem to indicate
configurability is desirable, I don't think this addresses your
concerns of having the trusted certs be writable by any user,
Alexandre.  Logically, having the path writable by any user is
equivalent to having the certs writable by any user.

So I think having the path(s) to check hardcoded would be better.  By
including the paths from all known distros, it should be possible to
build a single binary that will work on all of them.

Do you have a preference?

Thanks,
--Juan
-------------- next part --------------
From 6b4ec4d573990b5e4e20a6ca84810fa6ebfd3985 Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Fri, 17 Aug 2007 12:01:03 -0700
Subject: [PATCH] Add implementation of a root store that reads trusted CA certs from a local
file or directory
---
 dlls/crypt32/Makefile.in       |    1 
 dlls/crypt32/crypt32_private.h |    1 
 dlls/crypt32/rootstore.c       |  578 ++++++++++++++++++++++++++++++++++++++++
 dlls/crypt32/store.c           |    8 -
 4 files changed, 583 insertions(+), 5 deletions(-)

diff --git a/dlls/crypt32/Makefile.in b/dlls/crypt32/Makefile.in
index 84f21dc..b7319bf 100644
--- a/dlls/crypt32/Makefile.in
+++ b/dlls/crypt32/Makefile.in
@@ -24,6 +24,7 @@ C_SRCS = \
 	protectdata.c \
 	provstore.c \
 	regstore.c \
+	rootstore.c \
 	serialize.c \
 	sip.c \
 	store.c \
diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h
index ffd5a87..8111b15 100644
--- a/dlls/crypt32/crypt32_private.h
+++ b/dlls/crypt32/crypt32_private.h
@@ -250,6 +250,7 @@ PWINECRYPT_CERTSTORE CRYPT_FileNameOpenS
  DWORD dwFlags, const void *pvPara);
 PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv,
  DWORD dwFlags, const void *pvPara);
+PWINECRYPT_CERTSTORE CRYPT_RootOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags);
 
 /* Helper function for store reading functions and
  * CertAddSerializedElementToStore.  Returns a context of the appropriate type
diff --git a/dlls/crypt32/rootstore.c b/dlls/crypt32/rootstore.c
new file mode 100644
index 0000000..1b3b325
--- /dev/null
+++ b/dlls/crypt32/rootstore.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright 2007 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include "config.h"
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "wincrypt.h"
+#include "winternl.h"
+#include "wine/debug.h"
+#include "crypt32_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(crypt);
+
+#define INITIAL_CERT_BUFFER 1024
+
+struct DynamicBuffer
+{
+    DWORD allocated;
+    DWORD used;
+    BYTE *data;
+};
+
+static inline void reset_buffer(struct DynamicBuffer *buffer)
+{
+    buffer->used = 0;
+    if (buffer->data) buffer->data[0] = 0;
+}
+
+static BOOL add_line_to_buffer(struct DynamicBuffer *buffer, LPCSTR line)
+{
+    BOOL ret;
+
+    if (buffer->used + strlen(line) + 1 > buffer->allocated)
+    {
+        if (!buffer->allocated)
+        {
+            buffer->data = CryptMemAlloc(INITIAL_CERT_BUFFER);
+            if (buffer->data)
+            {
+                buffer->data[0] = 0;
+                buffer->allocated = INITIAL_CERT_BUFFER;
+            }
+        }
+        else
+        {
+            DWORD new_size = max(buffer->allocated * 2,
+             buffer->used + strlen(line) + 1);
+
+            buffer->data = CryptMemRealloc(buffer->data, new_size);
+            if (buffer->data)
+                buffer->allocated = new_size;
+        }
+    }
+    if (buffer->data)
+    {
+        strcpy((char *)buffer->data + strlen((char *)buffer->data), line);
+        /* Not strlen + 1, otherwise we'd count the NULL for every line's
+         * addition (but we overwrite the previous NULL character.)  Not an
+         * overrun, we allocate strlen + 1 bytes above.
+         */
+        buffer->used += strlen(line);
+        ret = TRUE;
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+static void import_base64_certs_from_fp(FILE *fp, HCERTSTORE store)
+{
+    char line[1024];
+    BOOL in_cert = FALSE;
+    struct DynamicBuffer saved_cert = { 0, 0, NULL };
+    int num_certs = 0;
+
+    TRACE("\n");
+    while (fgets(line, sizeof(line), fp))
+    {
+        static const char header[] = "-----BEGIN CERTIFICATE-----";
+        static const char trailer[] = "-----END CERTIFICATE-----";
+
+        if (!strncmp(line, header, strlen(header)))
+        {
+            TRACE("begin new certificate\n");
+            in_cert = TRUE;
+            reset_buffer(&saved_cert);
+        }
+        else if (!strncmp(line, trailer, strlen(trailer)))
+        {
+            DWORD size;
+
+            TRACE("end of certificate, adding cert\n");
+            in_cert = FALSE;
+            if (CryptStringToBinaryA((char *)saved_cert.data, saved_cert.used,
+             CRYPT_STRING_BASE64, NULL, &size, NULL, NULL))
+            {
+                LPBYTE buf = CryptMemAlloc(size);
+
+                if (buf)
+                {
+                    CryptStringToBinaryA((char *)saved_cert.data,
+                     saved_cert.used, CRYPT_STRING_BASE64, buf, &size, NULL,
+                     NULL);
+                    if (CertAddEncodedCertificateToStore(store,
+                     X509_ASN_ENCODING, buf, size, CERT_STORE_ADD_NEW, NULL))
+                        num_certs++;
+                }
+            }
+        }
+        else if (in_cert)
+            add_line_to_buffer(&saved_cert, line);
+    }
+    CryptMemFree(saved_cert.data);
+    TRACE("Read %d certs\n", num_certs);
+}
+
+static BOOL can_be_ca(PCCERT_CONTEXT cert)
+{
+    BOOL ret = FALSE;
+    PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
+     cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
+
+    if (ext)
+    {
+        CERT_BASIC_CONSTRAINTS_INFO *info;
+        DWORD size = 0;
+
+        ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
+         ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
+         NULL, (LPBYTE)&info, &size);
+        if (ret)
+        {
+            if (info->SubjectType.cbData == 1)
+                ret = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
+            LocalFree(info);
+        }
+    }
+    else
+    {
+        ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
+         cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
+        if (ext)
+        {
+            CERT_BASIC_CONSTRAINTS2_INFO *info;
+            DWORD size = 0;
+
+            ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
+             szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
+             CRYPT_DECODE_ALLOC_FLAG, NULL, (LPBYTE)&info, &size);
+            if (ret)
+            {
+                ret = info->fCA;
+                LocalFree(info);
+            }
+        }
+        else
+            ret = TRUE;
+    }
+    return ret;
+}
+
+static void check_and_store_certs(HCERTSTORE from, HCERTSTORE to)
+{
+    PCCERT_CONTEXT cert = NULL;
+    DWORD root_count = 0;
+
+    TRACE("\n");
+
+    do {
+        cert = CertEnumCertificatesInStore(from, cert);
+        if (cert)
+        {
+            PCCERT_CONTEXT issuer = NULL, prev_issuer = NULL;
+            DWORD flags;
+
+            /* Find a valid issuer for this cert. */
+            if (!prev_issuer)
+            {
+                do {
+                    flags =
+                     CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG;
+                    issuer = CertGetIssuerCertificateFromStore(from, cert,
+                     issuer, &flags);
+                    if (issuer)
+                        prev_issuer = CertDuplicateCertificateContext(issuer);
+                } while (issuer && flags);
+            }
+
+            if (!prev_issuer)
+            {
+                if (CertCompareCertificateName(cert->dwCertEncodingType,
+                 &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer))
+                {
+                    /* Self-signed cert, check its signature */
+                    if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING,
+                     CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)cert,
+                     CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)cert, 0, NULL))
+                        TRACE("rejecting %p, bad self signature\n", cert);
+                    else if (!can_be_ca(cert))
+                        TRACE("rejecting %p, can't act as CA\n", cert);
+                    else
+                    {
+                        if (CertAddCertificateContextToStore(to, cert,
+                         CERT_STORE_ADD_NEW, NULL))
+                            root_count++;
+                        else if (GetLastError() == CRYPT_E_EXISTS)
+                            TRACE("rejecting %p, already present\n", cert);
+                        else
+                            TRACE("adding %p failed\n", cert);
+                    }
+                }
+                else
+                    TRACE("rejecting %p, no issuer\n", cert);
+            }
+            else
+            {
+                if (flags & CERT_STORE_SIGNATURE_FLAG)
+                    TRACE("rejecting %p, bad signature\n", cert);
+                else if (flags & CERT_STORE_TIME_VALIDITY_FLAG)
+                    TRACE("rejecting %p, expired\n", cert);
+                else if (!can_be_ca(cert))
+                    TRACE("rejecting %p, can't act as CA\n", cert);
+                else
+                {
+                    if (CertAddCertificateContextToStore(to, cert,
+                     CERT_STORE_ADD_NEW, NULL))
+                        root_count++;
+                    else if (GetLastError() == CRYPT_E_EXISTS)
+                        TRACE("rejecting %p, already present\n", cert);
+                    else
+                        TRACE("adding %p failed\n", cert);
+                }
+                CertFreeCertificateContext(prev_issuer);
+            }
+        }
+    } while (cert);
+    TRACE("Added %d root certificates\n", root_count);
+}
+
+/* Copied from ntdll/file.c */
+static NTSTATUS FILE_GetNtStatus(void)
+{
+    int err = errno;
+
+    TRACE( "errno = %d\n", errno );
+    switch (err)
+    {
+    case EAGAIN:    return STATUS_SHARING_VIOLATION;
+    case EBADF:     return STATUS_INVALID_HANDLE;
+    case EBUSY:     return STATUS_DEVICE_BUSY;
+    case ENOSPC:    return STATUS_DISK_FULL;
+    case EPERM:
+    case EROFS:
+    case EACCES:    return STATUS_ACCESS_DENIED;
+    case ENOTDIR:   return STATUS_OBJECT_PATH_NOT_FOUND;
+    case ENOENT:    return STATUS_OBJECT_NAME_NOT_FOUND;
+    case EISDIR:    return STATUS_FILE_IS_A_DIRECTORY;
+    case EMFILE:
+    case ENFILE:    return STATUS_TOO_MANY_OPENED_FILES;
+    case EINVAL:    return STATUS_INVALID_PARAMETER;
+    case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
+    case EPIPE:     return STATUS_PIPE_DISCONNECTED;
+    case EIO:       return STATUS_DEVICE_NOT_READY;
+#ifdef ENOMEDIUM
+    case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
+#endif
+    case ENXIO:     return STATUS_NO_SUCH_DEVICE;
+    case ENOTTY:
+    case EOPNOTSUPP:return STATUS_NOT_SUPPORTED;
+    case ECONNRESET:return STATUS_PIPE_DISCONNECTED;
+    case EFAULT:    return STATUS_ACCESS_VIOLATION;
+    case ESPIPE:    return STATUS_ILLEGAL_FUNCTION;
+    case ENOEXEC:   /* ?? */
+    case EEXIST:    /* ?? */
+    default:
+        FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
+        return STATUS_UNSUCCESSFUL;
+    }
+}
+
+static void import_certs_from_file(int fd, HCERTSTORE store)
+{
+    FILE *fp;
+
+    TRACE("\n");
+
+    fp = fdopen(fd, "r");
+    if (fp)
+    {
+        import_base64_certs_from_fp(fp, store);
+        fclose(fp);
+    }
+    else
+        SetLastError(RtlNtStatusToDosError(FILE_GetNtStatus()));
+}
+
+static void import_certs_from_path(LPCSTR path, HCERTSTORE store,
+ BOOL allow_dir);
+
+static void import_certs_from_dir(LPCSTR path, HCERTSTORE store)
+{
+    DIR *dir;
+
+    TRACE("(%s, %p)\n", debugstr_a(path), store);
+
+    dir = opendir(path);
+    if (dir)
+    {
+        size_t bufsize = strlen(path) + 1 + PATH_MAX + 1;
+        char *filebuf = CryptMemAlloc(bufsize);
+
+        if (filebuf)
+        {
+            struct dirent *entry;
+            while ((entry = readdir(dir)))
+            {
+                if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
+                {
+                    snprintf(filebuf, bufsize, "%s/%s", path, entry->d_name);
+                    import_certs_from_path(filebuf, store, FALSE);
+                }
+            }
+            closedir(dir);
+            CryptMemFree(filebuf);
+        }
+    }
+    else
+        SetLastError(RtlNtStatusToDosError(FILE_GetNtStatus()));
+}
+
+static void import_certs_from_path(LPCSTR path, HCERTSTORE store,
+ BOOL allow_dir)
+{
+    int fd;
+
+    TRACE("(%s, %p, %d)\n", debugstr_a(path), store, allow_dir);
+
+    fd = open(path, O_RDONLY);
+    if (fd != -1)
+    {
+        struct stat st;
+
+        if (fstat(fd, &st) == 0)
+        {
+            if (S_ISREG(st.st_mode))
+                import_certs_from_file(fd, store);
+            else if (S_ISDIR(st.st_mode))
+            {
+                if (allow_dir)
+                    import_certs_from_dir(path, store);
+                else
+                    WARN("%s is a directory and directories are disallowed\n",
+                     debugstr_a(path));
+            }
+            else
+                ERR("%s: invalid file type\n", path);
+        }
+        else
+            SetLastError(RtlNtStatusToDosError(FILE_GetNtStatus()));
+        close(fd);
+    }
+    else
+    {
+        /* Don't set last error, not every file or directory is expected to
+         * exist.
+         */
+    }
+}
+
+static BOOL WINAPI CRYPT_RootWriteCert(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT cert, DWORD dwFlags)
+{
+    /* The root store can't have certs added */
+    return FALSE;
+}
+
+static BOOL WINAPI CRYPT_RootDeleteCert(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT cert, DWORD dwFlags)
+{
+    /* The root store can't have certs deleted */
+    return FALSE;
+}
+
+static BOOL WINAPI CRYPT_RootWriteCRL(HCERTSTORE hCertStore,
+ PCCRL_CONTEXT crl, DWORD dwFlags)
+{
+    /* The root store can have CRLs added.  At worst, a malicious application
+     * can DoS itself, as the changes aren't persisted in any way.
+     */
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_RootDeleteCRL(HCERTSTORE hCertStore,
+ PCCRL_CONTEXT crl, DWORD dwFlags)
+{
+    /* The root store can't have CRLs deleted */
+    return FALSE;
+}
+
+static void *rootProvFuncs[] = {
+    NULL, /* CERT_STORE_PROV_CLOSE_FUNC */
+    NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
+    CRYPT_RootWriteCert,
+    CRYPT_RootDeleteCert,
+    NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
+    NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
+    CRYPT_RootWriteCRL,
+    CRYPT_RootDeleteCRL,
+    NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
+    NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
+    NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */
+    NULL, /* CERT_STORE_PROV_DELETE_CTL_FUNC */
+    NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
+    NULL, /* CERT_STORE_PROV_CONTROL_FUNC */
+};
+
+static PWINECRYPT_CERTSTORE CRYPT_RootOpenStoreFromReg(void)
+{
+    static const WCHAR rootW[] = { 'S','o','f','t','w','a','r','e','\\',
+     'W','i','n','e','\\','C','r','y','p','t','o','\\','R','o','o','t',0 };
+    HCERTSTORE root = NULL;
+    HKEY hKey;
+    LONG rc;
+
+    TRACE("\n");
+    /* @@ Wine registry key: HKLM\Software\Wine\Crypto\Root */
+    if (!(rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, rootW, 0, NULL,
+     REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, NULL)))
+    {
+        char *rootStr = NULL;
+        DWORD valueType, len;
+
+        if (!(rc = RegQueryValueExA(hKey, "Path", 0, &valueType, NULL, &len)))
+        {
+            if (valueType == REG_SZ || valueType == REG_MULTI_SZ)
+            {
+                rootStr = CryptMemAlloc(len);
+                if (rootStr)
+                {
+                    if ((rc = RegQueryValueExA(hKey, "Path", 0, &valueType,
+                     (LPBYTE)rootStr, &len)))
+                    {
+                        TRACE("RegQueryValueExA failed: %d\n", rc);
+                        CryptMemFree(rootStr);
+                        rootStr = NULL;
+                        SetLastError(rc);
+                    }
+                    else if (valueType != REG_SZ && valueType != REG_MULTI_SZ)
+                    {
+                        TRACE("wrong registry type %d\n", valueType);
+                        CryptMemFree(rootStr);
+                        rootStr = NULL;
+                        SetLastError(ERROR_INVALID_DATA);
+                    }
+                }
+            }
+            else
+            {
+                TRACE("wrong registry type %d\n", valueType);
+                SetLastError(ERROR_INVALID_DATA);
+            }
+        }
+        else
+        {
+            TRACE("RegQueryValueExA failed: %d\n", rc);
+            SetLastError(rc);
+        }
+
+        if (rootStr)
+        {
+            HCERTSTORE from = CertOpenStore(CERT_STORE_PROV_MEMORY,
+             X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
+            HCERTSTORE to = CertOpenStore(CERT_STORE_PROV_MEMORY,
+             X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
+
+            if (from && to)
+            {
+                CERT_STORE_PROV_INFO provInfo = {
+                 sizeof(CERT_STORE_PROV_INFO),
+                 sizeof(rootProvFuncs) / sizeof(rootProvFuncs[0]),
+                 rootProvFuncs,
+                 NULL,
+                 0,
+                 NULL
+                };
+
+                if (valueType == REG_SZ)
+                    import_certs_from_path(rootStr, from, TRUE);
+                else
+                {
+                    char *path = rootStr;
+
+                    for (; path && *path; path += strlen(path) + 1)
+                        import_certs_from_path(path, from, TRUE);
+                }
+                check_and_store_certs(from, to);
+                root = CRYPT_ProvCreateStore(0, to, &provInfo);
+            }
+            CertCloseStore(from, 0);
+            CryptMemFree(rootStr);
+        }
+        RegCloseKey(hKey);
+    }
+    else
+    {
+        TRACE("RegCreateKeyExW failed: %d\n", rc);
+        SetLastError(rc);
+    }
+    TRACE("returning %p\n", root);
+    return root;
+}
+
+static PWINECRYPT_CERTSTORE CRYPT_rootStore;
+
+PWINECRYPT_CERTSTORE CRYPT_RootOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags)
+{
+    TRACE("(%ld, %08x)\n", hCryptProv, dwFlags);
+
+    if (dwFlags & CERT_STORE_DELETE_FLAG)
+    {
+        WARN("root store can't be deleted\n");
+        SetLastError(ERROR_ACCESS_DENIED);
+        return NULL;
+    }
+    switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK)
+    {
+    case CERT_SYSTEM_STORE_LOCAL_MACHINE:
+    case CERT_SYSTEM_STORE_CURRENT_USER:
+        break;
+    default:
+        TRACE("location %08x unsupported\n",
+         dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
+        SetLastError(E_INVALIDARG);
+        return NULL;
+    }
+    if (!CRYPT_rootStore)
+    {
+        HCERTSTORE root = CRYPT_RootOpenStoreFromReg();
+
+        InterlockedCompareExchangePointer((PVOID *)&CRYPT_rootStore, root,
+         NULL);
+        if (CRYPT_rootStore != root)
+            CertCloseStore(root, 0);
+    }
+    CertDuplicateStore(CRYPT_rootStore);
+    return CRYPT_rootStore;
+}
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c
index fd7ec8a..8a710a1 100644
--- a/dlls/crypt32/store.c
+++ b/dlls/crypt32/store.c
@@ -268,14 +268,10 @@ static WINECRYPT_CERTSTORE *CRYPT_MemOpe
     return (PWINECRYPT_CERTSTORE)store;
 }
 
-/* FIXME: this isn't complete for the Root store, in which the top-level
- * self-signed CA certs reside.  Adding a cert to the Root store should present
- * the user with a dialog indicating the consequences of doing so, and asking
- * the user to confirm whether the cert should be added.
- */
 static PWINECRYPT_CERTSTORE CRYPT_SysRegOpenStoreW(HCRYPTPROV hCryptProv,
  DWORD dwFlags, const void *pvPara)
 {
+    static const WCHAR rootW[] = { 'R','o','o','t',0 };
     static const WCHAR fmt[] = { '%','s','\\','%','s',0 };
     LPCWSTR storeName = (LPCWSTR)pvPara;
     LPWSTR storePath;
@@ -292,6 +288,8 @@ static PWINECRYPT_CERTSTORE CRYPT_SysReg
         SetLastError(E_INVALIDARG);
         return NULL;
     }
+    if (!lstrcmpiW(storeName, rootW))
+        return CRYPT_RootOpenStore(hCryptProv, dwFlags);
 
     ret = TRUE;
     switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK)
-- 
1.4.1


More information about the wine-patches mailing list