Skip to content

Commit 82ed2a0

Browse files
committed
CDRIVER-1154 verify cert on reconnect
Single-threaded clients had not re-checked the server certificate after a disconnect. Conflicts: src/mongoc/mongoc-cluster.c tests/test-mongoc-client.c
1 parent 256890d commit 82ed2a0

File tree

6 files changed

+207
-13
lines changed

6 files changed

+207
-13
lines changed

src/mongoc/mongoc-client.c

+11-3
Original file line numberDiff line numberDiff line change
@@ -330,12 +330,20 @@ mongoc_client_default_stream_initiator (const mongoc_uri_t *uri,
330330
connecttimeoutms = mongoc_uri_get_option_as_int32 (
331331
uri, "connecttimeoutms", MONGOC_DEFAULT_CONNECTTIMEOUTMS);
332332

333-
if (!mongoc_stream_tls_do_handshake (base_stream, connecttimeoutms) ||
334-
!mongoc_stream_tls_check_cert (base_stream, host->host)) {
333+
if (!mongoc_stream_tls_do_handshake (base_stream, connecttimeoutms)) {
335334
bson_set_error (error,
336335
MONGOC_ERROR_STREAM,
337336
MONGOC_ERROR_STREAM_SOCKET,
338-
"Failed to handshake and validate TLS certificate.");
337+
"TLS handshake failed.");
338+
mongoc_stream_destroy (base_stream);
339+
return NULL;
340+
}
341+
342+
if (!mongoc_stream_tls_check_cert (base_stream, host->host)) {
343+
bson_set_error (error,
344+
MONGOC_ERROR_STREAM,
345+
MONGOC_ERROR_STREAM_SOCKET,
346+
"Failed to verify peer certificate.");
339347
mongoc_stream_destroy (base_stream);
340348
return NULL;
341349
}

src/mongoc/mongoc-cluster.c

+30-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
#include "mongoc-util-private.h"
5050
#include "mongoc-write-concern-private.h"
5151
#include "mongoc-uri-private.h"
52-
52+
#ifdef MONGOC_ENABLE_SSL
53+
#include "mongoc-stream-tls.h"
54+
#endif
5355

5456
#undef MONGOC_LOG_DOMAIN
5557
#define MONGOC_LOG_DOMAIN "cluster"
@@ -1395,9 +1397,12 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
13951397
{
13961398
mongoc_stream_t *stream = NULL;
13971399
mongoc_topology_scanner_node_t *scanner_node;
1400+
int64_t timeout_ms;
13981401
int64_t expire_at;
13991402
bson_t reply;
14001403

1404+
timeout_ms = topology->connect_timeout_msec;
1405+
14011406
scanner_node = mongoc_topology_scanner_get_node (topology->scanner, sd->id);
14021407
BSON_ASSERT (scanner_node && !scanner_node->retired);
14031408
stream = scanner_node->stream;
@@ -1413,7 +1418,8 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
14131418
}
14141419
stream = scanner_node->stream;
14151420

1416-
expire_at = bson_get_monotonic_time() + topology->connect_timeout_msec * 1000;
1421+
expire_at = bson_get_monotonic_time() + timeout_ms * 1000;
1422+
14171423
if (!mongoc_stream_wait (stream, expire_at)) {
14181424
bson_set_error (error,
14191425
MONGOC_ERROR_STREAM,
@@ -1425,6 +1431,28 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster,
14251431
return NULL;
14261432
}
14271433

1434+
#ifdef MONGOC_ENABLE_SSL
1435+
if (scanner_node->ts->ssl_opts) {
1436+
if (!mongoc_stream_tls_do_handshake (stream, (int32_t) timeout_ms)) {
1437+
bson_set_error (error,
1438+
MONGOC_ERROR_STREAM,
1439+
MONGOC_ERROR_STREAM_SOCKET,
1440+
"TLS handshake failed.");
1441+
mongoc_topology_scanner_node_disconnect (scanner_node, true);
1442+
return NULL;
1443+
}
1444+
1445+
if (!mongoc_stream_tls_check_cert (stream, scanner_node->host.host)) {
1446+
bson_set_error (error,
1447+
MONGOC_ERROR_STREAM,
1448+
MONGOC_ERROR_STREAM_SOCKET,
1449+
"Failed to verify peer certificate.");
1450+
mongoc_topology_scanner_node_disconnect (scanner_node, true);
1451+
return NULL;
1452+
}
1453+
}
1454+
#endif
1455+
14281456
if (!_mongoc_stream_run_ismaster (cluster, stream, &reply, error)) {
14291457
mongoc_topology_scanner_node_disconnect (scanner_node, true);
14301458
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
bio_mongoc_shim = BIO_new (&gMongocStreamTlsRawMethods);
986992

tests/mock_server/mock-server.c

+25-6
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ struct _mock_server_t
5959
int64_t start_time;
6060

6161
#ifdef MONGOC_ENABLE_SSL
62-
mongoc_ssl_opt_t *ssl_opts;
62+
bool ssl;
63+
mongoc_ssl_opt_t ssl_opts;
6364
#endif
6465
};
6566

@@ -212,8 +213,6 @@ mock_server_down (void)
212213
*
213214
* Set server-side SSL options before calling mock_server_run.
214215
*
215-
* opts should be valid for server's lifetime.
216-
*
217216
* Returns:
218217
* None.
219218
*
@@ -226,7 +225,10 @@ void
226225
mock_server_set_ssl_opts (mock_server_t *server,
227226
mongoc_ssl_opt_t *opts)
228227
{
229-
server->ssl_opts = opts;
228+
mongoc_mutex_lock (&server->mutex);
229+
server->ssl = true;
230+
memcpy (&server->ssl_opts, opts, sizeof *opts);
231+
mongoc_mutex_unlock (&server->mutex);
230232
}
231233

232234
#endif
@@ -1385,14 +1387,17 @@ main_thread (void *data)
13851387
client_stream = mongoc_stream_socket_new (client_sock);
13861388

13871389
#ifdef MONGOC_ENABLE_SSL
1388-
if (server->ssl_opts) {
1390+
mongoc_mutex_lock (&server->mutex);
1391+
if (server->ssl) {
13891392
client_stream = mongoc_stream_tls_new (client_stream,
1390-
server->ssl_opts, 0);
1393+
&server->ssl_opts, 0);
13911394
if (!client_stream) {
1395+
mongoc_mutex_unlock (&server->mutex);
13921396
perror ("Failed to attach tls stream");
13931397
break;
13941398
}
13951399
}
1400+
mongoc_mutex_unlock (&server->mutex);
13961401
#endif
13971402
closure = (worker_closure_t *)bson_malloc (sizeof *closure);
13981403
closure->server = server;
@@ -1446,10 +1451,24 @@ worker_thread (void *data)
14461451
ssize_t i;
14471452
autoresponder_handle_t handle;
14481453

1454+
#ifdef MONGOC_ENABLE_SSL
1455+
bool ssl;
1456+
#endif
1457+
14491458
ENTRY;
14501459

14511460
BSON_ASSERT(closure);
14521461

1462+
#ifdef MONGOC_ENABLE_SSL
1463+
mongoc_mutex_lock (&server->mutex);
1464+
ssl = server->ssl;
1465+
mongoc_mutex_unlock (&server->mutex);
1466+
1467+
if (ssl) {
1468+
mongoc_stream_tls_do_handshake (client_stream, TIMEOUT);
1469+
}
1470+
#endif
1471+
14531472
_mongoc_buffer_init (&buffer, NULL, 0, NULL, NULL);
14541473
_mongoc_array_init (&autoresponders, sizeof (autoresponder_handle_t));
14551474

tests/test-mongoc-client.c

+133
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
#undef MONGOC_LOG_DOMAIN
2222
#define MONGOC_LOG_DOMAIN "client-test"
2323

24+
#define TRUST_DIR "tests/trust_dir"
25+
#define VERIFY_DIR TRUST_DIR "/verify"
26+
#define CAFILE TRUST_DIR "/verify/mongo_root.pem"
27+
#define PEMFILE_LOCALHOST TRUST_DIR "/keys/127.0.0.1.pem"
28+
#define PEMFILE_NOPASS TRUST_DIR "/keys/mongodb.com.pem"
29+
30+
2431
static char *
2532
gen_test_user (void)
2633
{
@@ -971,6 +978,126 @@ test_ssl_pooled (void)
971978
#endif
972979

973980

981+
982+
#ifdef MONGOC_ENABLE_SSL
983+
static bool
984+
_cmd (mock_server_t *server,
985+
mongoc_client_t *client,
986+
bool server_replies,
987+
bson_error_t *error)
988+
{
989+
future_t *future;
990+
request_t *request;
991+
bool r;
992+
993+
future = future_client_command_simple (client, "db", tmp_bson ("{'cmd': 1}"),
994+
NULL, NULL, error);
995+
request = mock_server_receives_command (server, "db", MONGOC_QUERY_SLAVE_OK,
996+
"{'cmd': 1}");
997+
ASSERT (request);
998+
999+
if (server_replies) {
1000+
mock_server_replies_simple (request, "{'ok': 1}");
1001+
} else {
1002+
mock_server_hangs_up (request);
1003+
}
1004+
1005+
r = future_get_bool (future);
1006+
1007+
future_destroy (future);
1008+
request_destroy (request);
1009+
1010+
return r;
1011+
}
1012+
1013+
1014+
1015+
static void
1016+
_test_ssl_reconnect (bool pooled)
1017+
{
1018+
mongoc_uri_t *uri;
1019+
mock_server_t *server;
1020+
mongoc_ssl_opt_t client_opts = { 0 };
1021+
mongoc_ssl_opt_t server_opts = { 0 };
1022+
mongoc_client_pool_t *pool = NULL;
1023+
mongoc_client_t *client;
1024+
bson_error_t error;
1025+
future_t *future;
1026+
1027+
client_opts.ca_file = CAFILE;
1028+
1029+
server_opts.ca_file = CAFILE;
1030+
server_opts.pem_file = PEMFILE_LOCALHOST;
1031+
1032+
server = mock_server_with_autoismaster (0);
1033+
mock_server_set_ssl_opts (server, &server_opts);
1034+
mock_server_run (server);
1035+
1036+
uri = mongoc_uri_copy (mock_server_get_uri (server));
1037+
mongoc_uri_set_option_as_int32 (uri, "serverSelectionTimeoutMS", 100);
1038+
1039+
if (pooled) {
1040+
pool = mongoc_client_pool_new (uri);
1041+
mongoc_client_pool_set_ssl_opts (pool, &client_opts);
1042+
client = mongoc_client_pool_pop (pool);
1043+
} else {
1044+
client = mongoc_client_new_from_uri (uri);
1045+
mongoc_client_set_ssl_opts (client, &client_opts);
1046+
}
1047+
1048+
ASSERT_OR_PRINT (_cmd (server, client, true /* server replies */, &error),
1049+
error);
1050+
1051+
/* man-in-the-middle: hostname switches from 127.0.0.1 to mongodb.com */
1052+
server_opts.pem_file = PEMFILE_NOPASS;
1053+
mock_server_set_ssl_opts (server, &server_opts);
1054+
1055+
/* server closes connections */
1056+
if (pooled) {
1057+
/* bg thread warns "failed to buffer", "handshake failed" */
1058+
suppress_one_message ();
1059+
suppress_one_message ();
1060+
}
1061+
1062+
ASSERT (!_cmd (server, client, false /* server hangs up */, &error));
1063+
1064+
/* next operation comes on a new connection, server verification fails */
1065+
future = future_client_command_simple (client, "db", tmp_bson ("{'cmd': 1}"),
1066+
NULL, NULL, &error);
1067+
ASSERT (!future_get_bool (future));
1068+
1069+
/* server selection errors don't say *why* they failed in C Driver 1.2 */
1070+
ASSERT_CMPINT (error.domain, ==, MONGOC_ERROR_SERVER_SELECTION);
1071+
ASSERT_CMPINT (error.code, ==, MONGOC_ERROR_SERVER_SELECTION_FAILURE);
1072+
1073+
if (pooled) {
1074+
mongoc_client_pool_push (pool, client);
1075+
mongoc_client_pool_destroy (pool);
1076+
} else {
1077+
mongoc_client_destroy (client);
1078+
}
1079+
1080+
future_destroy (future);
1081+
mock_server_destroy (server);
1082+
mongoc_uri_destroy (uri);
1083+
}
1084+
1085+
1086+
static void
1087+
test_ssl_reconnect_single (void)
1088+
{
1089+
_test_ssl_reconnect (false);
1090+
}
1091+
1092+
1093+
static void
1094+
test_ssl_reconnect_pooled (void)
1095+
{
1096+
_test_ssl_reconnect (true);
1097+
}
1098+
#endif
1099+
1100+
9741101
void
9751102
test_client_install (TestSuite *suite)
9761103
{
@@ -1012,5 +1139,11 @@ test_client_install (TestSuite *suite)
10121139
#ifdef MONGOC_ENABLE_SSL
10131140
TestSuite_Add (suite, "/Client/ssl_opts/single", test_ssl_single);
10141141
TestSuite_Add (suite, "/Client/ssl_opts/pooled", test_ssl_pooled);
1142+
TestSuite_Add (suite, "/Client/ssl/reconnect/single",
1143+
test_ssl_reconnect_single);
1144+
TestSuite_Add (suite, "/Client/ssl/reconnect/pooled",
1145+
test_ssl_reconnect_pooled);
1146+
#else
1147+
TestSuite_Add (suite, "/Client/ssl_disabled", test_mongoc_client_ssl_disabled);
10151148
#endif
10161149
}

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)