From c67dc1636596d9340b8e5fe8e0c41cc4aa90fb76 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 1 Jun 2018 16:28:56 -0400 Subject: [PATCH] CDRIVER-2579 handshake changes for auth Negotiate auth mechanisms when authenticating against a node. Single threaded clients negotiate in the topology scanner the first time running an ismaster against a node. Pooled clients negotiate when a cluster stream is created. --- src/libmongoc/src/mongoc/mongoc-cluster.c | 83 +++++++-- .../src/mongoc/mongoc-handshake-private.h | 15 ++ src/libmongoc/src/mongoc/mongoc-handshake.c | 38 ++++ .../src/mongoc/mongoc-server-description.c | 15 +- .../src/mongoc/mongoc-topology-private.h | 3 +- .../mongoc/mongoc-topology-scanner-private.h | 10 +- .../src/mongoc/mongoc-topology-scanner.c | 18 +- src/libmongoc/src/mongoc/mongoc-topology.c | 27 ++- src/libmongoc/src/mongoc/mongoc-uri-private.h | 3 + src/libmongoc/src/mongoc/mongoc-uri.c | 21 +++ src/libmongoc/tests/test-mongoc-scram.c | 173 ++++++++++++------ 11 files changed, 329 insertions(+), 77 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-cluster.c b/src/libmongoc/src/mongoc/mongoc-cluster.c index 8e9cf0ab5c..89fbd160b0 100644 --- a/src/libmongoc/src/mongoc/mongoc-cluster.c +++ b/src/libmongoc/src/mongoc/mongoc-cluster.c @@ -49,6 +49,7 @@ #include "mongoc-compression-private.h" #include "mongoc-cmd-private.h" #include "utlist.h" +#include "mongoc-handshake-private.h" #undef MONGOC_LOG_DOMAIN #define MONGOC_LOG_DOMAIN "cluster" @@ -683,11 +684,13 @@ mongoc_cluster_run_command_parts (mongoc_cluster_t *cluster, * * _mongoc_stream_run_ismaster -- * - * Run an ismaster command on the given stream. + * Run an ismaster command on the given stream. If + * @negotiate_sasl_supported_mechs is true, then saslSupportedMechs is + * added to the ismaster command. * * Returns: - * A mongoc_server_description_t you must destroy. If the call failed - * its error is set and its type is MONGOC_SERVER_UNKNOWN. + * A mongoc_server_description_t you must destroy or NULL. If the call + * failed its error is set and its type is MONGOC_SERVER_UNKNOWN. * *-------------------------------------------------------------------------- */ @@ -696,6 +699,7 @@ _mongoc_stream_run_ismaster (mongoc_cluster_t *cluster, mongoc_stream_t *stream, const char *address, uint32_t server_id, + bool negotiate_sasl_supported_mechs, bson_error_t *error) { const bson_t *command; @@ -705,7 +709,9 @@ _mongoc_stream_run_ismaster (mongoc_cluster_t *cluster, int64_t rtt_msec; mongoc_server_description_t *sd; mongoc_server_stream_t *server_stream; + bson_t *copied_command = NULL; bool r; + bson_iter_t iter; ENTRY; @@ -713,6 +719,14 @@ _mongoc_stream_run_ismaster (mongoc_cluster_t *cluster, BSON_ASSERT (stream); command = _mongoc_topology_get_ismaster (cluster->client->topology); + + if (negotiate_sasl_supported_mechs) { + copied_command = bson_copy (command); + _mongoc_handshake_append_sasl_supported_mechs (cluster->uri, + copied_command); + command = copied_command; + } + start = bson_get_monotonic_time (); server_stream = _mongoc_cluster_create_server_stream ( cluster->client->topology, server_id, stream, error); @@ -725,6 +739,18 @@ _mongoc_stream_run_ismaster (mongoc_cluster_t *cluster, parts.prohibit_lsid = true; if (!mongoc_cluster_run_command_parts ( cluster, server_stream, &parts, &reply, error)) { + if (negotiate_sasl_supported_mechs) { + if (bson_iter_init_find (&iter, &reply, "ok") && + !bson_iter_as_bool (&iter)) { + /* ismaster response returned ok: 0. According to auth spec: "If the + * isMaster of the MongoDB Handshake fails with an error, drivers + * MUST treat this an an authentication error." */ + error->domain = MONGOC_ERROR_CLIENT; + error->code = MONGOC_ERROR_CLIENT_AUTHENTICATE; + } + } + + bson_destroy (copied_command); bson_destroy (&reply); mongoc_server_stream_cleanup (server_stream); RETURN (NULL); @@ -753,6 +779,10 @@ _mongoc_stream_run_ismaster (mongoc_cluster_t *cluster, mongoc_server_stream_cleanup (server_stream); + if (copied_command) { + bson_destroy (copied_command); + } + RETURN (sd); } @@ -761,7 +791,7 @@ _mongoc_stream_run_ismaster (mongoc_cluster_t *cluster, * * _mongoc_cluster_run_ismaster -- * - * Run an ismaster command for the given node and handle result. + * Run an initial ismaster command for the given node and handle result. * * Returns: * mongoc_server_description_t on success, NULL otherwise. @@ -788,7 +818,12 @@ _mongoc_cluster_run_ismaster (mongoc_cluster_t *cluster, BSON_ASSERT (node->stream); sd = _mongoc_stream_run_ismaster ( - cluster, node->stream, node->connection_address, server_id, error); + cluster, + node->stream, + node->connection_address, + server_id, + _mongoc_uri_requires_auth_negotiation (cluster->uri), + error); if (!sd) { return NULL; @@ -1370,10 +1405,12 @@ _mongoc_cluster_auth_node_scram_sha_256 (mongoc_cluster_t *cluster, */ static bool -_mongoc_cluster_auth_node (mongoc_cluster_t *cluster, - mongoc_stream_t *stream, - mongoc_server_description_t *sd, - bson_error_t *error) +_mongoc_cluster_auth_node ( + mongoc_cluster_t *cluster, + mongoc_stream_t *stream, + mongoc_server_description_t *sd, + const mongoc_handshake_sasl_supported_mechs_t *sasl_supported_mechs, + bson_error_t *error) { bool ret = false; const char *mechanism; @@ -1385,7 +1422,18 @@ _mongoc_cluster_auth_node (mongoc_cluster_t *cluster, mechanism = mongoc_uri_get_auth_mechanism (cluster->uri); if (!mechanism) { - mechanism = "SCRAM-SHA-1"; + if (sasl_supported_mechs->scram_sha_256) { + /* Auth spec: "If SCRAM-SHA-256 is present in the list of mechanisms, + * then it MUST be used as the default; otherwise, SCRAM-SHA-1 MUST be + * used as the default, regardless of whether SCRAM-SHA-1 is in the + * list. Drivers MUST NOT attempt to use any other mechanism (e.g. + * PLAIN) as the default." [...] "If saslSupportedMechs is not present + * in the isMaster results for mechanism negotiation, then SCRAM-SHA-1 + * MUST be used when talking to servers >= 3.0." */ + mechanism = "SCRAM-SHA-256"; + } else { + mechanism = "SCRAM-SHA-1"; + } } if (0 == strcasecmp (mechanism, "MONGODB-CR")) { @@ -1545,6 +1593,7 @@ _mongoc_cluster_add_node (mongoc_cluster_t *cluster, mongoc_cluster_node_t *cluster_node = NULL; mongoc_stream_t *stream; mongoc_server_description_t *sd; + mongoc_handshake_sasl_supported_mechs_t sasl_supported_mechs; ENTRY; @@ -1571,16 +1620,17 @@ _mongoc_cluster_add_node (mongoc_cluster_t *cluster, /* take critical fields from a fresh ismaster */ cluster_node = _mongoc_cluster_node_new (stream, host->host_and_port); - /* CDRIVER-2579 pass 'saslSupportedMechs' to ismaster to determine the - * default auth mechanism. */ sd = _mongoc_cluster_run_ismaster (cluster, cluster_node, server_id, error); if (!sd) { GOTO (error); } + _mongoc_handshake_parse_sasl_supported_mechs (&sd->last_is_master, + &sasl_supported_mechs); + if (cluster->requires_auth) { if (!_mongoc_cluster_auth_node ( - cluster, cluster_node->stream, sd, error)) { + cluster, cluster_node->stream, sd, &sasl_supported_mechs, error)) { MONGOC_WARNING ("Failed authentication to %s (%s)", host->host_and_port, error->message); @@ -1816,8 +1866,11 @@ mongoc_cluster_fetch_stream_single (mongoc_cluster_t *cluster, /* stream open but not auth'ed: first use since connect or reconnect */ if (cluster->requires_auth && !scanner_node->has_auth) { - if (!_mongoc_cluster_auth_node ( - cluster, scanner_node->stream, sd, &sd->error)) { + if (!_mongoc_cluster_auth_node (cluster, + scanner_node->stream, + sd, + &scanner_node->sasl_supported_mechs, + &sd->error)) { memcpy (error, &sd->error, sizeof *error); mongoc_server_description_destroy (sd); return NULL; diff --git a/src/libmongoc/src/mongoc/mongoc-handshake-private.h b/src/libmongoc/src/mongoc/mongoc-handshake-private.h index 2a620e4094..d0fd3475c4 100644 --- a/src/libmongoc/src/mongoc/mongoc-handshake-private.h +++ b/src/libmongoc/src/mongoc/mongoc-handshake-private.h @@ -22,6 +22,7 @@ #error "Only can be included directly." #endif #include +#include BSON_BEGIN_DECLS @@ -111,6 +112,20 @@ _mongoc_handshake_get (void); bool _mongoc_handshake_appname_is_valid (const char *appname); +typedef struct { + bool scram_sha_256; + bool scram_sha_1; +} mongoc_handshake_sasl_supported_mechs_t; + +void +_mongoc_handshake_append_sasl_supported_mechs (const mongoc_uri_t *uri, + bson_t *ismaster); + +void +_mongoc_handshake_parse_sasl_supported_mechs ( + const bson_t *ismaster, + mongoc_handshake_sasl_supported_mechs_t *sasl_supported_mechs); + BSON_END_DECLS #endif diff --git a/src/libmongoc/src/mongoc/mongoc-handshake.c b/src/libmongoc/src/mongoc/mongoc-handshake.c index b7b9355cd3..c838c4cd4e 100644 --- a/src/libmongoc/src/mongoc/mongoc-handshake.c +++ b/src/libmongoc/src/mongoc/mongoc-handshake.c @@ -595,3 +595,41 @@ _mongoc_handshake_appname_is_valid (const char *appname) { return strlen (appname) <= MONGOC_HANDSHAKE_APPNAME_MAX; } + +void +_mongoc_handshake_append_sasl_supported_mechs (const mongoc_uri_t *uri, + bson_t *cmd) +{ + const char *username; + char *db_user; + username = mongoc_uri_get_username (uri); + db_user = + bson_strdup_printf ("%s.%s", mongoc_uri_get_auth_source (uri), username); + bson_append_utf8 (cmd, "saslSupportedMechs", 18, db_user, -1); + bson_free (db_user); +} + +void +_mongoc_handshake_parse_sasl_supported_mechs ( + const bson_t *ismaster, + mongoc_handshake_sasl_supported_mechs_t *sasl_supported_mechs) +{ + bson_iter_t iter; + memset (sasl_supported_mechs, 0, sizeof (*sasl_supported_mechs)); + if (bson_iter_init_find (&iter, ismaster, "saslSupportedMechs")) { + bson_iter_t array_iter; + if (BSON_ITER_HOLDS_ARRAY (&iter)) { + bson_iter_recurse (&iter, &array_iter); + while (bson_iter_next (&array_iter)) { + if (BSON_ITER_HOLDS_UTF8 (&array_iter)) { + const char *mechanism_name = bson_iter_utf8 (&array_iter, NULL); + if (0 == strcmp (mechanism_name, "SCRAM-SHA-256")) { + sasl_supported_mechs->scram_sha_256 = true; + } else if (0 == strcmp (mechanism_name, "SCRAM-SHA-1")) { + sasl_supported_mechs->scram_sha_1 = true; + } + } + } + } + } +} diff --git a/src/libmongoc/src/mongoc/mongoc-server-description.c b/src/libmongoc/src/mongoc/mongoc-server-description.c index 5ff7210683..0a3212ef5a 100644 --- a/src/libmongoc/src/mongoc/mongoc-server-description.c +++ b/src/libmongoc/src/mongoc/mongoc-server-description.c @@ -529,9 +529,20 @@ mongoc_server_description_handle_ismaster (mongoc_server_description_t *sd, while (bson_iter_next (&iter)) { num_keys++; if (strcmp ("ok", bson_iter_key (&iter)) == 0) { - /* ismaster responses never have ok: 0, but spec requires we check */ - if (!bson_iter_as_bool (&iter)) + if (!bson_iter_as_bool (&iter)) { + uint32_t unused; + const char *msg = NULL; + _mongoc_parse_error_reply (ismaster_response, false, &unused, &msg); + if (msg) { + bson_strncpy (sd->error.message, msg, sizeof (error->message)); + } + /* ismaster response returned ok: 0. According to auth spec: "If the + * isMaster of the MongoDB Handshake fails with an error, drivers + * MUST treat this an an authentication error." */ + sd->error.domain = MONGOC_ERROR_CLIENT; + sd->error.code = MONGOC_ERROR_CLIENT_AUTHENTICATE; goto failure; + } } else if (strcmp ("ismaster", bson_iter_key (&iter)) == 0) { if (!BSON_ITER_HOLDS_BOOL (&iter)) goto failure; diff --git a/src/libmongoc/src/mongoc/mongoc-topology-private.h b/src/libmongoc/src/mongoc/mongoc-topology-private.h index 9668477083..bb3ea99572 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-private.h +++ b/src/libmongoc/src/mongoc/mongoc-topology-private.h @@ -150,7 +150,8 @@ _mongoc_topology_end_sessions_cmd (mongoc_topology_t *topology, bson_t *cmd); void _mongoc_topology_do_blocking_scan (mongoc_topology_t *topology, bson_error_t *error); -bson_t* _mongoc_topology_get_ismaster (mongoc_topology_t* topology); +const bson_t * +_mongoc_topology_get_ismaster (mongoc_topology_t *topology); void _mongoc_topology_request_scan (mongoc_topology_t *topology); #endif diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h b/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h index b3b83c301f..7684fb96df 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h @@ -26,6 +26,7 @@ #include #include "mongoc-async-private.h" #include "mongoc-async-cmd-private.h" +#include "mongoc-handshake-private.h" #include "mongoc-host-list.h" #include "mongoc-apm-private.h" @@ -73,6 +74,11 @@ typedef struct mongoc_topology_scanner_node { struct addrinfo *dns_results; struct addrinfo *successful_dns_result; int64_t last_dns_cache; + + /* used by single-threaded clients to store negotiated sasl mechanisms on a + * node. */ + mongoc_handshake_sasl_supported_mechs_t sasl_supported_mechs; + bool negotiated_sasl_supported_mechs; } mongoc_topology_scanner_node_t; typedef struct mongoc_topology_scanner { @@ -101,6 +107,8 @@ typedef struct mongoc_topology_scanner { mongoc_apm_callbacks_t apm_callbacks; void *apm_context; int64_t dns_cache_timeout_ms; + /* only used by single-threaded clients to negotiate auth mechanisms. */ + bool negotiate_sasl_supported_mechs; } mongoc_topology_scanner_t; mongoc_topology_scanner_t * @@ -164,7 +172,7 @@ mongoc_topology_scanner_node_setup (mongoc_topology_scanner_node_t *node, mongoc_topology_scanner_node_t * mongoc_topology_scanner_get_node (mongoc_topology_scanner_t *ts, uint32_t id); -bson_t * +const bson_t * _mongoc_topology_scanner_get_ismaster (mongoc_topology_scanner_t *ts); bool diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index 8781d1e409..193ce314ae 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -35,6 +35,7 @@ #include "utlist.h" #include "mongoc-topology-private.h" #include "mongoc-host-list-private.h" +#include "mongoc-uri-private.h" #undef MONGOC_LOG_DOMAIN #define MONGOC_LOG_DOMAIN "topology_scanner" @@ -145,7 +146,7 @@ _build_ismaster_with_handshake (mongoc_topology_scanner_t *ts) * is called at the start of the scan in _mongoc_topology_run_background, when a * node is added in _mongoc_topology_reconcile_add_nodes, or when running an * ismaster directly on a node in _mongoc_stream_run_ismaster. */ -bson_t * +const bson_t * _mongoc_topology_scanner_get_ismaster (mongoc_topology_scanner_t *ts) { /* If this is the first time using the node or if it's the first time @@ -182,6 +183,11 @@ _begin_ismaster_cmd (mongoc_topology_scanner_node_t *node, bson_copy_to (_mongoc_topology_scanner_get_ismaster (ts), &cmd); } + if (node->ts->negotiate_sasl_supported_mechs && + !node->negotiated_sasl_supported_mechs) { + _mongoc_handshake_append_sasl_supported_mechs (ts->uri, &cmd); + } + if (!bson_empty (&ts->cluster_time)) { bson_append_document (&cmd, "$clusterTime", 12, &ts->cluster_time); } @@ -343,6 +349,9 @@ mongoc_topology_scanner_node_disconnect (mongoc_topology_scanner_node_t *node, } node->stream = NULL; + memset ( + &node->sasl_supported_mechs, 0, sizeof (node->sasl_supported_mechs)); + node->negotiated_sasl_supported_mechs = false; } } @@ -448,6 +457,13 @@ _async_success (mongoc_async_cmd_t *acmd, /* set our successful stream. */ BSON_ASSERT (!node->stream); node->stream = stream; + + if (ts->negotiate_sasl_supported_mechs && + !node->negotiated_sasl_supported_mechs) { + _mongoc_handshake_parse_sasl_supported_mechs ( + ismaster_response, &node->sasl_supported_mechs); + } + ts->cb (node->id, ismaster_response, rtt_msec, ts->cb_data, &acmd->error); } diff --git a/src/libmongoc/src/mongoc/mongoc-topology.c b/src/libmongoc/src/mongoc/mongoc-topology.c index abb74ef104..f1dbb1e1ab 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology.c +++ b/src/libmongoc/src/mongoc/mongoc-topology.c @@ -282,6 +282,14 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded) mongoc_cond_init (&topology->cond_client); mongoc_cond_init (&topology->cond_server); + if (single_threaded) { + /* single threaded clients negotiate sasl supported mechanisms during + * a topology scan. */ + if (_mongoc_uri_requires_auth_negotiation (uri)) { + topology->scanner->negotiate_sasl_supported_mechs = true; + } + } + topology_valid = true; service = mongoc_uri_get_service (uri); if (service) { @@ -1451,12 +1459,23 @@ _mongoc_topology_end_sessions_cmd (mongoc_topology_t *topology, bson_t *cmd) return i > 0; } -/* Locks topology->mutex and retrieves (possibly constructing) the handshake - * on the topology scanner. */ -bson_t * +/* + *-------------------------------------------------------------------------- + * + * _mongoc_topology_get_ismaster -- + * + * Locks topology->mutex and retrieves (possibly constructing) the + * handshake on the topology scanner. + * + * Returns: + * A bson_t representing an ismaster command. + * + *-------------------------------------------------------------------------- + */ +const bson_t * _mongoc_topology_get_ismaster (mongoc_topology_t *topology) { - bson_t *cmd; + const bson_t *cmd; mongoc_mutex_lock (&topology->mutex); cmd = _mongoc_topology_scanner_get_ismaster (topology->scanner); mongoc_mutex_unlock (&topology->mutex); diff --git a/src/libmongoc/src/mongoc/mongoc-uri-private.h b/src/libmongoc/src/mongoc/mongoc-uri-private.h index f313413f1c..aa572cafed 100644 --- a/src/libmongoc/src/mongoc/mongoc-uri-private.h +++ b/src/libmongoc/src/mongoc/mongoc-uri-private.h @@ -42,6 +42,9 @@ mongoc_uri_parse_options (mongoc_uri_t *uri, int32_t mongoc_uri_get_local_threshold_option (const mongoc_uri_t *uri); +bool +_mongoc_uri_requires_auth_negotiation (const mongoc_uri_t *uri); + BSON_END_DECLS diff --git a/src/libmongoc/src/mongoc/mongoc-uri.c b/src/libmongoc/src/mongoc/mongoc-uri.c index c86c4bfe9e..ea4c9650f9 100644 --- a/src/libmongoc/src/mongoc/mongoc-uri.c +++ b/src/libmongoc/src/mongoc/mongoc-uri.c @@ -2318,3 +2318,24 @@ mongoc_uri_set_option_as_utf8 (mongoc_uri_t *uri, return true; } + +/* + *-------------------------------------------------------------------------- + * + * _mongoc_uri_requires_auth_negotiation -- + * + * Returns true if auth mechanism is necessary for this uri. According + * to the auth spec: "If an application provides a username but does + * not provide an authentication mechanism, drivers MUST negotiate a + * mechanism". + * + * Returns: + * true if the driver should negotiate the auth mechanism for the uri + * + *-------------------------------------------------------------------------- + */ +bool +_mongoc_uri_requires_auth_negotiation (const mongoc_uri_t *uri) +{ + return mongoc_uri_get_username (uri) && !mongoc_uri_get_auth_mechanism (uri); +} diff --git a/src/libmongoc/tests/test-mongoc-scram.c b/src/libmongoc/tests/test-mongoc-scram.c index 0f8fd2a0ec..5ee17147ea 100644 --- a/src/libmongoc/tests/test-mongoc-scram.c +++ b/src/libmongoc/tests/test-mongoc-scram.c @@ -188,29 +188,44 @@ _drop_scram_users (void) } static void -_check_mechanism (const char *user, - const char *pwd, - const char *mechanism, - const char *mechanism_expected) +_check_mechanism (bool pooled, + const char *client_mech, + const char *server_mechs, + const char *expected_used_mech) { mock_server_t *server; - mongoc_client_t *client; + mongoc_client_pool_t *client_pool = NULL; + mongoc_client_t *client = NULL; mongoc_uri_t *uri; future_t *future; request_t *request; const bson_t *sasl_doc; - const char *mechanism_used; + const char *used_mech; + + server = mock_server_new (); + mock_server_auto_ismaster (server, + "{'ok': 1, 'minWireVersion': 3, " + "'maxWireVersion': %d, 'ismaster': true, " + "'saslSupportedMechs': [%s]}", + WIRE_VERSION_MAX, + server_mechs ? server_mechs : ""); - server = mock_server_with_autoismaster (WIRE_VERSION_MAX); mock_server_run (server); uri = mongoc_uri_copy (mock_server_get_uri (server)); - mongoc_uri_set_username (uri, user); - mongoc_uri_set_password (uri, pwd); - if (mechanism) { - mongoc_uri_set_auth_mechanism (uri, mechanism); + mongoc_uri_set_username (uri, "user"); + mongoc_uri_set_password (uri, "password"); + if (client_mech) { + mongoc_uri_set_auth_mechanism (uri, client_mech); } - client = mongoc_client_new_from_uri (uri); + if (pooled) { + client_pool = mongoc_client_pool_new (uri); + client = mongoc_client_pool_pop (client_pool); + /* suppress the auth failure logs from pooled clients. */ + capture_logs (true); + } else { + client = mongoc_client_new_from_uri (uri); + } future = future_client_command_simple (client, "admin", tmp_bson ("{'dbstats': 1}"), @@ -220,26 +235,34 @@ _check_mechanism (const char *user, request = mock_server_receives_msg (server, MONGOC_QUERY_NONE, tmp_bson ("{}")); sasl_doc = request_get_doc (request, 0); - mechanism_used = bson_lookup_utf8 (sasl_doc, "mechanism"); - ASSERT_CMPSTR (mechanism_used, mechanism_expected); + used_mech = bson_lookup_utf8 (sasl_doc, "mechanism"); + ASSERT_CMPSTR (used_mech, expected_used_mech); /* we're not actually going to auth, just hang up. */ mock_server_hangs_up (request); future_wait (future); future_destroy (future); request_destroy (request); mongoc_uri_destroy (uri); - mongoc_client_destroy (client); + if (pooled) { + mongoc_client_pool_push (client_pool, client); + mongoc_client_pool_destroy (client_pool); + capture_logs (false); + } else { + mongoc_client_destroy (client); + } mock_server_destroy (server); } static void -_try_auth (const char *user, +_try_auth (bool pooled, + const char *user, const char *pwd, const char *mechanism, bool should_succeed) { mongoc_uri_t *uri; - mongoc_client_t *client; + mongoc_client_pool_t *client_pool = NULL; + mongoc_client_t *client = NULL; bson_error_t error; bson_t reply; bool res; @@ -250,8 +273,16 @@ _try_auth (const char *user, if (mechanism) { mongoc_uri_set_auth_mechanism (uri, mechanism); } - client = mongoc_client_new_from_uri (uri); - mongoc_client_set_error_api (client, 2); + if (pooled) { + client_pool = mongoc_client_pool_new (uri); + mongoc_client_pool_set_error_api (client_pool, 2); + client = mongoc_client_pool_pop (client_pool); + /* suppress the auth failure logs from pooled clients. */ + capture_logs (true); + } else { + client = mongoc_client_new_from_uri (uri); + mongoc_client_set_error_api (client, 2); + } res = mongoc_client_command_simple (client, "admin", tmp_bson ("{'dbstats': 1}"), @@ -263,14 +294,76 @@ _try_auth (const char *user, ASSERT_MATCH (&reply, "{'db': 'admin', 'ok': 1}"); } else { ASSERT (!res); - ASSERT_ERROR_CONTAINS (error, - MONGOC_ERROR_CLIENT, - MONGOC_ERROR_CLIENT_AUTHENTICATE, - "Authentication failed"); + if (0 == strcmp (user, "unknown_user")) { + ASSERT_ERROR_CONTAINS (error, + MONGOC_ERROR_CLIENT, + MONGOC_ERROR_CLIENT_AUTHENTICATE, + "Could not find user"); + } else { + ASSERT_ERROR_CONTAINS (error, + MONGOC_ERROR_CLIENT, + MONGOC_ERROR_CLIENT_AUTHENTICATE, + "Authentication failed"); + } } bson_destroy (&reply); mongoc_uri_destroy (uri); - mongoc_client_destroy (client); + if (pooled) { + mongoc_client_pool_push (client_pool, client); + mongoc_client_pool_destroy (client_pool); + capture_logs (false); + } else { + mongoc_client_destroy (client); + } +} + +static void +_test_mongoc_scram_auth (bool pooled) +{ + /* Auth spec: "For each test user, verify that you can connect and run a + command requiring authentication for the following cases: + - Explicitly specifying each mechanism the user supports. + - Specifying no mechanism and relying on mechanism negotiation." */ + _try_auth (pooled, "sha1", "sha1", NULL, true); + _try_auth (pooled, "sha1", "sha1", "SCRAM-SHA-1", true); + _try_auth (pooled, "sha256", "sha256", NULL, true); + _try_auth (pooled, "sha256", "sha256", "SCRAM-SHA-256", true); + _try_auth (pooled, "both", "both", NULL, true); + _try_auth (pooled, "both", "both", "SCRAM-SHA-1", true); + _try_auth (pooled, "both", "both", "SCRAM-SHA-256", true); + + _check_mechanism (pooled, NULL, NULL, "SCRAM-SHA-1"); + _check_mechanism (pooled, NULL, "'SCRAM-SHA-1'", "SCRAM-SHA-1"); + _check_mechanism (pooled, NULL, "'SCRAM-SHA-256'", "SCRAM-SHA-256"); + _check_mechanism ( + pooled, NULL, "'SCRAM-SHA-1','SCRAM-SHA-256'", "SCRAM-SHA-256"); + + _check_mechanism (pooled, "SCRAM-SHA-1", NULL, "SCRAM-SHA-1"); + _check_mechanism (pooled, "SCRAM-SHA-1", "'SCRAM-SHA-1'", "SCRAM-SHA-1"); + _check_mechanism (pooled, "SCRAM-SHA-1", "'SCRAM-SHA-256'", "SCRAM-SHA-1"); + _check_mechanism ( + pooled, "SCRAM-SHA-1", "'SCRAM-SHA-1','SCRAM-SHA-256'", "SCRAM-SHA-1"); + + _check_mechanism (pooled, "SCRAM-SHA-256", NULL, "SCRAM-SHA-256"); + _check_mechanism (pooled, "SCRAM-SHA-256", "'SCRAM-SHA-1'", "SCRAM-SHA-256"); + _check_mechanism ( + pooled, "SCRAM-SHA-256", "'SCRAM-SHA-256'", "SCRAM-SHA-256"); + _check_mechanism (pooled, + "SCRAM-SHA-256", + "'SCRAM-SHA-1','SCRAM-SHA-256'", + "SCRAM-SHA-256"); + + /* Test some failure auths. */ + _try_auth (pooled, "sha1", "bad", NULL, false); + _try_auth (pooled, "sha256", "bad", NULL, false); + _try_auth (pooled, "both", "bad", NULL, false); + _try_auth (pooled, "sha1", "bad", "SCRAM-SHA-256", false); + _try_auth (pooled, "sha256", "bad", "SCRAM-SHA-1", false); + + /* Auth spec: "For a non-existent username, verify that not specifying a + * mechanism when connecting fails with the same error type that would occur + * with a correct username but incorrect password or mechanism." */ + _try_auth (pooled, "unknown_user", "bad", NULL, false); } /* test the auth tests described in the auth spec. */ @@ -280,36 +373,10 @@ test_mongoc_scram_auth (void *ctx) /* Auth spec: "Create three test users, one with only SHA-1, one with only * SHA-256 and one with both" */ _create_scram_users (); - /* Auth spec: "For each test user, verify that you can connect and run a - command requiring authentication for the following cases: - - Explicitly specifying each mechanism the user supports. - - Specifying no mechanism and relying on mechanism negotiation." - */ - _try_auth ("sha1", "sha1", NULL, true); - _try_auth ("sha1", "sha1", "SCRAM-SHA-1", true); - _try_auth ("sha256", "sha256", "SCRAM-SHA-256", true); - _try_auth ("both", "both", NULL, true); - _try_auth ("both", "both", "SCRAM-SHA-1", true); - _try_auth ("both", "both", "SCRAM-SHA-256", true); - - /* Auth spec: "For a test user supporting both SCRAM-SHA-1 and SCRAM-SHA-256, - * drivers should verify that negotiation selects SCRAM-SHA-256" */ - /* TODO: CDRIVER-2579, after mechanism is negotiated, SCRAM-SHA-256 should be - * the default: - * _check_mechanism ("sha256", "sha256", NULL, "SCRAM-SHA-256"); - * _try_auth ("sha256", "sha256", NULL, true); - */ - _check_mechanism ("sha1", "sha1", NULL, "SCRAM-SHA-1"); - _check_mechanism ("both", "both", NULL, "SCRAM-SHA-1"); - _check_mechanism ("both", "both", "SCRAM-SHA-1", "SCRAM-SHA-1"); - _check_mechanism ("both", "both", "SCRAM-SHA-256", "SCRAM-SHA-256"); - /* Test some failure auths. */ - _try_auth ("sha1", "bad", NULL, false); - _try_auth ("sha256", "bad", NULL, false); - _try_auth ("both", "bad", NULL, false); - _try_auth ("sha1", "bad", "SCRAM-SHA-256", false); - _try_auth ("sha256", "bad", "SCRAM-SHA-1", false); + _test_mongoc_scram_auth (false); + _test_mongoc_scram_auth (true); + _drop_scram_users (); }