aboutsummaryrefslogtreecommitdiff
path: root/lib/vtls
diff options
context:
space:
mode:
authorGilles Vollant <info@winimage.com>2019-09-13 11:24:00 +0200
committerDaniel Stenberg <daniel@haxx.se>2020-05-08 15:55:04 +0200
commit148534db57dda611cf8516e92e4d6e35fc1e5074 (patch)
tree0eab3d760825600ddcb5e20a028225db92f38eef /lib/vtls
parent76b9e8de7b565891329790c64897377aa6ec4ac0 (diff)
CURLOPT_SSL_OPTIONS: add *_NATIVE_CA to use Windows CA store (with openssl)
Closes #4346
Diffstat (limited to 'lib/vtls')
-rw-r--r--lib/vtls/openssl.c196
1 files changed, 177 insertions, 19 deletions
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 176fa522a..6f6b604c2 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -46,6 +46,11 @@
#include "multiif.h"
#include "strerror.h"
#include "curl_printf.h"
+
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+#endif
+
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/x509v3.h>
@@ -2720,31 +2725,184 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
#endif
+
+#if defined(USE_WIN32_CRYPTO)
+ /* Import certificates from the Windows root certificate store if requested.
+ https://stackoverflow.com/questions/9507184/
+ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
+ https://tools.ietf.org/html/rfc5280 */
+ if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) &&
+ (SSL_SET_OPTION(native_ca_store))) {
+ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+ HCERTSTORE hStore = CertOpenSystemStoreA((HCRYPTPROV_LEGACY)NULL, "ROOT");
+
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and is
+ declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is skipped.
+ 'result' is used to store only hard-fail conditions (such as out of
+ memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char cert_name[256];
+#endif
+
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"\n", cert_name);
+#endif
+
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
+
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+ continue;
+
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
+ }
+ else if(GetLastError())
+ continue;
+
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have EKU
+ * extended properties that specify valid uses for the certificate."
+ * The call below checks both, and behavior varies depending on what is
+ * found. For more details see CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
+ }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
+ good for all uses. If it returns zero, the certificate has no
+ valid uses." */
+ if(GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
+ }
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ continue;
+ }
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
+
+ /* Try to import the certificate. This may fail for legitimate reasons
+ such as duplicate certificate, which is allowed by MS but not
+ OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ infof(data, "SSL: Imported cert \"%s\"\n", cert_name);
+#else
+ do {} while(0);
+#endif
+ }
+ X509_free(x509);
+ }
+
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ if(result)
+ return result;
+
+ infof(data, "successfully set certificate verify locations "
+ "to windows ca store\n");
+ }
+ else {
+ infof(data, "error setting certificate verify locations "
+ "to windows ca store, continuing anyway\n");
+ }
+ }
+ else
+#endif
+
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
/* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
- if(ssl_cafile) {
- if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) {
- if(verifypeer) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate file: %s", ssl_cafile);
- return CURLE_SSL_CACERT_BADFILE;
+ {
+ if(ssl_cafile) {
+ if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) {
+ if(verifypeer) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate file: %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ /* Continue with a warning if no certificate verif is required. */
+ infof(data, "error setting certificate file, continuing anyway\n");
}
- /* Continue with a warning if no certificate verification is required. */
- infof(data, "error setting certificate file, continuing anyway\n");
+ infof(data, " CAfile: %s\n", ssl_cafile);
}
- infof(data, " CAfile: %s\n", ssl_cafile);
- }
- if(ssl_capath) {
- if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
- if(verifypeer) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate path: %s", ssl_capath);
- return CURLE_SSL_CACERT_BADFILE;
+ if(ssl_capath) {
+ if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
+ if(verifypeer) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate path: %s", ssl_capath);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ /* Continue with a warning if no certificate verif is required. */
+ infof(data, "error setting certificate path, continuing anyway\n");
}
- /* Continue with a warning if no certificate verification is required. */
- infof(data, "error setting certificate path, continuing anyway\n");
+ infof(data, " CApath: %s\n", ssl_capath);
}
- infof(data, " CApath: %s\n", ssl_capath);
}
#else
if(ssl_cafile || ssl_capath) {