Skip to content

Commit

Permalink
Revert "openssl: only use CA_BLOB if verifying peer"
Browse files Browse the repository at this point in the history
This reverts commit a3bcfab.
  • Loading branch information
Kyle Daling authored and Kyle Daling committed Jul 3, 2023
1 parent 29f33b3 commit 27675b4
Showing 1 changed file with 171 additions and 167 deletions.
338 changes: 171 additions & 167 deletions lib/vtls/openssl.c
Expand Up @@ -3003,7 +3003,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,
Expand All @@ -3027,214 +3027,218 @@ 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(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((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;
#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;

GetSystemTimeAsFileTime(&now);
if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
continue;
encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
if(!encoded_cert)
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())
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;
}

/* 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;
}

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((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
continue;
}

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;
}
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;
}
X509_free(x509);
imported_native_ca = true;
}
X509_free(x509);
}

free(enhkey_usage);
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
free(enhkey_usage);
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);

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(result)
return result;
}
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(ca_info_blob) {
result = load_cacert_from_memory(store, ca_info_blob);
if(result) {
if(result == CURLE_OUT_OF_MEMORY ||
(verifypeer && !imported_native_ca)) {
failf(data, "error importing CA certificate blob");
return result;
}
else {
imported_ca_info_blob = true;
infof(data, "successfully imported CA certificate blob");
}
/* 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");
}
}

if(ssl_cafile || ssl_capath) {
if(verifypeer && (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;
}
else
infof(data, "error setting certificate file, continuing anyway");
/* 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;
}
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 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;
}
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");
}
#endif
infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
}

#ifdef CURL_CA_FALLBACK
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(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);
}
#endif

if(ssl_crlfile) {
/* tell OpenSSL where to find CRL file that is used to check certificate
Expand Down

0 comments on commit 27675b4

Please sign in to comment.