17
17
#include "mongoc-util-private.h"
18
18
#include "mongoc-apm-private.h"
19
19
#include "mongoc-cmd-private.h"
20
+ #include "mongoc-handshake-private.h"
20
21
21
22
/*
22
23
* An Application Performance Management (APM) implementation, complying with
@@ -46,6 +47,24 @@ append_documents_from_cmd (const mongoc_cmd_t *cmd,
46
47
* Private initializer / cleanup functions.
47
48
*/
48
49
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
+ */
49
68
void
50
69
mongoc_apm_command_started_init (mongoc_apm_command_started_t * event ,
51
70
const bson_t * command ,
@@ -55,6 +74,7 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
55
74
int64_t operation_id ,
56
75
const mongoc_host_list_t * host ,
57
76
uint32_t server_id ,
77
+ bool * is_redacted , /* out */
58
78
void * context )
59
79
{
60
80
bson_iter_t iter ;
@@ -87,6 +107,21 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
87
107
event -> command_owned = false;
88
108
}
89
109
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
+
90
125
event -> database_name = database_name ;
91
126
event -> command_name = command_name ;
92
127
event -> request_id = request_id ;
@@ -97,10 +132,23 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
97
132
}
98
133
99
134
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
+ */
100
147
void
101
148
mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t * event ,
102
149
mongoc_cmd_t * cmd ,
103
150
int64_t request_id ,
151
+ bool * is_redacted , /* out */
104
152
void * context )
105
153
{
106
154
mongoc_apm_command_started_init (event ,
@@ -111,6 +159,7 @@ mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t *event,
111
159
cmd -> operation_id ,
112
160
& cmd -> server_stream -> sd -> host ,
113
161
cmd -> server_stream -> sd -> id ,
162
+ is_redacted ,
114
163
context );
115
164
116
165
/* OP_MSG document sequence for insert, update, or delete? */
@@ -127,6 +176,18 @@ mongoc_apm_command_started_cleanup (mongoc_apm_command_started_t *event)
127
176
}
128
177
129
178
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
+ */
130
191
void
131
192
mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t * event ,
132
193
int64_t duration ,
@@ -136,12 +197,23 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
136
197
int64_t operation_id ,
137
198
const mongoc_host_list_t * host ,
138
199
uint32_t server_id ,
200
+ bool force_redaction ,
139
201
void * context )
140
202
{
141
203
BSON_ASSERT (reply );
142
204
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
+
143
216
event -> duration = duration ;
144
- event -> reply = reply ;
145
217
event -> command_name = command_name ;
146
218
event -> request_id = request_id ;
147
219
event -> operation_id = operation_id ;
@@ -154,10 +226,24 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
154
226
void
155
227
mongoc_apm_command_succeeded_cleanup (mongoc_apm_command_succeeded_t * event )
156
228
{
157
- /* no-op */
229
+ if (event -> reply_owned ) {
230
+ bson_destroy (event -> reply );
231
+ }
158
232
}
159
233
160
234
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
+ */
161
247
void
162
248
mongoc_apm_command_failed_init (mongoc_apm_command_failed_t * event ,
163
249
int64_t duration ,
@@ -168,14 +254,25 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
168
254
int64_t operation_id ,
169
255
const mongoc_host_list_t * host ,
170
256
uint32_t server_id ,
257
+ bool force_redaction ,
171
258
void * context )
172
259
{
173
260
BSON_ASSERT (reply );
174
261
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
+
175
273
event -> duration = duration ;
176
274
event -> command_name = command_name ;
177
275
event -> error = error ;
178
- event -> reply = reply ;
179
276
event -> request_id = request_id ;
180
277
event -> operation_id = operation_id ;
181
278
event -> host = host ;
@@ -187,7 +284,9 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
187
284
void
188
285
mongoc_apm_command_failed_cleanup (mongoc_apm_command_failed_t * event )
189
286
{
190
- /* no-op */
287
+ if (event -> reply_owned ) {
288
+ bson_destroy (event -> reply );
289
+ }
191
290
}
192
291
193
292
@@ -775,3 +874,70 @@ mongoc_apm_set_server_heartbeat_failed_cb (
775
874
{
776
875
callbacks -> server_heartbeat_failed = cb ;
777
876
}
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
+ }
0 commit comments