Skip to content

Commit 5cb30b9

Browse files
CDRIVER-6045 Support TLS v1.3 in Secure Channel
1 parent ac38749 commit 5cb30b9

File tree

5 files changed

+201
-25
lines changed

5 files changed

+201
-25
lines changed

src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@
2424

2525
#include <bson/bson.h>
2626

27+
#include <subauth.h> // For SCH_CREDENTIALS
28+
2729
/* Its mandatory to indicate to Windows who is compiling the code */
2830
#define SECURITY_WIN32
31+
#define SCHANNEL_USE_BLACKLISTS 1 // For SCH_CREDENTIALS
2932
#include <schannel.h>
3033
#include <security.h>
3134

35+
#ifdef SCH_CREDENTIALS_VERSION
36+
#define HAVE_SCH_CREDENTIALS
37+
#endif
3238

3339
BSON_BEGIN_DECLS
3440

@@ -43,6 +49,9 @@ typedef enum {
4349
ssl_connect_done
4450
} ssl_connect_state;
4551

52+
/* enum for underlying type cred field in mongoc_secure_channel_cred */
53+
typedef enum { schannel_cred, sch_credentials } schannel_credential_type;
54+
4655
/* Structs to store Schannel handles */
4756
typedef struct {
4857
CredHandle cred_handle;
@@ -52,7 +61,8 @@ typedef struct {
5261
// `mongoc_secure_channel_cred` may be shared on multiple connections.
5362
typedef struct _mongoc_secure_channel_cred {
5463
PCCERT_CONTEXT cert; /* Owning. Optional client cert. */
55-
SCHANNEL_CRED cred; // TODO: switch to SCH_CREDENTIALS to support TLS v1.3
64+
schannel_credential_type cred_type;
65+
void *cred; /* Underlying type is specified by schannel_credential_type. */
5666
} mongoc_secure_channel_cred;
5767

5868
typedef struct {

src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c

Lines changed: 100 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,22 @@
6262
#include <mongoc/mongoc-stream-tls-private.h>
6363
#include <mongoc/mongoc-stream-tls-secure-channel-private.h>
6464
#include <mongoc/mongoc-trace-private.h>
65+
#include <mongoc/mongoc-util-private.h>
6566

6667
#include <mongoc/mongoc-log.h>
6768
#include <mongoc/mongoc-ssl.h>
6869
#include <mongoc/mongoc-stream-tls.h>
6970

7071
#include <bson/bson.h>
7172

73+
#include <subauth.h>
74+
7275
#undef MONGOC_LOG_DOMAIN
7376
#define MONGOC_LOG_DOMAIN "stream-tls-secure-channel"
7477

7578

7679
#define SECURITY_WIN32
80+
#define SCHANNEL_USE_BLACKLISTS 1
7781
#include <schannel.h>
7882
#include <schnlsp.h>
7983
#include <security.h>
@@ -87,6 +91,9 @@
8791
#define SP_PROT_TLS1_2_CLIENT 0x00000800
8892
#endif
8993

94+
#ifndef SP_PROT_TLS1_3_CLIENT
95+
#define SP_PROT_TLS1_3_CLIENT 0x00002000
96+
#endif
9097

9198
static void
9299
_mongoc_stream_tls_secure_channel_destroy(mongoc_stream_t *stream)
@@ -841,40 +848,96 @@ _mongoc_stream_tls_secure_channel_should_retry(mongoc_stream_t *stream)
841848
RETURN(mongoc_stream_should_retry(tls->base_stream));
842849
}
843850

844-
mongoc_secure_channel_cred *
845-
mongoc_secure_channel_cred_new(const mongoc_ssl_opt_t *opt)
851+
static DWORD
852+
get_cred_flags(const mongoc_ssl_opt_t *opt)
846853
{
847-
BSON_ASSERT_PARAM(opt);
848-
mongoc_secure_channel_cred *cred = bson_malloc0(sizeof(mongoc_secure_channel_cred));
854+
DWORD dwFlags;
849855

850-
cred->cred.dwVersion = SCHANNEL_CRED_VERSION;
851-
852-
/* SCHANNEL_CRED:
853-
* SCH_USE_STRONG_CRYPTO is not available in VS2010
854-
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa379810.aspx */
856+
/* SCH_USE_STRONG_CRYPTO is not available in VS2010
857+
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa379810.aspx */
855858
#ifdef SCH_USE_STRONG_CRYPTO
856-
cred->cred.dwFlags = SCH_USE_STRONG_CRYPTO;
859+
dwFlags = SCH_USE_STRONG_CRYPTO;
857860
#endif
858861

859862
/* By default, enable soft failing.
860863
* A certificate with no revocation check is a soft failure. */
861-
cred->cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK;
864+
dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK;
862865
/* An offline OCSP responder / CRL distribution list is a soft failure. */
863-
cred->cred.dwFlags |= SCH_CRED_IGNORE_REVOCATION_OFFLINE;
866+
dwFlags |= SCH_CRED_IGNORE_REVOCATION_OFFLINE;
864867
if (opt->weak_cert_validation) {
865-
cred->cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
868+
dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
866869
TRACE("%s", "disabled server certificate checks");
867870
} else {
868-
cred->cred.dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION;
871+
dwFlags |= SCH_CRED_AUTO_CRED_VALIDATION;
869872
if (!_mongoc_ssl_opts_disable_certificate_revocation_check(opt)) {
870-
cred->cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
873+
dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
871874
TRACE("%s", "enabled server certificate revocation checks");
872875
}
873876
TRACE("%s", "enabled server certificate checks");
874877
}
875878

876879
if (opt->allow_invalid_hostname) {
877-
cred->cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
880+
dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
881+
}
882+
883+
return dwFlags;
884+
}
885+
886+
#ifdef HAVE_SCH_CREDENTIALS
887+
888+
void *
889+
_mongoc_secure_channel_sch_credentials_new(const mongoc_ssl_opt_t *opt, PCCERT_CONTEXT *cert, DWORD enabled_protocols)
890+
{
891+
SCH_CREDENTIALS *cred = bson_malloc0(sizeof(SCH_CREDENTIALS));
892+
893+
cred->dwVersion = SCH_CREDENTIALS_VERSION;
894+
cred->dwFlags = get_cred_flags(opt);
895+
896+
if (*cert) {
897+
cred->cCreds = 1;
898+
cred->paCred = cert;
899+
}
900+
901+
cred->cTlsParameters = 1;
902+
cred->pTlsParameters = bson_malloc0(sizeof(TLS_PARAMETERS));
903+
cred->pTlsParameters->grbitDisabledProtocols = (DWORD)~enabled_protocols;
904+
905+
return (void *)cred;
906+
}
907+
908+
#endif
909+
910+
void *
911+
_mongoc_secure_channel_schannel_cred_new(const mongoc_ssl_opt_t *opt, PCCERT_CONTEXT *cert, DWORD enabled_protocols)
912+
{
913+
SCHANNEL_CRED *cred = bson_malloc0(sizeof(SCHANNEL_CRED));
914+
915+
cred->dwVersion = SCHANNEL_CRED_VERSION;
916+
cred->dwFlags = get_cred_flags(opt);
917+
918+
if (*cert) {
919+
cred->cCreds = 1;
920+
cred->paCred = cert;
921+
}
922+
923+
cred->grbitEnabledProtocols = enabled_protocols;
924+
925+
return (void *)cred;
926+
}
927+
928+
mongoc_secure_channel_cred *
929+
mongoc_secure_channel_cred_new(const mongoc_ssl_opt_t *opt)
930+
{
931+
BSON_ASSERT_PARAM(opt);
932+
mongoc_secure_channel_cred *cred = bson_malloc0(sizeof(mongoc_secure_channel_cred));
933+
934+
DWORD enabled_protocols = SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
935+
936+
/* TLS 1.3 is supported on Windows Server 2022 and newer.
937+
* Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */
938+
if (_mongoc_verify_windows_version(10, 0, 20348, false)) {
939+
// TODO - enable TLS 1.3 once renegotiation is supported.
940+
// enabled_protocols |= SP_PROT_TLS1_3_CLIENT;
878941
}
879942

880943
if (opt->ca_file) {
@@ -887,14 +950,22 @@ mongoc_secure_channel_cred_new(const mongoc_ssl_opt_t *opt)
887950

888951
if (opt->pem_file) {
889952
cred->cert = mongoc_secure_channel_setup_certificate(opt);
953+
}
890954

891-
if (cred->cert) {
892-
cred->cred.cCreds = 1;
893-
cred->cred.paCred = &cred->cert;
894-
}
955+
#ifdef HAVE_SCH_CREDENTIALS
956+
// SCH_CREDENTIALS is supported in Windows 10 1809 / Server 1809 and later
957+
if (_mongoc_verify_windows_version(10, 0, 17763, false)) {
958+
cred->cred = _mongoc_secure_channel_sch_credentials_new(opt, &cred->cert, enabled_protocols);
959+
cred->cred_type = sch_credentials;
960+
} else {
961+
cred->cred = _mongoc_secure_channel_schannel_cred_new(opt, &cred->cert, enabled_protocols);
962+
cred->cred_type = schannel_cred;
895963
}
964+
#else
965+
cred->cred = _mongoc_secure_channel_schannel_cred_new(opt, &cred->cert, enabled_protocols);
966+
cred->cred_type = schannel_cred;
967+
#endif
896968

897-
cred->cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
898969
return cred;
899970
}
900971

@@ -906,6 +977,13 @@ mongoc_secure_channel_cred_deleter(void *cred_void)
906977
return;
907978
}
908979
CertFreeCertificateContext(cred->cert);
980+
#ifdef HAVE_SCH_CREDENTIALS
981+
if (cred->cred_type == sch_credentials) {
982+
SCH_CREDENTIALS *sch_cred = (SCH_CREDENTIALS *)cred->cred;
983+
bson_free(sch_cred->pTlsParameters);
984+
}
985+
#endif
986+
bson_free(cred->cred);
909987
bson_free(cred);
910988
}
911989

@@ -982,7 +1060,7 @@ mongoc_stream_tls_secure_channel_new_with_creds(mongoc_stream_t *base_stream,
9821060
UNISP_NAME, /* security package */
9831061
SECPKG_CRED_OUTBOUND, /* we are preparing outbound connection */
9841062
NULL, /* Optional logon */
985-
&cred->cred, /* TLS "configuration", "auth data" */
1063+
cred->cred, /* TLS "configuration", "auth data" */
9861064
NULL, /* unused */
9871065
NULL, /* unused */
9881066
&secure_channel->cred_handle->cred_handle, /* credential OUT param */

src/libmongoc/src/mongoc/mongoc-util-private.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ typedef struct {
255255
uint64_t value;
256256
} mcd_optional_u64_t;
257257

258+
#ifdef _WIN32
259+
260+
/**
261+
* Returns true if the Windows version is greater than or equal to the required
262+
* desktop or server version.
263+
*/
264+
bool
265+
_mongoc_verify_windows_version(DWORD major_version, DWORD minor_version, DWORD build_number, bool strictly_equal);
266+
267+
#endif
268+
258269
BSON_END_DECLS
259270

260271
#endif /* MONGOC_UTIL_PRIVATE_H */

src/libmongoc/src/mongoc/mongoc-util.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@
3939
#include <mlib/loop.h>
4040
#include <mlib/time_point.h>
4141

42+
#ifdef _WIN32
43+
#include <winbase.h>
44+
#include <winnt.h>
45+
46+
#include <windows.h>
47+
#endif
48+
4249
#include <string.h>
4350

4451
/**
@@ -1025,3 +1032,62 @@ bin_to_hex(const uint8_t *bin, size_t bin_len)
10251032

10261033
return out;
10271034
}
1035+
1036+
#ifdef _WIN32
1037+
1038+
typedef NTSTATUS(APIENTRY *RTLVERIFYVERSIONINFO_FN)(PRTL_OSVERSIONINFOEXW VersionInfo,
1039+
ULONG TypeMask,
1040+
ULONGLONG ConditionMask);
1041+
1042+
bool
1043+
_mongoc_verify_windows_version(DWORD major_version, DWORD minor_version, DWORD build_number, bool strictly_equal)
1044+
{
1045+
static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo;
1046+
OSVERSIONINFOEXW osvi;
1047+
bool matched;
1048+
BYTE op = VER_GREATER_EQUAL;
1049+
1050+
if (strictly_equal) {
1051+
op = VER_EQUAL;
1052+
}
1053+
1054+
/* Windows version functions may not return the correct version for
1055+
later Windows versions unless the application is so manifested. Try
1056+
to use the more accurate kernel function RtlVerifyVersionInfo */
1057+
pRtlVerifyVersionInfo = (RTLVERIFYVERSIONINFO_FN)GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo");
1058+
1059+
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
1060+
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
1061+
osvi.dwMajorVersion = major_version;
1062+
osvi.dwMinorVersion = minor_version;
1063+
1064+
ULONGLONG mask = 0;
1065+
VER_SET_CONDITION(mask, VER_MAJORVERSION, op);
1066+
VER_SET_CONDITION(mask, VER_MINORVERSION, op);
1067+
1068+
if (pRtlVerifyVersionInfo) {
1069+
matched = (pRtlVerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) == 0);
1070+
} else {
1071+
matched = (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0);
1072+
}
1073+
1074+
// Compare build number separately if major and minor versions are equal
1075+
if (build_number && matched && _mongoc_verify_windows_version(major_version, minor_version, 0, true)) {
1076+
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
1077+
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
1078+
osvi.dwBuildNumber = build_number;
1079+
1080+
mask = 0;
1081+
VER_SET_CONDITION(mask, VER_BUILDNUMBER, op);
1082+
1083+
if (pRtlVerifyVersionInfo) {
1084+
matched = (pRtlVerifyVersionInfo(&osvi, VER_BUILDNUMBER, mask) == 0);
1085+
} else {
1086+
matched = (VerifyVersionInfoW(&osvi, VER_BUILDNUMBER, mask) != 0);
1087+
}
1088+
}
1089+
1090+
return matched;
1091+
}
1092+
1093+
#endif

src/libmongoc/tests/test-mongoc-secure-channel.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,22 @@ test_secure_channel_shared_creds_stream(void *unused)
9696
mongoc_shared_ptr_reset_null(&cred_ptr);
9797
}
9898

99-
// Test with bad SCHANNEL_CRED to exercise error path:
99+
// Test with bad SCHANNEL CREDENTIALS to exercise error path:
100100
{
101101
mongoc_secure_channel_cred *cred = mongoc_secure_channel_cred_new(&ssl_opt);
102102
mongoc_shared_ptr cred_ptr = mongoc_shared_ptr_create(cred, mongoc_secure_channel_cred_deleter);
103-
cred->cred.dwVersion = 0; // Invalid version.
103+
#ifdef HAVE_SCH_CREDENTIALS
104+
if (cred->cred_type == sch_credentials) {
105+
SCH_CREDENTIALS *sch_cred = (SCH_CREDENTIALS *)cred->cred;
106+
sch_cred->dwVersion = 0; // Invalid version.
107+
} else {
108+
SCHANNEL_CRED *sch_cred = (SCHANNEL_CRED *)cred->cred;
109+
sch_cred->dwVersion = 0;
110+
}
111+
#else
112+
SCHANNEL_CRED *sch_cred = (SCHANNEL_CRED *)cred->cred;
113+
sch_cred->dwVersion = 0;
114+
#endif
104115
capture_logs(true);
105116
mongoc_stream_t *stream = connect_with_secure_channel_cred(&ssl_opt, cred_ptr, &error);
106117
ASSERT(!stream);

0 commit comments

Comments
 (0)