From f80d6341c7ca194176a0f4f4643f46525f34e27c Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 07:02:19 -0400 Subject: [PATCH 01/32] CDRIVER-3228 store and free client cert context --- .../src/mongoc/mongoc-stream-tls-secure-channel-private.h | 1 + .../src/mongoc/mongoc-stream-tls-secure-channel.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h index 45d3cb1c7a6..086379d707c 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h @@ -44,6 +44,7 @@ typedef enum { typedef struct { CredHandle cred_handle; TimeStamp time_stamp; + PCCERT_CONTEXT cert; /* optional client cert. Kept to free later */ } mongoc_secure_channel_cred; typedef struct { diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c index ffa6fedaf00..f68475d24eb 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c @@ -162,6 +162,9 @@ _mongoc_stream_tls_secure_channel_destroy (mongoc_stream_t *stream) /* if the handle was not cached and the refcount is zero */ TRACE ("%s", "clear credential handle"); FreeCredentialsHandle (&secure_channel->cred->cred_handle); + if (secure_channel->cred->cert != NULL) { + CertFreeCertificateContext (secure_channel->cred->cert); + } bson_free (secure_channel->cred); } @@ -932,6 +935,10 @@ mongoc_stream_tls_secure_channel_new (mongoc_stream_t *base_stream, const char * schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT; secure_channel->cred = (mongoc_secure_channel_cred *) bson_malloc0 (sizeof (mongoc_secure_channel_cred)); + if (cert) { + // Store client cert to free later. + secure_channel->cred->cert = cert; + } /* Example: * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375454%28v=vs.85%29.aspx From 8f2f8b4e271a652fc144e4b4e2c6e5726ceaba21 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 07:08:06 -0400 Subject: [PATCH 02/32] free on successful load of client cert Do not return before `fail` label. --- .../src/mongoc/mongoc-secure-channel.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index e94dc74e5fc..4ad769d41f1 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -47,6 +47,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) { char *pem; FILE *file; + bool ret = false; bool success; HCRYPTKEY hKey; long pem_length; @@ -211,12 +212,13 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) CERT_KEY_PROV_HANDLE_PROP_ID, /* dwPropId */ 0, /* dwFlags */ (const void *) provider); /* pvData */ - if (success) { - TRACE ("%s", "Successfully loaded client certificate"); - return cert; + if (!success) { + MONGOC_ERROR ("Can't associate private key with public key: 0x%.8X", (unsigned int) GetLastError ()); + goto fail; } - MONGOC_ERROR ("Can't associate private key with public key: 0x%.8X", (unsigned int) GetLastError ()); + TRACE ("%s", "Successfully loaded client certificate"); + ret = true; fail: SecureZeroMemory (pem, pem_length); @@ -231,7 +233,14 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) bson_free (blob_private); } - return NULL; + if (!ret) { + if (cert) { + CertFreeCertificateContext (cert); + } + return NULL; + } + + return cert; } PCCERT_CONTEXT From 099028d6715c9d4096df2e70d78806c014fa5ecf Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 08:09:30 -0400 Subject: [PATCH 03/32] free `hKey` --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 4ad769d41f1..5879c8fd3c7 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -49,7 +49,6 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) FILE *file; bool ret = false; bool success; - HCRYPTKEY hKey; long pem_length; HCRYPTPROV provider; CERT_BLOB public_blob; @@ -193,6 +192,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) goto fail; } + HCRYPTKEY hKey; /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa380207%28v=vs.85%29.aspx */ success = CryptImportKey (provider, /* hProv */ @@ -205,6 +205,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) MONGOC_ERROR ("CryptImportKey for private key failed with error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } + CryptDestroyKey (hKey); /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa376573%28v=vs.85%29.aspx */ From 2b04fcf2acafa97ca2f07ff2c660552680b80f17 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 08:11:13 -0400 Subject: [PATCH 04/32] free pem file and cert when loading CA file --- .../src/mongoc/mongoc-secure-channel.c | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 5879c8fd3c7..d93a3248da5 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -254,8 +254,10 @@ mongoc_secure_channel_setup_certificate (mongoc_stream_tls_secure_channel_t *sec bool mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) { + bool ok = false; FILE *file; long length; + char *pem = NULL; const char *pem_key; HCERTSTORE cert_store = NULL; PCCERT_CONTEXT cert = NULL; @@ -278,38 +280,38 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann } // Read the whole file into one nul-terminated string - pem_key = (const char *) bson_malloc0 ((size_t) length + 1u); - bool read_ok = (size_t) length == fread ((void *) pem_key, 1, length, file); + pem = (char *) bson_malloc0 ((size_t) length + 1u); + bool read_ok = (size_t) length == fread ((void *) pem, 1, length, file); fclose (file); if (!read_ok) { MONGOC_WARNING ("Couldn't read certificate file '%s'", opt->ca_file); - return false; + goto fail; } /* If we have private keys or other fuzz, seek to the good stuff */ - pem_key = strstr (pem_key, "-----BEGIN CERTIFICATE-----"); + pem_key = strstr (pem, "-----BEGIN CERTIFICATE-----"); /*printf ("%s\n", pem_key);*/ if (!pem_key) { MONGOC_WARNING ("Couldn't find certificate in '%s'", opt->ca_file); - return false; + goto fail; } if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, NULL, &encrypted_cert_len, NULL, NULL)) { MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); - return false; + goto fail; } encrypted_cert = (LPBYTE) LocalAlloc (0, encrypted_cert_len); if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, encrypted_cert, &encrypted_cert_len, NULL, NULL)) { MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); - return false; + goto fail; } cert = CertCreateCertificateContext (X509_ASN_ENCODING, encrypted_cert, encrypted_cert_len); if (!cert) { MONGOC_WARNING ("Could not convert certificate"); - return false; + goto fail; } @@ -321,17 +323,24 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann if (cert_store == NULL) { MONGOC_ERROR ("Error opening certificate store"); - return false; + goto fail; } - if (CertAddCertificateContextToStore (cert_store, cert, CERT_STORE_ADD_USE_EXISTING, NULL)) { - TRACE ("%s", "Added the certificate !"); + if (!CertAddCertificateContextToStore (cert_store, cert, CERT_STORE_ADD_USE_EXISTING, NULL)) { + MONGOC_WARNING ("Failed adding the cert"); CertCloseStore (cert_store, 0); - return true; + goto fail; } - MONGOC_WARNING ("Failed adding the cert"); - CertCloseStore (cert_store, 0); + TRACE ("%s", "Added the certificate !"); + CertCloseStore (cert_store, 0); + ok = true; +fail: + LocalFree (encrypted_cert); + if (cert) { + CertFreeCertificateContext (cert); + } + bson_free (pem); return false; } From 5dc1e0fda43a494f565e721c1a7e111d1e15dc07 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 12:27:08 +0000 Subject: [PATCH 05/32] release provider context on error --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index d93a3248da5..cff280376cd 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -203,12 +203,14 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) &hKey); /* phKey, OUT */ if (!success) { MONGOC_ERROR ("CryptImportKey for private key failed with error 0x%.8X", (unsigned int) GetLastError ()); + CryptReleaseContext (provider, 0); goto fail; } CryptDestroyKey (hKey); /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa376573%28v=vs.85%29.aspx */ + // CertSetCertificateContextProperty takes ownership of `provider`. success = CertSetCertificateContextProperty (cert, /* pCertContext */ CERT_KEY_PROV_HANDLE_PROP_ID, /* dwPropId */ 0, /* dwFlags */ From 93ca5281f6d4337577456dd215237ad97d32b89c Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 12:32:19 +0000 Subject: [PATCH 06/32] NUL terminate pem file contents To ensure `strstr` does not read past memory on failure to find. This is consistent with reading in `mongoc_secure_channel_setup_ca` --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index cff280376cd..3bdbab4e8c8 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -70,12 +70,13 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) fseek (file, 0, SEEK_END); pem_length = ftell (file); fseek (file, 0, SEEK_SET); - if (pem_length < 1) { + if (pem_length < 1 || length > LONG_MAX - 1) { MONGOC_ERROR ("Couldn't determine file size of '%s'", filename); return NULL; } - pem = (char *) bson_malloc0 (pem_length); + // Read the whole file into one nul-terminated string + pem = (char *) bson_malloc0 ((size_t) pem_length + 1u); fread ((void *) pem, 1, pem_length, file); fclose (file); From 38a91290a732648267b4a99553c005063827aeec Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 12:57:09 +0000 Subject: [PATCH 07/32] remove commented out printf --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 3bdbab4e8c8..2acd2f79135 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -293,7 +293,6 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann /* If we have private keys or other fuzz, seek to the good stuff */ pem_key = strstr (pem, "-----BEGIN CERTIFICATE-----"); - /*printf ("%s\n", pem_key);*/ if (!pem_key) { MONGOC_WARNING ("Couldn't find certificate in '%s'", opt->ca_file); From 61e39c564086e165e5547fd979190aa729f44d0e Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 9 May 2025 13:36:51 +0000 Subject: [PATCH 08/32] refactor: add `read_file_and_null_terminate` helper --- .../src/mongoc/mongoc-secure-channel.c | 127 ++++++++++++------ 1 file changed, 84 insertions(+), 43 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 2acd2f79135..490578797e9 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -41,15 +41,92 @@ #define SECBUFFER_ALERT 17 #endif +// `read_file_and_null_terminate` reads a file into a NUL-terminated string. +// On success: returns a NUL-terminated string and (optionally) sets `*out_len` excluding NUL. +// On error: returns NULL. +static char * +read_file_and_null_terminate (const char *filename, size_t *out_len) +{ + BSON_ASSERT_PARAM (filename); + BSON_OPTIONAL_PARAM (out_len); + + bool ok = false; + char *contents = NULL; + char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; + + FILE *file = fopen (filename, "rb"); + if (!file) { + MONGOC_ERROR ("Failed to open file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + goto fail; + } + + if (0 != fseek (file, 0, SEEK_END)) { + MONGOC_ERROR ("Failed to seek in file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + goto fail; + } + + long file_len = ftell (file); + if (file_len < 0) { + MONGOC_ERROR ("Failed to get length of file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + goto fail; + } + + if (file_len > LONG_MAX - 1) { + MONGOC_ERROR ("Failed to get length of file: '%s'. File too large", filename); + goto fail; + } + + if (0 != fseek (file, 0, SEEK_SET)) { + MONGOC_ERROR ("Failed to seek in file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + goto fail; + } + + // Read the whole file into one nul-terminated string: + contents = (char *) bson_malloc ((size_t) file_len + 1u); + contents[file_len] = '\0'; + if ((size_t) file_len != fread (contents, 1, file_len, file)) { + if (feof (file)) { + MONGOC_ERROR ("Unexpected EOF reading file: '%s'", filename); + goto fail; + } else { + MONGOC_ERROR ("Failed to read file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + goto fail; + } + } + if (out_len) { + *out_len = (size_t) file_len; + } + + ok = true; +fail: + if (file) { + fclose (file); // Ignore error. + } + if (!ok) { + bson_free (contents); + contents = NULL; + } + return contents; +} + PCCERT_CONTEXT mongoc_secure_channel_setup_certificate_from_file (const char *filename) { char *pem; - FILE *file; bool ret = false; bool success; - long pem_length; + size_t pem_length; HCRYPTPROV provider; CERT_BLOB public_blob; const char *pem_public; @@ -60,26 +137,11 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) DWORD encrypted_private_len = 0; LPBYTE encrypted_private = NULL; - - file = fopen (filename, "rb"); - if (!file) { - MONGOC_ERROR ("Couldn't open file '%s'", filename); - return NULL; - } - - fseek (file, 0, SEEK_END); - pem_length = ftell (file); - fseek (file, 0, SEEK_SET); - if (pem_length < 1 || length > LONG_MAX - 1) { - MONGOC_ERROR ("Couldn't determine file size of '%s'", filename); - return NULL; + pem = read_file_and_null_terminate (filename, &pem_length); + if (!pem) { + goto fail; } - // Read the whole file into one nul-terminated string - pem = (char *) bson_malloc0 ((size_t) pem_length + 1u); - fread ((void *) pem, 1, pem_length, file); - fclose (file); - pem_public = strstr (pem, "-----BEGIN CERTIFICATE-----"); pem_private = strstr (pem, "-----BEGIN ENCRYPTED PRIVATE KEY-----"); @@ -258,8 +320,6 @@ bool mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) { bool ok = false; - FILE *file; - long length; char *pem = NULL; const char *pem_key; HCERTSTORE cert_store = NULL; @@ -267,30 +327,11 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann DWORD encrypted_cert_len = 0; LPBYTE encrypted_cert = NULL; - file = fopen (opt->ca_file, "rb"); - if (!file) { - MONGOC_ERROR ("Couldn't open file '%s'", opt->ca_file); + pem = read_file_and_null_terminate (opt->ca_file, NULL); + if (!pem) { return false; } - fseek (file, 0, SEEK_END); - length = ftell (file); - fseek (file, 0, SEEK_SET); - if (length < 1 || length > LONG_MAX - 1) { - MONGOC_WARNING ("Couldn't determine file size of '%s'", opt->ca_file); - fclose (file); - return false; - } - - // Read the whole file into one nul-terminated string - pem = (char *) bson_malloc0 ((size_t) length + 1u); - bool read_ok = (size_t) length == fread ((void *) pem, 1, length, file); - fclose (file); - if (!read_ok) { - MONGOC_WARNING ("Couldn't read certificate file '%s'", opt->ca_file); - goto fail; - } - /* If we have private keys or other fuzz, seek to the good stuff */ pem_key = strstr (pem, "-----BEGIN CERTIFICATE-----"); From 2130afd39fe3a92c447d9f55d8466ed3376d2de5 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 12 May 2025 15:47:12 +0000 Subject: [PATCH 09/32] rename `encrypted_*` to `encoded_*` Encrypted keys are not supported. "encoded" is consistent with naming in WinCrypt API. --- .../src/mongoc/mongoc-secure-channel.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 490578797e9..4f800648d28 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -134,8 +134,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) LPBYTE blob_private = NULL; PCCERT_CONTEXT cert = NULL; DWORD blob_private_len = 0; - DWORD encrypted_private_len = 0; - LPBYTE encrypted_private = NULL; + DWORD encoded_private_len = 0; + LPBYTE encoded_private = NULL; pem = read_file_and_null_terminate (filename, &pem_length); if (!pem) { @@ -189,7 +189,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) 0, /* cchString */ CRYPT_STRING_BASE64HEADER, /* dwFlags */ NULL, /* pbBinary */ - &encrypted_private_len, /* pcBinary, IN/OUT */ + &encoded_private_len, /* pcBinary, IN/OUT */ NULL, /* pdwSkip */ NULL); /* pdwFlags */ if (!success) { @@ -197,9 +197,9 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) goto fail; } - encrypted_private = (LPBYTE) bson_malloc0 (encrypted_private_len); + encoded_private = (LPBYTE) bson_malloc0 (encoded_private_len); success = CryptStringToBinaryA ( - pem_private, 0, CRYPT_STRING_BASE64HEADER, encrypted_private, &encrypted_private_len, NULL, NULL); + pem_private, 0, CRYPT_STRING_BASE64HEADER, encoded_private, &encoded_private_len, NULL, NULL); if (!success) { MONGOC_ERROR ("Failed to convert base64 private key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; @@ -209,8 +209,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) */ success = CryptDecodeObjectEx (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, /* dwCertEncodingType */ PKCS_RSA_PRIVATE_KEY, /* lpszStructType */ - encrypted_private, /* pbEncoded */ - encrypted_private_len, /* cbEncoded */ + encoded_private, /* pbEncoded */ + encoded_private_len, /* cbEncoded */ 0, /* dwFlags */ NULL, /* pDecodePara */ NULL, /* pvStructInfo */ @@ -232,8 +232,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) blob_private = (LPBYTE) bson_malloc0 (blob_private_len); success = CryptDecodeObjectEx (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, - encrypted_private, - encrypted_private_len, + encoded_private, + encoded_private_len, 0, NULL, blob_private, @@ -289,9 +289,9 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) fail: SecureZeroMemory (pem, pem_length); bson_free (pem); - if (encrypted_private) { - SecureZeroMemory (encrypted_private, encrypted_private_len); - bson_free (encrypted_private); + if (encoded_private) { + SecureZeroMemory (encoded_private, encoded_private_len); + bson_free (encoded_private); } if (blob_private) { @@ -324,8 +324,8 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann const char *pem_key; HCERTSTORE cert_store = NULL; PCCERT_CONTEXT cert = NULL; - DWORD encrypted_cert_len = 0; - LPBYTE encrypted_cert = NULL; + DWORD encoded_cert_len = 0; + LPBYTE encoded_cert = NULL; pem = read_file_and_null_terminate (opt->ca_file, NULL); if (!pem) { @@ -340,18 +340,18 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann goto fail; } - if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, NULL, &encrypted_cert_len, NULL, NULL)) { + if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, NULL, &encoded_cert_len, NULL, NULL)) { MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } - encrypted_cert = (LPBYTE) LocalAlloc (0, encrypted_cert_len); - if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, encrypted_cert, &encrypted_cert_len, NULL, NULL)) { + encoded_cert = (LPBYTE) LocalAlloc (0, encoded_cert_len); + if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, encoded_cert, &encoded_cert_len, NULL, NULL)) { MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } - cert = CertCreateCertificateContext (X509_ASN_ENCODING, encrypted_cert, encrypted_cert_len); + cert = CertCreateCertificateContext (X509_ASN_ENCODING, encoded_cert, encoded_cert_len); if (!cert) { MONGOC_WARNING ("Could not convert certificate"); goto fail; @@ -379,7 +379,7 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann CertCloseStore (cert_store, 0); ok = true; fail: - LocalFree (encrypted_cert); + LocalFree (encoded_cert); if (cert) { CertFreeCertificateContext (cert); } From 3fc2180934d8b796386608f728323ace1c267e07 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 12 May 2025 15:58:55 +0000 Subject: [PATCH 10/32] remove unlikely-to-encounter logs --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 4f800648d28..582be48b51a 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -78,14 +78,10 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) } if (file_len > LONG_MAX - 1) { - MONGOC_ERROR ("Failed to get length of file: '%s'. File too large", filename); goto fail; } if (0 != fseek (file, 0, SEEK_SET)) { - MONGOC_ERROR ("Failed to seek in file: '%s' with error: '%s'", - filename, - bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); goto fail; } From 04afd607db30fe9fa586be1f23664fa6663b0f04 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 12 May 2025 17:38:45 +0000 Subject: [PATCH 11/32] check if `pem_public` is NULL Avoids NULL deref if PEM file does not have public cert Add a regression test. --- .../src/mongoc/mongoc-secure-channel.c | 5 +++ src/libmongoc/tests/test-mongoc-x509.c | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 582be48b51a..082ee587d1c 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -139,6 +139,11 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) } pem_public = strstr (pem, "-----BEGIN CERTIFICATE-----"); + if (!pem_public) { + MONGOC_ERROR ("Can't find public certificate in '%s'", filename); + goto fail; + } + pem_private = strstr (pem, "-----BEGIN ENCRYPTED PRIVATE KEY-----"); if (pem_private) { diff --git a/src/libmongoc/tests/test-mongoc-x509.c b/src/libmongoc/tests/test-mongoc-x509.c index d482f8e373c..0c56e44b521 100644 --- a/src/libmongoc/tests/test-mongoc-x509.c +++ b/src/libmongoc/tests/test-mongoc-x509.c @@ -262,6 +262,48 @@ test_x509_auth (void *unused) "" /* message differs between server versions */); mongoc_uri_destroy (uri); } + + // Test auth fails when client certificate does not contain public certificate: + { + // Create URI: + mongoc_uri_t *uri = get_x509_uri (); + { + ASSERT ( + mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCERTIFICATEKEYFILE, CERT_TEST_DIR "/client-private.pem")); + ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA)); + ASSERT (mongoc_uri_set_option_as_bool (uri, MONGOC_URI_SERVERSELECTIONTRYONCE, true)); // Fail quickly. + } + + // Try auth: + bson_error_t error = {0}; + bool ok; + { + capture_logs (true); // Capture logs before connecting. OpenSSL reads PEM file during client construction. + mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL); + ok = try_insert (client, &error); +#if defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT) + ASSERT_CAPTURED_LOG ("tls", MONGOC_LOG_LEVEL_ERROR, "Type is not supported"); +#elif defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) + ASSERT_CAPTURED_LOG ("tls", MONGOC_LOG_LEVEL_ERROR, "Can't find public certificate"); +#elif defined(MONGOC_ENABLE_SSL_OPENSSL) + ASSERT_CAPTURED_LOG ("tls", MONGOC_LOG_LEVEL_ERROR, "Cannot find certificate"); +#endif + mongoc_client_destroy (client); + } + + ASSERT (!ok); +#if defined(MONGOC_ENABLE_SSL_OPENSSL) || defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT) + // OpenSSL and Secure Transport fail to create stream (prior to TLS). Resulting in a server selection error. + ASSERT_ERROR_CONTAINS ( + error, MONGOC_ERROR_SERVER_SELECTION, MONGOC_ERROR_SERVER_SELECTION_FAILURE, "connection error"); +#else + ASSERT_ERROR_CONTAINS (error, + MONGOC_ERROR_CLIENT, + MONGOC_ERROR_CLIENT_AUTHENTICATE, + "" /* message differs between server versions */); +#endif + mongoc_uri_destroy (uri); + } drop_x509_user (false); } #endif // MONGOC_ENABLE_SSL From e9cf761b99eb047ca6a313b54bdd1a0318432a9d Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 12 May 2025 18:55:36 +0000 Subject: [PATCH 12/32] revise comment about ownership --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 082ee587d1c..aaa66b248cd 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -274,7 +274,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa376573%28v=vs.85%29.aspx */ - // CertSetCertificateContextProperty takes ownership of `provider`. + // The CERT_KEY_PROV_HANDLE_PROP_ID property takes ownership of `provider`. success = CertSetCertificateContextProperty (cert, /* pCertContext */ CERT_KEY_PROV_HANDLE_PROP_ID, /* dwPropId */ 0, /* dwFlags */ From 58c2273103d691746fa468bd82839fe1066d38c9 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 12 May 2025 19:05:45 +0000 Subject: [PATCH 13/32] refactor: add base64 decode helper --- .../src/mongoc/mongoc-secure-channel.c | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index aaa66b248cd..822b9665b01 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -41,6 +41,26 @@ #define SECBUFFER_ALERT 17 #endif +// `decode_pem_base64` decodes a base-64 PEM blob with headers. +// Returns NULL on error. Use GetLastError() to retrieve Windows error code. +static LPBYTE +decode_pem_base64 (const char *base64_in, DWORD *out_len) +{ + // Get needed output length: + if (!CryptStringToBinaryA (base64_in, 0, CRYPT_STRING_BASE64HEADER, NULL, out_len, NULL, NULL)) { + return NULL; + } + + BSON_ASSERT (*out_len > 0); + LPBYTE out = (LPBYTE) bson_malloc (*out_len); + + if (!CryptStringToBinaryA (base64_in, 0, CRYPT_STRING_BASE64HEADER, out, out_len, NULL, NULL)) { + bson_free (out); + return NULL; + } + return out; +} + // `read_file_and_null_terminate` reads a file into a NUL-terminated string. // On success: returns a NUL-terminated string and (optionally) sets `*out_len` excluding NUL. // On error: returns NULL. @@ -186,22 +206,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa380285%28v=vs.85%29.aspx */ - success = CryptStringToBinaryA (pem_private, /* pszString */ - 0, /* cchString */ - CRYPT_STRING_BASE64HEADER, /* dwFlags */ - NULL, /* pbBinary */ - &encoded_private_len, /* pcBinary, IN/OUT */ - NULL, /* pdwSkip */ - NULL); /* pdwFlags */ - if (!success) { - MONGOC_ERROR ("Failed to convert base64 private key. Error 0x%.8X", (unsigned int) GetLastError ()); - goto fail; - } - - encoded_private = (LPBYTE) bson_malloc0 (encoded_private_len); - success = CryptStringToBinaryA ( - pem_private, 0, CRYPT_STRING_BASE64HEADER, encoded_private, &encoded_private_len, NULL, NULL); - if (!success) { + encoded_private = decode_pem_base64 (pem_private, &encoded_private_len); + if (!encoded_private) { MONGOC_ERROR ("Failed to convert base64 private key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } @@ -341,13 +347,8 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann goto fail; } - if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, NULL, &encoded_cert_len, NULL, NULL)) { - MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); - goto fail; - } - - encoded_cert = (LPBYTE) LocalAlloc (0, encoded_cert_len); - if (!CryptStringToBinaryA (pem_key, 0, CRYPT_STRING_BASE64HEADER, encoded_cert, &encoded_cert_len, NULL, NULL)) { + encoded_cert = decode_pem_base64 (pem_key, &encoded_cert_len); + if (!encoded_cert) { MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } @@ -380,7 +381,7 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann CertCloseStore (cert_store, 0); ok = true; fail: - LocalFree (encoded_cert); + bson_free (encoded_cert); if (cert) { CertFreeCertificateContext (cert); } From 5e44853ef71eda374829d7058271ca5864faf793 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Mon, 12 May 2025 16:37:03 +0000 Subject: [PATCH 14/32] remove call to `CryptQueryObject` for public cert `CryptQueryObject` is deprecated. The flag `CERT_QUERY_CONTENT_FLAG_ALL` is likely incorrect (only certificate is expected) --- .../src/mongoc/mongoc-secure-channel.c | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 822b9665b01..926f5d26442 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -144,7 +144,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) bool success; size_t pem_length; HCRYPTPROV provider; - CERT_BLOB public_blob; + DWORD encoded_cert_len; + LPBYTE encoded_cert = NULL; const char *pem_public; const char *pem_private; LPBYTE blob_private = NULL; @@ -181,23 +182,12 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) goto fail; } - public_blob.cbData = (DWORD) strlen (pem_public); - public_blob.pbData = (BYTE *) pem_public; - - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa380264%28v=vs.85%29.aspx - */ - CryptQueryObject (CERT_QUERY_OBJECT_BLOB, /* dwObjectType, blob or file */ - &public_blob, /* pvObject, Unicode filename */ - CERT_QUERY_CONTENT_FLAG_ALL, /* dwExpectedContentTypeFlags */ - CERT_QUERY_FORMAT_FLAG_ALL, /* dwExpectedFormatTypeFlags */ - 0, /* dwFlags, reserved for "future use" */ - NULL, /* pdwMsgAndCertEncodingType, OUT, unused */ - NULL, /* pdwContentType (dwExpectedContentTypeFlags), OUT, unused */ - NULL, /* pdwFormatType (dwExpectedFormatTypeFlags,), OUT, unused */ - NULL, /* phCertStore, OUT, HCERTSTORE.., unused, for now */ - NULL, /* phMsg, OUT, HCRYPTMSG, only for PKC7, unused */ - (const void **) &cert /* ppvContext, OUT, the Certificate Context */ - ); + encoded_cert = decode_pem_base64 (pem_public, &encoded_cert_len); + if (!encoded_cert) { + MONGOC_ERROR ("Failed to convert base64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); + goto fail; + } + cert = CertCreateCertificateContext (X509_ASN_ENCODING, encoded_cert, encoded_cert_len); if (!cert) { MONGOC_ERROR ("Failed to extract public key from '%s'. Error 0x%.8X", filename, (unsigned int) GetLastError ()); @@ -296,6 +286,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) fail: SecureZeroMemory (pem, pem_length); bson_free (pem); + bson_free (encoded_cert); if (encoded_private) { SecureZeroMemory (encoded_private, encoded_private_len); bson_free (encoded_private); From 30583ef9d849899396675d4403017b12d63c9c61 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Tue, 13 May 2025 14:31:15 +0000 Subject: [PATCH 15/32] return `ok` value when adding CA Value is not currently checked by caller. Return true on success to match existing patterns. --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 926f5d26442..cd6b2fbc7c3 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -377,7 +377,7 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann CertFreeCertificateContext (cert); } bson_free (pem); - return false; + return ok; } bool From 6899d968416e006f2868c049d040818d1b50606d Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Tue, 13 May 2025 15:14:29 +0000 Subject: [PATCH 16/32] remove call to `CryptQueryObject` for CRL Return was wrongly stored in a `CERT_CONTEXT` (needed `CRL_CONTEXT`). Use `CertCreateCRLContext` for consistency with other PEM-reading functions. --- .../src/mongoc/mongoc-secure-channel.c | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index cd6b2fbc7c3..4135937f667 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -384,38 +384,33 @@ bool mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) { HCERTSTORE cert_store = NULL; - PCCERT_CONTEXT cert = NULL; - LPWSTR str; - int chars; + PCCRL_CONTEXT crl = NULL; + bool ok = false; + DWORD encoded_crl_len = 0; + LPBYTE encoded_crl = NULL; - chars = MultiByteToWideChar (CP_ACP, 0, opt->crl_file, -1, NULL, 0); - if (chars < 1) { - MONGOC_WARNING ("Can't determine opt->crl_file length"); + char *pem = read_file_and_null_terminate (opt->crl_file, NULL); + if (!pem) { return false; } - str = (LPWSTR) bson_malloc0 (chars * sizeof (*str)); - MultiByteToWideChar (CP_ACP, 0, opt->crl_file, -1, str, chars); + const char *pem_begin = strstr (pem, "-----BEGIN X509 CRL-----"); + if (!pem_begin) { + MONGOC_WARNING ("Couldn't find CRL in '%s'", opt->crl_file); + goto fail; + } - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa380264%28v=vs.85%29.aspx - */ - CryptQueryObject (CERT_QUERY_OBJECT_FILE, /* dwObjectType, blob or file */ - str, /* pvObject, Unicode filename */ - CERT_QUERY_CONTENT_FLAG_CRL, /* dwExpectedContentTypeFlags */ - CERT_QUERY_FORMAT_FLAG_ALL, /* dwExpectedFormatTypeFlags */ - 0, /* dwFlags, reserved for "future use" */ - NULL, /* pdwMsgAndCertEncodingType, OUT, unused */ - NULL, /* pdwContentType (dwExpectedContentTypeFlags), OUT, unused */ - NULL, /* pdwFormatType (dwExpectedFormatTypeFlags,), OUT, unused */ - NULL, /* phCertStore, OUT, HCERTSTORE.., unused, for now */ - NULL, /* phMsg, OUT, HCRYPTMSG, only for PKC7, unused */ - (const void **) &cert /* ppvContext, OUT, the Certificate Context */ - ); - bson_free (str); + encoded_crl = decode_pem_base64 (pem_begin, &encoded_crl_len); + if (!encoded_crl) { + MONGOC_ERROR ("Failed to convert BASE64 CRL. Error 0x%.8X", (unsigned int) GetLastError ()); + goto fail; + } - if (!cert) { + crl = CertCreateCRLContext (X509_ASN_ENCODING, encoded_crl, encoded_crl_len); + + if (!crl) { MONGOC_WARNING ("Can't extract CRL from '%s'", opt->crl_file); - return false; + goto fail; } @@ -427,22 +422,27 @@ mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_chan if (cert_store == NULL) { MONGOC_ERROR ("Error opening certificate store"); - CertFreeCertificateContext (cert); - return false; + goto fail; } - if (CertAddCertificateContextToStore (cert_store, cert, CERT_STORE_ADD_USE_EXISTING, NULL)) { - TRACE ("%s", "Added the certificate !"); - CertFreeCertificateContext (cert); - CertCloseStore (cert_store, 0); - return true; + if (!CertAddCRLContextToStore (cert_store, crl, CERT_STORE_ADD_USE_EXISTING, NULL)) { + MONGOC_WARNING ("Failed adding the CRL"); + goto fail; } - MONGOC_WARNING ("Failed adding the cert"); - CertFreeCertificateContext (cert); - CertCloseStore (cert_store, 0); + TRACE ("%s", "Added the CRL!"); + ok = true; - return false; +fail: + if (cert_store) { + CertCloseStore (cert_store, 0); + } + if (crl) { + CertFreeCRLContext (crl); + } + bson_free (encoded_crl); + bson_free (pem); + return ok; } ssize_t From 6495cae2b7e110b8d44d4414e6477c84cc04b3c7 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Tue, 13 May 2025 18:16:01 +0000 Subject: [PATCH 17/32] test loading CRL --- .../mongoc/mongoc-secure-channel-private.h | 4 + .../src/mongoc/mongoc-secure-channel.c | 36 ++++++--- src/libmongoc/tests/test-mongoc-x509.c | 79 +++++++++++++++++++ 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h b/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h index d723664ec6f..c5b9e183788 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h @@ -38,6 +38,10 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann bool mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt); +// mongoc_secure_channel_load_crl is used in tests. +PCCRL_CONTEXT +mongoc_secure_channel_load_crl (const char* crl_file); + ssize_t mongoc_secure_channel_read (mongoc_stream_tls_t *tls, void *data, size_t data_length); diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 4135937f667..0ac1e079850 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -380,23 +380,22 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann return ok; } -bool -mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) +PCCRL_CONTEXT +mongoc_secure_channel_load_crl (const char *crl_file) { - HCERTSTORE cert_store = NULL; PCCRL_CONTEXT crl = NULL; bool ok = false; DWORD encoded_crl_len = 0; LPBYTE encoded_crl = NULL; - char *pem = read_file_and_null_terminate (opt->crl_file, NULL); + char *pem = read_file_and_null_terminate (crl_file, NULL); if (!pem) { - return false; + goto fail; } const char *pem_begin = strstr (pem, "-----BEGIN X509 CRL-----"); if (!pem_begin) { - MONGOC_WARNING ("Couldn't find CRL in '%s'", opt->crl_file); + MONGOC_WARNING ("Couldn't find CRL in '%s'", crl_file); goto fail; } @@ -409,10 +408,31 @@ mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_chan crl = CertCreateCRLContext (X509_ASN_ENCODING, encoded_crl, encoded_crl_len); if (!crl) { - MONGOC_WARNING ("Can't extract CRL from '%s'", opt->crl_file); + MONGOC_WARNING ("Can't extract CRL from '%s'", crl_file); goto fail; } + ok = true; +fail: + bson_free (encoded_crl); + bson_free (pem); + if (!ok) { + CertFreeCRLContext (crl); + crl = NULL; + } + return crl; +} + +bool +mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) +{ + HCERTSTORE cert_store = NULL; + bool ok = false; + + PCCRL_CONTEXT crl = mongoc_secure_channel_load_crl (opt->crl_file); + if (!crl) { + goto fail; + } cert_store = CertOpenStore (CERT_STORE_PROV_SYSTEM, /* provider */ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, /* certificate encoding */ @@ -440,8 +460,6 @@ mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_chan if (crl) { CertFreeCRLContext (crl); } - bson_free (encoded_crl); - bson_free (pem); return ok; } diff --git a/src/libmongoc/tests/test-mongoc-x509.c b/src/libmongoc/tests/test-mongoc-x509.c index 0c56e44b521..af66ea0dc0b 100644 --- a/src/libmongoc/tests/test-mongoc-x509.c +++ b/src/libmongoc/tests/test-mongoc-x509.c @@ -5,6 +5,10 @@ #include #endif +#ifdef MONGOC_ENABLE_SSL_SECURE_CHANNEL +#include +#endif + #include "TestSuite.h" #include "test-libmongoc.h" #include "test-conveniences.h" // tmp_bson @@ -306,6 +310,80 @@ test_x509_auth (void *unused) } drop_x509_user (false); } + +#ifdef MONGOC_ENABLE_SSL_SECURE_CHANNEL +static void +remove_crl_for_secure_channel (const char *crl_path) +{ + // Load CRL from file to query system store. + PCCRL_CONTEXT crl_from_file = mongoc_secure_channel_load_crl (crl_path); + ASSERT (crl_from_file); + + HCERTSTORE cert_store = CertOpenStore (CERT_STORE_PROV_SYSTEM, /* provider */ + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, /* certificate encoding */ + 0, /* unused */ + CERT_SYSTEM_STORE_LOCAL_MACHINE, /* dwFlags */ + L"Root"); /* system store name. "My" or "Root" */ + ASSERT (cert_store); + + PCCRL_CONTEXT crl_from_store = CertFindCRLInStore (cert_store, 0, 0, CRL_FIND_EXISTING, crl_from_file, NULL); + ASSERT (crl_from_store); + + if (!CertDeleteCRLFromStore (crl_from_store)) { + test_error ( + "Failed to delete CRL from store. Delete CRL manually to avoid test errors verifying server certificate."); + } + CertFreeCRLContext (crl_from_file); + CertFreeCRLContext (crl_from_store); + CertCloseStore (cert_store, 0); +} +#endif // MONGOC_ENABLE_SSL_SECURE_CHANNEL + +// test_crl tests connection fails when server certificate is in CRL list. +static void +test_crl (void *unused) +{ + BSON_UNUSED (unused); + +#if defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) + if (!test_framework_getenv_bool ("MONGOC_TEST_SCHANNEL_CRL")) { + printf ("Skipping. Test temporarily adds CRL to Windows certificate store. If removing the CRL fails, this may " + "cause later test failures and require removing the CRL file manually. To run test anyway, set the " + "environment variable MONGOC_TEST_SCHANNEL_CRL=ON\n"); + return; + } +#elif defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT) + printf ("Skipping. Secure Transport does not support crl_file.\n"); + return; +#endif + + // Create URI: + mongoc_uri_t *uri = test_framework_get_uri (); + ASSERT (mongoc_uri_set_option_as_bool (uri, MONGOC_URI_SERVERSELECTIONTRYONCE, true)); // Fail quickly. + + // Create SSL options with CRL file: + mongoc_ssl_opt_t ssl_opts = *test_framework_get_ssl_opts (); + ssl_opts.crl_file = CERT_TEST_DIR "/crl.pem"; + + // Try insert: + bson_error_t error = {0}; + mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL); + mongoc_client_set_ssl_opts (client, &ssl_opts); + capture_logs (true); + bool ok = try_insert (client, &error); +#ifdef MONGOC_ENABLE_SSL_SECURE_CHANNEL + remove_crl_for_secure_channel (ssl_opts.crl_file); + ASSERT_CAPTURED_LOG ("tls", MONGOC_LOG_LEVEL_ERROR, "Mutual Authentication failed"); +#else + ASSERT_NO_CAPTURED_LOGS ("tls"); +#endif + ASSERT (!ok); + ASSERT_ERROR_CONTAINS ( + error, MONGOC_ERROR_SERVER_SELECTION, MONGOC_ERROR_SERVER_SELECTION_FAILURE, "no suitable servers"); + + mongoc_client_destroy (client); + mongoc_uri_destroy (uri); +} #endif // MONGOC_ENABLE_SSL void @@ -313,6 +391,7 @@ test_x509_install (TestSuite *suite) { #ifdef MONGOC_ENABLE_SSL TestSuite_AddFull (suite, "/X509/auth", test_x509_auth, NULL, NULL, test_framework_skip_if_no_auth); + TestSuite_AddFull (suite, "/X509/crl", test_crl, NULL, NULL, test_framework_skip_if_no_server_ssl); #endif #ifdef MONGOC_ENABLE_OCSP_OPENSSL From e81bc5df4fdf98368e7fae3810e405efa457b165 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 13:48:40 +0000 Subject: [PATCH 18/32] use `const` --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 0ac1e079850..32b605e84e1 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -89,7 +89,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) goto fail; } - long file_len = ftell (file); + const long file_len = ftell (file); if (file_len < 0) { MONGOC_ERROR ("Failed to get length of file: '%s' with error: '%s'", filename, From c4ceedc6d17e1c4bb85b85191252e8c714db10e1 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 13:48:52 +0000 Subject: [PATCH 19/32] use `NUL` --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 32b605e84e1..8a9ad4cb548 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -105,7 +105,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) goto fail; } - // Read the whole file into one nul-terminated string: + // Read the whole file into one NUL-terminated string: contents = (char *) bson_malloc ((size_t) file_len + 1u); contents[file_len] = '\0'; if ((size_t) file_len != fread (contents, 1, file_len, file)) { From 18d41684f1981d36796af8070365e55875cf66bf Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 13:53:31 +0000 Subject: [PATCH 20/32] use `ferror` for `fseek` and `fread` To match error expectations in C99. --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 8a9ad4cb548..fb53a456995 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -85,7 +85,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) if (0 != fseek (file, 0, SEEK_END)) { MONGOC_ERROR ("Failed to seek in file: '%s' with error: '%s'", filename, - bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + bson_strerror_r (ferror (file), errmsg_buf, sizeof errmsg_buf)); goto fail; } @@ -115,7 +115,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) } else { MONGOC_ERROR ("Failed to read file: '%s' with error: '%s'", filename, - bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + bson_strerror_r (ferror (file), errmsg_buf, sizeof errmsg_buf)); goto fail; } } From 599638e0becda0e3fabe69c60713a68daf30aa77 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 13:59:16 +0000 Subject: [PATCH 21/32] clear and check for `errno` on failing `fopen` --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index fb53a456995..1638d266537 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -74,11 +74,16 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) char *contents = NULL; char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; + errno = 0; // C99 does not require `fopen` set errno. FILE *file = fopen (filename, "rb"); if (!file) { - MONGOC_ERROR ("Failed to open file: '%s' with error: '%s'", - filename, - bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + if (0 != errno) { + MONGOC_ERROR ("Failed to open file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); + } else { + MONGOC_ERROR ("Failed to open file: '%s'", filename); + } goto fail; } From 1b5c4b9039804c351956a7c26f67db15d03a03fb Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 14:00:23 +0000 Subject: [PATCH 22/32] add void cast To clarify error being returned is return value --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 1638d266537..b7aab37ab99 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -131,7 +131,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) ok = true; fail: if (file) { - fclose (file); // Ignore error. + (void) fclose (file); // Ignore error. } if (!ok) { bson_free (contents); From 0f0d51a5c91495eacc122cc8c77eeb2e2a7e02ae Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 14:13:58 +0000 Subject: [PATCH 23/32] call `SecureZeroMemory` on failed read --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index b7aab37ab99..220e1b83a5f 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -114,6 +114,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) contents = (char *) bson_malloc ((size_t) file_len + 1u); contents[file_len] = '\0'; if ((size_t) file_len != fread (contents, 1, file_len, file)) { + SecureZeroMemory (contents, file_len); if (feof (file)) { MONGOC_ERROR ("Unexpected EOF reading file: '%s'", filename); goto fail; From 958f5e0e2aac286af57108114836138189cee848 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 14:23:40 +0000 Subject: [PATCH 24/32] do not call SecureZeroMemory on NULL --- .../src/mongoc/mongoc-secure-channel.c | 6 ++- src/libmongoc/tests/test-mongoc-x509.c | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 220e1b83a5f..02fc223fd2b 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -290,8 +290,10 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) ret = true; fail: - SecureZeroMemory (pem, pem_length); - bson_free (pem); + if (pem) { + SecureZeroMemory (pem, pem_length); + bson_free (pem); + } bson_free (encoded_cert); if (encoded_private) { SecureZeroMemory (encoded_private, encoded_private_len); diff --git a/src/libmongoc/tests/test-mongoc-x509.c b/src/libmongoc/tests/test-mongoc-x509.c index af66ea0dc0b..b7dd935f87a 100644 --- a/src/libmongoc/tests/test-mongoc-x509.c +++ b/src/libmongoc/tests/test-mongoc-x509.c @@ -308,6 +308,47 @@ test_x509_auth (void *unused) #endif mongoc_uri_destroy (uri); } + + // Test auth fails when client certificate does not exist: + { + // Create URI: + mongoc_uri_t *uri = get_x509_uri (); + { + ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCERTIFICATEKEYFILE, CERT_TEST_DIR "/foobar.pem")); + ASSERT (mongoc_uri_set_option_as_utf8 (uri, MONGOC_URI_TLSCAFILE, CERT_CA)); + ASSERT (mongoc_uri_set_option_as_bool (uri, MONGOC_URI_SERVERSELECTIONTRYONCE, true)); // Fail quickly. + } + + // Try auth: + bson_error_t error = {0}; + bool ok; + { + mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL); + capture_logs (true); + ok = try_insert (client, &error); +#if defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT) + ASSERT_CAPTURED_LOG ("tls", MONGOC_LOG_LEVEL_ERROR, "Cannot find certificate"); +#elif defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) + ASSERT_CAPTURED_LOG ("tls", MONGOC_LOG_LEVEL_ERROR, "Failed to open file"); +#elif defined(MONGOC_ENABLE_SSL_OPENSSL) + ASSERT_NO_CAPTURED_LOGS ("tls"); +#endif + mongoc_client_destroy (client); + } + + ASSERT (!ok); +#if defined(MONGOC_ENABLE_SSL_OPENSSL) || defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT) + // OpenSSL fails to create stream (prior to TLS). Resulting in a server selection error. + ASSERT_ERROR_CONTAINS ( + error, MONGOC_ERROR_SERVER_SELECTION, MONGOC_ERROR_SERVER_SELECTION_FAILURE, "connection error"); +#else + ASSERT_ERROR_CONTAINS (error, + MONGOC_ERROR_CLIENT, + MONGOC_ERROR_CLIENT_AUTHENTICATE, + "" /* message differs between server versions */); +#endif + mongoc_uri_destroy (uri); + } drop_x509_user (false); } From d17f8484ab309dbf405a64c84fdc11329c61940b Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 14:25:13 +0000 Subject: [PATCH 25/32] reword comment --- .../src/mongoc/mongoc-stream-tls-secure-channel-private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h index 086379d707c..730c842aec2 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h @@ -44,7 +44,7 @@ typedef enum { typedef struct { CredHandle cred_handle; TimeStamp time_stamp; - PCCERT_CONTEXT cert; /* optional client cert. Kept to free later */ + PCCERT_CONTEXT cert; /* Owning. Optional client cert. */ } mongoc_secure_channel_cred; typedef struct { From 3d51fdb7f95d8bf6cd232d7c89b5bb089e019ccd Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 14 May 2025 14:42:38 +0000 Subject: [PATCH 26/32] remove unnecessary NULL checks --- .../src/mongoc/mongoc-secure-channel.c | 19 +++++-------------- .../mongoc/mongoc-stream-tls-secure-channel.c | 4 +--- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 02fc223fd2b..791e4e6d34e 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -306,9 +306,7 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) } if (!ret) { - if (cert) { - CertFreeCertificateContext (cert); - } + CertFreeCertificateContext (cert); return NULL; } @@ -372,18 +370,15 @@ mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_chann if (!CertAddCertificateContextToStore (cert_store, cert, CERT_STORE_ADD_USE_EXISTING, NULL)) { MONGOC_WARNING ("Failed adding the cert"); - CertCloseStore (cert_store, 0); goto fail; } TRACE ("%s", "Added the certificate !"); - CertCloseStore (cert_store, 0); ok = true; fail: + CertCloseStore (cert_store, 0); bson_free (encoded_cert); - if (cert) { - CertFreeCertificateContext (cert); - } + CertFreeCertificateContext (cert); bson_free (pem); return ok; } @@ -462,12 +457,8 @@ mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_chan ok = true; fail: - if (cert_store) { - CertCloseStore (cert_store, 0); - } - if (crl) { - CertFreeCRLContext (crl); - } + CertCloseStore (cert_store, 0); + CertFreeCRLContext (crl); return ok; } diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c index f68475d24eb..60f5a78f769 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c @@ -162,9 +162,7 @@ _mongoc_stream_tls_secure_channel_destroy (mongoc_stream_t *stream) /* if the handle was not cached and the refcount is zero */ TRACE ("%s", "clear credential handle"); FreeCredentialsHandle (&secure_channel->cred->cred_handle); - if (secure_channel->cred->cert != NULL) { - CertFreeCertificateContext (secure_channel->cred->cert); - } + CertFreeCertificateContext (secure_channel->cred->cert); bson_free (secure_channel->cred); } From 119b52954892556fa3f8ae393f618993e1194d55 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 15 May 2025 13:45:34 -0400 Subject: [PATCH 27/32] format --- src/libmongoc/src/mongoc/mongoc-secure-channel-private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h b/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h index c5b9e183788..6cc080efc56 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h @@ -40,7 +40,7 @@ mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_chan // mongoc_secure_channel_load_crl is used in tests. PCCRL_CONTEXT -mongoc_secure_channel_load_crl (const char* crl_file); +mongoc_secure_channel_load_crl (const char *crl_file); ssize_t mongoc_secure_channel_read (mongoc_stream_tls_t *tls, void *data, size_t data_length); From cd76a7a30927958cab257864b6153085e25dea65 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 15 May 2025 15:36:11 -0400 Subject: [PATCH 28/32] add `BSON_ASSERT_PARAM` Co-authored-by: Ezra Chung <88335979+eramongodb@users.noreply.github.com> --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 791e4e6d34e..5a86f06503a 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -46,6 +46,9 @@ static LPBYTE decode_pem_base64 (const char *base64_in, DWORD *out_len) { + BSON_ASSERT_PARAM (base64_in); + BSON_ASSERT_PARAM (out_len); + // Get needed output length: if (!CryptStringToBinaryA (base64_in, 0, CRYPT_STRING_BASE64HEADER, NULL, out_len, NULL, NULL)) { return NULL; From 63e0082c549c27ddb3d27d2422be4b1e77793000 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 15 May 2025 15:31:32 -0400 Subject: [PATCH 29/32] Revert "clear and check for `errno` on failing `fopen`" This reverts commit 599638e0becda0e3fabe69c60713a68daf30aa77. --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 5a86f06503a..5df91c86c30 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -77,16 +77,11 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) char *contents = NULL; char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; - errno = 0; // C99 does not require `fopen` set errno. FILE *file = fopen (filename, "rb"); if (!file) { - if (0 != errno) { - MONGOC_ERROR ("Failed to open file: '%s' with error: '%s'", - filename, - bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); - } else { - MONGOC_ERROR ("Failed to open file: '%s'", filename); - } + MONGOC_ERROR ("Failed to open file: '%s' with error: '%s'", + filename, + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); goto fail; } From d8022a95c31ce549eadac534fb37854cd5945896 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 15 May 2025 15:31:41 -0400 Subject: [PATCH 30/32] Revert "use `ferror` for `fseek` and `fread`" This reverts commit 18d41684f1981d36796af8070365e55875cf66bf. --- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 5df91c86c30..27ac19cfac6 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -88,7 +88,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) if (0 != fseek (file, 0, SEEK_END)) { MONGOC_ERROR ("Failed to seek in file: '%s' with error: '%s'", filename, - bson_strerror_r (ferror (file), errmsg_buf, sizeof errmsg_buf)); + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); goto fail; } @@ -119,7 +119,7 @@ read_file_and_null_terminate (const char *filename, size_t *out_len) } else { MONGOC_ERROR ("Failed to read file: '%s' with error: '%s'", filename, - bson_strerror_r (ferror (file), errmsg_buf, sizeof errmsg_buf)); + bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf)); goto fail; } } From 0292e6a5522988792132108139f700d228c15e4e Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 15 May 2025 15:43:59 -0400 Subject: [PATCH 31/32] remove unused params --- src/libmongoc/src/mongoc/mongoc-secure-channel-private.h | 6 +++--- src/libmongoc/src/mongoc/mongoc-secure-channel.c | 6 +++--- src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h b/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h index 6cc080efc56..673e2543da5 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel-private.h @@ -33,10 +33,10 @@ BSON_BEGIN_DECLS bool -mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt); +mongoc_secure_channel_setup_ca (mongoc_ssl_opt_t *opt); bool -mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt); +mongoc_secure_channel_setup_crl (mongoc_ssl_opt_t *opt); // mongoc_secure_channel_load_crl is used in tests. PCCRL_CONTEXT @@ -49,7 +49,7 @@ ssize_t mongoc_secure_channel_write (mongoc_stream_tls_t *tls, const void *data, size_t data_length); PCCERT_CONTEXT -mongoc_secure_channel_setup_certificate (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt); +mongoc_secure_channel_setup_certificate (mongoc_ssl_opt_t *opt); /* it may require 16k + some overhead to hold one decryptable block of data - do diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index 27ac19cfac6..a6615fe67b9 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -312,14 +312,14 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) } PCCERT_CONTEXT -mongoc_secure_channel_setup_certificate (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) +mongoc_secure_channel_setup_certificate (mongoc_ssl_opt_t *opt) { return mongoc_secure_channel_setup_certificate_from_file (opt->pem_file); } bool -mongoc_secure_channel_setup_ca (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) +mongoc_secure_channel_setup_ca (mongoc_ssl_opt_t *opt) { bool ok = false; char *pem = NULL; @@ -425,7 +425,7 @@ mongoc_secure_channel_load_crl (const char *crl_file) } bool -mongoc_secure_channel_setup_crl (mongoc_stream_tls_secure_channel_t *secure_channel, mongoc_ssl_opt_t *opt) +mongoc_secure_channel_setup_crl (mongoc_ssl_opt_t *opt) { HCERTSTORE cert_store = NULL; bool ok = false; diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c index 60f5a78f769..3dd3bc4f045 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c @@ -913,15 +913,15 @@ mongoc_stream_tls_secure_channel_new (mongoc_stream_t *base_stream, const char * } if (opt->ca_file) { - mongoc_secure_channel_setup_ca (secure_channel, opt); + mongoc_secure_channel_setup_ca (opt); } if (opt->crl_file) { - mongoc_secure_channel_setup_crl (secure_channel, opt); + mongoc_secure_channel_setup_crl (opt); } if (opt->pem_file) { - cert = mongoc_secure_channel_setup_certificate (secure_channel, opt); + cert = mongoc_secure_channel_setup_certificate (opt); if (cert) { schannel_cred.cCreds = 1; From d888831ed0dafc73160f8e5408b1b1b36bb7c6d7 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 15 May 2025 16:14:45 -0400 Subject: [PATCH 32/32] fail gracefully on zero length base64 --- .../src/mongoc/mongoc-secure-channel.c | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-secure-channel.c index a6615fe67b9..c656f7b9e21 100644 --- a/src/libmongoc/src/mongoc/mongoc-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-secure-channel.c @@ -42,22 +42,31 @@ #endif // `decode_pem_base64` decodes a base-64 PEM blob with headers. -// Returns NULL on error. Use GetLastError() to retrieve Windows error code. +// Returns NULL on error. static LPBYTE -decode_pem_base64 (const char *base64_in, DWORD *out_len) +decode_pem_base64 (const char *base64_in, DWORD *out_len, const char *descriptor, const char *filename) { BSON_ASSERT_PARAM (base64_in); BSON_ASSERT_PARAM (out_len); + BSON_ASSERT_PARAM (descriptor); + BSON_ASSERT_PARAM (filename); // Get needed output length: if (!CryptStringToBinaryA (base64_in, 0, CRYPT_STRING_BASE64HEADER, NULL, out_len, NULL, NULL)) { + MONGOC_ERROR ( + "Failed to convert base64 %s from '%s'. Error 0x%.8X", descriptor, filename, (unsigned int) GetLastError ()); + return NULL; + } + + if (*out_len == 0) { return NULL; } - BSON_ASSERT (*out_len > 0); LPBYTE out = (LPBYTE) bson_malloc (*out_len); if (!CryptStringToBinaryA (base64_in, 0, CRYPT_STRING_BASE64HEADER, out, out_len, NULL, NULL)) { + MONGOC_ERROR ( + "Failed to convert base64 %s from '%s'. Error 0x%.8X", descriptor, filename, (unsigned int) GetLastError ()); bson_free (out); return NULL; } @@ -186,9 +195,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) goto fail; } - encoded_cert = decode_pem_base64 (pem_public, &encoded_cert_len); + encoded_cert = decode_pem_base64 (pem_public, &encoded_cert_len, "public key", filename); if (!encoded_cert) { - MONGOC_ERROR ("Failed to convert base64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } cert = CertCreateCertificateContext (X509_ASN_ENCODING, encoded_cert, encoded_cert_len); @@ -200,9 +208,8 @@ mongoc_secure_channel_setup_certificate_from_file (const char *filename) /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa380285%28v=vs.85%29.aspx */ - encoded_private = decode_pem_base64 (pem_private, &encoded_private_len); + encoded_private = decode_pem_base64 (pem_private, &encoded_private_len, "private key", filename); if (!encoded_private) { - MONGOC_ERROR ("Failed to convert base64 private key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } @@ -342,9 +349,8 @@ mongoc_secure_channel_setup_ca (mongoc_ssl_opt_t *opt) goto fail; } - encoded_cert = decode_pem_base64 (pem_key, &encoded_cert_len); + encoded_cert = decode_pem_base64 (pem_key, &encoded_cert_len, "public key", opt->ca_file); if (!encoded_cert) { - MONGOC_ERROR ("Failed to convert BASE64 public key. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; } @@ -400,9 +406,8 @@ mongoc_secure_channel_load_crl (const char *crl_file) goto fail; } - encoded_crl = decode_pem_base64 (pem_begin, &encoded_crl_len); + encoded_crl = decode_pem_base64 (pem_begin, &encoded_crl_len, "CRL", crl_file); if (!encoded_crl) { - MONGOC_ERROR ("Failed to convert BASE64 CRL. Error 0x%.8X", (unsigned int) GetLastError ()); goto fail; }