diff options
author | Daniel Stenberg <daniel@haxx.se> | 2023-01-29 11:32:33 +0100 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2023-01-31 11:10:42 +0100 |
commit | a3bcfab4b577dde2ddac0f25bbe872037dd81d23 (patch) | |
tree | 1eab0ac6bd941e95db6b15d805be4d727ecb7e99 /lib/vtls | |
parent | 72d4f7d8f7733b9bfb501f34c18d513c238714cd (diff) | |
download | curl-a3bcfab4b577dde2ddac0f25bbe872037dd81d23.tar.gz |
openssl: only use CA_BLOB if verifying peer
Reported-by: Paul Groke
Bug: https://curl.se/mail/lib-2023-01/0070.html
Fixes #10351
Closes #10359
Diffstat (limited to 'lib/vtls')
-rw-r--r-- | lib/vtls/openssl.c | 338 |
1 files changed, 167 insertions, 171 deletions
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index ffd9e85e7..ec90d94a1 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3052,7 +3052,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, BIO_free(cbio); /* if we didn't end up importing anything, treat that as an error */ - return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); + return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; } static CURLcode populate_x509_store(struct Curl_cfilter *cf, @@ -3076,218 +3076,214 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, if(!store) return CURLE_OUT_OF_MEMORY; + if(verifypeer) { #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://datatracker.ietf.org/doc/html/rfc5280 */ - if((conn_config->verifypeer || conn_config->verifyhost) && - (ssl_config->native_ca_store)) { - HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("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; + /* 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://datatracker.ietf.org/doc/html/rfc5280 */ + if(ssl_config->native_ca_store) { + HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("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]; + char cert_name[256]; #endif - pContext = CertEnumCertificatesInStore(hStore, pContext); - if(!pContext) - break; + 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\"", cert_name); + 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\"", cert_name); #endif + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; - 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)) + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) 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 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; + } - 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((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) - continue; + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; } - 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(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((HRESULT)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; + if(!found) + continue; + } } + else + continue; } else continue; - } - else - continue; - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if(!x509) - 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) { + /* 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\"", cert_name); + infof(data, "SSL: Imported cert \"%s\"", cert_name); #endif - imported_native_ca = true; + imported_native_ca = true; + } + X509_free(x509); } - X509_free(x509); - } - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); - if(result) - return result; + if(result) + return result; + } + if(imported_native_ca) + infof(data, "successfully imported Windows CA store"); + else + infof(data, "error importing Windows CA store, continuing anyway"); } - if(imported_native_ca) - infof(data, "successfully imported Windows CA store"); - else - infof(data, "error importing Windows CA store, continuing anyway"); - } #endif - - if(ca_info_blob) { - result = load_cacert_from_memory(store, ca_info_blob); - if(result) { - if(result == CURLE_OUT_OF_MEMORY || - (verifypeer && !imported_native_ca)) { + if(ca_info_blob) { + result = load_cacert_from_memory(store, ca_info_blob); + if(result) { failf(data, "error importing CA certificate blob"); return result; } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); - } - else { - imported_ca_info_blob = true; - infof(data, "successfully imported CA certificate blob"); + else { + imported_ca_info_blob = true; + infof(data, "successfully imported CA certificate blob"); + } } - } - if(verifypeer && (ssl_cafile || ssl_capath)) { + if(ssl_cafile || ssl_capath) { #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) - /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate file, continuing anyway"); } - else - infof(data, "error setting certificate file, continuing anyway"); - } - if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* 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 && !X509_STORE_load_path(store, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate path, continuing anyway"); } - else - infof(data, "error setting certificate path, continuing anyway"); - } #else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - infof(data, "error setting certificate verify locations," - " continuing anyway"); + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ + if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + infof(data, "error setting certificate verify locations," + " continuing anyway"); + } } - } #endif - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } #ifdef CURL_CA_FALLBACK - if(verifypeer && !ssl_cafile && !ssl_capath && - !imported_native_ca && !imported_ca_info_blob) { - /* verifying the peer without any CA certificates won't - work so use openssl's built-in default as fallback */ - X509_STORE_set_default_paths(store); - } + if(!ssl_cafile && !ssl_capath && + !imported_native_ca && !imported_ca_info_blob) { + /* verifying the peer without any CA certificates won't + work so use openssl's built-in default as fallback */ + X509_STORE_set_default_paths(store); + } #endif + } if(ssl_crlfile) { /* tell OpenSSL where to find CRL file that is used to check certificate |