Skip to content

Commit 43fa582

Browse files
committed
CDRIVER-1154 verify cert on reconnect
Single-threaded clients had not re-checked the server certificate after a disconnect.
1 parent f1265e7 commit 43fa582

File tree

6 files changed

+204
-13
lines changed

6 files changed

+204
-13
lines changed

src/mongoc/mongoc-client.c

+11-3
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,20 @@ mongoc_client_default_stream_initiator (const mongoc_uri_t *uri,
340340
connecttimeoutms = mongoc_uri_get_option_as_int32 (
341341
uri, "connecttimeoutms", MONGOC_DEFAULT_CONNECTTIMEOUTMS);
342342

343-
if (!mongoc_stream_tls_do_handshake (base_stream, connecttimeoutms) ||
344-
!mongoc_stream_tls_check_cert (base_stream, host->host)) {
343+
if (!mongoc_stream_tls_do_handshake (base_stream, connecttimeoutms)) {
345344
bson_set_error (error,
346345
MONGOC_ERROR_STREAM,
347346
MONGOC_ERROR_STREAM_SOCKET,
348-
"Failed to handshake and validate TLS certificate.");
347+
"TLS handshake failed.");
348+
mongoc_stream_destroy (base_stream);
349+
return NULL;
350+
}
351+
352+
if (!mongoc_stream_tls_check_cert (base_stream, host->host)) {
353+
bson_set_error (error,
354+
MONGOC_ERROR_STREAM,
355+
MONGOC_ERROR_STREAM_SOCKET,
356+
"Failed to verify peer certificate.");
349357
mongoc_stream_destroy (base_stream);
350358
return NULL;
351359
}

src/mongoc/mongoc-cluster.c

+29-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@
4545
#include "mongoc-util-private.h"
4646
#include "mongoc-write-concern-private.h"
4747
#include "mongoc-uri-private.h"
48-
48+
#ifdef MONGOC_ENABLE_SSL
49+
#include "mongoc-stream-tls.h"
50+
#endif
4951

5052
#undef MONGOC_LOG_DOMAIN
5153
#define MONGOC_LOG_DOMAIN "cluster"
@@ -1441,10 +1443,12 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
14411443
mongoc_topology_t *topology;
14421444
mongoc_stream_t *stream;
14431445
mongoc_topology_scanner_node_t *scanner_node;
1446+
int64_t timeout_ms;
14441447
int64_t expire_at;
14451448
bson_t reply;
14461449

14471450
topology = cluster->client->topology;
1451+
timeout_ms = topology->connect_timeout_msec;
14481452

14491453
scanner_node = mongoc_topology_scanner_get_node (topology->scanner, sd->id);
14501454
BSON_ASSERT (scanner_node && !scanner_node->retired);
@@ -1461,7 +1465,8 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
14611465
}
14621466
stream = scanner_node->stream;
14631467

1464-
expire_at = bson_get_monotonic_time() + topology->connect_timeout_msec * 1000;
1468+
expire_at = bson_get_monotonic_time() + timeout_ms * 1000;
1469+
14651470
if (!mongoc_stream_wait (stream, expire_at)) {
14661471
bson_set_error (error,
14671472
MONGOC_ERROR_STREAM,
@@ -1473,6 +1478,28 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
14731478
return NULL;
14741479
}
14751480

1481+
#ifdef MONGOC_ENABLE_SSL
1482+
if (scanner_node->ts->ssl_opts) {
1483+
if (!mongoc_stream_tls_do_handshake (stream, (int32_t) timeout_ms)) {
1484+
bson_set_error (error,
1485+
MONGOC_ERROR_STREAM,
1486+
MONGOC_ERROR_STREAM_SOCKET,
1487+
"TLS handshake failed.");
1488+
mongoc_topology_scanner_node_disconnect (scanner_node, true);
1489+
return NULL;
1490+
}
1491+
1492+
if (!mongoc_stream_tls_check_cert (stream, scanner_node->host.host)) {
1493+
bson_set_error (error,
1494+
MONGOC_ERROR_STREAM,
1495+
MONGOC_ERROR_STREAM_SOCKET,
1496+
"Failed to verify peer certificate.");
1497+
mongoc_topology_scanner_node_disconnect (scanner_node, true);
1498+
return NULL;
1499+
}
1500+
}
1501+
#endif
1502+
14761503
if (!_mongoc_stream_run_ismaster (cluster, stream, &reply, error)) {
14771504
mongoc_topology_scanner_node_disconnect (scanner_node, true);
14781505
return NULL;

src/mongoc/mongoc-stream-tls.c

+6
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,12 @@ mongoc_stream_tls_new (mongoc_stream_t *base_stream,
981981
return NULL;
982982
}
983983

984+
if (opt->weak_cert_validation) {
985+
SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, NULL);
986+
} else {
987+
SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_PEER, NULL);
988+
}
989+
984990
bio_ssl = BIO_new_ssl (ssl_ctx, client);
985991
if (!bio_ssl) {
986992
return NULL;

tests/mock_server/mock-server.c

+25-6
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ struct _mock_server_t
5454
int64_t start_time;
5555

5656
#ifdef MONGOC_ENABLE_SSL
57-
mongoc_ssl_opt_t *ssl_opts;
57+
bool ssl;
58+
mongoc_ssl_opt_t ssl_opts;
5859
#endif
5960
};
6061

@@ -200,8 +201,6 @@ mock_server_down (void)
200201
*
201202
* Set server-side SSL options before calling mock_server_run.
202203
*
203-
* opts should be valid for server's lifetime.
204-
*
205204
* Returns:
206205
* None.
207206
*
@@ -214,7 +213,10 @@ void
214213
mock_server_set_ssl_opts (mock_server_t *server,
215214
mongoc_ssl_opt_t *opts)
216215
{
217-
server->ssl_opts = opts;
216+
mongoc_mutex_lock (&server->mutex);
217+
server->ssl = true;
218+
memcpy (&server->ssl_opts, opts, sizeof *opts);
219+
mongoc_mutex_unlock (&server->mutex);
218220
}
219221

220222
#endif
@@ -1458,14 +1460,17 @@ main_thread (void *data)
14581460
client_stream = mongoc_stream_socket_new (client_sock);
14591461

14601462
#ifdef MONGOC_ENABLE_SSL
1461-
if (server->ssl_opts) {
1463+
mongoc_mutex_lock (&server->mutex);
1464+
if (server->ssl) {
14621465
client_stream = mongoc_stream_tls_new (client_stream,
1463-
server->ssl_opts, 0);
1466+
&server->ssl_opts, 0);
14641467
if (!client_stream) {
1468+
mongoc_mutex_unlock (&server->mutex);
14651469
perror ("Failed to attach tls stream");
14661470
break;
14671471
}
14681472
}
1473+
mongoc_mutex_unlock (&server->mutex);
14691474
#endif
14701475
closure = (worker_closure_t *)bson_malloc (sizeof *closure);
14711476
closure->server = server;
@@ -1519,10 +1524,24 @@ worker_thread (void *data)
15191524
ssize_t i;
15201525
autoresponder_handle_t handle;
15211526

1527+
#ifdef MONGOC_ENABLE_SSL
1528+
bool ssl;
1529+
#endif
1530+
15221531
ENTRY;
15231532

15241533
BSON_ASSERT(closure);
15251534

1535+
#ifdef MONGOC_ENABLE_SSL
1536+
mongoc_mutex_lock (&server->mutex);
1537+
ssl = server->ssl;
1538+
mongoc_mutex_unlock (&server->mutex);
1539+
1540+
if (ssl) {
1541+
mongoc_stream_tls_do_handshake (client_stream, TIMEOUT);
1542+
}
1543+
#endif
1544+
15261545
_mongoc_buffer_init (&buffer, NULL, 0, NULL, NULL);
15271546
_mongoc_array_init (&autoresponders, sizeof (autoresponder_handle_t));
15281547

tests/test-mongoc-client.c

+131
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
#undef MONGOC_LOG_DOMAIN
1919
#define MONGOC_LOG_DOMAIN "client-test"
2020

21+
#define TRUST_DIR "tests/trust_dir"
22+
#define VERIFY_DIR TRUST_DIR "/verify"
23+
#define CAFILE TRUST_DIR "/verify/mongo_root.pem"
24+
#define PEMFILE_LOCALHOST TRUST_DIR "/keys/127.0.0.1.pem"
25+
#define PEMFILE_NOPASS TRUST_DIR "/keys/mongodb.com.pem"
26+
27+
2128
static char *
2229
gen_test_user (void)
2330
{
@@ -1105,6 +1112,126 @@ test_mongoc_client_ssl_disabled (void)
11051112
#endif
11061113

11071114

1115+
1116+
#ifdef MONGOC_ENABLE_SSL
1117+
static bool
1118+
_cmd (mock_server_t *server,
1119+
mongoc_client_t *client,
1120+
bool server_replies,
1121+
bson_error_t *error)
1122+
{
1123+
future_t *future;
1124+
request_t *request;
1125+
bool r;
1126+
1127+
future = future_client_command_simple (client, "db", tmp_bson ("{'cmd': 1}"),
1128+
NULL, NULL, error);
1129+
request = mock_server_receives_command (server, "db", MONGOC_QUERY_SLAVE_OK,
1130+
NULL);
1131+
ASSERT (request);
1132+
1133+
if (server_replies) {
1134+
mock_server_replies_simple (request, "{'ok': 1}");
1135+
} else {
1136+
mock_server_hangs_up (request);
1137+
}
1138+
1139+
r = future_get_bool (future);
1140+
1141+
future_destroy (future);
1142+
request_destroy (request);
1143+
1144+
return r;
1145+
}
1146+
1147+
1148+
1149+
static void
1150+
_test_ssl_reconnect (bool pooled)
1151+
{
1152+
mongoc_uri_t *uri;
1153+
mock_server_t *server;
1154+
mongoc_ssl_opt_t client_opts = { 0 };
1155+
mongoc_ssl_opt_t server_opts = { 0 };
1156+
mongoc_client_pool_t *pool = NULL;
1157+
mongoc_client_t *client;
1158+
bson_error_t error;
1159+
future_t *future;
1160+
1161+
client_opts.ca_file = CAFILE;
1162+
1163+
server_opts.ca_file = CAFILE;
1164+
server_opts.pem_file = PEMFILE_LOCALHOST;
1165+
1166+
server = mock_server_with_autoismaster (0);
1167+
mock_server_set_ssl_opts (server, &server_opts);
1168+
mock_server_run (server);
1169+
1170+
uri = mongoc_uri_copy (mock_server_get_uri (server));
1171+
mongoc_uri_set_option_as_int32 (uri, "serverSelectionTimeoutMS", 100);
1172+
1173+
if (pooled) {
1174+
pool = mongoc_client_pool_new (uri);
1175+
mongoc_client_pool_set_ssl_opts (pool, &client_opts);
1176+
client = mongoc_client_pool_pop (pool);
1177+
} else {
1178+
client = mongoc_client_new_from_uri (uri);
1179+
mongoc_client_set_ssl_opts (client, &client_opts);
1180+
}
1181+
1182+
ASSERT_OR_PRINT (_cmd (server, client, true /* server replies */, &error),
1183+
error);
1184+
1185+
/* man-in-the-middle: hostname switches from 127.0.0.1 to mongodb.com */
1186+
server_opts.pem_file = PEMFILE_NOPASS;
1187+
mock_server_set_ssl_opts (server, &server_opts);
1188+
1189+
/* server closes connections */
1190+
if (pooled) {
1191+
/* bg thread warns "failed to buffer", "handshake failed" */
1192+
suppress_one_message ();
1193+
suppress_one_message ();
1194+
}
1195+
1196+
ASSERT (!_cmd (server, client, false /* server hangs up */, &error));
1197+
1198+
/* next operation comes on a new connection, server verification fails */
1199+
future = future_client_command_simple (client, "db", tmp_bson ("{'cmd': 1}"),
1200+
NULL, NULL, &error);
1201+
ASSERT (!future_get_bool (future));
1202+
ASSERT_ERROR_CONTAINS (error,
1203+
MONGOC_ERROR_STREAM,
1204+
MONGOC_ERROR_STREAM_SOCKET,
1205+
"Failed to verify peer certificate");
1206+
1207+
if (pooled) {
1208+
mongoc_client_pool_push (pool, client);
1209+
mongoc_client_pool_destroy (pool);
1210+
} else {
1211+
mongoc_client_destroy (client);
1212+
}
1213+
1214+
future_destroy (future);
1215+
mock_server_destroy (server);
1216+
mongoc_uri_destroy (uri);
1217+
}
1218+
1219+
1220+
static void
1221+
test_ssl_reconnect_single (void)
1222+
{
1223+
_test_ssl_reconnect (false);
1224+
}
1225+
1226+
1227+
static void
1228+
test_ssl_reconnect_pooled (void)
1229+
{
1230+
_test_ssl_reconnect (true);
1231+
}
1232+
#endif
1233+
1234+
11081235
void
11091236
test_client_install (TestSuite *suite)
11101237
{
@@ -1149,6 +1276,10 @@ test_client_install (TestSuite *suite)
11491276
#ifdef MONGOC_ENABLE_SSL
11501277
TestSuite_Add (suite, "/Client/ssl_opts/single", test_ssl_single);
11511278
TestSuite_Add (suite, "/Client/ssl_opts/pooled", test_ssl_pooled);
1279+
TestSuite_Add (suite, "/Client/ssl/reconnect/single",
1280+
test_ssl_reconnect_single);
1281+
TestSuite_Add (suite, "/Client/ssl/reconnect/pooled",
1282+
test_ssl_reconnect_pooled);
11521283
#else
11531284
TestSuite_Add (suite, "/Client/ssl_disabled", test_mongoc_client_ssl_disabled);
11541285
#endif

tests/test-mongoc-stream-tls.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ test_mongoc_tls_crl (void)
150150

151151
ssl_test (&copt, &sopt, "rev.mongodb.com", &cr, &sr);
152152

153-
ASSERT (cr.result == SSL_TEST_SSL_VERIFY);
154-
ASSERT (sr.result == SSL_TEST_TIMEOUT);
153+
ASSERT (cr.result == SSL_TEST_SSL_HANDSHAKE);
154+
ASSERT (sr.result == SSL_TEST_SSL_HANDSHAKE);
155155
}
156156

157157

0 commit comments

Comments
 (0)