From fb35ae8ca301b399598daa6c4c3a91dd3b9428e7 Mon Sep 17 00:00:00 2001 From: Jiaqiang Xu Date: Mon, 18 May 2015 20:43:34 +0800 Subject: [PATCH] [win] Extract system CA certs to use with openssl. This allows users to set custom CA to system repository while we use openssl for https sync. --- configure.ac | 4 ++ daemon/Makefile.am | 2 +- daemon/http-tx-mgr.c | 142 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d7bd3259..a68cc83c 100644 --- a/configure.ac +++ b/configure.ac @@ -192,6 +192,7 @@ if test "$bwin32" = true; then LIB_PSAPI=-lpsapi LIB_MAC= MSVC_CFLAGS="-D__MSVCRT__ -D__MSVCRT_VERSION__=0x0601" + LIB_CRYPT32=-lcrypt32 elif test "$bmac" = true ; then LIB_WS32= LIB_GDI32= @@ -204,6 +205,7 @@ elif test "$bmac" = true ; then LIB_PSAPI= MSVC_CFLAGS= LIB_MAC="-framework CoreServices" + LIB_CRYPT32= else LIB_WS32= LIB_GDI32= @@ -216,6 +218,7 @@ else LIB_PSAPI= LIB_MAC= MSVC_CFLAGS= + LIB_CRYPT32= fi AC_SUBST(LIB_WS32) @@ -229,6 +232,7 @@ AC_SUBST(LIB_SHELL32) AC_SUBST(LIB_PSAPI) AC_SUBST(LIB_MAC) AC_SUBST(MSVC_CFLAGS) +AC_SUBST(LIB_CRYPT32) LIBEVENT_REQUIRED=2.0 diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 894bd5ac..060931e9 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -128,7 +128,7 @@ seaf_daemon_LDADD = $(top_builddir)/lib/libseafile_common.la \ @LIB_INTL@ \ @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \ $(top_builddir)/common/cdc/libcdc.la \ - $(top_builddir)/common/index/libindex.la ${LIB_WS32} \ + $(top_builddir)/common/index/libindex.la @LIB_WS32@ @LIB_CRYPT32@ \ @SEARPC_LIBS@ @CCNET_LIBS@ @GNOME_KEYRING_LIBS@ @JANSSON_LIBS@ @LIB_MAC@ @ZLIB_LIBS@ @CURL_LIBS@ seaf_daemon_LDFLAGS = @STATIC_COMPILE@ @CONSOLE@ diff --git a/daemon/http-tx-mgr.c b/daemon/http-tx-mgr.c index f7572046..0330011c 100644 --- a/daemon/http-tx-mgr.c +++ b/daemon/http-tx-mgr.c @@ -1,3 +1,5 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + #include "common.h" #include @@ -5,6 +7,13 @@ #include #include +#ifdef WIN32 +#include +#include +#include +#include +#endif + #include #include "seafile-config.h" @@ -66,6 +75,8 @@ struct _HttpTxPriv { pthread_mutex_t pools_lock; CcnetTimer *reset_bytes_timer; + + char *ca_bundle_path; }; typedef struct _HttpTxPriv HttpTxPriv; @@ -235,6 +246,8 @@ http_tx_manager_new (struct _SeafileSession *seaf) priv->connection_pools = g_hash_table_new (g_str_hash, g_str_equal); pthread_mutex_init (&priv->pools_lock, NULL); + priv->ca_bundle_path = g_build_filename (seaf->seaf_dir, "ca-bundle.pem", NULL); + mgr->priv = priv; return mgr; @@ -271,6 +284,11 @@ http_tx_manager_start (HttpTxManager *mgr) { curl_global_init (CURL_GLOBAL_ALL); +#ifdef WIN32 + /* Remove existing ca-bundle file on start. */ + g_unlink (mgr->priv->ca_bundle_path); +#endif + /* TODO: add a timer to clean up unused Http connections. */ mgr->priv->reset_bytes_timer = ccnet_timer_new (reset_bytes, @@ -282,6 +300,118 @@ http_tx_manager_start (HttpTxManager *mgr) /* Common Utility Functions. */ +#ifdef WIN32 + +static void +write_cert_name_to_pem_file (FILE *f, PCCERT_CONTEXT pc) +{ + char *name; + DWORD size; + + fprintf (f, "\n"); + + if (!CertGetCertificateContextProperty(pc, + CERT_FRIENDLY_NAME_PROP_ID, + NULL, &size)) { + seaf_warning ("Failed to get cert name: %lu\n", GetLastError()); + return; + } + + name = g_malloc ((gsize)size); + if (!name) { + seaf_warning ("Failed to alloc memory\n"); + return; + } + + if (!CertGetCertificateContextProperty(pc, + CERT_FRIENDLY_NAME_PROP_ID, + name, &size)) { + seaf_warning ("Failed to get cert name: %lu\n", GetLastError()); + g_free (name); + return; + } + + if (fwrite(name, (size_t)size, 1, f) != 1) { + seaf_warning ("Failed to write pem file.\n"); + g_free (name); + return; + } + fprintf (f, "\n"); + + g_free (name); +} + +static void +write_cert_to_pem_file (FILE *f, PCCERT_CONTEXT pc) +{ + const unsigned char *der = pc->pbCertEncoded; + X509 *cert; + + write_cert_name_to_pem_file (f, pc); + + cert = d2i_X509 (NULL, &der, (int)pc->cbCertEncoded); + if (!cert) { + seaf_warning ("Failed to parse certificate from DER.\n"); + return; + } + + if (!PEM_write_X509 (f, cert)) { + seaf_warning ("Failed to write certificate.\n"); + X509_free (cert); + return; + } + + X509_free (cert); +} + +static int +create_ca_bundle (const char *ca_bundle_path) +{ + HCERTSTORE store; + FILE *f; + + store = CertOpenSystemStoreW (0, L"ROOT"); + if (!store) { + seaf_warning ("Failed to open system cert store: %lu\n", GetLastError()); + return -1; + } + + f = g_fopen (ca_bundle_path, "w+"); + if (!f) { + seaf_warning ("Failed to open cabundle file\n"); + CertCloseStore(store, 0); + return -1; + } + + PCCERT_CONTEXT pc = NULL; + while (1) { + pc = CertFindCertificateInStore (store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pc); + if (!pc) + break; + write_cert_to_pem_file (f, pc); + } + + CertCloseStore(store, 0); + fclose (f); + + return 0; +} + +static void +load_ca_bundle (CURL *curl) +{ + char *ca_bundle_path = seaf->http_tx_mgr->priv->ca_bundle_path; + + if (!seaf_util_exists (ca_bundle_path)) { + if (create_ca_bundle (ca_bundle_path) < 0) + return; + } + + curl_easy_setopt (curl, CURLOPT_CAINFO, ca_bundle_path); +} + +#endif /* WIN32 */ + static void set_proxy (CURL *curl, gboolean is_https) { @@ -399,6 +529,10 @@ http_get (CURL *curl, const char *url, const char *token, curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); +#ifdef WIN32 + load_ca_bundle (curl); +#endif + int rc = curl_easy_perform (curl); if (rc != 0) { seaf_warning ("libcurl failed to GET %s: %s.\n", @@ -518,6 +652,10 @@ http_put (CURL *curl, const char *url, const char *token, curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); +#ifdef WIN32 + load_ca_bundle (curl); +#endif + int rc = curl_easy_perform (curl); if (rc != 0) { seaf_warning ("libcurl failed to PUT %s: %s.\n", @@ -600,6 +738,10 @@ http_post (CURL *curl, const char *url, const char *token, curl_easy_setopt(curl, CURLOPT_WRITEDATA, &rsp); } +#ifdef WIN32 + load_ca_bundle (curl); +#endif + gboolean is_https = (strncasecmp(url, "https", strlen("https")) == 0); set_proxy (curl, is_https);