Use CryptoAPI CA store
PATCH v3 1/1 Use CryptoAPI CA store
Hello,
Below you will find a revised version of a patch that I sent almost 9 months before.
It allows OpenVPN to verify certificates agains the Windows Certificate Store.
Changed since v2:
* Replace the global variable by a TLS options variable
* Added relevant man page entry
* Minor bugfixes (more #ifdef WIN32 to guard the variable declaration, spelling error etc.)
* diff against 2.1-rc4 instead of rc1
Please review and apply :-)
Regards,
Faidon
--
cryptoapi.c +85 -1
@@ -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 (*CertCreateCertificateContext) (DWORD dwCertEncodingType, +const BYTE *pbCertEncoded,DWORD cbCertEncoded) =NULL ;#endif /* Size of an SSL signature: MD5+SHA1 */
@@ -65,6 +67,8 @@ static BOOL WINAPI (*CryptAcquireCertifi#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106#define CRYPTOAPI_F_LOAD_LIBRARY 107#define CRYPTOAPI_F_GET_PROC_ADDRESS 108 +#define CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT 109 +#define CRYPTOAPI_F_CERT_GET_CERT_CHAIN 110static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { {ERR_PACK (ERR_LIB_CRYPTOAPI ,0 ,0 ),"microsoft cryptoapi" },
@@ -77,6 +81,8 @@ static ERR_STRING_DATA CRYPTOAPI_str_fun { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" }, { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" }, { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" }, + { ERR_PACK(0, CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT, 0), "CertCreateCertificateContext" }, + { ERR_PACK(0, CRYPTOAPI_F_CERT_GET_CERT_CHAIN, 0), "CertGetCertificateChain" }, { 0, NULL } };
@@ -364,7 +370,7 @@ int SSL_CTX_use_CryptoAPI_certificate(SS }/* 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 +467,81 @@ int SSL_CTX_use_CryptoAPI_certificate(SS }return 0 ; } + +int CryptoAPI_verify_certificate (X509 *x509) +{ +int ret =-1 ; +int len; +unsigned char *buf =NULL ; + +PCCERT_CONTEXT pCertContext =NULL ; +PCCERT_CHAIN_CONTEXT pChainContext =NULL ; +CERT_ENHKEY_USAGE EnhkeyUsage; +CERT_USAGE_MATCH CertUsage; +CERT_CHAIN_PARA ChainPara; + +/* Convert from internal X509 format to DER */ + len =i2d_X509 (x509, &buf); +if (len <0 ) { +SSLerr (SSL_F_SSL_CTX_USE_CERTIFICATE_FILE ,ERR_R_ASN1_LIB ); + 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 (CertCreateCertificateContext ==NULL ) { + CertCreateCertificateContext =GetProcAddress (crypt32dll, +"CertCreateCertificateContext" ); +if (CertCreateCertificateContext ==NULL ) { +CRYPTOAPIerr (CRYPTOAPI_F_GET_PROC_ADDRESS ); + goto err; + } + } +#endif + +/* Create a certificate context based on the above certificate */ + pCertContext =CertCreateCertificateContext (X509_ASN_ENCODING |PKCS_7_ASN_ENCODING , + buf, len); +if (pCertContext ==NULL ) { +CRYPTOAPIerr (CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT ); + goto err; + } + +/* Create an empty issuer list */ + EnhkeyUsage.cUsageIdentifier =0 ; + EnhkeyUsage.rgpszUsageIdentifier =NULL ; + CertUsage.dwType =USAGE_MATCH_TYPE_AND ; + CertUsage.Usage = EnhkeyUsage; + +/* Searching and matching criteria to be used when building the chain */ + ChainPara.cbSize =sizeof (CERT_CHAIN_PARA ); + ChainPara.RequestedUsage = CertUsage; + +/* Get the certificate chain of our certificate */ +if (!CertGetCertificateChain (NULL , pCertContext,NULL ,NULL , &ChainPara, +0 ,NULL , &pChainContext)) { +CRYPTOAPIerr (CRYPTOAPI_F_CERT_GET_CERT_CHAIN ); + goto err; + } + +/* return 1 when the certificate is trusted, 0 when it's not; -1 on error */ + ret = (pChainContext->TrustStatus.dwErrorStatus ==CERT_TRUST_NO_ERROR ); + + err: +if (buf) +OPENSSL_free (buf); +if (pChainContext) +CertFreeCertificateChain (pChainContext); +if (pCertContext) +CertFreeCertificateContext (pCertContext); + +return ret; +}
cryptoapi.h +1 -0
@@ -2,6 +2,7 @@#define _CRYPTOAPI_H_int SSL_CTX_use_CryptoAPI_certificate (SSL_CTX *ssl_ctx,const char *cert_prop); +int CryptoAPI_verify_certificate (X509 *x509);#endif /* !_CRYPTOAPI_H_ */
init.c +4 -0
@@ -1558,6 +1558,10 @@ do_init_crypto_tls (struct context *c, c to.remote_cert_eku = options->remote_cert_eku; to.es = c->c2.es; +#ifdef WIN32 + to.cryptoapi_ca = options->cryptoapi_ca; +#endif +#ifdef ENABLE_DEBUG to.gremlin = c->options.gremlin;#endif
openvpn.8 +20 -0
@@ -124,6 +124,7 @@ openvpn \- secure IP tunnel daemon. [\ \fB\-\-connect\-retry\-max\fR\ \fIn\fR\ ] [\ \fB\-\-crl\-verify\fR\ \fIcrl\fR\ ] [\ \fB\-\-cryptoapicert\fR\ \fIselect\-string\fR\ ] +[\ \fB\-\-cryptoapica\fR\ ] [\ \fB\-\-daemon\fR\ \fI[progname]\fR\ ] [\ \fB\-\-dev\-node\fR\ \fInode\fR\ ] [\ \fB\-\-dev\-type\fR\ \fIdevice\-type\fR\ ]
@@ -3769,7 +3770,26 @@ To select a certificate, based on certif The thumbprint hex string can easily be copy-and-pasted from the Windows Certificate Store GUI. +.\"********************************************************* +.TP +.B --cryptoapica +Use the Windows Certficate System Store to verify presented certificates +(Windows Only). + +Use this option instead of or in addition to +.B --ca. + +This makes it possible to verify certificates against the Certificate Store +instead of having OpenVPN read the certificate authority (CA) file. +Windows Certificate Store checks Certificate Revocation Lists (CRLs) +automatically and will reject revoked certificates. +The Certificate Store includes by default many known Certificate Authorities. +Use this setting with extreme caution and preferrably in combination with +.B --tls-verify +or +.B --tls-remote +options. .\"********************************************************* .TP .B --key-method m
options.c +15 -2
@@ -450,6 +450,8 @@ static const char usage_message[] =#ifdef WIN32 "--cryptoapicert select-string : Load the certificate and private key from the\n" " Windows Certificate System Store.\n" +"--cryptoapica : Check against Certificate Authorities stored in Windows\n" + " Certificate System Store.\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"
@@ -1236,6 +1238,7 @@ show_settings (const struct options *o)SHOW_STR (pkcs12_file);#ifdef WIN32 SHOW_STR (cryptoapi_cert); +SHOW_BOOL (cryptoapi_ca);#endif SHOW_STR (cipher_list);SHOW_STR (tls_verify);
@@ -1809,8 +1812,8 @@ options_postprocess (struct options *opt#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_ca))) +msg (M_USAGE ,"You must define CA file (--ca) or CA path (--capath) or CryptoAPI CA (--cryptoapica)" );if (options->cert_file)msg (M_USAGE ,"Parameter --cert cannot be used when --cryptoapicert is also specified." );if (options->priv_key_file)
@@ -1831,8 +1834,13 @@ options_postprocess (struct options *opt }else { +#ifdef WIN32 +if ((!(options->ca_file)) && (!(options->ca_path)) && (!(options->cryptoapi_ca))) +msg (M_USAGE ,"You must define CA file (--ca) or CA path (--capath) or CryptoAPI CAs (--cryptoapica)" ); +#else if ((!(options->ca_file)) && (!(options->ca_path)))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 );
@@ -4889,6 +4897,11 @@ add_option (struct options *options,VERIFY_PERMISSION (OPT_P_GENERAL ); options->cryptoapi_cert = p[1]; } +else if (streq (p[0 ],"cryptoapica" )) + { +VERIFY_PERMISSION (OPT_P_GENERAL ); + options->cryptoapi_ca = true; + }#endif else if (streq (p[0], "key") && p[1]) {
options.h +1 -0
@@ -424,6 +424,7 @@ struct options#ifdef WIN32 const char *cryptoapi_cert; +bool cryptoapi_ca;#endif /* data channel key exchange method */
proxy.c +1 -1
@@ -39,7 +39,7 @@#include "base64.h" #include "ntlm.h" -#ifdef WIN32 +#if defined(WIN32 ) && !defined(__MINGW32__)#include "ieproxy.h" #endif
ssl.c +11 -5
@@ -543,13 +543,19 @@ verify_callback (int preverify_ok, X509_msg (D_LOW ,"X509: %s" , subject);#endif -/* did peer present cert which was signed our root cert? */ +/* did peer present cert which was signed by our root cert? */ if (!preverify_ok) { -/* Remote site specified a certificate, but it's not correct */ -msg (D_TLS_ERRORS ,"VERIFY ERROR: depth=%d, error=%s: %s" , - ctx->error_depth,X509_verify_cert_error_string (ctx->error), subject); - goto err;/* Reject connection */ +#ifdef WIN32 +/* if cryptoapica was not enabled, fail; otherwise check against the CA Store */ + if (!(opt->cryptoapi_ca && CryptoAPI_verify_certificate(ctx->current_cert))) +#endif + { +/* Remote site specified a certificate, but it's not correct */ +msg (D_TLS_ERRORS ,"VERIFY ERROR: depth=%d, error=%s: %s" , + ctx->error_depth,X509_verify_cert_error_string (ctx->error), subject); + goto err;/* Reject connection */ + } }/* warn if cert chain is too deep */
ssl.h +3 -0
@@ -415,6 +415,9 @@ struct tls_optionsint ns_cert_type;unsigned remote_cert_ku[MAX_PARMS ];const char *remote_cert_eku; +#ifdef WIN32 +bool cryptoapi_ca; +#endif /* allow openvpn config info to be passed over control channel */
Quoted 4 lines · Faidon Liambotis
Hello,
Below you will find a revised version of a patch that I sent almost 9 months before.
Quoted 8 lines
It allows OpenVPN to verify certificates agains the Windows Certificate Store.
Changed since v2:
* Replace the global variable by a TLS options variable
* Added relevant man page entry
* Minor bugfixes (more #ifdef WIN32 to guard the variable declaration, spelling error etc.)
* diff against 2.1-rc4 instead of rc1
Why not use SSL_CTX_add_client_CA and add all CAPI root store into OpenSSL context?
Alon.
Quoted 2 lines · Alon Bar-Lev
Why not use
SSL_CTX_add_client_CAand add all CAPI root store into OpenSSL context?
My initial approach (v1) was that (albeit not with SSL_CTX_add_client_CA but with X509_STORE_add_cert).
This was flawed, for the reasons I mentioned in v2 changelog (which btw, you have seen and commented on :-):
- CRLs are not handled that way.
- It is hard, if not impossible, to build proper chains from the imported CAs. e.g. you can have an intermediate CA in Windows Certificate Store that is *not* trusted by Windows because the root CA doesn't exist in the respective store.
- It felt like a gross hack.
It seems that is much better to ask Windows to check whether the presented certificate is trusted or not than try to build the trust path yourself and import it to OpenSSL.
Regards,
Faidon
Quoted 3 lines · Faidon Liambotis
It seems that is much better to ask Windows to check whether the presented certificate is trusted or not than try to build the trust path yourself and import it to OpenSSL.
OK...
So you need to use CertVerifyCertificateChainPolicy() with CERT_CHAIN_POLICY_SSL
Alon.
Quoted 1 line · Alon Bar-Lev
So you need to use
CertVerifyCertificateChainPolicy()withCERT_CHAIN_POLICY_SSL
I'm no Microsoft developer (adn I don't want to be to be honest) but if I understand it right, it's better to call CertGetCertificateChain() as I am doing.
MSDN for CertVerifyCertificateChainPolicy()[1] says on the remarks:
"Use the CertGetCertificateChain function to enable and perform certificate revocation checking. The CertVerifyCertificateChainPolicy function does not check if certificates in the certificate chain are revoked."
I could be far off in reading the documentation; my tests however suggest that I got it right.
If you disagree, can you explain in more words your objection? :)
Thanks,
Faidon
Quoted 5 lines · Faidon Liambotis
Quoted 1 line · Alon Bar-Lev
So you need to use
CertVerifyCertificateChainPolicy()withCERT_CHAIN_POLICY_SSLI'm no Microsoft developer (adn I don't want to be to be honest) but if I understand it right, it's better to call
CertGetCertificateChain()as I am doing.
You need to use both, one for create the chain and the other to verify that it meets with system CTL for SSL.
Alon.
PATCH v4 1/1 Use CryptoAPI CA store (was Re: [PATCH v3] Use CryptoAPI CA store)
Quoted 9 lines · Alon Bar-Lev
Quoted 5 lines · Faidon Liambotis
Quoted 1 line · Alon Bar-Lev
So you need to use
CertVerifyCertificateChainPolicy()withCERT_CHAIN_POLICY_SSLI'm no Microsoft developer (adn I don't want to be to be honest) but if I understand it right, it's better to call
CertGetCertificateChain()as I am doing.You need to use both, one for create the chain and the other to verify that it meets with system CTL for SSL.
Seems that you are right. Below you will find -v4 of the patch that does that.
Also, my previous version didn't actually check for revocations, contrary to what I documented.
I added CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT to the dwFlags of CertGetCertificateChain.
Let me know what you think.
Thanks,
Faidon
--
cryptoapi.c +106 -1
@@ -45,9 +45,15 @@#define CERT_STORE_READONLY_FLAG 0x00008000#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 +#define CERT_CHAIN_REVOCATION_CHECK_END_CERT 0x10000000 +#define CERT_CHAIN_REVOCATION_CHECK_CHAIN 0x20000000 +#define CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x40000000 +#define CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY 0x80000000static 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 (*CertCreateCertificateContext) (DWORD dwCertEncodingType, +const BYTE *pbCertEncoded,DWORD cbCertEncoded) =NULL ;#endif /* Size of an SSL signature: MD5+SHA1 */
@@ -65,6 +71,9 @@ static BOOL WINAPI (*CryptAcquireCertifi#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106#define CRYPTOAPI_F_LOAD_LIBRARY 107#define CRYPTOAPI_F_GET_PROC_ADDRESS 108 +#define CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT 109 +#define CRYPTOAPI_F_CERT_GET_CERT_CHAIN 110 +#define CRYPTOAPI_F_CERT_VERIFY_CERT_CHAIN_POLICY 111static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { {ERR_PACK (ERR_LIB_CRYPTOAPI ,0 ,0 ),"microsoft cryptoapi" },
@@ -77,6 +86,9 @@ static ERR_STRING_DATA CRYPTOAPI_str_fun { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" }, { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" }, { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" }, + { ERR_PACK(0, CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT, 0), "CertCreateCertificateContext" }, + { ERR_PACK(0, CRYPTOAPI_F_CERT_GET_CERT_CHAIN, 0), "CertGetCertificateChain" }, + { ERR_PACK(0, CRYPTOAPI_F_CERT_VERIFY_CERT_CHAIN_POLICY, 0), "CertVerifyCertificateChainPolicy" }, { 0, NULL } };
@@ -364,7 +376,7 @@ int SSL_CTX_use_CryptoAPI_certificate(SS }/* 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 +473,96 @@ int SSL_CTX_use_CryptoAPI_certificate(SS }return 0 ; } + +int CryptoAPI_verify_certificate (X509 *x509) +{ +int ret =-1 ; +int len; +unsigned char *buf =NULL ; + +PCCERT_CONTEXT pCertContext =NULL ; +PCCERT_CHAIN_CONTEXT pChainContext =NULL ; +CERT_ENHKEY_USAGE EnhkeyUsage; +CERT_USAGE_MATCH CertUsage; +CERT_CHAIN_PARA ChainPara; +CERT_CHAIN_POLICY_PARA PolicyPara; +CERT_CHAIN_POLICY_STATUS PolicyStatus; + +/* Convert from internal X509 format to DER */ + len =i2d_X509 (x509, &buf); +if (len <0 ) { +SSLerr (SSL_F_SSL_CTX_USE_CERTIFICATE_FILE ,ERR_R_ASN1_LIB ); + 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 (CertCreateCertificateContext ==NULL ) { + CertCreateCertificateContext =GetProcAddress (crypt32dll, +"CertCreateCertificateContext" ); +if (CertCreateCertificateContext ==NULL ) { +CRYPTOAPIerr (CRYPTOAPI_F_GET_PROC_ADDRESS ); + goto err; + } + } +#endif + +/* Create a certificate context based on the above certificate */ + pCertContext =CertCreateCertificateContext (X509_ASN_ENCODING |PKCS_7_ASN_ENCODING , + buf, len); +if (pCertContext ==NULL ) { +CRYPTOAPIerr (CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT ); + goto err; + } + +/* Create an empty issuer list */ + EnhkeyUsage.cUsageIdentifier =0 ; + EnhkeyUsage.rgpszUsageIdentifier =NULL ; + CertUsage.dwType =USAGE_MATCH_TYPE_AND ; + CertUsage.Usage = EnhkeyUsage; + +/* Searching and matching criteria to be used when building the chain */ + ChainPara.cbSize =sizeof (CERT_CHAIN_PARA ); + ChainPara.RequestedUsage = CertUsage; + +/* Get the certificate chain of our certificate */ +if (!CertGetCertificateChain (NULL , pCertContext,NULL ,NULL , &ChainPara, +CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT , +NULL , &pChainContext)) { +CRYPTOAPIerr (CRYPTOAPI_F_CERT_GET_CERT_CHAIN ); + goto err; + } + + PolicyPara.cbSize =sizeof (CERT_CHAIN_POLICY_PARA ); + PolicyPara.dwFlags =0 ; + PolicyPara.pvExtraPolicyPara =NULL ; + + PolicyStatus.cbSize =sizeof (CERT_CHAIN_POLICY_STATUS ); + +if (!CertVerifyCertificateChainPolicy (CERT_CHAIN_POLICY_SSL , pChainContext, + &PolicyPara, &PolicyStatus)) { +CRYPTOAPIerr (CRYPTOAPI_F_CERT_VERIFY_CERT_CHAIN_POLICY ); + goto err; + } + +/* return 1 when the certificate is trusted, 0 when it's not */ + ret = !PolicyStatus.dwError; + + err: +if (buf) +OPENSSL_free (buf); +if (pChainContext) +CertFreeCertificateChain (pChainContext); +if (pCertContext) +CertFreeCertificateContext (pCertContext); + +return ret; +}
cryptoapi.h +1 -0
@@ -2,6 +2,7 @@#define _CRYPTOAPI_H_int SSL_CTX_use_CryptoAPI_certificate (SSL_CTX *ssl_ctx,const char *cert_prop); +int CryptoAPI_verify_certificate (X509 *x509);#endif /* !_CRYPTOAPI_H_ */
init.c +4 -0
@@ -1558,6 +1558,10 @@ do_init_crypto_tls (struct context *c, c to.remote_cert_eku = options->remote_cert_eku; to.es = c->c2.es; +#ifdef WIN32 + to.cryptoapi_ca = options->cryptoapi_ca; +#endif +#ifdef ENABLE_DEBUG to.gremlin = c->options.gremlin;#endif
openvpn.8 +20 -0
@@ -124,6 +124,7 @@ openvpn \- secure IP tunnel daemon. [\ \fB\-\-connect\-retry\-max\fR\ \fIn\fR\ ] [\ \fB\-\-crl\-verify\fR\ \fIcrl\fR\ ] [\ \fB\-\-cryptoapicert\fR\ \fIselect\-string\fR\ ] +[\ \fB\-\-cryptoapica\fR\ ] [\ \fB\-\-daemon\fR\ \fI[progname]\fR\ ] [\ \fB\-\-dev\-node\fR\ \fInode\fR\ ] [\ \fB\-\-dev\-type\fR\ \fIdevice\-type\fR\ ]
@@ -3769,7 +3770,26 @@ To select a certificate, based on certif The thumbprint hex string can easily be copy-and-pasted from the Windows Certificate Store GUI. +.\"********************************************************* +.TP +.B --cryptoapica +Use the Windows Certficate System Store to verify presented certificates +(Windows Only). + +Use this option instead of or in addition to +.B --ca. + +This makes it possible to verify certificates against the Certificate Store +instead of having OpenVPN read the certificate authority (CA) file. +Windows Certificate Store checks Certificate Revocation Lists (CRLs) +automatically and will reject revoked certificates. +The Certificate Store includes by default many known Certificate Authorities. +Use this setting with extreme caution and preferrably in combination with +.B --tls-verify +or +.B --tls-remote +options. .\"********************************************************* .TP .B --key-method m
options.c +15 -2
@@ -450,6 +450,8 @@ static const char usage_message[] =#ifdef WIN32 "--cryptoapicert select-string : Load the certificate and private key from the\n" " Windows Certificate System Store.\n" +"--cryptoapica : Check against Certificate Authorities stored in Windows\n" + " Certificate System Store.\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"
@@ -1236,6 +1238,7 @@ show_settings (const struct options *o)SHOW_STR (pkcs12_file);#ifdef WIN32 SHOW_STR (cryptoapi_cert); +SHOW_BOOL (cryptoapi_ca);#endif SHOW_STR (cipher_list);SHOW_STR (tls_verify);
@@ -1809,8 +1812,8 @@ options_postprocess (struct options *opt#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_ca))) +msg (M_USAGE ,"You must define CA file (--ca) or CA path (--capath) or CryptoAPI CA (--cryptoapica)" );if (options->cert_file)msg (M_USAGE ,"Parameter --cert cannot be used when --cryptoapicert is also specified." );if (options->priv_key_file)
@@ -1831,8 +1834,13 @@ options_postprocess (struct options *opt }else { +#ifdef WIN32 +if ((!(options->ca_file)) && (!(options->ca_path)) && (!(options->cryptoapi_ca))) +msg (M_USAGE ,"You must define CA file (--ca) or CA path (--capath) or CryptoAPI CAs (--cryptoapica)" ); +#else if ((!(options->ca_file)) && (!(options->ca_path)))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 );
@@ -4889,6 +4897,11 @@ add_option (struct options *options,VERIFY_PERMISSION (OPT_P_GENERAL ); options->cryptoapi_cert = p[1]; } +else if (streq (p[0 ],"cryptoapica" )) + { +VERIFY_PERMISSION (OPT_P_GENERAL ); + options->cryptoapi_ca = true; + }#endif else if (streq (p[0], "key") && p[1]) {
options.h +1 -0
@@ -424,6 +424,7 @@ struct options#ifdef WIN32 const char *cryptoapi_cert; +bool cryptoapi_ca;#endif /* data channel key exchange method */
proxy.c +1 -1
@@ -39,7 +39,7 @@#include "base64.h" #include "ntlm.h" -#ifdef WIN32 +#if defined(WIN32 ) && !defined(__MINGW32__)#include "ieproxy.h" #endif
ssl.c +11 -5
@@ -543,13 +543,19 @@ verify_callback (int preverify_ok, X509_msg (D_LOW ,"X509: %s" , subject);#endif -/* did peer present cert which was signed our root cert? */ +/* did peer present cert which was signed by our root cert? */ if (!preverify_ok) { -/* Remote site specified a certificate, but it's not correct */ -msg (D_TLS_ERRORS ,"VERIFY ERROR: depth=%d, error=%s: %s" , - ctx->error_depth,X509_verify_cert_error_string (ctx->error), subject); - goto err;/* Reject connection */ +#ifdef WIN32 +/* if cryptoapica was not enabled, fail; otherwise check against the CA Store */ + if (!(opt->cryptoapi_ca && CryptoAPI_verify_certificate(ctx->current_cert))) +#endif + { +/* Remote site specified a certificate, but it's not correct */ +msg (D_TLS_ERRORS ,"VERIFY ERROR: depth=%d, error=%s: %s" , + ctx->error_depth,X509_verify_cert_error_string (ctx->error), subject); + goto err;/* Reject connection */ + } }/* warn if cert chain is too deep */
ssl.h +3 -0
@@ -415,6 +415,9 @@ struct tls_optionsint ns_cert_type;unsigned remote_cert_ku[MAX_PARMS ];const char *remote_cert_eku; +#ifdef WIN32 +bool cryptoapi_ca; +#endif /* allow openvpn config info to be passed over control channel */
Re: [Openvpn-devel] [PATCH v4] Use CryptoAPI CA store (was Re: [PATCH v3] Use CryptoAPI CA store)
Quoted 6 lines · Faidon Liambotis
Quoted 2 lines · Alon Bar-Lev
You need to use both, one for create the chain and the other to verify that it meets with system CTL for SSL.
Seems that you are right. Below you will find -v4 of the patch that does that.
Quoted 6 lines
Also, my previous version didn't actually check for revocations, contrary to what I documented.
I addedCERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOTto the dwFlags of CertGetCertificateChain.Let me know what you think.
Alon, ping?
I'd really like to have this patch merged this time.
If you have any more comments or objections, just say so so I can fix them.
If you don't, just say so, so it can get merged.
Thanks,
Faidon
Re: [Openvpn-devel] [PATCH v4] Use CryptoAPI CA store (was Re: [PATCH v3] Use CryptoAPI CA store)
Quoted 1 line · Faidon Liambotis
Alon, ping?
Oh... missed your post.
Please don't keep change the subject line.
And replay all to every correction.
Quoted 2 lines
I'd really like to have this patch merged this time. If you don't, just say so, so it can get merged.
Only James can merge patches...
I only review this as I can help you improve it.
Quoted 1 line
If you have any more comments or objections, just say so so I can fix them.
Some more notes:
1. For CertGetCertificateChain() you should use the following flags:CERT_CHAIN_REVOCATION_CHECK_CHAIN
So that CA may suicide.
2. CertVerifyCertificateChainPolicy()
PolicyPara extra should be set:
fdwChecks should be SECURITY_FLAG_IGNORE_CERT_CN_INVALID as we check subject name within OpenVPN.
dwAuthType should be AUTHTYPE_CLIENT or AUTHTYPE_SERVER depending on the configuration. I think that peer-to-peer should be AUTHTYPE_SERVER.
3. I am not sure you require:
ChainPara.RequestedUsage = <something>
Have you tried to leave it zero?
4. I guess a cleaner code would load crypt32.dll once in the module, moving the LoadLibrary and GetProcAddress to a single function, calling it from initialization code.
Also if you already mess with this module, I guess a cleanup code should be added to free the library during termination... :)
Best Regards,
Alon Bar-Lev.