[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.
This commit is contained in:
Jiaqiang Xu 2015-05-18 20:43:34 +08:00
parent c50132945b
commit fb35ae8ca3
3 changed files with 147 additions and 1 deletions

View File

@ -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

View File

@ -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@

View File

@ -1,3 +1,5 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <pthread.h>
@ -5,6 +7,13 @@
#include <jansson.h>
#include <event2/buffer.h>
#ifdef WIN32
#include <windows.h>
#include <wincrypt.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#endif
#include <ccnet/ccnet-client.h>
#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);