winerootcerts: Add winerootcerts, a program to install root certificates into registry

Juan Lang juan.lang at gmail.com
Wed Aug 15 12:20:48 CDT 2007


With patch :/
-------------- next part --------------
From fd238f754dab5a8a6b85b3ca63c5deef1b18091a Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Wed, 15 Aug 2007 09:31:23 -0700
Subject: [PATCH] Add winerootcerts, a program to install root certificates into registry
---
 programs/winerootcerts/Makefile.in     |   16 +
 programs/winerootcerts/winerootcerts.c |  607 ++++++++++++++++++++++++++++++++
 2 files changed, 623 insertions(+), 0 deletions(-)

diff --git a/programs/winerootcerts/Makefile.in b/programs/winerootcerts/Makefile.in
new file mode 100644
index 0000000..261cd01
--- /dev/null
+++ b/programs/winerootcerts/Makefile.in
@@ -0,0 +1,16 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = winerootcerts.exe
+APPMODE   = -mconsole
+IMPORTS   = advapi32 kernel32 crypt32
+DELAYIMPORTS = wininet
+
+C_SRCS = \
+	winerootcerts.c
+
+ at MAKE_PROG_RULES@
+
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/programs/winerootcerts/winerootcerts.c b/programs/winerootcerts/winerootcerts.c
new file mode 100644
index 0000000..9951a30
--- /dev/null
+++ b/programs/winerootcerts/winerootcerts.c
@@ -0,0 +1,607 @@
+/*
+ * an application for importing root CA certificates into Wine's registry
+ *
+ * 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 <stdarg.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wincrypt.h>
+#include <wininet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(winerootcerts);
+
+#define INITIAL_CERT_BUFFER 1024
+
+static const char DefaultURL[] = "http://lxr.mozilla.org/seamonkey/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1";
+static const char DefaultStore[] = "Root";
+
+typedef enum _CertFileFormat {
+    FileFormatBase64,
+    FileFormatDER,
+    FileFormatMozilla,
+} CertFileFormat;
+
+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_byte_to_buffer(struct DynamicBuffer *buffer, BYTE byte)
+{
+    BOOL ret;
+
+    if (buffer->used + 1 > buffer->allocated)
+    {
+        if (!buffer->allocated)
+        {
+            buffer->data = HeapAlloc(GetProcessHeap(), 0, INITIAL_CERT_BUFFER);
+            if (buffer->data)
+            {
+                buffer->data[0] = 0;
+                buffer->allocated = INITIAL_CERT_BUFFER;
+            }
+        }
+        else
+        {
+            buffer->data = HeapReAlloc(GetProcessHeap(), 0, buffer->data,
+             buffer->allocated * 2);
+            if (buffer->data)
+                buffer->allocated *= 2;
+        }
+    }
+    if (buffer->data)
+    {
+        buffer->data[buffer->used++] = byte;
+        ret = TRUE;
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+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 = HeapAlloc(GetProcessHeap(), 0, 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 = HeapReAlloc(GetProcessHeap(), 0, 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_mozilla_from_fp(FILE *fp, HCERTSTORE store)
+{
+    char line[1024];
+    BOOL in_cert = FALSE, in_value = FALSE, error = FALSE;
+    struct DynamicBuffer saved_cert = { 0, 0, NULL };
+    int num_certs = 0;
+
+    while (fgets(line, sizeof(line), fp))
+    {
+        char *tok;
+
+        if (strlen(line) == 0)
+        {
+            WINE_TRACE("empty line, resetting\n");
+            error = in_cert = in_value = FALSE;
+        }
+        else if (strlen(line) >= 2 && line[strlen(line) - 2] == '\r' &&
+         line[strlen(line) - 1] == '\n')
+            line[strlen(line) - 2] = '\0';
+        else if (line[strlen(line) - 1] == '\n')
+            line[strlen(line) - 1] = '\0';
+
+        tok = strtok(line, " \t");
+        if (!tok || !strcmp(tok, "#"))
+            continue;
+        else if (in_value && !strcmp(tok, "END"))
+        {
+            WINE_TRACE("end of value, adding cert\n");
+            in_cert = in_value = FALSE;
+            if (!error)
+            {
+                if (CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+                 saved_cert.data, saved_cert.used, CERT_STORE_ADD_NEW, NULL))
+                    num_certs++;
+                else
+                    fprintf(stderr,
+                     "CertAddEncodedCertificateToStore failed: %08x\n",
+                     GetLastError());
+            }
+        }
+        else if (!strcmp(tok, "CKA_CLASS"))
+        {
+            tok += strlen(tok) + 1;
+            tok = strtok(tok, " \t");
+            if (!strcmp(tok, "CK_OBJECT_CLASS"))
+            {
+                tok += strlen(tok) + 1;
+                tok = strtok(tok, " \t");
+                if (!strcmp(tok, "CKO_CERTIFICATE"))
+                {
+                    WINE_TRACE("begin new certificate\n");
+                    in_cert = TRUE;
+                }
+            }
+        }
+        else if (in_cert && !strcmp(tok, "CKA_VALUE"))
+        {
+            tok += strlen(tok) + 1;
+            tok = strtok(tok, " \t");
+            if (!strcmp(tok, "MULTILINE_OCTAL"))
+            {
+                WINE_TRACE("begin cert value\n");
+                in_value = TRUE;
+                reset_buffer(&saved_cert);
+            }
+            else
+                WINE_WARN("unknown format %s\n", wine_dbgstr_a(tok));
+        }
+        else if (in_cert && in_value)
+        {
+            char *ptr = line;
+            unsigned int byte;
+
+            while (ptr && sscanf(ptr, "\\%o", &byte) == 1)
+            {
+                if (byte > 0xff)
+                    error = TRUE;
+                else
+                    add_byte_to_buffer(&saved_cert, byte);
+                ptr = strchr(ptr + 1, '\\');
+            }
+            if (ptr && *ptr)
+                WINE_TRACE("bad char: %c\n", *ptr);
+        }
+    }
+    printf("Read %d certs\n", num_certs);
+}
+
+static void import_base64_from_fp(FILE *fp, HCERTSTORE store)
+{
+    char line[1024];
+    BOOL in_cert = FALSE;
+    struct DynamicBuffer saved_cert = { 0, 0, NULL };
+    int num_certs = 0;
+
+    WINE_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)))
+        {
+            WINE_TRACE("begin new certificate\n");
+            in_cert = TRUE;
+            reset_buffer(&saved_cert);
+        }
+        else if (!strncmp(line, trailer, strlen(trailer)))
+        {
+            DWORD size;
+
+            WINE_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 = HeapAlloc(GetProcessHeap(), 0, 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
+                        fprintf(stderr, "Cert decoding failed: %08x\n",
+                         GetLastError());
+                }
+            }
+            else
+                fprintf(stderr, "base64 decoding failed: %08x\n",
+                 GetLastError());
+        }
+        else if (in_cert)
+            add_line_to_buffer(&saved_cert, line);
+    }
+    printf("Read %d certs\n", num_certs);
+}
+
+static void import_der_from_fp(FILE *fp, HCERTSTORE store)
+{
+    long size;
+    BYTE *buf;
+
+    fseek(fp, 0, SEEK_END);
+    size = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+    buf = HeapAlloc(GetProcessHeap(), 0, size);
+    if (buf)
+    {
+        if (fread(buf, 1, size, fp) == size)
+        {
+            if (!CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+             buf, size, CERT_STORE_ADD_NEW, NULL))
+                fprintf(stderr, "Cert decoding failed: %08x\n", GetLastError());
+        }
+        else
+            perror("fread");
+        HeapFree(GetProcessHeap(), 0, buf);
+    }
+}
+
+static FILE *download_url(const char *url)
+{
+    static BYTE buf[4096];
+    HINTERNET internet = NULL, opened_url = NULL;
+    FILE *fp = NULL;
+    DWORD bytes_read;
+    BOOL ret;
+
+    printf("Downloading from %s\n", url);
+
+    internet = InternetOpenA("Wine Root Cert tool",
+     INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+    if (!internet)
+        goto error;
+    opened_url = InternetOpenUrlA(internet, url, NULL, 0, 0, 0);
+    if (!opened_url)
+        goto error;
+    fp = tmpfile();
+    if (!fp)
+        goto error;
+
+    do {
+        ret = InternetReadFile(opened_url, buf, sizeof(buf), &bytes_read);
+        if (ret && bytes_read)
+        {
+            size_t bytes_written = fwrite(buf, 1, bytes_read, fp);
+
+            if (bytes_written != bytes_read)
+                ret = FALSE;
+        }
+    } while (ret && bytes_read);
+    if (!ret)
+    {
+        fclose(fp);
+        fp = NULL;
+    }
+    else
+        rewind(fp);
+
+error:
+    InternetCloseHandle(opened_url);
+    InternetCloseHandle(internet);
+    return fp;
+}
+
+static void print_cert(PCCERT_CONTEXT cert)
+{
+    DWORD len = CertNameToStrA(X509_ASN_ENCODING, &cert->pCertInfo->Subject,
+     CERT_SIMPLE_NAME_STR, NULL, 0);
+
+    if (len)
+    {
+        char *name = HeapAlloc(GetProcessHeap(), 0, len);
+
+        if (name)
+        {
+            CertNameToStrA(X509_ASN_ENCODING, &cert->pCertInfo->Subject,
+             CERT_SIMPLE_NAME_STR, name, len);
+            printf("%s", name);
+            HeapFree(GetProcessHeap(), 0, name);
+        }
+    }
+}
+
+static void reject_cert(PCCERT_CONTEXT cert, PCCERT_CONTEXT issuer,
+ LPCSTR reason)
+{
+    printf("Rejecting ");
+    print_cert(cert);
+    if (issuer)
+    {
+        printf(",\nissued by ");
+        print_cert(issuer);
+    }
+    printf(":\n%s\n", reason);
+}
+
+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 store, HCERTSTORE root,
+ LPCSTR store_name)
+{
+    PCCERT_CONTEXT cert = NULL;
+    HCERTSTORE roots;
+    DWORD root_count = 0;
+
+    WINE_TRACE("%s\n", wine_dbgstr_a(store_name));
+
+    roots = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
+     CERT_SYSTEM_STORE_LOCAL_MACHINE, store_name);
+    do {
+        cert = CertEnumCertificatesInStore(store, cert);
+        if (cert)
+        {
+            PCCERT_CONTEXT issuer = NULL, prev_issuer = NULL;
+            DWORD flags;
+
+            /* Find a valid issuer for this cert.  First check our existing
+             * roots:
+             */
+            do {
+                flags =
+                 CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG;
+                issuer = CertGetIssuerCertificateFromStore(root, cert, issuer,
+                 &flags);
+                if (issuer)
+                    prev_issuer = CertDuplicateCertificateContext(issuer);
+            } while (issuer && flags);
+            /* If that doesn't work, try the store we're building up: */
+            if (!prev_issuer)
+            {
+                do {
+                    flags =
+                     CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG;
+                    issuer = CertGetIssuerCertificateFromStore(store, 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))
+                        reject_cert(cert, NULL, "Bad self signature");
+                    else if (!can_be_ca(cert))
+                        reject_cert(cert, NULL, "Can't act as CA");
+                    else
+                    {
+                        CertAddCertificateContextToStore(roots, cert,
+                         CERT_STORE_ADD_NEW, NULL);
+                        root_count++;
+                    }
+                }
+                else
+                    reject_cert(cert, NULL, "No issuer");
+            }
+            else
+            {
+                if (flags & CERT_STORE_SIGNATURE_FLAG)
+                    reject_cert(cert, prev_issuer, "Bad signature");
+                else if (flags & CERT_STORE_TIME_VALIDITY_FLAG)
+                    reject_cert(cert, prev_issuer, "Expired");
+                else if (!can_be_ca(cert))
+                    reject_cert(cert, NULL, "Can't act as CA");
+                else
+                {
+                    CertAddCertificateContextToStore(roots, cert,
+                     CERT_STORE_ADD_NEW, NULL);
+                    root_count++;
+                }
+                CertFreeCertificateContext(prev_issuer);
+            }
+        }
+    } while (cert);
+    CertCloseStore(roots, 0);
+    printf("Added %d root certificates\n", root_count);
+}
+
+static void print_usage(void)
+{
+    printf("Usage: winerootcerts -h | [options]\n");
+    printf("\n");
+    printf("Options:\n");
+    printf("  -format mozilla|base64|der\n");
+    printf("    specifies the file format.\n");
+    printf("    Default: mozilla\n");
+    printf("  -file certfile\n");
+    printf("    specifies a local file to import.\n");
+    printf("  -url url\n");
+    printf("    specifies a URL to download certificates from.\n");
+    printf("    Default:  Mozilla's web CVS front end\n");
+    printf("  -store store\n");
+    printf("    specifies the certificate store to save the certificates in.\n");
+    printf("    Default:  root\n");
+}
+
+int main(int argc, char *argv[])
+{
+    const char *url = NULL;
+    const char *file = NULL;
+    const char *store_name = NULL;
+    FILE *fp = NULL;
+    CertFileFormat format = FileFormatMozilla;
+    int i;
+
+    for (i = 1; i < argc; i++)
+    {
+        if (!lstrcmpi(argv[i], "-?") || !lstrcmpi(argv[i], "-h") ||
+         !lstrcmpi(argv[i], "-help"))
+        {
+            print_usage();
+            exit(0);
+        }
+        else if (!lstrcmpi(argv[i], "-file"))
+        {
+            file = argv[i + 1];
+            i++;
+        }
+        else if (!lstrcmpi(argv[i], "-url"))
+        {
+            url = argv[i + 1];
+            i++;
+        }
+        else if (!lstrcmpi(argv[i], "-format"))
+        {
+            if (!lstrcmpi(argv[i + 1], "mozilla"))
+                format = FileFormatMozilla;
+            else if (!lstrcmpi(argv[i + 1], "base64"))
+                format = FileFormatBase64;
+            else if (!lstrcmpi(argv[i + 1], "der"))
+                format = FileFormatDER;
+            else
+            {
+                fprintf(stderr, "Unknown file format: %s\n", argv[i + 1]);
+                print_usage();
+                exit(1);
+            }
+            i++;
+        }
+        else if (!lstrcmpi(argv[i], "-store"))
+        {
+            store_name = argv[i + 1];
+            i++;
+        }
+    }
+
+    if (!url && !file)
+        url = DefaultURL;
+    if (url)
+        fp = download_url(url);
+    else
+        fp = fopen(file, "r");
+    if (!store_name)
+        store_name = DefaultStore;
+    if (fp)
+    {
+        HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY,
+         X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
+        HCERTSTORE root = CertOpenSystemStoreA(0, "Root");
+
+        if (store)
+        {
+            switch (format)
+            {
+            case FileFormatMozilla:
+                import_mozilla_from_fp(fp, store);
+                break;
+            case FileFormatBase64:
+                import_base64_from_fp(fp, store);
+                break;
+            case FileFormatDER:
+                import_der_from_fp(fp, store);
+            }
+            check_and_store_certs(store, root, store_name);
+            CertCloseStore(store, 0);
+            CertCloseStore(root, 0);
+        }
+        fclose(fp);
+    }
+    else
+        perror(file);
+    return 0;
+}
-- 
1.4.1


More information about the wine-patches mailing list