Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c5cae48
Switch to SCH_CREDENTIALS
Julia-Garland Sep 17, 2025
df6308c
Move winternl.h above schannel.h
Julia-Garland Sep 17, 2025
0767a31
Fix test
Julia-Garland Sep 17, 2025
1a4c08b
Use subauth instead of winternl
Julia-Garland Sep 17, 2025
68b306a
Enable TLS 1.3 for Windows Server 2022 and newer
Julia-Garland Sep 18, 2025
9c62ea9
Remove erroneous &
Julia-Garland Sep 18, 2025
9f05bd3
Revert "Remove erroneous &"
Julia-Garland Sep 18, 2025
05823c9
Modify tls_params allocation
Julia-Garland Sep 18, 2025
d69e630
Only use SCH_CREDENTIALS post-Windows Server 2019
Julia-Garland Sep 18, 2025
7e536be
Merge branch 'master' into cdriver-6045
Julia-Garland Sep 19, 2025
cb71275
Simplify preprocessor statement
Julia-Garland Sep 19, 2025
08e8cb5
More specific version check for 1.3 + introduce MONGOC_HAVE_SCH_CREDE…
Julia-Garland Sep 22, 2025
c8e3d64
Fix scope of HAVE_SCH_CREDENTIALS check
Julia-Garland Sep 22, 2025
f5cbb99
Generic windows version function
Julia-Garland Sep 23, 2025
757880b
Newline at end of file
Julia-Garland Sep 23, 2025
705c1f2
Fix ver_set_condition call
Julia-Garland Sep 23, 2025
dab537d
Debug
Julia-Garland Sep 23, 2025
1dc7f70
Malloc cred->cred
Julia-Garland Sep 23, 2025
e667f6d
Remove & in acquire cred handle
Julia-Garland Sep 23, 2025
c2ee9d5
Refactor setup logic
Julia-Garland Sep 24, 2025
95b5584
Add accidentally deleted lines
Julia-Garland Sep 24, 2025
34e0b61
Fix bug with paCred
Julia-Garland Sep 25, 2025
a4b302d
Cleanup
Julia-Garland Sep 25, 2025
337329c
Remove cmake msgs
Julia-Garland Sep 25, 2025
475f08a
Fix pointer bug with cert
Julia-Garland Sep 25, 2025
1e90324
More accurate version checking using Rtl variant
Julia-Garland Sep 26, 2025
807c5e4
Format
Julia-Garland Sep 26, 2025
62f8415
KA suggestions
Julia-Garland Sep 29, 2025
ddf9c75
Format
Julia-Garland Sep 29, 2025
49e4095
Improvements
Julia-Garland Oct 1, 2025
c88434e
Missing WIN32 check
Julia-Garland Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/libmongoc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,34 @@ else ()
set (MONGOC_HAVE_BCRYPT_PBKDF2 0)
endif ()

# Check if SCH_CREDENTIALS is defined in schannel.h
if (WIN32 AND MONGOC_ENABLE_SSL_SECURE_CHANNEL)

FILE (WRITE ${CMAKE_CURRENT_BINARY_DIR}/sch_credentials_test.c
"
#include <windows.h>
#include <subauth.h>
#define SCHANNEL_USE_BLACKLISTS
#include <schannel.h>

int main(void) {
SCH_CREDENTIALS cred;
(void)cred;
return 0;
}
")

TRY_COMPILE (HAVE_SCH_CREDENTIALS ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/sch_credentials_test.c CMAKE_FLAGS
"-Werror -DCMAKE_CXX_LINK_EXECUTABLE='echo not linking now...'" OUTPUT_VARIABLE LOG2)
endif()

if (HAVE_SCH_CREDENTIALS)
set (MONGOC_HAVE_SCH_CREDENTIALS 1)
else ()
set (MONGOC_HAVE_SCH_CREDENTIALS 0)
endif ()


set (MONGOC_SOURCES
${PROJECT_SOURCE_DIR}/src/mongoc/mcd-azure.c
Expand Down
9 changes: 9 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@
# undef MONGOC_ENABLE_SSL_SECURE_CHANNEL
#endif

/*
* MONGOC_HAVE_SCH_CREDENTIALS is set from configure to determine if
* our Schannel Windows library supports SCH_CREDENTIALS
*/
#define MONGOC_HAVE_SCH_CREDENTIALS @MONGOC_HAVE_SCH_CREDENTIALS@

#if MONGOC_HAVE_SCH_CREDENTIALS != 1
# undef MONGOC_HAVE_SCH_CREDENTIALS
#endif

/*
* MONGOC_ENABLE_CRYPTO_CNG is set from configure to determine if we are
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@

#include <bson/bson.h>

#include <subauth.h>

/* Its mandatory to indicate to Windows who is compiling the code */
#define SECURITY_WIN32
#define SCHANNEL_USE_BLACKLISTS 1
#include <schannel.h>
#include <security.h>


BSON_BEGIN_DECLS


Expand All @@ -43,6 +45,9 @@ typedef enum {
ssl_connect_done
} ssl_connect_state;

/* enum for underlying type cred field in mongoc_secure_channel_cred */
typedef enum { schannel_cred, sch_credentials } schannel_credential_type;

/* Structs to store Schannel handles */
typedef struct {
CredHandle cred_handle;
Expand All @@ -52,7 +57,8 @@ typedef struct {
// `mongoc_secure_channel_cred` may be shared on multiple connections.
typedef struct _mongoc_secure_channel_cred {
PCCERT_CONTEXT cert; /* Owning. Optional client cert. */
SCHANNEL_CRED cred; // TODO: switch to SCH_CREDENTIALS to support TLS v1.3
schannel_credential_type cred_type;
void *cred; /* Underlying type is specified by schannel_credential_type. */
} mongoc_secure_channel_cred;

typedef struct {
Expand Down
135 changes: 117 additions & 18 deletions src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,19 @@

#include <bson/bson.h>

#include <subauth.h>

#undef MONGOC_LOG_DOMAIN
#define MONGOC_LOG_DOMAIN "stream-tls-secure-channel"


#define SECURITY_WIN32
#define SCHANNEL_USE_BLACKLISTS 1
#include <schannel.h>
#include <schnlsp.h>
#include <security.h>
#include <versionhelpers.h>


/* mingw doesn't define these */
#ifndef SP_PROT_TLS1_1_CLIENT
Expand All @@ -87,6 +92,9 @@
#define SP_PROT_TLS1_2_CLIENT 0x00000800
#endif

#ifndef SP_PROT_TLS1_3_CLIENT
#define SP_PROT_TLS1_3_CLIENT 0x00002000
#endif

static void
_mongoc_stream_tls_secure_channel_destroy(mongoc_stream_t *stream)
Expand Down Expand Up @@ -841,40 +849,116 @@ _mongoc_stream_tls_secure_channel_should_retry(mongoc_stream_t *stream)
RETURN(mongoc_stream_should_retry(tls->base_stream));
}

mongoc_secure_channel_cred *
mongoc_secure_channel_cred_new(const mongoc_ssl_opt_t *opt)
#ifdef MONGOC_HAVE_SCH_CREDENTIALS

void *
_mongoc_secure_channel_sch_credentials_new(const mongoc_ssl_opt_t *opt, PCCERT_CONTEXT *cert, DWORD enabled_protocols)
{
BSON_ASSERT_PARAM(opt);
mongoc_secure_channel_cred *cred = bson_malloc0(sizeof(mongoc_secure_channel_cred));
SCH_CREDENTIALS *cred = bson_malloc0(sizeof(SCH_CREDENTIALS));

cred->cred.dwVersion = SCHANNEL_CRED_VERSION;
cred->dwVersion = SCH_CREDENTIALS_VERSION;

/* SCHANNEL_CRED:
* SCH_USE_STRONG_CRYPTO is not available in VS2010
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa379810.aspx */
#ifdef SCH_USE_STRONG_CRYPTO
cred->cred.dwFlags = SCH_USE_STRONG_CRYPTO;
cred->dwFlags = SCH_USE_STRONG_CRYPTO;
#endif

/* By default, enable soft failing.
* A certificate with no revocation check is a soft failure. */
cred->cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK;
cred->dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK;
/* An offline OCSP responder / CRL distribution list is a soft failure. */
cred->cred.dwFlags |= SCH_CRED_IGNORE_REVOCATION_OFFLINE;
cred->dwFlags |= SCH_CRED_IGNORE_REVOCATION_OFFLINE;
if (opt->weak_cert_validation) {
cred->cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
cred->dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
TRACE("%s", "disabled server certificate checks");
} else {
cred->cred.dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION;
cred->dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION;
if (!_mongoc_ssl_opts_disable_certificate_revocation_check(opt)) {
cred->cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
cred->dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
TRACE("%s", "enabled server certificate revocation checks");
}
TRACE("%s", "enabled server certificate checks");
}

if (opt->allow_invalid_hostname) {
cred->cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
cred->dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
}

if (*cert) {
cred->cCreds = 1;
cred->paCred = cert;
}

cred->cTlsParameters = 1;
cred->pTlsParameters = bson_malloc0(sizeof(TLS_PARAMETERS));
cred->pTlsParameters->grbitDisabledProtocols = (DWORD)~enabled_protocols;

return (void *)cred;
}

#endif

void *
_mongoc_secure_channel_schannel_cred_new(const mongoc_ssl_opt_t *opt, PCCERT_CONTEXT *cert, DWORD enabled_protocols)
{
SCHANNEL_CRED *cred = bson_malloc0(sizeof(SCHANNEL_CRED));

cred->dwVersion = SCHANNEL_CRED_VERSION;

/* SCHANNEL_CRED:
* SCH_USE_STRONG_CRYPTO is not available in VS2010
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa379810.aspx */
#ifdef SCH_USE_STRONG_CRYPTO
cred->dwFlags = SCH_USE_STRONG_CRYPTO;
#endif

/* By default, enable soft failing.
* A certificate with no revocation check is a soft failure. */
cred->dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK;
/* An offline OCSP responder / CRL distribution list is a soft failure. */
cred->dwFlags |= SCH_CRED_IGNORE_REVOCATION_OFFLINE;
if (opt->weak_cert_validation) {
cred->dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
TRACE("%s", "disabled server certificate checks");
} else {
cred->dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION;
if (!_mongoc_ssl_opts_disable_certificate_revocation_check(opt)) {
cred->dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
TRACE("%s", "enabled server certificate revocation checks");
}
TRACE("%s", "enabled server certificate checks");
}

if (opt->allow_invalid_hostname) {
cred->dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
}

if (*cert) {
cred->cCreds = 1;
cred->paCred = cert;
}

cred->grbitEnabledProtocols = enabled_protocols;

return (void *)cred;
}

mongoc_secure_channel_cred *
mongoc_secure_channel_cred_new(const mongoc_ssl_opt_t *opt)
{
BSON_ASSERT_PARAM(opt);
mongoc_secure_channel_cred *cred = bson_malloc0(sizeof(mongoc_secure_channel_cred));

bool is_server = IsWindowsServer();
DWORD enabled_protocols = SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;

/* TLS 1.3 is supported on Windows 11 (or Windows Server 2022) and newer.
* Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */
if ((is_server && _mongoc_verify_windows_version(10, 0, 19044, false)) ||
(!is_server && _mongoc_verify_windows_version(10, 0, 22000, false))) {
enabled_protocols |= SP_PROT_TLS1_3_CLIENT;
}

if (opt->ca_file) {
Expand All @@ -887,14 +971,22 @@ mongoc_secure_channel_cred_new(const mongoc_ssl_opt_t *opt)

if (opt->pem_file) {
cred->cert = mongoc_secure_channel_setup_certificate(opt);
}

if (cred->cert) {
cred->cred.cCreds = 1;
cred->cred.paCred = &cred->cert;
}
#ifdef MONGOC_HAVE_SCH_CREDENTIALS
// SCH_CREDENTIALS is supported in Windows 10 1809 / Server 1809 and later
if (_mongoc_verify_windows_version(10, 0, 17763, false)) {
cred->cred = _mongoc_secure_channel_sch_credentials_new(opt, &cred->cert, enabled_protocols);
cred->cred_type = sch_credentials;
} else {
cred->cred = _mongoc_secure_channel_schannel_cred_new(opt, &cred->cert, enabled_protocols);
cred->cred_type = schannel_cred;
}
#else
cred->cred = _mongoc_secure_channel_schannel_cred_new(opt, &cred->cert, enabled_protocols);
cred->cred_type = schannel_cred;
#endif

cred->cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
return cred;
}

Expand All @@ -906,6 +998,13 @@ mongoc_secure_channel_cred_deleter(void *cred_void)
return;
}
CertFreeCertificateContext(cred->cert);
#ifdef MONGOC_HAVE_SCH_CREDENTIALS
if (cred->cred_type == sch_credentials) {
SCH_CREDENTIALS *sch_cred = (SCH_CREDENTIALS *)cred->cred;
bson_free(sch_cred->pTlsParameters);
}
#endif
bson_free(cred->cred);
bson_free(cred);
}

Expand Down Expand Up @@ -982,7 +1081,7 @@ mongoc_stream_tls_secure_channel_new_with_creds(mongoc_stream_t *base_stream,
UNISP_NAME, /* security package */
SECPKG_CRED_OUTBOUND, /* we are preparing outbound connection */
NULL, /* Optional logon */
&cred->cred, /* TLS "configuration", "auth data" */
cred->cred, /* TLS "configuration", "auth data" */
NULL, /* unused */
NULL, /* unused */
&secure_channel->cred_handle->cred_handle, /* credential OUT param */
Expand Down
75 changes: 75 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-version-functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@

#include <mongoc/mongoc-version.h>

#ifdef _WIN32
#include <winbase.h>
#include <winnt.h>

#include <windows.h>
#endif

/**
* mongoc_get_major_version:
*
Expand Down Expand Up @@ -75,3 +82,71 @@ mongoc_check_version(int required_major, int required_minor, int required_micro)
{
return MONGOC_CHECK_VERSION(required_major, required_minor, required_micro);
}

#ifdef _WIN32

typedef NTSTATUS(APIENTRY *RTLVERIFYVERSIONINFO_FN)(PRTL_OSVERSIONINFOEXW VersionInfo,
ULONG TypeMask,
ULONGLONG ConditionMask);

/**
* _mongoc_verify_windows_version:
*
* True if the Windows version is greater than or equal to the required
* desktop or server version.
*/
bool
_mongoc_verify_windows_version(int major_version, int minor_version, int build_number, bool strictly_equal)
{
static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo;
OSVERSIONINFOEXW osvi;
bool matched;
int op = VER_GREATER_EQUAL;

if (strictly_equal) {
op = VER_EQUAL;
}

/* Windows version functions may not return the correct version for
later Windows versions unless the application is so manifested. Try
to use the more accurate kernel function RtlVerifyVersionInfo */
HMODULE hDll = LoadLibrary(TEXT("Ntdll.dll"));
if (hDll) {
pRtlVerifyVersionInfo = (RTLVERIFYVERSIONINFO_FN)GetProcAddress(hDll, "RtlVerifyVersionInfo");
}

ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
osvi.dwMajorVersion = major_version;
osvi.dwMinorVersion = minor_version;

ULONGLONG mask = 0;
VER_SET_CONDITION(mask, VER_MAJORVERSION, op);
VER_SET_CONDITION(mask, VER_MINORVERSION, op);

if (pRtlVerifyVersionInfo) {
matched = (pRtlVerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) == 0);
} else {
matched = (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0);
}

// Compare build number separately if major and minor versions are equal
if (build_number && matched && _mongoc_verify_windows_version(major_version, minor_version, 0, true)) {
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
osvi.dwBuildNumber = build_number;

mask = 0;
VER_SET_CONDITION(mask, VER_BUILDNUMBER, op);

if (pRtlVerifyVersionInfo) {
matched = (pRtlVerifyVersionInfo(&osvi, VER_BUILDNUMBER, mask) == 0);
} else {
matched = (VerifyVersionInfoW(&osvi, VER_BUILDNUMBER, mask) != 0);
}
}

return matched;
}

#endif
4 changes: 4 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-version-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ MONGOC_EXPORT(const char *)
mongoc_get_version(void);
MONGOC_EXPORT(bool)
mongoc_check_version(int required_major, int required_minor, int required_micro);
#ifdef _WIN32
MONGOC_EXPORT(bool)
_mongoc_verify_windows_version(int major_version, int minor_version, int build_number, bool strictly_equal);
#endif

BSON_END_DECLS

Expand Down
Loading