Hello, Below you will find a patch that creates a new configuration option --cryptoapicastore (the naming sucks, I know...) which enables certificate verification using Windows Certificate Stores (CA and ROOT). It can be used in addition to --cafile and --capath or standalone. I have compile and briefly runtime tested it and it works as intended. It should be used with care -and this should probably be noted somewhere- and preferrably in conjuction with tls-verify or tls-remote and/or password authentication. I have a couple of improvements in mind (e.g. optionally filtering by certificate subject) and the documentation for the manpage is missing, but I'm posting this early since I will be on vacation for the next week.
Happy holidays :-) Best regards, Faidon diff -Nur openvpn-2.1_rc1/cryptoapi.c openvpn-2.1_rc1-void/cryptoapi.c --- openvpn-2.1_rc1/cryptoapi.c 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1_rc1-void/cryptoapi.c 2006-12-22 19:20:34.000000000 +0200 @@ -48,6 +48,8 @@ static HINSTANCE crypt32dll = NULL; static BOOL WINAPI (*CryptAcquireCertificatePrivateKey) (PCCERT_CONTEXT pCert, DWORD dwFlags, void *pvReserved, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) = NULL; +static PCCERT_CONTEXT WINAPI (*CertEnumCertificatesInStore) (HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrevCertContext) = NULL; #endif /* Size of an SSL signature: MD5+SHA1 */ @@ -364,7 +366,8 @@ } /* cert_context->pbCertEncoded is the cert X509 DER encoded. */ - cert = d2i_X509(NULL, (unsigned char **) &cd->cert_context->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &cd->cert_context->pbCertEncoded, cd->cert_context->cbCertEncoded); if (cert == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); @@ -461,3 +464,57 @@ } return 0; } + +int SSL_CTX_add_CryptoAPI_certificate_store(SSL_CTX *ssl_ctx, const char *store) +{ + HCERTSTORE cs; + PCCERT_CONTEXT ctx = NULL; + X509 *cert = NULL; + + cs = CertOpenSystemStore(0, store); + if (cs == NULL) { + CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); + goto err; + } + +#ifdef __MINGW32_VERSION + /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1 + * anyway. This is a hack around that problem. */ + if (crypt32dll == NULL) { + crypt32dll = LoadLibrary("crypt32"); + if (crypt32dll == NULL) { + CRYPTOAPIerr(CRYPTOAPI_F_LOAD_LIBRARY); + goto err; + } + } + if (CertEnumCertificatesInStore == NULL) { + CertEnumCertificatesInStore = GetProcAddress(crypt32dll, + "CertEnumCertificatesInStore"); + if (CertEnumCertificatesInStore == NULL) { + CRYPTOAPIerr(CRYPTOAPI_F_GET_PROC_ADDRESS); + goto err; + } + } +#endif + + while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { + cert = d2i_X509(NULL, (const unsigned char **) &ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (cert == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); + goto err; + } + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + goto err; + } + X509_free(cert); + } + CertCloseStore(cs, 0); + return 1; + + err: + if (cert) + X509_free(cert); + return 0; +} diff -Nur openvpn-2.1_rc1/cryptoapi.h openvpn-2.1_rc1-void/cryptoapi.h --- openvpn-2.1_rc1/cryptoapi.h 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1_rc1-void/cryptoapi.h 2006-12-22 05:24:19.000000000 +0200 @@ -2,6 +2,7 @@ #define _CRYPTOAPI_H_ int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop); +int SSL_CTX_add_CryptoAPI_certificate_store(SSL_CTX *ssl_ctx, const char *store); #endif /* !_CRYPTOAPI_H_ */ diff -Nur openvpn-2.1_rc1/options.c openvpn-2.1_rc1-void/options.c --- openvpn-2.1_rc1/options.c 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1_rc1-void/options.c 2006-12-22 06:51:37.000000000 +0200 @@ -447,6 +447,7 @@ #ifdef WIN32 "--cryptoapicert select-string : Load the certificate and private key from the\n" " Windows Certificate System Store.\n" + "--cryptoapicastore : Add Windows Certificate Store Certificate Authorities.\n" #endif "--tls-cipher l : A list l of allowable TLS ciphers separated by : (optional).\n" " : Use --show-tls to see a list of supported TLS ciphers.\n" @@ -1233,6 +1234,7 @@ SHOW_STR (pkcs12_file); #ifdef WIN32 SHOW_STR (cryptoapi_cert); + SHOW_BOOL (cryptoapi_castore); #endif SHOW_STR (cipher_list); SHOW_STR (tls_verify); @@ -1806,8 +1808,8 @@ #ifdef WIN32 if (options->cryptoapi_cert) { - if ((!(options->ca_file)) && (!(options->ca_path))) - msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)"); + if ((!(options->ca_file)) && (!(options->ca_path)) && (!(options->cryptoapi_castore))) + msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath) or CryptoAPI CAs (--cryptoapicastore"); if (options->cert_file) msg(M_USAGE, "Parameter --cert cannot be used when --cryptoapicert is also specified."); if (options->priv_key_file) @@ -1828,8 +1830,12 @@ } else { - if ((!(options->ca_file)) && (!(options->ca_path))) + if ((!(options->ca_file)) && (!(options->ca_path)) && (!(options->cryptoapi_castore))) +#ifdef WIN32 + msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath) or CryptoAPI CAs (--cryptoapicastore"); +#else msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)"); +#endif if (pull) { const int sum = (options->cert_file != NULL) + (options->priv_key_file != NULL); @@ -4886,6 +4892,11 @@ VERIFY_PERMISSION (OPT_P_GENERAL); options->cryptoapi_cert = p[1]; } + else if (streq (p[0], "cryptoapicastore")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->cryptoapi_castore = true; + } #endif else if (streq (p[0], "key") && p[1]) { diff -Nur openvpn-2.1_rc1/options.h openvpn-2.1_rc1-void/options.h --- openvpn-2.1_rc1/options.h 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1_rc1-void/options.h 2006-12-22 06:51:05.000000000 +0200 @@ -424,6 +424,7 @@ #ifdef WIN32 const char *cryptoapi_cert; + bool cryptoapi_castore; #endif /* data channel key exchange method */ diff -Nur openvpn-2.1_rc1/ssl.c openvpn-2.1_rc1-void/ssl.c --- openvpn-2.1_rc1/ssl.c 2006-10-16 01:30:21.000000000 +0300 +++ openvpn-2.1_rc1-void/ssl.c 2006-12-22 06:55:45.000000000 +0200 @@ -1276,6 +1276,17 @@ } } +#ifdef WIN32 + if (options->cryptoapi_castore) + { + /* Add CAs from Windows' certification stores to our store */ + if (!SSL_CTX_add_CryptoAPI_certificate_store(ctx, "CA")) + msg (M_SSLERR, "Cannot load CA certificates from Microsoft Certificate Store"); + if (!SSL_CTX_add_CryptoAPI_certificate_store(ctx, "ROOT")) + msg (M_SSLERR, "Cannot load Root certificates from Microsoft Certificate Store"); + } +#endif + /* Enable the use of certificate chains */ if (using_cert_file) {