Skip to content

Commit 8a78b0e

Browse files
committed
CDRIVER-4000 Test redaction of sensitive commands (#814)
* Sync command monitoring spec tests This brings the spec tests up to date with mongodb/specifications#84ac002b * Add support for 1.5 unified test format schema This adds support for the "observeSensitiveCommands" monitoring option, as well as the new "auth" runOnRequirement. * Redact sensitive commands for APM * Test redaction of replies for sensitive commands * Force redaction of replies for sensitive commands Previously, the driver would not redact the reply to a hello command with speculative authentication unless the reply also was sensitive. As this makes the test completely useless, we've decided to always require redaction of replies when the command was redacted. * Document is_redacted and force_redaction arguments to APM initialisers * Make redaction helpers static
1 parent e1be09a commit 8a78b0e

7 files changed

+202
-8
lines changed

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

+15-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ struct _mongoc_apm_command_started_t {
6060

6161
struct _mongoc_apm_command_succeeded_t {
6262
int64_t duration;
63-
const bson_t *reply;
63+
bson_t *reply;
64+
bool reply_owned;
6465
const char *command_name;
6566
int64_t request_id;
6667
int64_t operation_id;
@@ -73,7 +74,8 @@ struct _mongoc_apm_command_failed_t {
7374
int64_t duration;
7475
const char *command_name;
7576
const bson_error_t *error;
76-
const bson_t *reply;
77+
bson_t *reply;
78+
bool reply_owned;
7779
int64_t request_id;
7880
int64_t operation_id;
7981
const mongoc_host_list_t *host;
@@ -153,12 +155,14 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
153155
int64_t operation_id,
154156
const mongoc_host_list_t *host,
155157
uint32_t server_id,
158+
bool *is_redacted, /* out */
156159
void *context);
157160

158161
void
159162
mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t *event,
160163
struct _mongoc_cmd_t *cmd,
161164
int64_t request_id,
165+
bool *is_redacted, /* out */
162166
void *context);
163167

164168
void
@@ -173,6 +177,7 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
173177
int64_t operation_id,
174178
const mongoc_host_list_t *host,
175179
uint32_t server_id,
180+
bool force_redaction,
176181
void *context);
177182

178183
void
@@ -188,11 +193,19 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
188193
int64_t operation_id,
189194
const mongoc_host_list_t *host,
190195
uint32_t server_id,
196+
bool force_redaction,
191197
void *context);
192198

193199
void
194200
mongoc_apm_command_failed_cleanup (mongoc_apm_command_failed_t *event);
195201

202+
bool
203+
mongoc_apm_is_sensitive_command (const char *command_name,
204+
const bson_t *command);
205+
206+
bool
207+
mongoc_apm_is_sensitive_reply (const char *command_name, const bson_t *reply);
208+
196209
BSON_END_DECLS
197210

198211
#endif /* MONGOC_APM_PRIVATE_H */

src/libmongoc/src/mongoc/mongoc-apm.c

+170-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "mongoc-util-private.h"
1818
#include "mongoc-apm-private.h"
1919
#include "mongoc-cmd-private.h"
20+
#include "mongoc-handshake-private.h"
2021

2122
/*
2223
* An Application Performance Management (APM) implementation, complying with
@@ -46,6 +47,24 @@ append_documents_from_cmd (const mongoc_cmd_t *cmd,
4647
* Private initializer / cleanup functions.
4748
*/
4849

50+
static void
51+
mongoc_apm_redact_command (bson_t *command);
52+
53+
static void
54+
mongoc_apm_redact_reply (bson_t *reply);
55+
56+
/*--------------------------------------------------------------------------
57+
*
58+
* mongoc_apm_command_started_init --
59+
*
60+
* Initialises the command started event.
61+
*
62+
* Side effects:
63+
* If provided, is_redacted indicates whether the command document was
64+
* redacted to hide sensitive information.
65+
*
66+
*--------------------------------------------------------------------------
67+
*/
4968
void
5069
mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
5170
const bson_t *command,
@@ -55,6 +74,7 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
5574
int64_t operation_id,
5675
const mongoc_host_list_t *host,
5776
uint32_t server_id,
77+
bool *is_redacted, /* out */
5878
void *context)
5979
{
6080
bson_iter_t iter;
@@ -87,6 +107,21 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
87107
event->command_owned = false;
88108
}
89109

110+
if (mongoc_apm_is_sensitive_command (command_name, command)) {
111+
if (!event->command_owned) {
112+
event->command = bson_copy (event->command);
113+
event->command_owned = true;
114+
}
115+
116+
if (is_redacted) {
117+
*is_redacted = true;
118+
}
119+
120+
mongoc_apm_redact_command (event->command);
121+
} else if (is_redacted) {
122+
*is_redacted = false;
123+
}
124+
90125
event->database_name = database_name;
91126
event->command_name = command_name;
92127
event->request_id = request_id;
@@ -97,10 +132,23 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
97132
}
98133

99134

135+
/*--------------------------------------------------------------------------
136+
*
137+
* mongoc_apm_command_started_init_with_cmd --
138+
*
139+
* Initialises the command started event from a mongoc_cmd_t.
140+
*
141+
* Side effects:
142+
* If provided, is_redacted indicates whether the command document was
143+
* redacted to hide sensitive information.
144+
*
145+
*--------------------------------------------------------------------------
146+
*/
100147
void
101148
mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t *event,
102149
mongoc_cmd_t *cmd,
103150
int64_t request_id,
151+
bool *is_redacted, /* out */
104152
void *context)
105153
{
106154
mongoc_apm_command_started_init (event,
@@ -111,6 +159,7 @@ mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t *event,
111159
cmd->operation_id,
112160
&cmd->server_stream->sd->host,
113161
cmd->server_stream->sd->id,
162+
is_redacted,
114163
context);
115164

116165
/* OP_MSG document sequence for insert, update, or delete? */
@@ -127,6 +176,18 @@ mongoc_apm_command_started_cleanup (mongoc_apm_command_started_t *event)
127176
}
128177

129178

179+
/*--------------------------------------------------------------------------
180+
*
181+
* mongoc_apm_command_succeeded_init --
182+
*
183+
* Initialises the command succeeded event.
184+
*
185+
* Parameters:
186+
* @force_redaction: If true, the reply document is always redacted,
187+
* regardless of whether the command contains sensitive information.
188+
*
189+
*--------------------------------------------------------------------------
190+
*/
130191
void
131192
mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
132193
int64_t duration,
@@ -136,12 +197,23 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
136197
int64_t operation_id,
137198
const mongoc_host_list_t *host,
138199
uint32_t server_id,
200+
bool force_redaction,
139201
void *context)
140202
{
141203
BSON_ASSERT (reply);
142204

205+
if (force_redaction || mongoc_apm_is_sensitive_reply (command_name, reply)) {
206+
event->reply = bson_copy (reply);
207+
event->reply_owned = true;
208+
209+
mongoc_apm_redact_reply (event->reply);
210+
} else {
211+
/* discard "const", we promise not to modify "reply" */
212+
event->reply = (bson_t *) reply;
213+
event->reply_owned = false;
214+
}
215+
143216
event->duration = duration;
144-
event->reply = reply;
145217
event->command_name = command_name;
146218
event->request_id = request_id;
147219
event->operation_id = operation_id;
@@ -154,10 +226,24 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
154226
void
155227
mongoc_apm_command_succeeded_cleanup (mongoc_apm_command_succeeded_t *event)
156228
{
157-
/* no-op */
229+
if (event->reply_owned) {
230+
bson_destroy (event->reply);
231+
}
158232
}
159233

160234

235+
/*--------------------------------------------------------------------------
236+
*
237+
* mongoc_apm_command_failed_init --
238+
*
239+
* Initialises the command failed event.
240+
*
241+
* Parameters:
242+
* @force_redaction: If true, the reply document is always redacted,
243+
* regardless of whether the command contains sensitive information.
244+
*
245+
*--------------------------------------------------------------------------
246+
*/
161247
void
162248
mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
163249
int64_t duration,
@@ -168,14 +254,25 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
168254
int64_t operation_id,
169255
const mongoc_host_list_t *host,
170256
uint32_t server_id,
257+
bool force_redaction,
171258
void *context)
172259
{
173260
BSON_ASSERT (reply);
174261

262+
if (force_redaction || mongoc_apm_is_sensitive_reply (command_name, reply)) {
263+
event->reply = bson_copy (reply);
264+
event->reply_owned = true;
265+
266+
mongoc_apm_redact_reply (event->reply);
267+
} else {
268+
/* discard "const", we promise not to modify "reply" */
269+
event->reply = (bson_t *) reply;
270+
event->reply_owned = false;
271+
}
272+
175273
event->duration = duration;
176274
event->command_name = command_name;
177275
event->error = error;
178-
event->reply = reply;
179276
event->request_id = request_id;
180277
event->operation_id = operation_id;
181278
event->host = host;
@@ -187,7 +284,9 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
187284
void
188285
mongoc_apm_command_failed_cleanup (mongoc_apm_command_failed_t *event)
189286
{
190-
/* no-op */
287+
if (event->reply_owned) {
288+
bson_destroy (event->reply);
289+
}
191290
}
192291

193292

@@ -775,3 +874,70 @@ mongoc_apm_set_server_heartbeat_failed_cb (
775874
{
776875
callbacks->server_heartbeat_failed = cb;
777876
}
877+
878+
static bool
879+
_mongoc_apm_is_sensitive_command_name (const char *command_name)
880+
{
881+
return 0 == strcasecmp (command_name, "authenticate") ||
882+
0 == strcasecmp (command_name, "saslStart") ||
883+
0 == strcasecmp (command_name, "saslContinue") ||
884+
0 == strcasecmp (command_name, "getnonce") ||
885+
0 == strcasecmp (command_name, "createUser") ||
886+
0 == strcasecmp (command_name, "updateUser") ||
887+
0 == strcasecmp (command_name, "copydbgetnonce") ||
888+
0 == strcasecmp (command_name, "copydbsaslstart") ||
889+
0 == strcasecmp (command_name, "copydb");
890+
}
891+
892+
bool
893+
mongoc_apm_is_sensitive_command (const char *command_name,
894+
const bson_t *command)
895+
{
896+
BSON_ASSERT (command);
897+
898+
if (_mongoc_apm_is_sensitive_command_name (command_name)) {
899+
return true;
900+
}
901+
902+
if (0 != strcasecmp (command_name, "hello") &&
903+
0 != strcasecmp (command_name, HANDSHAKE_CMD_LEGACY_HELLO)) {
904+
return false;
905+
}
906+
907+
return bson_has_field (command, "speculativeAuthenticate");
908+
}
909+
910+
void
911+
mongoc_apm_redact_command (bson_t *command)
912+
{
913+
BSON_ASSERT (command);
914+
915+
/* Reinit the command to have an empty document */
916+
bson_reinit (command);
917+
}
918+
919+
bool
920+
mongoc_apm_is_sensitive_reply (const char *command_name, const bson_t *reply)
921+
{
922+
BSON_ASSERT (reply);
923+
924+
if (_mongoc_apm_is_sensitive_command_name (command_name)) {
925+
return true;
926+
}
927+
928+
if (0 != strcasecmp (command_name, "hello") &&
929+
0 != strcasecmp (command_name, HANDSHAKE_CMD_LEGACY_HELLO)) {
930+
return false;
931+
}
932+
933+
return bson_has_field (reply, "speculativeAuthenticate");
934+
}
935+
936+
void
937+
mongoc_apm_redact_reply (bson_t *reply)
938+
{
939+
BSON_ASSERT (reply);
940+
941+
/* Reinit the reply to have an empty document */
942+
bson_reinit (reply);
943+
}

src/libmongoc/src/mongoc/mongoc-client.c

+3
Original file line numberDiff line numberDiff line change
@@ -2356,6 +2356,7 @@ _mongoc_client_monitor_op_killcursors (mongoc_cluster_t *cluster,
23562356
operation_id,
23572357
&server_stream->sd->host,
23582358
server_stream->sd->id,
2359+
NULL,
23592360
client->apm_context);
23602361

23612362
client->apm_callbacks.started (&event);
@@ -2402,6 +2403,7 @@ _mongoc_client_monitor_op_killcursors_succeeded (
24022403
operation_id,
24032404
&server_stream->sd->host,
24042405
server_stream->sd->id,
2406+
false,
24052407
client->apm_context);
24062408

24072409
client->apm_callbacks.succeeded (&event);
@@ -2444,6 +2446,7 @@ _mongoc_client_monitor_op_killcursors_failed (
24442446
operation_id,
24452447
&server_stream->sd->host,
24462448
server_stream->sd->id,
2449+
false,
24472450
client->apm_context);
24482451

24492452
client->apm_callbacks.failed (&event);

src/libmongoc/src/mongoc/mongoc-cluster.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
507507
bson_t encrypted = BSON_INITIALIZER;
508508
bson_t decrypted = BSON_INITIALIZER;
509509
mongoc_cmd_t encrypted_cmd;
510+
bool is_redacted = false;
510511

511512
server_stream = cmd->server_stream;
512513
server_id = server_stream->sd->id;
@@ -533,8 +534,11 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
533534
}
534535

535536
if (callbacks->started) {
536-
mongoc_apm_command_started_init_with_cmd (
537-
&started_event, cmd, request_id, cluster->client->apm_context);
537+
mongoc_apm_command_started_init_with_cmd (&started_event,
538+
cmd,
539+
request_id,
540+
&is_redacted,
541+
cluster->client->apm_context);
538542

539543
callbacks->started (&started_event);
540544
mongoc_apm_command_started_cleanup (&started_event);
@@ -578,6 +582,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
578582
cmd->operation_id,
579583
&server_stream->sd->host,
580584
server_id,
585+
is_redacted,
581586
cluster->client->apm_context);
582587

583588
callbacks->succeeded (&succeeded_event);
@@ -594,6 +599,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
594599
cmd->operation_id,
595600
&server_stream->sd->host,
596601
server_id,
602+
is_redacted,
597603
cluster->client->apm_context);
598604

599605
callbacks->failed (&failed_event);

0 commit comments

Comments
 (0)