Skip to content

Commit

Permalink
CDRIVER-2579 handshake changes for auth
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kevinAlbs committed Jun 13, 2018
1 parent 5ed4c92 commit c67dc16
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 77 deletions.
83 changes: 68 additions & 15 deletions src/libmongoc/src/mongoc/mongoc-cluster.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
*
*--------------------------------------------------------------------------
*/
Expand All @@ -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;
Expand All @@ -705,14 +709,24 @@ _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;

BSON_ASSERT (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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}

Expand All @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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")) {
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-handshake-private.h
Expand Up @@ -22,6 +22,7 @@
#error "Only <mongoc.h> can be included directly."
#endif
#include <bson.h>
#include <mongoc.h>

BSON_BEGIN_DECLS

Expand Down Expand Up @@ -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
38 changes: 38 additions & 0 deletions src/libmongoc/src/mongoc/mongoc-handshake.c
Expand Up @@ -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;
}
}
}
}
}
}
15 changes: 13 additions & 2 deletions src/libmongoc/src/mongoc/mongoc-server-description.c
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/libmongoc/src/mongoc/mongoc-topology-private.h
Expand Up @@ -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
10 changes: 9 additions & 1 deletion src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h
Expand Up @@ -26,6 +26,7 @@
#include <bson.h>
#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"

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 *
Expand Down Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion src/libmongoc/src/mongoc/mongoc-topology-scanner.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit c67dc16

Please sign in to comment.