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