Skip to content

Commit 6d8fc7e

Browse files
authored
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 6a5aabf commit 6d8fc7e

24 files changed

+913
-15
lines changed

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

Lines changed: 15 additions & 2 deletions
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

Lines changed: 170 additions & 4 deletions
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,7 @@ _mongoc_client_monitor_op_killcursors (mongoc_cluster_t *cluster,
23602360
operation_id,
23612361
&server_stream->sd->host,
23622362
server_stream->sd->id,
2363+
NULL,
23632364
client->apm_context);
23642365

23652366
client->apm_callbacks.started (&event);
@@ -2406,6 +2407,7 @@ _mongoc_client_monitor_op_killcursors_succeeded (
24062407
operation_id,
24072408
&server_stream->sd->host,
24082409
server_stream->sd->id,
2410+
false,
24092411
client->apm_context);
24102412

24112413
client->apm_callbacks.succeeded (&event);
@@ -2448,6 +2450,7 @@ _mongoc_client_monitor_op_killcursors_failed (
24482450
operation_id,
24492451
&server_stream->sd->host,
24502452
server_stream->sd->id,
2453+
false,
24512454
client->apm_context);
24522455

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

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
508508
bson_t encrypted = BSON_INITIALIZER;
509509
bson_t decrypted = BSON_INITIALIZER;
510510
mongoc_cmd_t encrypted_cmd;
511+
bool is_redacted = false;
511512

512513
server_stream = cmd->server_stream;
513514
server_id = server_stream->sd->id;
@@ -534,8 +535,11 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
534535
}
535536

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

540544
callbacks->started (&started_event);
541545
mongoc_apm_command_started_cleanup (&started_event);
@@ -579,6 +583,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
579583
cmd->operation_id,
580584
&server_stream->sd->host,
581585
server_id,
586+
is_redacted,
582587
cluster->client->apm_context);
583588

584589
callbacks->succeeded (&succeeded_event);
@@ -595,6 +600,7 @@ mongoc_cluster_run_command_monitored (mongoc_cluster_t *cluster,
595600
cmd->operation_id,
596601
&server_stream->sd->host,
597602
server_id,
603+
is_redacted,
598604
cluster->client->apm_context);
599605

600606
callbacks->failed (&failed_event);

0 commit comments

Comments
 (0)